UNPKG

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