UNPKG

270 kBJavaScriptView Raw
1/*! @sentry/browser 6.19.7 (5b3a175) | https://github.com/getsentry/sentry-javascript */
2var Sentry = (function (exports) {
3
4 /**
5 * TODO(v7): Remove this enum and replace with SeverityLevel
6 */
7 exports.Severity = void 0;
8 (function (Severity) {
9 /** JSDoc */
10 Severity["Fatal"] = "fatal";
11 /** JSDoc */
12 Severity["Error"] = "error";
13 /** JSDoc */
14 Severity["Warning"] = "warning";
15 /** JSDoc */
16 Severity["Log"] = "log";
17 /** JSDoc */
18 Severity["Info"] = "info";
19 /** JSDoc */
20 Severity["Debug"] = "debug";
21 /** JSDoc */
22 Severity["Critical"] = "critical";
23 })(exports.Severity || (exports.Severity = {}));
24
25 /**
26 * Consumes the promise and logs the error when it rejects.
27 * @param promise A promise to forget.
28 */
29 // eslint-disable-next-line @typescript-eslint/no-explicit-any
30 function forget(promise) {
31 void promise.then(null, e => {
32 // TODO: Use a better logging mechanism
33 // eslint-disable-next-line no-console
34 console.error(e);
35 });
36 }
37
38 /**
39 * NOTE: In order to avoid circular dependencies, if you add a function to this module and it needs to print something,
40 * you must either a) use `console.log` rather than the logger, or b) put your function elsewhere.
41 */
42 const fallbackGlobalObject = {};
43 /**
44 * Safely get global scope object
45 *
46 * @returns Global scope object
47 */
48 function getGlobalObject() {
49 return (typeof window !== 'undefined' // eslint-disable-line no-restricted-globals
50 ? window // eslint-disable-line no-restricted-globals
51 : typeof self !== 'undefined'
52 ? self
53 : fallbackGlobalObject);
54 }
55 /**
56 * Returns a global singleton contained in the global `__SENTRY__` object.
57 *
58 * If the singleton doesn't already exist in `__SENTRY__`, it will be created using the given factory
59 * function and added to the `__SENTRY__` object.
60 *
61 * @param name name of the global singleton on __SENTRY__
62 * @param creator creator Factory function to create the singleton if it doesn't already exist on `__SENTRY__`
63 * @param obj (Optional) The global object on which to look for `__SENTRY__`, if not `getGlobalObject`'s return value
64 * @returns the singleton
65 */
66 function getGlobalSingleton(name, creator, obj) {
67 const global = (obj || getGlobalObject());
68 const __SENTRY__ = (global.__SENTRY__ = global.__SENTRY__ || {});
69 const singleton = __SENTRY__[name] || (__SENTRY__[name] = creator());
70 return singleton;
71 }
72
73 /* eslint-disable @typescript-eslint/no-explicit-any */
74 /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
75 // eslint-disable-next-line @typescript-eslint/unbound-method
76 const objectToString = Object.prototype.toString;
77 /**
78 * Checks whether given value's type is one of a few Error or Error-like
79 * {@link isError}.
80 *
81 * @param wat A value to be checked.
82 * @returns A boolean representing the result.
83 */
84 function isError(wat) {
85 switch (objectToString.call(wat)) {
86 case '[object Error]':
87 case '[object Exception]':
88 case '[object DOMException]':
89 return true;
90 default:
91 return isInstanceOf(wat, Error);
92 }
93 }
94 function isBuiltin(wat, ty) {
95 return objectToString.call(wat) === `[object ${ty}]`;
96 }
97 /**
98 * Checks whether given value's type is ErrorEvent
99 * {@link isErrorEvent}.
100 *
101 * @param wat A value to be checked.
102 * @returns A boolean representing the result.
103 */
104 function isErrorEvent(wat) {
105 return isBuiltin(wat, 'ErrorEvent');
106 }
107 /**
108 * Checks whether given value's type is DOMError
109 * {@link isDOMError}.
110 *
111 * @param wat A value to be checked.
112 * @returns A boolean representing the result.
113 */
114 function isDOMError(wat) {
115 return isBuiltin(wat, 'DOMError');
116 }
117 /**
118 * Checks whether given value's type is DOMException
119 * {@link isDOMException}.
120 *
121 * @param wat A value to be checked.
122 * @returns A boolean representing the result.
123 */
124 function isDOMException(wat) {
125 return isBuiltin(wat, 'DOMException');
126 }
127 /**
128 * Checks whether given value's type is a string
129 * {@link isString}.
130 *
131 * @param wat A value to be checked.
132 * @returns A boolean representing the result.
133 */
134 function isString(wat) {
135 return isBuiltin(wat, 'String');
136 }
137 /**
138 * Checks whether given value is a primitive (undefined, null, number, boolean, string, bigint, symbol)
139 * {@link isPrimitive}.
140 *
141 * @param wat A value to be checked.
142 * @returns A boolean representing the result.
143 */
144 function isPrimitive(wat) {
145 return wat === null || (typeof wat !== 'object' && typeof wat !== 'function');
146 }
147 /**
148 * Checks whether given value's type is an object literal
149 * {@link isPlainObject}.
150 *
151 * @param wat A value to be checked.
152 * @returns A boolean representing the result.
153 */
154 function isPlainObject(wat) {
155 return isBuiltin(wat, 'Object');
156 }
157 /**
158 * Checks whether given value's type is an Event instance
159 * {@link isEvent}.
160 *
161 * @param wat A value to be checked.
162 * @returns A boolean representing the result.
163 */
164 function isEvent(wat) {
165 return typeof Event !== 'undefined' && isInstanceOf(wat, Event);
166 }
167 /**
168 * Checks whether given value's type is an Element instance
169 * {@link isElement}.
170 *
171 * @param wat A value to be checked.
172 * @returns A boolean representing the result.
173 */
174 function isElement(wat) {
175 return typeof Element !== 'undefined' && isInstanceOf(wat, Element);
176 }
177 /**
178 * Checks whether given value's type is an regexp
179 * {@link isRegExp}.
180 *
181 * @param wat A value to be checked.
182 * @returns A boolean representing the result.
183 */
184 function isRegExp(wat) {
185 return isBuiltin(wat, 'RegExp');
186 }
187 /**
188 * Checks whether given value has a then function.
189 * @param wat A value to be checked.
190 */
191 function isThenable(wat) {
192 // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
193 return Boolean(wat && wat.then && typeof wat.then === 'function');
194 }
195 /**
196 * Checks whether given value's type is a SyntheticEvent
197 * {@link isSyntheticEvent}.
198 *
199 * @param wat A value to be checked.
200 * @returns A boolean representing the result.
201 */
202 function isSyntheticEvent(wat) {
203 return isPlainObject(wat) && 'nativeEvent' in wat && 'preventDefault' in wat && 'stopPropagation' in wat;
204 }
205 /**
206 * Checks whether given value is NaN
207 * {@link isNaN}.
208 *
209 * @param wat A value to be checked.
210 * @returns A boolean representing the result.
211 */
212 function isNaN$1(wat) {
213 return typeof wat === 'number' && wat !== wat;
214 }
215 /**
216 * Checks whether given value's type is an instance of provided constructor.
217 * {@link isInstanceOf}.
218 *
219 * @param wat A value to be checked.
220 * @param base A constructor to be used in a check.
221 * @returns A boolean representing the result.
222 */
223 function isInstanceOf(wat, base) {
224 try {
225 return wat instanceof base;
226 }
227 catch (_e) {
228 return false;
229 }
230 }
231
232 /**
233 * Given a child DOM element, returns a query-selector statement describing that
234 * and its ancestors
235 * e.g. [HTMLElement] => body > div > input#foo.btn[name=baz]
236 * @returns generated DOM path
237 */
238 function htmlTreeAsString(elem, keyAttrs) {
239 // try/catch both:
240 // - accessing event.target (see getsentry/raven-js#838, #768)
241 // - `htmlTreeAsString` because it's complex, and just accessing the DOM incorrectly
242 // - can throw an exception in some circumstances.
243 try {
244 let currentElem = elem;
245 const MAX_TRAVERSE_HEIGHT = 5;
246 const MAX_OUTPUT_LEN = 80;
247 const out = [];
248 let height = 0;
249 let len = 0;
250 const separator = ' > ';
251 const sepLength = separator.length;
252 let nextStr;
253 // eslint-disable-next-line no-plusplus
254 while (currentElem && height++ < MAX_TRAVERSE_HEIGHT) {
255 nextStr = _htmlElementAsString(currentElem, keyAttrs);
256 // bail out if
257 // - nextStr is the 'html' element
258 // - the length of the string that would be created exceeds MAX_OUTPUT_LEN
259 // (ignore this limit if we are on the first iteration)
260 if (nextStr === 'html' || (height > 1 && len + out.length * sepLength + nextStr.length >= MAX_OUTPUT_LEN)) {
261 break;
262 }
263 out.push(nextStr);
264 len += nextStr.length;
265 currentElem = currentElem.parentNode;
266 }
267 return out.reverse().join(separator);
268 }
269 catch (_oO) {
270 return '<unknown>';
271 }
272 }
273 /**
274 * Returns a simple, query-selector representation of a DOM element
275 * e.g. [HTMLElement] => input#foo.btn[name=baz]
276 * @returns generated DOM path
277 */
278 function _htmlElementAsString(el, keyAttrs) {
279 const elem = el;
280 const out = [];
281 let className;
282 let classes;
283 let key;
284 let attr;
285 let i;
286 if (!elem || !elem.tagName) {
287 return '';
288 }
289 out.push(elem.tagName.toLowerCase());
290 // Pairs of attribute keys defined in `serializeAttribute` and their values on element.
291 const keyAttrPairs = keyAttrs && keyAttrs.length
292 ? keyAttrs.filter(keyAttr => elem.getAttribute(keyAttr)).map(keyAttr => [keyAttr, elem.getAttribute(keyAttr)])
293 : null;
294 if (keyAttrPairs && keyAttrPairs.length) {
295 keyAttrPairs.forEach(keyAttrPair => {
296 out.push(`[${keyAttrPair[0]}="${keyAttrPair[1]}"]`);
297 });
298 }
299 else {
300 if (elem.id) {
301 out.push(`#${elem.id}`);
302 }
303 // eslint-disable-next-line prefer-const
304 className = elem.className;
305 if (className && isString(className)) {
306 classes = className.split(/\s+/);
307 for (i = 0; i < classes.length; i++) {
308 out.push(`.${classes[i]}`);
309 }
310 }
311 }
312 const allowedAttrs = ['type', 'name', 'title', 'alt'];
313 for (i = 0; i < allowedAttrs.length; i++) {
314 key = allowedAttrs[i];
315 attr = elem.getAttribute(key);
316 if (attr) {
317 out.push(`[${key}="${attr}"]`);
318 }
319 }
320 return out.join('');
321 }
322 /**
323 * A safe form of location.href
324 */
325 function getLocationHref() {
326 const global = getGlobalObject();
327 try {
328 return global.document.location.href;
329 }
330 catch (oO) {
331 return '';
332 }
333 }
334
335 const setPrototypeOf = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array ? setProtoOf : mixinProperties);
336 /**
337 * setPrototypeOf polyfill using __proto__
338 */
339 // eslint-disable-next-line @typescript-eslint/ban-types
340 function setProtoOf(obj, proto) {
341 // @ts-ignore __proto__ does not exist on obj
342 obj.__proto__ = proto;
343 return obj;
344 }
345 /**
346 * setPrototypeOf polyfill using mixin
347 */
348 // eslint-disable-next-line @typescript-eslint/ban-types
349 function mixinProperties(obj, proto) {
350 for (const prop in proto) {
351 if (!Object.prototype.hasOwnProperty.call(obj, prop)) {
352 // @ts-ignore typescript complains about indexing so we remove
353 obj[prop] = proto[prop];
354 }
355 }
356 return obj;
357 }
358
359 /** An error emitted by Sentry SDKs and related utilities. */
360 class SentryError extends Error {
361 constructor(message) {
362 super(message);
363 this.message = message;
364 this.name = new.target.prototype.constructor.name;
365 setPrototypeOf(this, new.target.prototype);
366 }
367 }
368
369 /*
370 * This file defines flags and constants that can be modified during compile time in order to facilitate tree shaking
371 * for users.
372 *
373 * Debug flags need to be declared in each package individually and must not be imported across package boundaries,
374 * because some build tools have trouble tree-shaking imported guards.
375 *
376 * As a convention, we define debug flags in a `flags.ts` file in the root of a package's `src` folder.
377 *
378 * Debug flag files will contain "magic strings" like `true` that may get replaced with actual values during
379 * our, or the user's build process. Take care when introducing new flags - they must not throw if they are not
380 * replaced.
381 */
382 /** Flag that is true for debug builds, false otherwise. */
383 const IS_DEBUG_BUILD$3 = true;
384
385 /** Regular expression used to parse a Dsn. */
386 const DSN_REGEX = /^(?:(\w+):)\/\/(?:(\w+)(?::(\w+))?@)([\w.-]+)(?::(\d+))?\/(.+)/;
387 function isValidProtocol(protocol) {
388 return protocol === 'http' || protocol === 'https';
389 }
390 /**
391 * Renders the string representation of this Dsn.
392 *
393 * By default, this will render the public representation without the password
394 * component. To get the deprecated private representation, set `withPassword`
395 * to true.
396 *
397 * @param withPassword When set to true, the password will be included.
398 */
399 function dsnToString(dsn, withPassword = false) {
400 const { host, path, pass, port, projectId, protocol, publicKey } = dsn;
401 return (`${protocol}://${publicKey}${withPassword && pass ? `:${pass}` : ''}` +
402 `@${host}${port ? `:${port}` : ''}/${path ? `${path}/` : path}${projectId}`);
403 }
404 function dsnFromString(str) {
405 const match = DSN_REGEX.exec(str);
406 if (!match) {
407 throw new SentryError(`Invalid Sentry Dsn: ${str}`);
408 }
409 const [protocol, publicKey, pass = '', host, port = '', lastPath] = match.slice(1);
410 let path = '';
411 let projectId = lastPath;
412 const split = projectId.split('/');
413 if (split.length > 1) {
414 path = split.slice(0, -1).join('/');
415 projectId = split.pop();
416 }
417 if (projectId) {
418 const projectMatch = projectId.match(/^\d+/);
419 if (projectMatch) {
420 projectId = projectMatch[0];
421 }
422 }
423 return dsnFromComponents({ host, pass, path, projectId, port, protocol: protocol, publicKey });
424 }
425 function dsnFromComponents(components) {
426 // TODO this is for backwards compatibility, and can be removed in a future version
427 if ('user' in components && !('publicKey' in components)) {
428 components.publicKey = components.user;
429 }
430 return {
431 user: components.publicKey || '',
432 protocol: components.protocol,
433 publicKey: components.publicKey || '',
434 pass: components.pass || '',
435 host: components.host,
436 port: components.port || '',
437 path: components.path || '',
438 projectId: components.projectId,
439 };
440 }
441 function validateDsn(dsn) {
442 if (!IS_DEBUG_BUILD$3) {
443 return;
444 }
445 const { port, projectId, protocol } = dsn;
446 const requiredComponents = ['protocol', 'publicKey', 'host', 'projectId'];
447 requiredComponents.forEach(component => {
448 if (!dsn[component]) {
449 throw new SentryError(`Invalid Sentry Dsn: ${component} missing`);
450 }
451 });
452 if (!projectId.match(/^\d+$/)) {
453 throw new SentryError(`Invalid Sentry Dsn: Invalid projectId ${projectId}`);
454 }
455 if (!isValidProtocol(protocol)) {
456 throw new SentryError(`Invalid Sentry Dsn: Invalid protocol ${protocol}`);
457 }
458 if (port && isNaN(parseInt(port, 10))) {
459 throw new SentryError(`Invalid Sentry Dsn: Invalid port ${port}`);
460 }
461 return true;
462 }
463 /** The Sentry Dsn, identifying a Sentry instance and project. */
464 function makeDsn(from) {
465 const components = typeof from === 'string' ? dsnFromString(from) : dsnFromComponents(from);
466 validateDsn(components);
467 return components;
468 }
469
470 const SeverityLevels = ['fatal', 'error', 'warning', 'log', 'info', 'debug', 'critical'];
471
472 // TODO: Implement different loggers for different environments
473 const global$6 = getGlobalObject();
474 /** Prefix for logging strings */
475 const PREFIX = 'Sentry Logger ';
476 const CONSOLE_LEVELS = ['debug', 'info', 'warn', 'error', 'log', 'assert'];
477 /**
478 * Temporarily disable sentry console instrumentations.
479 *
480 * @param callback The function to run against the original `console` messages
481 * @returns The results of the callback
482 */
483 function consoleSandbox(callback) {
484 const global = getGlobalObject();
485 if (!('console' in global)) {
486 return callback();
487 }
488 const originalConsole = global.console;
489 const wrappedLevels = {};
490 // Restore all wrapped console methods
491 CONSOLE_LEVELS.forEach(level => {
492 // TODO(v7): Remove this check as it's only needed for Node 6
493 const originalWrappedFunc = originalConsole[level] && originalConsole[level].__sentry_original__;
494 if (level in global.console && originalWrappedFunc) {
495 wrappedLevels[level] = originalConsole[level];
496 originalConsole[level] = originalWrappedFunc;
497 }
498 });
499 try {
500 return callback();
501 }
502 finally {
503 // Revert restoration to wrapped state
504 Object.keys(wrappedLevels).forEach(level => {
505 originalConsole[level] = wrappedLevels[level];
506 });
507 }
508 }
509 function makeLogger() {
510 let enabled = false;
511 const logger = {
512 enable: () => {
513 enabled = true;
514 },
515 disable: () => {
516 enabled = false;
517 },
518 };
519 if (IS_DEBUG_BUILD$3) {
520 CONSOLE_LEVELS.forEach(name => {
521 // eslint-disable-next-line @typescript-eslint/no-explicit-any
522 logger[name] = (...args) => {
523 if (enabled) {
524 consoleSandbox(() => {
525 global$6.console[name](`${PREFIX}[${name}]:`, ...args);
526 });
527 }
528 };
529 });
530 }
531 else {
532 CONSOLE_LEVELS.forEach(name => {
533 logger[name] = () => undefined;
534 });
535 }
536 return logger;
537 }
538 // Ensure we only have a single logger instance, even if multiple versions of @sentry/utils are being used
539 let logger;
540 if (IS_DEBUG_BUILD$3) {
541 logger = getGlobalSingleton('logger', makeLogger);
542 }
543 else {
544 logger = makeLogger();
545 }
546
547 /**
548 * Truncates given string to the maximum characters count
549 *
550 * @param str An object that contains serializable values
551 * @param max Maximum number of characters in truncated string (0 = unlimited)
552 * @returns string Encoded
553 */
554 function truncate(str, max = 0) {
555 if (typeof str !== 'string' || max === 0) {
556 return str;
557 }
558 return str.length <= max ? str : `${str.substr(0, max)}...`;
559 }
560 /**
561 * Join values in array
562 * @param input array of values to be joined together
563 * @param delimiter string to be placed in-between values
564 * @returns Joined values
565 */
566 // eslint-disable-next-line @typescript-eslint/no-explicit-any
567 function safeJoin(input, delimiter) {
568 if (!Array.isArray(input)) {
569 return '';
570 }
571 const output = [];
572 // eslint-disable-next-line @typescript-eslint/prefer-for-of
573 for (let i = 0; i < input.length; i++) {
574 const value = input[i];
575 try {
576 output.push(String(value));
577 }
578 catch (e) {
579 output.push('[value cannot be serialized]');
580 }
581 }
582 return output.join(delimiter);
583 }
584 /**
585 * Checks if the value matches a regex or includes the string
586 * @param value The string value to be checked against
587 * @param pattern Either a regex or a string that must be contained in value
588 */
589 function isMatchingPattern(value, pattern) {
590 if (!isString(value)) {
591 return false;
592 }
593 if (isRegExp(pattern)) {
594 return pattern.test(value);
595 }
596 if (typeof pattern === 'string') {
597 return value.indexOf(pattern) !== -1;
598 }
599 return false;
600 }
601
602 /**
603 * Replace a method in an object with a wrapped version of itself.
604 *
605 * @param source An object that contains a method to be wrapped.
606 * @param name The name of the method to be wrapped.
607 * @param replacementFactory A higher-order function that takes the original version of the given method and returns a
608 * wrapped version. Note: The function returned by `replacementFactory` needs to be a non-arrow function, in order to
609 * preserve the correct value of `this`, and the original method must be called using `origMethod.call(this, <other
610 * args>)` or `origMethod.apply(this, [<other args>])` (rather than being called directly), again to preserve `this`.
611 * @returns void
612 */
613 function fill(source, name, replacementFactory) {
614 if (!(name in source)) {
615 return;
616 }
617 const original = source[name];
618 const wrapped = replacementFactory(original);
619 // Make sure it's a function first, as we need to attach an empty prototype for `defineProperties` to work
620 // otherwise it'll throw "TypeError: Object.defineProperties called on non-object"
621 if (typeof wrapped === 'function') {
622 try {
623 markFunctionWrapped(wrapped, original);
624 }
625 catch (_Oo) {
626 // This can throw if multiple fill happens on a global object like XMLHttpRequest
627 // Fixes https://github.com/getsentry/sentry-javascript/issues/2043
628 }
629 }
630 source[name] = wrapped;
631 }
632 /**
633 * Defines a non-enumerable property on the given object.
634 *
635 * @param obj The object on which to set the property
636 * @param name The name of the property to be set
637 * @param value The value to which to set the property
638 */
639 function addNonEnumerableProperty(obj, name, value) {
640 Object.defineProperty(obj, name, {
641 // enumerable: false, // the default, so we can save on bundle size by not explicitly setting it
642 value: value,
643 writable: true,
644 configurable: true,
645 });
646 }
647 /**
648 * Remembers the original function on the wrapped function and
649 * patches up the prototype.
650 *
651 * @param wrapped the wrapper function
652 * @param original the original function that gets wrapped
653 */
654 function markFunctionWrapped(wrapped, original) {
655 const proto = original.prototype || {};
656 wrapped.prototype = original.prototype = proto;
657 addNonEnumerableProperty(wrapped, '__sentry_original__', original);
658 }
659 /**
660 * This extracts the original function if available. See
661 * `markFunctionWrapped` for more information.
662 *
663 * @param func the function to unwrap
664 * @returns the unwrapped version of the function if available.
665 */
666 function getOriginalFunction(func) {
667 return func.__sentry_original__;
668 }
669 /**
670 * Encodes given object into url-friendly format
671 *
672 * @param object An object that contains serializable values
673 * @returns string Encoded
674 */
675 function urlEncode(object) {
676 return Object.keys(object)
677 .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(object[key])}`)
678 .join('&');
679 }
680 /**
681 * Transforms any object into an object literal with all its attributes
682 * attached to it.
683 *
684 * @param value Initial source that we have to transform in order for it to be usable by the serializer
685 */
686 function convertToPlainObject(value) {
687 let newObj = value;
688 if (isError(value)) {
689 newObj = Object.assign({ message: value.message, name: value.name, stack: value.stack }, getOwnProperties(value));
690 }
691 else if (isEvent(value)) {
692 const event = value;
693 newObj = Object.assign({ type: event.type, target: serializeEventTarget(event.target), currentTarget: serializeEventTarget(event.currentTarget) }, getOwnProperties(event));
694 if (typeof CustomEvent !== 'undefined' && isInstanceOf(value, CustomEvent)) {
695 newObj.detail = event.detail;
696 }
697 }
698 return newObj;
699 }
700 /** Creates a string representation of the target of an `Event` object */
701 function serializeEventTarget(target) {
702 try {
703 return isElement(target) ? htmlTreeAsString(target) : Object.prototype.toString.call(target);
704 }
705 catch (_oO) {
706 return '<unknown>';
707 }
708 }
709 /** Filters out all but an object's own properties */
710 function getOwnProperties(obj) {
711 const extractedProps = {};
712 for (const property in obj) {
713 if (Object.prototype.hasOwnProperty.call(obj, property)) {
714 extractedProps[property] = obj[property];
715 }
716 }
717 return extractedProps;
718 }
719 /**
720 * Given any captured exception, extract its keys and create a sorted
721 * and truncated list that will be used inside the event message.
722 * eg. `Non-error exception captured with keys: foo, bar, baz`
723 */
724 // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
725 function extractExceptionKeysForMessage(exception, maxLength = 40) {
726 const keys = Object.keys(convertToPlainObject(exception));
727 keys.sort();
728 if (!keys.length) {
729 return '[object has no keys]';
730 }
731 if (keys[0].length >= maxLength) {
732 return truncate(keys[0], maxLength);
733 }
734 for (let includedKeys = keys.length; includedKeys > 0; includedKeys--) {
735 const serialized = keys.slice(0, includedKeys).join(', ');
736 if (serialized.length > maxLength) {
737 continue;
738 }
739 if (includedKeys === keys.length) {
740 return serialized;
741 }
742 return truncate(serialized, maxLength);
743 }
744 return '';
745 }
746 /**
747 * Given any object, return the new object with removed keys that value was `undefined`.
748 * Works recursively on objects and arrays.
749 */
750 function dropUndefinedKeys(val) {
751 if (isPlainObject(val)) {
752 const rv = {};
753 for (const key of Object.keys(val)) {
754 if (typeof val[key] !== 'undefined') {
755 rv[key] = dropUndefinedKeys(val[key]);
756 }
757 }
758 return rv;
759 }
760 if (Array.isArray(val)) {
761 return val.map(dropUndefinedKeys);
762 }
763 return val;
764 }
765
766 const STACKTRACE_LIMIT = 50;
767 /**
768 * Creates a stack parser with the supplied line parsers
769 *
770 * StackFrames are returned in the correct order for Sentry Exception
771 * frames and with Sentry SDK internal frames removed from the top and bottom
772 *
773 */
774 function createStackParser(...parsers) {
775 const sortedParsers = parsers.sort((a, b) => a[0] - b[0]).map(p => p[1]);
776 return (stack, skipFirst = 0) => {
777 const frames = [];
778 for (const line of stack.split('\n').slice(skipFirst)) {
779 for (const parser of sortedParsers) {
780 const frame = parser(line);
781 if (frame) {
782 frames.push(frame);
783 break;
784 }
785 }
786 }
787 return stripSentryFramesAndReverse(frames);
788 };
789 }
790 /**
791 * @hidden
792 */
793 function stripSentryFramesAndReverse(stack) {
794 if (!stack.length) {
795 return [];
796 }
797 let localStack = stack;
798 const firstFrameFunction = localStack[0].function || '';
799 const lastFrameFunction = localStack[localStack.length - 1].function || '';
800 // If stack starts with one of our API calls, remove it (starts, meaning it's the top of the stack - aka last call)
801 if (firstFrameFunction.indexOf('captureMessage') !== -1 || firstFrameFunction.indexOf('captureException') !== -1) {
802 localStack = localStack.slice(1);
803 }
804 // If stack ends with one of our internal API calls, remove it (ends, meaning it's the bottom of the stack - aka top-most call)
805 if (lastFrameFunction.indexOf('sentryWrapped') !== -1) {
806 localStack = localStack.slice(0, -1);
807 }
808 // The frame where the crash happened, should be the last entry in the array
809 return localStack
810 .slice(0, STACKTRACE_LIMIT)
811 .map(frame => (Object.assign(Object.assign({}, frame), { filename: frame.filename || localStack[0].filename, function: frame.function || '?' })))
812 .reverse();
813 }
814 const defaultFunctionName = '<anonymous>';
815 /**
816 * Safely extract function name from itself
817 */
818 function getFunctionName(fn) {
819 try {
820 if (!fn || typeof fn !== 'function') {
821 return defaultFunctionName;
822 }
823 return fn.name || defaultFunctionName;
824 }
825 catch (e) {
826 // Just accessing custom props in some Selenium environments
827 // can cause a "Permission denied" exception (see raven-js#495).
828 return defaultFunctionName;
829 }
830 }
831
832 /**
833 * Tells whether current environment supports Fetch API
834 * {@link supportsFetch}.
835 *
836 * @returns Answer to the given question.
837 */
838 function supportsFetch() {
839 if (!('fetch' in getGlobalObject())) {
840 return false;
841 }
842 try {
843 new Headers();
844 new Request('');
845 new Response();
846 return true;
847 }
848 catch (e) {
849 return false;
850 }
851 }
852 /**
853 * isNativeFetch checks if the given function is a native implementation of fetch()
854 */
855 // eslint-disable-next-line @typescript-eslint/ban-types
856 function isNativeFetch(func) {
857 return func && /^function fetch\(\)\s+\{\s+\[native code\]\s+\}$/.test(func.toString());
858 }
859 /**
860 * Tells whether current environment supports Fetch API natively
861 * {@link supportsNativeFetch}.
862 *
863 * @returns true if `window.fetch` is natively implemented, false otherwise
864 */
865 function supportsNativeFetch() {
866 if (!supportsFetch()) {
867 return false;
868 }
869 const global = getGlobalObject();
870 // Fast path to avoid DOM I/O
871 // eslint-disable-next-line @typescript-eslint/unbound-method
872 if (isNativeFetch(global.fetch)) {
873 return true;
874 }
875 // window.fetch is implemented, but is polyfilled or already wrapped (e.g: by a chrome extension)
876 // so create a "pure" iframe to see if that has native fetch
877 let result = false;
878 const doc = global.document;
879 // eslint-disable-next-line deprecation/deprecation
880 if (doc && typeof doc.createElement === 'function') {
881 try {
882 const sandbox = doc.createElement('iframe');
883 sandbox.hidden = true;
884 doc.head.appendChild(sandbox);
885 if (sandbox.contentWindow && sandbox.contentWindow.fetch) {
886 // eslint-disable-next-line @typescript-eslint/unbound-method
887 result = isNativeFetch(sandbox.contentWindow.fetch);
888 }
889 doc.head.removeChild(sandbox);
890 }
891 catch (err) {
892 logger.warn('Could not create sandbox iframe for pure fetch check, bailing to window.fetch: ', err);
893 }
894 }
895 return result;
896 }
897 /**
898 * Tells whether current environment supports Referrer Policy API
899 * {@link supportsReferrerPolicy}.
900 *
901 * @returns Answer to the given question.
902 */
903 function supportsReferrerPolicy() {
904 // Despite all stars in the sky saying that Edge supports old draft syntax, aka 'never', 'always', 'origin' and 'default'
905 // (see https://caniuse.com/#feat=referrer-policy),
906 // it doesn't. And it throws an exception instead of ignoring this parameter...
907 // REF: https://github.com/getsentry/raven-js/issues/1233
908 if (!supportsFetch()) {
909 return false;
910 }
911 try {
912 new Request('_', {
913 referrerPolicy: 'origin',
914 });
915 return true;
916 }
917 catch (e) {
918 return false;
919 }
920 }
921 /**
922 * Tells whether current environment supports History API
923 * {@link supportsHistory}.
924 *
925 * @returns Answer to the given question.
926 */
927 function supportsHistory() {
928 // NOTE: in Chrome App environment, touching history.pushState, *even inside
929 // a try/catch block*, will cause Chrome to output an error to console.error
930 // borrowed from: https://github.com/angular/angular.js/pull/13945/files
931 const global = getGlobalObject();
932 /* eslint-disable @typescript-eslint/no-unsafe-member-access */
933 // eslint-disable-next-line @typescript-eslint/no-explicit-any
934 const chrome = global.chrome;
935 const isChromePackagedApp = chrome && chrome.app && chrome.app.runtime;
936 /* eslint-enable @typescript-eslint/no-unsafe-member-access */
937 const hasHistoryApi = 'history' in global && !!global.history.pushState && !!global.history.replaceState;
938 return !isChromePackagedApp && hasHistoryApi;
939 }
940
941 const global$5 = getGlobalObject();
942 /**
943 * Instrument native APIs to call handlers that can be used to create breadcrumbs, APM spans etc.
944 * - Console API
945 * - Fetch API
946 * - XHR API
947 * - History API
948 * - DOM API (click/typing)
949 * - Error API
950 * - UnhandledRejection API
951 */
952 const handlers = {};
953 const instrumented = {};
954 /** Instruments given API */
955 function instrument(type) {
956 if (instrumented[type]) {
957 return;
958 }
959 instrumented[type] = true;
960 switch (type) {
961 case 'console':
962 instrumentConsole();
963 break;
964 case 'dom':
965 instrumentDOM();
966 break;
967 case 'xhr':
968 instrumentXHR();
969 break;
970 case 'fetch':
971 instrumentFetch();
972 break;
973 case 'history':
974 instrumentHistory();
975 break;
976 case 'error':
977 instrumentError();
978 break;
979 case 'unhandledrejection':
980 instrumentUnhandledRejection();
981 break;
982 default:
983 logger.warn('unknown instrumentation type:', type);
984 return;
985 }
986 }
987 /**
988 * Add handler that will be called when given type of instrumentation triggers.
989 * Use at your own risk, this might break without changelog notice, only used internally.
990 * @hidden
991 */
992 function addInstrumentationHandler(type, callback) {
993 handlers[type] = handlers[type] || [];
994 handlers[type].push(callback);
995 instrument(type);
996 }
997 /** JSDoc */
998 function triggerHandlers(type, data) {
999 if (!type || !handlers[type]) {
1000 return;
1001 }
1002 for (const handler of handlers[type] || []) {
1003 try {
1004 handler(data);
1005 }
1006 catch (e) {
1007 logger.error(`Error while triggering instrumentation handler.\nType: ${type}\nName: ${getFunctionName(handler)}\nError:`, e);
1008 }
1009 }
1010 }
1011 /** JSDoc */
1012 function instrumentConsole() {
1013 if (!('console' in global$5)) {
1014 return;
1015 }
1016 CONSOLE_LEVELS.forEach(function (level) {
1017 if (!(level in global$5.console)) {
1018 return;
1019 }
1020 fill(global$5.console, level, function (originalConsoleMethod) {
1021 return function (...args) {
1022 triggerHandlers('console', { args, level });
1023 // this fails for some browsers. :(
1024 if (originalConsoleMethod) {
1025 originalConsoleMethod.apply(global$5.console, args);
1026 }
1027 };
1028 });
1029 });
1030 }
1031 /** JSDoc */
1032 function instrumentFetch() {
1033 if (!supportsNativeFetch()) {
1034 return;
1035 }
1036 fill(global$5, 'fetch', function (originalFetch) {
1037 return function (...args) {
1038 const handlerData = {
1039 args,
1040 fetchData: {
1041 method: getFetchMethod(args),
1042 url: getFetchUrl(args),
1043 },
1044 startTimestamp: Date.now(),
1045 };
1046 triggerHandlers('fetch', Object.assign({}, handlerData));
1047 // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1048 return originalFetch.apply(global$5, args).then((response) => {
1049 triggerHandlers('fetch', Object.assign(Object.assign({}, handlerData), { endTimestamp: Date.now(), response }));
1050 return response;
1051 }, (error) => {
1052 triggerHandlers('fetch', Object.assign(Object.assign({}, handlerData), { endTimestamp: Date.now(), error }));
1053 // NOTE: If you are a Sentry user, and you are seeing this stack frame,
1054 // it means the sentry.javascript SDK caught an error invoking your application code.
1055 // This is expected behavior and NOT indicative of a bug with sentry.javascript.
1056 throw error;
1057 });
1058 };
1059 });
1060 }
1061 /* eslint-disable @typescript-eslint/no-unsafe-member-access */
1062 /** Extract `method` from fetch call arguments */
1063 function getFetchMethod(fetchArgs = []) {
1064 if ('Request' in global$5 && isInstanceOf(fetchArgs[0], Request) && fetchArgs[0].method) {
1065 return String(fetchArgs[0].method).toUpperCase();
1066 }
1067 if (fetchArgs[1] && fetchArgs[1].method) {
1068 return String(fetchArgs[1].method).toUpperCase();
1069 }
1070 return 'GET';
1071 }
1072 /** Extract `url` from fetch call arguments */
1073 function getFetchUrl(fetchArgs = []) {
1074 if (typeof fetchArgs[0] === 'string') {
1075 return fetchArgs[0];
1076 }
1077 if ('Request' in global$5 && isInstanceOf(fetchArgs[0], Request)) {
1078 return fetchArgs[0].url;
1079 }
1080 return String(fetchArgs[0]);
1081 }
1082 /* eslint-enable @typescript-eslint/no-unsafe-member-access */
1083 /** JSDoc */
1084 function instrumentXHR() {
1085 if (!('XMLHttpRequest' in global$5)) {
1086 return;
1087 }
1088 const xhrproto = XMLHttpRequest.prototype;
1089 fill(xhrproto, 'open', function (originalOpen) {
1090 return function (...args) {
1091 // eslint-disable-next-line @typescript-eslint/no-this-alias
1092 const xhr = this;
1093 const url = args[1];
1094 const xhrInfo = (xhr.__sentry_xhr__ = {
1095 // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1096 method: isString(args[0]) ? args[0].toUpperCase() : args[0],
1097 url: args[1],
1098 });
1099 // if Sentry key appears in URL, don't capture it as a request
1100 // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1101 if (isString(url) && xhrInfo.method === 'POST' && url.match(/sentry_key/)) {
1102 xhr.__sentry_own_request__ = true;
1103 }
1104 const onreadystatechangeHandler = function () {
1105 if (xhr.readyState === 4) {
1106 try {
1107 // touching statusCode in some platforms throws
1108 // an exception
1109 xhrInfo.status_code = xhr.status;
1110 }
1111 catch (e) {
1112 /* do nothing */
1113 }
1114 triggerHandlers('xhr', {
1115 args,
1116 endTimestamp: Date.now(),
1117 startTimestamp: Date.now(),
1118 xhr,
1119 });
1120 }
1121 };
1122 if ('onreadystatechange' in xhr && typeof xhr.onreadystatechange === 'function') {
1123 fill(xhr, 'onreadystatechange', function (original) {
1124 return function (...readyStateArgs) {
1125 onreadystatechangeHandler();
1126 return original.apply(xhr, readyStateArgs);
1127 };
1128 });
1129 }
1130 else {
1131 xhr.addEventListener('readystatechange', onreadystatechangeHandler);
1132 }
1133 return originalOpen.apply(xhr, args);
1134 };
1135 });
1136 fill(xhrproto, 'send', function (originalSend) {
1137 return function (...args) {
1138 if (this.__sentry_xhr__ && args[0] !== undefined) {
1139 this.__sentry_xhr__.body = args[0];
1140 }
1141 triggerHandlers('xhr', {
1142 args,
1143 startTimestamp: Date.now(),
1144 xhr: this,
1145 });
1146 return originalSend.apply(this, args);
1147 };
1148 });
1149 }
1150 let lastHref;
1151 /** JSDoc */
1152 function instrumentHistory() {
1153 if (!supportsHistory()) {
1154 return;
1155 }
1156 const oldOnPopState = global$5.onpopstate;
1157 global$5.onpopstate = function (...args) {
1158 const to = global$5.location.href;
1159 // keep track of the current URL state, as we always receive only the updated state
1160 const from = lastHref;
1161 lastHref = to;
1162 triggerHandlers('history', {
1163 from,
1164 to,
1165 });
1166 if (oldOnPopState) {
1167 // Apparently this can throw in Firefox when incorrectly implemented plugin is installed.
1168 // https://github.com/getsentry/sentry-javascript/issues/3344
1169 // https://github.com/bugsnag/bugsnag-js/issues/469
1170 try {
1171 return oldOnPopState.apply(this, args);
1172 }
1173 catch (_oO) {
1174 // no-empty
1175 }
1176 }
1177 };
1178 /** @hidden */
1179 function historyReplacementFunction(originalHistoryFunction) {
1180 return function (...args) {
1181 const url = args.length > 2 ? args[2] : undefined;
1182 if (url) {
1183 // coerce to string (this is what pushState does)
1184 const from = lastHref;
1185 const to = String(url);
1186 // keep track of the current URL state, as we always receive only the updated state
1187 lastHref = to;
1188 triggerHandlers('history', {
1189 from,
1190 to,
1191 });
1192 }
1193 return originalHistoryFunction.apply(this, args);
1194 };
1195 }
1196 fill(global$5.history, 'pushState', historyReplacementFunction);
1197 fill(global$5.history, 'replaceState', historyReplacementFunction);
1198 }
1199 const debounceDuration = 1000;
1200 let debounceTimerID;
1201 let lastCapturedEvent;
1202 /**
1203 * Decide whether the current event should finish the debounce of previously captured one.
1204 * @param previous previously captured event
1205 * @param current event to be captured
1206 */
1207 function shouldShortcircuitPreviousDebounce(previous, current) {
1208 // If there was no previous event, it should always be swapped for the new one.
1209 if (!previous) {
1210 return true;
1211 }
1212 // If both events have different type, then user definitely performed two separate actions. e.g. click + keypress.
1213 if (previous.type !== current.type) {
1214 return true;
1215 }
1216 try {
1217 // If both events have the same type, it's still possible that actions were performed on different targets.
1218 // e.g. 2 clicks on different buttons.
1219 if (previous.target !== current.target) {
1220 return true;
1221 }
1222 }
1223 catch (e) {
1224 // just accessing `target` property can throw an exception in some rare circumstances
1225 // see: https://github.com/getsentry/sentry-javascript/issues/838
1226 }
1227 // If both events have the same type _and_ same `target` (an element which triggered an event, _not necessarily_
1228 // to which an event listener was attached), we treat them as the same action, as we want to capture
1229 // only one breadcrumb. e.g. multiple clicks on the same button, or typing inside a user input box.
1230 return false;
1231 }
1232 /**
1233 * Decide whether an event should be captured.
1234 * @param event event to be captured
1235 */
1236 function shouldSkipDOMEvent(event) {
1237 // We are only interested in filtering `keypress` events for now.
1238 if (event.type !== 'keypress') {
1239 return false;
1240 }
1241 try {
1242 const target = event.target;
1243 if (!target || !target.tagName) {
1244 return true;
1245 }
1246 // Only consider keypress events on actual input elements. This will disregard keypresses targeting body
1247 // e.g.tabbing through elements, hotkeys, etc.
1248 if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) {
1249 return false;
1250 }
1251 }
1252 catch (e) {
1253 // just accessing `target` property can throw an exception in some rare circumstances
1254 // see: https://github.com/getsentry/sentry-javascript/issues/838
1255 }
1256 return true;
1257 }
1258 /**
1259 * Wraps addEventListener to capture UI breadcrumbs
1260 * @param handler function that will be triggered
1261 * @param globalListener indicates whether event was captured by the global event listener
1262 * @returns wrapped breadcrumb events handler
1263 * @hidden
1264 */
1265 function makeDOMEventHandler(handler, globalListener = false) {
1266 return (event) => {
1267 // It's possible this handler might trigger multiple times for the same
1268 // event (e.g. event propagation through node ancestors).
1269 // Ignore if we've already captured that event.
1270 if (!event || lastCapturedEvent === event) {
1271 return;
1272 }
1273 // We always want to skip _some_ events.
1274 if (shouldSkipDOMEvent(event)) {
1275 return;
1276 }
1277 const name = event.type === 'keypress' ? 'input' : event.type;
1278 // If there is no debounce timer, it means that we can safely capture the new event and store it for future comparisons.
1279 if (debounceTimerID === undefined) {
1280 handler({
1281 event: event,
1282 name,
1283 global: globalListener,
1284 });
1285 lastCapturedEvent = event;
1286 }
1287 // If there is a debounce awaiting, see if the new event is different enough to treat it as a unique one.
1288 // If that's the case, emit the previous event and store locally the newly-captured DOM event.
1289 else if (shouldShortcircuitPreviousDebounce(lastCapturedEvent, event)) {
1290 handler({
1291 event: event,
1292 name,
1293 global: globalListener,
1294 });
1295 lastCapturedEvent = event;
1296 }
1297 // Start a new debounce timer that will prevent us from capturing multiple events that should be grouped together.
1298 clearTimeout(debounceTimerID);
1299 debounceTimerID = global$5.setTimeout(() => {
1300 debounceTimerID = undefined;
1301 }, debounceDuration);
1302 };
1303 }
1304 /** JSDoc */
1305 function instrumentDOM() {
1306 if (!('document' in global$5)) {
1307 return;
1308 }
1309 // Make it so that any click or keypress that is unhandled / bubbled up all the way to the document triggers our dom
1310 // handlers. (Normally we have only one, which captures a breadcrumb for each click or keypress.) Do this before
1311 // we instrument `addEventListener` so that we don't end up attaching this handler twice.
1312 const triggerDOMHandler = triggerHandlers.bind(null, 'dom');
1313 const globalDOMEventHandler = makeDOMEventHandler(triggerDOMHandler, true);
1314 global$5.document.addEventListener('click', globalDOMEventHandler, false);
1315 global$5.document.addEventListener('keypress', globalDOMEventHandler, false);
1316 // After hooking into click and keypress events bubbled up to `document`, we also hook into user-handled
1317 // clicks & keypresses, by adding an event listener of our own to any element to which they add a listener. That
1318 // way, whenever one of their handlers is triggered, ours will be, too. (This is needed because their handler
1319 // could potentially prevent the event from bubbling up to our global listeners. This way, our handler are still
1320 // guaranteed to fire at least once.)
1321 ['EventTarget', 'Node'].forEach((target) => {
1322 // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1323 const proto = global$5[target] && global$5[target].prototype;
1324 // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, no-prototype-builtins
1325 if (!proto || !proto.hasOwnProperty || !proto.hasOwnProperty('addEventListener')) {
1326 return;
1327 }
1328 fill(proto, 'addEventListener', function (originalAddEventListener) {
1329 return function (type, listener, options) {
1330 if (type === 'click' || type == 'keypress') {
1331 try {
1332 const el = this;
1333 const handlers = (el.__sentry_instrumentation_handlers__ = el.__sentry_instrumentation_handlers__ || {});
1334 const handlerForType = (handlers[type] = handlers[type] || { refCount: 0 });
1335 if (!handlerForType.handler) {
1336 const handler = makeDOMEventHandler(triggerDOMHandler);
1337 handlerForType.handler = handler;
1338 originalAddEventListener.call(this, type, handler, options);
1339 }
1340 handlerForType.refCount += 1;
1341 }
1342 catch (e) {
1343 // Accessing dom properties is always fragile.
1344 // Also allows us to skip `addEventListenrs` calls with no proper `this` context.
1345 }
1346 }
1347 return originalAddEventListener.call(this, type, listener, options);
1348 };
1349 });
1350 fill(proto, 'removeEventListener', function (originalRemoveEventListener) {
1351 return function (type, listener, options) {
1352 if (type === 'click' || type == 'keypress') {
1353 try {
1354 const el = this;
1355 const handlers = el.__sentry_instrumentation_handlers__ || {};
1356 const handlerForType = handlers[type];
1357 if (handlerForType) {
1358 handlerForType.refCount -= 1;
1359 // If there are no longer any custom handlers of the current type on this element, we can remove ours, too.
1360 if (handlerForType.refCount <= 0) {
1361 originalRemoveEventListener.call(this, type, handlerForType.handler, options);
1362 handlerForType.handler = undefined;
1363 delete handlers[type]; // eslint-disable-line @typescript-eslint/no-dynamic-delete
1364 }
1365 // If there are no longer any custom handlers of any type on this element, cleanup everything.
1366 if (Object.keys(handlers).length === 0) {
1367 delete el.__sentry_instrumentation_handlers__;
1368 }
1369 }
1370 }
1371 catch (e) {
1372 // Accessing dom properties is always fragile.
1373 // Also allows us to skip `addEventListenrs` calls with no proper `this` context.
1374 }
1375 }
1376 return originalRemoveEventListener.call(this, type, listener, options);
1377 };
1378 });
1379 });
1380 }
1381 let _oldOnErrorHandler = null;
1382 /** JSDoc */
1383 function instrumentError() {
1384 _oldOnErrorHandler = global$5.onerror;
1385 global$5.onerror = function (msg, url, line, column, error) {
1386 triggerHandlers('error', {
1387 column,
1388 error,
1389 line,
1390 msg,
1391 url,
1392 });
1393 if (_oldOnErrorHandler) {
1394 // eslint-disable-next-line prefer-rest-params
1395 return _oldOnErrorHandler.apply(this, arguments);
1396 }
1397 return false;
1398 };
1399 }
1400 let _oldOnUnhandledRejectionHandler = null;
1401 /** JSDoc */
1402 function instrumentUnhandledRejection() {
1403 _oldOnUnhandledRejectionHandler = global$5.onunhandledrejection;
1404 global$5.onunhandledrejection = function (e) {
1405 triggerHandlers('unhandledrejection', e);
1406 if (_oldOnUnhandledRejectionHandler) {
1407 // eslint-disable-next-line prefer-rest-params
1408 return _oldOnUnhandledRejectionHandler.apply(this, arguments);
1409 }
1410 return true;
1411 };
1412 }
1413
1414 /* eslint-disable @typescript-eslint/no-unsafe-member-access */
1415 /* eslint-disable @typescript-eslint/no-explicit-any */
1416 /**
1417 * Helper to decycle json objects
1418 */
1419 function memoBuilder() {
1420 const hasWeakSet = typeof WeakSet === 'function';
1421 const inner = hasWeakSet ? new WeakSet() : [];
1422 function memoize(obj) {
1423 if (hasWeakSet) {
1424 if (inner.has(obj)) {
1425 return true;
1426 }
1427 inner.add(obj);
1428 return false;
1429 }
1430 // eslint-disable-next-line @typescript-eslint/prefer-for-of
1431 for (let i = 0; i < inner.length; i++) {
1432 const value = inner[i];
1433 if (value === obj) {
1434 return true;
1435 }
1436 }
1437 inner.push(obj);
1438 return false;
1439 }
1440 function unmemoize(obj) {
1441 if (hasWeakSet) {
1442 inner.delete(obj);
1443 }
1444 else {
1445 for (let i = 0; i < inner.length; i++) {
1446 if (inner[i] === obj) {
1447 inner.splice(i, 1);
1448 break;
1449 }
1450 }
1451 }
1452 }
1453 return [memoize, unmemoize];
1454 }
1455
1456 /**
1457 * UUID4 generator
1458 *
1459 * @returns string Generated UUID4.
1460 */
1461 function uuid4() {
1462 const global = getGlobalObject();
1463 const crypto = global.crypto || global.msCrypto;
1464 if (!(crypto === void 0) && crypto.getRandomValues) {
1465 // Use window.crypto API if available
1466 const arr = new Uint16Array(8);
1467 crypto.getRandomValues(arr);
1468 // set 4 in byte 7
1469 // eslint-disable-next-line no-bitwise
1470 arr[3] = (arr[3] & 0xfff) | 0x4000;
1471 // set 2 most significant bits of byte 9 to '10'
1472 // eslint-disable-next-line no-bitwise
1473 arr[4] = (arr[4] & 0x3fff) | 0x8000;
1474 const pad = (num) => {
1475 let v = num.toString(16);
1476 while (v.length < 4) {
1477 v = `0${v}`;
1478 }
1479 return v;
1480 };
1481 return (pad(arr[0]) + pad(arr[1]) + pad(arr[2]) + pad(arr[3]) + pad(arr[4]) + pad(arr[5]) + pad(arr[6]) + pad(arr[7]));
1482 }
1483 // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
1484 return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, c => {
1485 // eslint-disable-next-line no-bitwise
1486 const r = (Math.random() * 16) | 0;
1487 // eslint-disable-next-line no-bitwise
1488 const v = c === 'x' ? r : (r & 0x3) | 0x8;
1489 return v.toString(16);
1490 });
1491 }
1492 /**
1493 * Parses string form of URL into an object
1494 * // borrowed from https://tools.ietf.org/html/rfc3986#appendix-B
1495 * // intentionally using regex and not <a/> href parsing trick because React Native and other
1496 * // environments where DOM might not be available
1497 * @returns parsed URL object
1498 */
1499 function parseUrl(url) {
1500 if (!url) {
1501 return {};
1502 }
1503 const match = url.match(/^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/);
1504 if (!match) {
1505 return {};
1506 }
1507 // coerce to undefined values to empty string so we don't get 'undefined'
1508 const query = match[6] || '';
1509 const fragment = match[8] || '';
1510 return {
1511 host: match[4],
1512 path: match[5],
1513 protocol: match[2],
1514 relative: match[5] + query + fragment,
1515 };
1516 }
1517 function getFirstException(event) {
1518 return event.exception && event.exception.values ? event.exception.values[0] : undefined;
1519 }
1520 /**
1521 * Extracts either message or type+value from an event that can be used for user-facing logs
1522 * @returns event's description
1523 */
1524 function getEventDescription(event) {
1525 const { message, event_id: eventId } = event;
1526 if (message) {
1527 return message;
1528 }
1529 const firstException = getFirstException(event);
1530 if (firstException) {
1531 if (firstException.type && firstException.value) {
1532 return `${firstException.type}: ${firstException.value}`;
1533 }
1534 return firstException.type || firstException.value || eventId || '<unknown>';
1535 }
1536 return eventId || '<unknown>';
1537 }
1538 /**
1539 * Adds exception values, type and value to an synthetic Exception.
1540 * @param event The event to modify.
1541 * @param value Value of the exception.
1542 * @param type Type of the exception.
1543 * @hidden
1544 */
1545 function addExceptionTypeValue(event, value, type) {
1546 const exception = (event.exception = event.exception || {});
1547 const values = (exception.values = exception.values || []);
1548 const firstException = (values[0] = values[0] || {});
1549 if (!firstException.value) {
1550 firstException.value = value || '';
1551 }
1552 if (!firstException.type) {
1553 firstException.type = type || 'Error';
1554 }
1555 }
1556 /**
1557 * Adds exception mechanism data to a given event. Uses defaults if the second parameter is not passed.
1558 *
1559 * @param event The event to modify.
1560 * @param newMechanism Mechanism data to add to the event.
1561 * @hidden
1562 */
1563 function addExceptionMechanism(event, newMechanism) {
1564 const firstException = getFirstException(event);
1565 if (!firstException) {
1566 return;
1567 }
1568 const defaultMechanism = { type: 'generic', handled: true };
1569 const currentMechanism = firstException.mechanism;
1570 firstException.mechanism = Object.assign(Object.assign(Object.assign({}, defaultMechanism), currentMechanism), newMechanism);
1571 if (newMechanism && 'data' in newMechanism) {
1572 const mergedData = Object.assign(Object.assign({}, (currentMechanism && currentMechanism.data)), newMechanism.data);
1573 firstException.mechanism.data = mergedData;
1574 }
1575 }
1576 /**
1577 * Checks whether or not we've already captured the given exception (note: not an identical exception - the very object
1578 * in question), and marks it captured if not.
1579 *
1580 * This is useful because it's possible for an error to get captured by more than one mechanism. After we intercept and
1581 * record an error, we rethrow it (assuming we've intercepted it before it's reached the top-level global handlers), so
1582 * that we don't interfere with whatever effects the error might have had were the SDK not there. At that point, because
1583 * the error has been rethrown, it's possible for it to bubble up to some other code we've instrumented. If it's not
1584 * caught after that, it will bubble all the way up to the global handlers (which of course we also instrument). This
1585 * function helps us ensure that even if we encounter the same error more than once, we only record it the first time we
1586 * see it.
1587 *
1588 * Note: It will ignore primitives (always return `false` and not mark them as seen), as properties can't be set on
1589 * them. {@link: Object.objectify} can be used on exceptions to convert any that are primitives into their equivalent
1590 * object wrapper forms so that this check will always work. However, because we need to flag the exact object which
1591 * will get rethrown, and because that rethrowing happens outside of the event processing pipeline, the objectification
1592 * must be done before the exception captured.
1593 *
1594 * @param A thrown exception to check or flag as having been seen
1595 * @returns `true` if the exception has already been captured, `false` if not (with the side effect of marking it seen)
1596 */
1597 function checkOrSetAlreadyCaught(exception) {
1598 // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1599 if (exception && exception.__sentry_captured__) {
1600 return true;
1601 }
1602 try {
1603 // set it this way rather than by assignment so that it's not ennumerable and therefore isn't recorded by the
1604 // `ExtraErrorData` integration
1605 addNonEnumerableProperty(exception, '__sentry_captured__', true);
1606 }
1607 catch (err) {
1608 // `exception` is a primitive, so we can't mark it seen
1609 }
1610 return false;
1611 }
1612
1613 /**
1614 * Recursively normalizes the given object.
1615 *
1616 * - Creates a copy to prevent original input mutation
1617 * - Skips non-enumerable properties
1618 * - When stringifying, calls `toJSON` if implemented
1619 * - Removes circular references
1620 * - Translates non-serializable values (`undefined`/`NaN`/functions) to serializable format
1621 * - Translates known global objects/classes to a string representations
1622 * - Takes care of `Error` object serialization
1623 * - Optionally limits depth of final output
1624 * - Optionally limits number of properties/elements included in any single object/array
1625 *
1626 * @param input The object to be normalized.
1627 * @param depth The max depth to which to normalize the object. (Anything deeper stringified whole.)
1628 * @param maxProperties The max number of elements or properties to be included in any single array or
1629 * object in the normallized output..
1630 * @returns A normalized version of the object, or `"**non-serializable**"` if any errors are thrown during normalization.
1631 */
1632 function normalize(input, depth = +Infinity, maxProperties = +Infinity) {
1633 try {
1634 // since we're at the outermost level, there is no key
1635 return visit('', input, depth, maxProperties);
1636 }
1637 catch (err) {
1638 return { ERROR: `**non-serializable** (${err})` };
1639 }
1640 }
1641 /** JSDoc */
1642 function normalizeToSize(object,
1643 // Default Node.js REPL depth
1644 depth = 3,
1645 // 100kB, as 200kB is max payload size, so half sounds reasonable
1646 maxSize = 100 * 1024) {
1647 const normalized = normalize(object, depth);
1648 if (jsonSize(normalized) > maxSize) {
1649 return normalizeToSize(object, depth - 1, maxSize);
1650 }
1651 return normalized;
1652 }
1653 /**
1654 * Visits a node to perform normalization on it
1655 *
1656 * @param key The key corresponding to the given node
1657 * @param value The node to be visited
1658 * @param depth Optional number indicating the maximum recursion depth
1659 * @param maxProperties Optional maximum number of properties/elements included in any single object/array
1660 * @param memo Optional Memo class handling decycling
1661 */
1662 function visit(key, value, depth = +Infinity, maxProperties = +Infinity, memo = memoBuilder()) {
1663 const [memoize, unmemoize] = memo;
1664 // If the value has a `toJSON` method, see if we can bail and let it do the work
1665 const valueWithToJSON = value;
1666 if (valueWithToJSON && typeof valueWithToJSON.toJSON === 'function') {
1667 try {
1668 return valueWithToJSON.toJSON();
1669 }
1670 catch (err) {
1671 // pass (The built-in `toJSON` failed, but we can still try to do it ourselves)
1672 }
1673 }
1674 // Get the simple cases out of the way first
1675 if (value === null || (['number', 'boolean', 'string'].includes(typeof value) && !isNaN$1(value))) {
1676 return value;
1677 }
1678 const stringified = stringifyValue(key, value);
1679 // Anything we could potentially dig into more (objects or arrays) will have come back as `"[object XXXX]"`.
1680 // Everything else will have already been serialized, so if we don't see that pattern, we're done.
1681 if (!stringified.startsWith('[object ')) {
1682 return stringified;
1683 }
1684 // We're also done if we've reached the max depth
1685 if (depth === 0) {
1686 // At this point we know `serialized` is a string of the form `"[object XXXX]"`. Clean it up so it's just `"[XXXX]"`.
1687 return stringified.replace('object ', '');
1688 }
1689 // If we've already visited this branch, bail out, as it's circular reference. If not, note that we're seeing it now.
1690 if (memoize(value)) {
1691 return '[Circular ~]';
1692 }
1693 // At this point we know we either have an object or an array, we haven't seen it before, and we're going to recurse
1694 // because we haven't yet reached the max depth. Create an accumulator to hold the results of visiting each
1695 // property/entry, and keep track of the number of items we add to it.
1696 const normalized = (Array.isArray(value) ? [] : {});
1697 let numAdded = 0;
1698 // Before we begin, convert`Error` and`Event` instances into plain objects, since some of each of their relevant
1699 // properties are non-enumerable and otherwise would get missed.
1700 const visitable = (isError(value) || isEvent(value) ? convertToPlainObject(value) : value);
1701 for (const visitKey in visitable) {
1702 // Avoid iterating over fields in the prototype if they've somehow been exposed to enumeration.
1703 if (!Object.prototype.hasOwnProperty.call(visitable, visitKey)) {
1704 continue;
1705 }
1706 if (numAdded >= maxProperties) {
1707 normalized[visitKey] = '[MaxProperties ~]';
1708 break;
1709 }
1710 // Recursively visit all the child nodes
1711 const visitValue = visitable[visitKey];
1712 normalized[visitKey] = visit(visitKey, visitValue, depth - 1, maxProperties, memo);
1713 numAdded += 1;
1714 }
1715 // Once we've visited all the branches, remove the parent from memo storage
1716 unmemoize(value);
1717 // Return accumulated values
1718 return normalized;
1719 }
1720 /**
1721 * Stringify the given value. Handles various known special values and types.
1722 *
1723 * Not meant to be used on simple primitives which already have a string representation, as it will, for example, turn
1724 * the number 1231 into "[Object Number]", nor on `null`, as it will throw.
1725 *
1726 * @param value The value to stringify
1727 * @returns A stringified representation of the given value
1728 */
1729 function stringifyValue(key,
1730 // this type is a tiny bit of a cheat, since this function does handle NaN (which is technically a number), but for
1731 // our internal use, it'll do
1732 value) {
1733 try {
1734 if (key === 'domain' && value && typeof value === 'object' && value._events) {
1735 return '[Domain]';
1736 }
1737 if (key === 'domainEmitter') {
1738 return '[DomainEmitter]';
1739 }
1740 // It's safe to use `global`, `window`, and `document` here in this manner, as we are asserting using `typeof` first
1741 // which won't throw if they are not present.
1742 if (typeof global !== 'undefined' && value === global) {
1743 return '[Global]';
1744 }
1745 // eslint-disable-next-line no-restricted-globals
1746 if (typeof window !== 'undefined' && value === window) {
1747 return '[Window]';
1748 }
1749 // eslint-disable-next-line no-restricted-globals
1750 if (typeof document !== 'undefined' && value === document) {
1751 return '[Document]';
1752 }
1753 // React's SyntheticEvent thingy
1754 if (isSyntheticEvent(value)) {
1755 return '[SyntheticEvent]';
1756 }
1757 if (typeof value === 'number' && value !== value) {
1758 return '[NaN]';
1759 }
1760 // this catches `undefined` (but not `null`, which is a primitive and can be serialized on its own)
1761 if (value === void 0) {
1762 return '[undefined]';
1763 }
1764 if (typeof value === 'function') {
1765 return `[Function: ${getFunctionName(value)}]`;
1766 }
1767 if (typeof value === 'symbol') {
1768 return `[${String(value)}]`;
1769 }
1770 // stringified BigInts are indistinguishable from regular numbers, so we need to label them to avoid confusion
1771 if (typeof value === 'bigint') {
1772 return `[BigInt: ${String(value)}]`;
1773 }
1774 // Now that we've knocked out all the special cases and the primitives, all we have left are objects. Simply casting
1775 // them to strings means that instances of classes which haven't defined their `toStringTag` will just come out as
1776 // `"[object Object]"`. If we instead look at the constructor's name (which is the same as the name of the class),
1777 // we can make sure that only plain objects come out that way.
1778 return `[object ${Object.getPrototypeOf(value).constructor.name}]`;
1779 }
1780 catch (err) {
1781 return `**non-serializable** (${err})`;
1782 }
1783 }
1784 /** Calculates bytes size of input string */
1785 function utf8Length(value) {
1786 // eslint-disable-next-line no-bitwise
1787 return ~-encodeURI(value).split(/%..|./).length;
1788 }
1789 /** Calculates bytes size of input object */
1790 function jsonSize(value) {
1791 return utf8Length(JSON.stringify(value));
1792 }
1793
1794 /* eslint-disable @typescript-eslint/explicit-function-return-type */
1795 /**
1796 * Creates a resolved sync promise.
1797 *
1798 * @param value the value to resolve the promise with
1799 * @returns the resolved sync promise
1800 */
1801 function resolvedSyncPromise(value) {
1802 return new SyncPromise(resolve => {
1803 resolve(value);
1804 });
1805 }
1806 /**
1807 * Creates a rejected sync promise.
1808 *
1809 * @param value the value to reject the promise with
1810 * @returns the rejected sync promise
1811 */
1812 function rejectedSyncPromise(reason) {
1813 return new SyncPromise((_, reject) => {
1814 reject(reason);
1815 });
1816 }
1817 /**
1818 * Thenable class that behaves like a Promise and follows it's interface
1819 * but is not async internally
1820 */
1821 class SyncPromise {
1822 constructor(executor) {
1823 this._state = 0 /* PENDING */;
1824 this._handlers = [];
1825 /** JSDoc */
1826 this._resolve = (value) => {
1827 this._setResult(1 /* RESOLVED */, value);
1828 };
1829 /** JSDoc */
1830 this._reject = (reason) => {
1831 this._setResult(2 /* REJECTED */, reason);
1832 };
1833 /** JSDoc */
1834 this._setResult = (state, value) => {
1835 if (this._state !== 0 /* PENDING */) {
1836 return;
1837 }
1838 if (isThenable(value)) {
1839 void value.then(this._resolve, this._reject);
1840 return;
1841 }
1842 this._state = state;
1843 this._value = value;
1844 this._executeHandlers();
1845 };
1846 /** JSDoc */
1847 this._executeHandlers = () => {
1848 if (this._state === 0 /* PENDING */) {
1849 return;
1850 }
1851 const cachedHandlers = this._handlers.slice();
1852 this._handlers = [];
1853 cachedHandlers.forEach(handler => {
1854 if (handler[0]) {
1855 return;
1856 }
1857 if (this._state === 1 /* RESOLVED */) {
1858 // eslint-disable-next-line @typescript-eslint/no-floating-promises
1859 handler[1](this._value);
1860 }
1861 if (this._state === 2 /* REJECTED */) {
1862 handler[2](this._value);
1863 }
1864 handler[0] = true;
1865 });
1866 };
1867 try {
1868 executor(this._resolve, this._reject);
1869 }
1870 catch (e) {
1871 this._reject(e);
1872 }
1873 }
1874 /** JSDoc */
1875 then(onfulfilled, onrejected) {
1876 return new SyncPromise((resolve, reject) => {
1877 this._handlers.push([
1878 false,
1879 result => {
1880 if (!onfulfilled) {
1881 // TODO: ¯\_(ツ)_/¯
1882 // TODO: FIXME
1883 resolve(result);
1884 }
1885 else {
1886 try {
1887 resolve(onfulfilled(result));
1888 }
1889 catch (e) {
1890 reject(e);
1891 }
1892 }
1893 },
1894 reason => {
1895 if (!onrejected) {
1896 reject(reason);
1897 }
1898 else {
1899 try {
1900 resolve(onrejected(reason));
1901 }
1902 catch (e) {
1903 reject(e);
1904 }
1905 }
1906 },
1907 ]);
1908 this._executeHandlers();
1909 });
1910 }
1911 /** JSDoc */
1912 catch(onrejected) {
1913 return this.then(val => val, onrejected);
1914 }
1915 /** JSDoc */
1916 finally(onfinally) {
1917 return new SyncPromise((resolve, reject) => {
1918 let val;
1919 let isRejected;
1920 return this.then(value => {
1921 isRejected = false;
1922 val = value;
1923 if (onfinally) {
1924 onfinally();
1925 }
1926 }, reason => {
1927 isRejected = true;
1928 val = reason;
1929 if (onfinally) {
1930 onfinally();
1931 }
1932 }).then(() => {
1933 if (isRejected) {
1934 reject(val);
1935 return;
1936 }
1937 resolve(val);
1938 });
1939 });
1940 }
1941 }
1942
1943 /**
1944 * Creates an new PromiseBuffer object with the specified limit
1945 * @param limit max number of promises that can be stored in the buffer
1946 */
1947 function makePromiseBuffer(limit) {
1948 const buffer = [];
1949 function isReady() {
1950 return limit === undefined || buffer.length < limit;
1951 }
1952 /**
1953 * Remove a promise from the queue.
1954 *
1955 * @param task Can be any PromiseLike<T>
1956 * @returns Removed promise.
1957 */
1958 function remove(task) {
1959 return buffer.splice(buffer.indexOf(task), 1)[0];
1960 }
1961 /**
1962 * Add a promise (representing an in-flight action) to the queue, and set it to remove itself on fulfillment.
1963 *
1964 * @param taskProducer A function producing any PromiseLike<T>; In previous versions this used to be `task:
1965 * PromiseLike<T>`, but under that model, Promises were instantly created on the call-site and their executor
1966 * functions therefore ran immediately. Thus, even if the buffer was full, the action still happened. By
1967 * requiring the promise to be wrapped in a function, we can defer promise creation until after the buffer
1968 * limit check.
1969 * @returns The original promise.
1970 */
1971 function add(taskProducer) {
1972 if (!isReady()) {
1973 return rejectedSyncPromise(new SentryError('Not adding Promise due to buffer limit reached.'));
1974 }
1975 // start the task and add its promise to the queue
1976 const task = taskProducer();
1977 if (buffer.indexOf(task) === -1) {
1978 buffer.push(task);
1979 }
1980 void task
1981 .then(() => remove(task))
1982 // Use `then(null, rejectionHandler)` rather than `catch(rejectionHandler)` so that we can use `PromiseLike`
1983 // rather than `Promise`. `PromiseLike` doesn't have a `.catch` method, making its polyfill smaller. (ES5 didn't
1984 // have promises, so TS has to polyfill when down-compiling.)
1985 .then(null, () => remove(task).then(null, () => {
1986 // We have to add another catch here because `remove()` starts a new promise chain.
1987 }));
1988 return task;
1989 }
1990 /**
1991 * Wait for all promises in the queue to resolve or for timeout to expire, whichever comes first.
1992 *
1993 * @param timeout The time, in ms, after which to resolve to `false` if the queue is still non-empty. Passing `0` (or
1994 * not passing anything) will make the promise wait as long as it takes for the queue to drain before resolving to
1995 * `true`.
1996 * @returns A promise which will resolve to `true` if the queue is already empty or drains before the timeout, and
1997 * `false` otherwise
1998 */
1999 function drain(timeout) {
2000 return new SyncPromise((resolve, reject) => {
2001 let counter = buffer.length;
2002 if (!counter) {
2003 return resolve(true);
2004 }
2005 // wait for `timeout` ms and then resolve to `false` (if not cancelled first)
2006 const capturedSetTimeout = setTimeout(() => {
2007 if (timeout && timeout > 0) {
2008 resolve(false);
2009 }
2010 }, timeout);
2011 // if all promises resolve in time, cancel the timer and resolve to `true`
2012 buffer.forEach(item => {
2013 void resolvedSyncPromise(item).then(() => {
2014 // eslint-disable-next-line no-plusplus
2015 if (!--counter) {
2016 clearTimeout(capturedSetTimeout);
2017 resolve(true);
2018 }
2019 }, reject);
2020 });
2021 });
2022 }
2023 return {
2024 $: buffer,
2025 add,
2026 drain,
2027 };
2028 }
2029
2030 function isSupportedSeverity(level) {
2031 return SeverityLevels.indexOf(level) !== -1;
2032 }
2033 /**
2034 * Converts a string-based level into a {@link Severity}.
2035 *
2036 * @param level string representation of Severity
2037 * @returns Severity
2038 */
2039 function severityFromString(level) {
2040 if (level === 'warn')
2041 return exports.Severity.Warning;
2042 if (isSupportedSeverity(level)) {
2043 return level;
2044 }
2045 return exports.Severity.Log;
2046 }
2047
2048 /**
2049 * Converts an HTTP status code to sentry status {@link EventStatus}.
2050 *
2051 * @param code number HTTP status code
2052 * @returns EventStatus
2053 */
2054 function eventStatusFromHttpCode(code) {
2055 if (code >= 200 && code < 300) {
2056 return 'success';
2057 }
2058 if (code === 429) {
2059 return 'rate_limit';
2060 }
2061 if (code >= 400 && code < 500) {
2062 return 'invalid';
2063 }
2064 if (code >= 500) {
2065 return 'failed';
2066 }
2067 return 'unknown';
2068 }
2069
2070 /**
2071 * A TimestampSource implementation for environments that do not support the Performance Web API natively.
2072 *
2073 * Note that this TimestampSource does not use a monotonic clock. A call to `nowSeconds` may return a timestamp earlier
2074 * than a previously returned value. We do not try to emulate a monotonic behavior in order to facilitate debugging. It
2075 * is more obvious to explain "why does my span have negative duration" than "why my spans have zero duration".
2076 */
2077 const dateTimestampSource = {
2078 nowSeconds: () => Date.now() / 1000,
2079 };
2080 /**
2081 * Returns a wrapper around the native Performance API browser implementation, or undefined for browsers that do not
2082 * support the API.
2083 *
2084 * Wrapping the native API works around differences in behavior from different browsers.
2085 */
2086 function getBrowserPerformance() {
2087 const { performance } = getGlobalObject();
2088 if (!performance || !performance.now) {
2089 return undefined;
2090 }
2091 // Replace performance.timeOrigin with our own timeOrigin based on Date.now().
2092 //
2093 // This is a partial workaround for browsers reporting performance.timeOrigin such that performance.timeOrigin +
2094 // performance.now() gives a date arbitrarily in the past.
2095 //
2096 // Additionally, computing timeOrigin in this way fills the gap for browsers where performance.timeOrigin is
2097 // undefined.
2098 //
2099 // The assumption that performance.timeOrigin + performance.now() ~= Date.now() is flawed, but we depend on it to
2100 // interact with data coming out of performance entries.
2101 //
2102 // Note that despite recommendations against it in the spec, browsers implement the Performance API with a clock that
2103 // might stop when the computer is asleep (and perhaps under other circumstances). Such behavior causes
2104 // performance.timeOrigin + performance.now() to have an arbitrary skew over Date.now(). In laptop computers, we have
2105 // observed skews that can be as long as days, weeks or months.
2106 //
2107 // See https://github.com/getsentry/sentry-javascript/issues/2590.
2108 //
2109 // BUG: despite our best intentions, this workaround has its limitations. It mostly addresses timings of pageload
2110 // transactions, but ignores the skew built up over time that can aversely affect timestamps of navigation
2111 // transactions of long-lived web pages.
2112 const timeOrigin = Date.now() - performance.now();
2113 return {
2114 now: () => performance.now(),
2115 timeOrigin,
2116 };
2117 }
2118 /**
2119 * The Performance API implementation for the current platform, if available.
2120 */
2121 const platformPerformance = getBrowserPerformance();
2122 const timestampSource = platformPerformance === undefined
2123 ? dateTimestampSource
2124 : {
2125 nowSeconds: () => (platformPerformance.timeOrigin + platformPerformance.now()) / 1000,
2126 };
2127 /**
2128 * Returns a timestamp in seconds since the UNIX epoch using the Date API.
2129 */
2130 const dateTimestampInSeconds = dateTimestampSource.nowSeconds.bind(dateTimestampSource);
2131 /**
2132 * Returns a timestamp in seconds since the UNIX epoch using either the Performance or Date APIs, depending on the
2133 * availability of the Performance API.
2134 *
2135 * See `usingPerformanceAPI` to test whether the Performance API is used.
2136 *
2137 * BUG: Note that because of how browsers implement the Performance API, the clock might stop when the computer is
2138 * asleep. This creates a skew between `dateTimestampInSeconds` and `timestampInSeconds`. The
2139 * skew can grow to arbitrary amounts like days, weeks or months.
2140 * See https://github.com/getsentry/sentry-javascript/issues/2590.
2141 */
2142 const timestampInSeconds = timestampSource.nowSeconds.bind(timestampSource);
2143 /**
2144 * The number of milliseconds since the UNIX epoch. This value is only usable in a browser, and only when the
2145 * performance API is available.
2146 */
2147 (() => {
2148 // Unfortunately browsers may report an inaccurate time origin data, through either performance.timeOrigin or
2149 // performance.timing.navigationStart, which results in poor results in performance data. We only treat time origin
2150 // data as reliable if they are within a reasonable threshold of the current time.
2151 const { performance } = getGlobalObject();
2152 if (!performance || !performance.now) {
2153 return undefined;
2154 }
2155 const threshold = 3600 * 1000;
2156 const performanceNow = performance.now();
2157 const dateNow = Date.now();
2158 // if timeOrigin isn't available set delta to threshold so it isn't used
2159 const timeOriginDelta = performance.timeOrigin
2160 ? Math.abs(performance.timeOrigin + performanceNow - dateNow)
2161 : threshold;
2162 const timeOriginIsReliable = timeOriginDelta < threshold;
2163 // While performance.timing.navigationStart is deprecated in favor of performance.timeOrigin, performance.timeOrigin
2164 // is not as widely supported. Namely, performance.timeOrigin is undefined in Safari as of writing.
2165 // Also as of writing, performance.timing is not available in Web Workers in mainstream browsers, so it is not always
2166 // a valid fallback. In the absence of an initial time provided by the browser, fallback to the current time from the
2167 // Date API.
2168 // eslint-disable-next-line deprecation/deprecation
2169 const navigationStart = performance.timing && performance.timing.navigationStart;
2170 const hasNavigationStart = typeof navigationStart === 'number';
2171 // if navigationStart isn't available set delta to threshold so it isn't used
2172 const navigationStartDelta = hasNavigationStart ? Math.abs(navigationStart + performanceNow - dateNow) : threshold;
2173 const navigationStartIsReliable = navigationStartDelta < threshold;
2174 if (timeOriginIsReliable || navigationStartIsReliable) {
2175 // Use the more reliable time origin
2176 if (timeOriginDelta <= navigationStartDelta) {
2177 return performance.timeOrigin;
2178 }
2179 else {
2180 return navigationStart;
2181 }
2182 }
2183 return dateNow;
2184 })();
2185
2186 /**
2187 * Creates an envelope.
2188 * Make sure to always explicitly provide the generic to this function
2189 * so that the envelope types resolve correctly.
2190 */
2191 function createEnvelope(headers, items = []) {
2192 return [headers, items];
2193 }
2194 /**
2195 * Get the type of the envelope. Grabs the type from the first envelope item.
2196 */
2197 function getEnvelopeType(envelope) {
2198 const [, [[firstItemHeader]]] = envelope;
2199 return firstItemHeader.type;
2200 }
2201 /**
2202 * Serializes an envelope into a string.
2203 */
2204 function serializeEnvelope(envelope) {
2205 const [headers, items] = envelope;
2206 const serializedHeaders = JSON.stringify(headers);
2207 // Have to cast items to any here since Envelope is a union type
2208 // Fixed in Typescript 4.2
2209 // TODO: Remove any[] cast when we upgrade to TS 4.2
2210 // https://github.com/microsoft/TypeScript/issues/36390
2211 // eslint-disable-next-line @typescript-eslint/no-explicit-any
2212 return items.reduce((acc, item) => {
2213 const [itemHeaders, payload] = item;
2214 // We do not serialize payloads that are primitives
2215 const serializedPayload = isPrimitive(payload) ? String(payload) : JSON.stringify(payload);
2216 return `${acc}\n${JSON.stringify(itemHeaders)}\n${serializedPayload}`;
2217 }, serializedHeaders);
2218 }
2219
2220 /**
2221 * Creates client report envelope
2222 * @param discarded_events An array of discard events
2223 * @param dsn A DSN that can be set on the header. Optional.
2224 */
2225 function createClientReportEnvelope(discarded_events, dsn, timestamp) {
2226 const clientReportItem = [
2227 { type: 'client_report' },
2228 {
2229 timestamp: timestamp || dateTimestampInSeconds(),
2230 discarded_events,
2231 },
2232 ];
2233 return createEnvelope(dsn ? { dsn } : {}, [clientReportItem]);
2234 }
2235
2236 const DEFAULT_RETRY_AFTER = 60 * 1000; // 60 seconds
2237 /**
2238 * Extracts Retry-After value from the request header or returns default value
2239 * @param header string representation of 'Retry-After' header
2240 * @param now current unix timestamp
2241 *
2242 */
2243 function parseRetryAfterHeader(header, now = Date.now()) {
2244 const headerDelay = parseInt(`${header}`, 10);
2245 if (!isNaN(headerDelay)) {
2246 return headerDelay * 1000;
2247 }
2248 const headerDate = Date.parse(`${header}`);
2249 if (!isNaN(headerDate)) {
2250 return headerDate - now;
2251 }
2252 return DEFAULT_RETRY_AFTER;
2253 }
2254 /**
2255 * Gets the time that given category is disabled until for rate limiting
2256 */
2257 function disabledUntil(limits, category) {
2258 return limits[category] || limits.all || 0;
2259 }
2260 /**
2261 * Checks if a category is rate limited
2262 */
2263 function isRateLimited(limits, category, now = Date.now()) {
2264 return disabledUntil(limits, category) > now;
2265 }
2266 /**
2267 * Update ratelimits from incoming headers.
2268 * Returns true if headers contains a non-empty rate limiting header.
2269 */
2270 function updateRateLimits(limits, headers, now = Date.now()) {
2271 const updatedRateLimits = Object.assign({}, limits);
2272 // "The name is case-insensitive."
2273 // https://developer.mozilla.org/en-US/docs/Web/API/Headers/get
2274 const rateLimitHeader = headers['x-sentry-rate-limits'];
2275 const retryAfterHeader = headers['retry-after'];
2276 if (rateLimitHeader) {
2277 /**
2278 * rate limit headers are of the form
2279 * <header>,<header>,..
2280 * where each <header> is of the form
2281 * <retry_after>: <categories>: <scope>: <reason_code>
2282 * where
2283 * <retry_after> is a delay in seconds
2284 * <categories> is the event type(s) (error, transaction, etc) being rate limited and is of the form
2285 * <category>;<category>;...
2286 * <scope> is what's being limited (org, project, or key) - ignored by SDK
2287 * <reason_code> is an arbitrary string like "org_quota" - ignored by SDK
2288 */
2289 for (const limit of rateLimitHeader.trim().split(',')) {
2290 const parameters = limit.split(':', 2);
2291 const headerDelay = parseInt(parameters[0], 10);
2292 const delay = (!isNaN(headerDelay) ? headerDelay : 60) * 1000; // 60sec default
2293 if (!parameters[1]) {
2294 updatedRateLimits.all = now + delay;
2295 }
2296 else {
2297 for (const category of parameters[1].split(';')) {
2298 updatedRateLimits[category] = now + delay;
2299 }
2300 }
2301 }
2302 }
2303 else if (retryAfterHeader) {
2304 updatedRateLimits.all = now + parseRetryAfterHeader(retryAfterHeader, now);
2305 }
2306 return updatedRateLimits;
2307 }
2308
2309 /**
2310 * Absolute maximum number of breadcrumbs added to an event.
2311 * The `maxBreadcrumbs` option cannot be higher than this value.
2312 */
2313 const MAX_BREADCRUMBS = 100;
2314 /**
2315 * Holds additional event information. {@link Scope.applyToEvent} will be
2316 * called by the client before an event will be sent.
2317 */
2318 class Scope {
2319 constructor() {
2320 /** Flag if notifying is happening. */
2321 this._notifyingListeners = false;
2322 /** Callback for client to receive scope changes. */
2323 this._scopeListeners = [];
2324 /** Callback list that will be called after {@link applyToEvent}. */
2325 this._eventProcessors = [];
2326 /** Array of breadcrumbs. */
2327 this._breadcrumbs = [];
2328 /** User */
2329 this._user = {};
2330 /** Tags */
2331 this._tags = {};
2332 /** Extra */
2333 this._extra = {};
2334 /** Contexts */
2335 this._contexts = {};
2336 /**
2337 * A place to stash data which is needed at some point in the SDK's event processing pipeline but which shouldn't get
2338 * sent to Sentry
2339 */
2340 this._sdkProcessingMetadata = {};
2341 }
2342 /**
2343 * Inherit values from the parent scope.
2344 * @param scope to clone.
2345 */
2346 static clone(scope) {
2347 const newScope = new Scope();
2348 if (scope) {
2349 newScope._breadcrumbs = [...scope._breadcrumbs];
2350 newScope._tags = Object.assign({}, scope._tags);
2351 newScope._extra = Object.assign({}, scope._extra);
2352 newScope._contexts = Object.assign({}, scope._contexts);
2353 newScope._user = scope._user;
2354 newScope._level = scope._level;
2355 newScope._span = scope._span;
2356 newScope._session = scope._session;
2357 newScope._transactionName = scope._transactionName;
2358 newScope._fingerprint = scope._fingerprint;
2359 newScope._eventProcessors = [...scope._eventProcessors];
2360 newScope._requestSession = scope._requestSession;
2361 }
2362 return newScope;
2363 }
2364 /**
2365 * Add internal on change listener. Used for sub SDKs that need to store the scope.
2366 * @hidden
2367 */
2368 addScopeListener(callback) {
2369 this._scopeListeners.push(callback);
2370 }
2371 /**
2372 * @inheritDoc
2373 */
2374 addEventProcessor(callback) {
2375 this._eventProcessors.push(callback);
2376 return this;
2377 }
2378 /**
2379 * @inheritDoc
2380 */
2381 setUser(user) {
2382 this._user = user || {};
2383 if (this._session) {
2384 this._session.update({ user });
2385 }
2386 this._notifyScopeListeners();
2387 return this;
2388 }
2389 /**
2390 * @inheritDoc
2391 */
2392 getUser() {
2393 return this._user;
2394 }
2395 /**
2396 * @inheritDoc
2397 */
2398 getRequestSession() {
2399 return this._requestSession;
2400 }
2401 /**
2402 * @inheritDoc
2403 */
2404 setRequestSession(requestSession) {
2405 this._requestSession = requestSession;
2406 return this;
2407 }
2408 /**
2409 * @inheritDoc
2410 */
2411 setTags(tags) {
2412 this._tags = Object.assign(Object.assign({}, this._tags), tags);
2413 this._notifyScopeListeners();
2414 return this;
2415 }
2416 /**
2417 * @inheritDoc
2418 */
2419 setTag(key, value) {
2420 this._tags = Object.assign(Object.assign({}, this._tags), { [key]: value });
2421 this._notifyScopeListeners();
2422 return this;
2423 }
2424 /**
2425 * @inheritDoc
2426 */
2427 setExtras(extras) {
2428 this._extra = Object.assign(Object.assign({}, this._extra), extras);
2429 this._notifyScopeListeners();
2430 return this;
2431 }
2432 /**
2433 * @inheritDoc
2434 */
2435 setExtra(key, extra) {
2436 this._extra = Object.assign(Object.assign({}, this._extra), { [key]: extra });
2437 this._notifyScopeListeners();
2438 return this;
2439 }
2440 /**
2441 * @inheritDoc
2442 */
2443 setFingerprint(fingerprint) {
2444 this._fingerprint = fingerprint;
2445 this._notifyScopeListeners();
2446 return this;
2447 }
2448 /**
2449 * @inheritDoc
2450 */
2451 setLevel(level) {
2452 this._level = level;
2453 this._notifyScopeListeners();
2454 return this;
2455 }
2456 /**
2457 * @inheritDoc
2458 */
2459 setTransactionName(name) {
2460 this._transactionName = name;
2461 this._notifyScopeListeners();
2462 return this;
2463 }
2464 /**
2465 * Can be removed in major version.
2466 * @deprecated in favor of {@link this.setTransactionName}
2467 */
2468 setTransaction(name) {
2469 return this.setTransactionName(name);
2470 }
2471 /**
2472 * @inheritDoc
2473 */
2474 setContext(key, context) {
2475 if (context === null) {
2476 // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
2477 delete this._contexts[key];
2478 }
2479 else {
2480 this._contexts = Object.assign(Object.assign({}, this._contexts), { [key]: context });
2481 }
2482 this._notifyScopeListeners();
2483 return this;
2484 }
2485 /**
2486 * @inheritDoc
2487 */
2488 setSpan(span) {
2489 this._span = span;
2490 this._notifyScopeListeners();
2491 return this;
2492 }
2493 /**
2494 * @inheritDoc
2495 */
2496 getSpan() {
2497 return this._span;
2498 }
2499 /**
2500 * @inheritDoc
2501 */
2502 getTransaction() {
2503 // Often, this span (if it exists at all) will be a transaction, but it's not guaranteed to be. Regardless, it will
2504 // have a pointer to the currently-active transaction.
2505 const span = this.getSpan();
2506 return span && span.transaction;
2507 }
2508 /**
2509 * @inheritDoc
2510 */
2511 setSession(session) {
2512 if (!session) {
2513 delete this._session;
2514 }
2515 else {
2516 this._session = session;
2517 }
2518 this._notifyScopeListeners();
2519 return this;
2520 }
2521 /**
2522 * @inheritDoc
2523 */
2524 getSession() {
2525 return this._session;
2526 }
2527 /**
2528 * @inheritDoc
2529 */
2530 update(captureContext) {
2531 if (!captureContext) {
2532 return this;
2533 }
2534 if (typeof captureContext === 'function') {
2535 const updatedScope = captureContext(this);
2536 return updatedScope instanceof Scope ? updatedScope : this;
2537 }
2538 if (captureContext instanceof Scope) {
2539 this._tags = Object.assign(Object.assign({}, this._tags), captureContext._tags);
2540 this._extra = Object.assign(Object.assign({}, this._extra), captureContext._extra);
2541 this._contexts = Object.assign(Object.assign({}, this._contexts), captureContext._contexts);
2542 if (captureContext._user && Object.keys(captureContext._user).length) {
2543 this._user = captureContext._user;
2544 }
2545 if (captureContext._level) {
2546 this._level = captureContext._level;
2547 }
2548 if (captureContext._fingerprint) {
2549 this._fingerprint = captureContext._fingerprint;
2550 }
2551 if (captureContext._requestSession) {
2552 this._requestSession = captureContext._requestSession;
2553 }
2554 }
2555 else if (isPlainObject(captureContext)) {
2556 // eslint-disable-next-line no-param-reassign
2557 captureContext = captureContext;
2558 this._tags = Object.assign(Object.assign({}, this._tags), captureContext.tags);
2559 this._extra = Object.assign(Object.assign({}, this._extra), captureContext.extra);
2560 this._contexts = Object.assign(Object.assign({}, this._contexts), captureContext.contexts);
2561 if (captureContext.user) {
2562 this._user = captureContext.user;
2563 }
2564 if (captureContext.level) {
2565 this._level = captureContext.level;
2566 }
2567 if (captureContext.fingerprint) {
2568 this._fingerprint = captureContext.fingerprint;
2569 }
2570 if (captureContext.requestSession) {
2571 this._requestSession = captureContext.requestSession;
2572 }
2573 }
2574 return this;
2575 }
2576 /**
2577 * @inheritDoc
2578 */
2579 clear() {
2580 this._breadcrumbs = [];
2581 this._tags = {};
2582 this._extra = {};
2583 this._user = {};
2584 this._contexts = {};
2585 this._level = undefined;
2586 this._transactionName = undefined;
2587 this._fingerprint = undefined;
2588 this._requestSession = undefined;
2589 this._span = undefined;
2590 this._session = undefined;
2591 this._notifyScopeListeners();
2592 return this;
2593 }
2594 /**
2595 * @inheritDoc
2596 */
2597 addBreadcrumb(breadcrumb, maxBreadcrumbs) {
2598 const maxCrumbs = typeof maxBreadcrumbs === 'number' ? Math.min(maxBreadcrumbs, MAX_BREADCRUMBS) : MAX_BREADCRUMBS;
2599 // No data has been changed, so don't notify scope listeners
2600 if (maxCrumbs <= 0) {
2601 return this;
2602 }
2603 const mergedBreadcrumb = Object.assign({ timestamp: dateTimestampInSeconds() }, breadcrumb);
2604 this._breadcrumbs = [...this._breadcrumbs, mergedBreadcrumb].slice(-maxCrumbs);
2605 this._notifyScopeListeners();
2606 return this;
2607 }
2608 /**
2609 * @inheritDoc
2610 */
2611 clearBreadcrumbs() {
2612 this._breadcrumbs = [];
2613 this._notifyScopeListeners();
2614 return this;
2615 }
2616 /**
2617 * Applies the current context and fingerprint to the event.
2618 * Note that breadcrumbs will be added by the client.
2619 * Also if the event has already breadcrumbs on it, we do not merge them.
2620 * @param event Event
2621 * @param hint May contain additional information about the original exception.
2622 * @hidden
2623 */
2624 applyToEvent(event, hint) {
2625 if (this._extra && Object.keys(this._extra).length) {
2626 event.extra = Object.assign(Object.assign({}, this._extra), event.extra);
2627 }
2628 if (this._tags && Object.keys(this._tags).length) {
2629 event.tags = Object.assign(Object.assign({}, this._tags), event.tags);
2630 }
2631 if (this._user && Object.keys(this._user).length) {
2632 event.user = Object.assign(Object.assign({}, this._user), event.user);
2633 }
2634 if (this._contexts && Object.keys(this._contexts).length) {
2635 event.contexts = Object.assign(Object.assign({}, this._contexts), event.contexts);
2636 }
2637 if (this._level) {
2638 event.level = this._level;
2639 }
2640 if (this._transactionName) {
2641 event.transaction = this._transactionName;
2642 }
2643 // We want to set the trace context for normal events only if there isn't already
2644 // a trace context on the event. There is a product feature in place where we link
2645 // errors with transaction and it relies on that.
2646 if (this._span) {
2647 event.contexts = Object.assign({ trace: this._span.getTraceContext() }, event.contexts);
2648 const transactionName = this._span.transaction && this._span.transaction.name;
2649 if (transactionName) {
2650 event.tags = Object.assign({ transaction: transactionName }, event.tags);
2651 }
2652 }
2653 this._applyFingerprint(event);
2654 event.breadcrumbs = [...(event.breadcrumbs || []), ...this._breadcrumbs];
2655 event.breadcrumbs = event.breadcrumbs.length > 0 ? event.breadcrumbs : undefined;
2656 event.sdkProcessingMetadata = this._sdkProcessingMetadata;
2657 return this._notifyEventProcessors([...getGlobalEventProcessors(), ...this._eventProcessors], event, hint);
2658 }
2659 /**
2660 * Add data which will be accessible during event processing but won't get sent to Sentry
2661 */
2662 setSDKProcessingMetadata(newData) {
2663 this._sdkProcessingMetadata = Object.assign(Object.assign({}, this._sdkProcessingMetadata), newData);
2664 return this;
2665 }
2666 /**
2667 * This will be called after {@link applyToEvent} is finished.
2668 */
2669 _notifyEventProcessors(processors, event, hint, index = 0) {
2670 return new SyncPromise((resolve, reject) => {
2671 const processor = processors[index];
2672 if (event === null || typeof processor !== 'function') {
2673 resolve(event);
2674 }
2675 else {
2676 const result = processor(Object.assign({}, event), hint);
2677 if (isThenable(result)) {
2678 void result
2679 .then(final => this._notifyEventProcessors(processors, final, hint, index + 1).then(resolve))
2680 .then(null, reject);
2681 }
2682 else {
2683 void this._notifyEventProcessors(processors, result, hint, index + 1)
2684 .then(resolve)
2685 .then(null, reject);
2686 }
2687 }
2688 });
2689 }
2690 /**
2691 * This will be called on every set call.
2692 */
2693 _notifyScopeListeners() {
2694 // We need this check for this._notifyingListeners to be able to work on scope during updates
2695 // If this check is not here we'll produce endless recursion when something is done with the scope
2696 // during the callback.
2697 if (!this._notifyingListeners) {
2698 this._notifyingListeners = true;
2699 this._scopeListeners.forEach(callback => {
2700 callback(this);
2701 });
2702 this._notifyingListeners = false;
2703 }
2704 }
2705 /**
2706 * Applies fingerprint from the scope to the event if there's one,
2707 * uses message if there's one instead or get rid of empty fingerprint
2708 */
2709 _applyFingerprint(event) {
2710 // Make sure it's an array first and we actually have something in place
2711 event.fingerprint = event.fingerprint
2712 ? Array.isArray(event.fingerprint)
2713 ? event.fingerprint
2714 : [event.fingerprint]
2715 : [];
2716 // If we have something on the scope, then merge it with event
2717 if (this._fingerprint) {
2718 event.fingerprint = event.fingerprint.concat(this._fingerprint);
2719 }
2720 // If we have no data at all, remove empty array default
2721 if (event.fingerprint && !event.fingerprint.length) {
2722 delete event.fingerprint;
2723 }
2724 }
2725 }
2726 /**
2727 * Returns the global event processors.
2728 */
2729 function getGlobalEventProcessors() {
2730 return getGlobalSingleton('globalEventProcessors', () => []);
2731 }
2732 /**
2733 * Add a EventProcessor to be kept globally.
2734 * @param callback EventProcessor to add
2735 */
2736 function addGlobalEventProcessor(callback) {
2737 getGlobalEventProcessors().push(callback);
2738 }
2739
2740 /**
2741 * @inheritdoc
2742 */
2743 class Session {
2744 constructor(context) {
2745 this.errors = 0;
2746 this.sid = uuid4();
2747 this.duration = 0;
2748 this.status = 'ok';
2749 this.init = true;
2750 this.ignoreDuration = false;
2751 // Both timestamp and started are in seconds since the UNIX epoch.
2752 const startingTime = timestampInSeconds();
2753 this.timestamp = startingTime;
2754 this.started = startingTime;
2755 if (context) {
2756 this.update(context);
2757 }
2758 }
2759 /** JSDoc */
2760 // eslint-disable-next-line complexity
2761 update(context = {}) {
2762 if (context.user) {
2763 if (!this.ipAddress && context.user.ip_address) {
2764 this.ipAddress = context.user.ip_address;
2765 }
2766 if (!this.did && !context.did) {
2767 this.did = context.user.id || context.user.email || context.user.username;
2768 }
2769 }
2770 this.timestamp = context.timestamp || timestampInSeconds();
2771 if (context.ignoreDuration) {
2772 this.ignoreDuration = context.ignoreDuration;
2773 }
2774 if (context.sid) {
2775 // Good enough uuid validation. — Kamil
2776 this.sid = context.sid.length === 32 ? context.sid : uuid4();
2777 }
2778 if (context.init !== undefined) {
2779 this.init = context.init;
2780 }
2781 if (!this.did && context.did) {
2782 this.did = `${context.did}`;
2783 }
2784 if (typeof context.started === 'number') {
2785 this.started = context.started;
2786 }
2787 if (this.ignoreDuration) {
2788 this.duration = undefined;
2789 }
2790 else if (typeof context.duration === 'number') {
2791 this.duration = context.duration;
2792 }
2793 else {
2794 const duration = this.timestamp - this.started;
2795 this.duration = duration >= 0 ? duration : 0;
2796 }
2797 if (context.release) {
2798 this.release = context.release;
2799 }
2800 if (context.environment) {
2801 this.environment = context.environment;
2802 }
2803 if (!this.ipAddress && context.ipAddress) {
2804 this.ipAddress = context.ipAddress;
2805 }
2806 if (!this.userAgent && context.userAgent) {
2807 this.userAgent = context.userAgent;
2808 }
2809 if (typeof context.errors === 'number') {
2810 this.errors = context.errors;
2811 }
2812 if (context.status) {
2813 this.status = context.status;
2814 }
2815 }
2816 /** JSDoc */
2817 close(status) {
2818 if (status) {
2819 this.update({ status });
2820 }
2821 else if (this.status === 'ok') {
2822 this.update({ status: 'exited' });
2823 }
2824 else {
2825 this.update();
2826 }
2827 }
2828 /** JSDoc */
2829 toJSON() {
2830 return dropUndefinedKeys({
2831 sid: `${this.sid}`,
2832 init: this.init,
2833 // Make sure that sec is converted to ms for date constructor
2834 started: new Date(this.started * 1000).toISOString(),
2835 timestamp: new Date(this.timestamp * 1000).toISOString(),
2836 status: this.status,
2837 errors: this.errors,
2838 did: typeof this.did === 'number' || typeof this.did === 'string' ? `${this.did}` : undefined,
2839 duration: this.duration,
2840 attrs: {
2841 release: this.release,
2842 environment: this.environment,
2843 ip_address: this.ipAddress,
2844 user_agent: this.userAgent,
2845 },
2846 });
2847 }
2848 }
2849
2850 /*
2851 * This file defines flags and constants that can be modified during compile time in order to facilitate tree shaking
2852 * for users.
2853 *
2854 * Debug flags need to be declared in each package individually and must not be imported across package boundaries,
2855 * because some build tools have trouble tree-shaking imported guards.
2856 *
2857 * As a convention, we define debug flags in a `flags.ts` file in the root of a package's `src` folder.
2858 *
2859 * Debug flag files will contain "magic strings" like `true` that may get replaced with actual values during
2860 * our, or the user's build process. Take care when introducing new flags - they must not throw if they are not
2861 * replaced.
2862 */
2863 /** Flag that is true for debug builds, false otherwise. */
2864 const IS_DEBUG_BUILD$2 = true;
2865
2866 /**
2867 * API compatibility version of this hub.
2868 *
2869 * WARNING: This number should only be increased when the global interface
2870 * changes and new methods are introduced.
2871 *
2872 * @hidden
2873 */
2874 const API_VERSION = 4;
2875 /**
2876 * Default maximum number of breadcrumbs added to an event. Can be overwritten
2877 * with {@link Options.maxBreadcrumbs}.
2878 */
2879 const DEFAULT_BREADCRUMBS = 100;
2880 /**
2881 * @inheritDoc
2882 */
2883 class Hub {
2884 /**
2885 * Creates a new instance of the hub, will push one {@link Layer} into the
2886 * internal stack on creation.
2887 *
2888 * @param client bound to the hub.
2889 * @param scope bound to the hub.
2890 * @param version number, higher number means higher priority.
2891 */
2892 constructor(client, scope = new Scope(), _version = API_VERSION) {
2893 this._version = _version;
2894 /** Is a {@link Layer}[] containing the client and scope */
2895 this._stack = [{}];
2896 this.getStackTop().scope = scope;
2897 if (client) {
2898 this.bindClient(client);
2899 }
2900 }
2901 /**
2902 * @inheritDoc
2903 */
2904 isOlderThan(version) {
2905 return this._version < version;
2906 }
2907 /**
2908 * @inheritDoc
2909 */
2910 bindClient(client) {
2911 const top = this.getStackTop();
2912 top.client = client;
2913 if (client && client.setupIntegrations) {
2914 client.setupIntegrations();
2915 }
2916 }
2917 /**
2918 * @inheritDoc
2919 */
2920 pushScope() {
2921 // We want to clone the content of prev scope
2922 const scope = Scope.clone(this.getScope());
2923 this.getStack().push({
2924 client: this.getClient(),
2925 scope,
2926 });
2927 return scope;
2928 }
2929 /**
2930 * @inheritDoc
2931 */
2932 popScope() {
2933 if (this.getStack().length <= 1)
2934 return false;
2935 return !!this.getStack().pop();
2936 }
2937 /**
2938 * @inheritDoc
2939 */
2940 withScope(callback) {
2941 const scope = this.pushScope();
2942 try {
2943 callback(scope);
2944 }
2945 finally {
2946 this.popScope();
2947 }
2948 }
2949 /**
2950 * @inheritDoc
2951 */
2952 getClient() {
2953 return this.getStackTop().client;
2954 }
2955 /** Returns the scope of the top stack. */
2956 getScope() {
2957 return this.getStackTop().scope;
2958 }
2959 /** Returns the scope stack for domains or the process. */
2960 getStack() {
2961 return this._stack;
2962 }
2963 /** Returns the topmost scope layer in the order domain > local > process. */
2964 getStackTop() {
2965 return this._stack[this._stack.length - 1];
2966 }
2967 /**
2968 * @inheritDoc
2969 */
2970 // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
2971 captureException(exception, hint) {
2972 const eventId = (this._lastEventId = hint && hint.event_id ? hint.event_id : uuid4());
2973 let finalHint = hint;
2974 // If there's no explicit hint provided, mimic the same thing that would happen
2975 // in the minimal itself to create a consistent behavior.
2976 // We don't do this in the client, as it's the lowest level API, and doing this,
2977 // would prevent user from having full control over direct calls.
2978 if (!hint) {
2979 let syntheticException;
2980 try {
2981 throw new Error('Sentry syntheticException');
2982 }
2983 catch (exception) {
2984 syntheticException = exception;
2985 }
2986 finalHint = {
2987 originalException: exception,
2988 syntheticException,
2989 };
2990 }
2991 this._invokeClient('captureException', exception, Object.assign(Object.assign({}, finalHint), { event_id: eventId }));
2992 return eventId;
2993 }
2994 /**
2995 * @inheritDoc
2996 */
2997 captureMessage(message, level, hint) {
2998 const eventId = (this._lastEventId = hint && hint.event_id ? hint.event_id : uuid4());
2999 let finalHint = hint;
3000 // If there's no explicit hint provided, mimic the same thing that would happen
3001 // in the minimal itself to create a consistent behavior.
3002 // We don't do this in the client, as it's the lowest level API, and doing this,
3003 // would prevent user from having full control over direct calls.
3004 if (!hint) {
3005 let syntheticException;
3006 try {
3007 throw new Error(message);
3008 }
3009 catch (exception) {
3010 syntheticException = exception;
3011 }
3012 finalHint = {
3013 originalException: message,
3014 syntheticException,
3015 };
3016 }
3017 this._invokeClient('captureMessage', message, level, Object.assign(Object.assign({}, finalHint), { event_id: eventId }));
3018 return eventId;
3019 }
3020 /**
3021 * @inheritDoc
3022 */
3023 captureEvent(event, hint) {
3024 const eventId = hint && hint.event_id ? hint.event_id : uuid4();
3025 if (event.type !== 'transaction') {
3026 this._lastEventId = eventId;
3027 }
3028 this._invokeClient('captureEvent', event, Object.assign(Object.assign({}, hint), { event_id: eventId }));
3029 return eventId;
3030 }
3031 /**
3032 * @inheritDoc
3033 */
3034 lastEventId() {
3035 return this._lastEventId;
3036 }
3037 /**
3038 * @inheritDoc
3039 */
3040 addBreadcrumb(breadcrumb, hint) {
3041 const { scope, client } = this.getStackTop();
3042 if (!scope || !client)
3043 return;
3044 // eslint-disable-next-line @typescript-eslint/unbound-method
3045 const { beforeBreadcrumb = null, maxBreadcrumbs = DEFAULT_BREADCRUMBS } = (client.getOptions && client.getOptions()) || {};
3046 if (maxBreadcrumbs <= 0)
3047 return;
3048 const timestamp = dateTimestampInSeconds();
3049 const mergedBreadcrumb = Object.assign({ timestamp }, breadcrumb);
3050 const finalBreadcrumb = beforeBreadcrumb
3051 ? consoleSandbox(() => beforeBreadcrumb(mergedBreadcrumb, hint))
3052 : mergedBreadcrumb;
3053 if (finalBreadcrumb === null)
3054 return;
3055 scope.addBreadcrumb(finalBreadcrumb, maxBreadcrumbs);
3056 }
3057 /**
3058 * @inheritDoc
3059 */
3060 setUser(user) {
3061 const scope = this.getScope();
3062 if (scope)
3063 scope.setUser(user);
3064 }
3065 /**
3066 * @inheritDoc
3067 */
3068 setTags(tags) {
3069 const scope = this.getScope();
3070 if (scope)
3071 scope.setTags(tags);
3072 }
3073 /**
3074 * @inheritDoc
3075 */
3076 setExtras(extras) {
3077 const scope = this.getScope();
3078 if (scope)
3079 scope.setExtras(extras);
3080 }
3081 /**
3082 * @inheritDoc
3083 */
3084 setTag(key, value) {
3085 const scope = this.getScope();
3086 if (scope)
3087 scope.setTag(key, value);
3088 }
3089 /**
3090 * @inheritDoc
3091 */
3092 setExtra(key, extra) {
3093 const scope = this.getScope();
3094 if (scope)
3095 scope.setExtra(key, extra);
3096 }
3097 /**
3098 * @inheritDoc
3099 */
3100 // eslint-disable-next-line @typescript-eslint/no-explicit-any
3101 setContext(name, context) {
3102 const scope = this.getScope();
3103 if (scope)
3104 scope.setContext(name, context);
3105 }
3106 /**
3107 * @inheritDoc
3108 */
3109 configureScope(callback) {
3110 const { scope, client } = this.getStackTop();
3111 if (scope && client) {
3112 callback(scope);
3113 }
3114 }
3115 /**
3116 * @inheritDoc
3117 */
3118 run(callback) {
3119 const oldHub = makeMain(this);
3120 try {
3121 callback(this);
3122 }
3123 finally {
3124 makeMain(oldHub);
3125 }
3126 }
3127 /**
3128 * @inheritDoc
3129 */
3130 getIntegration(integration) {
3131 const client = this.getClient();
3132 if (!client)
3133 return null;
3134 try {
3135 return client.getIntegration(integration);
3136 }
3137 catch (_oO) {
3138 IS_DEBUG_BUILD$2 && logger.warn(`Cannot retrieve integration ${integration.id} from the current Hub`);
3139 return null;
3140 }
3141 }
3142 /**
3143 * @inheritDoc
3144 */
3145 startSpan(context) {
3146 return this._callExtensionMethod('startSpan', context);
3147 }
3148 /**
3149 * @inheritDoc
3150 */
3151 startTransaction(context, customSamplingContext) {
3152 return this._callExtensionMethod('startTransaction', context, customSamplingContext);
3153 }
3154 /**
3155 * @inheritDoc
3156 */
3157 traceHeaders() {
3158 return this._callExtensionMethod('traceHeaders');
3159 }
3160 /**
3161 * @inheritDoc
3162 */
3163 captureSession(endSession = false) {
3164 // both send the update and pull the session from the scope
3165 if (endSession) {
3166 return this.endSession();
3167 }
3168 // only send the update
3169 this._sendSessionUpdate();
3170 }
3171 /**
3172 * @inheritDoc
3173 */
3174 endSession() {
3175 const layer = this.getStackTop();
3176 const scope = layer && layer.scope;
3177 const session = scope && scope.getSession();
3178 if (session) {
3179 session.close();
3180 }
3181 this._sendSessionUpdate();
3182 // the session is over; take it off of the scope
3183 if (scope) {
3184 scope.setSession();
3185 }
3186 }
3187 /**
3188 * @inheritDoc
3189 */
3190 startSession(context) {
3191 const { scope, client } = this.getStackTop();
3192 const { release, environment } = (client && client.getOptions()) || {};
3193 // Will fetch userAgent if called from browser sdk
3194 const global = getGlobalObject();
3195 const { userAgent } = global.navigator || {};
3196 const session = new Session(Object.assign(Object.assign(Object.assign({ release,
3197 environment }, (scope && { user: scope.getUser() })), (userAgent && { userAgent })), context));
3198 if (scope) {
3199 // End existing session if there's one
3200 const currentSession = scope.getSession && scope.getSession();
3201 if (currentSession && currentSession.status === 'ok') {
3202 currentSession.update({ status: 'exited' });
3203 }
3204 this.endSession();
3205 // Afterwards we set the new session on the scope
3206 scope.setSession(session);
3207 }
3208 return session;
3209 }
3210 /**
3211 * Sends the current Session on the scope
3212 */
3213 _sendSessionUpdate() {
3214 const { scope, client } = this.getStackTop();
3215 if (!scope)
3216 return;
3217 const session = scope.getSession && scope.getSession();
3218 if (session) {
3219 if (client && client.captureSession) {
3220 client.captureSession(session);
3221 }
3222 }
3223 }
3224 /**
3225 * Internal helper function to call a method on the top client if it exists.
3226 *
3227 * @param method The method to call on the client.
3228 * @param args Arguments to pass to the client function.
3229 */
3230 // eslint-disable-next-line @typescript-eslint/no-explicit-any
3231 _invokeClient(method, ...args) {
3232 const { scope, client } = this.getStackTop();
3233 if (client && client[method]) {
3234 // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
3235 client[method](...args, scope);
3236 }
3237 }
3238 /**
3239 * Calls global extension method and binding current instance to the function call
3240 */
3241 // @ts-ignore Function lacks ending return statement and return type does not include 'undefined'. ts(2366)
3242 // eslint-disable-next-line @typescript-eslint/no-explicit-any
3243 _callExtensionMethod(method, ...args) {
3244 const carrier = getMainCarrier();
3245 const sentry = carrier.__SENTRY__;
3246 if (sentry && sentry.extensions && typeof sentry.extensions[method] === 'function') {
3247 return sentry.extensions[method].apply(this, args);
3248 }
3249 IS_DEBUG_BUILD$2 && logger.warn(`Extension method ${method} couldn't be found, doing nothing.`);
3250 }
3251 }
3252 /**
3253 * Returns the global shim registry.
3254 *
3255 * FIXME: This function is problematic, because despite always returning a valid Carrier,
3256 * it has an optional `__SENTRY__` property, which then in turn requires us to always perform an unnecessary check
3257 * at the call-site. We always access the carrier through this function, so we can guarantee that `__SENTRY__` is there.
3258 **/
3259 function getMainCarrier() {
3260 const carrier = getGlobalObject();
3261 carrier.__SENTRY__ = carrier.__SENTRY__ || {
3262 extensions: {},
3263 hub: undefined,
3264 };
3265 return carrier;
3266 }
3267 /**
3268 * Replaces the current main hub with the passed one on the global object
3269 *
3270 * @returns The old replaced hub
3271 */
3272 function makeMain(hub) {
3273 const registry = getMainCarrier();
3274 const oldHub = getHubFromCarrier(registry);
3275 setHubOnCarrier(registry, hub);
3276 return oldHub;
3277 }
3278 /**
3279 * Returns the default hub instance.
3280 *
3281 * If a hub is already registered in the global carrier but this module
3282 * contains a more recent version, it replaces the registered version.
3283 * Otherwise, the currently registered hub will be returned.
3284 */
3285 function getCurrentHub() {
3286 // Get main carrier (global for every environment)
3287 const registry = getMainCarrier();
3288 // If there's no hub, or its an old API, assign a new one
3289 if (!hasHubOnCarrier(registry) || getHubFromCarrier(registry).isOlderThan(API_VERSION)) {
3290 setHubOnCarrier(registry, new Hub());
3291 }
3292 // Return hub that lives on a global object
3293 return getHubFromCarrier(registry);
3294 }
3295 /**
3296 * This will tell whether a carrier has a hub on it or not
3297 * @param carrier object
3298 */
3299 function hasHubOnCarrier(carrier) {
3300 return !!(carrier && carrier.__SENTRY__ && carrier.__SENTRY__.hub);
3301 }
3302 /**
3303 * This will create a new {@link Hub} and add to the passed object on
3304 * __SENTRY__.hub.
3305 * @param carrier object
3306 * @hidden
3307 */
3308 function getHubFromCarrier(carrier) {
3309 return getGlobalSingleton('hub', () => new Hub(), carrier);
3310 }
3311 /**
3312 * This will set passed {@link Hub} on the passed object's __SENTRY__.hub attribute
3313 * @param carrier object
3314 * @param hub Hub
3315 * @returns A boolean indicating success or failure
3316 */
3317 function setHubOnCarrier(carrier, hub) {
3318 if (!carrier)
3319 return false;
3320 const __SENTRY__ = (carrier.__SENTRY__ = carrier.__SENTRY__ || {});
3321 __SENTRY__.hub = hub;
3322 return true;
3323 }
3324
3325 /**
3326 * This calls a function on the current hub.
3327 * @param method function to call on hub.
3328 * @param args to pass to function.
3329 */
3330 // eslint-disable-next-line @typescript-eslint/no-explicit-any
3331 function callOnHub(method, ...args) {
3332 const hub = getCurrentHub();
3333 if (hub && hub[method]) {
3334 // eslint-disable-next-line @typescript-eslint/no-explicit-any
3335 return hub[method](...args);
3336 }
3337 throw new Error(`No hub defined or ${method} was not found on the hub, please open a bug report.`);
3338 }
3339 /**
3340 * Captures an exception event and sends it to Sentry.
3341 *
3342 * @param exception An exception-like object.
3343 * @returns The generated eventId.
3344 */
3345 // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
3346 function captureException(exception, captureContext) {
3347 const syntheticException = new Error('Sentry syntheticException');
3348 return callOnHub('captureException', exception, {
3349 captureContext,
3350 originalException: exception,
3351 syntheticException,
3352 });
3353 }
3354 /**
3355 * Captures a message event and sends it to Sentry.
3356 *
3357 * @param message The message to send to Sentry.
3358 * @param Severity Define the level of the message.
3359 * @returns The generated eventId.
3360 */
3361 function captureMessage(message, captureContext) {
3362 const syntheticException = new Error(message);
3363 // This is necessary to provide explicit scopes upgrade, without changing the original
3364 // arity of the `captureMessage(message, level)` method.
3365 const level = typeof captureContext === 'string' ? captureContext : undefined;
3366 const context = typeof captureContext !== 'string' ? { captureContext } : undefined;
3367 return callOnHub('captureMessage', message, level, Object.assign({ originalException: message, syntheticException }, context));
3368 }
3369 /**
3370 * Captures a manually created event and sends it to Sentry.
3371 *
3372 * @param event The event to send to Sentry.
3373 * @returns The generated eventId.
3374 */
3375 function captureEvent(event) {
3376 return callOnHub('captureEvent', event);
3377 }
3378 /**
3379 * Callback to set context information onto the scope.
3380 * @param callback Callback function that receives Scope.
3381 */
3382 function configureScope(callback) {
3383 callOnHub('configureScope', callback);
3384 }
3385 /**
3386 * Records a new breadcrumb which will be attached to future events.
3387 *
3388 * Breadcrumbs will be added to subsequent events to provide more context on
3389 * user's actions prior to an error or crash.
3390 *
3391 * @param breadcrumb The breadcrumb to record.
3392 */
3393 function addBreadcrumb(breadcrumb) {
3394 callOnHub('addBreadcrumb', breadcrumb);
3395 }
3396 /**
3397 * Sets context data with the given name.
3398 * @param name of the context
3399 * @param context Any kind of data. This data will be normalized.
3400 */
3401 // eslint-disable-next-line @typescript-eslint/no-explicit-any
3402 function setContext(name, context) {
3403 callOnHub('setContext', name, context);
3404 }
3405 /**
3406 * Set an object that will be merged sent as extra data with the event.
3407 * @param extras Extras object to merge into current context.
3408 */
3409 function setExtras(extras) {
3410 callOnHub('setExtras', extras);
3411 }
3412 /**
3413 * Set an object that will be merged sent as tags data with the event.
3414 * @param tags Tags context object to merge into current context.
3415 */
3416 function setTags(tags) {
3417 callOnHub('setTags', tags);
3418 }
3419 /**
3420 * Set key:value that will be sent as extra data with the event.
3421 * @param key String of extra
3422 * @param extra Any kind of data. This data will be normalized.
3423 */
3424 function setExtra(key, extra) {
3425 callOnHub('setExtra', key, extra);
3426 }
3427 /**
3428 * Set key:value that will be sent as tags data with the event.
3429 *
3430 * Can also be used to unset a tag, by passing `undefined`.
3431 *
3432 * @param key String key of tag
3433 * @param value Value of tag
3434 */
3435 function setTag(key, value) {
3436 callOnHub('setTag', key, value);
3437 }
3438 /**
3439 * Updates user context information for future events.
3440 *
3441 * @param user User context object to be set in the current context. Pass `null` to unset the user.
3442 */
3443 function setUser(user) {
3444 callOnHub('setUser', user);
3445 }
3446 /**
3447 * Creates a new scope with and executes the given operation within.
3448 * The scope is automatically removed once the operation
3449 * finishes or throws.
3450 *
3451 * This is essentially a convenience function for:
3452 *
3453 * pushScope();
3454 * callback();
3455 * popScope();
3456 *
3457 * @param callback that will be enclosed into push/popScope.
3458 */
3459 function withScope(callback) {
3460 callOnHub('withScope', callback);
3461 }
3462 /**
3463 * Starts a new `Transaction` and returns it. This is the entry point to manual tracing instrumentation.
3464 *
3465 * A tree structure can be built by adding child spans to the transaction, and child spans to other spans. To start a
3466 * new child span within the transaction or any span, call the respective `.startChild()` method.
3467 *
3468 * Every child span must be finished before the transaction is finished, otherwise the unfinished spans are discarded.
3469 *
3470 * The transaction must be finished with a call to its `.finish()` method, at which point the transaction with all its
3471 * finished child spans will be sent to Sentry.
3472 *
3473 * @param context Properties of the new `Transaction`.
3474 * @param customSamplingContext Information given to the transaction sampling function (along with context-dependent
3475 * default values). See {@link Options.tracesSampler}.
3476 *
3477 * @returns The transaction which was just started
3478 */
3479 function startTransaction(context, customSamplingContext) {
3480 return callOnHub('startTransaction', Object.assign({}, context), customSamplingContext);
3481 }
3482
3483 const SENTRY_API_VERSION = '7';
3484 /** Initializes API Details */
3485 function initAPIDetails(dsn, metadata, tunnel) {
3486 return {
3487 initDsn: dsn,
3488 metadata: metadata || {},
3489 dsn: makeDsn(dsn),
3490 tunnel,
3491 };
3492 }
3493 /** Returns the prefix to construct Sentry ingestion API endpoints. */
3494 function getBaseApiEndpoint(dsn) {
3495 const protocol = dsn.protocol ? `${dsn.protocol}:` : '';
3496 const port = dsn.port ? `:${dsn.port}` : '';
3497 return `${protocol}//${dsn.host}${port}${dsn.path ? `/${dsn.path}` : ''}/api/`;
3498 }
3499 /** Returns the ingest API endpoint for target. */
3500 function _getIngestEndpoint(dsn, target) {
3501 return `${getBaseApiEndpoint(dsn)}${dsn.projectId}/${target}/`;
3502 }
3503 /** Returns a URL-encoded string with auth config suitable for a query string. */
3504 function _encodedAuth(dsn) {
3505 return urlEncode({
3506 // We send only the minimum set of required information. See
3507 // https://github.com/getsentry/sentry-javascript/issues/2572.
3508 sentry_key: dsn.publicKey,
3509 sentry_version: SENTRY_API_VERSION,
3510 });
3511 }
3512 /** Returns the store endpoint URL. */
3513 function getStoreEndpoint(dsn) {
3514 return _getIngestEndpoint(dsn, 'store');
3515 }
3516 /**
3517 * Returns the store endpoint URL with auth in the query string.
3518 *
3519 * Sending auth as part of the query string and not as custom HTTP headers avoids CORS preflight requests.
3520 */
3521 function getStoreEndpointWithUrlEncodedAuth(dsn) {
3522 return `${getStoreEndpoint(dsn)}?${_encodedAuth(dsn)}`;
3523 }
3524 /** Returns the envelope endpoint URL. */
3525 function _getEnvelopeEndpoint(dsn) {
3526 return _getIngestEndpoint(dsn, 'envelope');
3527 }
3528 /**
3529 * Returns the envelope endpoint URL with auth in the query string.
3530 *
3531 * Sending auth as part of the query string and not as custom HTTP headers avoids CORS preflight requests.
3532 */
3533 function getEnvelopeEndpointWithUrlEncodedAuth(dsn, tunnel) {
3534 return tunnel ? tunnel : `${_getEnvelopeEndpoint(dsn)}?${_encodedAuth(dsn)}`;
3535 }
3536 /** Returns the url to the report dialog endpoint. */
3537 function getReportDialogEndpoint(dsnLike, dialogOptions) {
3538 const dsn = makeDsn(dsnLike);
3539 const endpoint = `${getBaseApiEndpoint(dsn)}embed/error-page/`;
3540 let encodedOptions = `dsn=${dsnToString(dsn)}`;
3541 for (const key in dialogOptions) {
3542 if (key === 'dsn') {
3543 continue;
3544 }
3545 if (key === 'user') {
3546 if (!dialogOptions.user) {
3547 continue;
3548 }
3549 if (dialogOptions.user.name) {
3550 encodedOptions += `&name=${encodeURIComponent(dialogOptions.user.name)}`;
3551 }
3552 if (dialogOptions.user.email) {
3553 encodedOptions += `&email=${encodeURIComponent(dialogOptions.user.email)}`;
3554 }
3555 }
3556 else {
3557 encodedOptions += `&${encodeURIComponent(key)}=${encodeURIComponent(dialogOptions[key])}`;
3558 }
3559 }
3560 return `${endpoint}?${encodedOptions}`;
3561 }
3562
3563 /*
3564 * This file defines flags and constants that can be modified during compile time in order to facilitate tree shaking
3565 * for users.
3566 *
3567 * Debug flags need to be declared in each package individually and must not be imported across package boundaries,
3568 * because some build tools have trouble tree-shaking imported guards.
3569 *
3570 * As a convention, we define debug flags in a `flags.ts` file in the root of a package's `src` folder.
3571 *
3572 * Debug flag files will contain "magic strings" like `true` that may get replaced with actual values during
3573 * our, or the user's build process. Take care when introducing new flags - they must not throw if they are not
3574 * replaced.
3575 */
3576 /** Flag that is true for debug builds, false otherwise. */
3577 const IS_DEBUG_BUILD$1 = true;
3578
3579 const installedIntegrations = [];
3580 /**
3581 * @private
3582 */
3583 function filterDuplicates(integrations) {
3584 return integrations.reduce((acc, integrations) => {
3585 if (acc.every(accIntegration => integrations.name !== accIntegration.name)) {
3586 acc.push(integrations);
3587 }
3588 return acc;
3589 }, []);
3590 }
3591 /** Gets integration to install */
3592 function getIntegrationsToSetup(options) {
3593 const defaultIntegrations = (options.defaultIntegrations && [...options.defaultIntegrations]) || [];
3594 const userIntegrations = options.integrations;
3595 let integrations = [...filterDuplicates(defaultIntegrations)];
3596 if (Array.isArray(userIntegrations)) {
3597 // Filter out integrations that are also included in user options
3598 integrations = [
3599 ...integrations.filter(integrations => userIntegrations.every(userIntegration => userIntegration.name !== integrations.name)),
3600 // And filter out duplicated user options integrations
3601 ...filterDuplicates(userIntegrations),
3602 ];
3603 }
3604 else if (typeof userIntegrations === 'function') {
3605 integrations = userIntegrations(integrations);
3606 integrations = Array.isArray(integrations) ? integrations : [integrations];
3607 }
3608 // Make sure that if present, `Debug` integration will always run last
3609 const integrationsNames = integrations.map(i => i.name);
3610 const alwaysLastToRun = 'Debug';
3611 if (integrationsNames.indexOf(alwaysLastToRun) !== -1) {
3612 integrations.push(...integrations.splice(integrationsNames.indexOf(alwaysLastToRun), 1));
3613 }
3614 return integrations;
3615 }
3616 /** Setup given integration */
3617 function setupIntegration(integration) {
3618 if (installedIntegrations.indexOf(integration.name) !== -1) {
3619 return;
3620 }
3621 integration.setupOnce(addGlobalEventProcessor, getCurrentHub);
3622 installedIntegrations.push(integration.name);
3623 logger.log(`Integration installed: ${integration.name}`);
3624 }
3625 /**
3626 * Given a list of integration instances this installs them all. When `withDefaults` is set to `true` then all default
3627 * integrations are added unless they were already provided before.
3628 * @param integrations array of integration instances
3629 * @param withDefault should enable default integrations
3630 */
3631 function setupIntegrations(options) {
3632 const integrations = {};
3633 getIntegrationsToSetup(options).forEach(integration => {
3634 integrations[integration.name] = integration;
3635 setupIntegration(integration);
3636 });
3637 // set the `initialized` flag so we don't run through the process again unecessarily; use `Object.defineProperty`
3638 // because by default it creates a property which is nonenumerable, which we want since `initialized` shouldn't be
3639 // considered a member of the index the way the actual integrations are
3640 addNonEnumerableProperty(integrations, 'initialized', true);
3641 return integrations;
3642 }
3643
3644 /* eslint-disable max-lines */
3645 const ALREADY_SEEN_ERROR = "Not capturing exception because it's already been captured.";
3646 /**
3647 * Base implementation for all JavaScript SDK clients.
3648 *
3649 * Call the constructor with the corresponding backend constructor and options
3650 * specific to the client subclass. To access these options later, use
3651 * {@link Client.getOptions}. Also, the Backend instance is available via
3652 * {@link Client.getBackend}.
3653 *
3654 * If a Dsn is specified in the options, it will be parsed and stored. Use
3655 * {@link Client.getDsn} to retrieve the Dsn at any moment. In case the Dsn is
3656 * invalid, the constructor will throw a {@link SentryException}. Note that
3657 * without a valid Dsn, the SDK will not send any events to Sentry.
3658 *
3659 * Before sending an event via the backend, it is passed through
3660 * {@link BaseClient._prepareEvent} to add SDK information and scope data
3661 * (breadcrumbs and context). To add more custom information, override this
3662 * method and extend the resulting prepared event.
3663 *
3664 * To issue automatically created events (e.g. via instrumentation), use
3665 * {@link Client.captureEvent}. It will prepare the event and pass it through
3666 * the callback lifecycle. To issue auto-breadcrumbs, use
3667 * {@link Client.addBreadcrumb}.
3668 *
3669 * @example
3670 * class NodeClient extends BaseClient<NodeBackend, NodeOptions> {
3671 * public constructor(options: NodeOptions) {
3672 * super(NodeBackend, options);
3673 * }
3674 *
3675 * // ...
3676 * }
3677 */
3678 class BaseClient {
3679 /**
3680 * Initializes this client instance.
3681 *
3682 * @param backendClass A constructor function to create the backend.
3683 * @param options Options for the client.
3684 */
3685 constructor(backendClass, options) {
3686 /** Array of used integrations. */
3687 this._integrations = {};
3688 /** Number of calls being processed */
3689 this._numProcessing = 0;
3690 this._backend = new backendClass(options);
3691 this._options = options;
3692 if (options.dsn) {
3693 this._dsn = makeDsn(options.dsn);
3694 }
3695 }
3696 /**
3697 * @inheritDoc
3698 */
3699 // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
3700 captureException(exception, hint, scope) {
3701 // ensure we haven't captured this very object before
3702 if (checkOrSetAlreadyCaught(exception)) {
3703 logger.log(ALREADY_SEEN_ERROR);
3704 return;
3705 }
3706 let eventId = hint && hint.event_id;
3707 this._process(this._getBackend()
3708 .eventFromException(exception, hint)
3709 .then(event => this._captureEvent(event, hint, scope))
3710 .then(result => {
3711 eventId = result;
3712 }));
3713 return eventId;
3714 }
3715 /**
3716 * @inheritDoc
3717 */
3718 captureMessage(message, level, hint, scope) {
3719 let eventId = hint && hint.event_id;
3720 const promisedEvent = isPrimitive(message)
3721 ? this._getBackend().eventFromMessage(String(message), level, hint)
3722 : this._getBackend().eventFromException(message, hint);
3723 this._process(promisedEvent
3724 .then(event => this._captureEvent(event, hint, scope))
3725 .then(result => {
3726 eventId = result;
3727 }));
3728 return eventId;
3729 }
3730 /**
3731 * @inheritDoc
3732 */
3733 captureEvent(event, hint, scope) {
3734 // ensure we haven't captured this very object before
3735 if (hint && hint.originalException && checkOrSetAlreadyCaught(hint.originalException)) {
3736 logger.log(ALREADY_SEEN_ERROR);
3737 return;
3738 }
3739 let eventId = hint && hint.event_id;
3740 this._process(this._captureEvent(event, hint, scope).then(result => {
3741 eventId = result;
3742 }));
3743 return eventId;
3744 }
3745 /**
3746 * @inheritDoc
3747 */
3748 captureSession(session) {
3749 if (!this._isEnabled()) {
3750 logger.warn('SDK not enabled, will not capture session.');
3751 return;
3752 }
3753 if (!(typeof session.release === 'string')) {
3754 logger.warn('Discarded session because of missing or non-string release');
3755 }
3756 else {
3757 this._sendSession(session);
3758 // After sending, we set init false to indicate it's not the first occurrence
3759 session.update({ init: false });
3760 }
3761 }
3762 /**
3763 * @inheritDoc
3764 */
3765 getDsn() {
3766 return this._dsn;
3767 }
3768 /**
3769 * @inheritDoc
3770 */
3771 getOptions() {
3772 return this._options;
3773 }
3774 /**
3775 * @inheritDoc
3776 */
3777 getTransport() {
3778 return this._getBackend().getTransport();
3779 }
3780 /**
3781 * @inheritDoc
3782 */
3783 flush(timeout) {
3784 return this._isClientDoneProcessing(timeout).then(clientFinished => {
3785 return this.getTransport()
3786 .close(timeout)
3787 .then(transportFlushed => clientFinished && transportFlushed);
3788 });
3789 }
3790 /**
3791 * @inheritDoc
3792 */
3793 close(timeout) {
3794 return this.flush(timeout).then(result => {
3795 this.getOptions().enabled = false;
3796 return result;
3797 });
3798 }
3799 /**
3800 * Sets up the integrations
3801 */
3802 setupIntegrations() {
3803 if (this._isEnabled() && !this._integrations.initialized) {
3804 this._integrations = setupIntegrations(this._options);
3805 }
3806 }
3807 /**
3808 * @inheritDoc
3809 */
3810 getIntegration(integration) {
3811 try {
3812 return this._integrations[integration.id] || null;
3813 }
3814 catch (_oO) {
3815 logger.warn(`Cannot retrieve integration ${integration.id} from the current Client`);
3816 return null;
3817 }
3818 }
3819 /** Updates existing session based on the provided event */
3820 _updateSessionFromEvent(session, event) {
3821 let crashed = false;
3822 let errored = false;
3823 const exceptions = event.exception && event.exception.values;
3824 if (exceptions) {
3825 errored = true;
3826 for (const ex of exceptions) {
3827 const mechanism = ex.mechanism;
3828 if (mechanism && mechanism.handled === false) {
3829 crashed = true;
3830 break;
3831 }
3832 }
3833 }
3834 // A session is updated and that session update is sent in only one of the two following scenarios:
3835 // 1. Session with non terminal status and 0 errors + an error occurred -> Will set error count to 1 and send update
3836 // 2. Session with non terminal status and 1 error + a crash occurred -> Will set status crashed and send update
3837 const sessionNonTerminal = session.status === 'ok';
3838 const shouldUpdateAndSend = (sessionNonTerminal && session.errors === 0) || (sessionNonTerminal && crashed);
3839 if (shouldUpdateAndSend) {
3840 session.update(Object.assign(Object.assign({}, (crashed && { status: 'crashed' })), { errors: session.errors || Number(errored || crashed) }));
3841 this.captureSession(session);
3842 }
3843 }
3844 /** Deliver captured session to Sentry */
3845 _sendSession(session) {
3846 this._getBackend().sendSession(session);
3847 }
3848 /**
3849 * Determine if the client is finished processing. Returns a promise because it will wait `timeout` ms before saying
3850 * "no" (resolving to `false`) in order to give the client a chance to potentially finish first.
3851 *
3852 * @param timeout The time, in ms, after which to resolve to `false` if the client is still busy. Passing `0` (or not
3853 * passing anything) will make the promise wait as long as it takes for processing to finish before resolving to
3854 * `true`.
3855 * @returns A promise which will resolve to `true` if processing is already done or finishes before the timeout, and
3856 * `false` otherwise
3857 */
3858 _isClientDoneProcessing(timeout) {
3859 return new SyncPromise(resolve => {
3860 let ticked = 0;
3861 const tick = 1;
3862 const interval = setInterval(() => {
3863 if (this._numProcessing == 0) {
3864 clearInterval(interval);
3865 resolve(true);
3866 }
3867 else {
3868 ticked += tick;
3869 if (timeout && ticked >= timeout) {
3870 clearInterval(interval);
3871 resolve(false);
3872 }
3873 }
3874 }, tick);
3875 });
3876 }
3877 /** Returns the current backend. */
3878 _getBackend() {
3879 return this._backend;
3880 }
3881 /** Determines whether this SDK is enabled and a valid Dsn is present. */
3882 _isEnabled() {
3883 return this.getOptions().enabled !== false && this._dsn !== undefined;
3884 }
3885 /**
3886 * Adds common information to events.
3887 *
3888 * The information includes release and environment from `options`,
3889 * breadcrumbs and context (extra, tags and user) from the scope.
3890 *
3891 * Information that is already present in the event is never overwritten. For
3892 * nested objects, such as the context, keys are merged.
3893 *
3894 * @param event The original event.
3895 * @param hint May contain additional information about the original exception.
3896 * @param scope A scope containing event metadata.
3897 * @returns A new event with more information.
3898 */
3899 _prepareEvent(event, scope, hint) {
3900 const { normalizeDepth = 3, normalizeMaxBreadth = 1000 } = this.getOptions();
3901 const prepared = Object.assign(Object.assign({}, event), { event_id: event.event_id || (hint && hint.event_id ? hint.event_id : uuid4()), timestamp: event.timestamp || dateTimestampInSeconds() });
3902 this._applyClientOptions(prepared);
3903 this._applyIntegrationsMetadata(prepared);
3904 // If we have scope given to us, use it as the base for further modifications.
3905 // This allows us to prevent unnecessary copying of data if `captureContext` is not provided.
3906 let finalScope = scope;
3907 if (hint && hint.captureContext) {
3908 finalScope = Scope.clone(finalScope).update(hint.captureContext);
3909 }
3910 // We prepare the result here with a resolved Event.
3911 let result = resolvedSyncPromise(prepared);
3912 // This should be the last thing called, since we want that
3913 // {@link Hub.addEventProcessor} gets the finished prepared event.
3914 if (finalScope) {
3915 // In case we have a hub we reassign it.
3916 result = finalScope.applyToEvent(prepared, hint);
3917 }
3918 return result.then(evt => {
3919 if (evt) {
3920 // TODO this is more of the hack trying to solve https://github.com/getsentry/sentry-javascript/issues/2809
3921 // it is only attached as extra data to the event if the event somehow skips being normalized
3922 evt.sdkProcessingMetadata = Object.assign(Object.assign({}, evt.sdkProcessingMetadata), { normalizeDepth: `${normalize(normalizeDepth)} (${typeof normalizeDepth})` });
3923 }
3924 if (typeof normalizeDepth === 'number' && normalizeDepth > 0) {
3925 return this._normalizeEvent(evt, normalizeDepth, normalizeMaxBreadth);
3926 }
3927 return evt;
3928 });
3929 }
3930 /**
3931 * Applies `normalize` function on necessary `Event` attributes to make them safe for serialization.
3932 * Normalized keys:
3933 * - `breadcrumbs.data`
3934 * - `user`
3935 * - `contexts`
3936 * - `extra`
3937 * @param event Event
3938 * @returns Normalized event
3939 */
3940 _normalizeEvent(event, depth, maxBreadth) {
3941 if (!event) {
3942 return null;
3943 }
3944 const normalized = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, event), (event.breadcrumbs && {
3945 breadcrumbs: event.breadcrumbs.map(b => (Object.assign(Object.assign({}, b), (b.data && {
3946 data: normalize(b.data, depth, maxBreadth),
3947 })))),
3948 })), (event.user && {
3949 user: normalize(event.user, depth, maxBreadth),
3950 })), (event.contexts && {
3951 contexts: normalize(event.contexts, depth, maxBreadth),
3952 })), (event.extra && {
3953 extra: normalize(event.extra, depth, maxBreadth),
3954 }));
3955 // event.contexts.trace stores information about a Transaction. Similarly,
3956 // event.spans[] stores information about child Spans. Given that a
3957 // Transaction is conceptually a Span, normalization should apply to both
3958 // Transactions and Spans consistently.
3959 // For now the decision is to skip normalization of Transactions and Spans,
3960 // so this block overwrites the normalized event to add back the original
3961 // Transaction information prior to normalization.
3962 if (event.contexts && event.contexts.trace) {
3963 // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
3964 normalized.contexts.trace = event.contexts.trace;
3965 }
3966 normalized.sdkProcessingMetadata = Object.assign(Object.assign({}, normalized.sdkProcessingMetadata), { baseClientNormalized: true });
3967 return normalized;
3968 }
3969 /**
3970 * Enhances event using the client configuration.
3971 * It takes care of all "static" values like environment, release and `dist`,
3972 * as well as truncating overly long values.
3973 * @param event event instance to be enhanced
3974 */
3975 _applyClientOptions(event) {
3976 const options = this.getOptions();
3977 const { environment, release, dist, maxValueLength = 250 } = options;
3978 if (!('environment' in event)) {
3979 event.environment = 'environment' in options ? environment : 'production';
3980 }
3981 if (event.release === undefined && release !== undefined) {
3982 event.release = release;
3983 }
3984 if (event.dist === undefined && dist !== undefined) {
3985 event.dist = dist;
3986 }
3987 if (event.message) {
3988 event.message = truncate(event.message, maxValueLength);
3989 }
3990 const exception = event.exception && event.exception.values && event.exception.values[0];
3991 if (exception && exception.value) {
3992 exception.value = truncate(exception.value, maxValueLength);
3993 }
3994 const request = event.request;
3995 if (request && request.url) {
3996 request.url = truncate(request.url, maxValueLength);
3997 }
3998 }
3999 /**
4000 * This function adds all used integrations to the SDK info in the event.
4001 * @param event The event that will be filled with all integrations.
4002 */
4003 _applyIntegrationsMetadata(event) {
4004 const integrationsArray = Object.keys(this._integrations);
4005 if (integrationsArray.length > 0) {
4006 event.sdk = event.sdk || {};
4007 event.sdk.integrations = [...(event.sdk.integrations || []), ...integrationsArray];
4008 }
4009 }
4010 /**
4011 * Tells the backend to send this event
4012 * @param event The Sentry event to send
4013 */
4014 _sendEvent(event) {
4015 this._getBackend().sendEvent(event);
4016 }
4017 /**
4018 * Processes the event and logs an error in case of rejection
4019 * @param event
4020 * @param hint
4021 * @param scope
4022 */
4023 _captureEvent(event, hint, scope) {
4024 return this._processEvent(event, hint, scope).then(finalEvent => {
4025 return finalEvent.event_id;
4026 }, reason => {
4027 logger.error(reason);
4028 return undefined;
4029 });
4030 }
4031 /**
4032 * Processes an event (either error or message) and sends it to Sentry.
4033 *
4034 * This also adds breadcrumbs and context information to the event. However,
4035 * platform specific meta data (such as the User's IP address) must be added
4036 * by the SDK implementor.
4037 *
4038 *
4039 * @param event The event to send to Sentry.
4040 * @param hint May contain additional information about the original exception.
4041 * @param scope A scope containing event metadata.
4042 * @returns A SyncPromise that resolves with the event or rejects in case event was/will not be send.
4043 */
4044 _processEvent(event, hint, scope) {
4045 // eslint-disable-next-line @typescript-eslint/unbound-method
4046 const { beforeSend, sampleRate } = this.getOptions();
4047 const transport = this.getTransport();
4048 function recordLostEvent(outcome, category) {
4049 if (transport.recordLostEvent) {
4050 transport.recordLostEvent(outcome, category);
4051 }
4052 }
4053 if (!this._isEnabled()) {
4054 return rejectedSyncPromise(new SentryError('SDK not enabled, will not capture event.'));
4055 }
4056 const isTransaction = event.type === 'transaction';
4057 // 1.0 === 100% events are sent
4058 // 0.0 === 0% events are sent
4059 // Sampling for transaction happens somewhere else
4060 if (!isTransaction && typeof sampleRate === 'number' && Math.random() > sampleRate) {
4061 recordLostEvent('sample_rate', 'event');
4062 return rejectedSyncPromise(new SentryError(`Discarding event because it's not included in the random sample (sampling rate = ${sampleRate})`));
4063 }
4064 return this._prepareEvent(event, scope, hint)
4065 .then(prepared => {
4066 if (prepared === null) {
4067 recordLostEvent('event_processor', event.type || 'event');
4068 throw new SentryError('An event processor returned null, will not send event.');
4069 }
4070 const isInternalException = hint && hint.data && hint.data.__sentry__ === true;
4071 if (isInternalException || isTransaction || !beforeSend) {
4072 return prepared;
4073 }
4074 const beforeSendResult = beforeSend(prepared, hint);
4075 return _ensureBeforeSendRv(beforeSendResult);
4076 })
4077 .then(processedEvent => {
4078 if (processedEvent === null) {
4079 recordLostEvent('before_send', event.type || 'event');
4080 throw new SentryError('`beforeSend` returned `null`, will not send event.');
4081 }
4082 const session = scope && scope.getSession && scope.getSession();
4083 if (!isTransaction && session) {
4084 this._updateSessionFromEvent(session, processedEvent);
4085 }
4086 this._sendEvent(processedEvent);
4087 return processedEvent;
4088 })
4089 .then(null, reason => {
4090 if (reason instanceof SentryError) {
4091 throw reason;
4092 }
4093 this.captureException(reason, {
4094 data: {
4095 __sentry__: true,
4096 },
4097 originalException: reason,
4098 });
4099 throw new SentryError(`Event processing pipeline threw an error, original event will not be sent. Details have been sent as a new event.\nReason: ${reason}`);
4100 });
4101 }
4102 /**
4103 * Occupies the client with processing and event
4104 */
4105 _process(promise) {
4106 this._numProcessing += 1;
4107 void promise.then(value => {
4108 this._numProcessing -= 1;
4109 return value;
4110 }, reason => {
4111 this._numProcessing -= 1;
4112 return reason;
4113 });
4114 }
4115 }
4116 /**
4117 * Verifies that return value of configured `beforeSend` is of expected type.
4118 */
4119 function _ensureBeforeSendRv(rv) {
4120 const nullErr = '`beforeSend` method has to return `null` or a valid event.';
4121 if (isThenable(rv)) {
4122 return rv.then(event => {
4123 if (!(isPlainObject(event) || event === null)) {
4124 throw new SentryError(nullErr);
4125 }
4126 return event;
4127 }, e => {
4128 throw new SentryError(`beforeSend rejected with ${e}`);
4129 });
4130 }
4131 else if (!(isPlainObject(rv) || rv === null)) {
4132 throw new SentryError(nullErr);
4133 }
4134 return rv;
4135 }
4136
4137 /** Extract sdk info from from the API metadata */
4138 function getSdkMetadataForEnvelopeHeader(api) {
4139 if (!api.metadata || !api.metadata.sdk) {
4140 return;
4141 }
4142 const { name, version } = api.metadata.sdk;
4143 return { name, version };
4144 }
4145 /**
4146 * Apply SdkInfo (name, version, packages, integrations) to the corresponding event key.
4147 * Merge with existing data if any.
4148 **/
4149 function enhanceEventWithSdkInfo(event, sdkInfo) {
4150 if (!sdkInfo) {
4151 return event;
4152 }
4153 event.sdk = event.sdk || {};
4154 event.sdk.name = event.sdk.name || sdkInfo.name;
4155 event.sdk.version = event.sdk.version || sdkInfo.version;
4156 event.sdk.integrations = [...(event.sdk.integrations || []), ...(sdkInfo.integrations || [])];
4157 event.sdk.packages = [...(event.sdk.packages || []), ...(sdkInfo.packages || [])];
4158 return event;
4159 }
4160 /** Creates an envelope from a Session */
4161 function createSessionEnvelope(session, api) {
4162 const sdkInfo = getSdkMetadataForEnvelopeHeader(api);
4163 const envelopeHeaders = Object.assign(Object.assign({ sent_at: new Date().toISOString() }, (sdkInfo && { sdk: sdkInfo })), (!!api.tunnel && { dsn: dsnToString(api.dsn) }));
4164 // I know this is hacky but we don't want to add `sessions` to request type since it's never rate limited
4165 const type = 'aggregates' in session ? 'sessions' : 'session';
4166 // TODO (v7) Have to cast type because envelope items do not accept a `SentryRequestType`
4167 const envelopeItem = [{ type }, session];
4168 const envelope = createEnvelope(envelopeHeaders, [envelopeItem]);
4169 return [envelope, type];
4170 }
4171 /** Creates a SentryRequest from a Session. */
4172 function sessionToSentryRequest(session, api) {
4173 const [envelope, type] = createSessionEnvelope(session, api);
4174 return {
4175 body: serializeEnvelope(envelope),
4176 type,
4177 url: getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel),
4178 };
4179 }
4180 /**
4181 * Create an Envelope from an event. Note that this is duplicated from below,
4182 * but on purpose as this will be refactored in v7.
4183 */
4184 function createEventEnvelope(event, api) {
4185 const sdkInfo = getSdkMetadataForEnvelopeHeader(api);
4186 const eventType = event.type || 'event';
4187 const { transactionSampling } = event.sdkProcessingMetadata || {};
4188 const { method: samplingMethod, rate: sampleRate } = transactionSampling || {};
4189 // TODO: Below is a temporary hack in order to debug a serialization error - see
4190 // https://github.com/getsentry/sentry-javascript/issues/2809,
4191 // https://github.com/getsentry/sentry-javascript/pull/4425, and
4192 // https://github.com/getsentry/sentry-javascript/pull/4574.
4193 //
4194 // TL; DR: even though we normalize all events (which should prevent this), something is causing `JSON.stringify` to
4195 // throw a circular reference error.
4196 //
4197 // When it's time to remove it:
4198 // 1. Delete everything between here and where the request object `req` is created, EXCEPT the line deleting
4199 // `sdkProcessingMetadata`
4200 // 2. Restore the original version of the request body, which is commented out
4201 // 3. Search for either of the PR URLs above and pull out the companion hacks in the browser playwright tests and the
4202 // baseClient tests in this package
4203 enhanceEventWithSdkInfo(event, api.metadata.sdk);
4204 event.tags = event.tags || {};
4205 event.extra = event.extra || {};
4206 // In theory, all events should be marked as having gone through normalization and so
4207 // we should never set this tag/extra data
4208 if (!(event.sdkProcessingMetadata && event.sdkProcessingMetadata.baseClientNormalized)) {
4209 event.tags.skippedNormalization = true;
4210 event.extra.normalizeDepth = event.sdkProcessingMetadata ? event.sdkProcessingMetadata.normalizeDepth : 'unset';
4211 }
4212 // prevent this data from being sent to sentry
4213 // TODO: This is NOT part of the hack - DO NOT DELETE
4214 delete event.sdkProcessingMetadata;
4215 const envelopeHeaders = Object.assign(Object.assign({ event_id: event.event_id, sent_at: new Date().toISOString() }, (sdkInfo && { sdk: sdkInfo })), (!!api.tunnel && { dsn: dsnToString(api.dsn) }));
4216 const eventItem = [
4217 {
4218 type: eventType,
4219 sample_rates: [{ id: samplingMethod, rate: sampleRate }],
4220 },
4221 event,
4222 ];
4223 return createEnvelope(envelopeHeaders, [eventItem]);
4224 }
4225 /** Creates a SentryRequest from an event. */
4226 function eventToSentryRequest(event, api) {
4227 const sdkInfo = getSdkMetadataForEnvelopeHeader(api);
4228 const eventType = event.type || 'event';
4229 const useEnvelope = eventType === 'transaction' || !!api.tunnel;
4230 const { transactionSampling } = event.sdkProcessingMetadata || {};
4231 const { method: samplingMethod, rate: sampleRate } = transactionSampling || {};
4232 // TODO: Below is a temporary hack in order to debug a serialization error - see
4233 // https://github.com/getsentry/sentry-javascript/issues/2809,
4234 // https://github.com/getsentry/sentry-javascript/pull/4425, and
4235 // https://github.com/getsentry/sentry-javascript/pull/4574.
4236 //
4237 // TL; DR: even though we normalize all events (which should prevent this), something is causing `JSON.stringify` to
4238 // throw a circular reference error.
4239 //
4240 // When it's time to remove it:
4241 // 1. Delete everything between here and where the request object `req` is created, EXCEPT the line deleting
4242 // `sdkProcessingMetadata`
4243 // 2. Restore the original version of the request body, which is commented out
4244 // 3. Search for either of the PR URLs above and pull out the companion hacks in the browser playwright tests and the
4245 // baseClient tests in this package
4246 enhanceEventWithSdkInfo(event, api.metadata.sdk);
4247 event.tags = event.tags || {};
4248 event.extra = event.extra || {};
4249 // In theory, all events should be marked as having gone through normalization and so
4250 // we should never set this tag/extra data
4251 if (!(event.sdkProcessingMetadata && event.sdkProcessingMetadata.baseClientNormalized)) {
4252 event.tags.skippedNormalization = true;
4253 event.extra.normalizeDepth = event.sdkProcessingMetadata ? event.sdkProcessingMetadata.normalizeDepth : 'unset';
4254 }
4255 // prevent this data from being sent to sentry
4256 // TODO: This is NOT part of the hack - DO NOT DELETE
4257 delete event.sdkProcessingMetadata;
4258 let body;
4259 try {
4260 // 99.9% of events should get through just fine - no change in behavior for them
4261 body = JSON.stringify(event);
4262 }
4263 catch (err) {
4264 // Record data about the error without replacing original event data, then force renormalization
4265 event.tags.JSONStringifyError = true;
4266 event.extra.JSONStringifyError = err;
4267 try {
4268 body = JSON.stringify(normalize(event));
4269 }
4270 catch (newErr) {
4271 // At this point even renormalization hasn't worked, meaning something about the event data has gone very wrong.
4272 // Time to cut our losses and record only the new error. With luck, even in the problematic cases we're trying to
4273 // debug with this hack, we won't ever land here.
4274 const innerErr = newErr;
4275 body = JSON.stringify({
4276 message: 'JSON.stringify error after renormalization',
4277 // setting `extra: { innerErr }` here for some reason results in an empty object, so unpack manually
4278 extra: { message: innerErr.message, stack: innerErr.stack },
4279 });
4280 }
4281 }
4282 const req = {
4283 // this is the relevant line of code before the hack was added, to make it easy to undo said hack once we've solved
4284 // the mystery
4285 // body: JSON.stringify(sdkInfo ? enhanceEventWithSdkInfo(event, api.metadata.sdk) : event),
4286 body,
4287 type: eventType,
4288 url: useEnvelope
4289 ? getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel)
4290 : getStoreEndpointWithUrlEncodedAuth(api.dsn),
4291 };
4292 // https://develop.sentry.dev/sdk/envelopes/
4293 // Since we don't need to manipulate envelopes nor store them, there is no
4294 // exported concept of an Envelope with operations including serialization and
4295 // deserialization. Instead, we only implement a minimal subset of the spec to
4296 // serialize events inline here.
4297 if (useEnvelope) {
4298 const envelopeHeaders = Object.assign(Object.assign({ event_id: event.event_id, sent_at: new Date().toISOString() }, (sdkInfo && { sdk: sdkInfo })), (!!api.tunnel && { dsn: dsnToString(api.dsn) }));
4299 const eventItem = [
4300 {
4301 type: eventType,
4302 sample_rates: [{ id: samplingMethod, rate: sampleRate }],
4303 },
4304 req.body,
4305 ];
4306 const envelope = createEnvelope(envelopeHeaders, [eventItem]);
4307 req.body = serializeEnvelope(envelope);
4308 }
4309 return req;
4310 }
4311
4312 /** Noop transport */
4313 class NoopTransport {
4314 /**
4315 * @inheritDoc
4316 */
4317 sendEvent(_) {
4318 return resolvedSyncPromise({
4319 reason: 'NoopTransport: Event has been skipped because no Dsn is configured.',
4320 status: 'skipped',
4321 });
4322 }
4323 /**
4324 * @inheritDoc
4325 */
4326 close(_) {
4327 return resolvedSyncPromise(true);
4328 }
4329 }
4330
4331 /**
4332 * This is the base implemention of a Backend.
4333 * @hidden
4334 */
4335 class BaseBackend {
4336 /** Creates a new backend instance. */
4337 constructor(options) {
4338 this._options = options;
4339 if (!this._options.dsn) {
4340 logger.warn('No DSN provided, backend will not do anything.');
4341 }
4342 this._transport = this._setupTransport();
4343 }
4344 /**
4345 * @inheritDoc
4346 */
4347 // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
4348 eventFromException(_exception, _hint) {
4349 throw new SentryError('Backend has to implement `eventFromException` method');
4350 }
4351 /**
4352 * @inheritDoc
4353 */
4354 eventFromMessage(_message, _level, _hint) {
4355 throw new SentryError('Backend has to implement `eventFromMessage` method');
4356 }
4357 /**
4358 * @inheritDoc
4359 */
4360 sendEvent(event) {
4361 // TODO(v7): Remove the if-else
4362 if (this._newTransport &&
4363 this._options.dsn &&
4364 this._options._experiments &&
4365 this._options._experiments.newTransport) {
4366 const api = initAPIDetails(this._options.dsn, this._options._metadata, this._options.tunnel);
4367 const env = createEventEnvelope(event, api);
4368 void this._newTransport.send(env).then(null, reason => {
4369 logger.error('Error while sending event:', reason);
4370 });
4371 }
4372 else {
4373 void this._transport.sendEvent(event).then(null, reason => {
4374 logger.error('Error while sending event:', reason);
4375 });
4376 }
4377 }
4378 /**
4379 * @inheritDoc
4380 */
4381 sendSession(session) {
4382 if (!this._transport.sendSession) {
4383 logger.warn("Dropping session because custom transport doesn't implement sendSession");
4384 return;
4385 }
4386 // TODO(v7): Remove the if-else
4387 if (this._newTransport &&
4388 this._options.dsn &&
4389 this._options._experiments &&
4390 this._options._experiments.newTransport) {
4391 const api = initAPIDetails(this._options.dsn, this._options._metadata, this._options.tunnel);
4392 const [env] = createSessionEnvelope(session, api);
4393 void this._newTransport.send(env).then(null, reason => {
4394 logger.error('Error while sending session:', reason);
4395 });
4396 }
4397 else {
4398 void this._transport.sendSession(session).then(null, reason => {
4399 logger.error('Error while sending session:', reason);
4400 });
4401 }
4402 }
4403 /**
4404 * @inheritDoc
4405 */
4406 getTransport() {
4407 return this._transport;
4408 }
4409 /**
4410 * Sets up the transport so it can be used later to send requests.
4411 */
4412 _setupTransport() {
4413 return new NoopTransport();
4414 }
4415 }
4416
4417 /**
4418 * Internal function to create a new SDK client instance. The client is
4419 * installed and then bound to the current scope.
4420 *
4421 * @param clientClass The client class to instantiate.
4422 * @param options Options to pass to the client.
4423 */
4424 function initAndBind(clientClass, options) {
4425 if (options.debug === true) {
4426 if (IS_DEBUG_BUILD$1) {
4427 logger.enable();
4428 }
4429 else {
4430 // use `console.warn` rather than `logger.warn` since by non-debug bundles have all `logger.x` statements stripped
4431 // eslint-disable-next-line no-console
4432 console.warn('[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.');
4433 }
4434 }
4435 const hub = getCurrentHub();
4436 const scope = hub.getScope();
4437 if (scope) {
4438 scope.update(options.initialScope);
4439 }
4440 const client = new clientClass(options);
4441 hub.bindClient(client);
4442 }
4443
4444 const DEFAULT_TRANSPORT_BUFFER_SIZE = 30;
4445 /**
4446 * Creates a `NewTransport`
4447 *
4448 * @param options
4449 * @param makeRequest
4450 */
4451 function createTransport(options, makeRequest, buffer = makePromiseBuffer(options.bufferSize || DEFAULT_TRANSPORT_BUFFER_SIZE)) {
4452 let rateLimits = {};
4453 const flush = (timeout) => buffer.drain(timeout);
4454 function send(envelope) {
4455 const envCategory = getEnvelopeType(envelope);
4456 const category = envCategory === 'event' ? 'error' : envCategory;
4457 const request = {
4458 category,
4459 body: serializeEnvelope(envelope),
4460 };
4461 // Don't add to buffer if transport is already rate-limited
4462 if (isRateLimited(rateLimits, category)) {
4463 return rejectedSyncPromise({
4464 status: 'rate_limit',
4465 reason: getRateLimitReason(rateLimits, category),
4466 });
4467 }
4468 const requestTask = () => makeRequest(request).then(({ body, headers, reason, statusCode }) => {
4469 const status = eventStatusFromHttpCode(statusCode);
4470 if (headers) {
4471 rateLimits = updateRateLimits(rateLimits, headers);
4472 }
4473 if (status === 'success') {
4474 return resolvedSyncPromise({ status, reason });
4475 }
4476 return rejectedSyncPromise({
4477 status,
4478 reason: reason ||
4479 body ||
4480 (status === 'rate_limit' ? getRateLimitReason(rateLimits, category) : 'Unknown transport error'),
4481 });
4482 });
4483 return buffer.add(requestTask);
4484 }
4485 return {
4486 send,
4487 flush,
4488 };
4489 }
4490 function getRateLimitReason(rateLimits, category) {
4491 return `Too many ${category} requests, backing off until: ${new Date(disabledUntil(rateLimits, category)).toISOString()}`;
4492 }
4493
4494 const SDK_VERSION = '6.19.7';
4495
4496 let originalFunctionToString;
4497 /** Patch toString calls to return proper name for wrapped functions */
4498 class FunctionToString {
4499 constructor() {
4500 /**
4501 * @inheritDoc
4502 */
4503 this.name = FunctionToString.id;
4504 }
4505 /**
4506 * @inheritDoc
4507 */
4508 setupOnce() {
4509 // eslint-disable-next-line @typescript-eslint/unbound-method
4510 originalFunctionToString = Function.prototype.toString;
4511 // eslint-disable-next-line @typescript-eslint/no-explicit-any
4512 Function.prototype.toString = function (...args) {
4513 const context = getOriginalFunction(this) || this;
4514 return originalFunctionToString.apply(context, args);
4515 };
4516 }
4517 }
4518 /**
4519 * @inheritDoc
4520 */
4521 FunctionToString.id = 'FunctionToString';
4522
4523 // "Script error." is hard coded into browsers for errors that it can't read.
4524 // this is the result of a script being pulled in from an external domain and CORS.
4525 const DEFAULT_IGNORE_ERRORS = [/^Script error\.?$/, /^Javascript error: Script error\.? on line 0$/];
4526 /** Inbound filters configurable by the user */
4527 class InboundFilters {
4528 constructor(_options = {}) {
4529 this._options = _options;
4530 /**
4531 * @inheritDoc
4532 */
4533 this.name = InboundFilters.id;
4534 }
4535 /**
4536 * @inheritDoc
4537 */
4538 setupOnce(addGlobalEventProcessor, getCurrentHub) {
4539 addGlobalEventProcessor((event) => {
4540 const hub = getCurrentHub();
4541 if (hub) {
4542 const self = hub.getIntegration(InboundFilters);
4543 if (self) {
4544 const client = hub.getClient();
4545 const clientOptions = client ? client.getOptions() : {};
4546 const options = _mergeOptions(self._options, clientOptions);
4547 return _shouldDropEvent$1(event, options) ? null : event;
4548 }
4549 }
4550 return event;
4551 });
4552 }
4553 }
4554 /**
4555 * @inheritDoc
4556 */
4557 InboundFilters.id = 'InboundFilters';
4558 /** JSDoc */
4559 function _mergeOptions(internalOptions = {}, clientOptions = {}) {
4560 return {
4561 allowUrls: [
4562 // eslint-disable-next-line deprecation/deprecation
4563 ...(internalOptions.whitelistUrls || []),
4564 ...(internalOptions.allowUrls || []),
4565 // eslint-disable-next-line deprecation/deprecation
4566 ...(clientOptions.whitelistUrls || []),
4567 ...(clientOptions.allowUrls || []),
4568 ],
4569 denyUrls: [
4570 // eslint-disable-next-line deprecation/deprecation
4571 ...(internalOptions.blacklistUrls || []),
4572 ...(internalOptions.denyUrls || []),
4573 // eslint-disable-next-line deprecation/deprecation
4574 ...(clientOptions.blacklistUrls || []),
4575 ...(clientOptions.denyUrls || []),
4576 ],
4577 ignoreErrors: [
4578 ...(internalOptions.ignoreErrors || []),
4579 ...(clientOptions.ignoreErrors || []),
4580 ...DEFAULT_IGNORE_ERRORS,
4581 ],
4582 ignoreInternal: internalOptions.ignoreInternal !== undefined ? internalOptions.ignoreInternal : true,
4583 };
4584 }
4585 /** JSDoc */
4586 function _shouldDropEvent$1(event, options) {
4587 if (options.ignoreInternal && _isSentryError(event)) {
4588 IS_DEBUG_BUILD$1 &&
4589 logger.warn(`Event dropped due to being internal Sentry Error.\nEvent: ${getEventDescription(event)}`);
4590 return true;
4591 }
4592 if (_isIgnoredError(event, options.ignoreErrors)) {
4593 IS_DEBUG_BUILD$1 &&
4594 logger.warn(`Event dropped due to being matched by \`ignoreErrors\` option.\nEvent: ${getEventDescription(event)}`);
4595 return true;
4596 }
4597 if (_isDeniedUrl(event, options.denyUrls)) {
4598 IS_DEBUG_BUILD$1 &&
4599 logger.warn(`Event dropped due to being matched by \`denyUrls\` option.\nEvent: ${getEventDescription(event)}.\nUrl: ${_getEventFilterUrl(event)}`);
4600 return true;
4601 }
4602 if (!_isAllowedUrl(event, options.allowUrls)) {
4603 IS_DEBUG_BUILD$1 &&
4604 logger.warn(`Event dropped due to not being matched by \`allowUrls\` option.\nEvent: ${getEventDescription(event)}.\nUrl: ${_getEventFilterUrl(event)}`);
4605 return true;
4606 }
4607 return false;
4608 }
4609 function _isIgnoredError(event, ignoreErrors) {
4610 if (!ignoreErrors || !ignoreErrors.length) {
4611 return false;
4612 }
4613 return _getPossibleEventMessages(event).some(message => ignoreErrors.some(pattern => isMatchingPattern(message, pattern)));
4614 }
4615 function _isDeniedUrl(event, denyUrls) {
4616 // TODO: Use Glob instead?
4617 if (!denyUrls || !denyUrls.length) {
4618 return false;
4619 }
4620 const url = _getEventFilterUrl(event);
4621 return !url ? false : denyUrls.some(pattern => isMatchingPattern(url, pattern));
4622 }
4623 function _isAllowedUrl(event, allowUrls) {
4624 // TODO: Use Glob instead?
4625 if (!allowUrls || !allowUrls.length) {
4626 return true;
4627 }
4628 const url = _getEventFilterUrl(event);
4629 return !url ? true : allowUrls.some(pattern => isMatchingPattern(url, pattern));
4630 }
4631 function _getPossibleEventMessages(event) {
4632 if (event.message) {
4633 return [event.message];
4634 }
4635 if (event.exception) {
4636 try {
4637 const { type = '', value = '' } = (event.exception.values && event.exception.values[0]) || {};
4638 return [`${value}`, `${type}: ${value}`];
4639 }
4640 catch (oO) {
4641 IS_DEBUG_BUILD$1 && logger.error(`Cannot extract message for event ${getEventDescription(event)}`);
4642 return [];
4643 }
4644 }
4645 return [];
4646 }
4647 function _isSentryError(event) {
4648 try {
4649 // @ts-ignore can't be a sentry error if undefined
4650 // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
4651 return event.exception.values[0].type === 'SentryError';
4652 }
4653 catch (e) {
4654 // ignore
4655 }
4656 return false;
4657 }
4658 function _getLastValidUrl(frames = []) {
4659 for (let i = frames.length - 1; i >= 0; i--) {
4660 const frame = frames[i];
4661 if (frame && frame.filename !== '<anonymous>' && frame.filename !== '[native code]') {
4662 return frame.filename || null;
4663 }
4664 }
4665 return null;
4666 }
4667 function _getEventFilterUrl(event) {
4668 try {
4669 if (event.stacktrace) {
4670 return _getLastValidUrl(event.stacktrace.frames);
4671 }
4672 let frames;
4673 try {
4674 // @ts-ignore we only care about frames if the whole thing here is defined
4675 frames = event.exception.values[0].stacktrace.frames;
4676 }
4677 catch (e) {
4678 // ignore
4679 }
4680 return frames ? _getLastValidUrl(frames) : null;
4681 }
4682 catch (oO) {
4683 IS_DEBUG_BUILD$1 && logger.error(`Cannot extract url for event ${getEventDescription(event)}`);
4684 return null;
4685 }
4686 }
4687
4688 var CoreIntegrations = /*#__PURE__*/Object.freeze({
4689 __proto__: null,
4690 FunctionToString: FunctionToString,
4691 InboundFilters: InboundFilters
4692 });
4693
4694 // global reference to slice
4695 const UNKNOWN_FUNCTION = '?';
4696 const OPERA10_PRIORITY = 10;
4697 const OPERA11_PRIORITY = 20;
4698 const CHROME_PRIORITY = 30;
4699 const WINJS_PRIORITY = 40;
4700 const GECKO_PRIORITY = 50;
4701 function createFrame(filename, func, lineno, colno) {
4702 const frame = {
4703 filename,
4704 function: func,
4705 // All browser frames are considered in_app
4706 in_app: true,
4707 };
4708 if (lineno !== undefined) {
4709 frame.lineno = lineno;
4710 }
4711 if (colno !== undefined) {
4712 frame.colno = colno;
4713 }
4714 return frame;
4715 }
4716 // Chromium based browsers: Chrome, Brave, new Opera, new Edge
4717 const chromeRegex = /^\s*at (?:(.*?) ?\((?:address at )?)?((?:file|https?|blob|chrome-extension|address|native|eval|webpack|<anonymous>|[-a-z]+:|.*bundle|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;
4718 const chromeEvalRegex = /\((\S*)(?::(\d+))(?::(\d+))\)/;
4719 const chrome = line => {
4720 const parts = chromeRegex.exec(line);
4721 if (parts) {
4722 const isEval = parts[2] && parts[2].indexOf('eval') === 0; // start of line
4723 if (isEval) {
4724 const subMatch = chromeEvalRegex.exec(parts[2]);
4725 if (subMatch) {
4726 // throw out eval line/column and use top-most line/column number
4727 parts[2] = subMatch[1]; // url
4728 parts[3] = subMatch[2]; // line
4729 parts[4] = subMatch[3]; // column
4730 }
4731 }
4732 // Kamil: One more hack won't hurt us right? Understanding and adding more rules on top of these regexps right now
4733 // would be way too time consuming. (TODO: Rewrite whole RegExp to be more readable)
4734 const [func, filename] = extractSafariExtensionDetails(parts[1] || UNKNOWN_FUNCTION, parts[2]);
4735 return createFrame(filename, func, parts[3] ? +parts[3] : undefined, parts[4] ? +parts[4] : undefined);
4736 }
4737 return;
4738 };
4739 const chromeStackParser = [CHROME_PRIORITY, chrome];
4740 // gecko regex: `(?:bundle|\d+\.js)`: `bundle` is for react native, `\d+\.js` also but specifically for ram bundles because it
4741 // generates filenames without a prefix like `file://` the filenames in the stacktrace are just 42.js
4742 // We need this specific case for now because we want no other regex to match.
4743 const geckoREgex = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)?((?:file|https?|blob|chrome|webpack|resource|moz-extension|capacitor).*?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js)|\/[\w\-. /=]+)(?::(\d+))?(?::(\d+))?\s*$/i;
4744 const geckoEvalRegex = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i;
4745 const gecko = line => {
4746 const parts = geckoREgex.exec(line);
4747 if (parts) {
4748 const isEval = parts[3] && parts[3].indexOf(' > eval') > -1;
4749 if (isEval) {
4750 const subMatch = geckoEvalRegex.exec(parts[3]);
4751 if (subMatch) {
4752 // throw out eval line/column and use top-most line number
4753 parts[1] = parts[1] || 'eval';
4754 parts[3] = subMatch[1];
4755 parts[4] = subMatch[2];
4756 parts[5] = ''; // no column when eval
4757 }
4758 }
4759 let filename = parts[3];
4760 let func = parts[1] || UNKNOWN_FUNCTION;
4761 [func, filename] = extractSafariExtensionDetails(func, filename);
4762 return createFrame(filename, func, parts[4] ? +parts[4] : undefined, parts[5] ? +parts[5] : undefined);
4763 }
4764 return;
4765 };
4766 const geckoStackParser = [GECKO_PRIORITY, gecko];
4767 const winjsRegex = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i;
4768 const winjs = line => {
4769 const parts = winjsRegex.exec(line);
4770 return parts
4771 ? createFrame(parts[2], parts[1] || UNKNOWN_FUNCTION, +parts[3], parts[4] ? +parts[4] : undefined)
4772 : undefined;
4773 };
4774 const winjsStackParser = [WINJS_PRIORITY, winjs];
4775 const opera10Regex = / line (\d+).*script (?:in )?(\S+)(?:: in function (\S+))?$/i;
4776 const opera10 = line => {
4777 const parts = opera10Regex.exec(line);
4778 return parts ? createFrame(parts[2], parts[3] || UNKNOWN_FUNCTION, +parts[1]) : undefined;
4779 };
4780 const opera10StackParser = [OPERA10_PRIORITY, opera10];
4781 const opera11Regex = / line (\d+), column (\d+)\s*(?:in (?:<anonymous function: ([^>]+)>|([^)]+))\(.*\))? in (.*):\s*$/i;
4782 const opera11 = line => {
4783 const parts = opera11Regex.exec(line);
4784 return parts ? createFrame(parts[5], parts[3] || parts[4] || UNKNOWN_FUNCTION, +parts[1], +parts[2]) : undefined;
4785 };
4786 const opera11StackParser = [OPERA11_PRIORITY, opera11];
4787 /**
4788 * Safari web extensions, starting version unknown, can produce "frames-only" stacktraces.
4789 * What it means, is that instead of format like:
4790 *
4791 * Error: wat
4792 * at function@url:row:col
4793 * at function@url:row:col
4794 * at function@url:row:col
4795 *
4796 * it produces something like:
4797 *
4798 * function@url:row:col
4799 * function@url:row:col
4800 * function@url:row:col
4801 *
4802 * Because of that, it won't be captured by `chrome` RegExp and will fall into `Gecko` branch.
4803 * This function is extracted so that we can use it in both places without duplicating the logic.
4804 * Unfortunately "just" changing RegExp is too complicated now and making it pass all tests
4805 * and fix this case seems like an impossible, or at least way too time-consuming task.
4806 */
4807 const extractSafariExtensionDetails = (func, filename) => {
4808 const isSafariExtension = func.indexOf('safari-extension') !== -1;
4809 const isSafariWebExtension = func.indexOf('safari-web-extension') !== -1;
4810 return isSafariExtension || isSafariWebExtension
4811 ? [
4812 func.indexOf('@') !== -1 ? func.split('@')[0] : UNKNOWN_FUNCTION,
4813 isSafariExtension ? `safari-extension:${filename}` : `safari-web-extension:${filename}`,
4814 ]
4815 : [func, filename];
4816 };
4817
4818 /**
4819 * This function creates an exception from an TraceKitStackTrace
4820 * @param stacktrace TraceKitStackTrace that will be converted to an exception
4821 * @hidden
4822 */
4823 function exceptionFromError(ex) {
4824 // Get the frames first since Opera can lose the stack if we touch anything else first
4825 const frames = parseStackFrames(ex);
4826 const exception = {
4827 type: ex && ex.name,
4828 value: extractMessage(ex),
4829 };
4830 if (frames.length) {
4831 exception.stacktrace = { frames };
4832 }
4833 if (exception.type === undefined && exception.value === '') {
4834 exception.value = 'Unrecoverable error caught';
4835 }
4836 return exception;
4837 }
4838 /**
4839 * @hidden
4840 */
4841 function eventFromPlainObject(exception, syntheticException, isUnhandledRejection) {
4842 const event = {
4843 exception: {
4844 values: [
4845 {
4846 type: isEvent(exception) ? exception.constructor.name : isUnhandledRejection ? 'UnhandledRejection' : 'Error',
4847 value: `Non-Error ${isUnhandledRejection ? 'promise rejection' : 'exception'} captured with keys: ${extractExceptionKeysForMessage(exception)}`,
4848 },
4849 ],
4850 },
4851 extra: {
4852 __serialized__: normalizeToSize(exception),
4853 },
4854 };
4855 if (syntheticException) {
4856 const frames = parseStackFrames(syntheticException);
4857 if (frames.length) {
4858 event.stacktrace = { frames };
4859 }
4860 }
4861 return event;
4862 }
4863 /**
4864 * @hidden
4865 */
4866 function eventFromError(ex) {
4867 return {
4868 exception: {
4869 values: [exceptionFromError(ex)],
4870 },
4871 };
4872 }
4873 /** Parses stack frames from an error */
4874 function parseStackFrames(ex) {
4875 // Access and store the stacktrace property before doing ANYTHING
4876 // else to it because Opera is not very good at providing it
4877 // reliably in other circumstances.
4878 const stacktrace = ex.stacktrace || ex.stack || '';
4879 const popSize = getPopSize(ex);
4880 try {
4881 return createStackParser(opera10StackParser, opera11StackParser, chromeStackParser, winjsStackParser, geckoStackParser)(stacktrace, popSize);
4882 }
4883 catch (e) {
4884 // no-empty
4885 }
4886 return [];
4887 }
4888 // Based on our own mapping pattern - https://github.com/getsentry/sentry/blob/9f08305e09866c8bd6d0c24f5b0aabdd7dd6c59c/src/sentry/lang/javascript/errormapping.py#L83-L108
4889 const reactMinifiedRegexp = /Minified React error #\d+;/i;
4890 function getPopSize(ex) {
4891 if (ex) {
4892 if (typeof ex.framesToPop === 'number') {
4893 return ex.framesToPop;
4894 }
4895 if (reactMinifiedRegexp.test(ex.message)) {
4896 return 1;
4897 }
4898 }
4899 return 0;
4900 }
4901 /**
4902 * There are cases where stacktrace.message is an Event object
4903 * https://github.com/getsentry/sentry-javascript/issues/1949
4904 * In this specific case we try to extract stacktrace.message.error.message
4905 */
4906 function extractMessage(ex) {
4907 const message = ex && ex.message;
4908 if (!message) {
4909 return 'No error message';
4910 }
4911 if (message.error && typeof message.error.message === 'string') {
4912 return message.error.message;
4913 }
4914 return message;
4915 }
4916 /**
4917 * Creates an {@link Event} from all inputs to `captureException` and non-primitive inputs to `captureMessage`.
4918 * @hidden
4919 */
4920 function eventFromException(exception, hint, attachStacktrace) {
4921 const syntheticException = (hint && hint.syntheticException) || undefined;
4922 const event = eventFromUnknownInput(exception, syntheticException, attachStacktrace);
4923 addExceptionMechanism(event); // defaults to { type: 'generic', handled: true }
4924 event.level = exports.Severity.Error;
4925 if (hint && hint.event_id) {
4926 event.event_id = hint.event_id;
4927 }
4928 return resolvedSyncPromise(event);
4929 }
4930 /**
4931 * Builds and Event from a Message
4932 * @hidden
4933 */
4934 function eventFromMessage(message, level = exports.Severity.Info, hint, attachStacktrace) {
4935 const syntheticException = (hint && hint.syntheticException) || undefined;
4936 const event = eventFromString(message, syntheticException, attachStacktrace);
4937 event.level = level;
4938 if (hint && hint.event_id) {
4939 event.event_id = hint.event_id;
4940 }
4941 return resolvedSyncPromise(event);
4942 }
4943 /**
4944 * @hidden
4945 */
4946 function eventFromUnknownInput(exception, syntheticException, attachStacktrace, isUnhandledRejection) {
4947 let event;
4948 if (isErrorEvent(exception) && exception.error) {
4949 // If it is an ErrorEvent with `error` property, extract it to get actual Error
4950 const errorEvent = exception;
4951 return eventFromError(errorEvent.error);
4952 }
4953 // If it is a `DOMError` (which is a legacy API, but still supported in some browsers) then we just extract the name
4954 // and message, as it doesn't provide anything else. According to the spec, all `DOMExceptions` should also be
4955 // `Error`s, but that's not the case in IE11, so in that case we treat it the same as we do a `DOMError`.
4956 //
4957 // https://developer.mozilla.org/en-US/docs/Web/API/DOMError
4958 // https://developer.mozilla.org/en-US/docs/Web/API/DOMException
4959 // https://webidl.spec.whatwg.org/#es-DOMException-specialness
4960 if (isDOMError(exception) || isDOMException(exception)) {
4961 const domException = exception;
4962 if ('stack' in exception) {
4963 event = eventFromError(exception);
4964 }
4965 else {
4966 const name = domException.name || (isDOMError(domException) ? 'DOMError' : 'DOMException');
4967 const message = domException.message ? `${name}: ${domException.message}` : name;
4968 event = eventFromString(message, syntheticException, attachStacktrace);
4969 addExceptionTypeValue(event, message);
4970 }
4971 if ('code' in domException) {
4972 event.tags = Object.assign(Object.assign({}, event.tags), { 'DOMException.code': `${domException.code}` });
4973 }
4974 return event;
4975 }
4976 if (isError(exception)) {
4977 // we have a real Error object, do nothing
4978 return eventFromError(exception);
4979 }
4980 if (isPlainObject(exception) || isEvent(exception)) {
4981 // If it's a plain object or an instance of `Event` (the built-in JS kind, not this SDK's `Event` type), serialize
4982 // it manually. This will allow us to group events based on top-level keys which is much better than creating a new
4983 // group on any key/value change.
4984 const objectException = exception;
4985 event = eventFromPlainObject(objectException, syntheticException, isUnhandledRejection);
4986 addExceptionMechanism(event, {
4987 synthetic: true,
4988 });
4989 return event;
4990 }
4991 // If none of previous checks were valid, then it means that it's not:
4992 // - an instance of DOMError
4993 // - an instance of DOMException
4994 // - an instance of Event
4995 // - an instance of Error
4996 // - a valid ErrorEvent (one with an error property)
4997 // - a plain Object
4998 //
4999 // So bail out and capture it as a simple message:
5000 event = eventFromString(exception, syntheticException, attachStacktrace);
5001 addExceptionTypeValue(event, `${exception}`, undefined);
5002 addExceptionMechanism(event, {
5003 synthetic: true,
5004 });
5005 return event;
5006 }
5007 /**
5008 * @hidden
5009 */
5010 function eventFromString(input, syntheticException, attachStacktrace) {
5011 const event = {
5012 message: input,
5013 };
5014 if (attachStacktrace && syntheticException) {
5015 const frames = parseStackFrames(syntheticException);
5016 if (frames.length) {
5017 event.stacktrace = { frames };
5018 }
5019 }
5020 return event;
5021 }
5022
5023 /*
5024 * This file defines flags and constants that can be modified during compile time in order to facilitate tree shaking
5025 * for users.
5026 *
5027 * Debug flags need to be declared in each package individually and must not be imported across package boundaries,
5028 * because some build tools have trouble tree-shaking imported guards.
5029 *
5030 * As a convention, we define debug flags in a `flags.ts` file in the root of a package's `src` folder.
5031 *
5032 * Debug flag files will contain "magic strings" like `true` that may get replaced with actual values during
5033 * our, or the user's build process. Take care when introducing new flags - they must not throw if they are not
5034 * replaced.
5035 */
5036 /** Flag that is true for debug builds, false otherwise. */
5037 const IS_DEBUG_BUILD = true;
5038
5039 const global$4 = getGlobalObject();
5040 let cachedFetchImpl;
5041 /**
5042 * A special usecase for incorrectly wrapped Fetch APIs in conjunction with ad-blockers.
5043 * Whenever someone wraps the Fetch API and returns the wrong promise chain,
5044 * this chain becomes orphaned and there is no possible way to capture it's rejections
5045 * other than allowing it bubble up to this very handler. eg.
5046 *
5047 * const f = window.fetch;
5048 * window.fetch = function () {
5049 * const p = f.apply(this, arguments);
5050 *
5051 * p.then(function() {
5052 * console.log('hi.');
5053 * });
5054 *
5055 * return p;
5056 * }
5057 *
5058 * `p.then(function () { ... })` is producing a completely separate promise chain,
5059 * however, what's returned is `p` - the result of original `fetch` call.
5060 *
5061 * This mean, that whenever we use the Fetch API to send our own requests, _and_
5062 * some ad-blocker blocks it, this orphaned chain will _always_ reject,
5063 * effectively causing another event to be captured.
5064 * This makes a whole process become an infinite loop, which we need to somehow
5065 * deal with, and break it in one way or another.
5066 *
5067 * To deal with this issue, we are making sure that we _always_ use the real
5068 * browser Fetch API, instead of relying on what `window.fetch` exposes.
5069 * The only downside to this would be missing our own requests as breadcrumbs,
5070 * but because we are already not doing this, it should be just fine.
5071 *
5072 * Possible failed fetch error messages per-browser:
5073 *
5074 * Chrome: Failed to fetch
5075 * Edge: Failed to Fetch
5076 * Firefox: NetworkError when attempting to fetch resource
5077 * Safari: resource blocked by content blocker
5078 */
5079 function getNativeFetchImplementation() {
5080 if (cachedFetchImpl) {
5081 return cachedFetchImpl;
5082 }
5083 /* eslint-disable @typescript-eslint/unbound-method */
5084 // Fast path to avoid DOM I/O
5085 if (isNativeFetch(global$4.fetch)) {
5086 return (cachedFetchImpl = global$4.fetch.bind(global$4));
5087 }
5088 const document = global$4.document;
5089 let fetchImpl = global$4.fetch;
5090 // eslint-disable-next-line deprecation/deprecation
5091 if (document && typeof document.createElement === 'function') {
5092 try {
5093 const sandbox = document.createElement('iframe');
5094 sandbox.hidden = true;
5095 document.head.appendChild(sandbox);
5096 const contentWindow = sandbox.contentWindow;
5097 if (contentWindow && contentWindow.fetch) {
5098 fetchImpl = contentWindow.fetch;
5099 }
5100 document.head.removeChild(sandbox);
5101 }
5102 catch (e) {
5103 logger.warn('Could not create sandbox iframe for pure fetch check, bailing to window.fetch: ', e);
5104 }
5105 }
5106 return (cachedFetchImpl = fetchImpl.bind(global$4));
5107 /* eslint-enable @typescript-eslint/unbound-method */
5108 }
5109 /**
5110 * Sends sdk client report using sendBeacon or fetch as a fallback if available
5111 *
5112 * @param url report endpoint
5113 * @param body report payload
5114 */
5115 function sendReport(url, body) {
5116 const isRealNavigator = Object.prototype.toString.call(global$4 && global$4.navigator) === '[object Navigator]';
5117 const hasSendBeacon = isRealNavigator && typeof global$4.navigator.sendBeacon === 'function';
5118 if (hasSendBeacon) {
5119 // Prevent illegal invocations - https://xgwang.me/posts/you-may-not-know-beacon/#it-may-throw-error%2C-be-sure-to-catch
5120 const sendBeacon = global$4.navigator.sendBeacon.bind(global$4.navigator);
5121 return sendBeacon(url, body);
5122 }
5123 if (supportsFetch()) {
5124 const fetch = getNativeFetchImplementation();
5125 return forget(fetch(url, {
5126 body,
5127 method: 'POST',
5128 credentials: 'omit',
5129 keepalive: true,
5130 }));
5131 }
5132 }
5133
5134 function requestTypeToCategory(ty) {
5135 const tyStr = ty;
5136 return tyStr === 'event' ? 'error' : tyStr;
5137 }
5138 const global$3 = getGlobalObject();
5139 /** Base Transport class implementation */
5140 class BaseTransport {
5141 constructor(options) {
5142 this.options = options;
5143 /** A simple buffer holding all requests. */
5144 this._buffer = makePromiseBuffer(30);
5145 /** Locks transport after receiving rate limits in a response */
5146 this._rateLimits = {};
5147 this._outcomes = {};
5148 this._api = initAPIDetails(options.dsn, options._metadata, options.tunnel);
5149 // eslint-disable-next-line deprecation/deprecation
5150 this.url = getStoreEndpointWithUrlEncodedAuth(this._api.dsn);
5151 if (this.options.sendClientReports && global$3.document) {
5152 global$3.document.addEventListener('visibilitychange', () => {
5153 if (global$3.document.visibilityState === 'hidden') {
5154 this._flushOutcomes();
5155 }
5156 });
5157 }
5158 }
5159 /**
5160 * @inheritDoc
5161 */
5162 sendEvent(event) {
5163 return this._sendRequest(eventToSentryRequest(event, this._api), event);
5164 }
5165 /**
5166 * @inheritDoc
5167 */
5168 sendSession(session) {
5169 return this._sendRequest(sessionToSentryRequest(session, this._api), session);
5170 }
5171 /**
5172 * @inheritDoc
5173 */
5174 close(timeout) {
5175 return this._buffer.drain(timeout);
5176 }
5177 /**
5178 * @inheritDoc
5179 */
5180 recordLostEvent(reason, category) {
5181 var _a;
5182 if (!this.options.sendClientReports) {
5183 return;
5184 }
5185 // We want to track each category (event, transaction, session) separately
5186 // but still keep the distinction between different type of outcomes.
5187 // We could use nested maps, but it's much easier to read and type this way.
5188 // A correct type for map-based implementation if we want to go that route
5189 // would be `Partial<Record<SentryRequestType, Partial<Record<Outcome, number>>>>`
5190 const key = `${requestTypeToCategory(category)}:${reason}`;
5191 IS_DEBUG_BUILD && logger.log(`Adding outcome: ${key}`);
5192 this._outcomes[key] = (_a = this._outcomes[key], (_a !== null && _a !== void 0 ? _a : 0)) + 1;
5193 }
5194 /**
5195 * Send outcomes as an envelope
5196 */
5197 _flushOutcomes() {
5198 if (!this.options.sendClientReports) {
5199 return;
5200 }
5201 const outcomes = this._outcomes;
5202 this._outcomes = {};
5203 // Nothing to send
5204 if (!Object.keys(outcomes).length) {
5205 IS_DEBUG_BUILD && logger.log('No outcomes to flush');
5206 return;
5207 }
5208 IS_DEBUG_BUILD && logger.log(`Flushing outcomes:\n${JSON.stringify(outcomes, null, 2)}`);
5209 const url = getEnvelopeEndpointWithUrlEncodedAuth(this._api.dsn, this._api.tunnel);
5210 const discardedEvents = Object.keys(outcomes).map(key => {
5211 const [category, reason] = key.split(':');
5212 return {
5213 reason,
5214 category,
5215 quantity: outcomes[key],
5216 };
5217 // TODO: Improve types on discarded_events to get rid of cast
5218 });
5219 const envelope = createClientReportEnvelope(discardedEvents, this._api.tunnel && dsnToString(this._api.dsn));
5220 try {
5221 sendReport(url, serializeEnvelope(envelope));
5222 }
5223 catch (e) {
5224 IS_DEBUG_BUILD && logger.error(e);
5225 }
5226 }
5227 /**
5228 * Handle Sentry repsonse for promise-based transports.
5229 */
5230 _handleResponse({ requestType, response, headers, resolve, reject, }) {
5231 const status = eventStatusFromHttpCode(response.status);
5232 this._rateLimits = updateRateLimits(this._rateLimits, headers);
5233 // eslint-disable-next-line deprecation/deprecation
5234 if (this._isRateLimited(requestType)) {
5235 IS_DEBUG_BUILD &&
5236 // eslint-disable-next-line deprecation/deprecation
5237 logger.warn(`Too many ${requestType} requests, backing off until: ${this._disabledUntil(requestType)}`);
5238 }
5239 if (status === 'success') {
5240 resolve({ status });
5241 return;
5242 }
5243 reject(response);
5244 }
5245 /**
5246 * Gets the time that given category is disabled until for rate limiting
5247 *
5248 * @deprecated Please use `disabledUntil` from @sentry/utils
5249 */
5250 _disabledUntil(requestType) {
5251 const category = requestTypeToCategory(requestType);
5252 return new Date(disabledUntil(this._rateLimits, category));
5253 }
5254 /**
5255 * Checks if a category is rate limited
5256 *
5257 * @deprecated Please use `isRateLimited` from @sentry/utils
5258 */
5259 _isRateLimited(requestType) {
5260 const category = requestTypeToCategory(requestType);
5261 return isRateLimited(this._rateLimits, category);
5262 }
5263 }
5264
5265 /** `fetch` based transport */
5266 class FetchTransport extends BaseTransport {
5267 constructor(options, fetchImpl = getNativeFetchImplementation()) {
5268 super(options);
5269 this._fetch = fetchImpl;
5270 }
5271 /**
5272 * @param sentryRequest Prepared SentryRequest to be delivered
5273 * @param originalPayload Original payload used to create SentryRequest
5274 */
5275 _sendRequest(sentryRequest, originalPayload) {
5276 // eslint-disable-next-line deprecation/deprecation
5277 if (this._isRateLimited(sentryRequest.type)) {
5278 this.recordLostEvent('ratelimit_backoff', sentryRequest.type);
5279 return Promise.reject({
5280 event: originalPayload,
5281 type: sentryRequest.type,
5282 // eslint-disable-next-line deprecation/deprecation
5283 reason: `Transport for ${sentryRequest.type} requests locked till ${this._disabledUntil(sentryRequest.type)} due to too many requests.`,
5284 status: 429,
5285 });
5286 }
5287 const options = {
5288 body: sentryRequest.body,
5289 method: 'POST',
5290 // Despite all stars in the sky saying that Edge supports old draft syntax, aka 'never', 'always', 'origin' and 'default'
5291 // (see https://caniuse.com/#feat=referrer-policy),
5292 // it doesn't. And it throws an exception instead of ignoring this parameter...
5293 // REF: https://github.com/getsentry/raven-js/issues/1233
5294 referrerPolicy: (supportsReferrerPolicy() ? 'origin' : ''),
5295 };
5296 if (this.options.fetchParameters !== undefined) {
5297 Object.assign(options, this.options.fetchParameters);
5298 }
5299 if (this.options.headers !== undefined) {
5300 options.headers = this.options.headers;
5301 }
5302 return this._buffer
5303 .add(() => new SyncPromise((resolve, reject) => {
5304 void this._fetch(sentryRequest.url, options)
5305 .then(response => {
5306 const headers = {
5307 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'),
5308 'retry-after': response.headers.get('Retry-After'),
5309 };
5310 this._handleResponse({
5311 requestType: sentryRequest.type,
5312 response,
5313 headers,
5314 resolve,
5315 reject,
5316 });
5317 })
5318 .catch(reject);
5319 }))
5320 .then(undefined, reason => {
5321 // It's either buffer rejection or any other xhr/fetch error, which are treated as NetworkError.
5322 if (reason instanceof SentryError) {
5323 this.recordLostEvent('queue_overflow', sentryRequest.type);
5324 }
5325 else {
5326 this.recordLostEvent('network_error', sentryRequest.type);
5327 }
5328 throw reason;
5329 });
5330 }
5331 }
5332
5333 /** `XHR` based transport */
5334 class XHRTransport extends BaseTransport {
5335 /**
5336 * @param sentryRequest Prepared SentryRequest to be delivered
5337 * @param originalPayload Original payload used to create SentryRequest
5338 */
5339 _sendRequest(sentryRequest, originalPayload) {
5340 // eslint-disable-next-line deprecation/deprecation
5341 if (this._isRateLimited(sentryRequest.type)) {
5342 this.recordLostEvent('ratelimit_backoff', sentryRequest.type);
5343 return Promise.reject({
5344 event: originalPayload,
5345 type: sentryRequest.type,
5346 // eslint-disable-next-line deprecation/deprecation
5347 reason: `Transport for ${sentryRequest.type} requests locked till ${this._disabledUntil(sentryRequest.type)} due to too many requests.`,
5348 status: 429,
5349 });
5350 }
5351 return this._buffer
5352 .add(() => new SyncPromise((resolve, reject) => {
5353 const request = new XMLHttpRequest();
5354 request.onreadystatechange = () => {
5355 if (request.readyState === 4) {
5356 const headers = {
5357 'x-sentry-rate-limits': request.getResponseHeader('X-Sentry-Rate-Limits'),
5358 'retry-after': request.getResponseHeader('Retry-After'),
5359 };
5360 this._handleResponse({ requestType: sentryRequest.type, response: request, headers, resolve, reject });
5361 }
5362 };
5363 request.open('POST', sentryRequest.url);
5364 for (const header in this.options.headers) {
5365 if (Object.prototype.hasOwnProperty.call(this.options.headers, header)) {
5366 request.setRequestHeader(header, this.options.headers[header]);
5367 }
5368 }
5369 request.send(sentryRequest.body);
5370 }))
5371 .then(undefined, reason => {
5372 // It's either buffer rejection or any other xhr/fetch error, which are treated as NetworkError.
5373 if (reason instanceof SentryError) {
5374 this.recordLostEvent('queue_overflow', sentryRequest.type);
5375 }
5376 else {
5377 this.recordLostEvent('network_error', sentryRequest.type);
5378 }
5379 throw reason;
5380 });
5381 }
5382 }
5383
5384 /**
5385 * Creates a Transport that uses the Fetch API to send events to Sentry.
5386 */
5387 function makeNewFetchTransport(options, nativeFetch = getNativeFetchImplementation()) {
5388 function makeRequest(request) {
5389 const requestOptions = Object.assign({ body: request.body, method: 'POST', referrerPolicy: 'origin' }, options.requestOptions);
5390 return nativeFetch(options.url, requestOptions).then(response => {
5391 return response.text().then(body => ({
5392 body,
5393 headers: {
5394 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'),
5395 'retry-after': response.headers.get('Retry-After'),
5396 },
5397 reason: response.statusText,
5398 statusCode: response.status,
5399 }));
5400 });
5401 }
5402 return createTransport({ bufferSize: options.bufferSize }, makeRequest);
5403 }
5404
5405 /**
5406 * The DONE ready state for XmlHttpRequest
5407 *
5408 * Defining it here as a constant b/c XMLHttpRequest.DONE is not always defined
5409 * (e.g. during testing, it is `undefined`)
5410 *
5411 * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/readyState}
5412 */
5413 const XHR_READYSTATE_DONE = 4;
5414 /**
5415 * Creates a Transport that uses the XMLHttpRequest API to send events to Sentry.
5416 */
5417 function makeNewXHRTransport(options) {
5418 function makeRequest(request) {
5419 return new SyncPromise((resolve, _reject) => {
5420 const xhr = new XMLHttpRequest();
5421 xhr.onreadystatechange = () => {
5422 if (xhr.readyState === XHR_READYSTATE_DONE) {
5423 const response = {
5424 body: xhr.response,
5425 headers: {
5426 'x-sentry-rate-limits': xhr.getResponseHeader('X-Sentry-Rate-Limits'),
5427 'retry-after': xhr.getResponseHeader('Retry-After'),
5428 },
5429 reason: xhr.statusText,
5430 statusCode: xhr.status,
5431 };
5432 resolve(response);
5433 }
5434 };
5435 xhr.open('POST', options.url);
5436 for (const header in options.headers) {
5437 if (Object.prototype.hasOwnProperty.call(options.headers, header)) {
5438 xhr.setRequestHeader(header, options.headers[header]);
5439 }
5440 }
5441 xhr.send(request.body);
5442 });
5443 }
5444 return createTransport({ bufferSize: options.bufferSize }, makeRequest);
5445 }
5446
5447 var index = /*#__PURE__*/Object.freeze({
5448 __proto__: null,
5449 BaseTransport: BaseTransport,
5450 FetchTransport: FetchTransport,
5451 XHRTransport: XHRTransport,
5452 makeNewFetchTransport: makeNewFetchTransport,
5453 makeNewXHRTransport: makeNewXHRTransport
5454 });
5455
5456 /**
5457 * The Sentry Browser SDK Backend.
5458 * @hidden
5459 */
5460 class BrowserBackend extends BaseBackend {
5461 /**
5462 * @inheritDoc
5463 */
5464 eventFromException(exception, hint) {
5465 return eventFromException(exception, hint, this._options.attachStacktrace);
5466 }
5467 /**
5468 * @inheritDoc
5469 */
5470 eventFromMessage(message, level = exports.Severity.Info, hint) {
5471 return eventFromMessage(message, level, hint, this._options.attachStacktrace);
5472 }
5473 /**
5474 * @inheritDoc
5475 */
5476 _setupTransport() {
5477 if (!this._options.dsn) {
5478 // We return the noop transport here in case there is no Dsn.
5479 return super._setupTransport();
5480 }
5481 const transportOptions = Object.assign(Object.assign({}, this._options.transportOptions), { dsn: this._options.dsn, tunnel: this._options.tunnel, sendClientReports: this._options.sendClientReports, _metadata: this._options._metadata });
5482 const api = initAPIDetails(transportOptions.dsn, transportOptions._metadata, transportOptions.tunnel);
5483 const url = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel);
5484 if (this._options.transport) {
5485 return new this._options.transport(transportOptions);
5486 }
5487 if (supportsFetch()) {
5488 const requestOptions = Object.assign({}, transportOptions.fetchParameters);
5489 this._newTransport = makeNewFetchTransport({ requestOptions, url });
5490 return new FetchTransport(transportOptions);
5491 }
5492 this._newTransport = makeNewXHRTransport({
5493 url,
5494 headers: transportOptions.headers,
5495 });
5496 return new XHRTransport(transportOptions);
5497 }
5498 }
5499
5500 const global$2 = getGlobalObject();
5501 let ignoreOnError = 0;
5502 /**
5503 * @hidden
5504 */
5505 function shouldIgnoreOnError() {
5506 return ignoreOnError > 0;
5507 }
5508 /**
5509 * @hidden
5510 */
5511 function ignoreNextOnError() {
5512 // onerror should trigger before setTimeout
5513 ignoreOnError += 1;
5514 setTimeout(() => {
5515 ignoreOnError -= 1;
5516 });
5517 }
5518 /**
5519 * Instruments the given function and sends an event to Sentry every time the
5520 * function throws an exception.
5521 *
5522 * @param fn A function to wrap.
5523 * @returns The wrapped function.
5524 * @hidden
5525 */
5526 function wrap$1(fn, options = {}, before) {
5527 // for future readers what this does is wrap a function and then create
5528 // a bi-directional wrapping between them.
5529 //
5530 // example: wrapped = wrap(original);
5531 // original.__sentry_wrapped__ -> wrapped
5532 // wrapped.__sentry_original__ -> original
5533 if (typeof fn !== 'function') {
5534 return fn;
5535 }
5536 try {
5537 // if we're dealing with a function that was previously wrapped, return
5538 // the original wrapper.
5539 const wrapper = fn.__sentry_wrapped__;
5540 if (wrapper) {
5541 return wrapper;
5542 }
5543 // We don't wanna wrap it twice
5544 if (getOriginalFunction(fn)) {
5545 return fn;
5546 }
5547 }
5548 catch (e) {
5549 // Just accessing custom props in some Selenium environments
5550 // can cause a "Permission denied" exception (see raven-js#495).
5551 // Bail on wrapping and return the function as-is (defers to window.onerror).
5552 return fn;
5553 }
5554 /* eslint-disable prefer-rest-params */
5555 // eslint-disable-next-line @typescript-eslint/no-explicit-any
5556 const sentryWrapped = function () {
5557 const args = Array.prototype.slice.call(arguments);
5558 try {
5559 if (before && typeof before === 'function') {
5560 before.apply(this, arguments);
5561 }
5562 // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
5563 const wrappedArguments = args.map((arg) => wrap$1(arg, options));
5564 // Attempt to invoke user-land function
5565 // NOTE: If you are a Sentry user, and you are seeing this stack frame, it
5566 // means the sentry.javascript SDK caught an error invoking your application code. This
5567 // is expected behavior and NOT indicative of a bug with sentry.javascript.
5568 return fn.apply(this, wrappedArguments);
5569 }
5570 catch (ex) {
5571 ignoreNextOnError();
5572 withScope((scope) => {
5573 scope.addEventProcessor((event) => {
5574 if (options.mechanism) {
5575 addExceptionTypeValue(event, undefined, undefined);
5576 addExceptionMechanism(event, options.mechanism);
5577 }
5578 event.extra = Object.assign(Object.assign({}, event.extra), { arguments: args });
5579 return event;
5580 });
5581 captureException(ex);
5582 });
5583 throw ex;
5584 }
5585 };
5586 /* eslint-enable prefer-rest-params */
5587 // Accessing some objects may throw
5588 // ref: https://github.com/getsentry/sentry-javascript/issues/1168
5589 try {
5590 for (const property in fn) {
5591 if (Object.prototype.hasOwnProperty.call(fn, property)) {
5592 sentryWrapped[property] = fn[property];
5593 }
5594 }
5595 }
5596 catch (_oO) { } // eslint-disable-line no-empty
5597 // Signal that this function has been wrapped/filled already
5598 // for both debugging and to prevent it to being wrapped/filled twice
5599 markFunctionWrapped(sentryWrapped, fn);
5600 addNonEnumerableProperty(fn, '__sentry_wrapped__', sentryWrapped);
5601 // Restore original function name (not all browsers allow that)
5602 try {
5603 const descriptor = Object.getOwnPropertyDescriptor(sentryWrapped, 'name');
5604 if (descriptor.configurable) {
5605 Object.defineProperty(sentryWrapped, 'name', {
5606 get() {
5607 return fn.name;
5608 },
5609 });
5610 }
5611 // eslint-disable-next-line no-empty
5612 }
5613 catch (_oO) { }
5614 return sentryWrapped;
5615 }
5616 /**
5617 * Injects the Report Dialog script
5618 * @hidden
5619 */
5620 function injectReportDialog(options = {}) {
5621 if (!global$2.document) {
5622 return;
5623 }
5624 if (!options.eventId) {
5625 IS_DEBUG_BUILD && logger.error('Missing eventId option in showReportDialog call');
5626 return;
5627 }
5628 if (!options.dsn) {
5629 IS_DEBUG_BUILD && logger.error('Missing dsn option in showReportDialog call');
5630 return;
5631 }
5632 const script = global$2.document.createElement('script');
5633 script.async = true;
5634 script.src = getReportDialogEndpoint(options.dsn, options);
5635 if (options.onLoad) {
5636 // eslint-disable-next-line @typescript-eslint/unbound-method
5637 script.onload = options.onLoad;
5638 }
5639 const injectionPoint = global$2.document.head || global$2.document.body;
5640 if (injectionPoint) {
5641 injectionPoint.appendChild(script);
5642 }
5643 }
5644
5645 /* eslint-disable @typescript-eslint/no-unsafe-member-access */
5646 /** Global handlers */
5647 class GlobalHandlers {
5648 /** JSDoc */
5649 constructor(options) {
5650 /**
5651 * @inheritDoc
5652 */
5653 this.name = GlobalHandlers.id;
5654 /**
5655 * Stores references functions to installing handlers. Will set to undefined
5656 * after they have been run so that they are not used twice.
5657 */
5658 this._installFunc = {
5659 onerror: _installGlobalOnErrorHandler,
5660 onunhandledrejection: _installGlobalOnUnhandledRejectionHandler,
5661 };
5662 this._options = Object.assign({ onerror: true, onunhandledrejection: true }, options);
5663 }
5664 /**
5665 * @inheritDoc
5666 */
5667 setupOnce() {
5668 Error.stackTraceLimit = 50;
5669 const options = this._options;
5670 // We can disable guard-for-in as we construct the options object above + do checks against
5671 // `this._installFunc` for the property.
5672 // eslint-disable-next-line guard-for-in
5673 for (const key in options) {
5674 const installFunc = this._installFunc[key];
5675 if (installFunc && options[key]) {
5676 globalHandlerLog(key);
5677 installFunc();
5678 this._installFunc[key] = undefined;
5679 }
5680 }
5681 }
5682 }
5683 /**
5684 * @inheritDoc
5685 */
5686 GlobalHandlers.id = 'GlobalHandlers';
5687 /** JSDoc */
5688 function _installGlobalOnErrorHandler() {
5689 addInstrumentationHandler('error',
5690 // eslint-disable-next-line @typescript-eslint/no-explicit-any
5691 (data) => {
5692 const [hub, attachStacktrace] = getHubAndAttachStacktrace();
5693 if (!hub.getIntegration(GlobalHandlers)) {
5694 return;
5695 }
5696 const { msg, url, line, column, error } = data;
5697 if (shouldIgnoreOnError() || (error && error.__sentry_own_request__)) {
5698 return;
5699 }
5700 const event = error === undefined && isString(msg)
5701 ? _eventFromIncompleteOnError(msg, url, line, column)
5702 : _enhanceEventWithInitialFrame(eventFromUnknownInput(error || msg, undefined, attachStacktrace, false), url, line, column);
5703 event.level = exports.Severity.Error;
5704 addMechanismAndCapture(hub, error, event, 'onerror');
5705 });
5706 }
5707 /** JSDoc */
5708 function _installGlobalOnUnhandledRejectionHandler() {
5709 addInstrumentationHandler('unhandledrejection',
5710 // eslint-disable-next-line @typescript-eslint/no-explicit-any
5711 (e) => {
5712 const [hub, attachStacktrace] = getHubAndAttachStacktrace();
5713 if (!hub.getIntegration(GlobalHandlers)) {
5714 return;
5715 }
5716 let error = e;
5717 // dig the object of the rejection out of known event types
5718 try {
5719 // PromiseRejectionEvents store the object of the rejection under 'reason'
5720 // see https://developer.mozilla.org/en-US/docs/Web/API/PromiseRejectionEvent
5721 if ('reason' in e) {
5722 error = e.reason;
5723 }
5724 // something, somewhere, (likely a browser extension) effectively casts PromiseRejectionEvents
5725 // to CustomEvents, moving the `promise` and `reason` attributes of the PRE into
5726 // the CustomEvent's `detail` attribute, since they're not part of CustomEvent's spec
5727 // see https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent and
5728 // https://github.com/getsentry/sentry-javascript/issues/2380
5729 else if ('detail' in e && 'reason' in e.detail) {
5730 error = e.detail.reason;
5731 }
5732 }
5733 catch (_oO) {
5734 // no-empty
5735 }
5736 if (shouldIgnoreOnError() || (error && error.__sentry_own_request__)) {
5737 return true;
5738 }
5739 const event = isPrimitive(error)
5740 ? _eventFromRejectionWithPrimitive(error)
5741 : eventFromUnknownInput(error, undefined, attachStacktrace, true);
5742 event.level = exports.Severity.Error;
5743 addMechanismAndCapture(hub, error, event, 'onunhandledrejection');
5744 return;
5745 });
5746 }
5747 /**
5748 * Create an event from a promise rejection where the `reason` is a primitive.
5749 *
5750 * @param reason: The `reason` property of the promise rejection
5751 * @returns An Event object with an appropriate `exception` value
5752 */
5753 function _eventFromRejectionWithPrimitive(reason) {
5754 return {
5755 exception: {
5756 values: [
5757 {
5758 type: 'UnhandledRejection',
5759 // String() is needed because the Primitive type includes symbols (which can't be automatically stringified)
5760 value: `Non-Error promise rejection captured with value: ${String(reason)}`,
5761 },
5762 ],
5763 },
5764 };
5765 }
5766 /**
5767 * This function creates a stack from an old, error-less onerror handler.
5768 */
5769 // eslint-disable-next-line @typescript-eslint/no-explicit-any
5770 function _eventFromIncompleteOnError(msg, url, line, column) {
5771 const ERROR_TYPES_RE = /^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?(.*)$/i;
5772 // If 'message' is ErrorEvent, get real message from inside
5773 let message = isErrorEvent(msg) ? msg.message : msg;
5774 let name = 'Error';
5775 const groups = message.match(ERROR_TYPES_RE);
5776 if (groups) {
5777 name = groups[1];
5778 message = groups[2];
5779 }
5780 const event = {
5781 exception: {
5782 values: [
5783 {
5784 type: name,
5785 value: message,
5786 },
5787 ],
5788 },
5789 };
5790 return _enhanceEventWithInitialFrame(event, url, line, column);
5791 }
5792 /** JSDoc */
5793 // eslint-disable-next-line @typescript-eslint/no-explicit-any
5794 function _enhanceEventWithInitialFrame(event, url, line, column) {
5795 // event.exception
5796 const e = (event.exception = event.exception || {});
5797 // event.exception.values
5798 const ev = (e.values = e.values || []);
5799 // event.exception.values[0]
5800 const ev0 = (ev[0] = ev[0] || {});
5801 // event.exception.values[0].stacktrace
5802 const ev0s = (ev0.stacktrace = ev0.stacktrace || {});
5803 // event.exception.values[0].stacktrace.frames
5804 const ev0sf = (ev0s.frames = ev0s.frames || []);
5805 const colno = isNaN(parseInt(column, 10)) ? undefined : column;
5806 const lineno = isNaN(parseInt(line, 10)) ? undefined : line;
5807 const filename = isString(url) && url.length > 0 ? url : getLocationHref();
5808 // event.exception.values[0].stacktrace.frames
5809 if (ev0sf.length === 0) {
5810 ev0sf.push({
5811 colno,
5812 filename,
5813 function: '?',
5814 in_app: true,
5815 lineno,
5816 });
5817 }
5818 return event;
5819 }
5820 function globalHandlerLog(type) {
5821 IS_DEBUG_BUILD && logger.log(`Global Handler attached: ${type}`);
5822 }
5823 function addMechanismAndCapture(hub, error, event, type) {
5824 addExceptionMechanism(event, {
5825 handled: false,
5826 type,
5827 });
5828 hub.captureEvent(event, {
5829 originalException: error,
5830 });
5831 }
5832 function getHubAndAttachStacktrace() {
5833 const hub = getCurrentHub();
5834 const client = hub.getClient();
5835 const attachStacktrace = client && client.getOptions().attachStacktrace;
5836 return [hub, attachStacktrace];
5837 }
5838
5839 const DEFAULT_EVENT_TARGET = [
5840 'EventTarget',
5841 'Window',
5842 'Node',
5843 'ApplicationCache',
5844 'AudioTrackList',
5845 'ChannelMergerNode',
5846 'CryptoOperation',
5847 'EventSource',
5848 'FileReader',
5849 'HTMLUnknownElement',
5850 'IDBDatabase',
5851 'IDBRequest',
5852 'IDBTransaction',
5853 'KeyOperation',
5854 'MediaController',
5855 'MessagePort',
5856 'ModalWindow',
5857 'Notification',
5858 'SVGElementInstance',
5859 'Screen',
5860 'TextTrack',
5861 'TextTrackCue',
5862 'TextTrackList',
5863 'WebSocket',
5864 'WebSocketWorker',
5865 'Worker',
5866 'XMLHttpRequest',
5867 'XMLHttpRequestEventTarget',
5868 'XMLHttpRequestUpload',
5869 ];
5870 /** Wrap timer functions and event targets to catch errors and provide better meta data */
5871 class TryCatch {
5872 /**
5873 * @inheritDoc
5874 */
5875 constructor(options) {
5876 /**
5877 * @inheritDoc
5878 */
5879 this.name = TryCatch.id;
5880 this._options = Object.assign({ XMLHttpRequest: true, eventTarget: true, requestAnimationFrame: true, setInterval: true, setTimeout: true }, options);
5881 }
5882 /**
5883 * Wrap timer functions and event targets to catch errors
5884 * and provide better metadata.
5885 */
5886 setupOnce() {
5887 const global = getGlobalObject();
5888 if (this._options.setTimeout) {
5889 fill(global, 'setTimeout', _wrapTimeFunction);
5890 }
5891 if (this._options.setInterval) {
5892 fill(global, 'setInterval', _wrapTimeFunction);
5893 }
5894 if (this._options.requestAnimationFrame) {
5895 fill(global, 'requestAnimationFrame', _wrapRAF);
5896 }
5897 if (this._options.XMLHttpRequest && 'XMLHttpRequest' in global) {
5898 fill(XMLHttpRequest.prototype, 'send', _wrapXHR);
5899 }
5900 const eventTargetOption = this._options.eventTarget;
5901 if (eventTargetOption) {
5902 const eventTarget = Array.isArray(eventTargetOption) ? eventTargetOption : DEFAULT_EVENT_TARGET;
5903 eventTarget.forEach(_wrapEventTarget);
5904 }
5905 }
5906 }
5907 /**
5908 * @inheritDoc
5909 */
5910 TryCatch.id = 'TryCatch';
5911 /** JSDoc */
5912 function _wrapTimeFunction(original) {
5913 // eslint-disable-next-line @typescript-eslint/no-explicit-any
5914 return function (...args) {
5915 const originalCallback = args[0];
5916 args[0] = wrap$1(originalCallback, {
5917 mechanism: {
5918 data: { function: getFunctionName(original) },
5919 handled: true,
5920 type: 'instrument',
5921 },
5922 });
5923 return original.apply(this, args);
5924 };
5925 }
5926 /** JSDoc */
5927 // eslint-disable-next-line @typescript-eslint/no-explicit-any
5928 function _wrapRAF(original) {
5929 // eslint-disable-next-line @typescript-eslint/no-explicit-any
5930 return function (callback) {
5931 // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
5932 return original.apply(this, [
5933 wrap$1(callback, {
5934 mechanism: {
5935 data: {
5936 function: 'requestAnimationFrame',
5937 handler: getFunctionName(original),
5938 },
5939 handled: true,
5940 type: 'instrument',
5941 },
5942 }),
5943 ]);
5944 };
5945 }
5946 /** JSDoc */
5947 function _wrapXHR(originalSend) {
5948 // eslint-disable-next-line @typescript-eslint/no-explicit-any
5949 return function (...args) {
5950 // eslint-disable-next-line @typescript-eslint/no-this-alias
5951 const xhr = this;
5952 const xmlHttpRequestProps = ['onload', 'onerror', 'onprogress', 'onreadystatechange'];
5953 xmlHttpRequestProps.forEach(prop => {
5954 if (prop in xhr && typeof xhr[prop] === 'function') {
5955 // eslint-disable-next-line @typescript-eslint/no-explicit-any
5956 fill(xhr, prop, function (original) {
5957 const wrapOptions = {
5958 mechanism: {
5959 data: {
5960 function: prop,
5961 handler: getFunctionName(original),
5962 },
5963 handled: true,
5964 type: 'instrument',
5965 },
5966 };
5967 // If Instrument integration has been called before TryCatch, get the name of original function
5968 const originalFunction = getOriginalFunction(original);
5969 if (originalFunction) {
5970 wrapOptions.mechanism.data.handler = getFunctionName(originalFunction);
5971 }
5972 // Otherwise wrap directly
5973 return wrap$1(original, wrapOptions);
5974 });
5975 }
5976 });
5977 return originalSend.apply(this, args);
5978 };
5979 }
5980 /** JSDoc */
5981 function _wrapEventTarget(target) {
5982 // eslint-disable-next-line @typescript-eslint/no-explicit-any
5983 const global = getGlobalObject();
5984 // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
5985 const proto = global[target] && global[target].prototype;
5986 // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, no-prototype-builtins
5987 if (!proto || !proto.hasOwnProperty || !proto.hasOwnProperty('addEventListener')) {
5988 return;
5989 }
5990 fill(proto, 'addEventListener', function (original) {
5991 return function (eventName, fn, options) {
5992 try {
5993 if (typeof fn.handleEvent === 'function') {
5994 fn.handleEvent = wrap$1(fn.handleEvent.bind(fn), {
5995 mechanism: {
5996 data: {
5997 function: 'handleEvent',
5998 handler: getFunctionName(fn),
5999 target,
6000 },
6001 handled: true,
6002 type: 'instrument',
6003 },
6004 });
6005 }
6006 }
6007 catch (err) {
6008 // can sometimes get 'Permission denied to access property "handle Event'
6009 }
6010 return original.apply(this, [
6011 eventName,
6012 // eslint-disable-next-line @typescript-eslint/no-explicit-any
6013 wrap$1(fn, {
6014 mechanism: {
6015 data: {
6016 function: 'addEventListener',
6017 handler: getFunctionName(fn),
6018 target,
6019 },
6020 handled: true,
6021 type: 'instrument',
6022 },
6023 }),
6024 options,
6025 ]);
6026 };
6027 });
6028 fill(proto, 'removeEventListener', function (originalRemoveEventListener) {
6029 return function (eventName, fn, options) {
6030 /**
6031 * There are 2 possible scenarios here:
6032 *
6033 * 1. Someone passes a callback, which was attached prior to Sentry initialization, or by using unmodified
6034 * method, eg. `document.addEventListener.call(el, name, handler). In this case, we treat this function
6035 * as a pass-through, and call original `removeEventListener` with it.
6036 *
6037 * 2. Someone passes a callback, which was attached after Sentry was initialized, which means that it was using
6038 * our wrapped version of `addEventListener`, which internally calls `wrap` helper.
6039 * This helper "wraps" whole callback inside a try/catch statement, and attached appropriate metadata to it,
6040 * in order for us to make a distinction between wrapped/non-wrapped functions possible.
6041 * If a function was wrapped, it has additional property of `__sentry_wrapped__`, holding the handler.
6042 *
6043 * When someone adds a handler prior to initialization, and then do it again, but after,
6044 * then we have to detach both of them. Otherwise, if we'd detach only wrapped one, it'd be impossible
6045 * to get rid of the initial handler and it'd stick there forever.
6046 */
6047 const wrappedEventHandler = fn;
6048 try {
6049 const originalEventHandler = wrappedEventHandler && wrappedEventHandler.__sentry_wrapped__;
6050 if (originalEventHandler) {
6051 originalRemoveEventListener.call(this, eventName, originalEventHandler, options);
6052 }
6053 }
6054 catch (e) {
6055 // ignore, accessing __sentry_wrapped__ will throw in some Selenium environments
6056 }
6057 return originalRemoveEventListener.call(this, eventName, wrappedEventHandler, options);
6058 };
6059 });
6060 }
6061
6062 /* eslint-disable @typescript-eslint/no-unsafe-member-access */
6063 /**
6064 * Default Breadcrumbs instrumentations
6065 * TODO: Deprecated - with v6, this will be renamed to `Instrument`
6066 */
6067 class Breadcrumbs {
6068 /**
6069 * @inheritDoc
6070 */
6071 constructor(options) {
6072 /**
6073 * @inheritDoc
6074 */
6075 this.name = Breadcrumbs.id;
6076 this._options = Object.assign({ console: true, dom: true, fetch: true, history: true, sentry: true, xhr: true }, options);
6077 }
6078 /**
6079 * Create a breadcrumb of `sentry` from the events themselves
6080 */
6081 addSentryBreadcrumb(event) {
6082 if (!this._options.sentry) {
6083 return;
6084 }
6085 getCurrentHub().addBreadcrumb({
6086 category: `sentry.${event.type === 'transaction' ? 'transaction' : 'event'}`,
6087 event_id: event.event_id,
6088 level: event.level,
6089 message: getEventDescription(event),
6090 }, {
6091 event,
6092 });
6093 }
6094 /**
6095 * Instrument browser built-ins w/ breadcrumb capturing
6096 * - Console API
6097 * - DOM API (click/typing)
6098 * - XMLHttpRequest API
6099 * - Fetch API
6100 * - History API
6101 */
6102 setupOnce() {
6103 if (this._options.console) {
6104 addInstrumentationHandler('console', _consoleBreadcrumb);
6105 }
6106 if (this._options.dom) {
6107 addInstrumentationHandler('dom', _domBreadcrumb(this._options.dom));
6108 }
6109 if (this._options.xhr) {
6110 addInstrumentationHandler('xhr', _xhrBreadcrumb);
6111 }
6112 if (this._options.fetch) {
6113 addInstrumentationHandler('fetch', _fetchBreadcrumb);
6114 }
6115 if (this._options.history) {
6116 addInstrumentationHandler('history', _historyBreadcrumb);
6117 }
6118 }
6119 }
6120 /**
6121 * @inheritDoc
6122 */
6123 Breadcrumbs.id = 'Breadcrumbs';
6124 /**
6125 * A HOC that creaes a function that creates breadcrumbs from DOM API calls.
6126 * This is a HOC so that we get access to dom options in the closure.
6127 */
6128 // eslint-disable-next-line @typescript-eslint/no-explicit-any
6129 function _domBreadcrumb(dom) {
6130 // eslint-disable-next-line @typescript-eslint/no-explicit-any
6131 function _innerDomBreadcrumb(handlerData) {
6132 let target;
6133 let keyAttrs = typeof dom === 'object' ? dom.serializeAttribute : undefined;
6134 if (typeof keyAttrs === 'string') {
6135 keyAttrs = [keyAttrs];
6136 }
6137 // Accessing event.target can throw (see getsentry/raven-js#838, #768)
6138 try {
6139 target = handlerData.event.target
6140 ? htmlTreeAsString(handlerData.event.target, keyAttrs)
6141 : htmlTreeAsString(handlerData.event, keyAttrs);
6142 }
6143 catch (e) {
6144 target = '<unknown>';
6145 }
6146 if (target.length === 0) {
6147 return;
6148 }
6149 getCurrentHub().addBreadcrumb({
6150 category: `ui.${handlerData.name}`,
6151 message: target,
6152 }, {
6153 event: handlerData.event,
6154 name: handlerData.name,
6155 global: handlerData.global,
6156 });
6157 }
6158 return _innerDomBreadcrumb;
6159 }
6160 /**
6161 * Creates breadcrumbs from console API calls
6162 */
6163 // eslint-disable-next-line @typescript-eslint/no-explicit-any
6164 function _consoleBreadcrumb(handlerData) {
6165 const breadcrumb = {
6166 category: 'console',
6167 data: {
6168 arguments: handlerData.args,
6169 logger: 'console',
6170 },
6171 level: severityFromString(handlerData.level),
6172 message: safeJoin(handlerData.args, ' '),
6173 };
6174 if (handlerData.level === 'assert') {
6175 if (handlerData.args[0] === false) {
6176 breadcrumb.message = `Assertion failed: ${safeJoin(handlerData.args.slice(1), ' ') || 'console.assert'}`;
6177 breadcrumb.data.arguments = handlerData.args.slice(1);
6178 }
6179 else {
6180 // Don't capture a breadcrumb for passed assertions
6181 return;
6182 }
6183 }
6184 getCurrentHub().addBreadcrumb(breadcrumb, {
6185 input: handlerData.args,
6186 level: handlerData.level,
6187 });
6188 }
6189 /**
6190 * Creates breadcrumbs from XHR API calls
6191 */
6192 // eslint-disable-next-line @typescript-eslint/no-explicit-any
6193 function _xhrBreadcrumb(handlerData) {
6194 if (handlerData.endTimestamp) {
6195 // We only capture complete, non-sentry requests
6196 if (handlerData.xhr.__sentry_own_request__) {
6197 return;
6198 }
6199 const { method, url, status_code, body } = handlerData.xhr.__sentry_xhr__ || {};
6200 getCurrentHub().addBreadcrumb({
6201 category: 'xhr',
6202 data: {
6203 method,
6204 url,
6205 status_code,
6206 },
6207 type: 'http',
6208 }, {
6209 xhr: handlerData.xhr,
6210 input: body,
6211 });
6212 return;
6213 }
6214 }
6215 /**
6216 * Creates breadcrumbs from fetch API calls
6217 */
6218 // eslint-disable-next-line @typescript-eslint/no-explicit-any
6219 function _fetchBreadcrumb(handlerData) {
6220 // We only capture complete fetch requests
6221 if (!handlerData.endTimestamp) {
6222 return;
6223 }
6224 if (handlerData.fetchData.url.match(/sentry_key/) && handlerData.fetchData.method === 'POST') {
6225 // We will not create breadcrumbs for fetch requests that contain `sentry_key` (internal sentry requests)
6226 return;
6227 }
6228 if (handlerData.error) {
6229 getCurrentHub().addBreadcrumb({
6230 category: 'fetch',
6231 data: handlerData.fetchData,
6232 level: exports.Severity.Error,
6233 type: 'http',
6234 }, {
6235 data: handlerData.error,
6236 input: handlerData.args,
6237 });
6238 }
6239 else {
6240 getCurrentHub().addBreadcrumb({
6241 category: 'fetch',
6242 data: Object.assign(Object.assign({}, handlerData.fetchData), { status_code: handlerData.response.status }),
6243 type: 'http',
6244 }, {
6245 input: handlerData.args,
6246 response: handlerData.response,
6247 });
6248 }
6249 }
6250 /**
6251 * Creates breadcrumbs from history API calls
6252 */
6253 // eslint-disable-next-line @typescript-eslint/no-explicit-any
6254 function _historyBreadcrumb(handlerData) {
6255 const global = getGlobalObject();
6256 let from = handlerData.from;
6257 let to = handlerData.to;
6258 const parsedLoc = parseUrl(global.location.href);
6259 let parsedFrom = parseUrl(from);
6260 const parsedTo = parseUrl(to);
6261 // Initial pushState doesn't provide `from` information
6262 if (!parsedFrom.path) {
6263 parsedFrom = parsedLoc;
6264 }
6265 // Use only the path component of the URL if the URL matches the current
6266 // document (almost all the time when using pushState)
6267 if (parsedLoc.protocol === parsedTo.protocol && parsedLoc.host === parsedTo.host) {
6268 to = parsedTo.relative;
6269 }
6270 if (parsedLoc.protocol === parsedFrom.protocol && parsedLoc.host === parsedFrom.host) {
6271 from = parsedFrom.relative;
6272 }
6273 getCurrentHub().addBreadcrumb({
6274 category: 'navigation',
6275 data: {
6276 from,
6277 to,
6278 },
6279 });
6280 }
6281
6282 const DEFAULT_KEY = 'cause';
6283 const DEFAULT_LIMIT = 5;
6284 /** Adds SDK info to an event. */
6285 class LinkedErrors {
6286 /**
6287 * @inheritDoc
6288 */
6289 constructor(options = {}) {
6290 /**
6291 * @inheritDoc
6292 */
6293 this.name = LinkedErrors.id;
6294 this._key = options.key || DEFAULT_KEY;
6295 this._limit = options.limit || DEFAULT_LIMIT;
6296 }
6297 /**
6298 * @inheritDoc
6299 */
6300 setupOnce() {
6301 addGlobalEventProcessor((event, hint) => {
6302 const self = getCurrentHub().getIntegration(LinkedErrors);
6303 return self ? _handler(self._key, self._limit, event, hint) : event;
6304 });
6305 }
6306 }
6307 /**
6308 * @inheritDoc
6309 */
6310 LinkedErrors.id = 'LinkedErrors';
6311 /**
6312 * @inheritDoc
6313 */
6314 function _handler(key, limit, event, hint) {
6315 if (!event.exception || !event.exception.values || !hint || !isInstanceOf(hint.originalException, Error)) {
6316 return event;
6317 }
6318 const linkedErrors = _walkErrorTree(limit, hint.originalException, key);
6319 event.exception.values = [...linkedErrors, ...event.exception.values];
6320 return event;
6321 }
6322 /**
6323 * JSDOC
6324 */
6325 function _walkErrorTree(limit, error, key, stack = []) {
6326 if (!isInstanceOf(error[key], Error) || stack.length + 1 >= limit) {
6327 return stack;
6328 }
6329 const exception = exceptionFromError(error[key]);
6330 return _walkErrorTree(limit, error[key], key, [exception, ...stack]);
6331 }
6332
6333 const global$1 = getGlobalObject();
6334 /** UserAgent */
6335 class UserAgent {
6336 constructor() {
6337 /**
6338 * @inheritDoc
6339 */
6340 this.name = UserAgent.id;
6341 }
6342 /**
6343 * @inheritDoc
6344 */
6345 setupOnce() {
6346 addGlobalEventProcessor((event) => {
6347 if (getCurrentHub().getIntegration(UserAgent)) {
6348 // if none of the information we want exists, don't bother
6349 if (!global$1.navigator && !global$1.location && !global$1.document) {
6350 return event;
6351 }
6352 // grab as much info as exists and add it to the event
6353 const url = (event.request && event.request.url) || (global$1.location && global$1.location.href);
6354 const { referrer } = global$1.document || {};
6355 const { userAgent } = global$1.navigator || {};
6356 const headers = Object.assign(Object.assign(Object.assign({}, (event.request && event.request.headers)), (referrer && { Referer: referrer })), (userAgent && { 'User-Agent': userAgent }));
6357 const request = Object.assign(Object.assign({}, (url && { url })), { headers });
6358 return Object.assign(Object.assign({}, event), { request });
6359 }
6360 return event;
6361 });
6362 }
6363 }
6364 /**
6365 * @inheritDoc
6366 */
6367 UserAgent.id = 'UserAgent';
6368
6369 /** Deduplication filter */
6370 class Dedupe {
6371 constructor() {
6372 /**
6373 * @inheritDoc
6374 */
6375 this.name = Dedupe.id;
6376 }
6377 /**
6378 * @inheritDoc
6379 */
6380 setupOnce(addGlobalEventProcessor, getCurrentHub) {
6381 addGlobalEventProcessor((currentEvent) => {
6382 const self = getCurrentHub().getIntegration(Dedupe);
6383 if (self) {
6384 // Juuust in case something goes wrong
6385 try {
6386 if (_shouldDropEvent(currentEvent, self._previousEvent)) {
6387 IS_DEBUG_BUILD && logger.warn('Event dropped due to being a duplicate of previously captured event.');
6388 return null;
6389 }
6390 }
6391 catch (_oO) {
6392 return (self._previousEvent = currentEvent);
6393 }
6394 return (self._previousEvent = currentEvent);
6395 }
6396 return currentEvent;
6397 });
6398 }
6399 }
6400 /**
6401 * @inheritDoc
6402 */
6403 Dedupe.id = 'Dedupe';
6404 /** JSDoc */
6405 function _shouldDropEvent(currentEvent, previousEvent) {
6406 if (!previousEvent) {
6407 return false;
6408 }
6409 if (_isSameMessageEvent(currentEvent, previousEvent)) {
6410 return true;
6411 }
6412 if (_isSameExceptionEvent(currentEvent, previousEvent)) {
6413 return true;
6414 }
6415 return false;
6416 }
6417 /** JSDoc */
6418 function _isSameMessageEvent(currentEvent, previousEvent) {
6419 const currentMessage = currentEvent.message;
6420 const previousMessage = previousEvent.message;
6421 // If neither event has a message property, they were both exceptions, so bail out
6422 if (!currentMessage && !previousMessage) {
6423 return false;
6424 }
6425 // If only one event has a stacktrace, but not the other one, they are not the same
6426 if ((currentMessage && !previousMessage) || (!currentMessage && previousMessage)) {
6427 return false;
6428 }
6429 if (currentMessage !== previousMessage) {
6430 return false;
6431 }
6432 if (!_isSameFingerprint(currentEvent, previousEvent)) {
6433 return false;
6434 }
6435 if (!_isSameStacktrace(currentEvent, previousEvent)) {
6436 return false;
6437 }
6438 return true;
6439 }
6440 /** JSDoc */
6441 function _isSameExceptionEvent(currentEvent, previousEvent) {
6442 const previousException = _getExceptionFromEvent(previousEvent);
6443 const currentException = _getExceptionFromEvent(currentEvent);
6444 if (!previousException || !currentException) {
6445 return false;
6446 }
6447 if (previousException.type !== currentException.type || previousException.value !== currentException.value) {
6448 return false;
6449 }
6450 if (!_isSameFingerprint(currentEvent, previousEvent)) {
6451 return false;
6452 }
6453 if (!_isSameStacktrace(currentEvent, previousEvent)) {
6454 return false;
6455 }
6456 return true;
6457 }
6458 /** JSDoc */
6459 function _isSameStacktrace(currentEvent, previousEvent) {
6460 let currentFrames = _getFramesFromEvent(currentEvent);
6461 let previousFrames = _getFramesFromEvent(previousEvent);
6462 // If neither event has a stacktrace, they are assumed to be the same
6463 if (!currentFrames && !previousFrames) {
6464 return true;
6465 }
6466 // If only one event has a stacktrace, but not the other one, they are not the same
6467 if ((currentFrames && !previousFrames) || (!currentFrames && previousFrames)) {
6468 return false;
6469 }
6470 currentFrames = currentFrames;
6471 previousFrames = previousFrames;
6472 // If number of frames differ, they are not the same
6473 if (previousFrames.length !== currentFrames.length) {
6474 return false;
6475 }
6476 // Otherwise, compare the two
6477 for (let i = 0; i < previousFrames.length; i++) {
6478 const frameA = previousFrames[i];
6479 const frameB = currentFrames[i];
6480 if (frameA.filename !== frameB.filename ||
6481 frameA.lineno !== frameB.lineno ||
6482 frameA.colno !== frameB.colno ||
6483 frameA.function !== frameB.function) {
6484 return false;
6485 }
6486 }
6487 return true;
6488 }
6489 /** JSDoc */
6490 function _isSameFingerprint(currentEvent, previousEvent) {
6491 let currentFingerprint = currentEvent.fingerprint;
6492 let previousFingerprint = previousEvent.fingerprint;
6493 // If neither event has a fingerprint, they are assumed to be the same
6494 if (!currentFingerprint && !previousFingerprint) {
6495 return true;
6496 }
6497 // If only one event has a fingerprint, but not the other one, they are not the same
6498 if ((currentFingerprint && !previousFingerprint) || (!currentFingerprint && previousFingerprint)) {
6499 return false;
6500 }
6501 currentFingerprint = currentFingerprint;
6502 previousFingerprint = previousFingerprint;
6503 // Otherwise, compare the two
6504 try {
6505 return !!(currentFingerprint.join('') === previousFingerprint.join(''));
6506 }
6507 catch (_oO) {
6508 return false;
6509 }
6510 }
6511 /** JSDoc */
6512 function _getExceptionFromEvent(event) {
6513 return event.exception && event.exception.values && event.exception.values[0];
6514 }
6515 /** JSDoc */
6516 function _getFramesFromEvent(event) {
6517 const exception = event.exception;
6518 if (exception) {
6519 try {
6520 // @ts-ignore Object could be undefined
6521 return exception.values[0].stacktrace.frames;
6522 }
6523 catch (_oO) {
6524 return undefined;
6525 }
6526 }
6527 else if (event.stacktrace) {
6528 return event.stacktrace.frames;
6529 }
6530 return undefined;
6531 }
6532
6533 var BrowserIntegrations = /*#__PURE__*/Object.freeze({
6534 __proto__: null,
6535 GlobalHandlers: GlobalHandlers,
6536 TryCatch: TryCatch,
6537 Breadcrumbs: Breadcrumbs,
6538 LinkedErrors: LinkedErrors,
6539 UserAgent: UserAgent,
6540 Dedupe: Dedupe
6541 });
6542
6543 /**
6544 * The Sentry Browser SDK Client.
6545 *
6546 * @see BrowserOptions for documentation on configuration options.
6547 * @see SentryClient for usage documentation.
6548 */
6549 class BrowserClient extends BaseClient {
6550 /**
6551 * Creates a new Browser SDK instance.
6552 *
6553 * @param options Configuration options for this SDK.
6554 */
6555 constructor(options = {}) {
6556 options._metadata = options._metadata || {};
6557 options._metadata.sdk = options._metadata.sdk || {
6558 name: 'sentry.javascript.browser',
6559 packages: [
6560 {
6561 name: 'npm:@sentry/browser',
6562 version: SDK_VERSION,
6563 },
6564 ],
6565 version: SDK_VERSION,
6566 };
6567 super(BrowserBackend, options);
6568 }
6569 /**
6570 * Show a report dialog to the user to send feedback to a specific event.
6571 *
6572 * @param options Set individual options for the dialog
6573 */
6574 showReportDialog(options = {}) {
6575 // doesn't work without a document (React Native)
6576 const document = getGlobalObject().document;
6577 if (!document) {
6578 return;
6579 }
6580 if (!this._isEnabled()) {
6581 IS_DEBUG_BUILD && logger.error('Trying to call showReportDialog with Sentry Client disabled');
6582 return;
6583 }
6584 injectReportDialog(Object.assign(Object.assign({}, options), { dsn: options.dsn || this.getDsn() }));
6585 }
6586 /**
6587 * @inheritDoc
6588 */
6589 _prepareEvent(event, scope, hint) {
6590 event.platform = event.platform || 'javascript';
6591 return super._prepareEvent(event, scope, hint);
6592 }
6593 /**
6594 * @inheritDoc
6595 */
6596 _sendEvent(event) {
6597 const integration = this.getIntegration(Breadcrumbs);
6598 if (integration) {
6599 integration.addSentryBreadcrumb(event);
6600 }
6601 super._sendEvent(event);
6602 }
6603 }
6604
6605 const defaultIntegrations = [
6606 new InboundFilters(),
6607 new FunctionToString(),
6608 new TryCatch(),
6609 new Breadcrumbs(),
6610 new GlobalHandlers(),
6611 new LinkedErrors(),
6612 new Dedupe(),
6613 new UserAgent(),
6614 ];
6615 /**
6616 * The Sentry Browser SDK Client.
6617 *
6618 * To use this SDK, call the {@link init} function as early as possible when
6619 * loading the web page. To set context information or send manual events, use
6620 * the provided methods.
6621 *
6622 * @example
6623 *
6624 * ```
6625 *
6626 * import { init } from '@sentry/browser';
6627 *
6628 * init({
6629 * dsn: '__DSN__',
6630 * // ...
6631 * });
6632 * ```
6633 *
6634 * @example
6635 * ```
6636 *
6637 * import { configureScope } from '@sentry/browser';
6638 * configureScope((scope: Scope) => {
6639 * scope.setExtra({ battery: 0.7 });
6640 * scope.setTag({ user_mode: 'admin' });
6641 * scope.setUser({ id: '4711' });
6642 * });
6643 * ```
6644 *
6645 * @example
6646 * ```
6647 *
6648 * import { addBreadcrumb } from '@sentry/browser';
6649 * addBreadcrumb({
6650 * message: 'My Breadcrumb',
6651 * // ...
6652 * });
6653 * ```
6654 *
6655 * @example
6656 *
6657 * ```
6658 *
6659 * import * as Sentry from '@sentry/browser';
6660 * Sentry.captureMessage('Hello, world!');
6661 * Sentry.captureException(new Error('Good bye'));
6662 * Sentry.captureEvent({
6663 * message: 'Manual',
6664 * stacktrace: [
6665 * // ...
6666 * ],
6667 * });
6668 * ```
6669 *
6670 * @see {@link BrowserOptions} for documentation on configuration options.
6671 */
6672 function init(options = {}) {
6673 if (options.defaultIntegrations === undefined) {
6674 options.defaultIntegrations = defaultIntegrations;
6675 }
6676 if (options.release === undefined) {
6677 const window = getGlobalObject();
6678 // This supports the variable that sentry-webpack-plugin injects
6679 if (window.SENTRY_RELEASE && window.SENTRY_RELEASE.id) {
6680 options.release = window.SENTRY_RELEASE.id;
6681 }
6682 }
6683 if (options.autoSessionTracking === undefined) {
6684 options.autoSessionTracking = true;
6685 }
6686 if (options.sendClientReports === undefined) {
6687 options.sendClientReports = true;
6688 }
6689 initAndBind(BrowserClient, options);
6690 if (options.autoSessionTracking) {
6691 startSessionTracking();
6692 }
6693 }
6694 /**
6695 * Present the user with a report dialog.
6696 *
6697 * @param options Everything is optional, we try to fetch all info need from the global scope.
6698 */
6699 function showReportDialog(options = {}) {
6700 const hub = getCurrentHub();
6701 const scope = hub.getScope();
6702 if (scope) {
6703 options.user = Object.assign(Object.assign({}, scope.getUser()), options.user);
6704 }
6705 if (!options.eventId) {
6706 options.eventId = hub.lastEventId();
6707 }
6708 const client = hub.getClient();
6709 if (client) {
6710 client.showReportDialog(options);
6711 }
6712 }
6713 /**
6714 * This is the getter for lastEventId.
6715 *
6716 * @returns The last event id of a captured event.
6717 */
6718 function lastEventId() {
6719 return getCurrentHub().lastEventId();
6720 }
6721 /**
6722 * This function is here to be API compatible with the loader.
6723 * @hidden
6724 */
6725 function forceLoad() {
6726 // Noop
6727 }
6728 /**
6729 * This function is here to be API compatible with the loader.
6730 * @hidden
6731 */
6732 function onLoad(callback) {
6733 callback();
6734 }
6735 /**
6736 * Call `flush()` on the current client, if there is one. See {@link Client.flush}.
6737 *
6738 * @param timeout Maximum time in ms the client should wait to flush its event queue. Omitting this parameter will cause
6739 * the client to wait until all events are sent before resolving the promise.
6740 * @returns A promise which resolves to `true` if the queue successfully drains before the timeout, or `false` if it
6741 * doesn't (or if there's no client defined).
6742 */
6743 function flush(timeout) {
6744 const client = getCurrentHub().getClient();
6745 if (client) {
6746 return client.flush(timeout);
6747 }
6748 IS_DEBUG_BUILD && logger.warn('Cannot flush events. No client defined.');
6749 return resolvedSyncPromise(false);
6750 }
6751 /**
6752 * Call `close()` on the current client, if there is one. See {@link Client.close}.
6753 *
6754 * @param timeout Maximum time in ms the client should wait to flush its event queue before shutting down. Omitting this
6755 * parameter will cause the client to wait until all events are sent before disabling itself.
6756 * @returns A promise which resolves to `true` if the queue successfully drains before the timeout, or `false` if it
6757 * doesn't (or if there's no client defined).
6758 */
6759 function close(timeout) {
6760 const client = getCurrentHub().getClient();
6761 if (client) {
6762 return client.close(timeout);
6763 }
6764 IS_DEBUG_BUILD && logger.warn('Cannot flush events and disable SDK. No client defined.');
6765 return resolvedSyncPromise(false);
6766 }
6767 /**
6768 * Wrap code within a try/catch block so the SDK is able to capture errors.
6769 *
6770 * @param fn A function to wrap.
6771 *
6772 * @returns The result of wrapped function call.
6773 */
6774 // eslint-disable-next-line @typescript-eslint/no-explicit-any
6775 function wrap(fn) {
6776 return wrap$1(fn)();
6777 }
6778 function startSessionOnHub(hub) {
6779 hub.startSession({ ignoreDuration: true });
6780 hub.captureSession();
6781 }
6782 /**
6783 * Enable automatic Session Tracking for the initial page load.
6784 */
6785 function startSessionTracking() {
6786 const window = getGlobalObject();
6787 const document = window.document;
6788 if (typeof document === 'undefined') {
6789 IS_DEBUG_BUILD && logger.warn('Session tracking in non-browser environment with @sentry/browser is not supported.');
6790 return;
6791 }
6792 const hub = getCurrentHub();
6793 // The only way for this to be false is for there to be a version mismatch between @sentry/browser (>= 6.0.0) and
6794 // @sentry/hub (< 5.27.0). In the simple case, there won't ever be such a mismatch, because the two packages are
6795 // pinned at the same version in package.json, but there are edge cases where it's possible. See
6796 // https://github.com/getsentry/sentry-javascript/issues/3207 and
6797 // https://github.com/getsentry/sentry-javascript/issues/3234 and
6798 // https://github.com/getsentry/sentry-javascript/issues/3278.
6799 if (!hub.captureSession) {
6800 return;
6801 }
6802 // The session duration for browser sessions does not track a meaningful
6803 // concept that can be used as a metric.
6804 // Automatically captured sessions are akin to page views, and thus we
6805 // discard their duration.
6806 startSessionOnHub(hub);
6807 // We want to create a session for every navigation as well
6808 addInstrumentationHandler('history', ({ from, to }) => {
6809 // Don't create an additional session for the initial route or if the location did not change
6810 if (!(from === undefined || from === to)) {
6811 startSessionOnHub(getCurrentHub());
6812 }
6813 });
6814 }
6815
6816 // TODO: Remove in the next major release and rely only on @sentry/core SDK_VERSION and SdkInfo metadata
6817 const SDK_NAME = 'sentry.javascript.browser';
6818
6819 let windowIntegrations = {};
6820 // This block is needed to add compatibility with the integrations packages when used with a CDN
6821 const _window = getGlobalObject();
6822 if (_window.Sentry && _window.Sentry.Integrations) {
6823 windowIntegrations = _window.Sentry.Integrations;
6824 }
6825 const INTEGRATIONS = Object.assign(Object.assign(Object.assign({}, windowIntegrations), CoreIntegrations), BrowserIntegrations);
6826
6827 exports.BrowserClient = BrowserClient;
6828 exports.Hub = Hub;
6829 exports.Integrations = INTEGRATIONS;
6830 exports.SDK_NAME = SDK_NAME;
6831 exports.SDK_VERSION = SDK_VERSION;
6832 exports.Scope = Scope;
6833 exports.Session = Session;
6834 exports.Transports = index;
6835 exports.addBreadcrumb = addBreadcrumb;
6836 exports.addGlobalEventProcessor = addGlobalEventProcessor;
6837 exports.captureEvent = captureEvent;
6838 exports.captureException = captureException;
6839 exports.captureMessage = captureMessage;
6840 exports.close = close;
6841 exports.configureScope = configureScope;
6842 exports.defaultIntegrations = defaultIntegrations;
6843 exports.eventFromException = eventFromException;
6844 exports.eventFromMessage = eventFromMessage;
6845 exports.flush = flush;
6846 exports.forceLoad = forceLoad;
6847 exports.getCurrentHub = getCurrentHub;
6848 exports.getHubFromCarrier = getHubFromCarrier;
6849 exports.init = init;
6850 exports.injectReportDialog = injectReportDialog;
6851 exports.lastEventId = lastEventId;
6852 exports.makeMain = makeMain;
6853 exports.onLoad = onLoad;
6854 exports.setContext = setContext;
6855 exports.setExtra = setExtra;
6856 exports.setExtras = setExtras;
6857 exports.setTag = setTag;
6858 exports.setTags = setTags;
6859 exports.setUser = setUser;
6860 exports.showReportDialog = showReportDialog;
6861 exports.startTransaction = startTransaction;
6862 exports.withScope = withScope;
6863 exports.wrap = wrap;
6864
6865 return exports;
6866
6867})({});
6868//# sourceMappingURL=bundle.es6.js.map