@andre1502/react-utilities 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +340 -0
- package/dist/Config/Config.d.ts +28 -0
- package/dist/Config/Config.js +200 -0
- package/dist/Config/Config.js.map +1 -0
- package/dist/Config/GoogleAuth.d.ts +15 -0
- package/dist/Config/GoogleAuth.js +71 -0
- package/dist/Config/GoogleAuth.js.map +1 -0
- package/dist/Config/Locales.d.ts +18 -0
- package/dist/Config/Locales.js +62 -0
- package/dist/Config/Locales.js.map +1 -0
- package/dist/Config/Output.d.ts +10 -0
- package/dist/Config/Output.js +28 -0
- package/dist/Config/Output.js.map +1 -0
- package/dist/Config/Sitemap.d.ts +13 -0
- package/dist/Config/Sitemap.js +73 -0
- package/dist/Config/Sitemap.js.map +1 -0
- package/dist/Format/NumberFormat.d.ts +6 -0
- package/dist/Format/NumberFormat.js +77 -0
- package/dist/Format/NumberFormat.js.map +1 -0
- package/dist/Format/NumberParser.d.ts +9 -0
- package/dist/Format/NumberParser.js +51 -0
- package/dist/Format/NumberParser.js.map +1 -0
- package/dist/React-BaJ1KfGF.js +87 -0
- package/dist/React-BaJ1KfGF.js.map +1 -0
- package/dist/React-qUl0CBmE.js +109 -0
- package/dist/React-qUl0CBmE.js.map +1 -0
- package/dist/ReactNative-CqUrY2ZJ.js +3856 -0
- package/dist/ReactNative-CqUrY2ZJ.js.map +1 -0
- package/dist/ReactNative-mNnws-b5.js +3834 -0
- package/dist/ReactNative-mNnws-b5.js.map +1 -0
- package/dist/Sentry/Build.d.ts +9 -0
- package/dist/Sentry/Build.js +88 -0
- package/dist/Sentry/Build.js.map +1 -0
- package/dist/Sentry/React.d.ts +18 -0
- package/dist/Sentry/React.js +104 -0
- package/dist/Sentry/React.js.map +1 -0
- package/dist/Sentry/ReactNative.d.ts +18 -0
- package/dist/Sentry/ReactNative.js +114 -0
- package/dist/Sentry/ReactNative.js.map +1 -0
- package/dist/Sentry/Utils.d.ts +2 -0
- package/dist/Sentry/Utils.js +24 -0
- package/dist/Sentry/Utils.js.map +1 -0
- package/dist/Utils/Files.d.ts +7 -0
- package/dist/Utils/Files.js +52 -0
- package/dist/Utils/Files.js.map +1 -0
- package/dist/Utils-Cq948gfa.js +20 -0
- package/dist/Utils-Cq948gfa.js.map +1 -0
- package/dist/Utils-Dilye04y.js +22 -0
- package/dist/Utils-Dilye04y.js.map +1 -0
- package/dist/config-cli.cjs +471 -0
- package/dist/config-cli.cjs.map +1 -0
- package/dist/config-cli.d.ts +34 -0
- package/dist/config-cli.js +220 -0
- package/dist/config-cli.js.map +1 -0
- package/dist/config-cli.mjs +443 -0
- package/dist/config-cli.mjs.map +1 -0
- package/dist/enums/CurrencySymbolEnum.d.ts +5 -0
- package/dist/enums/CurrencySymbolEnum.js +13 -0
- package/dist/enums/CurrencySymbolEnum.js.map +1 -0
- package/dist/format.cjs +122 -0
- package/dist/format.cjs.map +1 -0
- package/dist/format.d.ts +3 -0
- package/dist/format.js +55 -0
- package/dist/format.js.map +1 -0
- package/dist/format.mjs +117 -0
- package/dist/format.mjs.map +1 -0
- package/dist/index-cli.cjs +24 -0
- package/dist/index-cli.cjs.map +1 -0
- package/dist/index-cli.d.ts +2 -0
- package/dist/index-cli.js +28 -0
- package/dist/index-cli.js.map +1 -0
- package/dist/index-cli.mjs +12 -0
- package/dist/index-cli.mjs.map +1 -0
- package/dist/index-fmt.cjs +16 -0
- package/dist/index-fmt.cjs.map +1 -0
- package/dist/index-fmt.d.ts +1 -0
- package/dist/index-fmt.js +17 -0
- package/dist/index-fmt.js.map +1 -0
- package/dist/index-fmt.mjs +3 -0
- package/dist/index-fmt.mjs.map +1 -0
- package/dist/index-rn.cjs +21 -0
- package/dist/index-rn.cjs.map +1 -0
- package/dist/index-rn.d.ts +2 -0
- package/dist/index-rn.js +28 -0
- package/dist/index-rn.js.map +1 -0
- package/dist/index-rn.mjs +6 -0
- package/dist/index-rn.mjs.map +1 -0
- package/dist/index.cjs +21 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +6 -0
- package/dist/index.mjs.map +1 -0
- package/dist/interfaces/Config/ConfigOptions.d.ts +8 -0
- package/dist/interfaces/Config/ConfigOptions.js +6 -0
- package/dist/interfaces/Config/ConfigOptions.js.map +1 -0
- package/dist/interfaces/Config/OutputMap.d.ts +3 -0
- package/dist/interfaces/Config/OutputMap.js +6 -0
- package/dist/interfaces/Config/OutputMap.js.map +1 -0
- package/dist/interfaces/Config/OutputOptions.d.ts +7 -0
- package/dist/interfaces/Config/OutputOptions.js +6 -0
- package/dist/interfaces/Config/OutputOptions.js.map +1 -0
- package/dist/interfaces/Config/SitemapMap.d.ts +5 -0
- package/dist/interfaces/Config/SitemapMap.js +6 -0
- package/dist/interfaces/Config/SitemapMap.js.map +1 -0
- package/dist/interfaces/Config/StringMap.d.ts +3 -0
- package/dist/interfaces/Config/StringMap.js +6 -0
- package/dist/interfaces/Config/StringMap.js.map +1 -0
- package/dist/interfaces/Format/FormatOptions.d.ts +14 -0
- package/dist/interfaces/Format/FormatOptions.js +6 -0
- package/dist/interfaces/Format/FormatOptions.js.map +1 -0
- package/dist/interfaces/Sentry/InitOptions.d.ts +20 -0
- package/dist/interfaces/Sentry/InitOptions.js +6 -0
- package/dist/interfaces/Sentry/InitOptions.js.map +1 -0
- package/dist/interfaces/Sentry/InitOptionsRN.d.ts +4 -0
- package/dist/interfaces/Sentry/InitOptionsRN.js +6 -0
- package/dist/interfaces/Sentry/InitOptionsRN.js.map +1 -0
- package/dist/interfaces/Sentry/SourceMapOptions.d.ts +7 -0
- package/dist/interfaces/Sentry/SourceMapOptions.js +6 -0
- package/dist/interfaces/Sentry/SourceMapOptions.js.map +1 -0
- package/dist/sentry-cli.cjs +119 -0
- package/dist/sentry-cli.cjs.map +1 -0
- package/dist/sentry-cli.d.ts +9 -0
- package/dist/sentry-cli.js +50 -0
- package/dist/sentry-cli.js.map +1 -0
- package/dist/sentry-cli.mjs +98 -0
- package/dist/sentry-cli.mjs.map +1 -0
- package/dist/sentry-rn.cjs +11 -0
- package/dist/sentry-rn.cjs.map +1 -0
- package/dist/sentry-rn.d.ts +2 -0
- package/dist/sentry-rn.js +28 -0
- package/dist/sentry-rn.js.map +1 -0
- package/dist/sentry-rn.mjs +4 -0
- package/dist/sentry-rn.mjs.map +1 -0
- package/dist/sentry.cjs +11 -0
- package/dist/sentry.cjs.map +1 -0
- package/dist/sentry.d.ts +2 -0
- package/dist/sentry.js +28 -0
- package/dist/sentry.js.map +1 -0
- package/dist/sentry.mjs +4 -0
- package/dist/sentry.mjs.map +1 -0
- package/dist/types/Config/OptionType.d.ts +2 -0
- package/dist/types/Config/OptionType.js +6 -0
- package/dist/types/Config/OptionType.js.map +1 -0
- package/dist/types/Format/OptionType.d.ts +3 -0
- package/dist/types/Format/OptionType.js +6 -0
- package/dist/types/Format/OptionType.js.map +1 -0
- package/dist/types/Sentry/OptionType.d.ts +1 -0
- package/dist/types/Sentry/OptionType.js +6 -0
- package/dist/types/Sentry/OptionType.js.map +1 -0
- package/package.json +139 -0
- package/src/Config/Config.ts +258 -0
- package/src/Config/GoogleAuth.ts +54 -0
- package/src/Config/Locales.ts +72 -0
- package/src/Config/Output.ts +29 -0
- package/src/Config/Sitemap.ts +67 -0
- package/src/Format/NumberFormat.ts +96 -0
- package/src/Format/NumberParser.ts +42 -0
- package/src/Sentry/Build.ts +67 -0
- package/src/Sentry/React.ts +127 -0
- package/src/Sentry/ReactNative.ts +133 -0
- package/src/Sentry/Utils.ts +26 -0
- package/src/Utils/Files.ts +51 -0
- package/src/config-cli.ts +152 -0
- package/src/enums/CurrencySymbolEnum.ts +5 -0
- package/src/format.ts +39 -0
- package/src/index-cli.ts +2 -0
- package/src/index-fmt.ts +1 -0
- package/src/index-rn.ts +2 -0
- package/src/index.ts +2 -0
- package/src/interfaces/Config/ConfigOptions.ts +9 -0
- package/src/interfaces/Config/OutputMap.ts +3 -0
- package/src/interfaces/Config/OutputOptions.ts +8 -0
- package/src/interfaces/Config/SitemapMap.ts +5 -0
- package/src/interfaces/Config/StringMap.ts +3 -0
- package/src/interfaces/Format/FormatOptions.ts +19 -0
- package/src/interfaces/Sentry/InitOptions.ts +23 -0
- package/src/interfaces/Sentry/InitOptionsRN.ts +5 -0
- package/src/interfaces/Sentry/SourceMapOptions.ts +7 -0
- package/src/sentry-cli.ts +16 -0
- package/src/sentry-rn.ts +2 -0
- package/src/sentry.ts +2 -0
- package/src/types/Config/OptionType.ts +2 -0
- package/src/types/Format/OptionType.ts +15 -0
- package/src/types/Sentry/OptionType.ts +1 -0
@@ -0,0 +1,3856 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
var Sentry = require('@sentry/react-native');
|
4
|
+
var Utils = require('./Utils-Dilye04y.js');
|
5
|
+
|
6
|
+
function _interopNamespaceDefault(e) {
|
7
|
+
var n = Object.create(null);
|
8
|
+
if (e) {
|
9
|
+
Object.keys(e).forEach(function (k) {
|
10
|
+
if (k !== 'default') {
|
11
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
12
|
+
Object.defineProperty(n, k, d.get ? d : {
|
13
|
+
enumerable: true,
|
14
|
+
get: function () { return e[k]; }
|
15
|
+
});
|
16
|
+
}
|
17
|
+
});
|
18
|
+
}
|
19
|
+
n.default = e;
|
20
|
+
return Object.freeze(n);
|
21
|
+
}
|
22
|
+
|
23
|
+
var Sentry__namespace = /*#__PURE__*/_interopNamespaceDefault(Sentry);
|
24
|
+
|
25
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
26
|
+
const objectToString = Object.prototype.toString;
|
27
|
+
/**
|
28
|
+
* Checks whether given value is an instance of the given built-in class.
|
29
|
+
*
|
30
|
+
* @param wat The value to be checked
|
31
|
+
* @param className
|
32
|
+
* @returns A boolean representing the result.
|
33
|
+
*/
|
34
|
+
function isBuiltin(wat, className) {
|
35
|
+
return objectToString.call(wat) === `[object ${className}]`;
|
36
|
+
}
|
37
|
+
|
38
|
+
/**
|
39
|
+
* Checks whether given value's type is a string
|
40
|
+
* {@link isString}.
|
41
|
+
*
|
42
|
+
* @param wat A value to be checked.
|
43
|
+
* @returns A boolean representing the result.
|
44
|
+
*/
|
45
|
+
function isString(wat) {
|
46
|
+
return isBuiltin(wat, 'String');
|
47
|
+
}
|
48
|
+
|
49
|
+
/**
|
50
|
+
* Checks whether given value's type is an object literal, or a class instance.
|
51
|
+
* {@link isPlainObject}.
|
52
|
+
*
|
53
|
+
* @param wat A value to be checked.
|
54
|
+
* @returns A boolean representing the result.
|
55
|
+
*/
|
56
|
+
function isPlainObject(wat) {
|
57
|
+
return isBuiltin(wat, 'Object');
|
58
|
+
}
|
59
|
+
|
60
|
+
/**
|
61
|
+
* Checks whether given value has a then function.
|
62
|
+
* @param wat A value to be checked.
|
63
|
+
*/
|
64
|
+
function isThenable(wat) {
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
66
|
+
return Boolean(wat && wat.then && typeof wat.then === 'function');
|
67
|
+
}
|
68
|
+
|
69
|
+
/**
|
70
|
+
* Checks whether given value's type is a Vue ViewModel.
|
71
|
+
*
|
72
|
+
* @param wat A value to be checked.
|
73
|
+
* @returns A boolean representing the result.
|
74
|
+
*/
|
75
|
+
function isVueViewModel(wat) {
|
76
|
+
// Not using Object.prototype.toString because in Vue 3 it would read the instance's Symbol(Symbol.toStringTag) property.
|
77
|
+
return !!(typeof wat === 'object' && wat !== null && ((wat ).__isVue || (wat )._isVue));
|
78
|
+
}
|
79
|
+
|
80
|
+
/**
|
81
|
+
* Join values in array
|
82
|
+
* @param input array of values to be joined together
|
83
|
+
* @param delimiter string to be placed in-between values
|
84
|
+
* @returns Joined values
|
85
|
+
*/
|
86
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
87
|
+
function safeJoin(input, delimiter) {
|
88
|
+
if (!Array.isArray(input)) {
|
89
|
+
return '';
|
90
|
+
}
|
91
|
+
|
92
|
+
const output = [];
|
93
|
+
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
94
|
+
for (let i = 0; i < input.length; i++) {
|
95
|
+
const value = input[i];
|
96
|
+
try {
|
97
|
+
// This is a hack to fix a Vue3-specific bug that causes an infinite loop of
|
98
|
+
// console warnings. This happens when a Vue template is rendered with
|
99
|
+
// an undeclared variable, which we try to stringify, ultimately causing
|
100
|
+
// Vue to issue another warning which repeats indefinitely.
|
101
|
+
// see: https://github.com/getsentry/sentry-javascript/pull/8981
|
102
|
+
if (isVueViewModel(value)) {
|
103
|
+
output.push('[VueViewModel]');
|
104
|
+
} else {
|
105
|
+
output.push(String(value));
|
106
|
+
}
|
107
|
+
} catch (e) {
|
108
|
+
output.push('[value cannot be serialized]');
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
return output.join(delimiter);
|
113
|
+
}
|
114
|
+
|
115
|
+
/** Internal global with common properties and Sentry extensions */
|
116
|
+
|
117
|
+
// The code below for 'isGlobalObj' and 'GLOBAL_OBJ' was copied from core-js before modification
|
118
|
+
// https://github.com/zloirock/core-js/blob/1b944df55282cdc99c90db5f49eb0b6eda2cc0a3/packages/core-js/internals/global.js
|
119
|
+
// core-js has the following licence:
|
120
|
+
//
|
121
|
+
// Copyright (c) 2014-2022 Denis Pushkarev
|
122
|
+
//
|
123
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
124
|
+
// of this software and associated documentation files (the "Software"), to deal
|
125
|
+
// in the Software without restriction, including without limitation the rights
|
126
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
127
|
+
// copies of the Software, and to permit persons to whom the Software is
|
128
|
+
// furnished to do so, subject to the following conditions:
|
129
|
+
//
|
130
|
+
// The above copyright notice and this permission notice shall be included in
|
131
|
+
// all copies or substantial portions of the Software.
|
132
|
+
//
|
133
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
134
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
135
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
136
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
137
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
138
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
139
|
+
// THE SOFTWARE.
|
140
|
+
|
141
|
+
/** Returns 'obj' if it's the global object, otherwise returns undefined */
|
142
|
+
function isGlobalObj(obj) {
|
143
|
+
return obj && obj.Math == Math ? obj : undefined;
|
144
|
+
}
|
145
|
+
|
146
|
+
/** Get's the global object for the current JavaScript runtime */
|
147
|
+
const GLOBAL_OBJ =
|
148
|
+
(typeof globalThis == 'object' && isGlobalObj(globalThis)) ||
|
149
|
+
// eslint-disable-next-line no-restricted-globals
|
150
|
+
(typeof window == 'object' && isGlobalObj(window)) ||
|
151
|
+
(typeof self == 'object' && isGlobalObj(self)) ||
|
152
|
+
(typeof global == 'object' && isGlobalObj(global)) ||
|
153
|
+
(function () {
|
154
|
+
return this;
|
155
|
+
})() ||
|
156
|
+
{};
|
157
|
+
|
158
|
+
/**
|
159
|
+
* @deprecated Use GLOBAL_OBJ instead or WINDOW from @sentry/browser. This will be removed in v8
|
160
|
+
*/
|
161
|
+
function getGlobalObject() {
|
162
|
+
return GLOBAL_OBJ ;
|
163
|
+
}
|
164
|
+
|
165
|
+
/**
|
166
|
+
* Returns a global singleton contained in the global `__SENTRY__` object.
|
167
|
+
*
|
168
|
+
* If the singleton doesn't already exist in `__SENTRY__`, it will be created using the given factory
|
169
|
+
* function and added to the `__SENTRY__` object.
|
170
|
+
*
|
171
|
+
* @param name name of the global singleton on __SENTRY__
|
172
|
+
* @param creator creator Factory function to create the singleton if it doesn't already exist on `__SENTRY__`
|
173
|
+
* @param obj (Optional) The global object on which to look for `__SENTRY__`, if not `GLOBAL_OBJ`'s return value
|
174
|
+
* @returns the singleton
|
175
|
+
*/
|
176
|
+
function getGlobalSingleton(name, creator, obj) {
|
177
|
+
const gbl = (obj || GLOBAL_OBJ) ;
|
178
|
+
const __SENTRY__ = (gbl.__SENTRY__ = gbl.__SENTRY__ || {});
|
179
|
+
const singleton = __SENTRY__[name] || (__SENTRY__[name] = creator());
|
180
|
+
return singleton;
|
181
|
+
}
|
182
|
+
|
183
|
+
/**
|
184
|
+
* 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.
|
185
|
+
*
|
186
|
+
* ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking.
|
187
|
+
*/
|
188
|
+
const DEBUG_BUILD$2 = (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__);
|
189
|
+
|
190
|
+
/** Prefix for logging strings */
|
191
|
+
const PREFIX = 'Sentry Logger ';
|
192
|
+
|
193
|
+
const CONSOLE_LEVELS = [
|
194
|
+
'debug',
|
195
|
+
'info',
|
196
|
+
'warn',
|
197
|
+
'error',
|
198
|
+
'log',
|
199
|
+
'assert',
|
200
|
+
'trace',
|
201
|
+
] ;
|
202
|
+
|
203
|
+
/** This may be mutated by the console instrumentation. */
|
204
|
+
const originalConsoleMethods
|
205
|
+
|
206
|
+
= {};
|
207
|
+
|
208
|
+
/** JSDoc */
|
209
|
+
|
210
|
+
/**
|
211
|
+
* Temporarily disable sentry console instrumentations.
|
212
|
+
*
|
213
|
+
* @param callback The function to run against the original `console` messages
|
214
|
+
* @returns The results of the callback
|
215
|
+
*/
|
216
|
+
function consoleSandbox(callback) {
|
217
|
+
if (!('console' in GLOBAL_OBJ)) {
|
218
|
+
return callback();
|
219
|
+
}
|
220
|
+
|
221
|
+
const console = GLOBAL_OBJ.console ;
|
222
|
+
const wrappedFuncs = {};
|
223
|
+
|
224
|
+
const wrappedLevels = Object.keys(originalConsoleMethods) ;
|
225
|
+
|
226
|
+
// Restore all wrapped console methods
|
227
|
+
wrappedLevels.forEach(level => {
|
228
|
+
const originalConsoleMethod = originalConsoleMethods[level] ;
|
229
|
+
wrappedFuncs[level] = console[level] ;
|
230
|
+
console[level] = originalConsoleMethod;
|
231
|
+
});
|
232
|
+
|
233
|
+
try {
|
234
|
+
return callback();
|
235
|
+
} finally {
|
236
|
+
// Revert restoration to wrapped state
|
237
|
+
wrappedLevels.forEach(level => {
|
238
|
+
console[level] = wrappedFuncs[level] ;
|
239
|
+
});
|
240
|
+
}
|
241
|
+
}
|
242
|
+
|
243
|
+
function makeLogger() {
|
244
|
+
let enabled = false;
|
245
|
+
const logger = {
|
246
|
+
enable: () => {
|
247
|
+
enabled = true;
|
248
|
+
},
|
249
|
+
disable: () => {
|
250
|
+
enabled = false;
|
251
|
+
},
|
252
|
+
isEnabled: () => enabled,
|
253
|
+
};
|
254
|
+
|
255
|
+
if (DEBUG_BUILD$2) {
|
256
|
+
CONSOLE_LEVELS.forEach(name => {
|
257
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
258
|
+
logger[name] = (...args) => {
|
259
|
+
if (enabled) {
|
260
|
+
consoleSandbox(() => {
|
261
|
+
GLOBAL_OBJ.console[name](`${PREFIX}[${name}]:`, ...args);
|
262
|
+
});
|
263
|
+
}
|
264
|
+
};
|
265
|
+
});
|
266
|
+
} else {
|
267
|
+
CONSOLE_LEVELS.forEach(name => {
|
268
|
+
logger[name] = () => undefined;
|
269
|
+
});
|
270
|
+
}
|
271
|
+
|
272
|
+
return logger ;
|
273
|
+
}
|
274
|
+
|
275
|
+
const logger = makeLogger();
|
276
|
+
|
277
|
+
/**
|
278
|
+
* Replace a method in an object with a wrapped version of itself.
|
279
|
+
*
|
280
|
+
* @param source An object that contains a method to be wrapped.
|
281
|
+
* @param name The name of the method to be wrapped.
|
282
|
+
* @param replacementFactory A higher-order function that takes the original version of the given method and returns a
|
283
|
+
* wrapped version. Note: The function returned by `replacementFactory` needs to be a non-arrow function, in order to
|
284
|
+
* preserve the correct value of `this`, and the original method must be called using `origMethod.call(this, <other
|
285
|
+
* args>)` or `origMethod.apply(this, [<other args>])` (rather than being called directly), again to preserve `this`.
|
286
|
+
* @returns void
|
287
|
+
*/
|
288
|
+
function fill(source, name, replacementFactory) {
|
289
|
+
if (!(name in source)) {
|
290
|
+
return;
|
291
|
+
}
|
292
|
+
|
293
|
+
const original = source[name] ;
|
294
|
+
const wrapped = replacementFactory(original) ;
|
295
|
+
|
296
|
+
// Make sure it's a function first, as we need to attach an empty prototype for `defineProperties` to work
|
297
|
+
// otherwise it'll throw "TypeError: Object.defineProperties called on non-object"
|
298
|
+
if (typeof wrapped === 'function') {
|
299
|
+
markFunctionWrapped(wrapped, original);
|
300
|
+
}
|
301
|
+
|
302
|
+
source[name] = wrapped;
|
303
|
+
}
|
304
|
+
|
305
|
+
/**
|
306
|
+
* Defines a non-enumerable property on the given object.
|
307
|
+
*
|
308
|
+
* @param obj The object on which to set the property
|
309
|
+
* @param name The name of the property to be set
|
310
|
+
* @param value The value to which to set the property
|
311
|
+
*/
|
312
|
+
function addNonEnumerableProperty(obj, name, value) {
|
313
|
+
try {
|
314
|
+
Object.defineProperty(obj, name, {
|
315
|
+
// enumerable: false, // the default, so we can save on bundle size by not explicitly setting it
|
316
|
+
value: value,
|
317
|
+
writable: true,
|
318
|
+
configurable: true,
|
319
|
+
});
|
320
|
+
} catch (o_O) {
|
321
|
+
DEBUG_BUILD$2 && logger.log(`Failed to add non-enumerable property "${name}" to object`, obj);
|
322
|
+
}
|
323
|
+
}
|
324
|
+
|
325
|
+
/**
|
326
|
+
* Remembers the original function on the wrapped function and
|
327
|
+
* patches up the prototype.
|
328
|
+
*
|
329
|
+
* @param wrapped the wrapper function
|
330
|
+
* @param original the original function that gets wrapped
|
331
|
+
*/
|
332
|
+
function markFunctionWrapped(wrapped, original) {
|
333
|
+
try {
|
334
|
+
const proto = original.prototype || {};
|
335
|
+
wrapped.prototype = original.prototype = proto;
|
336
|
+
addNonEnumerableProperty(wrapped, '__sentry_original__', original);
|
337
|
+
} catch (o_O) {} // eslint-disable-line no-empty
|
338
|
+
}
|
339
|
+
|
340
|
+
/**
|
341
|
+
* Given any object, return a new object having removed all fields whose value was `undefined`.
|
342
|
+
* Works recursively on objects and arrays.
|
343
|
+
*
|
344
|
+
* Attention: This function keeps circular references in the returned object.
|
345
|
+
*/
|
346
|
+
function dropUndefinedKeys(inputValue) {
|
347
|
+
// This map keeps track of what already visited nodes map to.
|
348
|
+
// Our Set - based memoBuilder doesn't work here because we want to the output object to have the same circular
|
349
|
+
// references as the input object.
|
350
|
+
const memoizationMap = new Map();
|
351
|
+
|
352
|
+
// This function just proxies `_dropUndefinedKeys` to keep the `memoBuilder` out of this function's API
|
353
|
+
return _dropUndefinedKeys(inputValue, memoizationMap);
|
354
|
+
}
|
355
|
+
|
356
|
+
function _dropUndefinedKeys(inputValue, memoizationMap) {
|
357
|
+
if (isPojo(inputValue)) {
|
358
|
+
// If this node has already been visited due to a circular reference, return the object it was mapped to in the new object
|
359
|
+
const memoVal = memoizationMap.get(inputValue);
|
360
|
+
if (memoVal !== undefined) {
|
361
|
+
return memoVal ;
|
362
|
+
}
|
363
|
+
|
364
|
+
const returnValue = {};
|
365
|
+
// Store the mapping of this value in case we visit it again, in case of circular data
|
366
|
+
memoizationMap.set(inputValue, returnValue);
|
367
|
+
|
368
|
+
for (const key of Object.keys(inputValue)) {
|
369
|
+
if (typeof inputValue[key] !== 'undefined') {
|
370
|
+
returnValue[key] = _dropUndefinedKeys(inputValue[key], memoizationMap);
|
371
|
+
}
|
372
|
+
}
|
373
|
+
|
374
|
+
return returnValue ;
|
375
|
+
}
|
376
|
+
|
377
|
+
if (Array.isArray(inputValue)) {
|
378
|
+
// If this node has already been visited due to a circular reference, return the array it was mapped to in the new object
|
379
|
+
const memoVal = memoizationMap.get(inputValue);
|
380
|
+
if (memoVal !== undefined) {
|
381
|
+
return memoVal ;
|
382
|
+
}
|
383
|
+
|
384
|
+
const returnValue = [];
|
385
|
+
// Store the mapping of this value in case we visit it again, in case of circular data
|
386
|
+
memoizationMap.set(inputValue, returnValue);
|
387
|
+
|
388
|
+
inputValue.forEach((item) => {
|
389
|
+
returnValue.push(_dropUndefinedKeys(item, memoizationMap));
|
390
|
+
});
|
391
|
+
|
392
|
+
return returnValue ;
|
393
|
+
}
|
394
|
+
|
395
|
+
return inputValue;
|
396
|
+
}
|
397
|
+
|
398
|
+
function isPojo(input) {
|
399
|
+
if (!isPlainObject(input)) {
|
400
|
+
return false;
|
401
|
+
}
|
402
|
+
|
403
|
+
try {
|
404
|
+
const name = (Object.getPrototypeOf(input) ).constructor.name;
|
405
|
+
return !name || name === 'Object';
|
406
|
+
} catch (e) {
|
407
|
+
return true;
|
408
|
+
}
|
409
|
+
}
|
410
|
+
|
411
|
+
const defaultFunctionName = '<anonymous>';
|
412
|
+
|
413
|
+
/**
|
414
|
+
* Safely extract function name from itself
|
415
|
+
*/
|
416
|
+
function getFunctionName(fn) {
|
417
|
+
try {
|
418
|
+
if (!fn || typeof fn !== 'function') {
|
419
|
+
return defaultFunctionName;
|
420
|
+
}
|
421
|
+
return fn.name || defaultFunctionName;
|
422
|
+
} catch (e) {
|
423
|
+
// Just accessing custom props in some Selenium environments
|
424
|
+
// can cause a "Permission denied" exception (see raven-js#495).
|
425
|
+
return defaultFunctionName;
|
426
|
+
}
|
427
|
+
}
|
428
|
+
|
429
|
+
// We keep the handlers globally
|
430
|
+
const handlers = {};
|
431
|
+
const instrumented = {};
|
432
|
+
|
433
|
+
/** Add a handler function. */
|
434
|
+
function addHandler(type, handler) {
|
435
|
+
handlers[type] = handlers[type] || [];
|
436
|
+
(handlers[type] ).push(handler);
|
437
|
+
}
|
438
|
+
|
439
|
+
/** Maybe run an instrumentation function, unless it was already called. */
|
440
|
+
function maybeInstrument(type, instrumentFn) {
|
441
|
+
if (!instrumented[type]) {
|
442
|
+
instrumentFn();
|
443
|
+
instrumented[type] = true;
|
444
|
+
}
|
445
|
+
}
|
446
|
+
|
447
|
+
/** Trigger handlers for a given instrumentation type. */
|
448
|
+
function triggerHandlers(type, data) {
|
449
|
+
const typeHandlers = type && handlers[type];
|
450
|
+
if (!typeHandlers) {
|
451
|
+
return;
|
452
|
+
}
|
453
|
+
|
454
|
+
for (const handler of typeHandlers) {
|
455
|
+
try {
|
456
|
+
handler(data);
|
457
|
+
} catch (e) {
|
458
|
+
DEBUG_BUILD$2 &&
|
459
|
+
logger.error(
|
460
|
+
`Error while triggering instrumentation handler.\nType: ${type}\nName: ${getFunctionName(handler)}\nError:`,
|
461
|
+
e,
|
462
|
+
);
|
463
|
+
}
|
464
|
+
}
|
465
|
+
}
|
466
|
+
|
467
|
+
/**
|
468
|
+
* Add an instrumentation handler for when a console.xxx method is called.
|
469
|
+
*
|
470
|
+
* Use at your own risk, this might break without changelog notice, only used internally.
|
471
|
+
* @hidden
|
472
|
+
*/
|
473
|
+
function addConsoleInstrumentationHandler(handler) {
|
474
|
+
const type = 'console';
|
475
|
+
addHandler(type, handler);
|
476
|
+
maybeInstrument(type, instrumentConsole);
|
477
|
+
}
|
478
|
+
|
479
|
+
function instrumentConsole() {
|
480
|
+
if (!('console' in GLOBAL_OBJ)) {
|
481
|
+
return;
|
482
|
+
}
|
483
|
+
|
484
|
+
CONSOLE_LEVELS.forEach(function (level) {
|
485
|
+
if (!(level in GLOBAL_OBJ.console)) {
|
486
|
+
return;
|
487
|
+
}
|
488
|
+
|
489
|
+
fill(GLOBAL_OBJ.console, level, function (originalConsoleMethod) {
|
490
|
+
originalConsoleMethods[level] = originalConsoleMethod;
|
491
|
+
|
492
|
+
return function (...args) {
|
493
|
+
const handlerData = { args, level };
|
494
|
+
triggerHandlers('console', handlerData);
|
495
|
+
|
496
|
+
const log = originalConsoleMethods[level];
|
497
|
+
log && log.apply(GLOBAL_OBJ.console, args);
|
498
|
+
};
|
499
|
+
});
|
500
|
+
});
|
501
|
+
}
|
502
|
+
|
503
|
+
/**
|
504
|
+
* UUID4 generator
|
505
|
+
*
|
506
|
+
* @returns string Generated UUID4.
|
507
|
+
*/
|
508
|
+
function uuid4() {
|
509
|
+
const gbl = GLOBAL_OBJ ;
|
510
|
+
const crypto = gbl.crypto || gbl.msCrypto;
|
511
|
+
|
512
|
+
let getRandomByte = () => Math.random() * 16;
|
513
|
+
try {
|
514
|
+
if (crypto && crypto.randomUUID) {
|
515
|
+
return crypto.randomUUID().replace(/-/g, '');
|
516
|
+
}
|
517
|
+
if (crypto && crypto.getRandomValues) {
|
518
|
+
getRandomByte = () => {
|
519
|
+
// crypto.getRandomValues might return undefined instead of the typed array
|
520
|
+
// in old Chromium versions (e.g. 23.0.1235.0 (151422))
|
521
|
+
// However, `typedArray` is still filled in-place.
|
522
|
+
// @see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#typedarray
|
523
|
+
const typedArray = new Uint8Array(1);
|
524
|
+
crypto.getRandomValues(typedArray);
|
525
|
+
return typedArray[0];
|
526
|
+
};
|
527
|
+
}
|
528
|
+
} catch (_) {
|
529
|
+
// some runtimes can crash invoking crypto
|
530
|
+
// https://github.com/getsentry/sentry-javascript/issues/8935
|
531
|
+
}
|
532
|
+
|
533
|
+
// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
|
534
|
+
// Concatenating the following numbers as strings results in '10000000100040008000100000000000'
|
535
|
+
return (([1e7] ) + 1e3 + 4e3 + 8e3 + 1e11).replace(/[018]/g, c =>
|
536
|
+
// eslint-disable-next-line no-bitwise
|
537
|
+
((c ) ^ ((getRandomByte() & 15) >> ((c ) / 4))).toString(16),
|
538
|
+
);
|
539
|
+
}
|
540
|
+
|
541
|
+
function getFirstException(event) {
|
542
|
+
return event.exception && event.exception.values ? event.exception.values[0] : undefined;
|
543
|
+
}
|
544
|
+
|
545
|
+
/**
|
546
|
+
* Adds exception mechanism data to a given event. Uses defaults if the second parameter is not passed.
|
547
|
+
*
|
548
|
+
* @param event The event to modify.
|
549
|
+
* @param newMechanism Mechanism data to add to the event.
|
550
|
+
* @hidden
|
551
|
+
*/
|
552
|
+
function addExceptionMechanism(event, newMechanism) {
|
553
|
+
const firstException = getFirstException(event);
|
554
|
+
if (!firstException) {
|
555
|
+
return;
|
556
|
+
}
|
557
|
+
|
558
|
+
const defaultMechanism = { type: 'generic', handled: true };
|
559
|
+
const currentMechanism = firstException.mechanism;
|
560
|
+
firstException.mechanism = { ...defaultMechanism, ...currentMechanism, ...newMechanism };
|
561
|
+
|
562
|
+
if (newMechanism && 'data' in newMechanism) {
|
563
|
+
const mergedData = { ...(currentMechanism && currentMechanism.data), ...newMechanism.data };
|
564
|
+
firstException.mechanism.data = mergedData;
|
565
|
+
}
|
566
|
+
}
|
567
|
+
|
568
|
+
/**
|
569
|
+
* Checks whether the given input is already an array, and if it isn't, wraps it in one.
|
570
|
+
*
|
571
|
+
* @param maybeArray Input to turn into an array, if necessary
|
572
|
+
* @returns The input, if already an array, or an array with the input as the only element, if not
|
573
|
+
*/
|
574
|
+
function arrayify(maybeArray) {
|
575
|
+
return Array.isArray(maybeArray) ? maybeArray : [maybeArray];
|
576
|
+
}
|
577
|
+
|
578
|
+
// eslint-disable-next-line deprecation/deprecation
|
579
|
+
const WINDOW$1 = getGlobalObject();
|
580
|
+
|
581
|
+
/**
|
582
|
+
* Tells whether current environment supports Fetch API
|
583
|
+
* {@link supportsFetch}.
|
584
|
+
*
|
585
|
+
* @returns Answer to the given question.
|
586
|
+
*/
|
587
|
+
function supportsFetch() {
|
588
|
+
if (!('fetch' in WINDOW$1)) {
|
589
|
+
return false;
|
590
|
+
}
|
591
|
+
|
592
|
+
try {
|
593
|
+
new Headers();
|
594
|
+
new Request('http://www.example.com');
|
595
|
+
new Response();
|
596
|
+
return true;
|
597
|
+
} catch (e) {
|
598
|
+
return false;
|
599
|
+
}
|
600
|
+
}
|
601
|
+
/**
|
602
|
+
* isNativeFetch checks if the given function is a native implementation of fetch()
|
603
|
+
*/
|
604
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
605
|
+
function isNativeFetch(func) {
|
606
|
+
return func && /^function fetch\(\)\s+\{\s+\[native code\]\s+\}$/.test(func.toString());
|
607
|
+
}
|
608
|
+
|
609
|
+
/**
|
610
|
+
* Tells whether current environment supports Fetch API natively
|
611
|
+
* {@link supportsNativeFetch}.
|
612
|
+
*
|
613
|
+
* @returns true if `window.fetch` is natively implemented, false otherwise
|
614
|
+
*/
|
615
|
+
function supportsNativeFetch() {
|
616
|
+
if (typeof EdgeRuntime === 'string') {
|
617
|
+
return true;
|
618
|
+
}
|
619
|
+
|
620
|
+
if (!supportsFetch()) {
|
621
|
+
return false;
|
622
|
+
}
|
623
|
+
|
624
|
+
// Fast path to avoid DOM I/O
|
625
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
626
|
+
if (isNativeFetch(WINDOW$1.fetch)) {
|
627
|
+
return true;
|
628
|
+
}
|
629
|
+
|
630
|
+
// window.fetch is implemented, but is polyfilled or already wrapped (e.g: by a chrome extension)
|
631
|
+
// so create a "pure" iframe to see if that has native fetch
|
632
|
+
let result = false;
|
633
|
+
const doc = WINDOW$1.document;
|
634
|
+
// eslint-disable-next-line deprecation/deprecation
|
635
|
+
if (doc && typeof (doc.createElement ) === 'function') {
|
636
|
+
try {
|
637
|
+
const sandbox = doc.createElement('iframe');
|
638
|
+
sandbox.hidden = true;
|
639
|
+
doc.head.appendChild(sandbox);
|
640
|
+
if (sandbox.contentWindow && sandbox.contentWindow.fetch) {
|
641
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
642
|
+
result = isNativeFetch(sandbox.contentWindow.fetch);
|
643
|
+
}
|
644
|
+
doc.head.removeChild(sandbox);
|
645
|
+
} catch (err) {
|
646
|
+
DEBUG_BUILD$2 &&
|
647
|
+
logger.warn('Could not create sandbox iframe for pure fetch check, bailing to window.fetch: ', err);
|
648
|
+
}
|
649
|
+
}
|
650
|
+
|
651
|
+
return result;
|
652
|
+
}
|
653
|
+
|
654
|
+
/**
|
655
|
+
* Add an instrumentation handler for when a fetch request happens.
|
656
|
+
* The handler function is called once when the request starts and once when it ends,
|
657
|
+
* which can be identified by checking if it has an `endTimestamp`.
|
658
|
+
*
|
659
|
+
* Use at your own risk, this might break without changelog notice, only used internally.
|
660
|
+
* @hidden
|
661
|
+
*/
|
662
|
+
function addFetchInstrumentationHandler(handler) {
|
663
|
+
const type = 'fetch';
|
664
|
+
addHandler(type, handler);
|
665
|
+
maybeInstrument(type, instrumentFetch);
|
666
|
+
}
|
667
|
+
|
668
|
+
function instrumentFetch() {
|
669
|
+
if (!supportsNativeFetch()) {
|
670
|
+
return;
|
671
|
+
}
|
672
|
+
|
673
|
+
fill(GLOBAL_OBJ, 'fetch', function (originalFetch) {
|
674
|
+
return function (...args) {
|
675
|
+
const { method, url } = parseFetchArgs(args);
|
676
|
+
|
677
|
+
const handlerData = {
|
678
|
+
args,
|
679
|
+
fetchData: {
|
680
|
+
method,
|
681
|
+
url,
|
682
|
+
},
|
683
|
+
startTimestamp: Date.now(),
|
684
|
+
};
|
685
|
+
|
686
|
+
triggerHandlers('fetch', {
|
687
|
+
...handlerData,
|
688
|
+
});
|
689
|
+
|
690
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
691
|
+
return originalFetch.apply(GLOBAL_OBJ, args).then(
|
692
|
+
(response) => {
|
693
|
+
const finishedHandlerData = {
|
694
|
+
...handlerData,
|
695
|
+
endTimestamp: Date.now(),
|
696
|
+
response,
|
697
|
+
};
|
698
|
+
|
699
|
+
triggerHandlers('fetch', finishedHandlerData);
|
700
|
+
return response;
|
701
|
+
},
|
702
|
+
(error) => {
|
703
|
+
const erroredHandlerData = {
|
704
|
+
...handlerData,
|
705
|
+
endTimestamp: Date.now(),
|
706
|
+
error,
|
707
|
+
};
|
708
|
+
|
709
|
+
triggerHandlers('fetch', erroredHandlerData);
|
710
|
+
// NOTE: If you are a Sentry user, and you are seeing this stack frame,
|
711
|
+
// it means the sentry.javascript SDK caught an error invoking your application code.
|
712
|
+
// This is expected behavior and NOT indicative of a bug with sentry.javascript.
|
713
|
+
throw error;
|
714
|
+
},
|
715
|
+
);
|
716
|
+
};
|
717
|
+
});
|
718
|
+
}
|
719
|
+
|
720
|
+
function hasProp(obj, prop) {
|
721
|
+
return !!obj && typeof obj === 'object' && !!(obj )[prop];
|
722
|
+
}
|
723
|
+
|
724
|
+
function getUrlFromResource(resource) {
|
725
|
+
if (typeof resource === 'string') {
|
726
|
+
return resource;
|
727
|
+
}
|
728
|
+
|
729
|
+
if (!resource) {
|
730
|
+
return '';
|
731
|
+
}
|
732
|
+
|
733
|
+
if (hasProp(resource, 'url')) {
|
734
|
+
return resource.url;
|
735
|
+
}
|
736
|
+
|
737
|
+
if (resource.toString) {
|
738
|
+
return resource.toString();
|
739
|
+
}
|
740
|
+
|
741
|
+
return '';
|
742
|
+
}
|
743
|
+
|
744
|
+
/**
|
745
|
+
* Parses the fetch arguments to find the used Http method and the url of the request.
|
746
|
+
* Exported for tests only.
|
747
|
+
*/
|
748
|
+
function parseFetchArgs(fetchArgs) {
|
749
|
+
if (fetchArgs.length === 0) {
|
750
|
+
return { method: 'GET', url: '' };
|
751
|
+
}
|
752
|
+
|
753
|
+
if (fetchArgs.length === 2) {
|
754
|
+
const [url, options] = fetchArgs ;
|
755
|
+
|
756
|
+
return {
|
757
|
+
url: getUrlFromResource(url),
|
758
|
+
method: hasProp(options, 'method') ? String(options.method).toUpperCase() : 'GET',
|
759
|
+
};
|
760
|
+
}
|
761
|
+
|
762
|
+
const arg = fetchArgs[0];
|
763
|
+
return {
|
764
|
+
url: getUrlFromResource(arg ),
|
765
|
+
method: hasProp(arg, 'method') ? String(arg.method).toUpperCase() : 'GET',
|
766
|
+
};
|
767
|
+
}
|
768
|
+
|
769
|
+
const WINDOW = GLOBAL_OBJ ;
|
770
|
+
|
771
|
+
const SENTRY_XHR_DATA_KEY = '__sentry_xhr_v3__';
|
772
|
+
|
773
|
+
/**
|
774
|
+
* Add an instrumentation handler for when an XHR request happens.
|
775
|
+
* The handler function is called once when the request starts and once when it ends,
|
776
|
+
* which can be identified by checking if it has an `endTimestamp`.
|
777
|
+
*
|
778
|
+
* Use at your own risk, this might break without changelog notice, only used internally.
|
779
|
+
* @hidden
|
780
|
+
*/
|
781
|
+
function addXhrInstrumentationHandler(handler) {
|
782
|
+
const type = 'xhr';
|
783
|
+
addHandler(type, handler);
|
784
|
+
maybeInstrument(type, instrumentXHR);
|
785
|
+
}
|
786
|
+
|
787
|
+
/** Exported only for tests. */
|
788
|
+
function instrumentXHR() {
|
789
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
790
|
+
if (!(WINDOW ).XMLHttpRequest) {
|
791
|
+
return;
|
792
|
+
}
|
793
|
+
|
794
|
+
const xhrproto = XMLHttpRequest.prototype;
|
795
|
+
|
796
|
+
fill(xhrproto, 'open', function (originalOpen) {
|
797
|
+
return function ( ...args) {
|
798
|
+
const startTimestamp = Date.now();
|
799
|
+
|
800
|
+
// open() should always be called with two or more arguments
|
801
|
+
// But to be on the safe side, we actually validate this and bail out if we don't have a method & url
|
802
|
+
const method = isString(args[0]) ? args[0].toUpperCase() : undefined;
|
803
|
+
const url = parseUrl(args[1]);
|
804
|
+
|
805
|
+
if (!method || !url) {
|
806
|
+
return originalOpen.apply(this, args);
|
807
|
+
}
|
808
|
+
|
809
|
+
this[SENTRY_XHR_DATA_KEY] = {
|
810
|
+
method,
|
811
|
+
url,
|
812
|
+
request_headers: {},
|
813
|
+
};
|
814
|
+
|
815
|
+
// if Sentry key appears in URL, don't capture it as a request
|
816
|
+
if (method === 'POST' && url.match(/sentry_key/)) {
|
817
|
+
this.__sentry_own_request__ = true;
|
818
|
+
}
|
819
|
+
|
820
|
+
const onreadystatechangeHandler = () => {
|
821
|
+
// For whatever reason, this is not the same instance here as from the outer method
|
822
|
+
const xhrInfo = this[SENTRY_XHR_DATA_KEY];
|
823
|
+
|
824
|
+
if (!xhrInfo) {
|
825
|
+
return;
|
826
|
+
}
|
827
|
+
|
828
|
+
if (this.readyState === 4) {
|
829
|
+
try {
|
830
|
+
// touching statusCode in some platforms throws
|
831
|
+
// an exception
|
832
|
+
xhrInfo.status_code = this.status;
|
833
|
+
} catch (e) {
|
834
|
+
/* do nothing */
|
835
|
+
}
|
836
|
+
|
837
|
+
const handlerData = {
|
838
|
+
args: [method, url],
|
839
|
+
endTimestamp: Date.now(),
|
840
|
+
startTimestamp,
|
841
|
+
xhr: this,
|
842
|
+
};
|
843
|
+
triggerHandlers('xhr', handlerData);
|
844
|
+
}
|
845
|
+
};
|
846
|
+
|
847
|
+
if ('onreadystatechange' in this && typeof this.onreadystatechange === 'function') {
|
848
|
+
fill(this, 'onreadystatechange', function (original) {
|
849
|
+
return function ( ...readyStateArgs) {
|
850
|
+
onreadystatechangeHandler();
|
851
|
+
return original.apply(this, readyStateArgs);
|
852
|
+
};
|
853
|
+
});
|
854
|
+
} else {
|
855
|
+
this.addEventListener('readystatechange', onreadystatechangeHandler);
|
856
|
+
}
|
857
|
+
|
858
|
+
// Intercepting `setRequestHeader` to access the request headers of XHR instance.
|
859
|
+
// This will only work for user/library defined headers, not for the default/browser-assigned headers.
|
860
|
+
// Request cookies are also unavailable for XHR, as `Cookie` header can't be defined by `setRequestHeader`.
|
861
|
+
fill(this, 'setRequestHeader', function (original) {
|
862
|
+
return function ( ...setRequestHeaderArgs) {
|
863
|
+
const [header, value] = setRequestHeaderArgs;
|
864
|
+
|
865
|
+
const xhrInfo = this[SENTRY_XHR_DATA_KEY];
|
866
|
+
|
867
|
+
if (xhrInfo && isString(header) && isString(value)) {
|
868
|
+
xhrInfo.request_headers[header.toLowerCase()] = value;
|
869
|
+
}
|
870
|
+
|
871
|
+
return original.apply(this, setRequestHeaderArgs);
|
872
|
+
};
|
873
|
+
});
|
874
|
+
|
875
|
+
return originalOpen.apply(this, args);
|
876
|
+
};
|
877
|
+
});
|
878
|
+
|
879
|
+
fill(xhrproto, 'send', function (originalSend) {
|
880
|
+
return function ( ...args) {
|
881
|
+
const sentryXhrData = this[SENTRY_XHR_DATA_KEY];
|
882
|
+
|
883
|
+
if (!sentryXhrData) {
|
884
|
+
return originalSend.apply(this, args);
|
885
|
+
}
|
886
|
+
|
887
|
+
if (args[0] !== undefined) {
|
888
|
+
sentryXhrData.body = args[0];
|
889
|
+
}
|
890
|
+
|
891
|
+
const handlerData = {
|
892
|
+
args: [sentryXhrData.method, sentryXhrData.url],
|
893
|
+
startTimestamp: Date.now(),
|
894
|
+
xhr: this,
|
895
|
+
};
|
896
|
+
triggerHandlers('xhr', handlerData);
|
897
|
+
|
898
|
+
return originalSend.apply(this, args);
|
899
|
+
};
|
900
|
+
});
|
901
|
+
}
|
902
|
+
|
903
|
+
function parseUrl(url) {
|
904
|
+
if (isString(url)) {
|
905
|
+
return url;
|
906
|
+
}
|
907
|
+
|
908
|
+
try {
|
909
|
+
// url can be a string or URL
|
910
|
+
// but since URL is not available in IE11, we do not check for it,
|
911
|
+
// but simply assume it is an URL and return `toString()` from it (which returns the full URL)
|
912
|
+
// If that fails, we just return undefined
|
913
|
+
return (url ).toString();
|
914
|
+
} catch (e2) {} // eslint-disable-line no-empty
|
915
|
+
|
916
|
+
return undefined;
|
917
|
+
}
|
918
|
+
|
919
|
+
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
920
|
+
|
921
|
+
/** SyncPromise internal states */
|
922
|
+
var States; (function (States) {
|
923
|
+
/** Pending */
|
924
|
+
const PENDING = 0; States[States["PENDING"] = PENDING] = "PENDING";
|
925
|
+
/** Resolved / OK */
|
926
|
+
const RESOLVED = 1; States[States["RESOLVED"] = RESOLVED] = "RESOLVED";
|
927
|
+
/** Rejected / Error */
|
928
|
+
const REJECTED = 2; States[States["REJECTED"] = REJECTED] = "REJECTED";
|
929
|
+
})(States || (States = {}));
|
930
|
+
|
931
|
+
/**
|
932
|
+
* Thenable class that behaves like a Promise and follows it's interface
|
933
|
+
* but is not async internally
|
934
|
+
*/
|
935
|
+
class SyncPromise {
|
936
|
+
|
937
|
+
constructor(
|
938
|
+
executor,
|
939
|
+
) {SyncPromise.prototype.__init.call(this);SyncPromise.prototype.__init2.call(this);SyncPromise.prototype.__init3.call(this);SyncPromise.prototype.__init4.call(this);
|
940
|
+
this._state = States.PENDING;
|
941
|
+
this._handlers = [];
|
942
|
+
|
943
|
+
try {
|
944
|
+
executor(this._resolve, this._reject);
|
945
|
+
} catch (e) {
|
946
|
+
this._reject(e);
|
947
|
+
}
|
948
|
+
}
|
949
|
+
|
950
|
+
/** JSDoc */
|
951
|
+
then(
|
952
|
+
onfulfilled,
|
953
|
+
onrejected,
|
954
|
+
) {
|
955
|
+
return new SyncPromise((resolve, reject) => {
|
956
|
+
this._handlers.push([
|
957
|
+
false,
|
958
|
+
result => {
|
959
|
+
if (!onfulfilled) {
|
960
|
+
// TODO: ¯\_(ツ)_/¯
|
961
|
+
// TODO: FIXME
|
962
|
+
resolve(result );
|
963
|
+
} else {
|
964
|
+
try {
|
965
|
+
resolve(onfulfilled(result));
|
966
|
+
} catch (e) {
|
967
|
+
reject(e);
|
968
|
+
}
|
969
|
+
}
|
970
|
+
},
|
971
|
+
reason => {
|
972
|
+
if (!onrejected) {
|
973
|
+
reject(reason);
|
974
|
+
} else {
|
975
|
+
try {
|
976
|
+
resolve(onrejected(reason));
|
977
|
+
} catch (e) {
|
978
|
+
reject(e);
|
979
|
+
}
|
980
|
+
}
|
981
|
+
},
|
982
|
+
]);
|
983
|
+
this._executeHandlers();
|
984
|
+
});
|
985
|
+
}
|
986
|
+
|
987
|
+
/** JSDoc */
|
988
|
+
catch(
|
989
|
+
onrejected,
|
990
|
+
) {
|
991
|
+
return this.then(val => val, onrejected);
|
992
|
+
}
|
993
|
+
|
994
|
+
/** JSDoc */
|
995
|
+
finally(onfinally) {
|
996
|
+
return new SyncPromise((resolve, reject) => {
|
997
|
+
let val;
|
998
|
+
let isRejected;
|
999
|
+
|
1000
|
+
return this.then(
|
1001
|
+
value => {
|
1002
|
+
isRejected = false;
|
1003
|
+
val = value;
|
1004
|
+
if (onfinally) {
|
1005
|
+
onfinally();
|
1006
|
+
}
|
1007
|
+
},
|
1008
|
+
reason => {
|
1009
|
+
isRejected = true;
|
1010
|
+
val = reason;
|
1011
|
+
if (onfinally) {
|
1012
|
+
onfinally();
|
1013
|
+
}
|
1014
|
+
},
|
1015
|
+
).then(() => {
|
1016
|
+
if (isRejected) {
|
1017
|
+
reject(val);
|
1018
|
+
return;
|
1019
|
+
}
|
1020
|
+
|
1021
|
+
resolve(val );
|
1022
|
+
});
|
1023
|
+
});
|
1024
|
+
}
|
1025
|
+
|
1026
|
+
/** JSDoc */
|
1027
|
+
__init() {this._resolve = (value) => {
|
1028
|
+
this._setResult(States.RESOLVED, value);
|
1029
|
+
};}
|
1030
|
+
|
1031
|
+
/** JSDoc */
|
1032
|
+
__init2() {this._reject = (reason) => {
|
1033
|
+
this._setResult(States.REJECTED, reason);
|
1034
|
+
};}
|
1035
|
+
|
1036
|
+
/** JSDoc */
|
1037
|
+
__init3() {this._setResult = (state, value) => {
|
1038
|
+
if (this._state !== States.PENDING) {
|
1039
|
+
return;
|
1040
|
+
}
|
1041
|
+
|
1042
|
+
if (isThenable(value)) {
|
1043
|
+
void (value ).then(this._resolve, this._reject);
|
1044
|
+
return;
|
1045
|
+
}
|
1046
|
+
|
1047
|
+
this._state = state;
|
1048
|
+
this._value = value;
|
1049
|
+
|
1050
|
+
this._executeHandlers();
|
1051
|
+
};}
|
1052
|
+
|
1053
|
+
/** JSDoc */
|
1054
|
+
__init4() {this._executeHandlers = () => {
|
1055
|
+
if (this._state === States.PENDING) {
|
1056
|
+
return;
|
1057
|
+
}
|
1058
|
+
|
1059
|
+
const cachedHandlers = this._handlers.slice();
|
1060
|
+
this._handlers = [];
|
1061
|
+
|
1062
|
+
cachedHandlers.forEach(handler => {
|
1063
|
+
if (handler[0]) {
|
1064
|
+
return;
|
1065
|
+
}
|
1066
|
+
|
1067
|
+
if (this._state === States.RESOLVED) {
|
1068
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
1069
|
+
handler[1](this._value );
|
1070
|
+
}
|
1071
|
+
|
1072
|
+
if (this._state === States.REJECTED) {
|
1073
|
+
handler[2](this._value);
|
1074
|
+
}
|
1075
|
+
|
1076
|
+
handler[0] = true;
|
1077
|
+
});
|
1078
|
+
};}
|
1079
|
+
}
|
1080
|
+
|
1081
|
+
// Note: Ideally the `SeverityLevel` type would be derived from `validSeverityLevels`, but that would mean either
|
1082
|
+
//
|
1083
|
+
// a) moving `validSeverityLevels` to `@sentry/types`,
|
1084
|
+
// b) moving the`SeverityLevel` type here, or
|
1085
|
+
// c) importing `validSeverityLevels` from here into `@sentry/types`.
|
1086
|
+
//
|
1087
|
+
// Option A would make `@sentry/types` a runtime dependency of `@sentry/utils` (not good), and options B and C would
|
1088
|
+
// create a circular dependency between `@sentry/types` and `@sentry/utils` (also not good). So a TODO accompanying the
|
1089
|
+
// type, reminding anyone who changes it to change this list also, will have to do.
|
1090
|
+
|
1091
|
+
const validSeverityLevels = ['fatal', 'error', 'warning', 'log', 'info', 'debug'];
|
1092
|
+
|
1093
|
+
/**
|
1094
|
+
* Converts a string-based level into a `SeverityLevel`, normalizing it along the way.
|
1095
|
+
*
|
1096
|
+
* @param level String representation of desired `SeverityLevel`.
|
1097
|
+
* @returns The `SeverityLevel` corresponding to the given string, or 'log' if the string isn't a valid level.
|
1098
|
+
*/
|
1099
|
+
function severityLevelFromString(level) {
|
1100
|
+
return (level === 'warn' ? 'warning' : validSeverityLevels.includes(level) ? level : 'log') ;
|
1101
|
+
}
|
1102
|
+
|
1103
|
+
const ONE_SECOND_IN_MS = 1000;
|
1104
|
+
|
1105
|
+
/**
|
1106
|
+
* A partial definition of the [Performance Web API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Performance}
|
1107
|
+
* for accessing a high-resolution monotonic clock.
|
1108
|
+
*/
|
1109
|
+
|
1110
|
+
/**
|
1111
|
+
* Returns a timestamp in seconds since the UNIX epoch using the Date API.
|
1112
|
+
*
|
1113
|
+
* TODO(v8): Return type should be rounded.
|
1114
|
+
*/
|
1115
|
+
function dateTimestampInSeconds() {
|
1116
|
+
return Date.now() / ONE_SECOND_IN_MS;
|
1117
|
+
}
|
1118
|
+
|
1119
|
+
/**
|
1120
|
+
* Returns a wrapper around the native Performance API browser implementation, or undefined for browsers that do not
|
1121
|
+
* support the API.
|
1122
|
+
*
|
1123
|
+
* Wrapping the native API works around differences in behavior from different browsers.
|
1124
|
+
*/
|
1125
|
+
function createUnixTimestampInSecondsFunc() {
|
1126
|
+
const { performance } = GLOBAL_OBJ ;
|
1127
|
+
if (!performance || !performance.now) {
|
1128
|
+
return dateTimestampInSeconds;
|
1129
|
+
}
|
1130
|
+
|
1131
|
+
// Some browser and environments don't have a timeOrigin, so we fallback to
|
1132
|
+
// using Date.now() to compute the starting time.
|
1133
|
+
const approxStartingTimeOrigin = Date.now() - performance.now();
|
1134
|
+
const timeOrigin = performance.timeOrigin == undefined ? approxStartingTimeOrigin : performance.timeOrigin;
|
1135
|
+
|
1136
|
+
// performance.now() is a monotonic clock, which means it starts at 0 when the process begins. To get the current
|
1137
|
+
// wall clock time (actual UNIX timestamp), we need to add the starting time origin and the current time elapsed.
|
1138
|
+
//
|
1139
|
+
// TODO: This does not account for the case where the monotonic clock that powers performance.now() drifts from the
|
1140
|
+
// wall clock time, which causes the returned timestamp to be inaccurate. We should investigate how to detect and
|
1141
|
+
// correct for this.
|
1142
|
+
// See: https://github.com/getsentry/sentry-javascript/issues/2590
|
1143
|
+
// See: https://github.com/mdn/content/issues/4713
|
1144
|
+
// See: https://dev.to/noamr/when-a-millisecond-is-not-a-millisecond-3h6
|
1145
|
+
return () => {
|
1146
|
+
return (timeOrigin + performance.now()) / ONE_SECOND_IN_MS;
|
1147
|
+
};
|
1148
|
+
}
|
1149
|
+
|
1150
|
+
/**
|
1151
|
+
* Returns a timestamp in seconds since the UNIX epoch using either the Performance or Date APIs, depending on the
|
1152
|
+
* availability of the Performance API.
|
1153
|
+
*
|
1154
|
+
* BUG: Note that because of how browsers implement the Performance API, the clock might stop when the computer is
|
1155
|
+
* asleep. This creates a skew between `dateTimestampInSeconds` and `timestampInSeconds`. The
|
1156
|
+
* skew can grow to arbitrary amounts like days, weeks or months.
|
1157
|
+
* See https://github.com/getsentry/sentry-javascript/issues/2590.
|
1158
|
+
*/
|
1159
|
+
const timestampInSeconds = createUnixTimestampInSecondsFunc();
|
1160
|
+
|
1161
|
+
/**
|
1162
|
+
* The number of milliseconds since the UNIX epoch. This value is only usable in a browser, and only when the
|
1163
|
+
* performance API is available.
|
1164
|
+
*/
|
1165
|
+
(() => {
|
1166
|
+
// Unfortunately browsers may report an inaccurate time origin data, through either performance.timeOrigin or
|
1167
|
+
// performance.timing.navigationStart, which results in poor results in performance data. We only treat time origin
|
1168
|
+
// data as reliable if they are within a reasonable threshold of the current time.
|
1169
|
+
|
1170
|
+
const { performance } = GLOBAL_OBJ ;
|
1171
|
+
if (!performance || !performance.now) {
|
1172
|
+
return undefined;
|
1173
|
+
}
|
1174
|
+
|
1175
|
+
const threshold = 3600 * 1000;
|
1176
|
+
const performanceNow = performance.now();
|
1177
|
+
const dateNow = Date.now();
|
1178
|
+
|
1179
|
+
// if timeOrigin isn't available set delta to threshold so it isn't used
|
1180
|
+
const timeOriginDelta = performance.timeOrigin
|
1181
|
+
? Math.abs(performance.timeOrigin + performanceNow - dateNow)
|
1182
|
+
: threshold;
|
1183
|
+
const timeOriginIsReliable = timeOriginDelta < threshold;
|
1184
|
+
|
1185
|
+
// While performance.timing.navigationStart is deprecated in favor of performance.timeOrigin, performance.timeOrigin
|
1186
|
+
// is not as widely supported. Namely, performance.timeOrigin is undefined in Safari as of writing.
|
1187
|
+
// Also as of writing, performance.timing is not available in Web Workers in mainstream browsers, so it is not always
|
1188
|
+
// a valid fallback. In the absence of an initial time provided by the browser, fallback to the current time from the
|
1189
|
+
// Date API.
|
1190
|
+
// eslint-disable-next-line deprecation/deprecation
|
1191
|
+
const navigationStart = performance.timing && performance.timing.navigationStart;
|
1192
|
+
const hasNavigationStart = typeof navigationStart === 'number';
|
1193
|
+
// if navigationStart isn't available set delta to threshold so it isn't used
|
1194
|
+
const navigationStartDelta = hasNavigationStart ? Math.abs(navigationStart + performanceNow - dateNow) : threshold;
|
1195
|
+
const navigationStartIsReliable = navigationStartDelta < threshold;
|
1196
|
+
|
1197
|
+
if (timeOriginIsReliable || navigationStartIsReliable) {
|
1198
|
+
// Use the more reliable time origin
|
1199
|
+
if (timeOriginDelta <= navigationStartDelta) {
|
1200
|
+
return performance.timeOrigin;
|
1201
|
+
} else {
|
1202
|
+
return navigationStart;
|
1203
|
+
}
|
1204
|
+
}
|
1205
|
+
return dateNow;
|
1206
|
+
})();
|
1207
|
+
|
1208
|
+
/**
|
1209
|
+
* 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.
|
1210
|
+
*
|
1211
|
+
* ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking.
|
1212
|
+
*/
|
1213
|
+
const DEBUG_BUILD$1 = (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__);
|
1214
|
+
|
1215
|
+
const DEFAULT_ENVIRONMENT = 'production';
|
1216
|
+
|
1217
|
+
/**
|
1218
|
+
* Returns the global event processors.
|
1219
|
+
* @deprecated Global event processors will be removed in v8.
|
1220
|
+
*/
|
1221
|
+
function getGlobalEventProcessors() {
|
1222
|
+
return getGlobalSingleton('globalEventProcessors', () => []);
|
1223
|
+
}
|
1224
|
+
|
1225
|
+
/**
|
1226
|
+
* Process an array of event processors, returning the processed event (or `null` if the event was dropped).
|
1227
|
+
*/
|
1228
|
+
function notifyEventProcessors(
|
1229
|
+
processors,
|
1230
|
+
event,
|
1231
|
+
hint,
|
1232
|
+
index = 0,
|
1233
|
+
) {
|
1234
|
+
return new SyncPromise((resolve, reject) => {
|
1235
|
+
const processor = processors[index];
|
1236
|
+
if (event === null || typeof processor !== 'function') {
|
1237
|
+
resolve(event);
|
1238
|
+
} else {
|
1239
|
+
const result = processor({ ...event }, hint) ;
|
1240
|
+
|
1241
|
+
DEBUG_BUILD$1 && processor.id && result === null && logger.log(`Event processor "${processor.id}" dropped event`);
|
1242
|
+
|
1243
|
+
if (isThenable(result)) {
|
1244
|
+
void result
|
1245
|
+
.then(final => notifyEventProcessors(processors, final, hint, index + 1).then(resolve))
|
1246
|
+
.then(null, reject);
|
1247
|
+
} else {
|
1248
|
+
void notifyEventProcessors(processors, result, hint, index + 1)
|
1249
|
+
.then(resolve)
|
1250
|
+
.then(null, reject);
|
1251
|
+
}
|
1252
|
+
}
|
1253
|
+
});
|
1254
|
+
}
|
1255
|
+
|
1256
|
+
/**
|
1257
|
+
* Creates a new `Session` object by setting certain default parameters. If optional @param context
|
1258
|
+
* is passed, the passed properties are applied to the session object.
|
1259
|
+
*
|
1260
|
+
* @param context (optional) additional properties to be applied to the returned session object
|
1261
|
+
*
|
1262
|
+
* @returns a new `Session` object
|
1263
|
+
*/
|
1264
|
+
function makeSession(context) {
|
1265
|
+
// Both timestamp and started are in seconds since the UNIX epoch.
|
1266
|
+
const startingTime = timestampInSeconds();
|
1267
|
+
|
1268
|
+
const session = {
|
1269
|
+
sid: uuid4(),
|
1270
|
+
init: true,
|
1271
|
+
timestamp: startingTime,
|
1272
|
+
started: startingTime,
|
1273
|
+
duration: 0,
|
1274
|
+
status: 'ok',
|
1275
|
+
errors: 0,
|
1276
|
+
ignoreDuration: false,
|
1277
|
+
toJSON: () => sessionToJSON(session),
|
1278
|
+
};
|
1279
|
+
|
1280
|
+
if (context) {
|
1281
|
+
updateSession(session, context);
|
1282
|
+
}
|
1283
|
+
|
1284
|
+
return session;
|
1285
|
+
}
|
1286
|
+
|
1287
|
+
/**
|
1288
|
+
* Updates a session object with the properties passed in the context.
|
1289
|
+
*
|
1290
|
+
* Note that this function mutates the passed object and returns void.
|
1291
|
+
* (Had to do this instead of returning a new and updated session because closing and sending a session
|
1292
|
+
* makes an update to the session after it was passed to the sending logic.
|
1293
|
+
* @see BaseClient.captureSession )
|
1294
|
+
*
|
1295
|
+
* @param session the `Session` to update
|
1296
|
+
* @param context the `SessionContext` holding the properties that should be updated in @param session
|
1297
|
+
*/
|
1298
|
+
// eslint-disable-next-line complexity
|
1299
|
+
function updateSession(session, context = {}) {
|
1300
|
+
if (context.user) {
|
1301
|
+
if (!session.ipAddress && context.user.ip_address) {
|
1302
|
+
session.ipAddress = context.user.ip_address;
|
1303
|
+
}
|
1304
|
+
|
1305
|
+
if (!session.did && !context.did) {
|
1306
|
+
session.did = context.user.id || context.user.email || context.user.username;
|
1307
|
+
}
|
1308
|
+
}
|
1309
|
+
|
1310
|
+
session.timestamp = context.timestamp || timestampInSeconds();
|
1311
|
+
|
1312
|
+
if (context.abnormal_mechanism) {
|
1313
|
+
session.abnormal_mechanism = context.abnormal_mechanism;
|
1314
|
+
}
|
1315
|
+
|
1316
|
+
if (context.ignoreDuration) {
|
1317
|
+
session.ignoreDuration = context.ignoreDuration;
|
1318
|
+
}
|
1319
|
+
if (context.sid) {
|
1320
|
+
// Good enough uuid validation. — Kamil
|
1321
|
+
session.sid = context.sid.length === 32 ? context.sid : uuid4();
|
1322
|
+
}
|
1323
|
+
if (context.init !== undefined) {
|
1324
|
+
session.init = context.init;
|
1325
|
+
}
|
1326
|
+
if (!session.did && context.did) {
|
1327
|
+
session.did = `${context.did}`;
|
1328
|
+
}
|
1329
|
+
if (typeof context.started === 'number') {
|
1330
|
+
session.started = context.started;
|
1331
|
+
}
|
1332
|
+
if (session.ignoreDuration) {
|
1333
|
+
session.duration = undefined;
|
1334
|
+
} else if (typeof context.duration === 'number') {
|
1335
|
+
session.duration = context.duration;
|
1336
|
+
} else {
|
1337
|
+
const duration = session.timestamp - session.started;
|
1338
|
+
session.duration = duration >= 0 ? duration : 0;
|
1339
|
+
}
|
1340
|
+
if (context.release) {
|
1341
|
+
session.release = context.release;
|
1342
|
+
}
|
1343
|
+
if (context.environment) {
|
1344
|
+
session.environment = context.environment;
|
1345
|
+
}
|
1346
|
+
if (!session.ipAddress && context.ipAddress) {
|
1347
|
+
session.ipAddress = context.ipAddress;
|
1348
|
+
}
|
1349
|
+
if (!session.userAgent && context.userAgent) {
|
1350
|
+
session.userAgent = context.userAgent;
|
1351
|
+
}
|
1352
|
+
if (typeof context.errors === 'number') {
|
1353
|
+
session.errors = context.errors;
|
1354
|
+
}
|
1355
|
+
if (context.status) {
|
1356
|
+
session.status = context.status;
|
1357
|
+
}
|
1358
|
+
}
|
1359
|
+
|
1360
|
+
/**
|
1361
|
+
* Closes a session by setting its status and updating the session object with it.
|
1362
|
+
* Internally calls `updateSession` to update the passed session object.
|
1363
|
+
*
|
1364
|
+
* Note that this function mutates the passed session (@see updateSession for explanation).
|
1365
|
+
*
|
1366
|
+
* @param session the `Session` object to be closed
|
1367
|
+
* @param status the `SessionStatus` with which the session was closed. If you don't pass a status,
|
1368
|
+
* this function will keep the previously set status, unless it was `'ok'` in which case
|
1369
|
+
* it is changed to `'exited'`.
|
1370
|
+
*/
|
1371
|
+
function closeSession(session, status) {
|
1372
|
+
let context = {};
|
1373
|
+
if (session.status === 'ok') {
|
1374
|
+
context = { status: 'exited' };
|
1375
|
+
}
|
1376
|
+
|
1377
|
+
updateSession(session, context);
|
1378
|
+
}
|
1379
|
+
|
1380
|
+
/**
|
1381
|
+
* Serializes a passed session object to a JSON object with a slightly different structure.
|
1382
|
+
* This is necessary because the Sentry backend requires a slightly different schema of a session
|
1383
|
+
* than the one the JS SDKs use internally.
|
1384
|
+
*
|
1385
|
+
* @param session the session to be converted
|
1386
|
+
*
|
1387
|
+
* @returns a JSON object of the passed session
|
1388
|
+
*/
|
1389
|
+
function sessionToJSON(session) {
|
1390
|
+
return dropUndefinedKeys({
|
1391
|
+
sid: `${session.sid}`,
|
1392
|
+
init: session.init,
|
1393
|
+
// Make sure that sec is converted to ms for date constructor
|
1394
|
+
started: new Date(session.started * 1000).toISOString(),
|
1395
|
+
timestamp: new Date(session.timestamp * 1000).toISOString(),
|
1396
|
+
status: session.status,
|
1397
|
+
errors: session.errors,
|
1398
|
+
did: typeof session.did === 'number' || typeof session.did === 'string' ? `${session.did}` : undefined,
|
1399
|
+
duration: session.duration,
|
1400
|
+
abnormal_mechanism: session.abnormal_mechanism,
|
1401
|
+
attrs: {
|
1402
|
+
release: session.release,
|
1403
|
+
environment: session.environment,
|
1404
|
+
ip_address: session.ipAddress,
|
1405
|
+
user_agent: session.userAgent,
|
1406
|
+
},
|
1407
|
+
});
|
1408
|
+
}
|
1409
|
+
|
1410
|
+
const TRACE_FLAG_SAMPLED = 0x1;
|
1411
|
+
|
1412
|
+
/**
|
1413
|
+
* Convert a span to a trace context, which can be sent as the `trace` context in an event.
|
1414
|
+
*/
|
1415
|
+
function spanToTraceContext(span) {
|
1416
|
+
const { spanId: span_id, traceId: trace_id } = span.spanContext();
|
1417
|
+
const { data, op, parent_span_id, status, tags, origin } = spanToJSON(span);
|
1418
|
+
|
1419
|
+
return dropUndefinedKeys({
|
1420
|
+
data,
|
1421
|
+
op,
|
1422
|
+
parent_span_id,
|
1423
|
+
span_id,
|
1424
|
+
status,
|
1425
|
+
tags,
|
1426
|
+
trace_id,
|
1427
|
+
origin,
|
1428
|
+
});
|
1429
|
+
}
|
1430
|
+
|
1431
|
+
/**
|
1432
|
+
* Convert a span to a JSON representation.
|
1433
|
+
* Note that all fields returned here are optional and need to be guarded against.
|
1434
|
+
*
|
1435
|
+
* Note: Because of this, we currently have a circular type dependency (which we opted out of in package.json).
|
1436
|
+
* This is not avoidable as we need `spanToJSON` in `spanUtils.ts`, which in turn is needed by `span.ts` for backwards compatibility.
|
1437
|
+
* And `spanToJSON` needs the Span class from `span.ts` to check here.
|
1438
|
+
* TODO v8: When we remove the deprecated stuff from `span.ts`, we can remove the circular dependency again.
|
1439
|
+
*/
|
1440
|
+
function spanToJSON(span) {
|
1441
|
+
if (spanIsSpanClass(span)) {
|
1442
|
+
return span.getSpanJSON();
|
1443
|
+
}
|
1444
|
+
|
1445
|
+
// Fallback: We also check for `.toJSON()` here...
|
1446
|
+
// eslint-disable-next-line deprecation/deprecation
|
1447
|
+
if (typeof span.toJSON === 'function') {
|
1448
|
+
// eslint-disable-next-line deprecation/deprecation
|
1449
|
+
return span.toJSON();
|
1450
|
+
}
|
1451
|
+
|
1452
|
+
return {};
|
1453
|
+
}
|
1454
|
+
|
1455
|
+
/**
|
1456
|
+
* Sadly, due to circular dependency checks we cannot actually import the Span class here and check for instanceof.
|
1457
|
+
* :( So instead we approximate this by checking if it has the `getSpanJSON` method.
|
1458
|
+
*/
|
1459
|
+
function spanIsSpanClass(span) {
|
1460
|
+
return typeof (span ).getSpanJSON === 'function';
|
1461
|
+
}
|
1462
|
+
|
1463
|
+
/**
|
1464
|
+
* Returns true if a span is sampled.
|
1465
|
+
* In most cases, you should just use `span.isRecording()` instead.
|
1466
|
+
* However, this has a slightly different semantic, as it also returns false if the span is finished.
|
1467
|
+
* So in the case where this distinction is important, use this method.
|
1468
|
+
*/
|
1469
|
+
function spanIsSampled(span) {
|
1470
|
+
// We align our trace flags with the ones OpenTelemetry use
|
1471
|
+
// So we also check for sampled the same way they do.
|
1472
|
+
const { traceFlags } = span.spanContext();
|
1473
|
+
// eslint-disable-next-line no-bitwise
|
1474
|
+
return Boolean(traceFlags & TRACE_FLAG_SAMPLED);
|
1475
|
+
}
|
1476
|
+
|
1477
|
+
/**
|
1478
|
+
* Parse either an `EventHint` directly, or convert a `CaptureContext` to an `EventHint`.
|
1479
|
+
* This is used to allow to update method signatures that used to accept a `CaptureContext` but should now accept an `EventHint`.
|
1480
|
+
*/
|
1481
|
+
function parseEventHintOrCaptureContext(
|
1482
|
+
hint,
|
1483
|
+
) {
|
1484
|
+
if (!hint) {
|
1485
|
+
return undefined;
|
1486
|
+
}
|
1487
|
+
|
1488
|
+
// If you pass a Scope or `() => Scope` as CaptureContext, we just return this as captureContext
|
1489
|
+
if (hintIsScopeOrFunction(hint)) {
|
1490
|
+
return { captureContext: hint };
|
1491
|
+
}
|
1492
|
+
|
1493
|
+
if (hintIsScopeContext(hint)) {
|
1494
|
+
return {
|
1495
|
+
captureContext: hint,
|
1496
|
+
};
|
1497
|
+
}
|
1498
|
+
|
1499
|
+
return hint;
|
1500
|
+
}
|
1501
|
+
|
1502
|
+
function hintIsScopeOrFunction(
|
1503
|
+
hint,
|
1504
|
+
) {
|
1505
|
+
return hint instanceof Scope || typeof hint === 'function';
|
1506
|
+
}
|
1507
|
+
|
1508
|
+
const captureContextKeys = [
|
1509
|
+
'user',
|
1510
|
+
'level',
|
1511
|
+
'extra',
|
1512
|
+
'contexts',
|
1513
|
+
'tags',
|
1514
|
+
'fingerprint',
|
1515
|
+
'requestSession',
|
1516
|
+
'propagationContext',
|
1517
|
+
] ;
|
1518
|
+
|
1519
|
+
function hintIsScopeContext(hint) {
|
1520
|
+
return Object.keys(hint).some(key => captureContextKeys.includes(key ));
|
1521
|
+
}
|
1522
|
+
|
1523
|
+
/**
|
1524
|
+
* Captures an exception event and sends it to Sentry.
|
1525
|
+
*
|
1526
|
+
* @param exception The exception to capture.
|
1527
|
+
* @param hint Optional additional data to attach to the Sentry event.
|
1528
|
+
* @returns the id of the captured Sentry event.
|
1529
|
+
*/
|
1530
|
+
function captureException(
|
1531
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
1532
|
+
exception,
|
1533
|
+
hint,
|
1534
|
+
) {
|
1535
|
+
// eslint-disable-next-line deprecation/deprecation
|
1536
|
+
return getCurrentHub().captureException(exception, parseEventHintOrCaptureContext(hint));
|
1537
|
+
}
|
1538
|
+
|
1539
|
+
/**
|
1540
|
+
* Captures a message event and sends it to Sentry.
|
1541
|
+
*
|
1542
|
+
* @param exception The exception to capture.
|
1543
|
+
* @param captureContext Define the level of the message or pass in additional data to attach to the message.
|
1544
|
+
* @returns the id of the captured message.
|
1545
|
+
*/
|
1546
|
+
function captureMessage(
|
1547
|
+
message,
|
1548
|
+
// eslint-disable-next-line deprecation/deprecation
|
1549
|
+
captureContext,
|
1550
|
+
) {
|
1551
|
+
// This is necessary to provide explicit scopes upgrade, without changing the original
|
1552
|
+
// arity of the `captureMessage(message, level)` method.
|
1553
|
+
const level = typeof captureContext === 'string' ? captureContext : undefined;
|
1554
|
+
const context = typeof captureContext !== 'string' ? { captureContext } : undefined;
|
1555
|
+
// eslint-disable-next-line deprecation/deprecation
|
1556
|
+
return getCurrentHub().captureMessage(message, level, context);
|
1557
|
+
}
|
1558
|
+
|
1559
|
+
/**
|
1560
|
+
* Captures a manually created event and sends it to Sentry.
|
1561
|
+
*
|
1562
|
+
* @param exception The event to send to Sentry.
|
1563
|
+
* @param hint Optional additional data to attach to the Sentry event.
|
1564
|
+
* @returns the id of the captured event.
|
1565
|
+
*/
|
1566
|
+
function captureEvent(event, hint) {
|
1567
|
+
// eslint-disable-next-line deprecation/deprecation
|
1568
|
+
return getCurrentHub().captureEvent(event, hint);
|
1569
|
+
}
|
1570
|
+
|
1571
|
+
/**
|
1572
|
+
* Creates a new scope with and executes the given operation within.
|
1573
|
+
* The scope is automatically removed once the operation
|
1574
|
+
* finishes or throws.
|
1575
|
+
*
|
1576
|
+
* This is essentially a convenience function for:
|
1577
|
+
*
|
1578
|
+
* pushScope();
|
1579
|
+
* callback();
|
1580
|
+
* popScope();
|
1581
|
+
*/
|
1582
|
+
|
1583
|
+
/**
|
1584
|
+
* Either creates a new active scope, or sets the given scope as active scope in the given callback.
|
1585
|
+
*/
|
1586
|
+
function withScope(
|
1587
|
+
...rest
|
1588
|
+
) {
|
1589
|
+
// eslint-disable-next-line deprecation/deprecation
|
1590
|
+
const hub = getCurrentHub();
|
1591
|
+
|
1592
|
+
// If a scope is defined, we want to make this the active scope instead of the default one
|
1593
|
+
if (rest.length === 2) {
|
1594
|
+
const [scope, callback] = rest;
|
1595
|
+
if (!scope) {
|
1596
|
+
// eslint-disable-next-line deprecation/deprecation
|
1597
|
+
return hub.withScope(callback);
|
1598
|
+
}
|
1599
|
+
|
1600
|
+
// eslint-disable-next-line deprecation/deprecation
|
1601
|
+
return hub.withScope(() => {
|
1602
|
+
// eslint-disable-next-line deprecation/deprecation
|
1603
|
+
hub.getStackTop().scope = scope ;
|
1604
|
+
return callback(scope );
|
1605
|
+
});
|
1606
|
+
}
|
1607
|
+
|
1608
|
+
// eslint-disable-next-line deprecation/deprecation
|
1609
|
+
return hub.withScope(rest[0]);
|
1610
|
+
}
|
1611
|
+
|
1612
|
+
/**
|
1613
|
+
* Get the currently active client.
|
1614
|
+
*/
|
1615
|
+
function getClient() {
|
1616
|
+
// eslint-disable-next-line deprecation/deprecation
|
1617
|
+
return getCurrentHub().getClient();
|
1618
|
+
}
|
1619
|
+
|
1620
|
+
/**
|
1621
|
+
* Get the currently active scope.
|
1622
|
+
*/
|
1623
|
+
function getCurrentScope() {
|
1624
|
+
// eslint-disable-next-line deprecation/deprecation
|
1625
|
+
return getCurrentHub().getScope();
|
1626
|
+
}
|
1627
|
+
|
1628
|
+
/**
|
1629
|
+
* Returns the root span of a given span.
|
1630
|
+
*
|
1631
|
+
* As long as we use `Transaction`s internally, the returned root span
|
1632
|
+
* will be a `Transaction` but be aware that this might change in the future.
|
1633
|
+
*
|
1634
|
+
* If the given span has no root span or transaction, `undefined` is returned.
|
1635
|
+
*/
|
1636
|
+
function getRootSpan(span) {
|
1637
|
+
// TODO (v8): Remove this check and just return span
|
1638
|
+
// eslint-disable-next-line deprecation/deprecation
|
1639
|
+
return span.transaction;
|
1640
|
+
}
|
1641
|
+
|
1642
|
+
/**
|
1643
|
+
* Creates a dynamic sampling context from a client.
|
1644
|
+
*
|
1645
|
+
* Dispatches the `createDsc` lifecycle hook as a side effect.
|
1646
|
+
*/
|
1647
|
+
function getDynamicSamplingContextFromClient(
|
1648
|
+
trace_id,
|
1649
|
+
client,
|
1650
|
+
scope,
|
1651
|
+
) {
|
1652
|
+
const options = client.getOptions();
|
1653
|
+
|
1654
|
+
const { publicKey: public_key } = client.getDsn() || {};
|
1655
|
+
// TODO(v8): Remove segment from User
|
1656
|
+
// eslint-disable-next-line deprecation/deprecation
|
1657
|
+
const { segment: user_segment } = (scope && scope.getUser()) || {};
|
1658
|
+
|
1659
|
+
const dsc = dropUndefinedKeys({
|
1660
|
+
environment: options.environment || DEFAULT_ENVIRONMENT,
|
1661
|
+
release: options.release,
|
1662
|
+
user_segment,
|
1663
|
+
public_key,
|
1664
|
+
trace_id,
|
1665
|
+
}) ;
|
1666
|
+
|
1667
|
+
client.emit && client.emit('createDsc', dsc);
|
1668
|
+
|
1669
|
+
return dsc;
|
1670
|
+
}
|
1671
|
+
|
1672
|
+
/**
|
1673
|
+
* A Span with a frozen dynamic sampling context.
|
1674
|
+
*/
|
1675
|
+
|
1676
|
+
/**
|
1677
|
+
* Creates a dynamic sampling context from a span (and client and scope)
|
1678
|
+
*
|
1679
|
+
* @param span the span from which a few values like the root span name and sample rate are extracted.
|
1680
|
+
*
|
1681
|
+
* @returns a dynamic sampling context
|
1682
|
+
*/
|
1683
|
+
function getDynamicSamplingContextFromSpan(span) {
|
1684
|
+
const client = getClient();
|
1685
|
+
if (!client) {
|
1686
|
+
return {};
|
1687
|
+
}
|
1688
|
+
|
1689
|
+
// passing emit=false here to only emit later once the DSC is actually populated
|
1690
|
+
const dsc = getDynamicSamplingContextFromClient(spanToJSON(span).trace_id || '', client, getCurrentScope());
|
1691
|
+
|
1692
|
+
// TODO (v8): Remove v7FrozenDsc as a Transaction will no longer have _frozenDynamicSamplingContext
|
1693
|
+
const txn = getRootSpan(span) ;
|
1694
|
+
if (!txn) {
|
1695
|
+
return dsc;
|
1696
|
+
}
|
1697
|
+
|
1698
|
+
// TODO (v8): Remove v7FrozenDsc as a Transaction will no longer have _frozenDynamicSamplingContext
|
1699
|
+
// For now we need to avoid breaking users who directly created a txn with a DSC, where this field is still set.
|
1700
|
+
// @see Transaction class constructor
|
1701
|
+
const v7FrozenDsc = txn && txn._frozenDynamicSamplingContext;
|
1702
|
+
if (v7FrozenDsc) {
|
1703
|
+
return v7FrozenDsc;
|
1704
|
+
}
|
1705
|
+
|
1706
|
+
// TODO (v8): Replace txn.metadata with txn.attributes[]
|
1707
|
+
// We can't do this yet because attributes aren't always set yet.
|
1708
|
+
// eslint-disable-next-line deprecation/deprecation
|
1709
|
+
const { sampleRate: maybeSampleRate, source } = txn.metadata;
|
1710
|
+
if (maybeSampleRate != null) {
|
1711
|
+
dsc.sample_rate = `${maybeSampleRate}`;
|
1712
|
+
}
|
1713
|
+
|
1714
|
+
// We don't want to have a transaction name in the DSC if the source is "url" because URLs might contain PII
|
1715
|
+
const jsonSpan = spanToJSON(txn);
|
1716
|
+
|
1717
|
+
// after JSON conversion, txn.name becomes jsonSpan.description
|
1718
|
+
if (source && source !== 'url') {
|
1719
|
+
dsc.transaction = jsonSpan.description;
|
1720
|
+
}
|
1721
|
+
|
1722
|
+
dsc.sampled = String(spanIsSampled(txn));
|
1723
|
+
|
1724
|
+
client.emit && client.emit('createDsc', dsc);
|
1725
|
+
|
1726
|
+
return dsc;
|
1727
|
+
}
|
1728
|
+
|
1729
|
+
/**
|
1730
|
+
* Applies data from the scope to the event and runs all event processors on it.
|
1731
|
+
*/
|
1732
|
+
function applyScopeDataToEvent(event, data) {
|
1733
|
+
const { fingerprint, span, breadcrumbs, sdkProcessingMetadata } = data;
|
1734
|
+
|
1735
|
+
// Apply general data
|
1736
|
+
applyDataToEvent(event, data);
|
1737
|
+
|
1738
|
+
// We want to set the trace context for normal events only if there isn't already
|
1739
|
+
// a trace context on the event. There is a product feature in place where we link
|
1740
|
+
// errors with transaction and it relies on that.
|
1741
|
+
if (span) {
|
1742
|
+
applySpanToEvent(event, span);
|
1743
|
+
}
|
1744
|
+
|
1745
|
+
applyFingerprintToEvent(event, fingerprint);
|
1746
|
+
applyBreadcrumbsToEvent(event, breadcrumbs);
|
1747
|
+
applySdkMetadataToEvent(event, sdkProcessingMetadata);
|
1748
|
+
}
|
1749
|
+
|
1750
|
+
function applyDataToEvent(event, data) {
|
1751
|
+
const {
|
1752
|
+
extra,
|
1753
|
+
tags,
|
1754
|
+
user,
|
1755
|
+
contexts,
|
1756
|
+
level,
|
1757
|
+
// eslint-disable-next-line deprecation/deprecation
|
1758
|
+
transactionName,
|
1759
|
+
} = data;
|
1760
|
+
|
1761
|
+
const cleanedExtra = dropUndefinedKeys(extra);
|
1762
|
+
if (cleanedExtra && Object.keys(cleanedExtra).length) {
|
1763
|
+
event.extra = { ...cleanedExtra, ...event.extra };
|
1764
|
+
}
|
1765
|
+
|
1766
|
+
const cleanedTags = dropUndefinedKeys(tags);
|
1767
|
+
if (cleanedTags && Object.keys(cleanedTags).length) {
|
1768
|
+
event.tags = { ...cleanedTags, ...event.tags };
|
1769
|
+
}
|
1770
|
+
|
1771
|
+
const cleanedUser = dropUndefinedKeys(user);
|
1772
|
+
if (cleanedUser && Object.keys(cleanedUser).length) {
|
1773
|
+
event.user = { ...cleanedUser, ...event.user };
|
1774
|
+
}
|
1775
|
+
|
1776
|
+
const cleanedContexts = dropUndefinedKeys(contexts);
|
1777
|
+
if (cleanedContexts && Object.keys(cleanedContexts).length) {
|
1778
|
+
event.contexts = { ...cleanedContexts, ...event.contexts };
|
1779
|
+
}
|
1780
|
+
|
1781
|
+
if (level) {
|
1782
|
+
event.level = level;
|
1783
|
+
}
|
1784
|
+
|
1785
|
+
if (transactionName) {
|
1786
|
+
event.transaction = transactionName;
|
1787
|
+
}
|
1788
|
+
}
|
1789
|
+
|
1790
|
+
function applyBreadcrumbsToEvent(event, breadcrumbs) {
|
1791
|
+
const mergedBreadcrumbs = [...(event.breadcrumbs || []), ...breadcrumbs];
|
1792
|
+
event.breadcrumbs = mergedBreadcrumbs.length ? mergedBreadcrumbs : undefined;
|
1793
|
+
}
|
1794
|
+
|
1795
|
+
function applySdkMetadataToEvent(event, sdkProcessingMetadata) {
|
1796
|
+
event.sdkProcessingMetadata = {
|
1797
|
+
...event.sdkProcessingMetadata,
|
1798
|
+
...sdkProcessingMetadata,
|
1799
|
+
};
|
1800
|
+
}
|
1801
|
+
|
1802
|
+
function applySpanToEvent(event, span) {
|
1803
|
+
event.contexts = { trace: spanToTraceContext(span), ...event.contexts };
|
1804
|
+
const rootSpan = getRootSpan(span);
|
1805
|
+
if (rootSpan) {
|
1806
|
+
event.sdkProcessingMetadata = {
|
1807
|
+
dynamicSamplingContext: getDynamicSamplingContextFromSpan(span),
|
1808
|
+
...event.sdkProcessingMetadata,
|
1809
|
+
};
|
1810
|
+
const transactionName = spanToJSON(rootSpan).description;
|
1811
|
+
if (transactionName) {
|
1812
|
+
event.tags = { transaction: transactionName, ...event.tags };
|
1813
|
+
}
|
1814
|
+
}
|
1815
|
+
}
|
1816
|
+
|
1817
|
+
/**
|
1818
|
+
* Applies fingerprint from the scope to the event if there's one,
|
1819
|
+
* uses message if there's one instead or get rid of empty fingerprint
|
1820
|
+
*/
|
1821
|
+
function applyFingerprintToEvent(event, fingerprint) {
|
1822
|
+
// Make sure it's an array first and we actually have something in place
|
1823
|
+
event.fingerprint = event.fingerprint ? arrayify(event.fingerprint) : [];
|
1824
|
+
|
1825
|
+
// If we have something on the scope, then merge it with event
|
1826
|
+
if (fingerprint) {
|
1827
|
+
event.fingerprint = event.fingerprint.concat(fingerprint);
|
1828
|
+
}
|
1829
|
+
|
1830
|
+
// If we have no data at all, remove empty array default
|
1831
|
+
if (event.fingerprint && !event.fingerprint.length) {
|
1832
|
+
delete event.fingerprint;
|
1833
|
+
}
|
1834
|
+
}
|
1835
|
+
|
1836
|
+
/**
|
1837
|
+
* Default value for maximum number of breadcrumbs added to an event.
|
1838
|
+
*/
|
1839
|
+
const DEFAULT_MAX_BREADCRUMBS = 100;
|
1840
|
+
|
1841
|
+
/**
|
1842
|
+
* Holds additional event information. {@link Scope.applyToEvent} will be
|
1843
|
+
* called by the client before an event will be sent.
|
1844
|
+
*/
|
1845
|
+
class Scope {
|
1846
|
+
/** Flag if notifying is happening. */
|
1847
|
+
|
1848
|
+
/** Callback for client to receive scope changes. */
|
1849
|
+
|
1850
|
+
/** Callback list that will be called after {@link applyToEvent}. */
|
1851
|
+
|
1852
|
+
/** Array of breadcrumbs. */
|
1853
|
+
|
1854
|
+
/** User */
|
1855
|
+
|
1856
|
+
/** Tags */
|
1857
|
+
|
1858
|
+
/** Extra */
|
1859
|
+
|
1860
|
+
/** Contexts */
|
1861
|
+
|
1862
|
+
/** Attachments */
|
1863
|
+
|
1864
|
+
/** Propagation Context for distributed tracing */
|
1865
|
+
|
1866
|
+
/**
|
1867
|
+
* A place to stash data which is needed at some point in the SDK's event processing pipeline but which shouldn't get
|
1868
|
+
* sent to Sentry
|
1869
|
+
*/
|
1870
|
+
|
1871
|
+
/** Fingerprint */
|
1872
|
+
|
1873
|
+
/** Severity */
|
1874
|
+
// eslint-disable-next-line deprecation/deprecation
|
1875
|
+
|
1876
|
+
/**
|
1877
|
+
* Transaction Name
|
1878
|
+
*/
|
1879
|
+
|
1880
|
+
/** Span */
|
1881
|
+
|
1882
|
+
/** Session */
|
1883
|
+
|
1884
|
+
/** Request Mode Session Status */
|
1885
|
+
|
1886
|
+
/** The client on this scope */
|
1887
|
+
|
1888
|
+
// NOTE: Any field which gets added here should get added not only to the constructor but also to the `clone` method.
|
1889
|
+
|
1890
|
+
constructor() {
|
1891
|
+
this._notifyingListeners = false;
|
1892
|
+
this._scopeListeners = [];
|
1893
|
+
this._eventProcessors = [];
|
1894
|
+
this._breadcrumbs = [];
|
1895
|
+
this._attachments = [];
|
1896
|
+
this._user = {};
|
1897
|
+
this._tags = {};
|
1898
|
+
this._extra = {};
|
1899
|
+
this._contexts = {};
|
1900
|
+
this._sdkProcessingMetadata = {};
|
1901
|
+
this._propagationContext = generatePropagationContext();
|
1902
|
+
}
|
1903
|
+
|
1904
|
+
/**
|
1905
|
+
* Inherit values from the parent scope.
|
1906
|
+
* @deprecated Use `scope.clone()` and `new Scope()` instead.
|
1907
|
+
*/
|
1908
|
+
static clone(scope) {
|
1909
|
+
return scope ? scope.clone() : new Scope();
|
1910
|
+
}
|
1911
|
+
|
1912
|
+
/**
|
1913
|
+
* Clone this scope instance.
|
1914
|
+
*/
|
1915
|
+
clone() {
|
1916
|
+
const newScope = new Scope();
|
1917
|
+
newScope._breadcrumbs = [...this._breadcrumbs];
|
1918
|
+
newScope._tags = { ...this._tags };
|
1919
|
+
newScope._extra = { ...this._extra };
|
1920
|
+
newScope._contexts = { ...this._contexts };
|
1921
|
+
newScope._user = this._user;
|
1922
|
+
newScope._level = this._level;
|
1923
|
+
newScope._span = this._span;
|
1924
|
+
newScope._session = this._session;
|
1925
|
+
newScope._transactionName = this._transactionName;
|
1926
|
+
newScope._fingerprint = this._fingerprint;
|
1927
|
+
newScope._eventProcessors = [...this._eventProcessors];
|
1928
|
+
newScope._requestSession = this._requestSession;
|
1929
|
+
newScope._attachments = [...this._attachments];
|
1930
|
+
newScope._sdkProcessingMetadata = { ...this._sdkProcessingMetadata };
|
1931
|
+
newScope._propagationContext = { ...this._propagationContext };
|
1932
|
+
newScope._client = this._client;
|
1933
|
+
|
1934
|
+
return newScope;
|
1935
|
+
}
|
1936
|
+
|
1937
|
+
/** Update the client on the scope. */
|
1938
|
+
setClient(client) {
|
1939
|
+
this._client = client;
|
1940
|
+
}
|
1941
|
+
|
1942
|
+
/**
|
1943
|
+
* Get the client assigned to this scope.
|
1944
|
+
*
|
1945
|
+
* It is generally recommended to use the global function `Sentry.getClient()` instead, unless you know what you are doing.
|
1946
|
+
*/
|
1947
|
+
getClient() {
|
1948
|
+
return this._client;
|
1949
|
+
}
|
1950
|
+
|
1951
|
+
/**
|
1952
|
+
* Add internal on change listener. Used for sub SDKs that need to store the scope.
|
1953
|
+
* @hidden
|
1954
|
+
*/
|
1955
|
+
addScopeListener(callback) {
|
1956
|
+
this._scopeListeners.push(callback);
|
1957
|
+
}
|
1958
|
+
|
1959
|
+
/**
|
1960
|
+
* @inheritDoc
|
1961
|
+
*/
|
1962
|
+
addEventProcessor(callback) {
|
1963
|
+
this._eventProcessors.push(callback);
|
1964
|
+
return this;
|
1965
|
+
}
|
1966
|
+
|
1967
|
+
/**
|
1968
|
+
* @inheritDoc
|
1969
|
+
*/
|
1970
|
+
setUser(user) {
|
1971
|
+
// If null is passed we want to unset everything, but still define keys,
|
1972
|
+
// so that later down in the pipeline any existing values are cleared.
|
1973
|
+
this._user = user || {
|
1974
|
+
email: undefined,
|
1975
|
+
id: undefined,
|
1976
|
+
ip_address: undefined,
|
1977
|
+
segment: undefined,
|
1978
|
+
username: undefined,
|
1979
|
+
};
|
1980
|
+
|
1981
|
+
if (this._session) {
|
1982
|
+
updateSession(this._session, { user });
|
1983
|
+
}
|
1984
|
+
|
1985
|
+
this._notifyScopeListeners();
|
1986
|
+
return this;
|
1987
|
+
}
|
1988
|
+
|
1989
|
+
/**
|
1990
|
+
* @inheritDoc
|
1991
|
+
*/
|
1992
|
+
getUser() {
|
1993
|
+
return this._user;
|
1994
|
+
}
|
1995
|
+
|
1996
|
+
/**
|
1997
|
+
* @inheritDoc
|
1998
|
+
*/
|
1999
|
+
getRequestSession() {
|
2000
|
+
return this._requestSession;
|
2001
|
+
}
|
2002
|
+
|
2003
|
+
/**
|
2004
|
+
* @inheritDoc
|
2005
|
+
*/
|
2006
|
+
setRequestSession(requestSession) {
|
2007
|
+
this._requestSession = requestSession;
|
2008
|
+
return this;
|
2009
|
+
}
|
2010
|
+
|
2011
|
+
/**
|
2012
|
+
* @inheritDoc
|
2013
|
+
*/
|
2014
|
+
setTags(tags) {
|
2015
|
+
this._tags = {
|
2016
|
+
...this._tags,
|
2017
|
+
...tags,
|
2018
|
+
};
|
2019
|
+
this._notifyScopeListeners();
|
2020
|
+
return this;
|
2021
|
+
}
|
2022
|
+
|
2023
|
+
/**
|
2024
|
+
* @inheritDoc
|
2025
|
+
*/
|
2026
|
+
setTag(key, value) {
|
2027
|
+
this._tags = { ...this._tags, [key]: value };
|
2028
|
+
this._notifyScopeListeners();
|
2029
|
+
return this;
|
2030
|
+
}
|
2031
|
+
|
2032
|
+
/**
|
2033
|
+
* @inheritDoc
|
2034
|
+
*/
|
2035
|
+
setExtras(extras) {
|
2036
|
+
this._extra = {
|
2037
|
+
...this._extra,
|
2038
|
+
...extras,
|
2039
|
+
};
|
2040
|
+
this._notifyScopeListeners();
|
2041
|
+
return this;
|
2042
|
+
}
|
2043
|
+
|
2044
|
+
/**
|
2045
|
+
* @inheritDoc
|
2046
|
+
*/
|
2047
|
+
setExtra(key, extra) {
|
2048
|
+
this._extra = { ...this._extra, [key]: extra };
|
2049
|
+
this._notifyScopeListeners();
|
2050
|
+
return this;
|
2051
|
+
}
|
2052
|
+
|
2053
|
+
/**
|
2054
|
+
* @inheritDoc
|
2055
|
+
*/
|
2056
|
+
setFingerprint(fingerprint) {
|
2057
|
+
this._fingerprint = fingerprint;
|
2058
|
+
this._notifyScopeListeners();
|
2059
|
+
return this;
|
2060
|
+
}
|
2061
|
+
|
2062
|
+
/**
|
2063
|
+
* @inheritDoc
|
2064
|
+
*/
|
2065
|
+
setLevel(
|
2066
|
+
// eslint-disable-next-line deprecation/deprecation
|
2067
|
+
level,
|
2068
|
+
) {
|
2069
|
+
this._level = level;
|
2070
|
+
this._notifyScopeListeners();
|
2071
|
+
return this;
|
2072
|
+
}
|
2073
|
+
|
2074
|
+
/**
|
2075
|
+
* Sets the transaction name on the scope for future events.
|
2076
|
+
*/
|
2077
|
+
setTransactionName(name) {
|
2078
|
+
this._transactionName = name;
|
2079
|
+
this._notifyScopeListeners();
|
2080
|
+
return this;
|
2081
|
+
}
|
2082
|
+
|
2083
|
+
/**
|
2084
|
+
* @inheritDoc
|
2085
|
+
*/
|
2086
|
+
setContext(key, context) {
|
2087
|
+
if (context === null) {
|
2088
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
2089
|
+
delete this._contexts[key];
|
2090
|
+
} else {
|
2091
|
+
this._contexts[key] = context;
|
2092
|
+
}
|
2093
|
+
|
2094
|
+
this._notifyScopeListeners();
|
2095
|
+
return this;
|
2096
|
+
}
|
2097
|
+
|
2098
|
+
/**
|
2099
|
+
* Sets the Span on the scope.
|
2100
|
+
* @param span Span
|
2101
|
+
* @deprecated Instead of setting a span on a scope, use `startSpan()`/`startSpanManual()` instead.
|
2102
|
+
*/
|
2103
|
+
setSpan(span) {
|
2104
|
+
this._span = span;
|
2105
|
+
this._notifyScopeListeners();
|
2106
|
+
return this;
|
2107
|
+
}
|
2108
|
+
|
2109
|
+
/**
|
2110
|
+
* Returns the `Span` if there is one.
|
2111
|
+
* @deprecated Use `getActiveSpan()` instead.
|
2112
|
+
*/
|
2113
|
+
getSpan() {
|
2114
|
+
return this._span;
|
2115
|
+
}
|
2116
|
+
|
2117
|
+
/**
|
2118
|
+
* Returns the `Transaction` attached to the scope (if there is one).
|
2119
|
+
* @deprecated You should not rely on the transaction, but just use `startSpan()` APIs instead.
|
2120
|
+
*/
|
2121
|
+
getTransaction() {
|
2122
|
+
// Often, this span (if it exists at all) will be a transaction, but it's not guaranteed to be. Regardless, it will
|
2123
|
+
// have a pointer to the currently-active transaction.
|
2124
|
+
const span = this._span;
|
2125
|
+
// Cannot replace with getRootSpan because getRootSpan returns a span, not a transaction
|
2126
|
+
// Also, this method will be removed anyway.
|
2127
|
+
// eslint-disable-next-line deprecation/deprecation
|
2128
|
+
return span && span.transaction;
|
2129
|
+
}
|
2130
|
+
|
2131
|
+
/**
|
2132
|
+
* @inheritDoc
|
2133
|
+
*/
|
2134
|
+
setSession(session) {
|
2135
|
+
if (!session) {
|
2136
|
+
delete this._session;
|
2137
|
+
} else {
|
2138
|
+
this._session = session;
|
2139
|
+
}
|
2140
|
+
this._notifyScopeListeners();
|
2141
|
+
return this;
|
2142
|
+
}
|
2143
|
+
|
2144
|
+
/**
|
2145
|
+
* @inheritDoc
|
2146
|
+
*/
|
2147
|
+
getSession() {
|
2148
|
+
return this._session;
|
2149
|
+
}
|
2150
|
+
|
2151
|
+
/**
|
2152
|
+
* @inheritDoc
|
2153
|
+
*/
|
2154
|
+
update(captureContext) {
|
2155
|
+
if (!captureContext) {
|
2156
|
+
return this;
|
2157
|
+
}
|
2158
|
+
|
2159
|
+
const scopeToMerge = typeof captureContext === 'function' ? captureContext(this) : captureContext;
|
2160
|
+
|
2161
|
+
if (scopeToMerge instanceof Scope) {
|
2162
|
+
const scopeData = scopeToMerge.getScopeData();
|
2163
|
+
|
2164
|
+
this._tags = { ...this._tags, ...scopeData.tags };
|
2165
|
+
this._extra = { ...this._extra, ...scopeData.extra };
|
2166
|
+
this._contexts = { ...this._contexts, ...scopeData.contexts };
|
2167
|
+
if (scopeData.user && Object.keys(scopeData.user).length) {
|
2168
|
+
this._user = scopeData.user;
|
2169
|
+
}
|
2170
|
+
if (scopeData.level) {
|
2171
|
+
this._level = scopeData.level;
|
2172
|
+
}
|
2173
|
+
if (scopeData.fingerprint.length) {
|
2174
|
+
this._fingerprint = scopeData.fingerprint;
|
2175
|
+
}
|
2176
|
+
if (scopeToMerge.getRequestSession()) {
|
2177
|
+
this._requestSession = scopeToMerge.getRequestSession();
|
2178
|
+
}
|
2179
|
+
if (scopeData.propagationContext) {
|
2180
|
+
this._propagationContext = scopeData.propagationContext;
|
2181
|
+
}
|
2182
|
+
} else if (isPlainObject(scopeToMerge)) {
|
2183
|
+
const scopeContext = captureContext ;
|
2184
|
+
this._tags = { ...this._tags, ...scopeContext.tags };
|
2185
|
+
this._extra = { ...this._extra, ...scopeContext.extra };
|
2186
|
+
this._contexts = { ...this._contexts, ...scopeContext.contexts };
|
2187
|
+
if (scopeContext.user) {
|
2188
|
+
this._user = scopeContext.user;
|
2189
|
+
}
|
2190
|
+
if (scopeContext.level) {
|
2191
|
+
this._level = scopeContext.level;
|
2192
|
+
}
|
2193
|
+
if (scopeContext.fingerprint) {
|
2194
|
+
this._fingerprint = scopeContext.fingerprint;
|
2195
|
+
}
|
2196
|
+
if (scopeContext.requestSession) {
|
2197
|
+
this._requestSession = scopeContext.requestSession;
|
2198
|
+
}
|
2199
|
+
if (scopeContext.propagationContext) {
|
2200
|
+
this._propagationContext = scopeContext.propagationContext;
|
2201
|
+
}
|
2202
|
+
}
|
2203
|
+
|
2204
|
+
return this;
|
2205
|
+
}
|
2206
|
+
|
2207
|
+
/**
|
2208
|
+
* @inheritDoc
|
2209
|
+
*/
|
2210
|
+
clear() {
|
2211
|
+
this._breadcrumbs = [];
|
2212
|
+
this._tags = {};
|
2213
|
+
this._extra = {};
|
2214
|
+
this._user = {};
|
2215
|
+
this._contexts = {};
|
2216
|
+
this._level = undefined;
|
2217
|
+
this._transactionName = undefined;
|
2218
|
+
this._fingerprint = undefined;
|
2219
|
+
this._requestSession = undefined;
|
2220
|
+
this._span = undefined;
|
2221
|
+
this._session = undefined;
|
2222
|
+
this._notifyScopeListeners();
|
2223
|
+
this._attachments = [];
|
2224
|
+
this._propagationContext = generatePropagationContext();
|
2225
|
+
return this;
|
2226
|
+
}
|
2227
|
+
|
2228
|
+
/**
|
2229
|
+
* @inheritDoc
|
2230
|
+
*/
|
2231
|
+
addBreadcrumb(breadcrumb, maxBreadcrumbs) {
|
2232
|
+
const maxCrumbs = typeof maxBreadcrumbs === 'number' ? maxBreadcrumbs : DEFAULT_MAX_BREADCRUMBS;
|
2233
|
+
|
2234
|
+
// No data has been changed, so don't notify scope listeners
|
2235
|
+
if (maxCrumbs <= 0) {
|
2236
|
+
return this;
|
2237
|
+
}
|
2238
|
+
|
2239
|
+
const mergedBreadcrumb = {
|
2240
|
+
timestamp: dateTimestampInSeconds(),
|
2241
|
+
...breadcrumb,
|
2242
|
+
};
|
2243
|
+
|
2244
|
+
const breadcrumbs = this._breadcrumbs;
|
2245
|
+
breadcrumbs.push(mergedBreadcrumb);
|
2246
|
+
this._breadcrumbs = breadcrumbs.length > maxCrumbs ? breadcrumbs.slice(-maxCrumbs) : breadcrumbs;
|
2247
|
+
|
2248
|
+
this._notifyScopeListeners();
|
2249
|
+
|
2250
|
+
return this;
|
2251
|
+
}
|
2252
|
+
|
2253
|
+
/**
|
2254
|
+
* @inheritDoc
|
2255
|
+
*/
|
2256
|
+
getLastBreadcrumb() {
|
2257
|
+
return this._breadcrumbs[this._breadcrumbs.length - 1];
|
2258
|
+
}
|
2259
|
+
|
2260
|
+
/**
|
2261
|
+
* @inheritDoc
|
2262
|
+
*/
|
2263
|
+
clearBreadcrumbs() {
|
2264
|
+
this._breadcrumbs = [];
|
2265
|
+
this._notifyScopeListeners();
|
2266
|
+
return this;
|
2267
|
+
}
|
2268
|
+
|
2269
|
+
/**
|
2270
|
+
* @inheritDoc
|
2271
|
+
*/
|
2272
|
+
addAttachment(attachment) {
|
2273
|
+
this._attachments.push(attachment);
|
2274
|
+
return this;
|
2275
|
+
}
|
2276
|
+
|
2277
|
+
/**
|
2278
|
+
* @inheritDoc
|
2279
|
+
* @deprecated Use `getScopeData()` instead.
|
2280
|
+
*/
|
2281
|
+
getAttachments() {
|
2282
|
+
const data = this.getScopeData();
|
2283
|
+
|
2284
|
+
return data.attachments;
|
2285
|
+
}
|
2286
|
+
|
2287
|
+
/**
|
2288
|
+
* @inheritDoc
|
2289
|
+
*/
|
2290
|
+
clearAttachments() {
|
2291
|
+
this._attachments = [];
|
2292
|
+
return this;
|
2293
|
+
}
|
2294
|
+
|
2295
|
+
/** @inheritDoc */
|
2296
|
+
getScopeData() {
|
2297
|
+
const {
|
2298
|
+
_breadcrumbs,
|
2299
|
+
_attachments,
|
2300
|
+
_contexts,
|
2301
|
+
_tags,
|
2302
|
+
_extra,
|
2303
|
+
_user,
|
2304
|
+
_level,
|
2305
|
+
_fingerprint,
|
2306
|
+
_eventProcessors,
|
2307
|
+
_propagationContext,
|
2308
|
+
_sdkProcessingMetadata,
|
2309
|
+
_transactionName,
|
2310
|
+
_span,
|
2311
|
+
} = this;
|
2312
|
+
|
2313
|
+
return {
|
2314
|
+
breadcrumbs: _breadcrumbs,
|
2315
|
+
attachments: _attachments,
|
2316
|
+
contexts: _contexts,
|
2317
|
+
tags: _tags,
|
2318
|
+
extra: _extra,
|
2319
|
+
user: _user,
|
2320
|
+
level: _level,
|
2321
|
+
fingerprint: _fingerprint || [],
|
2322
|
+
eventProcessors: _eventProcessors,
|
2323
|
+
propagationContext: _propagationContext,
|
2324
|
+
sdkProcessingMetadata: _sdkProcessingMetadata,
|
2325
|
+
transactionName: _transactionName,
|
2326
|
+
span: _span,
|
2327
|
+
};
|
2328
|
+
}
|
2329
|
+
|
2330
|
+
/**
|
2331
|
+
* Applies data from the scope to the event and runs all event processors on it.
|
2332
|
+
*
|
2333
|
+
* @param event Event
|
2334
|
+
* @param hint Object containing additional information about the original exception, for use by the event processors.
|
2335
|
+
* @hidden
|
2336
|
+
* @deprecated Use `applyScopeDataToEvent()` directly
|
2337
|
+
*/
|
2338
|
+
applyToEvent(
|
2339
|
+
event,
|
2340
|
+
hint = {},
|
2341
|
+
additionalEventProcessors = [],
|
2342
|
+
) {
|
2343
|
+
applyScopeDataToEvent(event, this.getScopeData());
|
2344
|
+
|
2345
|
+
// TODO (v8): Update this order to be: Global > Client > Scope
|
2346
|
+
const eventProcessors = [
|
2347
|
+
...additionalEventProcessors,
|
2348
|
+
// eslint-disable-next-line deprecation/deprecation
|
2349
|
+
...getGlobalEventProcessors(),
|
2350
|
+
...this._eventProcessors,
|
2351
|
+
];
|
2352
|
+
|
2353
|
+
return notifyEventProcessors(eventProcessors, event, hint);
|
2354
|
+
}
|
2355
|
+
|
2356
|
+
/**
|
2357
|
+
* Add data which will be accessible during event processing but won't get sent to Sentry
|
2358
|
+
*/
|
2359
|
+
setSDKProcessingMetadata(newData) {
|
2360
|
+
this._sdkProcessingMetadata = { ...this._sdkProcessingMetadata, ...newData };
|
2361
|
+
|
2362
|
+
return this;
|
2363
|
+
}
|
2364
|
+
|
2365
|
+
/**
|
2366
|
+
* @inheritDoc
|
2367
|
+
*/
|
2368
|
+
setPropagationContext(context) {
|
2369
|
+
this._propagationContext = context;
|
2370
|
+
return this;
|
2371
|
+
}
|
2372
|
+
|
2373
|
+
/**
|
2374
|
+
* @inheritDoc
|
2375
|
+
*/
|
2376
|
+
getPropagationContext() {
|
2377
|
+
return this._propagationContext;
|
2378
|
+
}
|
2379
|
+
|
2380
|
+
/**
|
2381
|
+
* Capture an exception for this scope.
|
2382
|
+
*
|
2383
|
+
* @param exception The exception to capture.
|
2384
|
+
* @param hint Optinal additional data to attach to the Sentry event.
|
2385
|
+
* @returns the id of the captured Sentry event.
|
2386
|
+
*/
|
2387
|
+
captureException(exception, hint) {
|
2388
|
+
const eventId = hint && hint.event_id ? hint.event_id : uuid4();
|
2389
|
+
|
2390
|
+
if (!this._client) {
|
2391
|
+
logger.warn('No client configured on scope - will not capture exception!');
|
2392
|
+
return eventId;
|
2393
|
+
}
|
2394
|
+
|
2395
|
+
const syntheticException = new Error('Sentry syntheticException');
|
2396
|
+
|
2397
|
+
this._client.captureException(
|
2398
|
+
exception,
|
2399
|
+
{
|
2400
|
+
originalException: exception,
|
2401
|
+
syntheticException,
|
2402
|
+
...hint,
|
2403
|
+
event_id: eventId,
|
2404
|
+
},
|
2405
|
+
this,
|
2406
|
+
);
|
2407
|
+
|
2408
|
+
return eventId;
|
2409
|
+
}
|
2410
|
+
|
2411
|
+
/**
|
2412
|
+
* Capture a message for this scope.
|
2413
|
+
*
|
2414
|
+
* @param message The message to capture.
|
2415
|
+
* @param level An optional severity level to report the message with.
|
2416
|
+
* @param hint Optional additional data to attach to the Sentry event.
|
2417
|
+
* @returns the id of the captured message.
|
2418
|
+
*/
|
2419
|
+
captureMessage(message, level, hint) {
|
2420
|
+
const eventId = hint && hint.event_id ? hint.event_id : uuid4();
|
2421
|
+
|
2422
|
+
if (!this._client) {
|
2423
|
+
logger.warn('No client configured on scope - will not capture message!');
|
2424
|
+
return eventId;
|
2425
|
+
}
|
2426
|
+
|
2427
|
+
const syntheticException = new Error(message);
|
2428
|
+
|
2429
|
+
this._client.captureMessage(
|
2430
|
+
message,
|
2431
|
+
level,
|
2432
|
+
{
|
2433
|
+
originalException: message,
|
2434
|
+
syntheticException,
|
2435
|
+
...hint,
|
2436
|
+
event_id: eventId,
|
2437
|
+
},
|
2438
|
+
this,
|
2439
|
+
);
|
2440
|
+
|
2441
|
+
return eventId;
|
2442
|
+
}
|
2443
|
+
|
2444
|
+
/**
|
2445
|
+
* Captures a manually created event for this scope and sends it to Sentry.
|
2446
|
+
*
|
2447
|
+
* @param exception The event to capture.
|
2448
|
+
* @param hint Optional additional data to attach to the Sentry event.
|
2449
|
+
* @returns the id of the captured event.
|
2450
|
+
*/
|
2451
|
+
captureEvent(event, hint) {
|
2452
|
+
const eventId = hint && hint.event_id ? hint.event_id : uuid4();
|
2453
|
+
|
2454
|
+
if (!this._client) {
|
2455
|
+
logger.warn('No client configured on scope - will not capture event!');
|
2456
|
+
return eventId;
|
2457
|
+
}
|
2458
|
+
|
2459
|
+
this._client.captureEvent(event, { ...hint, event_id: eventId }, this);
|
2460
|
+
|
2461
|
+
return eventId;
|
2462
|
+
}
|
2463
|
+
|
2464
|
+
/**
|
2465
|
+
* This will be called on every set call.
|
2466
|
+
*/
|
2467
|
+
_notifyScopeListeners() {
|
2468
|
+
// We need this check for this._notifyingListeners to be able to work on scope during updates
|
2469
|
+
// If this check is not here we'll produce endless recursion when something is done with the scope
|
2470
|
+
// during the callback.
|
2471
|
+
if (!this._notifyingListeners) {
|
2472
|
+
this._notifyingListeners = true;
|
2473
|
+
this._scopeListeners.forEach(callback => {
|
2474
|
+
callback(this);
|
2475
|
+
});
|
2476
|
+
this._notifyingListeners = false;
|
2477
|
+
}
|
2478
|
+
}
|
2479
|
+
}
|
2480
|
+
|
2481
|
+
function generatePropagationContext() {
|
2482
|
+
return {
|
2483
|
+
traceId: uuid4(),
|
2484
|
+
spanId: uuid4().substring(16),
|
2485
|
+
};
|
2486
|
+
}
|
2487
|
+
|
2488
|
+
const SDK_VERSION = '7.117.0';
|
2489
|
+
|
2490
|
+
/**
|
2491
|
+
* API compatibility version of this hub.
|
2492
|
+
*
|
2493
|
+
* WARNING: This number should only be increased when the global interface
|
2494
|
+
* changes and new methods are introduced.
|
2495
|
+
*
|
2496
|
+
* @hidden
|
2497
|
+
*/
|
2498
|
+
const API_VERSION = parseFloat(SDK_VERSION);
|
2499
|
+
|
2500
|
+
/**
|
2501
|
+
* Default maximum number of breadcrumbs added to an event. Can be overwritten
|
2502
|
+
* with {@link Options.maxBreadcrumbs}.
|
2503
|
+
*/
|
2504
|
+
const DEFAULT_BREADCRUMBS = 100;
|
2505
|
+
|
2506
|
+
/**
|
2507
|
+
* @deprecated The `Hub` class will be removed in version 8 of the SDK in favour of `Scope` and `Client` objects.
|
2508
|
+
*
|
2509
|
+
* If you previously used the `Hub` class directly, replace it with `Scope` and `Client` objects. More information:
|
2510
|
+
* - [Multiple Sentry Instances](https://docs.sentry.io/platforms/javascript/best-practices/multiple-sentry-instances/)
|
2511
|
+
* - [Browser Extensions](https://docs.sentry.io/platforms/javascript/best-practices/browser-extensions/)
|
2512
|
+
*
|
2513
|
+
* Some of our APIs are typed with the Hub class instead of the interface (e.g. `getCurrentHub`). Most of them are deprecated
|
2514
|
+
* themselves and will also be removed in version 8. More information:
|
2515
|
+
* - [Migration Guide](https://github.com/getsentry/sentry-javascript/blob/develop/MIGRATION.md#deprecate-hub)
|
2516
|
+
*/
|
2517
|
+
// eslint-disable-next-line deprecation/deprecation
|
2518
|
+
class Hub {
|
2519
|
+
/** Is a {@link Layer}[] containing the client and scope */
|
2520
|
+
|
2521
|
+
/** Contains the last event id of a captured event. */
|
2522
|
+
|
2523
|
+
/**
|
2524
|
+
* Creates a new instance of the hub, will push one {@link Layer} into the
|
2525
|
+
* internal stack on creation.
|
2526
|
+
*
|
2527
|
+
* @param client bound to the hub.
|
2528
|
+
* @param scope bound to the hub.
|
2529
|
+
* @param version number, higher number means higher priority.
|
2530
|
+
*
|
2531
|
+
* @deprecated Instantiation of Hub objects is deprecated and the constructor will be removed in version 8 of the SDK.
|
2532
|
+
*
|
2533
|
+
* If you are currently using the Hub for multi-client use like so:
|
2534
|
+
*
|
2535
|
+
* ```
|
2536
|
+
* // OLD
|
2537
|
+
* const hub = new Hub();
|
2538
|
+
* hub.bindClient(client);
|
2539
|
+
* makeMain(hub)
|
2540
|
+
* ```
|
2541
|
+
*
|
2542
|
+
* instead initialize the client as follows:
|
2543
|
+
*
|
2544
|
+
* ```
|
2545
|
+
* // NEW
|
2546
|
+
* Sentry.withIsolationScope(() => {
|
2547
|
+
* Sentry.setCurrentClient(client);
|
2548
|
+
* client.init();
|
2549
|
+
* });
|
2550
|
+
* ```
|
2551
|
+
*
|
2552
|
+
* If you are using the Hub to capture events like so:
|
2553
|
+
*
|
2554
|
+
* ```
|
2555
|
+
* // OLD
|
2556
|
+
* const client = new Client();
|
2557
|
+
* const hub = new Hub(client);
|
2558
|
+
* hub.captureException()
|
2559
|
+
* ```
|
2560
|
+
*
|
2561
|
+
* instead capture isolated events as follows:
|
2562
|
+
*
|
2563
|
+
* ```
|
2564
|
+
* // NEW
|
2565
|
+
* const client = new Client();
|
2566
|
+
* const scope = new Scope();
|
2567
|
+
* scope.setClient(client);
|
2568
|
+
* scope.captureException();
|
2569
|
+
* ```
|
2570
|
+
*/
|
2571
|
+
constructor(
|
2572
|
+
client,
|
2573
|
+
scope,
|
2574
|
+
isolationScope,
|
2575
|
+
_version = API_VERSION,
|
2576
|
+
) {this._version = _version;
|
2577
|
+
let assignedScope;
|
2578
|
+
if (!scope) {
|
2579
|
+
assignedScope = new Scope();
|
2580
|
+
assignedScope.setClient(client);
|
2581
|
+
} else {
|
2582
|
+
assignedScope = scope;
|
2583
|
+
}
|
2584
|
+
|
2585
|
+
let assignedIsolationScope;
|
2586
|
+
if (!isolationScope) {
|
2587
|
+
assignedIsolationScope = new Scope();
|
2588
|
+
assignedIsolationScope.setClient(client);
|
2589
|
+
} else {
|
2590
|
+
assignedIsolationScope = isolationScope;
|
2591
|
+
}
|
2592
|
+
|
2593
|
+
this._stack = [{ scope: assignedScope }];
|
2594
|
+
|
2595
|
+
if (client) {
|
2596
|
+
// eslint-disable-next-line deprecation/deprecation
|
2597
|
+
this.bindClient(client);
|
2598
|
+
}
|
2599
|
+
|
2600
|
+
this._isolationScope = assignedIsolationScope;
|
2601
|
+
}
|
2602
|
+
|
2603
|
+
/**
|
2604
|
+
* Checks if this hub's version is older than the given version.
|
2605
|
+
*
|
2606
|
+
* @param version A version number to compare to.
|
2607
|
+
* @return True if the given version is newer; otherwise false.
|
2608
|
+
*
|
2609
|
+
* @deprecated This will be removed in v8.
|
2610
|
+
*/
|
2611
|
+
isOlderThan(version) {
|
2612
|
+
return this._version < version;
|
2613
|
+
}
|
2614
|
+
|
2615
|
+
/**
|
2616
|
+
* This binds the given client to the current scope.
|
2617
|
+
* @param client An SDK client (client) instance.
|
2618
|
+
*
|
2619
|
+
* @deprecated Use `initAndBind()` directly, or `setCurrentClient()` and/or `client.init()` instead.
|
2620
|
+
*/
|
2621
|
+
bindClient(client) {
|
2622
|
+
// eslint-disable-next-line deprecation/deprecation
|
2623
|
+
const top = this.getStackTop();
|
2624
|
+
top.client = client;
|
2625
|
+
top.scope.setClient(client);
|
2626
|
+
// eslint-disable-next-line deprecation/deprecation
|
2627
|
+
if (client && client.setupIntegrations) {
|
2628
|
+
// eslint-disable-next-line deprecation/deprecation
|
2629
|
+
client.setupIntegrations();
|
2630
|
+
}
|
2631
|
+
}
|
2632
|
+
|
2633
|
+
/**
|
2634
|
+
* @inheritDoc
|
2635
|
+
*
|
2636
|
+
* @deprecated Use `withScope` instead.
|
2637
|
+
*/
|
2638
|
+
pushScope() {
|
2639
|
+
// We want to clone the content of prev scope
|
2640
|
+
// eslint-disable-next-line deprecation/deprecation
|
2641
|
+
const scope = this.getScope().clone();
|
2642
|
+
// eslint-disable-next-line deprecation/deprecation
|
2643
|
+
this.getStack().push({
|
2644
|
+
// eslint-disable-next-line deprecation/deprecation
|
2645
|
+
client: this.getClient(),
|
2646
|
+
scope,
|
2647
|
+
});
|
2648
|
+
return scope;
|
2649
|
+
}
|
2650
|
+
|
2651
|
+
/**
|
2652
|
+
* @inheritDoc
|
2653
|
+
*
|
2654
|
+
* @deprecated Use `withScope` instead.
|
2655
|
+
*/
|
2656
|
+
popScope() {
|
2657
|
+
// eslint-disable-next-line deprecation/deprecation
|
2658
|
+
if (this.getStack().length <= 1) return false;
|
2659
|
+
// eslint-disable-next-line deprecation/deprecation
|
2660
|
+
return !!this.getStack().pop();
|
2661
|
+
}
|
2662
|
+
|
2663
|
+
/**
|
2664
|
+
* @inheritDoc
|
2665
|
+
*
|
2666
|
+
* @deprecated Use `Sentry.withScope()` instead.
|
2667
|
+
*/
|
2668
|
+
withScope(callback) {
|
2669
|
+
// eslint-disable-next-line deprecation/deprecation
|
2670
|
+
const scope = this.pushScope();
|
2671
|
+
|
2672
|
+
let maybePromiseResult;
|
2673
|
+
try {
|
2674
|
+
maybePromiseResult = callback(scope);
|
2675
|
+
} catch (e) {
|
2676
|
+
// eslint-disable-next-line deprecation/deprecation
|
2677
|
+
this.popScope();
|
2678
|
+
throw e;
|
2679
|
+
}
|
2680
|
+
|
2681
|
+
if (isThenable(maybePromiseResult)) {
|
2682
|
+
// @ts-expect-error - isThenable returns the wrong type
|
2683
|
+
return maybePromiseResult.then(
|
2684
|
+
res => {
|
2685
|
+
// eslint-disable-next-line deprecation/deprecation
|
2686
|
+
this.popScope();
|
2687
|
+
return res;
|
2688
|
+
},
|
2689
|
+
e => {
|
2690
|
+
// eslint-disable-next-line deprecation/deprecation
|
2691
|
+
this.popScope();
|
2692
|
+
throw e;
|
2693
|
+
},
|
2694
|
+
);
|
2695
|
+
}
|
2696
|
+
|
2697
|
+
// eslint-disable-next-line deprecation/deprecation
|
2698
|
+
this.popScope();
|
2699
|
+
return maybePromiseResult;
|
2700
|
+
}
|
2701
|
+
|
2702
|
+
/**
|
2703
|
+
* @inheritDoc
|
2704
|
+
*
|
2705
|
+
* @deprecated Use `Sentry.getClient()` instead.
|
2706
|
+
*/
|
2707
|
+
getClient() {
|
2708
|
+
// eslint-disable-next-line deprecation/deprecation
|
2709
|
+
return this.getStackTop().client ;
|
2710
|
+
}
|
2711
|
+
|
2712
|
+
/**
|
2713
|
+
* Returns the scope of the top stack.
|
2714
|
+
*
|
2715
|
+
* @deprecated Use `Sentry.getCurrentScope()` instead.
|
2716
|
+
*/
|
2717
|
+
getScope() {
|
2718
|
+
// eslint-disable-next-line deprecation/deprecation
|
2719
|
+
return this.getStackTop().scope;
|
2720
|
+
}
|
2721
|
+
|
2722
|
+
/**
|
2723
|
+
* @deprecated Use `Sentry.getIsolationScope()` instead.
|
2724
|
+
*/
|
2725
|
+
getIsolationScope() {
|
2726
|
+
return this._isolationScope;
|
2727
|
+
}
|
2728
|
+
|
2729
|
+
/**
|
2730
|
+
* Returns the scope stack for domains or the process.
|
2731
|
+
* @deprecated This will be removed in v8.
|
2732
|
+
*/
|
2733
|
+
getStack() {
|
2734
|
+
return this._stack;
|
2735
|
+
}
|
2736
|
+
|
2737
|
+
/**
|
2738
|
+
* Returns the topmost scope layer in the order domain > local > process.
|
2739
|
+
* @deprecated This will be removed in v8.
|
2740
|
+
*/
|
2741
|
+
getStackTop() {
|
2742
|
+
return this._stack[this._stack.length - 1];
|
2743
|
+
}
|
2744
|
+
|
2745
|
+
/**
|
2746
|
+
* @inheritDoc
|
2747
|
+
*
|
2748
|
+
* @deprecated Use `Sentry.captureException()` instead.
|
2749
|
+
*/
|
2750
|
+
captureException(exception, hint) {
|
2751
|
+
const eventId = (this._lastEventId = hint && hint.event_id ? hint.event_id : uuid4());
|
2752
|
+
const syntheticException = new Error('Sentry syntheticException');
|
2753
|
+
// eslint-disable-next-line deprecation/deprecation
|
2754
|
+
this.getScope().captureException(exception, {
|
2755
|
+
originalException: exception,
|
2756
|
+
syntheticException,
|
2757
|
+
...hint,
|
2758
|
+
event_id: eventId,
|
2759
|
+
});
|
2760
|
+
|
2761
|
+
return eventId;
|
2762
|
+
}
|
2763
|
+
|
2764
|
+
/**
|
2765
|
+
* @inheritDoc
|
2766
|
+
*
|
2767
|
+
* @deprecated Use `Sentry.captureMessage()` instead.
|
2768
|
+
*/
|
2769
|
+
captureMessage(
|
2770
|
+
message,
|
2771
|
+
// eslint-disable-next-line deprecation/deprecation
|
2772
|
+
level,
|
2773
|
+
hint,
|
2774
|
+
) {
|
2775
|
+
const eventId = (this._lastEventId = hint && hint.event_id ? hint.event_id : uuid4());
|
2776
|
+
const syntheticException = new Error(message);
|
2777
|
+
// eslint-disable-next-line deprecation/deprecation
|
2778
|
+
this.getScope().captureMessage(message, level, {
|
2779
|
+
originalException: message,
|
2780
|
+
syntheticException,
|
2781
|
+
...hint,
|
2782
|
+
event_id: eventId,
|
2783
|
+
});
|
2784
|
+
|
2785
|
+
return eventId;
|
2786
|
+
}
|
2787
|
+
|
2788
|
+
/**
|
2789
|
+
* @inheritDoc
|
2790
|
+
*
|
2791
|
+
* @deprecated Use `Sentry.captureEvent()` instead.
|
2792
|
+
*/
|
2793
|
+
captureEvent(event, hint) {
|
2794
|
+
const eventId = hint && hint.event_id ? hint.event_id : uuid4();
|
2795
|
+
if (!event.type) {
|
2796
|
+
this._lastEventId = eventId;
|
2797
|
+
}
|
2798
|
+
// eslint-disable-next-line deprecation/deprecation
|
2799
|
+
this.getScope().captureEvent(event, { ...hint, event_id: eventId });
|
2800
|
+
return eventId;
|
2801
|
+
}
|
2802
|
+
|
2803
|
+
/**
|
2804
|
+
* @inheritDoc
|
2805
|
+
*
|
2806
|
+
* @deprecated This will be removed in v8.
|
2807
|
+
*/
|
2808
|
+
lastEventId() {
|
2809
|
+
return this._lastEventId;
|
2810
|
+
}
|
2811
|
+
|
2812
|
+
/**
|
2813
|
+
* @inheritDoc
|
2814
|
+
*
|
2815
|
+
* @deprecated Use `Sentry.addBreadcrumb()` instead.
|
2816
|
+
*/
|
2817
|
+
addBreadcrumb(breadcrumb, hint) {
|
2818
|
+
// eslint-disable-next-line deprecation/deprecation
|
2819
|
+
const { scope, client } = this.getStackTop();
|
2820
|
+
|
2821
|
+
if (!client) return;
|
2822
|
+
|
2823
|
+
const { beforeBreadcrumb = null, maxBreadcrumbs = DEFAULT_BREADCRUMBS } =
|
2824
|
+
(client.getOptions && client.getOptions()) || {};
|
2825
|
+
|
2826
|
+
if (maxBreadcrumbs <= 0) return;
|
2827
|
+
|
2828
|
+
const timestamp = dateTimestampInSeconds();
|
2829
|
+
const mergedBreadcrumb = { timestamp, ...breadcrumb };
|
2830
|
+
const finalBreadcrumb = beforeBreadcrumb
|
2831
|
+
? (consoleSandbox(() => beforeBreadcrumb(mergedBreadcrumb, hint)) )
|
2832
|
+
: mergedBreadcrumb;
|
2833
|
+
|
2834
|
+
if (finalBreadcrumb === null) return;
|
2835
|
+
|
2836
|
+
if (client.emit) {
|
2837
|
+
client.emit('beforeAddBreadcrumb', finalBreadcrumb, hint);
|
2838
|
+
}
|
2839
|
+
|
2840
|
+
// TODO(v8): I know this comment doesn't make much sense because the hub will be deprecated but I still wanted to
|
2841
|
+
// write it down. In theory, we would have to add the breadcrumbs to the isolation scope here, however, that would
|
2842
|
+
// duplicate all of the breadcrumbs. There was the possibility of adding breadcrumbs to both, the isolation scope
|
2843
|
+
// and the normal scope, and deduplicating it down the line in the event processing pipeline. However, that would
|
2844
|
+
// have been very fragile, because the breadcrumb objects would have needed to keep their identity all throughout
|
2845
|
+
// the event processing pipeline.
|
2846
|
+
// In the new implementation, the top level `Sentry.addBreadcrumb()` should ONLY write to the isolation scope.
|
2847
|
+
|
2848
|
+
scope.addBreadcrumb(finalBreadcrumb, maxBreadcrumbs);
|
2849
|
+
}
|
2850
|
+
|
2851
|
+
/**
|
2852
|
+
* @inheritDoc
|
2853
|
+
* @deprecated Use `Sentry.setUser()` instead.
|
2854
|
+
*/
|
2855
|
+
setUser(user) {
|
2856
|
+
// TODO(v8): The top level `Sentry.setUser()` function should write ONLY to the isolation scope.
|
2857
|
+
// eslint-disable-next-line deprecation/deprecation
|
2858
|
+
this.getScope().setUser(user);
|
2859
|
+
// eslint-disable-next-line deprecation/deprecation
|
2860
|
+
this.getIsolationScope().setUser(user);
|
2861
|
+
}
|
2862
|
+
|
2863
|
+
/**
|
2864
|
+
* @inheritDoc
|
2865
|
+
* @deprecated Use `Sentry.setTags()` instead.
|
2866
|
+
*/
|
2867
|
+
setTags(tags) {
|
2868
|
+
// TODO(v8): The top level `Sentry.setTags()` function should write ONLY to the isolation scope.
|
2869
|
+
// eslint-disable-next-line deprecation/deprecation
|
2870
|
+
this.getScope().setTags(tags);
|
2871
|
+
// eslint-disable-next-line deprecation/deprecation
|
2872
|
+
this.getIsolationScope().setTags(tags);
|
2873
|
+
}
|
2874
|
+
|
2875
|
+
/**
|
2876
|
+
* @inheritDoc
|
2877
|
+
* @deprecated Use `Sentry.setExtras()` instead.
|
2878
|
+
*/
|
2879
|
+
setExtras(extras) {
|
2880
|
+
// TODO(v8): The top level `Sentry.setExtras()` function should write ONLY to the isolation scope.
|
2881
|
+
// eslint-disable-next-line deprecation/deprecation
|
2882
|
+
this.getScope().setExtras(extras);
|
2883
|
+
// eslint-disable-next-line deprecation/deprecation
|
2884
|
+
this.getIsolationScope().setExtras(extras);
|
2885
|
+
}
|
2886
|
+
|
2887
|
+
/**
|
2888
|
+
* @inheritDoc
|
2889
|
+
* @deprecated Use `Sentry.setTag()` instead.
|
2890
|
+
*/
|
2891
|
+
setTag(key, value) {
|
2892
|
+
// TODO(v8): The top level `Sentry.setTag()` function should write ONLY to the isolation scope.
|
2893
|
+
// eslint-disable-next-line deprecation/deprecation
|
2894
|
+
this.getScope().setTag(key, value);
|
2895
|
+
// eslint-disable-next-line deprecation/deprecation
|
2896
|
+
this.getIsolationScope().setTag(key, value);
|
2897
|
+
}
|
2898
|
+
|
2899
|
+
/**
|
2900
|
+
* @inheritDoc
|
2901
|
+
* @deprecated Use `Sentry.setExtra()` instead.
|
2902
|
+
*/
|
2903
|
+
setExtra(key, extra) {
|
2904
|
+
// TODO(v8): The top level `Sentry.setExtra()` function should write ONLY to the isolation scope.
|
2905
|
+
// eslint-disable-next-line deprecation/deprecation
|
2906
|
+
this.getScope().setExtra(key, extra);
|
2907
|
+
// eslint-disable-next-line deprecation/deprecation
|
2908
|
+
this.getIsolationScope().setExtra(key, extra);
|
2909
|
+
}
|
2910
|
+
|
2911
|
+
/**
|
2912
|
+
* @inheritDoc
|
2913
|
+
* @deprecated Use `Sentry.setContext()` instead.
|
2914
|
+
*/
|
2915
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2916
|
+
setContext(name, context) {
|
2917
|
+
// TODO(v8): The top level `Sentry.setContext()` function should write ONLY to the isolation scope.
|
2918
|
+
// eslint-disable-next-line deprecation/deprecation
|
2919
|
+
this.getScope().setContext(name, context);
|
2920
|
+
// eslint-disable-next-line deprecation/deprecation
|
2921
|
+
this.getIsolationScope().setContext(name, context);
|
2922
|
+
}
|
2923
|
+
|
2924
|
+
/**
|
2925
|
+
* @inheritDoc
|
2926
|
+
*
|
2927
|
+
* @deprecated Use `getScope()` directly.
|
2928
|
+
*/
|
2929
|
+
configureScope(callback) {
|
2930
|
+
// eslint-disable-next-line deprecation/deprecation
|
2931
|
+
const { scope, client } = this.getStackTop();
|
2932
|
+
if (client) {
|
2933
|
+
callback(scope);
|
2934
|
+
}
|
2935
|
+
}
|
2936
|
+
|
2937
|
+
/**
|
2938
|
+
* @inheritDoc
|
2939
|
+
*/
|
2940
|
+
// eslint-disable-next-line deprecation/deprecation
|
2941
|
+
run(callback) {
|
2942
|
+
// eslint-disable-next-line deprecation/deprecation
|
2943
|
+
const oldHub = makeMain(this);
|
2944
|
+
try {
|
2945
|
+
callback(this);
|
2946
|
+
} finally {
|
2947
|
+
// eslint-disable-next-line deprecation/deprecation
|
2948
|
+
makeMain(oldHub);
|
2949
|
+
}
|
2950
|
+
}
|
2951
|
+
|
2952
|
+
/**
|
2953
|
+
* @inheritDoc
|
2954
|
+
* @deprecated Use `Sentry.getClient().getIntegrationByName()` instead.
|
2955
|
+
*/
|
2956
|
+
getIntegration(integration) {
|
2957
|
+
// eslint-disable-next-line deprecation/deprecation
|
2958
|
+
const client = this.getClient();
|
2959
|
+
if (!client) return null;
|
2960
|
+
try {
|
2961
|
+
// eslint-disable-next-line deprecation/deprecation
|
2962
|
+
return client.getIntegration(integration);
|
2963
|
+
} catch (_oO) {
|
2964
|
+
DEBUG_BUILD$1 && logger.warn(`Cannot retrieve integration ${integration.id} from the current Hub`);
|
2965
|
+
return null;
|
2966
|
+
}
|
2967
|
+
}
|
2968
|
+
|
2969
|
+
/**
|
2970
|
+
* Starts a new `Transaction` and returns it. This is the entry point to manual tracing instrumentation.
|
2971
|
+
*
|
2972
|
+
* A tree structure can be built by adding child spans to the transaction, and child spans to other spans. To start a
|
2973
|
+
* new child span within the transaction or any span, call the respective `.startChild()` method.
|
2974
|
+
*
|
2975
|
+
* Every child span must be finished before the transaction is finished, otherwise the unfinished spans are discarded.
|
2976
|
+
*
|
2977
|
+
* The transaction must be finished with a call to its `.end()` method, at which point the transaction with all its
|
2978
|
+
* finished child spans will be sent to Sentry.
|
2979
|
+
*
|
2980
|
+
* @param context Properties of the new `Transaction`.
|
2981
|
+
* @param customSamplingContext Information given to the transaction sampling function (along with context-dependent
|
2982
|
+
* default values). See {@link Options.tracesSampler}.
|
2983
|
+
*
|
2984
|
+
* @returns The transaction which was just started
|
2985
|
+
*
|
2986
|
+
* @deprecated Use `startSpan()`, `startSpanManual()` or `startInactiveSpan()` instead.
|
2987
|
+
*/
|
2988
|
+
startTransaction(context, customSamplingContext) {
|
2989
|
+
const result = this._callExtensionMethod('startTransaction', context, customSamplingContext);
|
2990
|
+
|
2991
|
+
if (DEBUG_BUILD$1 && !result) {
|
2992
|
+
// eslint-disable-next-line deprecation/deprecation
|
2993
|
+
const client = this.getClient();
|
2994
|
+
if (!client) {
|
2995
|
+
logger.warn(
|
2996
|
+
"Tracing extension 'startTransaction' is missing. You should 'init' the SDK before calling 'startTransaction'",
|
2997
|
+
);
|
2998
|
+
} else {
|
2999
|
+
logger.warn(`Tracing extension 'startTransaction' has not been added. Call 'addTracingExtensions' before calling 'init':
|
3000
|
+
Sentry.addTracingExtensions();
|
3001
|
+
Sentry.init({...});
|
3002
|
+
`);
|
3003
|
+
}
|
3004
|
+
}
|
3005
|
+
|
3006
|
+
return result;
|
3007
|
+
}
|
3008
|
+
|
3009
|
+
/**
|
3010
|
+
* @inheritDoc
|
3011
|
+
* @deprecated Use `spanToTraceHeader()` instead.
|
3012
|
+
*/
|
3013
|
+
traceHeaders() {
|
3014
|
+
return this._callExtensionMethod('traceHeaders');
|
3015
|
+
}
|
3016
|
+
|
3017
|
+
/**
|
3018
|
+
* @inheritDoc
|
3019
|
+
*
|
3020
|
+
* @deprecated Use top level `captureSession` instead.
|
3021
|
+
*/
|
3022
|
+
captureSession(endSession = false) {
|
3023
|
+
// both send the update and pull the session from the scope
|
3024
|
+
if (endSession) {
|
3025
|
+
// eslint-disable-next-line deprecation/deprecation
|
3026
|
+
return this.endSession();
|
3027
|
+
}
|
3028
|
+
|
3029
|
+
// only send the update
|
3030
|
+
this._sendSessionUpdate();
|
3031
|
+
}
|
3032
|
+
|
3033
|
+
/**
|
3034
|
+
* @inheritDoc
|
3035
|
+
* @deprecated Use top level `endSession` instead.
|
3036
|
+
*/
|
3037
|
+
endSession() {
|
3038
|
+
// eslint-disable-next-line deprecation/deprecation
|
3039
|
+
const layer = this.getStackTop();
|
3040
|
+
const scope = layer.scope;
|
3041
|
+
const session = scope.getSession();
|
3042
|
+
if (session) {
|
3043
|
+
closeSession(session);
|
3044
|
+
}
|
3045
|
+
this._sendSessionUpdate();
|
3046
|
+
|
3047
|
+
// the session is over; take it off of the scope
|
3048
|
+
scope.setSession();
|
3049
|
+
}
|
3050
|
+
|
3051
|
+
/**
|
3052
|
+
* @inheritDoc
|
3053
|
+
* @deprecated Use top level `startSession` instead.
|
3054
|
+
*/
|
3055
|
+
startSession(context) {
|
3056
|
+
// eslint-disable-next-line deprecation/deprecation
|
3057
|
+
const { scope, client } = this.getStackTop();
|
3058
|
+
const { release, environment = DEFAULT_ENVIRONMENT } = (client && client.getOptions()) || {};
|
3059
|
+
|
3060
|
+
// Will fetch userAgent if called from browser sdk
|
3061
|
+
const { userAgent } = GLOBAL_OBJ.navigator || {};
|
3062
|
+
|
3063
|
+
const session = makeSession({
|
3064
|
+
release,
|
3065
|
+
environment,
|
3066
|
+
user: scope.getUser(),
|
3067
|
+
...(userAgent && { userAgent }),
|
3068
|
+
...context,
|
3069
|
+
});
|
3070
|
+
|
3071
|
+
// End existing session if there's one
|
3072
|
+
const currentSession = scope.getSession && scope.getSession();
|
3073
|
+
if (currentSession && currentSession.status === 'ok') {
|
3074
|
+
updateSession(currentSession, { status: 'exited' });
|
3075
|
+
}
|
3076
|
+
// eslint-disable-next-line deprecation/deprecation
|
3077
|
+
this.endSession();
|
3078
|
+
|
3079
|
+
// Afterwards we set the new session on the scope
|
3080
|
+
scope.setSession(session);
|
3081
|
+
|
3082
|
+
return session;
|
3083
|
+
}
|
3084
|
+
|
3085
|
+
/**
|
3086
|
+
* Returns if default PII should be sent to Sentry and propagated in ourgoing requests
|
3087
|
+
* when Tracing is used.
|
3088
|
+
*
|
3089
|
+
* @deprecated Use top-level `getClient().getOptions().sendDefaultPii` instead. This function
|
3090
|
+
* only unnecessarily increased API surface but only wrapped accessing the option.
|
3091
|
+
*/
|
3092
|
+
shouldSendDefaultPii() {
|
3093
|
+
// eslint-disable-next-line deprecation/deprecation
|
3094
|
+
const client = this.getClient();
|
3095
|
+
const options = client && client.getOptions();
|
3096
|
+
return Boolean(options && options.sendDefaultPii);
|
3097
|
+
}
|
3098
|
+
|
3099
|
+
/**
|
3100
|
+
* Sends the current Session on the scope
|
3101
|
+
*/
|
3102
|
+
_sendSessionUpdate() {
|
3103
|
+
// eslint-disable-next-line deprecation/deprecation
|
3104
|
+
const { scope, client } = this.getStackTop();
|
3105
|
+
|
3106
|
+
const session = scope.getSession();
|
3107
|
+
if (session && client && client.captureSession) {
|
3108
|
+
client.captureSession(session);
|
3109
|
+
}
|
3110
|
+
}
|
3111
|
+
|
3112
|
+
/**
|
3113
|
+
* Calls global extension method and binding current instance to the function call
|
3114
|
+
*/
|
3115
|
+
// @ts-expect-error Function lacks ending return statement and return type does not include 'undefined'. ts(2366)
|
3116
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
3117
|
+
_callExtensionMethod(method, ...args) {
|
3118
|
+
const carrier = getMainCarrier();
|
3119
|
+
const sentry = carrier.__SENTRY__;
|
3120
|
+
if (sentry && sentry.extensions && typeof sentry.extensions[method] === 'function') {
|
3121
|
+
return sentry.extensions[method].apply(this, args);
|
3122
|
+
}
|
3123
|
+
DEBUG_BUILD$1 && logger.warn(`Extension method ${method} couldn't be found, doing nothing.`);
|
3124
|
+
}
|
3125
|
+
}
|
3126
|
+
|
3127
|
+
/**
|
3128
|
+
* Returns the global shim registry.
|
3129
|
+
*
|
3130
|
+
* FIXME: This function is problematic, because despite always returning a valid Carrier,
|
3131
|
+
* it has an optional `__SENTRY__` property, which then in turn requires us to always perform an unnecessary check
|
3132
|
+
* at the call-site. We always access the carrier through this function, so we can guarantee that `__SENTRY__` is there.
|
3133
|
+
**/
|
3134
|
+
function getMainCarrier() {
|
3135
|
+
GLOBAL_OBJ.__SENTRY__ = GLOBAL_OBJ.__SENTRY__ || {
|
3136
|
+
extensions: {},
|
3137
|
+
hub: undefined,
|
3138
|
+
};
|
3139
|
+
return GLOBAL_OBJ;
|
3140
|
+
}
|
3141
|
+
|
3142
|
+
/**
|
3143
|
+
* Replaces the current main hub with the passed one on the global object
|
3144
|
+
*
|
3145
|
+
* @returns The old replaced hub
|
3146
|
+
*
|
3147
|
+
* @deprecated Use `setCurrentClient()` instead.
|
3148
|
+
*/
|
3149
|
+
// eslint-disable-next-line deprecation/deprecation
|
3150
|
+
function makeMain(hub) {
|
3151
|
+
const registry = getMainCarrier();
|
3152
|
+
const oldHub = getHubFromCarrier(registry);
|
3153
|
+
setHubOnCarrier(registry, hub);
|
3154
|
+
return oldHub;
|
3155
|
+
}
|
3156
|
+
|
3157
|
+
/**
|
3158
|
+
* Returns the default hub instance.
|
3159
|
+
*
|
3160
|
+
* If a hub is already registered in the global carrier but this module
|
3161
|
+
* contains a more recent version, it replaces the registered version.
|
3162
|
+
* Otherwise, the currently registered hub will be returned.
|
3163
|
+
*
|
3164
|
+
* @deprecated Use the respective replacement method directly instead.
|
3165
|
+
*/
|
3166
|
+
// eslint-disable-next-line deprecation/deprecation
|
3167
|
+
function getCurrentHub() {
|
3168
|
+
// Get main carrier (global for every environment)
|
3169
|
+
const registry = getMainCarrier();
|
3170
|
+
|
3171
|
+
if (registry.__SENTRY__ && registry.__SENTRY__.acs) {
|
3172
|
+
const hub = registry.__SENTRY__.acs.getCurrentHub();
|
3173
|
+
|
3174
|
+
if (hub) {
|
3175
|
+
return hub;
|
3176
|
+
}
|
3177
|
+
}
|
3178
|
+
|
3179
|
+
// Return hub that lives on a global object
|
3180
|
+
return getGlobalHub(registry);
|
3181
|
+
}
|
3182
|
+
|
3183
|
+
// eslint-disable-next-line deprecation/deprecation
|
3184
|
+
function getGlobalHub(registry = getMainCarrier()) {
|
3185
|
+
// If there's no hub, or its an old API, assign a new one
|
3186
|
+
|
3187
|
+
if (
|
3188
|
+
!hasHubOnCarrier(registry) ||
|
3189
|
+
// eslint-disable-next-line deprecation/deprecation
|
3190
|
+
getHubFromCarrier(registry).isOlderThan(API_VERSION)
|
3191
|
+
) {
|
3192
|
+
// eslint-disable-next-line deprecation/deprecation
|
3193
|
+
setHubOnCarrier(registry, new Hub());
|
3194
|
+
}
|
3195
|
+
|
3196
|
+
// Return hub that lives on a global object
|
3197
|
+
return getHubFromCarrier(registry);
|
3198
|
+
}
|
3199
|
+
|
3200
|
+
/**
|
3201
|
+
* This will tell whether a carrier has a hub on it or not
|
3202
|
+
* @param carrier object
|
3203
|
+
*/
|
3204
|
+
function hasHubOnCarrier(carrier) {
|
3205
|
+
return !!(carrier && carrier.__SENTRY__ && carrier.__SENTRY__.hub);
|
3206
|
+
}
|
3207
|
+
|
3208
|
+
/**
|
3209
|
+
* This will create a new {@link Hub} and add to the passed object on
|
3210
|
+
* __SENTRY__.hub.
|
3211
|
+
* @param carrier object
|
3212
|
+
* @hidden
|
3213
|
+
*/
|
3214
|
+
// eslint-disable-next-line deprecation/deprecation
|
3215
|
+
function getHubFromCarrier(carrier) {
|
3216
|
+
// eslint-disable-next-line deprecation/deprecation
|
3217
|
+
return getGlobalSingleton('hub', () => new Hub(), carrier);
|
3218
|
+
}
|
3219
|
+
|
3220
|
+
/**
|
3221
|
+
* This will set passed {@link Hub} on the passed object's __SENTRY__.hub attribute
|
3222
|
+
* @param carrier object
|
3223
|
+
* @param hub Hub
|
3224
|
+
* @returns A boolean indicating success or failure
|
3225
|
+
*/
|
3226
|
+
// eslint-disable-next-line deprecation/deprecation
|
3227
|
+
function setHubOnCarrier(carrier, hub) {
|
3228
|
+
if (!carrier) return false;
|
3229
|
+
const __SENTRY__ = (carrier.__SENTRY__ = carrier.__SENTRY__ || {});
|
3230
|
+
__SENTRY__.hub = hub;
|
3231
|
+
return true;
|
3232
|
+
}
|
3233
|
+
|
3234
|
+
/**
|
3235
|
+
* Convert a new integration function to the legacy class syntax.
|
3236
|
+
* In v8, we can remove this and instead export the integration functions directly.
|
3237
|
+
*
|
3238
|
+
* @deprecated This will be removed in v8!
|
3239
|
+
*/
|
3240
|
+
function convertIntegrationFnToClass(
|
3241
|
+
name,
|
3242
|
+
fn,
|
3243
|
+
) {
|
3244
|
+
return Object.assign(
|
3245
|
+
function ConvertedIntegration(...args) {
|
3246
|
+
return fn(...args);
|
3247
|
+
},
|
3248
|
+
{ id: name },
|
3249
|
+
) ;
|
3250
|
+
}
|
3251
|
+
|
3252
|
+
/**
|
3253
|
+
* Define an integration function that can be used to create an integration instance.
|
3254
|
+
* Note that this by design hides the implementation details of the integration, as they are considered internal.
|
3255
|
+
*/
|
3256
|
+
function defineIntegration(fn) {
|
3257
|
+
return fn;
|
3258
|
+
}
|
3259
|
+
|
3260
|
+
/**
|
3261
|
+
* Checks whether given url points to Sentry server
|
3262
|
+
* @param url url to verify
|
3263
|
+
*
|
3264
|
+
* TODO(v8): Remove Hub fallback type
|
3265
|
+
*/
|
3266
|
+
// eslint-disable-next-line deprecation/deprecation
|
3267
|
+
function isSentryRequestUrl(url, hubOrClient) {
|
3268
|
+
const client =
|
3269
|
+
hubOrClient && isHub(hubOrClient)
|
3270
|
+
? // eslint-disable-next-line deprecation/deprecation
|
3271
|
+
hubOrClient.getClient()
|
3272
|
+
: hubOrClient;
|
3273
|
+
const dsn = client && client.getDsn();
|
3274
|
+
const tunnel = client && client.getOptions().tunnel;
|
3275
|
+
|
3276
|
+
return checkDsn(url, dsn) || checkTunnel(url, tunnel);
|
3277
|
+
}
|
3278
|
+
|
3279
|
+
function checkTunnel(url, tunnel) {
|
3280
|
+
if (!tunnel) {
|
3281
|
+
return false;
|
3282
|
+
}
|
3283
|
+
|
3284
|
+
return removeTrailingSlash(url) === removeTrailingSlash(tunnel);
|
3285
|
+
}
|
3286
|
+
|
3287
|
+
function checkDsn(url, dsn) {
|
3288
|
+
return dsn ? url.includes(dsn.host) : false;
|
3289
|
+
}
|
3290
|
+
|
3291
|
+
function removeTrailingSlash(str) {
|
3292
|
+
return str[str.length - 1] === '/' ? str.slice(0, -1) : str;
|
3293
|
+
}
|
3294
|
+
|
3295
|
+
// eslint-disable-next-line deprecation/deprecation
|
3296
|
+
function isHub(hubOrClient) {
|
3297
|
+
// eslint-disable-next-line deprecation/deprecation
|
3298
|
+
return (hubOrClient ).getClient !== undefined;
|
3299
|
+
}
|
3300
|
+
|
3301
|
+
const INTEGRATION_NAME$1 = 'CaptureConsole';
|
3302
|
+
|
3303
|
+
const _captureConsoleIntegration = ((options = {}) => {
|
3304
|
+
const levels = options.levels || CONSOLE_LEVELS;
|
3305
|
+
|
3306
|
+
return {
|
3307
|
+
name: INTEGRATION_NAME$1,
|
3308
|
+
// TODO v8: Remove this
|
3309
|
+
setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function
|
3310
|
+
setup(client) {
|
3311
|
+
if (!('console' in GLOBAL_OBJ)) {
|
3312
|
+
return;
|
3313
|
+
}
|
3314
|
+
|
3315
|
+
addConsoleInstrumentationHandler(({ args, level }) => {
|
3316
|
+
if (getClient() !== client || !levels.includes(level)) {
|
3317
|
+
return;
|
3318
|
+
}
|
3319
|
+
|
3320
|
+
consoleHandler(args, level);
|
3321
|
+
});
|
3322
|
+
},
|
3323
|
+
};
|
3324
|
+
}) ;
|
3325
|
+
|
3326
|
+
const captureConsoleIntegration = defineIntegration(_captureConsoleIntegration);
|
3327
|
+
|
3328
|
+
/**
|
3329
|
+
* Send Console API calls as Sentry Events.
|
3330
|
+
* @deprecated Use `captureConsoleIntegration()` instead.
|
3331
|
+
*/
|
3332
|
+
// eslint-disable-next-line deprecation/deprecation
|
3333
|
+
convertIntegrationFnToClass(
|
3334
|
+
INTEGRATION_NAME$1,
|
3335
|
+
captureConsoleIntegration,
|
3336
|
+
)
|
3337
|
+
|
3338
|
+
;
|
3339
|
+
|
3340
|
+
function consoleHandler(args, level) {
|
3341
|
+
const captureContext = {
|
3342
|
+
level: severityLevelFromString(level),
|
3343
|
+
extra: {
|
3344
|
+
arguments: args,
|
3345
|
+
},
|
3346
|
+
};
|
3347
|
+
|
3348
|
+
withScope(scope => {
|
3349
|
+
scope.addEventProcessor(event => {
|
3350
|
+
event.logger = 'console';
|
3351
|
+
|
3352
|
+
addExceptionMechanism(event, {
|
3353
|
+
handled: false,
|
3354
|
+
type: 'console',
|
3355
|
+
});
|
3356
|
+
|
3357
|
+
return event;
|
3358
|
+
});
|
3359
|
+
|
3360
|
+
if (level === 'assert' && args[0] === false) {
|
3361
|
+
const message = `Assertion failed: ${safeJoin(args.slice(1), ' ') || 'console.assert'}`;
|
3362
|
+
scope.setExtra('arguments', args.slice(1));
|
3363
|
+
captureMessage(message, captureContext);
|
3364
|
+
return;
|
3365
|
+
}
|
3366
|
+
|
3367
|
+
const error = args.find(arg => arg instanceof Error);
|
3368
|
+
if (level === 'error' && error) {
|
3369
|
+
captureException(error, captureContext);
|
3370
|
+
return;
|
3371
|
+
}
|
3372
|
+
|
3373
|
+
const message = safeJoin(args, ' ');
|
3374
|
+
captureMessage(message, captureContext);
|
3375
|
+
});
|
3376
|
+
}
|
3377
|
+
|
3378
|
+
/**
|
3379
|
+
* 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.
|
3380
|
+
*
|
3381
|
+
* ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking.
|
3382
|
+
*/
|
3383
|
+
const DEBUG_BUILD = (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__);
|
3384
|
+
|
3385
|
+
const INTEGRATION_NAME = 'HttpClient';
|
3386
|
+
|
3387
|
+
const _httpClientIntegration = ((options = {}) => {
|
3388
|
+
const _options = {
|
3389
|
+
failedRequestStatusCodes: [[500, 599]],
|
3390
|
+
failedRequestTargets: [/.*/],
|
3391
|
+
...options,
|
3392
|
+
};
|
3393
|
+
|
3394
|
+
return {
|
3395
|
+
name: INTEGRATION_NAME,
|
3396
|
+
// TODO v8: Remove this
|
3397
|
+
setupOnce() {}, // eslint-disable-line @typescript-eslint/no-empty-function
|
3398
|
+
setup(client) {
|
3399
|
+
_wrapFetch(client, _options);
|
3400
|
+
_wrapXHR(client, _options);
|
3401
|
+
},
|
3402
|
+
};
|
3403
|
+
}) ;
|
3404
|
+
|
3405
|
+
const httpClientIntegration = defineIntegration(_httpClientIntegration);
|
3406
|
+
|
3407
|
+
/**
|
3408
|
+
* Create events for failed client side HTTP requests.
|
3409
|
+
* @deprecated Use `httpClientIntegration()` instead.
|
3410
|
+
*/
|
3411
|
+
// eslint-disable-next-line deprecation/deprecation
|
3412
|
+
convertIntegrationFnToClass(INTEGRATION_NAME, httpClientIntegration)
|
3413
|
+
|
3414
|
+
;
|
3415
|
+
|
3416
|
+
/**
|
3417
|
+
* Interceptor function for fetch requests
|
3418
|
+
*
|
3419
|
+
* @param requestInfo The Fetch API request info
|
3420
|
+
* @param response The Fetch API response
|
3421
|
+
* @param requestInit The request init object
|
3422
|
+
*/
|
3423
|
+
function _fetchResponseHandler(
|
3424
|
+
options,
|
3425
|
+
requestInfo,
|
3426
|
+
response,
|
3427
|
+
requestInit,
|
3428
|
+
) {
|
3429
|
+
if (_shouldCaptureResponse(options, response.status, response.url)) {
|
3430
|
+
const request = _getRequest(requestInfo, requestInit);
|
3431
|
+
|
3432
|
+
let requestHeaders, responseHeaders, requestCookies, responseCookies;
|
3433
|
+
|
3434
|
+
if (_shouldSendDefaultPii()) {
|
3435
|
+
[{ headers: requestHeaders, cookies: requestCookies }, { headers: responseHeaders, cookies: responseCookies }] = [
|
3436
|
+
{ cookieHeader: 'Cookie', obj: request },
|
3437
|
+
{ cookieHeader: 'Set-Cookie', obj: response },
|
3438
|
+
].map(({ cookieHeader, obj }) => {
|
3439
|
+
const headers = _extractFetchHeaders(obj.headers);
|
3440
|
+
let cookies;
|
3441
|
+
|
3442
|
+
try {
|
3443
|
+
const cookieString = headers[cookieHeader] || headers[cookieHeader.toLowerCase()] || undefined;
|
3444
|
+
|
3445
|
+
if (cookieString) {
|
3446
|
+
cookies = _parseCookieString(cookieString);
|
3447
|
+
}
|
3448
|
+
} catch (e) {
|
3449
|
+
DEBUG_BUILD && logger.log(`Could not extract cookies from header ${cookieHeader}`);
|
3450
|
+
}
|
3451
|
+
|
3452
|
+
return {
|
3453
|
+
headers,
|
3454
|
+
cookies,
|
3455
|
+
};
|
3456
|
+
});
|
3457
|
+
}
|
3458
|
+
|
3459
|
+
const event = _createEvent({
|
3460
|
+
url: request.url,
|
3461
|
+
method: request.method,
|
3462
|
+
status: response.status,
|
3463
|
+
requestHeaders,
|
3464
|
+
responseHeaders,
|
3465
|
+
requestCookies,
|
3466
|
+
responseCookies,
|
3467
|
+
});
|
3468
|
+
|
3469
|
+
captureEvent(event);
|
3470
|
+
}
|
3471
|
+
}
|
3472
|
+
|
3473
|
+
/**
|
3474
|
+
* Interceptor function for XHR requests
|
3475
|
+
*
|
3476
|
+
* @param xhr The XHR request
|
3477
|
+
* @param method The HTTP method
|
3478
|
+
* @param headers The HTTP headers
|
3479
|
+
*/
|
3480
|
+
function _xhrResponseHandler(
|
3481
|
+
options,
|
3482
|
+
xhr,
|
3483
|
+
method,
|
3484
|
+
headers,
|
3485
|
+
) {
|
3486
|
+
if (_shouldCaptureResponse(options, xhr.status, xhr.responseURL)) {
|
3487
|
+
let requestHeaders, responseCookies, responseHeaders;
|
3488
|
+
|
3489
|
+
if (_shouldSendDefaultPii()) {
|
3490
|
+
try {
|
3491
|
+
const cookieString = xhr.getResponseHeader('Set-Cookie') || xhr.getResponseHeader('set-cookie') || undefined;
|
3492
|
+
|
3493
|
+
if (cookieString) {
|
3494
|
+
responseCookies = _parseCookieString(cookieString);
|
3495
|
+
}
|
3496
|
+
} catch (e) {
|
3497
|
+
DEBUG_BUILD && logger.log('Could not extract cookies from response headers');
|
3498
|
+
}
|
3499
|
+
|
3500
|
+
try {
|
3501
|
+
responseHeaders = _getXHRResponseHeaders(xhr);
|
3502
|
+
} catch (e) {
|
3503
|
+
DEBUG_BUILD && logger.log('Could not extract headers from response');
|
3504
|
+
}
|
3505
|
+
|
3506
|
+
requestHeaders = headers;
|
3507
|
+
}
|
3508
|
+
|
3509
|
+
const event = _createEvent({
|
3510
|
+
url: xhr.responseURL,
|
3511
|
+
method,
|
3512
|
+
status: xhr.status,
|
3513
|
+
requestHeaders,
|
3514
|
+
// Can't access request cookies from XHR
|
3515
|
+
responseHeaders,
|
3516
|
+
responseCookies,
|
3517
|
+
});
|
3518
|
+
|
3519
|
+
captureEvent(event);
|
3520
|
+
}
|
3521
|
+
}
|
3522
|
+
|
3523
|
+
/**
|
3524
|
+
* Extracts response size from `Content-Length` header when possible
|
3525
|
+
*
|
3526
|
+
* @param headers
|
3527
|
+
* @returns The response size in bytes or undefined
|
3528
|
+
*/
|
3529
|
+
function _getResponseSizeFromHeaders(headers) {
|
3530
|
+
if (headers) {
|
3531
|
+
const contentLength = headers['Content-Length'] || headers['content-length'];
|
3532
|
+
|
3533
|
+
if (contentLength) {
|
3534
|
+
return parseInt(contentLength, 10);
|
3535
|
+
}
|
3536
|
+
}
|
3537
|
+
|
3538
|
+
return undefined;
|
3539
|
+
}
|
3540
|
+
|
3541
|
+
/**
|
3542
|
+
* Creates an object containing cookies from the given cookie string
|
3543
|
+
*
|
3544
|
+
* @param cookieString The cookie string to parse
|
3545
|
+
* @returns The parsed cookies
|
3546
|
+
*/
|
3547
|
+
function _parseCookieString(cookieString) {
|
3548
|
+
return cookieString.split('; ').reduce((acc, cookie) => {
|
3549
|
+
const [key, value] = cookie.split('=');
|
3550
|
+
acc[key] = value;
|
3551
|
+
return acc;
|
3552
|
+
}, {});
|
3553
|
+
}
|
3554
|
+
|
3555
|
+
/**
|
3556
|
+
* Extracts the headers as an object from the given Fetch API request or response object
|
3557
|
+
*
|
3558
|
+
* @param headers The headers to extract
|
3559
|
+
* @returns The extracted headers as an object
|
3560
|
+
*/
|
3561
|
+
function _extractFetchHeaders(headers) {
|
3562
|
+
const result = {};
|
3563
|
+
|
3564
|
+
headers.forEach((value, key) => {
|
3565
|
+
result[key] = value;
|
3566
|
+
});
|
3567
|
+
|
3568
|
+
return result;
|
3569
|
+
}
|
3570
|
+
|
3571
|
+
/**
|
3572
|
+
* Extracts the response headers as an object from the given XHR object
|
3573
|
+
*
|
3574
|
+
* @param xhr The XHR object to extract the response headers from
|
3575
|
+
* @returns The response headers as an object
|
3576
|
+
*/
|
3577
|
+
function _getXHRResponseHeaders(xhr) {
|
3578
|
+
const headers = xhr.getAllResponseHeaders();
|
3579
|
+
|
3580
|
+
if (!headers) {
|
3581
|
+
return {};
|
3582
|
+
}
|
3583
|
+
|
3584
|
+
return headers.split('\r\n').reduce((acc, line) => {
|
3585
|
+
const [key, value] = line.split(': ');
|
3586
|
+
acc[key] = value;
|
3587
|
+
return acc;
|
3588
|
+
}, {});
|
3589
|
+
}
|
3590
|
+
|
3591
|
+
/**
|
3592
|
+
* Checks if the given target url is in the given list of targets
|
3593
|
+
*
|
3594
|
+
* @param target The target url to check
|
3595
|
+
* @returns true if the target url is in the given list of targets, false otherwise
|
3596
|
+
*/
|
3597
|
+
function _isInGivenRequestTargets(
|
3598
|
+
failedRequestTargets,
|
3599
|
+
target,
|
3600
|
+
) {
|
3601
|
+
return failedRequestTargets.some((givenRequestTarget) => {
|
3602
|
+
if (typeof givenRequestTarget === 'string') {
|
3603
|
+
return target.includes(givenRequestTarget);
|
3604
|
+
}
|
3605
|
+
|
3606
|
+
return givenRequestTarget.test(target);
|
3607
|
+
});
|
3608
|
+
}
|
3609
|
+
|
3610
|
+
/**
|
3611
|
+
* Checks if the given status code is in the given range
|
3612
|
+
*
|
3613
|
+
* @param status The status code to check
|
3614
|
+
* @returns true if the status code is in the given range, false otherwise
|
3615
|
+
*/
|
3616
|
+
function _isInGivenStatusRanges(
|
3617
|
+
failedRequestStatusCodes,
|
3618
|
+
status,
|
3619
|
+
) {
|
3620
|
+
return failedRequestStatusCodes.some((range) => {
|
3621
|
+
if (typeof range === 'number') {
|
3622
|
+
return range === status;
|
3623
|
+
}
|
3624
|
+
|
3625
|
+
return status >= range[0] && status <= range[1];
|
3626
|
+
});
|
3627
|
+
}
|
3628
|
+
|
3629
|
+
/**
|
3630
|
+
* Wraps `fetch` function to capture request and response data
|
3631
|
+
*/
|
3632
|
+
function _wrapFetch(client, options) {
|
3633
|
+
if (!supportsNativeFetch()) {
|
3634
|
+
return;
|
3635
|
+
}
|
3636
|
+
|
3637
|
+
addFetchInstrumentationHandler(handlerData => {
|
3638
|
+
if (getClient() !== client) {
|
3639
|
+
return;
|
3640
|
+
}
|
3641
|
+
|
3642
|
+
const { response, args } = handlerData;
|
3643
|
+
const [requestInfo, requestInit] = args ;
|
3644
|
+
|
3645
|
+
if (!response) {
|
3646
|
+
return;
|
3647
|
+
}
|
3648
|
+
|
3649
|
+
_fetchResponseHandler(options, requestInfo, response , requestInit);
|
3650
|
+
});
|
3651
|
+
}
|
3652
|
+
|
3653
|
+
/**
|
3654
|
+
* Wraps XMLHttpRequest to capture request and response data
|
3655
|
+
*/
|
3656
|
+
function _wrapXHR(client, options) {
|
3657
|
+
if (!('XMLHttpRequest' in GLOBAL_OBJ)) {
|
3658
|
+
return;
|
3659
|
+
}
|
3660
|
+
|
3661
|
+
addXhrInstrumentationHandler(handlerData => {
|
3662
|
+
if (getClient() !== client) {
|
3663
|
+
return;
|
3664
|
+
}
|
3665
|
+
|
3666
|
+
const xhr = handlerData.xhr ;
|
3667
|
+
|
3668
|
+
const sentryXhrData = xhr[SENTRY_XHR_DATA_KEY];
|
3669
|
+
|
3670
|
+
if (!sentryXhrData) {
|
3671
|
+
return;
|
3672
|
+
}
|
3673
|
+
|
3674
|
+
const { method, request_headers: headers } = sentryXhrData;
|
3675
|
+
|
3676
|
+
try {
|
3677
|
+
_xhrResponseHandler(options, xhr, method, headers);
|
3678
|
+
} catch (e) {
|
3679
|
+
DEBUG_BUILD && logger.warn('Error while extracting response event form XHR response', e);
|
3680
|
+
}
|
3681
|
+
});
|
3682
|
+
}
|
3683
|
+
|
3684
|
+
/**
|
3685
|
+
* Checks whether to capture given response as an event
|
3686
|
+
*
|
3687
|
+
* @param status response status code
|
3688
|
+
* @param url response url
|
3689
|
+
*/
|
3690
|
+
function _shouldCaptureResponse(options, status, url) {
|
3691
|
+
return (
|
3692
|
+
_isInGivenStatusRanges(options.failedRequestStatusCodes, status) &&
|
3693
|
+
_isInGivenRequestTargets(options.failedRequestTargets, url) &&
|
3694
|
+
!isSentryRequestUrl(url, getClient())
|
3695
|
+
);
|
3696
|
+
}
|
3697
|
+
|
3698
|
+
/**
|
3699
|
+
* Creates a synthetic Sentry event from given response data
|
3700
|
+
*
|
3701
|
+
* @param data response data
|
3702
|
+
* @returns event
|
3703
|
+
*/
|
3704
|
+
function _createEvent(data
|
3705
|
+
|
3706
|
+
) {
|
3707
|
+
const message = `HTTP Client Error with status code: ${data.status}`;
|
3708
|
+
|
3709
|
+
const event = {
|
3710
|
+
message,
|
3711
|
+
exception: {
|
3712
|
+
values: [
|
3713
|
+
{
|
3714
|
+
type: 'Error',
|
3715
|
+
value: message,
|
3716
|
+
},
|
3717
|
+
],
|
3718
|
+
},
|
3719
|
+
request: {
|
3720
|
+
url: data.url,
|
3721
|
+
method: data.method,
|
3722
|
+
headers: data.requestHeaders,
|
3723
|
+
cookies: data.requestCookies,
|
3724
|
+
},
|
3725
|
+
contexts: {
|
3726
|
+
response: {
|
3727
|
+
status_code: data.status,
|
3728
|
+
headers: data.responseHeaders,
|
3729
|
+
cookies: data.responseCookies,
|
3730
|
+
body_size: _getResponseSizeFromHeaders(data.responseHeaders),
|
3731
|
+
},
|
3732
|
+
},
|
3733
|
+
};
|
3734
|
+
|
3735
|
+
addExceptionMechanism(event, {
|
3736
|
+
type: 'http.client',
|
3737
|
+
handled: false,
|
3738
|
+
});
|
3739
|
+
|
3740
|
+
return event;
|
3741
|
+
}
|
3742
|
+
|
3743
|
+
function _getRequest(requestInfo, requestInit) {
|
3744
|
+
if (!requestInit && requestInfo instanceof Request) {
|
3745
|
+
return requestInfo;
|
3746
|
+
}
|
3747
|
+
|
3748
|
+
// If both are set, we try to construct a new Request with the given arguments
|
3749
|
+
// However, if e.g. the original request has a `body`, this will throw an error because it was already accessed
|
3750
|
+
// In this case, as a fallback, we just use the original request - using both is rather an edge case
|
3751
|
+
if (requestInfo instanceof Request && requestInfo.bodyUsed) {
|
3752
|
+
return requestInfo;
|
3753
|
+
}
|
3754
|
+
|
3755
|
+
return new Request(requestInfo, requestInit);
|
3756
|
+
}
|
3757
|
+
|
3758
|
+
function _shouldSendDefaultPii() {
|
3759
|
+
const client = getClient();
|
3760
|
+
return client ? Boolean(client.getOptions().sendDefaultPii) : false;
|
3761
|
+
}
|
3762
|
+
|
3763
|
+
/**
|
3764
|
+
* Initialize Sentry for React Native.
|
3765
|
+
*
|
3766
|
+
* @param {InitOptionsRN} options
|
3767
|
+
* @return {void}
|
3768
|
+
*/
|
3769
|
+
const initSentry = options => {
|
3770
|
+
let shouldSendToSentry = options?.requiredEnvForSendToSentry?.includes(options.env);
|
3771
|
+
let ignoreErrors = [/StallTracking/];
|
3772
|
+
if (options?.ignoreErrorsOptions) {
|
3773
|
+
ignoreErrors = ignoreErrors.concat(options.ignoreErrorsOptions);
|
3774
|
+
}
|
3775
|
+
if (options?.options?.ignoreErrors) {
|
3776
|
+
ignoreErrors = ignoreErrors.concat(options.options.ignoreErrors);
|
3777
|
+
delete options.options.ignoreErrors;
|
3778
|
+
}
|
3779
|
+
let integrations = [];
|
3780
|
+
if (options?.httpClientIntegrationOptions?.failedRequestStatusCodes && options?.httpClientIntegrationOptions?.failedRequestTargets) {
|
3781
|
+
integrations.push(httpClientIntegration({
|
3782
|
+
failedRequestStatusCodes: options.httpClientIntegrationOptions.failedRequestStatusCodes,
|
3783
|
+
failedRequestTargets: options.httpClientIntegrationOptions.failedRequestTargets
|
3784
|
+
}));
|
3785
|
+
}
|
3786
|
+
if (options?.captureConsoleIntegrationOptions?.levels) {
|
3787
|
+
integrations.push(captureConsoleIntegration({
|
3788
|
+
levels: options.captureConsoleIntegrationOptions.levels
|
3789
|
+
}));
|
3790
|
+
}
|
3791
|
+
if (options?.options?.hasOwnProperty('integrations')) {
|
3792
|
+
options.options.integrations = [...options.options.integrations, ...integrations];
|
3793
|
+
} else {
|
3794
|
+
options.options = options.options || {};
|
3795
|
+
options.options.integrations = integrations;
|
3796
|
+
}
|
3797
|
+
let sentryOptions = {
|
3798
|
+
dsn: options.dsn,
|
3799
|
+
debug: options.debug,
|
3800
|
+
environment: options.env,
|
3801
|
+
ignoreErrors: ignoreErrors,
|
3802
|
+
sampleRate: 1.0,
|
3803
|
+
maxBreadcrumbs: 50,
|
3804
|
+
autoSessionTracking: true,
|
3805
|
+
attachScreenshot: true,
|
3806
|
+
enableCaptureFailedRequests: true,
|
3807
|
+
enableTracing: true,
|
3808
|
+
tracesSampleRate: 1.0,
|
3809
|
+
enableNative: true,
|
3810
|
+
autoInitializeNativeSdk: true,
|
3811
|
+
enableNativeCrashHandling: true,
|
3812
|
+
enableNativeNagger: true,
|
3813
|
+
enableAutoSessionTracking: true,
|
3814
|
+
enableNdkScopeSync: true,
|
3815
|
+
attachThreads: true,
|
3816
|
+
enableAutoPerformanceTracing: true,
|
3817
|
+
enableWatchdogTerminationTracking: true,
|
3818
|
+
enableAppHangTracking: true,
|
3819
|
+
appHangTimeoutInterval: 5,
|
3820
|
+
sendDefaultPii: true,
|
3821
|
+
beforeSend: event => {
|
3822
|
+
if (event?.message?.includes('StallTracking') || !shouldSendToSentry) {
|
3823
|
+
return null;
|
3824
|
+
}
|
3825
|
+
return event;
|
3826
|
+
}
|
3827
|
+
};
|
3828
|
+
if (options?.release) {
|
3829
|
+
sentryOptions.release = options.release;
|
3830
|
+
}
|
3831
|
+
if (options?.beforeSend) {
|
3832
|
+
sentryOptions.beforeSend = options.beforeSend;
|
3833
|
+
}
|
3834
|
+
if (options?.options) {
|
3835
|
+
sentryOptions = {
|
3836
|
+
...sentryOptions,
|
3837
|
+
...options.options
|
3838
|
+
};
|
3839
|
+
}
|
3840
|
+
Sentry__namespace.init(sentryOptions);
|
3841
|
+
};
|
3842
|
+
/**
|
3843
|
+
* Record additional http data for Sentry.
|
3844
|
+
*
|
3845
|
+
* @param {any | null} config
|
3846
|
+
* @param {any | null} request
|
3847
|
+
* @param {any | null} response
|
3848
|
+
* @return {void}
|
3849
|
+
*/
|
3850
|
+
const recordAdditionalSentryHttp = (config, request, response) => {
|
3851
|
+
Utils.recordSentryHttp(Sentry__namespace, config, request, response);
|
3852
|
+
};
|
3853
|
+
|
3854
|
+
exports.initSentry = initSentry;
|
3855
|
+
exports.recordAdditionalSentryHttp = recordAdditionalSentryHttp;
|
3856
|
+
//# sourceMappingURL=ReactNative-CqUrY2ZJ.js.map
|