@andre1502/react-utilities 0.9.7 → 1.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.
- package/dist/Config/Config.js +2 -4
- package/dist/Config/Config.js.map +1 -1
- package/dist/Config/GoogleAuth.js +1 -1
- package/dist/Config/GoogleAuth.js.map +1 -1
- package/dist/Config/Sitemap.js +1 -1
- package/dist/Config/Sitemap.js.map +1 -1
- package/dist/EnvironmentEnum-BjXsfSRZ.js.map +1 -1
- package/dist/EnvironmentEnum-UcQ6Il1O.js.map +1 -1
- package/dist/Format/NumberParser.js.map +1 -1
- package/dist/Hooks/useDevice.d.ts +3 -0
- package/dist/Hooks/useDevice.js +47 -0
- package/dist/Hooks/useDevice.js.map +1 -0
- package/dist/Hooks/useSEStore.d.ts +5 -0
- package/dist/Hooks/useSound.d.ts +9 -0
- package/dist/Hooks/useSound.js +47 -0
- package/dist/Hooks/useSound.js.map +1 -0
- package/dist/Hooks/useWebSocket.d.ts +9 -0
- package/dist/Hooks/useWebSocket.js +120 -0
- package/dist/Hooks/useWebSocket.js.map +1 -0
- package/dist/I18n/I18n.d.ts +1 -1
- package/dist/I18n/I18n.js +9 -5
- package/dist/I18n/I18n.js.map +1 -1
- package/dist/NumberFormat-CvvBWhHc.js.map +1 -1
- package/dist/NumberFormat-glmpbk7E.js.map +1 -1
- package/dist/React-BaJ1KfGF.js.map +1 -1
- package/dist/React-qUl0CBmE.js.map +1 -1
- package/dist/ReactNative-Ckbnh5vm.js +1770 -0
- package/dist/ReactNative-Ckbnh5vm.js.map +1 -0
- package/dist/ReactNative-DLA9Xwp4.js +1792 -0
- package/dist/ReactNative-DLA9Xwp4.js.map +1 -0
- package/dist/Sentry/Build.js +1 -1
- package/dist/Sentry/Build.js.map +1 -1
- package/dist/Sentry/React.js.map +1 -1
- package/dist/Sentry/ReactNative.js +3 -3
- package/dist/Sentry/ReactNative.js.map +1 -1
- package/dist/Utils/Array.d.ts +3 -0
- package/dist/Utils/Array.js +26 -0
- package/dist/Utils/Array.js.map +1 -0
- package/dist/Utils/Files.js +2 -2
- package/dist/Utils/Files.js.map +1 -1
- package/dist/Utils/Pagination.d.ts +3 -0
- package/dist/Utils/Pagination.js +32 -0
- package/dist/Utils/Pagination.js.map +1 -0
- package/dist/Utils/Utils.d.ts +10 -0
- package/dist/Utils/Utils.js +98 -0
- package/dist/Utils/Utils.js.map +1 -0
- package/dist/Utils-Bnk2KHAB.js +70 -0
- package/dist/Utils-Bnk2KHAB.js.map +1 -0
- package/dist/Utils-Cq948gfa.js.map +1 -1
- package/dist/Utils-DLJ3-s9J.js +61 -0
- package/dist/Utils-DLJ3-s9J.js.map +1 -0
- package/dist/Utils-Dilye04y.js.map +1 -1
- package/dist/config-cli.cjs +2 -3
- package/dist/config-cli.cjs.map +1 -1
- package/dist/config-cli.js +3 -3
- package/dist/config-cli.js.map +1 -1
- package/dist/config-cli.mjs +2 -2
- package/dist/config-cli.mjs.map +1 -1
- package/dist/enums/CurrencySymbolEnum.js +4 -2
- package/dist/enums/CurrencySymbolEnum.js.map +1 -1
- package/dist/enums/DeviceEnum.d.ts +4 -0
- package/dist/enums/DeviceEnum.js +12 -0
- package/dist/enums/DeviceEnum.js.map +1 -0
- package/dist/hooks.cjs +13 -0
- package/dist/hooks.cjs.map +1 -0
- package/dist/hooks.d.ts +9 -0
- package/dist/hooks.js +87 -0
- package/dist/hooks.js.map +1 -0
- package/dist/hooks.mjs +4 -0
- package/dist/hooks.mjs.map +1 -0
- package/dist/i18n.cjs +3104 -6
- package/dist/i18n.cjs.map +1 -1
- package/dist/i18n.mjs +3104 -2
- package/dist/i18n.mjs.map +1 -1
- package/dist/index-cli.cjs +1 -1
- package/dist/index-cli.mjs +1 -1
- package/dist/index-rn.cjs +5 -5
- package/dist/index-rn.mjs +3 -3
- package/dist/index.cjs +25 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6 -2
- package/dist/index.mjs.map +1 -1
- package/dist/sentry-cli.cjs.map +1 -1
- package/dist/sentry-cli.js +1 -1
- package/dist/sentry-cli.js.map +1 -1
- package/dist/sentry-cli.mjs.map +1 -1
- package/dist/sentry-rn.cjs +1 -1
- package/dist/sentry-rn.mjs +1 -1
- package/dist/useWebSocket-GlUpioz3.js +168 -0
- package/dist/useWebSocket-GlUpioz3.js.map +1 -0
- package/dist/useWebSocket-vgu8TAsa.js +163 -0
- package/dist/useWebSocket-vgu8TAsa.js.map +1 -0
- package/dist/utils.cjs +58 -0
- package/dist/utils.cjs.map +1 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +39 -0
- package/dist/utils.js.map +1 -0
- package/dist/utils.mjs +43 -0
- package/dist/utils.mjs.map +1 -0
- package/package.json +51 -40
- package/src/Config/Config.ts +2 -2
- package/src/Hooks/useDevice.ts +34 -0
- package/src/Hooks/useSEStore.tsx +29 -0
- package/src/Hooks/useSound.ts +44 -0
- package/src/Hooks/useWebSocket.ts +115 -0
- package/src/I18n/I18n.ts +1 -4
- package/src/Sentry/ReactNative.ts +2 -5
- package/src/Utils/Array.ts +23 -0
- package/src/Utils/Pagination.ts +42 -0
- package/src/Utils/Utils.ts +75 -0
- package/src/enums/DeviceEnum.ts +4 -0
- package/src/hooks.ts +11 -0
- package/src/index.ts +2 -0
- package/src/utils.ts +3 -0
- package/dist/I18n-BIBLVzaQ.js +0 -3313
- package/dist/I18n-BIBLVzaQ.js.map +0 -1
- package/dist/I18n-tdxuTc45.js +0 -3316
- package/dist/I18n-tdxuTc45.js.map +0 -1
- package/dist/ReactNative-CqUrY2ZJ.js +0 -3856
- package/dist/ReactNative-CqUrY2ZJ.js.map +0 -1
- package/dist/ReactNative-mNnws-b5.js +0 -3834
- package/dist/ReactNative-mNnws-b5.js.map +0 -1
@@ -0,0 +1,1770 @@
|
|
1
|
+
import * as Sentry from '@sentry/react-native';
|
2
|
+
import { r as recordSentryHttp } from './Utils-Cq948gfa.js';
|
3
|
+
|
4
|
+
// This is a magic string replaced by rollup
|
5
|
+
|
6
|
+
const SDK_VERSION = "8.54.0" ;
|
7
|
+
|
8
|
+
/** Get's the global object for the current JavaScript runtime */
|
9
|
+
const GLOBAL_OBJ = globalThis ;
|
10
|
+
|
11
|
+
/**
|
12
|
+
* Returns a global singleton contained in the global `__SENTRY__[]` object.
|
13
|
+
*
|
14
|
+
* If the singleton doesn't already exist in `__SENTRY__`, it will be created using the given factory
|
15
|
+
* function and added to the `__SENTRY__` object.
|
16
|
+
*
|
17
|
+
* @param name name of the global singleton on __SENTRY__
|
18
|
+
* @param creator creator Factory function to create the singleton if it doesn't already exist on `__SENTRY__`
|
19
|
+
* @param obj (Optional) The global object on which to look for `__SENTRY__`, if not `GLOBAL_OBJ`'s return value
|
20
|
+
* @returns the singleton
|
21
|
+
*/
|
22
|
+
function getGlobalSingleton(name, creator, obj) {
|
23
|
+
const gbl = (obj || GLOBAL_OBJ) ;
|
24
|
+
const __SENTRY__ = (gbl.__SENTRY__ = gbl.__SENTRY__ || {});
|
25
|
+
const versionedCarrier = (__SENTRY__[SDK_VERSION] = __SENTRY__[SDK_VERSION] || {});
|
26
|
+
return versionedCarrier[name] || (versionedCarrier[name] = creator());
|
27
|
+
}
|
28
|
+
|
29
|
+
/**
|
30
|
+
* This serves as a build time flag that will be true by default, but false in non-debug builds or if users replace `__SENTRY_DEBUG__` in their generated code.
|
31
|
+
*
|
32
|
+
* ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking.
|
33
|
+
*/
|
34
|
+
const DEBUG_BUILD = (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__);
|
35
|
+
|
36
|
+
/** Prefix for logging strings */
|
37
|
+
const PREFIX = 'Sentry Logger ';
|
38
|
+
|
39
|
+
const CONSOLE_LEVELS = [
|
40
|
+
'debug',
|
41
|
+
'info',
|
42
|
+
'warn',
|
43
|
+
'error',
|
44
|
+
'log',
|
45
|
+
'assert',
|
46
|
+
'trace',
|
47
|
+
] ;
|
48
|
+
|
49
|
+
/** This may be mutated by the console instrumentation. */
|
50
|
+
const originalConsoleMethods
|
51
|
+
|
52
|
+
= {};
|
53
|
+
|
54
|
+
/** JSDoc */
|
55
|
+
|
56
|
+
/**
|
57
|
+
* Temporarily disable sentry console instrumentations.
|
58
|
+
*
|
59
|
+
* @param callback The function to run against the original `console` messages
|
60
|
+
* @returns The results of the callback
|
61
|
+
*/
|
62
|
+
function consoleSandbox(callback) {
|
63
|
+
if (!('console' in GLOBAL_OBJ)) {
|
64
|
+
return callback();
|
65
|
+
}
|
66
|
+
|
67
|
+
const console = GLOBAL_OBJ.console ;
|
68
|
+
const wrappedFuncs = {};
|
69
|
+
|
70
|
+
const wrappedLevels = Object.keys(originalConsoleMethods) ;
|
71
|
+
|
72
|
+
// Restore all wrapped console methods
|
73
|
+
wrappedLevels.forEach(level => {
|
74
|
+
const originalConsoleMethod = originalConsoleMethods[level] ;
|
75
|
+
wrappedFuncs[level] = console[level] ;
|
76
|
+
console[level] = originalConsoleMethod;
|
77
|
+
});
|
78
|
+
|
79
|
+
try {
|
80
|
+
return callback();
|
81
|
+
} finally {
|
82
|
+
// Revert restoration to wrapped state
|
83
|
+
wrappedLevels.forEach(level => {
|
84
|
+
console[level] = wrappedFuncs[level] ;
|
85
|
+
});
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
function makeLogger() {
|
90
|
+
let enabled = false;
|
91
|
+
const logger = {
|
92
|
+
enable: () => {
|
93
|
+
enabled = true;
|
94
|
+
},
|
95
|
+
disable: () => {
|
96
|
+
enabled = false;
|
97
|
+
},
|
98
|
+
isEnabled: () => enabled,
|
99
|
+
};
|
100
|
+
|
101
|
+
if (DEBUG_BUILD) {
|
102
|
+
CONSOLE_LEVELS.forEach(name => {
|
103
|
+
logger[name] = (...args) => {
|
104
|
+
if (enabled) {
|
105
|
+
consoleSandbox(() => {
|
106
|
+
GLOBAL_OBJ.console[name](`${PREFIX}[${name}]:`, ...args);
|
107
|
+
});
|
108
|
+
}
|
109
|
+
};
|
110
|
+
});
|
111
|
+
} else {
|
112
|
+
CONSOLE_LEVELS.forEach(name => {
|
113
|
+
logger[name] = () => undefined;
|
114
|
+
});
|
115
|
+
}
|
116
|
+
|
117
|
+
return logger ;
|
118
|
+
}
|
119
|
+
|
120
|
+
/**
|
121
|
+
* This is a logger singleton which either logs things or no-ops if logging is not enabled.
|
122
|
+
* The logger is a singleton on the carrier, to ensure that a consistent logger is used throughout the SDK.
|
123
|
+
*/
|
124
|
+
const logger = getGlobalSingleton('logger', makeLogger);
|
125
|
+
|
126
|
+
const defaultFunctionName = '<anonymous>';
|
127
|
+
|
128
|
+
/**
|
129
|
+
* Safely extract function name from itself
|
130
|
+
*/
|
131
|
+
function getFunctionName(fn) {
|
132
|
+
try {
|
133
|
+
if (!fn || typeof fn !== 'function') {
|
134
|
+
return defaultFunctionName;
|
135
|
+
}
|
136
|
+
return fn.name || defaultFunctionName;
|
137
|
+
} catch (e) {
|
138
|
+
// Just accessing custom props in some Selenium environments
|
139
|
+
// can cause a "Permission denied" exception (see raven-js#495).
|
140
|
+
return defaultFunctionName;
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
// We keep the handlers globally
|
145
|
+
const handlers = {};
|
146
|
+
const instrumented = {};
|
147
|
+
|
148
|
+
/** Add a handler function. */
|
149
|
+
function addHandler(type, handler) {
|
150
|
+
handlers[type] = handlers[type] || [];
|
151
|
+
(handlers[type] ).push(handler);
|
152
|
+
}
|
153
|
+
|
154
|
+
/** Maybe run an instrumentation function, unless it was already called. */
|
155
|
+
function maybeInstrument(type, instrumentFn) {
|
156
|
+
if (!instrumented[type]) {
|
157
|
+
instrumented[type] = true;
|
158
|
+
try {
|
159
|
+
instrumentFn();
|
160
|
+
} catch (e) {
|
161
|
+
DEBUG_BUILD && logger.error(`Error while instrumenting ${type}`, e);
|
162
|
+
}
|
163
|
+
}
|
164
|
+
}
|
165
|
+
|
166
|
+
/** Trigger handlers for a given instrumentation type. */
|
167
|
+
function triggerHandlers(type, data) {
|
168
|
+
const typeHandlers = handlers[type];
|
169
|
+
if (!typeHandlers) {
|
170
|
+
return;
|
171
|
+
}
|
172
|
+
|
173
|
+
for (const handler of typeHandlers) {
|
174
|
+
try {
|
175
|
+
handler(data);
|
176
|
+
} catch (e) {
|
177
|
+
DEBUG_BUILD &&
|
178
|
+
logger.error(
|
179
|
+
`Error while triggering instrumentation handler.\nType: ${type}\nName: ${getFunctionName(handler)}\nError:`,
|
180
|
+
e,
|
181
|
+
);
|
182
|
+
}
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
/**
|
187
|
+
* An object that contains globally accessible properties and maintains a scope stack.
|
188
|
+
* @hidden
|
189
|
+
*/
|
190
|
+
|
191
|
+
/**
|
192
|
+
* Returns the global shim registry.
|
193
|
+
*
|
194
|
+
* FIXME: This function is problematic, because despite always returning a valid Carrier,
|
195
|
+
* it has an optional `__SENTRY__` property, which then in turn requires us to always perform an unnecessary check
|
196
|
+
* at the call-site. We always access the carrier through this function, so we can guarantee that `__SENTRY__` is there.
|
197
|
+
**/
|
198
|
+
function getMainCarrier() {
|
199
|
+
// This ensures a Sentry carrier exists
|
200
|
+
getSentryCarrier(GLOBAL_OBJ);
|
201
|
+
return GLOBAL_OBJ;
|
202
|
+
}
|
203
|
+
|
204
|
+
/** Will either get the existing sentry carrier, or create a new one. */
|
205
|
+
function getSentryCarrier(carrier) {
|
206
|
+
const __SENTRY__ = (carrier.__SENTRY__ = carrier.__SENTRY__ || {});
|
207
|
+
|
208
|
+
// For now: First SDK that sets the .version property wins
|
209
|
+
__SENTRY__.version = __SENTRY__.version || SDK_VERSION;
|
210
|
+
|
211
|
+
// Intentionally populating and returning the version of "this" SDK instance
|
212
|
+
// rather than what's set in .version so that "this" SDK always gets its carrier
|
213
|
+
return (__SENTRY__[SDK_VERSION] = __SENTRY__[SDK_VERSION] || {});
|
214
|
+
}
|
215
|
+
|
216
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
217
|
+
const objectToString = Object.prototype.toString;
|
218
|
+
/**
|
219
|
+
* Checks whether given value is an instance of the given built-in class.
|
220
|
+
*
|
221
|
+
* @param wat The value to be checked
|
222
|
+
* @param className
|
223
|
+
* @returns A boolean representing the result.
|
224
|
+
*/
|
225
|
+
function isBuiltin(wat, className) {
|
226
|
+
return objectToString.call(wat) === `[object ${className}]`;
|
227
|
+
}
|
228
|
+
|
229
|
+
/**
|
230
|
+
* Checks whether given value's type is an object literal, or a class instance.
|
231
|
+
* {@link isPlainObject}.
|
232
|
+
*
|
233
|
+
* @param wat A value to be checked.
|
234
|
+
* @returns A boolean representing the result.
|
235
|
+
*/
|
236
|
+
function isPlainObject(wat) {
|
237
|
+
return isBuiltin(wat, 'Object');
|
238
|
+
}
|
239
|
+
|
240
|
+
/**
|
241
|
+
* Checks whether given value has a then function.
|
242
|
+
* @param wat A value to be checked.
|
243
|
+
*/
|
244
|
+
function isThenable(wat) {
|
245
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
246
|
+
return Boolean(wat && wat.then && typeof wat.then === 'function');
|
247
|
+
}
|
248
|
+
|
249
|
+
/**
|
250
|
+
* Checks whether given value's type is a Vue ViewModel.
|
251
|
+
*
|
252
|
+
* @param wat A value to be checked.
|
253
|
+
* @returns A boolean representing the result.
|
254
|
+
*/
|
255
|
+
function isVueViewModel(wat) {
|
256
|
+
// Not using Object.prototype.toString because in Vue 3 it would read the instance's Symbol(Symbol.toStringTag) property.
|
257
|
+
return !!(typeof wat === 'object' && wat !== null && ((wat ).__isVue || (wat )._isVue));
|
258
|
+
}
|
259
|
+
|
260
|
+
/**
|
261
|
+
* Join values in array
|
262
|
+
* @param input array of values to be joined together
|
263
|
+
* @param delimiter string to be placed in-between values
|
264
|
+
* @returns Joined values
|
265
|
+
*/
|
266
|
+
function safeJoin(input, delimiter) {
|
267
|
+
if (!Array.isArray(input)) {
|
268
|
+
return '';
|
269
|
+
}
|
270
|
+
|
271
|
+
const output = [];
|
272
|
+
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
273
|
+
for (let i = 0; i < input.length; i++) {
|
274
|
+
const value = input[i];
|
275
|
+
try {
|
276
|
+
// This is a hack to fix a Vue3-specific bug that causes an infinite loop of
|
277
|
+
// console warnings. This happens when a Vue template is rendered with
|
278
|
+
// an undeclared variable, which we try to stringify, ultimately causing
|
279
|
+
// Vue to issue another warning which repeats indefinitely.
|
280
|
+
// see: https://github.com/getsentry/sentry-javascript/pull/8981
|
281
|
+
if (isVueViewModel(value)) {
|
282
|
+
output.push('[VueViewModel]');
|
283
|
+
} else {
|
284
|
+
output.push(String(value));
|
285
|
+
}
|
286
|
+
} catch (e) {
|
287
|
+
output.push('[value cannot be serialized]');
|
288
|
+
}
|
289
|
+
}
|
290
|
+
|
291
|
+
return output.join(delimiter);
|
292
|
+
}
|
293
|
+
|
294
|
+
/**
|
295
|
+
* Replace a method in an object with a wrapped version of itself.
|
296
|
+
*
|
297
|
+
* @param source An object that contains a method to be wrapped.
|
298
|
+
* @param name The name of the method to be wrapped.
|
299
|
+
* @param replacementFactory A higher-order function that takes the original version of the given method and returns a
|
300
|
+
* wrapped version. Note: The function returned by `replacementFactory` needs to be a non-arrow function, in order to
|
301
|
+
* preserve the correct value of `this`, and the original method must be called using `origMethod.call(this, <other
|
302
|
+
* args>)` or `origMethod.apply(this, [<other args>])` (rather than being called directly), again to preserve `this`.
|
303
|
+
* @returns void
|
304
|
+
*/
|
305
|
+
function fill(source, name, replacementFactory) {
|
306
|
+
if (!(name in source)) {
|
307
|
+
return;
|
308
|
+
}
|
309
|
+
|
310
|
+
const original = source[name] ;
|
311
|
+
const wrapped = replacementFactory(original) ;
|
312
|
+
|
313
|
+
// Make sure it's a function first, as we need to attach an empty prototype for `defineProperties` to work
|
314
|
+
// otherwise it'll throw "TypeError: Object.defineProperties called on non-object"
|
315
|
+
if (typeof wrapped === 'function') {
|
316
|
+
markFunctionWrapped(wrapped, original);
|
317
|
+
}
|
318
|
+
|
319
|
+
try {
|
320
|
+
source[name] = wrapped;
|
321
|
+
} catch (e) {
|
322
|
+
DEBUG_BUILD && logger.log(`Failed to replace method "${name}" in object`, source);
|
323
|
+
}
|
324
|
+
}
|
325
|
+
|
326
|
+
/**
|
327
|
+
* Defines a non-enumerable property on the given object.
|
328
|
+
*
|
329
|
+
* @param obj The object on which to set the property
|
330
|
+
* @param name The name of the property to be set
|
331
|
+
* @param value The value to which to set the property
|
332
|
+
*/
|
333
|
+
function addNonEnumerableProperty(obj, name, value) {
|
334
|
+
try {
|
335
|
+
Object.defineProperty(obj, name, {
|
336
|
+
// enumerable: false, // the default, so we can save on bundle size by not explicitly setting it
|
337
|
+
value: value,
|
338
|
+
writable: true,
|
339
|
+
configurable: true,
|
340
|
+
});
|
341
|
+
} catch (o_O) {
|
342
|
+
DEBUG_BUILD && logger.log(`Failed to add non-enumerable property "${name}" to object`, obj);
|
343
|
+
}
|
344
|
+
}
|
345
|
+
|
346
|
+
/**
|
347
|
+
* Remembers the original function on the wrapped function and
|
348
|
+
* patches up the prototype.
|
349
|
+
*
|
350
|
+
* @param wrapped the wrapper function
|
351
|
+
* @param original the original function that gets wrapped
|
352
|
+
*/
|
353
|
+
function markFunctionWrapped(wrapped, original) {
|
354
|
+
try {
|
355
|
+
const proto = original.prototype || {};
|
356
|
+
wrapped.prototype = original.prototype = proto;
|
357
|
+
addNonEnumerableProperty(wrapped, '__sentry_original__', original);
|
358
|
+
} catch (o_O) {} // eslint-disable-line no-empty
|
359
|
+
}
|
360
|
+
|
361
|
+
const ONE_SECOND_IN_MS = 1000;
|
362
|
+
|
363
|
+
/**
|
364
|
+
* A partial definition of the [Performance Web API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Performance}
|
365
|
+
* for accessing a high-resolution monotonic clock.
|
366
|
+
*/
|
367
|
+
|
368
|
+
/**
|
369
|
+
* Returns a timestamp in seconds since the UNIX epoch using the Date API.
|
370
|
+
*
|
371
|
+
* TODO(v8): Return type should be rounded.
|
372
|
+
*/
|
373
|
+
function dateTimestampInSeconds() {
|
374
|
+
return Date.now() / ONE_SECOND_IN_MS;
|
375
|
+
}
|
376
|
+
|
377
|
+
/**
|
378
|
+
* Returns a wrapper around the native Performance API browser implementation, or undefined for browsers that do not
|
379
|
+
* support the API.
|
380
|
+
*
|
381
|
+
* Wrapping the native API works around differences in behavior from different browsers.
|
382
|
+
*/
|
383
|
+
function createUnixTimestampInSecondsFunc() {
|
384
|
+
const { performance } = GLOBAL_OBJ ;
|
385
|
+
if (!performance || !performance.now) {
|
386
|
+
return dateTimestampInSeconds;
|
387
|
+
}
|
388
|
+
|
389
|
+
// Some browser and environments don't have a timeOrigin, so we fallback to
|
390
|
+
// using Date.now() to compute the starting time.
|
391
|
+
const approxStartingTimeOrigin = Date.now() - performance.now();
|
392
|
+
const timeOrigin = performance.timeOrigin == undefined ? approxStartingTimeOrigin : performance.timeOrigin;
|
393
|
+
|
394
|
+
// performance.now() is a monotonic clock, which means it starts at 0 when the process begins. To get the current
|
395
|
+
// wall clock time (actual UNIX timestamp), we need to add the starting time origin and the current time elapsed.
|
396
|
+
//
|
397
|
+
// TODO: This does not account for the case where the monotonic clock that powers performance.now() drifts from the
|
398
|
+
// wall clock time, which causes the returned timestamp to be inaccurate. We should investigate how to detect and
|
399
|
+
// correct for this.
|
400
|
+
// See: https://github.com/getsentry/sentry-javascript/issues/2590
|
401
|
+
// See: https://github.com/mdn/content/issues/4713
|
402
|
+
// See: https://dev.to/noamr/when-a-millisecond-is-not-a-millisecond-3h6
|
403
|
+
return () => {
|
404
|
+
return (timeOrigin + performance.now()) / ONE_SECOND_IN_MS;
|
405
|
+
};
|
406
|
+
}
|
407
|
+
|
408
|
+
/**
|
409
|
+
* Returns a timestamp in seconds since the UNIX epoch using either the Performance or Date APIs, depending on the
|
410
|
+
* availability of the Performance API.
|
411
|
+
*
|
412
|
+
* BUG: Note that because of how browsers implement the Performance API, the clock might stop when the computer is
|
413
|
+
* asleep. This creates a skew between `dateTimestampInSeconds` and `timestampInSeconds`. The
|
414
|
+
* skew can grow to arbitrary amounts like days, weeks or months.
|
415
|
+
* See https://github.com/getsentry/sentry-javascript/issues/2590.
|
416
|
+
*/
|
417
|
+
const timestampInSeconds = createUnixTimestampInSecondsFunc();
|
418
|
+
|
419
|
+
/**
|
420
|
+
* The number of milliseconds since the UNIX epoch. This value is only usable in a browser, and only when the
|
421
|
+
* performance API is available.
|
422
|
+
*/
|
423
|
+
(() => {
|
424
|
+
// Unfortunately browsers may report an inaccurate time origin data, through either performance.timeOrigin or
|
425
|
+
// performance.timing.navigationStart, which results in poor results in performance data. We only treat time origin
|
426
|
+
// data as reliable if they are within a reasonable threshold of the current time.
|
427
|
+
|
428
|
+
const { performance } = GLOBAL_OBJ ;
|
429
|
+
if (!performance || !performance.now) {
|
430
|
+
return undefined;
|
431
|
+
}
|
432
|
+
|
433
|
+
const threshold = 3600 * 1000;
|
434
|
+
const performanceNow = performance.now();
|
435
|
+
const dateNow = Date.now();
|
436
|
+
|
437
|
+
// if timeOrigin isn't available set delta to threshold so it isn't used
|
438
|
+
const timeOriginDelta = performance.timeOrigin
|
439
|
+
? Math.abs(performance.timeOrigin + performanceNow - dateNow)
|
440
|
+
: threshold;
|
441
|
+
const timeOriginIsReliable = timeOriginDelta < threshold;
|
442
|
+
|
443
|
+
// While performance.timing.navigationStart is deprecated in favor of performance.timeOrigin, performance.timeOrigin
|
444
|
+
// is not as widely supported. Namely, performance.timeOrigin is undefined in Safari as of writing.
|
445
|
+
// Also as of writing, performance.timing is not available in Web Workers in mainstream browsers, so it is not always
|
446
|
+
// a valid fallback. In the absence of an initial time provided by the browser, fallback to the current time from the
|
447
|
+
// Date API.
|
448
|
+
// eslint-disable-next-line deprecation/deprecation
|
449
|
+
const navigationStart = performance.timing && performance.timing.navigationStart;
|
450
|
+
const hasNavigationStart = typeof navigationStart === 'number';
|
451
|
+
// if navigationStart isn't available set delta to threshold so it isn't used
|
452
|
+
const navigationStartDelta = hasNavigationStart ? Math.abs(navigationStart + performanceNow - dateNow) : threshold;
|
453
|
+
const navigationStartIsReliable = navigationStartDelta < threshold;
|
454
|
+
|
455
|
+
if (timeOriginIsReliable || navigationStartIsReliable) {
|
456
|
+
// Use the more reliable time origin
|
457
|
+
if (timeOriginDelta <= navigationStartDelta) {
|
458
|
+
return performance.timeOrigin;
|
459
|
+
} else {
|
460
|
+
return navigationStart;
|
461
|
+
}
|
462
|
+
}
|
463
|
+
return dateNow;
|
464
|
+
})();
|
465
|
+
|
466
|
+
/**
|
467
|
+
* UUID4 generator
|
468
|
+
*
|
469
|
+
* @returns string Generated UUID4.
|
470
|
+
*/
|
471
|
+
function uuid4() {
|
472
|
+
const gbl = GLOBAL_OBJ ;
|
473
|
+
const crypto = gbl.crypto || gbl.msCrypto;
|
474
|
+
|
475
|
+
let getRandomByte = () => Math.random() * 16;
|
476
|
+
try {
|
477
|
+
if (crypto && crypto.randomUUID) {
|
478
|
+
return crypto.randomUUID().replace(/-/g, '');
|
479
|
+
}
|
480
|
+
if (crypto && crypto.getRandomValues) {
|
481
|
+
getRandomByte = () => {
|
482
|
+
// crypto.getRandomValues might return undefined instead of the typed array
|
483
|
+
// in old Chromium versions (e.g. 23.0.1235.0 (151422))
|
484
|
+
// However, `typedArray` is still filled in-place.
|
485
|
+
// @see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#typedarray
|
486
|
+
const typedArray = new Uint8Array(1);
|
487
|
+
crypto.getRandomValues(typedArray);
|
488
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
489
|
+
return typedArray[0];
|
490
|
+
};
|
491
|
+
}
|
492
|
+
} catch (_) {
|
493
|
+
// some runtimes can crash invoking crypto
|
494
|
+
// https://github.com/getsentry/sentry-javascript/issues/8935
|
495
|
+
}
|
496
|
+
|
497
|
+
// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
|
498
|
+
// Concatenating the following numbers as strings results in '10000000100040008000100000000000'
|
499
|
+
return (([1e7] ) + 1e3 + 4e3 + 8e3 + 1e11).replace(/[018]/g, c =>
|
500
|
+
// eslint-disable-next-line no-bitwise
|
501
|
+
((c ) ^ ((getRandomByte() & 15) >> ((c ) / 4))).toString(16),
|
502
|
+
);
|
503
|
+
}
|
504
|
+
|
505
|
+
function getFirstException(event) {
|
506
|
+
return event.exception && event.exception.values ? event.exception.values[0] : undefined;
|
507
|
+
}
|
508
|
+
|
509
|
+
/**
|
510
|
+
* Adds exception mechanism data to a given event. Uses defaults if the second parameter is not passed.
|
511
|
+
*
|
512
|
+
* @param event The event to modify.
|
513
|
+
* @param newMechanism Mechanism data to add to the event.
|
514
|
+
* @hidden
|
515
|
+
*/
|
516
|
+
function addExceptionMechanism(event, newMechanism) {
|
517
|
+
const firstException = getFirstException(event);
|
518
|
+
if (!firstException) {
|
519
|
+
return;
|
520
|
+
}
|
521
|
+
|
522
|
+
const defaultMechanism = { type: 'generic', handled: true };
|
523
|
+
const currentMechanism = firstException.mechanism;
|
524
|
+
firstException.mechanism = { ...defaultMechanism, ...currentMechanism, ...newMechanism };
|
525
|
+
|
526
|
+
if (newMechanism && 'data' in newMechanism) {
|
527
|
+
const mergedData = { ...(currentMechanism && currentMechanism.data), ...newMechanism.data };
|
528
|
+
firstException.mechanism.data = mergedData;
|
529
|
+
}
|
530
|
+
}
|
531
|
+
|
532
|
+
/**
|
533
|
+
* Updates a session object with the properties passed in the context.
|
534
|
+
*
|
535
|
+
* Note that this function mutates the passed object and returns void.
|
536
|
+
* (Had to do this instead of returning a new and updated session because closing and sending a session
|
537
|
+
* makes an update to the session after it was passed to the sending logic.
|
538
|
+
* @see BaseClient.captureSession )
|
539
|
+
*
|
540
|
+
* @param session the `Session` to update
|
541
|
+
* @param context the `SessionContext` holding the properties that should be updated in @param session
|
542
|
+
*/
|
543
|
+
// eslint-disable-next-line complexity
|
544
|
+
function updateSession(session, context = {}) {
|
545
|
+
if (context.user) {
|
546
|
+
if (!session.ipAddress && context.user.ip_address) {
|
547
|
+
session.ipAddress = context.user.ip_address;
|
548
|
+
}
|
549
|
+
|
550
|
+
if (!session.did && !context.did) {
|
551
|
+
session.did = context.user.id || context.user.email || context.user.username;
|
552
|
+
}
|
553
|
+
}
|
554
|
+
|
555
|
+
session.timestamp = context.timestamp || timestampInSeconds();
|
556
|
+
|
557
|
+
if (context.abnormal_mechanism) {
|
558
|
+
session.abnormal_mechanism = context.abnormal_mechanism;
|
559
|
+
}
|
560
|
+
|
561
|
+
if (context.ignoreDuration) {
|
562
|
+
session.ignoreDuration = context.ignoreDuration;
|
563
|
+
}
|
564
|
+
if (context.sid) {
|
565
|
+
// Good enough uuid validation. — Kamil
|
566
|
+
session.sid = context.sid.length === 32 ? context.sid : uuid4();
|
567
|
+
}
|
568
|
+
if (context.init !== undefined) {
|
569
|
+
session.init = context.init;
|
570
|
+
}
|
571
|
+
if (!session.did && context.did) {
|
572
|
+
session.did = `${context.did}`;
|
573
|
+
}
|
574
|
+
if (typeof context.started === 'number') {
|
575
|
+
session.started = context.started;
|
576
|
+
}
|
577
|
+
if (session.ignoreDuration) {
|
578
|
+
session.duration = undefined;
|
579
|
+
} else if (typeof context.duration === 'number') {
|
580
|
+
session.duration = context.duration;
|
581
|
+
} else {
|
582
|
+
const duration = session.timestamp - session.started;
|
583
|
+
session.duration = duration >= 0 ? duration : 0;
|
584
|
+
}
|
585
|
+
if (context.release) {
|
586
|
+
session.release = context.release;
|
587
|
+
}
|
588
|
+
if (context.environment) {
|
589
|
+
session.environment = context.environment;
|
590
|
+
}
|
591
|
+
if (!session.ipAddress && context.ipAddress) {
|
592
|
+
session.ipAddress = context.ipAddress;
|
593
|
+
}
|
594
|
+
if (!session.userAgent && context.userAgent) {
|
595
|
+
session.userAgent = context.userAgent;
|
596
|
+
}
|
597
|
+
if (typeof context.errors === 'number') {
|
598
|
+
session.errors = context.errors;
|
599
|
+
}
|
600
|
+
if (context.status) {
|
601
|
+
session.status = context.status;
|
602
|
+
}
|
603
|
+
}
|
604
|
+
|
605
|
+
/**
|
606
|
+
* Generate a random, valid trace ID.
|
607
|
+
*/
|
608
|
+
function generateTraceId() {
|
609
|
+
return uuid4();
|
610
|
+
}
|
611
|
+
|
612
|
+
/**
|
613
|
+
* Generate a random, valid span ID.
|
614
|
+
*/
|
615
|
+
function generateSpanId() {
|
616
|
+
return uuid4().substring(16);
|
617
|
+
}
|
618
|
+
|
619
|
+
/**
|
620
|
+
* Shallow merge two objects.
|
621
|
+
* Does not mutate the passed in objects.
|
622
|
+
* Undefined/empty values in the merge object will overwrite existing values.
|
623
|
+
*
|
624
|
+
* By default, this merges 2 levels deep.
|
625
|
+
*/
|
626
|
+
function merge(initialObj, mergeObj, levels = 2) {
|
627
|
+
// If the merge value is not an object, or we have no merge levels left,
|
628
|
+
// we just set the value to the merge value
|
629
|
+
if (!mergeObj || typeof mergeObj !== 'object' || levels <= 0) {
|
630
|
+
return mergeObj;
|
631
|
+
}
|
632
|
+
|
633
|
+
// If the merge object is an empty object, and the initial object is not undefined, we return the initial object
|
634
|
+
if (initialObj && mergeObj && Object.keys(mergeObj).length === 0) {
|
635
|
+
return initialObj;
|
636
|
+
}
|
637
|
+
|
638
|
+
// Clone object
|
639
|
+
const output = { ...initialObj };
|
640
|
+
|
641
|
+
// Merge values into output, resursively
|
642
|
+
for (const key in mergeObj) {
|
643
|
+
if (Object.prototype.hasOwnProperty.call(mergeObj, key)) {
|
644
|
+
output[key] = merge(output[key], mergeObj[key], levels - 1);
|
645
|
+
}
|
646
|
+
}
|
647
|
+
|
648
|
+
return output;
|
649
|
+
}
|
650
|
+
|
651
|
+
const SCOPE_SPAN_FIELD = '_sentrySpan';
|
652
|
+
|
653
|
+
/**
|
654
|
+
* Set the active span for a given scope.
|
655
|
+
* NOTE: This should NOT be used directly, but is only used internally by the trace methods.
|
656
|
+
*/
|
657
|
+
function _setSpanForScope(scope, span) {
|
658
|
+
if (span) {
|
659
|
+
addNonEnumerableProperty(scope , SCOPE_SPAN_FIELD, span);
|
660
|
+
} else {
|
661
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
662
|
+
delete (scope )[SCOPE_SPAN_FIELD];
|
663
|
+
}
|
664
|
+
}
|
665
|
+
|
666
|
+
/**
|
667
|
+
* Get the active span for a given scope.
|
668
|
+
* NOTE: This should NOT be used directly, but is only used internally by the trace methods.
|
669
|
+
*/
|
670
|
+
function _getSpanForScope(scope) {
|
671
|
+
return scope[SCOPE_SPAN_FIELD];
|
672
|
+
}
|
673
|
+
|
674
|
+
/**
|
675
|
+
* Default value for maximum number of breadcrumbs added to an event.
|
676
|
+
*/
|
677
|
+
const DEFAULT_MAX_BREADCRUMBS = 100;
|
678
|
+
|
679
|
+
/**
|
680
|
+
* Holds additional event information.
|
681
|
+
*/
|
682
|
+
class ScopeClass {
|
683
|
+
/** Flag if notifying is happening. */
|
684
|
+
|
685
|
+
/** Callback for client to receive scope changes. */
|
686
|
+
|
687
|
+
/** Callback list that will be called during event processing. */
|
688
|
+
|
689
|
+
/** Array of breadcrumbs. */
|
690
|
+
|
691
|
+
/** User */
|
692
|
+
|
693
|
+
/** Tags */
|
694
|
+
|
695
|
+
/** Extra */
|
696
|
+
|
697
|
+
/** Contexts */
|
698
|
+
|
699
|
+
/** Attachments */
|
700
|
+
|
701
|
+
/** Propagation Context for distributed tracing */
|
702
|
+
|
703
|
+
/**
|
704
|
+
* A place to stash data which is needed at some point in the SDK's event processing pipeline but which shouldn't get
|
705
|
+
* sent to Sentry
|
706
|
+
*/
|
707
|
+
|
708
|
+
/** Fingerprint */
|
709
|
+
|
710
|
+
/** Severity */
|
711
|
+
|
712
|
+
/**
|
713
|
+
* Transaction Name
|
714
|
+
*
|
715
|
+
* IMPORTANT: The transaction name on the scope has nothing to do with root spans/transaction objects.
|
716
|
+
* It's purpose is to assign a transaction to the scope that's added to non-transaction events.
|
717
|
+
*/
|
718
|
+
|
719
|
+
/** Session */
|
720
|
+
|
721
|
+
/** Request Mode Session Status */
|
722
|
+
// eslint-disable-next-line deprecation/deprecation
|
723
|
+
|
724
|
+
/** The client on this scope */
|
725
|
+
|
726
|
+
/** Contains the last event id of a captured event. */
|
727
|
+
|
728
|
+
// NOTE: Any field which gets added here should get added not only to the constructor but also to the `clone` method.
|
729
|
+
|
730
|
+
constructor() {
|
731
|
+
this._notifyingListeners = false;
|
732
|
+
this._scopeListeners = [];
|
733
|
+
this._eventProcessors = [];
|
734
|
+
this._breadcrumbs = [];
|
735
|
+
this._attachments = [];
|
736
|
+
this._user = {};
|
737
|
+
this._tags = {};
|
738
|
+
this._extra = {};
|
739
|
+
this._contexts = {};
|
740
|
+
this._sdkProcessingMetadata = {};
|
741
|
+
this._propagationContext = {
|
742
|
+
traceId: generateTraceId(),
|
743
|
+
spanId: generateSpanId(),
|
744
|
+
};
|
745
|
+
}
|
746
|
+
|
747
|
+
/**
|
748
|
+
* @inheritDoc
|
749
|
+
*/
|
750
|
+
clone() {
|
751
|
+
const newScope = new ScopeClass();
|
752
|
+
newScope._breadcrumbs = [...this._breadcrumbs];
|
753
|
+
newScope._tags = { ...this._tags };
|
754
|
+
newScope._extra = { ...this._extra };
|
755
|
+
newScope._contexts = { ...this._contexts };
|
756
|
+
if (this._contexts.flags) {
|
757
|
+
// We need to copy the `values` array so insertions on a cloned scope
|
758
|
+
// won't affect the original array.
|
759
|
+
newScope._contexts.flags = {
|
760
|
+
values: [...this._contexts.flags.values],
|
761
|
+
};
|
762
|
+
}
|
763
|
+
|
764
|
+
newScope._user = this._user;
|
765
|
+
newScope._level = this._level;
|
766
|
+
newScope._session = this._session;
|
767
|
+
newScope._transactionName = this._transactionName;
|
768
|
+
newScope._fingerprint = this._fingerprint;
|
769
|
+
newScope._eventProcessors = [...this._eventProcessors];
|
770
|
+
newScope._requestSession = this._requestSession;
|
771
|
+
newScope._attachments = [...this._attachments];
|
772
|
+
newScope._sdkProcessingMetadata = { ...this._sdkProcessingMetadata };
|
773
|
+
newScope._propagationContext = { ...this._propagationContext };
|
774
|
+
newScope._client = this._client;
|
775
|
+
newScope._lastEventId = this._lastEventId;
|
776
|
+
|
777
|
+
_setSpanForScope(newScope, _getSpanForScope(this));
|
778
|
+
|
779
|
+
return newScope;
|
780
|
+
}
|
781
|
+
|
782
|
+
/**
|
783
|
+
* @inheritDoc
|
784
|
+
*/
|
785
|
+
setClient(client) {
|
786
|
+
this._client = client;
|
787
|
+
}
|
788
|
+
|
789
|
+
/**
|
790
|
+
* @inheritDoc
|
791
|
+
*/
|
792
|
+
setLastEventId(lastEventId) {
|
793
|
+
this._lastEventId = lastEventId;
|
794
|
+
}
|
795
|
+
|
796
|
+
/**
|
797
|
+
* @inheritDoc
|
798
|
+
*/
|
799
|
+
getClient() {
|
800
|
+
return this._client ;
|
801
|
+
}
|
802
|
+
|
803
|
+
/**
|
804
|
+
* @inheritDoc
|
805
|
+
*/
|
806
|
+
lastEventId() {
|
807
|
+
return this._lastEventId;
|
808
|
+
}
|
809
|
+
|
810
|
+
/**
|
811
|
+
* @inheritDoc
|
812
|
+
*/
|
813
|
+
addScopeListener(callback) {
|
814
|
+
this._scopeListeners.push(callback);
|
815
|
+
}
|
816
|
+
|
817
|
+
/**
|
818
|
+
* @inheritDoc
|
819
|
+
*/
|
820
|
+
addEventProcessor(callback) {
|
821
|
+
this._eventProcessors.push(callback);
|
822
|
+
return this;
|
823
|
+
}
|
824
|
+
|
825
|
+
/**
|
826
|
+
* @inheritDoc
|
827
|
+
*/
|
828
|
+
setUser(user) {
|
829
|
+
// If null is passed we want to unset everything, but still define keys,
|
830
|
+
// so that later down in the pipeline any existing values are cleared.
|
831
|
+
this._user = user || {
|
832
|
+
email: undefined,
|
833
|
+
id: undefined,
|
834
|
+
ip_address: undefined,
|
835
|
+
username: undefined,
|
836
|
+
};
|
837
|
+
|
838
|
+
if (this._session) {
|
839
|
+
updateSession(this._session, { user });
|
840
|
+
}
|
841
|
+
|
842
|
+
this._notifyScopeListeners();
|
843
|
+
return this;
|
844
|
+
}
|
845
|
+
|
846
|
+
/**
|
847
|
+
* @inheritDoc
|
848
|
+
*/
|
849
|
+
getUser() {
|
850
|
+
return this._user;
|
851
|
+
}
|
852
|
+
|
853
|
+
/**
|
854
|
+
* @inheritDoc
|
855
|
+
*/
|
856
|
+
// eslint-disable-next-line deprecation/deprecation
|
857
|
+
getRequestSession() {
|
858
|
+
return this._requestSession;
|
859
|
+
}
|
860
|
+
|
861
|
+
/**
|
862
|
+
* @inheritDoc
|
863
|
+
*/
|
864
|
+
// eslint-disable-next-line deprecation/deprecation
|
865
|
+
setRequestSession(requestSession) {
|
866
|
+
this._requestSession = requestSession;
|
867
|
+
return this;
|
868
|
+
}
|
869
|
+
|
870
|
+
/**
|
871
|
+
* @inheritDoc
|
872
|
+
*/
|
873
|
+
setTags(tags) {
|
874
|
+
this._tags = {
|
875
|
+
...this._tags,
|
876
|
+
...tags,
|
877
|
+
};
|
878
|
+
this._notifyScopeListeners();
|
879
|
+
return this;
|
880
|
+
}
|
881
|
+
|
882
|
+
/**
|
883
|
+
* @inheritDoc
|
884
|
+
*/
|
885
|
+
setTag(key, value) {
|
886
|
+
this._tags = { ...this._tags, [key]: value };
|
887
|
+
this._notifyScopeListeners();
|
888
|
+
return this;
|
889
|
+
}
|
890
|
+
|
891
|
+
/**
|
892
|
+
* @inheritDoc
|
893
|
+
*/
|
894
|
+
setExtras(extras) {
|
895
|
+
this._extra = {
|
896
|
+
...this._extra,
|
897
|
+
...extras,
|
898
|
+
};
|
899
|
+
this._notifyScopeListeners();
|
900
|
+
return this;
|
901
|
+
}
|
902
|
+
|
903
|
+
/**
|
904
|
+
* @inheritDoc
|
905
|
+
*/
|
906
|
+
setExtra(key, extra) {
|
907
|
+
this._extra = { ...this._extra, [key]: extra };
|
908
|
+
this._notifyScopeListeners();
|
909
|
+
return this;
|
910
|
+
}
|
911
|
+
|
912
|
+
/**
|
913
|
+
* @inheritDoc
|
914
|
+
*/
|
915
|
+
setFingerprint(fingerprint) {
|
916
|
+
this._fingerprint = fingerprint;
|
917
|
+
this._notifyScopeListeners();
|
918
|
+
return this;
|
919
|
+
}
|
920
|
+
|
921
|
+
/**
|
922
|
+
* @inheritDoc
|
923
|
+
*/
|
924
|
+
setLevel(level) {
|
925
|
+
this._level = level;
|
926
|
+
this._notifyScopeListeners();
|
927
|
+
return this;
|
928
|
+
}
|
929
|
+
|
930
|
+
/**
|
931
|
+
* Sets the transaction name on the scope so that the name of e.g. taken server route or
|
932
|
+
* the page location is attached to future events.
|
933
|
+
*
|
934
|
+
* IMPORTANT: Calling this function does NOT change the name of the currently active
|
935
|
+
* root span. If you want to change the name of the active root span, use
|
936
|
+
* `Sentry.updateSpanName(rootSpan, 'new name')` instead.
|
937
|
+
*
|
938
|
+
* By default, the SDK updates the scope's transaction name automatically on sensible
|
939
|
+
* occasions, such as a page navigation or when handling a new request on the server.
|
940
|
+
*/
|
941
|
+
setTransactionName(name) {
|
942
|
+
this._transactionName = name;
|
943
|
+
this._notifyScopeListeners();
|
944
|
+
return this;
|
945
|
+
}
|
946
|
+
|
947
|
+
/**
|
948
|
+
* @inheritDoc
|
949
|
+
*/
|
950
|
+
setContext(key, context) {
|
951
|
+
if (context === null) {
|
952
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
953
|
+
delete this._contexts[key];
|
954
|
+
} else {
|
955
|
+
this._contexts[key] = context;
|
956
|
+
}
|
957
|
+
|
958
|
+
this._notifyScopeListeners();
|
959
|
+
return this;
|
960
|
+
}
|
961
|
+
|
962
|
+
/**
|
963
|
+
* @inheritDoc
|
964
|
+
*/
|
965
|
+
setSession(session) {
|
966
|
+
if (!session) {
|
967
|
+
delete this._session;
|
968
|
+
} else {
|
969
|
+
this._session = session;
|
970
|
+
}
|
971
|
+
this._notifyScopeListeners();
|
972
|
+
return this;
|
973
|
+
}
|
974
|
+
|
975
|
+
/**
|
976
|
+
* @inheritDoc
|
977
|
+
*/
|
978
|
+
getSession() {
|
979
|
+
return this._session;
|
980
|
+
}
|
981
|
+
|
982
|
+
/**
|
983
|
+
* @inheritDoc
|
984
|
+
*/
|
985
|
+
update(captureContext) {
|
986
|
+
if (!captureContext) {
|
987
|
+
return this;
|
988
|
+
}
|
989
|
+
|
990
|
+
const scopeToMerge = typeof captureContext === 'function' ? captureContext(this) : captureContext;
|
991
|
+
|
992
|
+
const [scopeInstance, requestSession] =
|
993
|
+
scopeToMerge instanceof Scope
|
994
|
+
? // eslint-disable-next-line deprecation/deprecation
|
995
|
+
[scopeToMerge.getScopeData(), scopeToMerge.getRequestSession()]
|
996
|
+
: isPlainObject(scopeToMerge)
|
997
|
+
? [captureContext , (captureContext ).requestSession]
|
998
|
+
: [];
|
999
|
+
|
1000
|
+
const { tags, extra, user, contexts, level, fingerprint = [], propagationContext } = scopeInstance || {};
|
1001
|
+
|
1002
|
+
this._tags = { ...this._tags, ...tags };
|
1003
|
+
this._extra = { ...this._extra, ...extra };
|
1004
|
+
this._contexts = { ...this._contexts, ...contexts };
|
1005
|
+
|
1006
|
+
if (user && Object.keys(user).length) {
|
1007
|
+
this._user = user;
|
1008
|
+
}
|
1009
|
+
|
1010
|
+
if (level) {
|
1011
|
+
this._level = level;
|
1012
|
+
}
|
1013
|
+
|
1014
|
+
if (fingerprint.length) {
|
1015
|
+
this._fingerprint = fingerprint;
|
1016
|
+
}
|
1017
|
+
|
1018
|
+
if (propagationContext) {
|
1019
|
+
this._propagationContext = propagationContext;
|
1020
|
+
}
|
1021
|
+
|
1022
|
+
if (requestSession) {
|
1023
|
+
this._requestSession = requestSession;
|
1024
|
+
}
|
1025
|
+
|
1026
|
+
return this;
|
1027
|
+
}
|
1028
|
+
|
1029
|
+
/**
|
1030
|
+
* @inheritDoc
|
1031
|
+
*/
|
1032
|
+
clear() {
|
1033
|
+
// client is not cleared here on purpose!
|
1034
|
+
this._breadcrumbs = [];
|
1035
|
+
this._tags = {};
|
1036
|
+
this._extra = {};
|
1037
|
+
this._user = {};
|
1038
|
+
this._contexts = {};
|
1039
|
+
this._level = undefined;
|
1040
|
+
this._transactionName = undefined;
|
1041
|
+
this._fingerprint = undefined;
|
1042
|
+
this._requestSession = undefined;
|
1043
|
+
this._session = undefined;
|
1044
|
+
_setSpanForScope(this, undefined);
|
1045
|
+
this._attachments = [];
|
1046
|
+
this.setPropagationContext({ traceId: generateTraceId() });
|
1047
|
+
|
1048
|
+
this._notifyScopeListeners();
|
1049
|
+
return this;
|
1050
|
+
}
|
1051
|
+
|
1052
|
+
/**
|
1053
|
+
* @inheritDoc
|
1054
|
+
*/
|
1055
|
+
addBreadcrumb(breadcrumb, maxBreadcrumbs) {
|
1056
|
+
const maxCrumbs = typeof maxBreadcrumbs === 'number' ? maxBreadcrumbs : DEFAULT_MAX_BREADCRUMBS;
|
1057
|
+
|
1058
|
+
// No data has been changed, so don't notify scope listeners
|
1059
|
+
if (maxCrumbs <= 0) {
|
1060
|
+
return this;
|
1061
|
+
}
|
1062
|
+
|
1063
|
+
const mergedBreadcrumb = {
|
1064
|
+
timestamp: dateTimestampInSeconds(),
|
1065
|
+
...breadcrumb,
|
1066
|
+
};
|
1067
|
+
|
1068
|
+
this._breadcrumbs.push(mergedBreadcrumb);
|
1069
|
+
if (this._breadcrumbs.length > maxCrumbs) {
|
1070
|
+
this._breadcrumbs = this._breadcrumbs.slice(-maxCrumbs);
|
1071
|
+
if (this._client) {
|
1072
|
+
this._client.recordDroppedEvent('buffer_overflow', 'log_item');
|
1073
|
+
}
|
1074
|
+
}
|
1075
|
+
|
1076
|
+
this._notifyScopeListeners();
|
1077
|
+
|
1078
|
+
return this;
|
1079
|
+
}
|
1080
|
+
|
1081
|
+
/**
|
1082
|
+
* @inheritDoc
|
1083
|
+
*/
|
1084
|
+
getLastBreadcrumb() {
|
1085
|
+
return this._breadcrumbs[this._breadcrumbs.length - 1];
|
1086
|
+
}
|
1087
|
+
|
1088
|
+
/**
|
1089
|
+
* @inheritDoc
|
1090
|
+
*/
|
1091
|
+
clearBreadcrumbs() {
|
1092
|
+
this._breadcrumbs = [];
|
1093
|
+
this._notifyScopeListeners();
|
1094
|
+
return this;
|
1095
|
+
}
|
1096
|
+
|
1097
|
+
/**
|
1098
|
+
* @inheritDoc
|
1099
|
+
*/
|
1100
|
+
addAttachment(attachment) {
|
1101
|
+
this._attachments.push(attachment);
|
1102
|
+
return this;
|
1103
|
+
}
|
1104
|
+
|
1105
|
+
/**
|
1106
|
+
* @inheritDoc
|
1107
|
+
*/
|
1108
|
+
clearAttachments() {
|
1109
|
+
this._attachments = [];
|
1110
|
+
return this;
|
1111
|
+
}
|
1112
|
+
|
1113
|
+
/** @inheritDoc */
|
1114
|
+
getScopeData() {
|
1115
|
+
return {
|
1116
|
+
breadcrumbs: this._breadcrumbs,
|
1117
|
+
attachments: this._attachments,
|
1118
|
+
contexts: this._contexts,
|
1119
|
+
tags: this._tags,
|
1120
|
+
extra: this._extra,
|
1121
|
+
user: this._user,
|
1122
|
+
level: this._level,
|
1123
|
+
fingerprint: this._fingerprint || [],
|
1124
|
+
eventProcessors: this._eventProcessors,
|
1125
|
+
propagationContext: this._propagationContext,
|
1126
|
+
sdkProcessingMetadata: this._sdkProcessingMetadata,
|
1127
|
+
transactionName: this._transactionName,
|
1128
|
+
span: _getSpanForScope(this),
|
1129
|
+
};
|
1130
|
+
}
|
1131
|
+
|
1132
|
+
/**
|
1133
|
+
* @inheritDoc
|
1134
|
+
*/
|
1135
|
+
setSDKProcessingMetadata(newData) {
|
1136
|
+
this._sdkProcessingMetadata = merge(this._sdkProcessingMetadata, newData, 2);
|
1137
|
+
return this;
|
1138
|
+
}
|
1139
|
+
|
1140
|
+
/**
|
1141
|
+
* @inheritDoc
|
1142
|
+
*/
|
1143
|
+
setPropagationContext(
|
1144
|
+
context,
|
1145
|
+
) {
|
1146
|
+
this._propagationContext = {
|
1147
|
+
// eslint-disable-next-line deprecation/deprecation
|
1148
|
+
spanId: generateSpanId(),
|
1149
|
+
...context,
|
1150
|
+
};
|
1151
|
+
return this;
|
1152
|
+
}
|
1153
|
+
|
1154
|
+
/**
|
1155
|
+
* @inheritDoc
|
1156
|
+
*/
|
1157
|
+
getPropagationContext() {
|
1158
|
+
return this._propagationContext;
|
1159
|
+
}
|
1160
|
+
|
1161
|
+
/**
|
1162
|
+
* @inheritDoc
|
1163
|
+
*/
|
1164
|
+
captureException(exception, hint) {
|
1165
|
+
const eventId = hint && hint.event_id ? hint.event_id : uuid4();
|
1166
|
+
|
1167
|
+
if (!this._client) {
|
1168
|
+
logger.warn('No client configured on scope - will not capture exception!');
|
1169
|
+
return eventId;
|
1170
|
+
}
|
1171
|
+
|
1172
|
+
const syntheticException = new Error('Sentry syntheticException');
|
1173
|
+
|
1174
|
+
this._client.captureException(
|
1175
|
+
exception,
|
1176
|
+
{
|
1177
|
+
originalException: exception,
|
1178
|
+
syntheticException,
|
1179
|
+
...hint,
|
1180
|
+
event_id: eventId,
|
1181
|
+
},
|
1182
|
+
this,
|
1183
|
+
);
|
1184
|
+
|
1185
|
+
return eventId;
|
1186
|
+
}
|
1187
|
+
|
1188
|
+
/**
|
1189
|
+
* @inheritDoc
|
1190
|
+
*/
|
1191
|
+
captureMessage(message, level, hint) {
|
1192
|
+
const eventId = hint && hint.event_id ? hint.event_id : uuid4();
|
1193
|
+
|
1194
|
+
if (!this._client) {
|
1195
|
+
logger.warn('No client configured on scope - will not capture message!');
|
1196
|
+
return eventId;
|
1197
|
+
}
|
1198
|
+
|
1199
|
+
const syntheticException = new Error(message);
|
1200
|
+
|
1201
|
+
this._client.captureMessage(
|
1202
|
+
message,
|
1203
|
+
level,
|
1204
|
+
{
|
1205
|
+
originalException: message,
|
1206
|
+
syntheticException,
|
1207
|
+
...hint,
|
1208
|
+
event_id: eventId,
|
1209
|
+
},
|
1210
|
+
this,
|
1211
|
+
);
|
1212
|
+
|
1213
|
+
return eventId;
|
1214
|
+
}
|
1215
|
+
|
1216
|
+
/**
|
1217
|
+
* @inheritDoc
|
1218
|
+
*/
|
1219
|
+
captureEvent(event, hint) {
|
1220
|
+
const eventId = hint && hint.event_id ? hint.event_id : uuid4();
|
1221
|
+
|
1222
|
+
if (!this._client) {
|
1223
|
+
logger.warn('No client configured on scope - will not capture event!');
|
1224
|
+
return eventId;
|
1225
|
+
}
|
1226
|
+
|
1227
|
+
this._client.captureEvent(event, { ...hint, event_id: eventId }, this);
|
1228
|
+
|
1229
|
+
return eventId;
|
1230
|
+
}
|
1231
|
+
|
1232
|
+
/**
|
1233
|
+
* This will be called on every set call.
|
1234
|
+
*/
|
1235
|
+
_notifyScopeListeners() {
|
1236
|
+
// We need this check for this._notifyingListeners to be able to work on scope during updates
|
1237
|
+
// If this check is not here we'll produce endless recursion when something is done with the scope
|
1238
|
+
// during the callback.
|
1239
|
+
if (!this._notifyingListeners) {
|
1240
|
+
this._notifyingListeners = true;
|
1241
|
+
this._scopeListeners.forEach(callback => {
|
1242
|
+
callback(this);
|
1243
|
+
});
|
1244
|
+
this._notifyingListeners = false;
|
1245
|
+
}
|
1246
|
+
}
|
1247
|
+
}
|
1248
|
+
|
1249
|
+
/**
|
1250
|
+
* Holds additional event information.
|
1251
|
+
*/
|
1252
|
+
const Scope = ScopeClass;
|
1253
|
+
|
1254
|
+
/** Get the default current scope. */
|
1255
|
+
function getDefaultCurrentScope() {
|
1256
|
+
return getGlobalSingleton('defaultCurrentScope', () => new Scope());
|
1257
|
+
}
|
1258
|
+
|
1259
|
+
/** Get the default isolation scope. */
|
1260
|
+
function getDefaultIsolationScope() {
|
1261
|
+
return getGlobalSingleton('defaultIsolationScope', () => new Scope());
|
1262
|
+
}
|
1263
|
+
|
1264
|
+
/**
|
1265
|
+
* This is an object that holds a stack of scopes.
|
1266
|
+
*/
|
1267
|
+
class AsyncContextStack {
|
1268
|
+
|
1269
|
+
constructor(scope, isolationScope) {
|
1270
|
+
let assignedScope;
|
1271
|
+
if (!scope) {
|
1272
|
+
assignedScope = new Scope();
|
1273
|
+
} else {
|
1274
|
+
assignedScope = scope;
|
1275
|
+
}
|
1276
|
+
|
1277
|
+
let assignedIsolationScope;
|
1278
|
+
if (!isolationScope) {
|
1279
|
+
assignedIsolationScope = new Scope();
|
1280
|
+
} else {
|
1281
|
+
assignedIsolationScope = isolationScope;
|
1282
|
+
}
|
1283
|
+
|
1284
|
+
// scope stack for domains or the process
|
1285
|
+
this._stack = [{ scope: assignedScope }];
|
1286
|
+
this._isolationScope = assignedIsolationScope;
|
1287
|
+
}
|
1288
|
+
|
1289
|
+
/**
|
1290
|
+
* Fork a scope for the stack.
|
1291
|
+
*/
|
1292
|
+
withScope(callback) {
|
1293
|
+
const scope = this._pushScope();
|
1294
|
+
|
1295
|
+
let maybePromiseResult;
|
1296
|
+
try {
|
1297
|
+
maybePromiseResult = callback(scope);
|
1298
|
+
} catch (e) {
|
1299
|
+
this._popScope();
|
1300
|
+
throw e;
|
1301
|
+
}
|
1302
|
+
|
1303
|
+
if (isThenable(maybePromiseResult)) {
|
1304
|
+
// @ts-expect-error - isThenable returns the wrong type
|
1305
|
+
return maybePromiseResult.then(
|
1306
|
+
res => {
|
1307
|
+
this._popScope();
|
1308
|
+
return res;
|
1309
|
+
},
|
1310
|
+
e => {
|
1311
|
+
this._popScope();
|
1312
|
+
throw e;
|
1313
|
+
},
|
1314
|
+
);
|
1315
|
+
}
|
1316
|
+
|
1317
|
+
this._popScope();
|
1318
|
+
return maybePromiseResult;
|
1319
|
+
}
|
1320
|
+
|
1321
|
+
/**
|
1322
|
+
* Get the client of the stack.
|
1323
|
+
*/
|
1324
|
+
getClient() {
|
1325
|
+
return this.getStackTop().client ;
|
1326
|
+
}
|
1327
|
+
|
1328
|
+
/**
|
1329
|
+
* Returns the scope of the top stack.
|
1330
|
+
*/
|
1331
|
+
getScope() {
|
1332
|
+
return this.getStackTop().scope;
|
1333
|
+
}
|
1334
|
+
|
1335
|
+
/**
|
1336
|
+
* Get the isolation scope for the stack.
|
1337
|
+
*/
|
1338
|
+
getIsolationScope() {
|
1339
|
+
return this._isolationScope;
|
1340
|
+
}
|
1341
|
+
|
1342
|
+
/**
|
1343
|
+
* Returns the topmost scope layer in the order domain > local > process.
|
1344
|
+
*/
|
1345
|
+
getStackTop() {
|
1346
|
+
return this._stack[this._stack.length - 1] ;
|
1347
|
+
}
|
1348
|
+
|
1349
|
+
/**
|
1350
|
+
* Push a scope to the stack.
|
1351
|
+
*/
|
1352
|
+
_pushScope() {
|
1353
|
+
// We want to clone the content of prev scope
|
1354
|
+
const scope = this.getScope().clone();
|
1355
|
+
this._stack.push({
|
1356
|
+
client: this.getClient(),
|
1357
|
+
scope,
|
1358
|
+
});
|
1359
|
+
return scope;
|
1360
|
+
}
|
1361
|
+
|
1362
|
+
/**
|
1363
|
+
* Pop a scope from the stack.
|
1364
|
+
*/
|
1365
|
+
_popScope() {
|
1366
|
+
if (this._stack.length <= 1) return false;
|
1367
|
+
return !!this._stack.pop();
|
1368
|
+
}
|
1369
|
+
}
|
1370
|
+
|
1371
|
+
/**
|
1372
|
+
* Get the global async context stack.
|
1373
|
+
* This will be removed during the v8 cycle and is only here to make migration easier.
|
1374
|
+
*/
|
1375
|
+
function getAsyncContextStack() {
|
1376
|
+
const registry = getMainCarrier();
|
1377
|
+
const sentry = getSentryCarrier(registry);
|
1378
|
+
|
1379
|
+
return (sentry.stack = sentry.stack || new AsyncContextStack(getDefaultCurrentScope(), getDefaultIsolationScope()));
|
1380
|
+
}
|
1381
|
+
|
1382
|
+
function withScope$1(callback) {
|
1383
|
+
return getAsyncContextStack().withScope(callback);
|
1384
|
+
}
|
1385
|
+
|
1386
|
+
function withSetScope(scope, callback) {
|
1387
|
+
const stack = getAsyncContextStack() ;
|
1388
|
+
return stack.withScope(() => {
|
1389
|
+
stack.getStackTop().scope = scope;
|
1390
|
+
return callback(scope);
|
1391
|
+
});
|
1392
|
+
}
|
1393
|
+
|
1394
|
+
function withIsolationScope(callback) {
|
1395
|
+
return getAsyncContextStack().withScope(() => {
|
1396
|
+
return callback(getAsyncContextStack().getIsolationScope());
|
1397
|
+
});
|
1398
|
+
}
|
1399
|
+
|
1400
|
+
/**
|
1401
|
+
* Get the stack-based async context strategy.
|
1402
|
+
*/
|
1403
|
+
function getStackAsyncContextStrategy() {
|
1404
|
+
return {
|
1405
|
+
withIsolationScope,
|
1406
|
+
withScope: withScope$1,
|
1407
|
+
withSetScope,
|
1408
|
+
withSetIsolationScope: (_isolationScope, callback) => {
|
1409
|
+
return withIsolationScope(callback);
|
1410
|
+
},
|
1411
|
+
getCurrentScope: () => getAsyncContextStack().getScope(),
|
1412
|
+
getIsolationScope: () => getAsyncContextStack().getIsolationScope(),
|
1413
|
+
};
|
1414
|
+
}
|
1415
|
+
|
1416
|
+
/**
|
1417
|
+
* Get the current async context strategy.
|
1418
|
+
* If none has been setup, the default will be used.
|
1419
|
+
*/
|
1420
|
+
function getAsyncContextStrategy(carrier) {
|
1421
|
+
const sentry = getSentryCarrier(carrier);
|
1422
|
+
|
1423
|
+
if (sentry.acs) {
|
1424
|
+
return sentry.acs;
|
1425
|
+
}
|
1426
|
+
|
1427
|
+
// Otherwise, use the default one (stack)
|
1428
|
+
return getStackAsyncContextStrategy();
|
1429
|
+
}
|
1430
|
+
|
1431
|
+
/**
|
1432
|
+
* Get the currently active scope.
|
1433
|
+
*/
|
1434
|
+
function getCurrentScope() {
|
1435
|
+
const carrier = getMainCarrier();
|
1436
|
+
const acs = getAsyncContextStrategy(carrier);
|
1437
|
+
return acs.getCurrentScope();
|
1438
|
+
}
|
1439
|
+
|
1440
|
+
/**
|
1441
|
+
* Creates a new scope with and executes the given operation within.
|
1442
|
+
* The scope is automatically removed once the operation
|
1443
|
+
* finishes or throws.
|
1444
|
+
*/
|
1445
|
+
|
1446
|
+
/**
|
1447
|
+
* Either creates a new active scope, or sets the given scope as active scope in the given callback.
|
1448
|
+
*/
|
1449
|
+
function withScope(
|
1450
|
+
...rest
|
1451
|
+
) {
|
1452
|
+
const carrier = getMainCarrier();
|
1453
|
+
const acs = getAsyncContextStrategy(carrier);
|
1454
|
+
|
1455
|
+
// If a scope is defined, we want to make this the active scope instead of the default one
|
1456
|
+
if (rest.length === 2) {
|
1457
|
+
const [scope, callback] = rest;
|
1458
|
+
|
1459
|
+
if (!scope) {
|
1460
|
+
return acs.withScope(callback);
|
1461
|
+
}
|
1462
|
+
|
1463
|
+
return acs.withSetScope(scope, callback);
|
1464
|
+
}
|
1465
|
+
|
1466
|
+
return acs.withScope(rest[0]);
|
1467
|
+
}
|
1468
|
+
|
1469
|
+
/**
|
1470
|
+
* Get the currently active client.
|
1471
|
+
*/
|
1472
|
+
function getClient() {
|
1473
|
+
return getCurrentScope().getClient();
|
1474
|
+
}
|
1475
|
+
|
1476
|
+
/**
|
1477
|
+
* Parse either an `EventHint` directly, or convert a `CaptureContext` to an `EventHint`.
|
1478
|
+
* This is used to allow to update method signatures that used to accept a `CaptureContext` but should now accept an `EventHint`.
|
1479
|
+
*/
|
1480
|
+
function parseEventHintOrCaptureContext(
|
1481
|
+
hint,
|
1482
|
+
) {
|
1483
|
+
if (!hint) {
|
1484
|
+
return undefined;
|
1485
|
+
}
|
1486
|
+
|
1487
|
+
// If you pass a Scope or `() => Scope` as CaptureContext, we just return this as captureContext
|
1488
|
+
if (hintIsScopeOrFunction(hint)) {
|
1489
|
+
return { captureContext: hint };
|
1490
|
+
}
|
1491
|
+
|
1492
|
+
if (hintIsScopeContext(hint)) {
|
1493
|
+
return {
|
1494
|
+
captureContext: hint,
|
1495
|
+
};
|
1496
|
+
}
|
1497
|
+
|
1498
|
+
return hint;
|
1499
|
+
}
|
1500
|
+
|
1501
|
+
function hintIsScopeOrFunction(
|
1502
|
+
hint,
|
1503
|
+
) {
|
1504
|
+
return hint instanceof Scope || typeof hint === 'function';
|
1505
|
+
}
|
1506
|
+
|
1507
|
+
const captureContextKeys = [
|
1508
|
+
'user',
|
1509
|
+
'level',
|
1510
|
+
'extra',
|
1511
|
+
'contexts',
|
1512
|
+
'tags',
|
1513
|
+
'fingerprint',
|
1514
|
+
'requestSession',
|
1515
|
+
'propagationContext',
|
1516
|
+
] ;
|
1517
|
+
|
1518
|
+
function hintIsScopeContext(hint) {
|
1519
|
+
return Object.keys(hint).some(key => captureContextKeys.includes(key ));
|
1520
|
+
}
|
1521
|
+
|
1522
|
+
/**
|
1523
|
+
* Captures an exception event and sends it to Sentry.
|
1524
|
+
*
|
1525
|
+
* @param exception The exception to capture.
|
1526
|
+
* @param hint Optional additional data to attach to the Sentry event.
|
1527
|
+
* @returns the id of the captured Sentry event.
|
1528
|
+
*/
|
1529
|
+
function captureException(exception, hint) {
|
1530
|
+
return getCurrentScope().captureException(exception, parseEventHintOrCaptureContext(hint));
|
1531
|
+
}
|
1532
|
+
|
1533
|
+
/**
|
1534
|
+
* Captures a message event and sends it to Sentry.
|
1535
|
+
*
|
1536
|
+
* @param message The message to send to Sentry.
|
1537
|
+
* @param captureContext Define the level of the message or pass in additional data to attach to the message.
|
1538
|
+
* @returns the id of the captured message.
|
1539
|
+
*/
|
1540
|
+
function captureMessage(message, captureContext) {
|
1541
|
+
// This is necessary to provide explicit scopes upgrade, without changing the original
|
1542
|
+
// arity of the `captureMessage(message, level)` method.
|
1543
|
+
const level = typeof captureContext === 'string' ? captureContext : undefined;
|
1544
|
+
const context = typeof captureContext !== 'string' ? { captureContext } : undefined;
|
1545
|
+
return getCurrentScope().captureMessage(message, level, context);
|
1546
|
+
}
|
1547
|
+
|
1548
|
+
/**
|
1549
|
+
* Define an integration function that can be used to create an integration instance.
|
1550
|
+
* Note that this by design hides the implementation details of the integration, as they are considered internal.
|
1551
|
+
*/
|
1552
|
+
function defineIntegration(fn) {
|
1553
|
+
return fn;
|
1554
|
+
}
|
1555
|
+
|
1556
|
+
/**
|
1557
|
+
* Add an instrumentation handler for when a console.xxx method is called.
|
1558
|
+
*
|
1559
|
+
* Use at your own risk, this might break without changelog notice, only used internally.
|
1560
|
+
* @hidden
|
1561
|
+
*/
|
1562
|
+
function addConsoleInstrumentationHandler(handler) {
|
1563
|
+
const type = 'console';
|
1564
|
+
addHandler(type, handler);
|
1565
|
+
maybeInstrument(type, instrumentConsole);
|
1566
|
+
}
|
1567
|
+
|
1568
|
+
function instrumentConsole() {
|
1569
|
+
if (!('console' in GLOBAL_OBJ)) {
|
1570
|
+
return;
|
1571
|
+
}
|
1572
|
+
|
1573
|
+
CONSOLE_LEVELS.forEach(function (level) {
|
1574
|
+
if (!(level in GLOBAL_OBJ.console)) {
|
1575
|
+
return;
|
1576
|
+
}
|
1577
|
+
|
1578
|
+
fill(GLOBAL_OBJ.console, level, function (originalConsoleMethod) {
|
1579
|
+
originalConsoleMethods[level] = originalConsoleMethod;
|
1580
|
+
|
1581
|
+
return function (...args) {
|
1582
|
+
const handlerData = { args, level };
|
1583
|
+
triggerHandlers('console', handlerData);
|
1584
|
+
|
1585
|
+
const log = originalConsoleMethods[level];
|
1586
|
+
log && log.apply(GLOBAL_OBJ.console, args);
|
1587
|
+
};
|
1588
|
+
});
|
1589
|
+
});
|
1590
|
+
}
|
1591
|
+
|
1592
|
+
/**
|
1593
|
+
* @deprecated This variable has been deprecated and will be removed in the next major version.
|
1594
|
+
*/
|
1595
|
+
|
1596
|
+
/**
|
1597
|
+
* Converts a string-based level into a `SeverityLevel`, normalizing it along the way.
|
1598
|
+
*
|
1599
|
+
* @param level String representation of desired `SeverityLevel`.
|
1600
|
+
* @returns The `SeverityLevel` corresponding to the given string, or 'log' if the string isn't a valid level.
|
1601
|
+
*/
|
1602
|
+
function severityLevelFromString(level) {
|
1603
|
+
return (
|
1604
|
+
level === 'warn' ? 'warning' : ['fatal', 'error', 'warning', 'log', 'info', 'debug'].includes(level) ? level : 'log'
|
1605
|
+
) ;
|
1606
|
+
}
|
1607
|
+
|
1608
|
+
const INTEGRATION_NAME = 'CaptureConsole';
|
1609
|
+
|
1610
|
+
const _captureConsoleIntegration = ((options = {}) => {
|
1611
|
+
const levels = options.levels || CONSOLE_LEVELS;
|
1612
|
+
// TODO(v9): Flip default value to `true`
|
1613
|
+
const handled = !!options.handled;
|
1614
|
+
|
1615
|
+
return {
|
1616
|
+
name: INTEGRATION_NAME,
|
1617
|
+
setup(client) {
|
1618
|
+
if (!('console' in GLOBAL_OBJ)) {
|
1619
|
+
return;
|
1620
|
+
}
|
1621
|
+
|
1622
|
+
addConsoleInstrumentationHandler(({ args, level }) => {
|
1623
|
+
if (getClient() !== client || !levels.includes(level)) {
|
1624
|
+
return;
|
1625
|
+
}
|
1626
|
+
|
1627
|
+
consoleHandler(args, level, handled);
|
1628
|
+
});
|
1629
|
+
},
|
1630
|
+
};
|
1631
|
+
}) ;
|
1632
|
+
|
1633
|
+
/**
|
1634
|
+
* Send Console API calls as Sentry Events.
|
1635
|
+
*/
|
1636
|
+
const captureConsoleIntegration = defineIntegration(_captureConsoleIntegration);
|
1637
|
+
|
1638
|
+
function consoleHandler(args, level, handled) {
|
1639
|
+
const captureContext = {
|
1640
|
+
level: severityLevelFromString(level),
|
1641
|
+
extra: {
|
1642
|
+
arguments: args,
|
1643
|
+
},
|
1644
|
+
};
|
1645
|
+
|
1646
|
+
withScope(scope => {
|
1647
|
+
scope.addEventProcessor(event => {
|
1648
|
+
event.logger = 'console';
|
1649
|
+
|
1650
|
+
addExceptionMechanism(event, {
|
1651
|
+
handled,
|
1652
|
+
type: 'console',
|
1653
|
+
});
|
1654
|
+
|
1655
|
+
return event;
|
1656
|
+
});
|
1657
|
+
|
1658
|
+
if (level === 'assert') {
|
1659
|
+
if (!args[0]) {
|
1660
|
+
const message = `Assertion failed: ${safeJoin(args.slice(1), ' ') || 'console.assert'}`;
|
1661
|
+
scope.setExtra('arguments', args.slice(1));
|
1662
|
+
captureMessage(message, captureContext);
|
1663
|
+
}
|
1664
|
+
return;
|
1665
|
+
}
|
1666
|
+
|
1667
|
+
const error = args.find(arg => arg instanceof Error);
|
1668
|
+
if (error) {
|
1669
|
+
captureException(error, captureContext);
|
1670
|
+
return;
|
1671
|
+
}
|
1672
|
+
|
1673
|
+
const message = safeJoin(args, ' ');
|
1674
|
+
captureMessage(message, captureContext);
|
1675
|
+
});
|
1676
|
+
}
|
1677
|
+
|
1678
|
+
/**
|
1679
|
+
* Initialize Sentry for React Native.
|
1680
|
+
*
|
1681
|
+
* @param {InitOptionsRN} options
|
1682
|
+
* @return {void}
|
1683
|
+
*/
|
1684
|
+
const initSentry = options => {
|
1685
|
+
let shouldSendToSentry = options?.requiredEnvForSendToSentry?.includes(options.env);
|
1686
|
+
let ignoreErrors = [/StallTracking/];
|
1687
|
+
if (options?.ignoreErrorsOptions) {
|
1688
|
+
ignoreErrors = ignoreErrors.concat(options.ignoreErrorsOptions);
|
1689
|
+
}
|
1690
|
+
if (options?.options?.ignoreErrors) {
|
1691
|
+
ignoreErrors = ignoreErrors.concat(options.options.ignoreErrors);
|
1692
|
+
delete options.options.ignoreErrors;
|
1693
|
+
}
|
1694
|
+
let integrations = [];
|
1695
|
+
if (options?.httpClientIntegrationOptions?.failedRequestStatusCodes && options?.httpClientIntegrationOptions?.failedRequestTargets) {
|
1696
|
+
integrations.push(Sentry.httpClientIntegration({
|
1697
|
+
failedRequestStatusCodes: options.httpClientIntegrationOptions.failedRequestStatusCodes,
|
1698
|
+
failedRequestTargets: options.httpClientIntegrationOptions.failedRequestTargets
|
1699
|
+
}));
|
1700
|
+
}
|
1701
|
+
if (options?.captureConsoleIntegrationOptions?.levels) {
|
1702
|
+
integrations.push(captureConsoleIntegration({
|
1703
|
+
levels: options.captureConsoleIntegrationOptions.levels
|
1704
|
+
}));
|
1705
|
+
}
|
1706
|
+
if (options?.options?.hasOwnProperty('integrations')) {
|
1707
|
+
options.options.integrations = [...options.options.integrations, ...integrations];
|
1708
|
+
} else {
|
1709
|
+
options.options = options.options || {};
|
1710
|
+
options.options.integrations = integrations;
|
1711
|
+
}
|
1712
|
+
let sentryOptions = {
|
1713
|
+
dsn: options.dsn,
|
1714
|
+
debug: options.debug,
|
1715
|
+
environment: options.env,
|
1716
|
+
ignoreErrors: ignoreErrors,
|
1717
|
+
sampleRate: 1.0,
|
1718
|
+
maxBreadcrumbs: 50,
|
1719
|
+
autoSessionTracking: true,
|
1720
|
+
attachScreenshot: true,
|
1721
|
+
enableCaptureFailedRequests: true,
|
1722
|
+
enableTracing: true,
|
1723
|
+
tracesSampleRate: 1.0,
|
1724
|
+
enableNative: true,
|
1725
|
+
autoInitializeNativeSdk: true,
|
1726
|
+
enableNativeCrashHandling: true,
|
1727
|
+
enableNativeNagger: true,
|
1728
|
+
enableAutoSessionTracking: true,
|
1729
|
+
enableNdkScopeSync: true,
|
1730
|
+
attachThreads: true,
|
1731
|
+
enableAutoPerformanceTracing: true,
|
1732
|
+
enableWatchdogTerminationTracking: true,
|
1733
|
+
enableAppHangTracking: true,
|
1734
|
+
appHangTimeoutInterval: 5,
|
1735
|
+
sendDefaultPii: true,
|
1736
|
+
beforeSend: event => {
|
1737
|
+
if (event?.message?.includes('StallTracking') || !shouldSendToSentry) {
|
1738
|
+
return null;
|
1739
|
+
}
|
1740
|
+
return event;
|
1741
|
+
}
|
1742
|
+
};
|
1743
|
+
if (options?.release) {
|
1744
|
+
sentryOptions.release = options.release;
|
1745
|
+
}
|
1746
|
+
if (options?.beforeSend) {
|
1747
|
+
sentryOptions.beforeSend = options.beforeSend;
|
1748
|
+
}
|
1749
|
+
if (options?.options) {
|
1750
|
+
sentryOptions = {
|
1751
|
+
...sentryOptions,
|
1752
|
+
...options.options
|
1753
|
+
};
|
1754
|
+
}
|
1755
|
+
Sentry.init(sentryOptions);
|
1756
|
+
};
|
1757
|
+
/**
|
1758
|
+
* Record additional http data for Sentry.
|
1759
|
+
*
|
1760
|
+
* @param {any | null} config
|
1761
|
+
* @param {any | null} request
|
1762
|
+
* @param {any | null} response
|
1763
|
+
* @return {void}
|
1764
|
+
*/
|
1765
|
+
const recordAdditionalSentryHttp = (config, request, response) => {
|
1766
|
+
recordSentryHttp(Sentry, config, request, response);
|
1767
|
+
};
|
1768
|
+
|
1769
|
+
export { initSentry as i, recordAdditionalSentryHttp as r };
|
1770
|
+
//# sourceMappingURL=ReactNative-Ckbnh5vm.js.map
|