History

Raven.js 3.25.2 (30b6d4e)

https://github.com/getsentry/sentry-javascript/blob/d5e44e2cf87ccecefc9a07bc460f403b3a0f46fd/dist/raven.js Reformatted using Prettier.io

Simon Legner committed 2 years ago

Commit 91eb5251 parent 0dd0ad81 file · +1037 −125Browse files

Unified · Split

assets/javascripts/vendor/raven.js+1037−125
@@ -1,10 +1,10 @@
1/*! Raven.js 3.20.1 (42adaf5) | github.com/getsentry/raven-js */1/*! Raven.js 3.25.2 (30b6d4e) | github.com/getsentry/raven-js */
2 2
3/*3/*
4 * Includes TraceKit4 * Includes TraceKit
5 * https://github.com/getsentry/TraceKit5 * https://github.com/getsentry/TraceKit
6 *6 *
7 * Copyright 2017 Matt Robenolt and other contributors7 * Copyright 2018 Matt Robenolt and other contributors
8 * Released under the BSD license8 * Released under the BSD license
9 * https://github.com/getsentry/raven-js/blob/master/LICENSE9 * https://github.com/getsentry/raven-js/blob/master/LICENSE
10 *10 *
@@ -77,6 +77,8 @@ · 66 unmodified lines
77 ],77 ],
78 2: [78 2: [
79 function (_dereq_, module, exports) {79 function (_dereq_, module, exports) {
80 var utils = _dereq_(5);
81
80 var wrapMethod = function (console, level, callback) {82 var wrapMethod = function (console, level, callback) {
81 var originalConsoleLevel = console[level];83 var originalConsoleLevel = console[level];
82 var originalConsole = console;84 var originalConsole = console;
@@ -90,7 +92,7 @@ · 7 unmodified lines
90 console[level] = function () {92 console[level] = function () {
91 var args = [].slice.call(arguments);93 var args = [].slice.call(arguments);
92 94
93 var msg = "" + args.join(" ");95 var msg = utils.safeJoin(args, " ");
94 var data = {96 var data = {
95 level: sentryLevel,97 level: sentryLevel,
96 logger: "console",98 logger: "console",
@@ -102,7 +104,7 @@ · 5 unmodified lines
102 // Default browsers message104 // Default browsers message
103 msg =105 msg =
104 "Assertion failed: " +106 "Assertion failed: " +
105 (args.slice(1).join(" ") || "console.assert");107 (utils.safeJoin(args.slice(1), " ") || "console.assert");
106 data.extra.arguments = args.slice(1);108 data.extra.arguments = args.slice(1);
107 callback && callback(msg, data);109 callback && callback(msg, data);
108 }110 }
@@ -127,7 +129,7 @@ · 18 unmodified lines
127 wrapMethod: wrapMethod,129 wrapMethod: wrapMethod,
128 };130 };
129 },131 },
130 {},132 { 5: 5 },
131 ],133 ],
132 3: [134 3: [
133 function (_dereq_, module, exports) {135 function (_dereq_, module, exports) {
@@ -136,12 +138,16 @@ · 2 unmodified lines
136 138
137 var TraceKit = _dereq_(6);139 var TraceKit = _dereq_(6);
138 var stringify = _dereq_(7);140 var stringify = _dereq_(7);
141 var md5 = _dereq_(8);
139 var RavenConfigError = _dereq_(1);142 var RavenConfigError = _dereq_(1);
140 143
141 var utils = _dereq_(5);144 var utils = _dereq_(5);
145 var isErrorEvent = utils.isErrorEvent;
146 var isDOMError = utils.isDOMError;
147 var isDOMException = utils.isDOMException;
142 var isError = utils.isError;148 var isError = utils.isError;
143 var isObject = utils.isObject;149 var isObject = utils.isObject;
144 var isErrorEvent = utils.isErrorEvent;150 var isPlainObject = utils.isPlainObject;
145 var isUndefined = utils.isUndefined;151 var isUndefined = utils.isUndefined;
146 var isFunction = utils.isFunction;152 var isFunction = utils.isFunction;
147 var isString = utils.isString;153 var isString = utils.isString;
@@ -160,6 +166,11 @@ · 12 unmodified lines
160 var isSameStacktrace = utils.isSameStacktrace;166 var isSameStacktrace = utils.isSameStacktrace;
161 var parseUrl = utils.parseUrl;167 var parseUrl = utils.parseUrl;
162 var fill = utils.fill;168 var fill = utils.fill;
169 var supportsFetch = utils.supportsFetch;
170 var supportsReferrerPolicy = utils.supportsReferrerPolicy;
171 var serializeKeysForMessage = utils.serializeKeysForMessage;
172 var serializeException = utils.serializeException;
173 var sanitize = utils.sanitize;
163 174
164 var wrapConsoleMethod = _dereq_(2).wrapMethod;175 var wrapConsoleMethod = _dereq_(2).wrapMethod;
165 176
@@ -207,20 +218,33 @@ · 41 unmodified lines
207 this._globalProject = null;218 this._globalProject = null;
208 this._globalContext = {};219 this._globalContext = {};
209 this._globalOptions = {220 this._globalOptions = {
221 // SENTRY_RELEASE can be injected by https://github.com/getsentry/sentry-webpack-plugin
222 release: _window.SENTRY_RELEASE && _window.SENTRY_RELEASE.id,
210 logger: "javascript",223 logger: "javascript",
211 ignoreErrors: [],224 ignoreErrors: [],
212 ignoreUrls: [],225 ignoreUrls: [],
213 whitelistUrls: [],226 whitelistUrls: [],
214 includePaths: [],227 includePaths: [],
228 headers: null,
215 collectWindowErrors: true,229 collectWindowErrors: true,
230 captureUnhandledRejections: true,
216 maxMessageLength: 0,231 maxMessageLength: 0,
217
218 // By default, truncates URL values to 250 chars232 // By default, truncates URL values to 250 chars
219 maxUrlLength: 250,233 maxUrlLength: 250,
220 stackTraceLimit: 50,234 stackTraceLimit: 50,
221 autoBreadcrumbs: true,235 autoBreadcrumbs: true,
222 instrument: true,236 instrument: true,
223 sampleRate: 1,237 sampleRate: 1,
238 sanitizeKeys: [],
239 };
240 this._fetchDefaults = {
241 method: "POST",
242 keepalive: true,
243 // Despite all stars in the sky saying that Edge supports old draft syntax, aka 'never', 'always', 'origin' and 'default
244 // https://caniuse.com/#feat=referrer-policy
245 // It doesn't. And it throw exception instead of ignoring this parameter...
246 // REF: https://github.com/getsentry/raven-js/issues/1233
247 referrerPolicy: supportsReferrerPolicy() ? "origin" : "",
224 };248 };
225 this._ignoreOnError = 0;249 this._ignoreOnError = 0;
226 this._isRavenInstalled = false;250 this._isRavenInstalled = false;
@@ -257,7 +281,7 @@ · 30 unmodified lines
257 // webpack (using a build step causes webpack #1617). Grunt verifies that281 // webpack (using a build step causes webpack #1617). Grunt verifies that
258 // this value matches package.json during build.282 // this value matches package.json during build.
259 // See: https://github.com/getsentry/raven-js/issues/465283 // See: https://github.com/getsentry/raven-js/issues/465
260 VERSION: "3.20.1",284 VERSION: "3.25.2",
261 285
262 debug: false,286 debug: false,
263 287
@@ -376,6 +400,10 @@ · 112 unmodified lines
376 self._handleOnErrorStackInfo.apply(self, arguments);400 self._handleOnErrorStackInfo.apply(self, arguments);
377 });401 });
378 402
403 if (self._globalOptions.captureUnhandledRejections) {
404 self._attachPromiseRejectionHandler();
405 }
406
379 self._patchFunctionToString();407 self._patchFunctionToString();
380 408
381 if (409 if (
@@ -538,7 +566,7 @@ · 156 unmodified lines
538 return wrapped;566 return wrapped;
539 },567 },
540 568
541 /*569 /**
542 * Uninstalls the global error handler.570 * Uninstalls the global error handler.
543 *571 *
544 * @return {Raven}572 * @return {Raven}
@@ -546,8 +574,10 @@ · 1 unmodified line
546 uninstall: function () {574 uninstall: function () {
547 TraceKit.report.uninstall();575 TraceKit.report.uninstall();
548 576
577 this._detachPromiseRejectionHandler();
549 this._unpatchFunctionToString();578 this._unpatchFunctionToString();
550 this._restoreBuiltIns();579 this._restoreBuiltIns();
580 this._restoreConsole();
551 581
552 Error.stackTraceLimit = this._originalErrorStackTraceLimit;582 Error.stackTraceLimit = this._originalErrorStackTraceLimit;
553 this._isRavenInstalled = false;583 this._isRavenInstalled = false;
@@ -555,7 +585,58 @@ · 1 unmodified line
555 return this;585 return this;
556 },586 },
557 587
558 /*588 /**
589 * Callback used for `unhandledrejection` event
590 *
591 * @param {PromiseRejectionEvent} event An object containing
592 * promise: the Promise that was rejected
593 * reason: the value with which the Promise was rejected
594 * @return void
595 */
596 _promiseRejectionHandler: function (event) {
597 this._logDebug(
598 "debug",
599 "Raven caught unhandled promise rejection:",
600 event,
601 );
602 this.captureException(event.reason, {
603 extra: {
604 unhandledPromiseRejection: true,
605 },
606 });
607 },
608
609 /**
610 * Installs the global promise rejection handler.
611 *
612 * @return {raven}
613 */
614 _attachPromiseRejectionHandler: function () {
615 this._promiseRejectionHandler =
616 this._promiseRejectionHandler.bind(this);
617 _window.addEventListener &&
618 _window.addEventListener(
619 "unhandledrejection",
620 this._promiseRejectionHandler,
621 );
622 return this;
623 },
624
625 /**
626 * Uninstalls the global promise rejection handler.
627 *
628 * @return {raven}
629 */
630 _detachPromiseRejectionHandler: function () {
631 _window.removeEventListener &&
632 _window.removeEventListener(
633 "unhandledrejection",
634 this._promiseRejectionHandler,
635 );
636 return this;
637 },
638
639 /**
559 * Manually capture an exception and send it over to Sentry640 * Manually capture an exception and send it over to Sentry
560 *641 *
561 * @param {error} ex An exception to be logged642 * @param {error} ex An exception to be logged
@@ -563,30 +644,60 @@ · 1 unmodified line
563 * @return {Raven}644 * @return {Raven}
564 */645 */
565 captureException: function (ex, options) {646 captureException: function (ex, options) {
566 // Cases for sending ex as a message, rather than an exception647 options = objectMerge(
567 var isNotError = !isError(ex);648 { trimHeadFrames: 0 },
568 var isNotErrorEvent = !isErrorEvent(ex);649 options ? options : {},
569 var isErrorEventWithoutError = isErrorEvent(ex) && !ex.error;650 );
570 651
571 if (652 if (isErrorEvent(ex) && ex.error) {
572 (isNotError && isNotErrorEvent) ||653 // If it is an ErrorEvent with `error` property, extract it to get actual Error
573 isErrorEventWithoutError654 ex = ex.error;
574 ) {655 } else if (isDOMError(ex) || isDOMException(ex)) {
656 // If it is a DOMError or DOMException (which are legacy APIs, but still supported in some browsers)
657 // then we just extract the name and message, as they don't provide anything else
658 // https://developer.mozilla.org/en-US/docs/Web/API/DOMError
659 // https://developer.mozilla.org/en-US/docs/Web/API/DOMException
660 var name =
661 ex.name || (isDOMError(ex) ? "DOMError" : "DOMException");
662 var message = ex.message ? name + ": " + ex.message : name;
663
664 return this.captureMessage(
665 message,
666 objectMerge(options, {
667 // neither DOMError or DOMException provide stack trace and we most likely wont get it this way as well
668 // but it's barely any overhead so we may at least try
669 stacktrace: true,
670 trimHeadFrames: options.trimHeadFrames + 1,
671 }),
672 );
673 } else if (isError(ex)) {
674 // we have a real Error object
675 ex = ex;
676 } else if (isPlainObject(ex)) {
677 // If it is plain Object, serialize it manually and extract options
678 // This will allow us to group events based on top-level keys
679 // which is much better than creating new group when any key/value change
680 options = this._getCaptureExceptionOptionsFromPlainObject(
681 options,
682 ex,
683 );
684 ex = new Error(options.message);
685 } else {
686 // If none of previous checks were valid, then it means that
687 // it's not a DOMError/DOMException
688 // it's not a plain Object
689 // it's not a valid ErrorEvent (one with an error property)
690 // it's not an Error
691 // So bail out and capture it as a simple message:
575 return this.captureMessage(692 return this.captureMessage(
576 ex,693 ex,
577 objectMerge(694 objectMerge(options, {
578 {695 stacktrace: true, // if we fall back to captureMessage, default to attempting a new trace
579 trimHeadFrames: 1,696 trimHeadFrames: options.trimHeadFrames + 1,
580 stacktrace: true, // if we fall back to captureMessage, default to attempting a new trace697 }),
581 },
582 options,
583 ),
584 );698 );
585 }699 }
586 700
587 // Get actual Error from ErrorEvent
588 if (isErrorEvent(ex)) ex = ex.error;
589
590 // Store the raw exception object for potential debugging and introspection701 // Store the raw exception object for potential debugging and introspection
591 this._lastCapturedException = ex;702 this._lastCapturedException = ex;
592 703
@@ -607,6 +718,23 @@ · 14 unmodified lines
607 return this;718 return this;
608 },719 },
609 720
721 _getCaptureExceptionOptionsFromPlainObject: function (
722 currentOptions,
723 ex,
724 ) {
725 var exKeys = Object.keys(ex).sort();
726 var options = objectMerge(currentOptions, {
727 message:
728 "Non-Error exception captured with keys: " +
729 serializeKeysForMessage(exKeys),
730 fingerprint: [md5(exKeys)],
731 extra: currentOptions.extra || {},
732 });
733 options.extra.__serialized__ = serializeException(ex);
734
735 return options;
736 },
737
610 /*738 /*
611 * Manually send a message to Sentry739 * Manually send a message to Sentry
612 *740 *
@@ -626,10 +754,11 @@ · 13 unmodified lines
626 }754 }
627 755
628 options = options || {};756 options = options || {};
757 msg = msg + ""; // Make sure it's actually a string
629 758
630 var data = objectMerge(759 var data = objectMerge(
631 {760 {
632 message: msg + "", // Make sure it's actually a string761 message: msg,
633 },762 },
634 options,763 options,
635 );764 );
@@ -651,6 +780,17 @@ · 15 unmodified lines
651 780
652 // stack[0] is `throw new Error(msg)` call itself, we are interested in the frame that was just before that, stack[1]781 // stack[0] is `throw new Error(msg)` call itself, we are interested in the frame that was just before that, stack[1]
653 var initialCall = isArray(stack.stack) && stack.stack[1];782 var initialCall = isArray(stack.stack) && stack.stack[1];
783
784 // if stack[1] is `Raven.captureException`, it means that someone passed a string to it and we redirected that call
785 // to be handled by `captureMessage`, thus `initialCall` is the 3rd one, not 2nd
786 // initialCall => captureException(string) => captureMessage(string)
787 if (
788 initialCall &&
789 initialCall.func === "Raven.captureException"
790 ) {
791 initialCall = stack.stack[2];
792 }
793
654 var fileurl = (initialCall && initialCall.url) || "";794 var fileurl = (initialCall && initialCall.url) || "";
655 795
656 if (796 if (
@@ -671,18 +811,21 @@ · 14 unmodified lines
671 this._globalOptions.stacktrace ||811 this._globalOptions.stacktrace ||
672 (options && options.stacktrace)812 (options && options.stacktrace)
673 ) {813 ) {
814 // fingerprint on msg, not stack trace (legacy behavior, could be revisited)
815 data.fingerprint =
816 data.fingerprint == null ? msg : data.fingerprint;
817
674 options = objectMerge(818 options = objectMerge(
675 {819 {
676 // fingerprint on msg, not stack trace (legacy behavior, could be820 trimHeadFrames: 0,
677 // revisited)
678 fingerprint: msg,
679 // since we know this is a synthetic trace, the top N-most frames
680 // MUST be from Raven.js, so mark them as in_app later by setting
681 // trimHeadFrames
682 trimHeadFrames: (options.trimHeadFrames || 0) + 1,
683 },821 },
684 options,822 options,
685 );823 );
824 // Since we know this is a synthetic trace, the top frame (this function call)
825 // MUST be from Raven.js, so mark it for trimming
826 // We add to the trim counter so that callers can choose to trim extra frames, such
827 // as utility functions.
828 options.trimHeadFrames += 1;
686 829
687 var frames = this._prepareFrames(stack, options);830 var frames = this._prepareFrames(stack, options);
688 data.stacktrace = {831 data.stacktrace = {
@@ -691,6 +834,13 @@ · 2 unmodified lines
691 };834 };
692 }835 }
693 836
837 // Make sure that fingerprint is always wrapped in an array
838 if (data.fingerprint) {
839 data.fingerprint = isArray(data.fingerprint)
840 ? data.fingerprint
841 : [data.fingerprint];
842 }
843
694 // Fire away!844 // Fire away!
695 this._send(data);845 this._send(data);
696 846
@@ -976,7 +1126,7 @@ · 279 unmodified lines
976 },1126 },
977 1127
978 _triggerEvent: function (eventType, options) {1128 _triggerEvent: function (eventType, options) {
979 // NOTE: `event` is a native browser thing, so let's avoid conflicting with it1129 // NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it
980 var evt, key;1130 var evt, key;
981 1131
982 if (!this._hasDocument) return;1132 if (!this._hasDocument) return;
@@ -1290,7 +1440,7 @@ · 307 unmodified lines
1290 1440
1291 fill(_window, "setTimeout", wrapTimeFn, wrappedBuiltIns);1441 fill(_window, "setTimeout", wrapTimeFn, wrappedBuiltIns);
1292 fill(_window, "setInterval", wrapTimeFn, wrappedBuiltIns);1442 fill(_window, "setInterval", wrapTimeFn, wrappedBuiltIns);
1293 if (_requestAnimationFrame) {1443 if (_window.requestAnimationFrame) {
1294 fill(1444 fill(
1295 _window,1445 _window,
1296 "requestAnimationFrame",1446 "requestAnimationFrame",
@@ -1365,7 +1515,8 @@ · 68 unmodified lines
1365 }1515 }
1366 1516
1367 if (autoBreadcrumbs.xhr && "XMLHttpRequest" in _window) {1517 if (autoBreadcrumbs.xhr && "XMLHttpRequest" in _window) {
1368 var xhrproto = XMLHttpRequest.prototype;1518 var xhrproto =
1519 _window.XMLHttpRequest && _window.XMLHttpRequest.prototype;
1369 fill(1520 fill(
1370 xhrproto,1521 xhrproto,
1371 "open",1522 "open",
@@ -1395,7 +1546,7 @@ · 23 unmodified lines
1395 xhrproto,1546 xhrproto,
1396 "send",1547 "send",
1397 function (origSend) {1548 function (origSend) {
1398 return function (data) {1549 return function () {
1399 // preserve arity1550 // preserve arity
1400 var xhr = this;1551 var xhr = this;
1401 1552
@@ -1450,12 +1601,12 @@ · 48 unmodified lines
1450 );1601 );
1451 }1602 }
1452 1603
1453 if (autoBreadcrumbs.xhr && "fetch" in _window) {1604 if (autoBreadcrumbs.xhr && supportsFetch()) {
1454 fill(1605 fill(
1455 _window,1606 _window,
1456 "fetch",1607 "fetch",
1457 function (origFetch) {1608 function (origFetch) {
1458 return function (fn, t) {1609 return function () {
1459 // preserve arity1610 // preserve arity
1460 // Make a copy of the arguments to prevent deoptimization1611 // Make a copy of the arguments to prevent deoptimization
1461 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments1612 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments
@@ -1482,6 +1633,11 @@ · 20 unmodified lines
1482 url = "" + fetchInput;1633 url = "" + fetchInput;
1483 }1634 }
1484 1635
1636 // if Sentry key appears in URL, don't capture, as it's our own request
1637 if (url.indexOf(self._globalKey) !== -1) {
1638 return origFetch.apply(this, args);
1639 }
1640
1485 if (args[1] && args[1].method) {1641 if (args[1] && args[1].method) {
1486 method = args[1].method;1642 method = args[1].method;
1487 }1643 }
@@ -1492,18 +1648,29 @@ · 4 unmodified lines
1492 status_code: null,1648 status_code: null,
1493 };1649 };
1494 1650
1495 self.captureBreadcrumb({
1496 type: "http",
1497 category: "fetch",
1498 data: fetchData,
1499 });
1500
1501 return origFetch1651 return origFetch
1502 .apply(this, args)1652 .apply(this, args)
1503 .then(function (response) {1653 .then(function (response) {
1504 fetchData.status_code = response.status;1654 fetchData.status_code = response.status;
1505 1655
1656 self.captureBreadcrumb({
1657 type: "http",
1658 category: "fetch",
1659 data: fetchData,
1660 });
1661
1506 return response;1662 return response;
1663 })
1664 ["catch"](function (err) {
1665 // if there is an error performing the request
1666 self.captureBreadcrumb({
1667 type: "http",
1668 category: "fetch",
1669 data: fetchData,
1670 level: "error",
1671 });
1672
1673 throw err;
1507 });1674 });
1508 };1675 };
1509 },1676 },
@@ -1525,7 +1692,7 @@ · 15 unmodified lines
1525 self._keypressEventHandler(),1692 self._keypressEventHandler(),
1526 false,1693 false,
1527 );1694 );
1528 } else {1695 } else if (_document.attachEvent) {
1529 // IE8 Compatibility1696 // IE8 Compatibility
1530 _document.attachEvent(1697 _document.attachEvent(
1531 "onclick",1698 "onclick",
@@ -1548,8 +1715,8 @@ · 16 unmodified lines
1548 var hasPushAndReplaceState =1715 var hasPushAndReplaceState =
1549 !isChromePackagedApp &&1716 !isChromePackagedApp &&
1550 _window.history &&1717 _window.history &&
1551 history.pushState &&1718 _window.history.pushState &&
1552 history.replaceState;1719 _window.history.replaceState;
1553 if (autoBreadcrumbs.location && hasPushAndReplaceState) {1720 if (autoBreadcrumbs.location && hasPushAndReplaceState) {
1554 // TODO: remove onpopstate handler on uninstall()1721 // TODO: remove onpopstate handler on uninstall()
1555 var oldOnPopState = _window.onpopstate;1722 var oldOnPopState = _window.onpopstate;
@@ -1579,13 +1746,13 @@ · 23 unmodified lines
1579 };1746 };
1580 1747
1581 fill(1748 fill(
1582 history,1749 _window.history,
1583 "pushState",1750 "pushState",
1584 historyReplacementFunction,1751 historyReplacementFunction,
1585 wrappedBuiltIns,1752 wrappedBuiltIns,
1586 );1753 );
1587 fill(1754 fill(
1588 history,1755 _window.history,
1589 "replaceState",1756 "replaceState",
1590 historyReplacementFunction,1757 historyReplacementFunction,
1591 wrappedBuiltIns,1758 wrappedBuiltIns,
@@ -1629,6 +1796,14 @@ · 37 unmodified lines
1629 }1796 }
1630 },1797 },
1631 1798
1799 _restoreConsole: function () {
1800 // eslint-disable-next-line guard-for-in
1801 for (var method in this._originalConsoleMethods) {
1802 this._originalConsole[method] =
1803 this._originalConsoleMethods[method];
1804 }
1805 },
1806
1632 _drainPlugins: function () {1807 _drainPlugins: function () {
1633 var self = this;1808 var self = this;
1634 1809
@@ -1822,7 +1997,7 @@ · 187 unmodified lines
1822 },1997 },
1823 ],1998 ],
1824 },1999 },
1825 culprit: fileurl,2000 transaction: fileurl,
1826 },2001 },
1827 options,2002 options,
1828 );2003 );
@@ -1905,18 +2080,18 @@ · 76 unmodified lines
1905 2080
1906 if (this._hasNavigator && _navigator.userAgent) {2081 if (this._hasNavigator && _navigator.userAgent) {
1907 httpData.headers = {2082 httpData.headers = {
1908 "User-Agent": navigator.userAgent,2083 "User-Agent": _navigator.userAgent,
1909 };2084 };
1910 }2085 }
1911 2086
1912 if (this._hasDocument) {2087 // Check in `window` instead of `document`, as we may be in ServiceWorker environment
1913 if (_document.location && _document.location.href) {2088 if (_window.location && _window.location.href) {
1914 httpData.url = _document.location.href;2089 httpData.url = _window.location.href;
1915 }2090 }
1916 if (_document.referrer) {2091
1917 if (!httpData.headers) httpData.headers = {};2092 if (this._hasDocument && _document.referrer) {
1918 httpData.headers.Referer = _document.referrer;2093 if (!httpData.headers) httpData.headers = {};
1919 }2094 httpData.headers.Referer = _document.referrer;
1920 }2095 }
1921 2096
1922 return httpData;2097 return httpData;
@@ -1949,7 +2124,7 @@ · 26 unmodified lines
1949 if (2124 if (
1950 !last ||2125 !last ||
1951 current.message !== last.message || // defined for captureMessage2126 current.message !== last.message || // defined for captureMessage
1952 current.culprit !== last.culprit // defined for captureException/onerror2127 current.transaction !== last.transaction // defined for captureException/onerror
1953 )2128 )
1954 return false;2129 return false;
1955 2130
@@ -1982,8 +2157,14 @@ · 26 unmodified lines
1982 try {2157 try {
1983 // If Retry-After is not in Access-Control-Expose-Headers, most2158 // If Retry-After is not in Access-Control-Expose-Headers, most
1984 // browsers will throw an exception trying to access it2159 // browsers will throw an exception trying to access it
1985 retry = request.getResponseHeader("Retry-After");2160 if (supportsFetch()) {
1986 retry = parseInt(retry, 10) * 1000; // Retry-After is returned in seconds2161 retry = request.headers.get("Retry-After");
2162 } else {
2163 retry = request.getResponseHeader("Retry-After");
2164 }
2165
2166 // Retry-After is returned in seconds
2167 retry = parseInt(retry, 10) * 1000;
1987 } catch (e) {2168 } catch (e) {
1988 /* eslint no-empty:0 */2169 /* eslint no-empty:0 */
1989 }2170 }
@@ -2037,9 +2218,6 @@ · 47 unmodified lines
2037 };2218 };
2038 }2219 }
2039 2220
2040 // If there are no tags/extra, strip the key from the payload alltogther.
2041 if (isEmptyObject(data.tags)) delete data.tags;
2042
2043 if (this._globalContext.user) {2221 if (this._globalContext.user) {
2044 // sentry.interfaces.User2222 // sentry.interfaces.User
2045 data.user = this._globalContext.user;2223 data.user = this._globalContext.user;
@@ -2056,6 +2234,19 @@ · 10 unmodified lines
2056 if (globalOptions.serverName)2234 if (globalOptions.serverName)
2057 data.server_name = globalOptions.serverName;2235 data.server_name = globalOptions.serverName;
2058 2236
2237 data = this._sanitizeData(data);
2238
2239 // Cleanup empty properties before sending them to the server
2240 Object.keys(data).forEach(function (key) {
2241 if (
2242 data[key] == null ||
2243 data[key] === "" ||
2244 isEmptyObject(data[key])
2245 ) {
2246 delete data[key];
2247 }
2248 });
2249
2059 if (isFunction(globalOptions.dataCallback)) {2250 if (isFunction(globalOptions.dataCallback)) {
2060 data = globalOptions.dataCallback(data) || data;2251 data = globalOptions.dataCallback(data) || data;
2061 }2252 }
@@ -2093,6 +2284,10 @@ · 31 unmodified lines
2093 }2284 }
2094 },2285 },
2095 2286
2287 _sanitizeData: function (data) {
2288 return sanitize(data, this._globalOptions.sanitizeKeys);
2289 },
2290
2096 _getUuid: function () {2291 _getUuid: function () {
2097 return uuid4();2292 return uuid4();
2098 },2293 },
@@ -2197,6 +2392,61 @@ · 98 unmodified lines
2197 },2392 },
2198 2393
2199 _makeRequest: function (opts) {2394 _makeRequest: function (opts) {
2395 // Auth is intentionally sent as part of query string (NOT as custom HTTP header) to avoid preflight CORS requests
2396 var url = opts.url + "?" + urlencode(opts.auth);
2397
2398 var evaluatedHeaders = null;
2399 var evaluatedFetchParameters = {};
2400
2401 if (opts.options.headers) {
2402 evaluatedHeaders = this._evaluateHash(opts.options.headers);
2403 }
2404
2405 if (opts.options.fetchParameters) {
2406 evaluatedFetchParameters = this._evaluateHash(
2407 opts.options.fetchParameters,
2408 );
2409 }
2410
2411 if (supportsFetch()) {
2412 evaluatedFetchParameters.body = stringify(opts.data);
2413
2414 var defaultFetchOptions = objectMerge(
2415 {},
2416 this._fetchDefaults,
2417 );
2418 var fetchOptions = objectMerge(
2419 defaultFetchOptions,
2420 evaluatedFetchParameters,
2421 );
2422
2423 if (evaluatedHeaders) {
2424 fetchOptions.headers = evaluatedHeaders;
2425 }
2426
2427 return _window
2428 .fetch(url, fetchOptions)
2429 .then(function (response) {
2430 if (response.ok) {
2431 opts.onSuccess && opts.onSuccess();
2432 } else {
2433 var error = new Error(
2434 "Sentry error code: " + response.status,
2435 );
2436 // It's called request only to keep compatibility with XHR interface
2437 // and not add more redundant checks in setBackoffState method
2438 error.request = response;
2439 opts.onError && opts.onError(error);
2440 }
2441 })
2442 ["catch"](function () {
2443 opts.onError &&
2444 opts.onError(
2445 new Error("Sentry error code: network unavailable"),
2446 );
2447 });
2448 }
2449
2200 var request =2450 var request =
2201 _window.XMLHttpRequest && new _window.XMLHttpRequest();2451 _window.XMLHttpRequest && new _window.XMLHttpRequest();
2202 if (!request) return;2452 if (!request) return;
@@ -2208,8 +2458,6 @@ · 5 unmodified lines
2208 2458
2209 if (!hasCORS) return;2459 if (!hasCORS) return;
2210 2460
2211 var url = opts.url;
2212
2213 if ("withCredentials" in request) {2461 if ("withCredentials" in request) {
2214 request.onreadystatechange = function () {2462 request.onreadystatechange = function () {
2215 if (request.readyState !== 4) {2463 if (request.readyState !== 4) {
@@ -2243,14 +2491,37 @@ · 27 unmodified lines
2243 }2491 }
2244 }2492 }
2245 2493
2246 // NOTE: auth is intentionally sent as part of query string (NOT as custom2494 request.open("POST", url);
2247 // HTTP header) so as to avoid preflight CORS requests2495
2248 request.open("POST", url + "?" + urlencode(opts.auth));2496 if (evaluatedHeaders) {
2497 each(evaluatedHeaders, function (key, value) {
2498 request.setRequestHeader(key, value);
2499 });
2500 }
2501
2249 request.send(stringify(opts.data));2502 request.send(stringify(opts.data));
2250 },2503 },
2251 2504
2505 _evaluateHash: function (hash) {
2506 var evaluated = {};
2507
2508 for (var key in hash) {
2509 if (hash.hasOwnProperty(key)) {
2510 var value = hash[key];
2511 evaluated[key] =
2512 typeof value === "function" ? value() : value;
2513 }
2514 }
2515
2516 return evaluated;
2517 },
2518
2252 _logDebug: function (level) {2519 _logDebug: function (level) {
2253 if (this._originalConsoleMethods[level] && this.debug) {2520 // We allow `Raven.debug` and `Raven.config(DSN, { debug: true })` to not make backward incompatible API change
2521 if (
2522 this._originalConsoleMethods[level] &&
2523 (this.debug || this._globalOptions.debug)
2524 ) {
2254 // In IE<10 console methods do not have their own 'apply' method2525 // In IE<10 console methods do not have their own 'apply' method
2255 Function.prototype.apply.call(2526 Function.prototype.apply.call(
2256 this._originalConsoleMethods[level],2527 this._originalConsoleMethods[level],
@@ -2288,7 +2559,7 @@ · 31 unmodified lines
2288 : {},2559 : {},
2289 );2560 );
2290 },2561 },
2291 { 1: 1, 2: 2, 5: 5, 6: 6, 7: 7 },2562 { 1: 1, 2: 2, 5: 5, 6: 6, 7: 7, 8: 8 },
2292 ],2563 ],
2293 4: [2564 4: [
2294 function (_dereq_, module, exports) {2565 function (_dereq_, module, exports) {
@@ -2328,6 +2599,42 @@ · 33 unmodified lines
2328 Raven.afterLoad();2599 Raven.afterLoad();
2329 2600
2330 module.exports = Raven;2601 module.exports = Raven;
2602
2603 /**
2604 * DISCLAIMER:
2605 *
2606 * Expose `Client` constructor for cases where user want to track multiple "sub-applications" in one larger app.
2607 * It's not meant to be used by a wide audience, so pleaaase make sure that you know what you're doing before using it.
2608 * Accidentally calling `install` multiple times, may result in an unexpected behavior that's very hard to debug.
2609 *
2610 * It's called `Client' to be in-line with Raven Node implementation.
2611 *
2612 * HOWTO:
2613 *
2614 * import Raven from 'raven-js';
2615 *
2616 * const someAppReporter = new Raven.Client();
2617 * const someOtherAppReporter = new Raven.Client();
2618 *
2619 * someAppReporter.config('__DSN__', {
2620 * ...config goes here
2621 * });
2622 *
2623 * someOtherAppReporter.config('__OTHER_DSN__', {
2624 * ...config goes here
2625 * });
2626 *
2627 * someAppReporter.captureMessage(...);
2628 * someAppReporter.captureException(...);
2629 * someAppReporter.captureBreadcrumb(...);
2630 *
2631 * someOtherAppReporter.captureMessage(...);
2632 * someOtherAppReporter.captureException(...);
2633 * someOtherAppReporter.captureBreadcrumb(...);
2634 *
2635 * It should "just work".
2636 */
2637 module.exports.Client = RavenConstructor;
2331 }).call(2638 }).call(
2332 this,2639 this,
2333 typeof global !== "undefined"2640 typeof global !== "undefined"
@@ -2344,6 +2651,8 @@ · 10 unmodified lines
2344 5: [2651 5: [
2345 function (_dereq_, module, exports) {2652 function (_dereq_, module, exports) {
2346 (function (global) {2653 (function (global) {
2654 var stringify = _dereq_(7);
2655
2347 var _window =2656 var _window =
2348 typeof window !== "undefined"2657 typeof window !== "undefined"
2349 ? window2658 ? window
@@ -2360,7 +2669,7 @@ · 10 unmodified lines
2360 // Yanked from https://git.io/vS8DV re-used under CC02669 // Yanked from https://git.io/vS8DV re-used under CC0
2361 // with some tiny modifications2670 // with some tiny modifications
2362 function isError(value) {2671 function isError(value) {
2363 switch ({}.toString.call(value)) {2672 switch (Object.prototype.toString.call(value)) {
2364 case "[object Error]":2673 case "[object Error]":
2365 return true;2674 return true;
2366 case "[object Exception]":2675 case "[object Exception]":
@@ -2374,8 +2683,20 @@ · 7 unmodified lines
2374 2683
2375 function isErrorEvent(value) {2684 function isErrorEvent(value) {
2376 return (2685 return (
2377 supportsErrorEvent() &&2686 Object.prototype.toString.call(value) === "[object ErrorEvent]"
2378 {}.toString.call(value) === "[object ErrorEvent]"2687 );
2688 }
2689
2690 function isDOMError(value) {
2691 return (
2692 Object.prototype.toString.call(value) === "[object DOMError]"
2693 );
2694 }
2695
2696 function isDOMException(value) {
2697 return (
2698 Object.prototype.toString.call(value) ===
2699 "[object DOMException]"
2379 );2700 );
2380 }2701 }
2381 2702
@@ -2387,6 +2708,10 @@ · 5 unmodified lines
2387 return typeof what === "function";2708 return typeof what === "function";
2388 }2709 }
2389 2710
2711 function isPlainObject(what) {
2712 return Object.prototype.toString.call(what) === "[object Object]";
2713 }
2714
2390 function isString(what) {2715 function isString(what) {
2391 return Object.prototype.toString.call(what) === "[object String]";2716 return Object.prototype.toString.call(what) === "[object String]";
2392 }2717 }
@@ -2396,6 +2721,8 @@ · 3 unmodified lines
2396 }2721 }
2397 2722
2398 function isEmptyObject(what) {2723 function isEmptyObject(what) {
2724 if (!isPlainObject(what)) return false;
2725
2399 for (var _ in what) {2726 for (var _ in what) {
2400 if (what.hasOwnProperty(_)) {2727 if (what.hasOwnProperty(_)) {
2401 return false;2728 return false;
@@ -2413,6 +2740,59 @@ · 11 unmodified lines
2413 }2740 }
2414 }2741 }
2415 2742
2743 function supportsDOMError() {
2744 try {
2745 new DOMError(""); // eslint-disable-line no-new
2746 return true;
2747 } catch (e) {
2748 return false;
2749 }
2750 }
2751
2752 function supportsDOMException() {
2753 try {
2754 new DOMException(""); // eslint-disable-line no-new
2755 return true;
2756 } catch (e) {
2757 return false;
2758 }
2759 }
2760
2761 function supportsFetch() {
2762 if (!("fetch" in _window)) return false;
2763
2764 try {
2765 new Headers(); // eslint-disable-line no-new
2766 new Request(""); // eslint-disable-line no-new
2767 new Response(); // eslint-disable-line no-new
2768 return true;
2769 } catch (e) {
2770 return false;
2771 }
2772 }
2773
2774 // Despite all stars in the sky saying that Edge supports old draft syntax, aka 'never', 'always', 'origin' and 'default
2775 // https://caniuse.com/#feat=referrer-policy
2776 // It doesn't. And it throw exception instead of ignoring this parameter...
2777 // REF: https://github.com/getsentry/raven-js/issues/1233
2778 function supportsReferrerPolicy() {
2779 if (!supportsFetch()) return false;
2780
2781 try {
2782 // eslint-disable-next-line no-new
2783 new Request("pickleRick", {
2784 referrerPolicy: "origin",
2785 });
2786 return true;
2787 } catch (e) {
2788 return false;
2789 }
2790 }
2791
2792 function supportsPromiseRejectionEvent() {
2793 return typeof PromiseRejectionEvent === "function";
2794 }
2795
2416 function wrappedCallback(callback) {2796 function wrappedCallback(callback) {
2417 function dataCallback(data, original) {2797 function dataCallback(data, original) {
2418 var normalizedData = callback(data) || data;2798 var normalizedData = callback(data) || data;
@@ -2470,9 +2850,15 @@ · 51 unmodified lines
2470 }2850 }
2471 2851
2472 function truncate(str, max) {2852 function truncate(str, max) {
2473 return !max || str.length <= max2853 if (typeof max !== "number") {
2474 ? str2854 throw new Error(
2475 : str.substr(0, max) + "\u2026";2855 "2nd argument to `truncate` function should be a number",
2856 );
2857 }
2858 if (typeof str !== "string" || max === 0) {
2859 return str;
2860 }
2861 return str.length <= max ? str : str.substr(0, max) + "\u2026";
2476 }2862 }
2477 2863
2478 /**2864 /**
@@ -2521,14 +2907,14 @@ · 42 unmodified lines
2521 return pairs.join("&");2907 return pairs.join("&");
2522 }2908 }
2523 2909
2524 // borrowed from https://datatracker.ietf.org/doc/html/rfc3986#appendix-B2910 // borrowed from https://tools.ietf.org/html/rfc3986#appendix-B
2525 // intentionally using regex and not <a/> href parsing trick because React Native and other2911 // intentionally using regex and not <a/> href parsing trick because React Native and other
2526 // environments where DOM might not be available2912 // environments where DOM might not be available
2527 function parseUrl(url) {2913 function parseUrl(url) {
2914 if (typeof url !== "string") return {};
2528 var match = url.match(2915 var match = url.match(
2529 /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/,2916 /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/,
2530 );2917 );
2531 if (!match) return {};
2532 2918
2533 // coerce to undefined values to empty string so we don't get 'undefined'2919 // coerce to undefined values to empty string so we don't get 'undefined'
2534 var query = match[6] || "";2920 var query = match[6] || "";
@@ -2675,6 +3061,13 @@ · 140 unmodified lines
2675 return !!(!!a ^ !!b);3061 return !!(!!a ^ !!b);
2676 }3062 }
2677 3063
3064 /**
3065 * Returns true if both parameters are undefined
3066 */
3067 function isBothUndefined(a, b) {
3068 return isUndefined(a) && isUndefined(b);
3069 }
3070
2678 /**3071 /**
2679 * Returns true if the two input exception interfaces have the same content3072 * Returns true if the two input exception interfaces have the same content
2680 */3073 */
@@ -2687,6 +3080,9 @@ · 6 unmodified lines
2687 if (ex1.type !== ex2.type || ex1.value !== ex2.value)3080 if (ex1.type !== ex2.type || ex1.value !== ex2.value)
2688 return false;3081 return false;
2689 3082
3083 // in case both stacktraces are undefined, we can't decide so default to false
3084 if (isBothUndefined(ex1.stacktrace, ex2.stacktrace)) return false;
3085
2690 return isSameStacktrace(ex1.stacktrace, ex2.stacktrace);3086 return isSameStacktrace(ex1.stacktrace, ex2.stacktrace);
2691 }3087 }
2692 3088
@@ -2726,6 +3122,7 @@ · 33 unmodified lines
2726 * @param track {optional} record instrumentation to an array3122 * @param track {optional} record instrumentation to an array
2727 */3123 */
2728 function fill(obj, name, replacement, track) {3124 function fill(obj, name, replacement, track) {
3125 if (obj == null) return;
2729 var orig = obj[name];3126 var orig = obj[name];
2730 obj[name] = replacement(orig);3127 obj[name] = replacement(orig);
2731 obj[name].__raven__ = true;3128 obj[name].__raven__ = true;
@@ -2735,16 +3132,190 @@ · 3 unmodified lines
2735 }3132 }
2736 }3133 }
2737 3134
3135 /**
3136 * Join values in array
3137 * @param input array of values to be joined together
3138 * @param delimiter string to be placed in-between values
3139 * @returns {string}
3140 */
3141 function safeJoin(input, delimiter) {
3142 if (!isArray(input)) return "";
3143
3144 var output = [];
3145
3146 for (var i = 0; i < input.length; i++) {
3147 try {
3148 output.push(String(input[i]));
3149 } catch (e) {
3150 output.push("[value cannot be serialized]");
3151 }
3152 }
3153
3154 return output.join(delimiter);
3155 }
3156
3157 // Default Node.js REPL depth
3158 var MAX_SERIALIZE_EXCEPTION_DEPTH = 3;
3159 // 50kB, as 100kB is max payload size, so half sounds reasonable
3160 var MAX_SERIALIZE_EXCEPTION_SIZE = 50 * 1024;
3161 var MAX_SERIALIZE_KEYS_LENGTH = 40;
3162
3163 function utf8Length(value) {
3164 return ~-encodeURI(value).split(/%..|./).length;
3165 }
3166
3167 function jsonSize(value) {
3168 return utf8Length(JSON.stringify(value));
3169 }
3170
3171 function serializeValue(value) {
3172 if (typeof value === "string") {
3173 var maxLength = 40;
3174 return truncate(value, maxLength);
3175 } else if (
3176 typeof value === "number" ||
3177 typeof value === "boolean" ||
3178 typeof value === "undefined"
3179 ) {
3180 return value;
3181 }
3182
3183 var type = Object.prototype.toString.call(value);
3184
3185 // Node.js REPL notation
3186 if (type === "[object Object]") return "[Object]";
3187 if (type === "[object Array]") return "[Array]";
3188 if (type === "[object Function]")
3189 return value.name
3190 ? "[Function: " + value.name + "]"
3191 : "[Function]";
3192
3193 return value;
3194 }
3195
3196 function serializeObject(value, depth) {
3197 if (depth === 0) return serializeValue(value);
3198
3199 if (isPlainObject(value)) {
3200 return Object.keys(value).reduce(function (acc, key) {
3201 acc[key] = serializeObject(value[key], depth - 1);
3202 return acc;
3203 }, {});
3204 } else if (Array.isArray(value)) {
3205 return value.map(function (val) {
3206 return serializeObject(val, depth - 1);
3207 });
3208 }
3209
3210 return serializeValue(value);
3211 }
3212
3213 function serializeException(ex, depth, maxSize) {
3214 if (!isPlainObject(ex)) return ex;
3215
3216 depth =
3217 typeof depth !== "number"
3218 ? MAX_SERIALIZE_EXCEPTION_DEPTH
3219 : depth;
3220 maxSize =
3221 typeof depth !== "number"
3222 ? MAX_SERIALIZE_EXCEPTION_SIZE
3223 : maxSize;
3224
3225 var serialized = serializeObject(ex, depth);
3226
3227 if (jsonSize(stringify(serialized)) > maxSize) {
3228 return serializeException(ex, depth - 1);
3229 }
3230
3231 return serialized;
3232 }
3233
3234 function serializeKeysForMessage(keys, maxLength) {
3235 if (typeof keys === "number" || typeof keys === "string")
3236 return keys.toString();
3237 if (!Array.isArray(keys)) return "";
3238
3239 keys = keys.filter(function (key) {
3240 return typeof key === "string";
3241 });
3242 if (keys.length === 0) return "[object has no keys]";
3243
3244 maxLength =
3245 typeof maxLength !== "number"
3246 ? MAX_SERIALIZE_KEYS_LENGTH
3247 : maxLength;
3248 if (keys[0].length >= maxLength) return keys[0];
3249
3250 for (var usedKeys = keys.length; usedKeys > 0; usedKeys--) {
3251 var serialized = keys.slice(0, usedKeys).join(", ");
3252 if (serialized.length > maxLength) continue;
3253 if (usedKeys === keys.length) return serialized;
3254 return serialized + "\u2026";
3255 }
3256
3257 return "";
3258 }
3259
3260 function sanitize(input, sanitizeKeys) {
3261 if (
3262 !isArray(sanitizeKeys) ||
3263 (isArray(sanitizeKeys) && sanitizeKeys.length === 0)
3264 )
3265 return input;
3266
3267 var sanitizeRegExp = joinRegExp(sanitizeKeys);
3268 var sanitizeMask = "********";
3269 var safeInput;
3270
3271 try {
3272 safeInput = JSON.parse(stringify(input));
3273 } catch (o_O) {
3274 return input;
3275 }
3276
3277 function sanitizeWorker(workerInput) {
3278 if (isArray(workerInput)) {
3279 return workerInput.map(function (val) {
3280 return sanitizeWorker(val);
3281 });
3282 }
3283
3284 if (isPlainObject(workerInput)) {
3285 return Object.keys(workerInput).reduce(function (acc, k) {
3286 if (sanitizeRegExp.test(k)) {
3287 acc[k] = sanitizeMask;
3288 } else {
3289 acc[k] = sanitizeWorker(workerInput[k]);
3290 }
3291 return acc;
3292 }, {});
3293 }
3294
3295 return workerInput;
3296 }
3297
3298 return sanitizeWorker(safeInput);
3299 }
3300
2738 module.exports = {3301 module.exports = {
2739 isObject: isObject,3302 isObject: isObject,
2740 isError: isError,3303 isError: isError,
2741 isErrorEvent: isErrorEvent,3304 isErrorEvent: isErrorEvent,
3305 isDOMError: isDOMError,
3306 isDOMException: isDOMException,
2742 isUndefined: isUndefined,3307 isUndefined: isUndefined,
2743 isFunction: isFunction,3308 isFunction: isFunction,
3309 isPlainObject: isPlainObject,
2744 isString: isString,3310 isString: isString,
2745 isArray: isArray,3311 isArray: isArray,
2746 isEmptyObject: isEmptyObject,3312 isEmptyObject: isEmptyObject,
2747 supportsErrorEvent: supportsErrorEvent,3313 supportsErrorEvent: supportsErrorEvent,
3314 supportsDOMError: supportsDOMError,
3315 supportsDOMException: supportsDOMException,
3316 supportsFetch: supportsFetch,
3317 supportsReferrerPolicy: supportsReferrerPolicy,
3318 supportsPromiseRejectionEvent: supportsPromiseRejectionEvent,
2748 wrappedCallback: wrappedCallback,3319 wrappedCallback: wrappedCallback,
2749 each: each,3320 each: each,
2750 objectMerge: objectMerge,3321 objectMerge: objectMerge,
@@ -2760,6 +3331,10 @@ · 9 unmodified lines
2760 isSameStacktrace: isSameStacktrace,3331 isSameStacktrace: isSameStacktrace,
2761 parseUrl: parseUrl,3332 parseUrl: parseUrl,
2762 fill: fill,3333 fill: fill,
3334 safeJoin: safeJoin,
3335 serializeException: serializeException,
3336 serializeKeysForMessage: serializeKeysForMessage,
3337 sanitize: sanitize,
2763 };3338 };
2764 }).call(3339 }).call(
2765 this,3340 this,
@@ -2772,7 +3347,7 @@ · 6 unmodified lines
2772 : {},3347 : {},
2773 );3348 );
2774 },3349 },
2775 {},3350 { 7: 7 },
2776 ],3351 ],
2777 6: [3352 6: [
2778 function (_dereq_, module, exports) {3353 function (_dereq_, module, exports) {
@@ -2780,14 +3355,14 @@ · 1 unmodified line
2780 var utils = _dereq_(5);3355 var utils = _dereq_(5);
2781 3356
2782 /*3357 /*
2783 TraceKit - Cross brower stack traces3358 TraceKit - Cross brower stack traces
2784 3359
2785 This was originally forked from github.com/occ/TraceKit, but has since been3360 This was originally forked from github.com/occ/TraceKit, but has since been
2786 largely re-written and is now maintained as part of raven-js. Tests for3361 largely re-written and is now maintained as part of raven-js. Tests for
2787 this are in test/vendor.3362 this are in test/vendor.
2788 3363
2789 MIT license3364 MIT license
2790*/3365 */
2791 3366
2792 var TraceKit = {3367 var TraceKit = {
2793 collectWindowErrors: true,3368 collectWindowErrors: true,
@@ -2815,10 +3390,25 @@ · 21 unmodified lines
2815 function getLocationHref() {3390 function getLocationHref() {
2816 if (typeof document === "undefined" || document.location == null)3391 if (typeof document === "undefined" || document.location == null)
2817 return "";3392 return "";
2818
2819 return document.location.href;3393 return document.location.href;
2820 }3394 }
2821 3395
3396 function getLocationOrigin() {
3397 if (typeof document === "undefined" || document.location == null)
3398 return "";
3399
3400 // Oh dear IE10...
3401 if (!document.location.origin) {
3402 document.location.origin =
3403 document.location.protocol +
3404 "//" +
3405 document.location.hostname +
3406 (document.location.port ? ":" + document.location.port : "");
3407 }
3408
3409 return document.location.origin;
3410 }
3411
2822 /**3412 /**
2823 * TraceKit.report: cross-browser processing of unhandled exceptions3413 * TraceKit.report: cross-browser processing of unhandled exceptions
2824 *3414 *
@@ -2925,7 +3515,7 @@ · 100 unmodified lines
2925 /**3515 /**
2926 * Ensures all global unhandled exceptions are recorded.3516 * Ensures all global unhandled exceptions are recorded.
2927 * Supported by Gecko and IE.3517 * Supported by Gecko and IE.
2928 * @param {string} message Error message.3518 * @param {string} msg Error message.
2929 * @param {string} url URL of script that generated the exception.3519 * @param {string} url URL of script that generated the exception.
2930 * @param {(number|string)} lineNo The line number at which the error3520 * @param {(number|string)} lineNo The line number at which the error
2931 * occurred.3521 * occurred.
@@ -2933,8 +3523,12 @@ · 1 unmodified line
2933 * occurred.3523 * occurred.
2934 * @param {?Error} ex The actual Error object.3524 * @param {?Error} ex The actual Error object.
2935 */3525 */
2936 function traceKitWindowOnError(message, url, lineNo, colNo, ex) {3526 function traceKitWindowOnError(msg, url, lineNo, colNo, ex) {
2937 var stack = null;3527 var stack = null;
3528 // If 'ex' is ErrorEvent, get real Error from inside
3529 var exception = utils.isErrorEvent(ex) ? ex.error : ex;
3530 // If 'msg' is ErrorEvent, get real message from inside
3531 var message = utils.isErrorEvent(msg) ? msg.message : msg;
2938 3532
2939 if (lastExceptionStack) {3533 if (lastExceptionStack) {
2940 TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(3534 TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(
@@ -2944,13 +3538,13 @@ · 3 unmodified lines
2944 message,3538 message,
2945 );3539 );
2946 processLastException();3540 processLastException();
2947 } else if (ex && utils.isError(ex)) {3541 } else if (exception && utils.isError(exception)) {
2948 // non-string `ex` arg; attempt to extract stack trace3542 // non-string `exception` arg; attempt to extract stack trace
2949 3543
2950 // New chrome and blink send along a real error object3544 // New chrome and blink send along a real error object
2951 // Let's just report that like a normal error.3545 // Let's just report that like a normal error.
2952 // See: https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror3546 // See: https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror
2953 stack = TraceKit.computeStackTrace(ex);3547 stack = TraceKit.computeStackTrace(exception);
2954 notifyHandlers(stack, true);3548 notifyHandlers(stack, true);
2955 } else {3549 } else {
2956 var location = {3550 var location = {
@@ -2960,13 +3554,13 @@ · 3 unmodified lines
2960 };3554 };
2961 3555
2962 var name = undefined;3556 var name = undefined;
2963 var msg = message; // must be new var or will modify original `arguments`
2964 var groups;3557 var groups;
3558
2965 if ({}.toString.call(message) === "[object String]") {3559 if ({}.toString.call(message) === "[object String]") {
2966 var groups = message.match(ERROR_TYPES_RE);3560 var groups = message.match(ERROR_TYPES_RE);
2967 if (groups) {3561 if (groups) {
2968 name = groups[1];3562 name = groups[1];
2969 msg = groups[2];3563 message = groups[2];
2970 }3564 }
2971 }3565 }
2972 3566
@@ -2974,7 +3568,7 @@ · 1 unmodified line
2974 3568
2975 stack = {3569 stack = {
2976 name: name,3570 name: name,
2977 message: msg,3571 message: message,
2978 url: getLocationHref(),3572 url: getLocationHref(),
2979 stack: [location],3573 stack: [location],
2980 };3574 };
@@ -3163,20 +3757,22 @@ · 182 unmodified lines
3163 if (typeof ex.stack === "undefined" || !ex.stack) return;3757 if (typeof ex.stack === "undefined" || !ex.stack) return;
3164 3758
3165 var chrome =3759 var chrome =
3166 /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack|<anonymous>|[a-z]:|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,3760 /^\s*at (?:(.*?) ?\()?((?:file|https?|blob|chrome-extension|native|eval|webpack|<anonymous>|[a-z]:|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;
3167 gecko =3761 var winjs =
3168 /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i,3762 /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx(?:-web)|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i;
3169 winjs =3763 // NOTE: blob urls are now supposed to always have an origin, therefore it's format
3170 /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i,3764 // which is `blob:http://url/path/with-some-uuid`, is matched by `blob.*?:\/` as well
3171 // Used to additionally parse URL/line/column from eval frames3765 var gecko =
3172 geckoEval = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i,3766 /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|moz-extension).*?:\/.*?|\[native code\]|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i;
3173 chromeEval = /\((\S*)(?::(\d+))(?::(\d+))\)/,3767 // Used to additionally parse URL/line/column from eval frames
3174 lines = ex.stack.split("\n"),3768 var geckoEval = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i;
3175 stack = [],3769 var chromeEval = /\((\S*)(?::(\d+))(?::(\d+))\)/;
3176 submatch,3770 var lines = ex.stack.split("\n");
3177 parts,3771 var stack = [];
3178 element,3772 var submatch;
3179 reference = /^(.*) is undefined$/.exec(ex.message);3773 var parts;
3774 var element;
3775 var reference = /^(.*) is undefined$/.exec(ex.message);
3180 3776
3181 for (var i = 0, j = lines.length; i < j; ++i) {3777 for (var i = 0, j = lines.length; i < j; ++i) {
3182 if ((parts = chrome.exec(lines[i]))) {3778 if ((parts = chrome.exec(lines[i]))) {
@@ -3236,6 +3832,47 @@ · 53 unmodified lines
3236 element.func = UNKNOWN_FUNCTION;3832 element.func = UNKNOWN_FUNCTION;
3237 }3833 }
3238 3834
3835 if (element.url && element.url.substr(0, 5) === "blob:") {
3836 // Special case for handling JavaScript loaded into a blob.
3837 // We use a synchronous AJAX request here as a blob is already in
3838 // memory - it's not making a network request. This will generate a warning
3839 // in the browser console, but there has already been an error so that's not
3840 // that much of an issue.
3841 var xhr = new XMLHttpRequest();
3842 xhr.open("GET", element.url, false);
3843 xhr.send(null);
3844
3845 // If we failed to download the source, skip this patch
3846 if (xhr.status === 200) {
3847 var source = xhr.responseText || "";
3848
3849 // We trim the source down to the last 300 characters as sourceMappingURL is always at the end of the file.
3850 // Why 300? To be in line with: https://github.com/getsentry/sentry/blob/4af29e8f2350e20c28a6933354e4f42437b4ba42/src/sentry/lang/javascript/processor.py#L164-L175
3851 source = source.slice(-300);
3852
3853 // Now we dig out the source map URL
3854 var sourceMaps = source.match(
3855 /\/\/# sourceMappingURL=(.*)$/,
3856 );
3857
3858 // If we don't find a source map comment or we find more than one, continue on to the next element.
3859 if (sourceMaps) {
3860 var sourceMapAddress = sourceMaps[1];
3861
3862 // Now we check to see if it's a relative URL.
3863 // If it is, convert it to an absolute one.
3864 if (sourceMapAddress.charAt(0) === "~") {
3865 sourceMapAddress =
3866 getLocationOrigin() + sourceMapAddress.slice(1);
3867 }
3868
3869 // Now we strip the '.map' off of the end of the URL and update the
3870 // element so that Sentry can match the map to the blob.
3871 element.url = sourceMapAddress.slice(0, -4);
3872 }
3873 }
3874 }
3875
3239 stack.push(element);3876 stack.push(element);
3240 }3877 }
3241 3878
@@ -3449,15 +4086,15 @@ · 207 unmodified lines
3449 7: [4086 7: [
3450 function (_dereq_, module, exports) {4087 function (_dereq_, module, exports) {
3451 /*4088 /*
3452 json-stringify-safe4089 json-stringify-safe
3453 Like JSON.stringify, but doesn't throw on circular references.4090 Like JSON.stringify, but doesn't throw on circular references.
3454 4091
3455 Originally forked from https://github.com/isaacs/json-stringify-safe4092 Originally forked from https://github.com/isaacs/json-stringify-safe
3456 version 5.0.1 on 3/8/2017 and modified to handle Errors serialization4093 version 5.0.1 on 3/8/2017 and modified to handle Errors serialization
3457 and IE8 compatibility. Tests for this are in test/vendor.4094 and IE8 compatibility. Tests for this are in test/vendor.
3458 4095
3459 ISC license: https://github.com/isaacs/json-stringify-safe/blob/master/LICENSE4096 ISC license: https://github.com/isaacs/json-stringify-safe/blob/master/LICENSE
3460*/4097 */
3461 4098
3462 exports = module.exports = stringify;4099 exports = module.exports = stringify;
3463 exports.getSerialize = serializer;4100 exports.getSerialize = serializer;
@@ -3535,6 +4172,281 @@ · 71 unmodified lines
3535 },4172 },
3536 {},4173 {},
3537 ],4174 ],
4175 8: [
4176 function (_dereq_, module, exports) {
4177 /*
4178 * JavaScript MD5
4179 * https://github.com/blueimp/JavaScript-MD5
4180 *
4181 * Copyright 2011, Sebastian Tschan
4182 * https://blueimp.net
4183 *
4184 * Licensed under the MIT license:
4185 * https://opensource.org/licenses/MIT
4186 *
4187 * Based on
4188 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
4189 * Digest Algorithm, as defined in RFC 1321.
4190 * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
4191 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
4192 * Distributed under the BSD License
4193 * See http://pajhome.org.uk/crypt/md5 for more info.
4194 */
4195
4196 /*
4197 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
4198 * to work around bugs in some JS interpreters.
4199 */
4200 function safeAdd(x, y) {
4201 var lsw = (x & 0xffff) + (y & 0xffff);
4202 var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
4203 return (msw << 16) | (lsw & 0xffff);
4204 }
4205
4206 /*
4207 * Bitwise rotate a 32-bit number to the left.
4208 */
4209 function bitRotateLeft(num, cnt) {
4210 return (num << cnt) | (num >>> (32 - cnt));
4211 }
4212
4213 /*
4214 * These functions implement the four basic operations the algorithm uses.
4215 */
4216 function md5cmn(q, a, b, x, s, t) {
4217 return safeAdd(
4218 bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s),
4219 b,
4220 );
4221 }
4222 function md5ff(a, b, c, d, x, s, t) {
4223 return md5cmn((b & c) | (~b & d), a, b, x, s, t);
4224 }
4225 function md5gg(a, b, c, d, x, s, t) {
4226 return md5cmn((b & d) | (c & ~d), a, b, x, s, t);
4227 }
4228 function md5hh(a, b, c, d, x, s, t) {
4229 return md5cmn(b ^ c ^ d, a, b, x, s, t);
4230 }
4231 function md5ii(a, b, c, d, x, s, t) {
4232 return md5cmn(c ^ (b | ~d), a, b, x, s, t);
4233 }
4234
4235 /*
4236 * Calculate the MD5 of an array of little-endian words, and a bit length.
4237 */
4238 function binlMD5(x, len) {
4239 /* append padding */
4240 x[len >> 5] |= 0x80 << len % 32;
4241 x[(((len + 64) >>> 9) << 4) + 14] = len;
4242
4243 var i;
4244 var olda;
4245 var oldb;
4246 var oldc;
4247 var oldd;
4248 var a = 1732584193;
4249 var b = -271733879;
4250 var c = -1732584194;
4251 var d = 271733878;
4252
4253 for (i = 0; i < x.length; i += 16) {
4254 olda = a;
4255 oldb = b;
4256 oldc = c;
4257 oldd = d;
4258
4259 a = md5ff(a, b, c, d, x[i], 7, -680876936);
4260 d = md5ff(d, a, b, c, x[i + 1], 12, -389564586);
4261 c = md5ff(c, d, a, b, x[i + 2], 17, 606105819);
4262 b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330);
4263 a = md5ff(a, b, c, d, x[i + 4], 7, -176418897);
4264 d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426);
4265 c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341);
4266 b = md5ff(b, c, d, a, x[i + 7], 22, -45705983);
4267 a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416);
4268 d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417);
4269 c = md5ff(c, d, a, b, x[i + 10], 17, -42063);
4270 b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162);
4271 a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682);
4272 d = md5ff(d, a, b, c, x[i + 13], 12, -40341101);
4273 c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290);
4274 b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329);
4275
4276 a = md5gg(a, b, c, d, x[i + 1], 5, -165796510);
4277 d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632);
4278 c = md5gg(c, d, a, b, x[i + 11], 14, 643717713);
4279 b = md5gg(b, c, d, a, x[i], 20, -373897302);
4280 a = md5gg(a, b, c, d, x[i + 5], 5, -701558691);
4281 d = md5gg(d, a, b, c, x[i + 10], 9, 38016083);
4282 c = md5gg(c, d, a, b, x[i + 15], 14, -660478335);
4283 b = md5gg(b, c, d, a, x[i + 4], 20, -405537848);
4284 a = md5gg(a, b, c, d, x[i + 9], 5, 568446438);
4285 d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690);
4286 c = md5gg(c, d, a, b, x[i + 3], 14, -187363961);
4287 b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501);
4288 a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467);
4289 d = md5gg(d, a, b, c, x[i + 2], 9, -51403784);
4290 c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473);
4291 b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734);
4292
4293 a = md5hh(a, b, c, d, x[i + 5], 4, -378558);
4294 d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463);
4295 c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562);
4296 b = md5hh(b, c, d, a, x[i + 14], 23, -35309556);
4297 a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060);
4298 d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353);
4299 c = md5hh(c, d, a, b, x[i + 7], 16, -155497632);
4300 b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640);
4301 a = md5hh(a, b, c, d, x[i + 13], 4, 681279174);
4302 d = md5hh(d, a, b, c, x[i], 11, -358537222);
4303 c = md5hh(c, d, a, b, x[i + 3], 16, -722521979);
4304 b = md5hh(b, c, d, a, x[i + 6], 23, 76029189);
4305 a = md5hh(a, b, c, d, x[i + 9], 4, -640364487);
4306 d = md5hh(d, a, b, c, x[i + 12], 11, -421815835);
4307 c = md5hh(c, d, a, b, x[i + 15], 16, 530742520);
4308 b = md5hh(b, c, d, a, x[i + 2], 23, -995338651);
4309
4310 a = md5ii(a, b, c, d, x[i], 6, -198630844);
4311 d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415);
4312 c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905);
4313 b = md5ii(b, c, d, a, x[i + 5], 21, -57434055);
4314 a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571);
4315 d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606);
4316 c = md5ii(c, d, a, b, x[i + 10], 15, -1051523);
4317 b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799);
4318 a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359);
4319 d = md5ii(d, a, b, c, x[i + 15], 10, -30611744);
4320 c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380);
4321 b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649);
4322 a = md5ii(a, b, c, d, x[i + 4], 6, -145523070);
4323 d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379);
4324 c = md5ii(c, d, a, b, x[i + 2], 15, 718787259);
4325 b = md5ii(b, c, d, a, x[i + 9], 21, -343485551);
4326
4327 a = safeAdd(a, olda);
4328 b = safeAdd(b, oldb);
4329 c = safeAdd(c, oldc);
4330 d = safeAdd(d, oldd);
4331 }
4332 return [a, b, c, d];
4333 }
4334
4335 /*
4336 * Convert an array of little-endian words to a string
4337 */
4338 function binl2rstr(input) {
4339 var i;
4340 var output = "";
4341 var length32 = input.length * 32;
4342 for (i = 0; i < length32; i += 8) {
4343 output += String.fromCharCode((input[i >> 5] >>> i % 32) & 0xff);
4344 }
4345 return output;
4346 }
4347
4348 /*
4349 * Convert a raw string to an array of little-endian words
4350 * Characters >255 have their high-byte silently ignored.
4351 */
4352 function rstr2binl(input) {
4353 var i;
4354 var output = [];
4355 output[(input.length >> 2) - 1] = undefined;
4356 for (i = 0; i < output.length; i += 1) {
4357 output[i] = 0;
4358 }
4359 var length8 = input.length * 8;
4360 for (i = 0; i < length8; i += 8) {
4361 output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << i % 32;
4362 }
4363 return output;
4364 }
4365
4366 /*
4367 * Calculate the MD5 of a raw string
4368 */
4369 function rstrMD5(s) {
4370 return binl2rstr(binlMD5(rstr2binl(s), s.length * 8));
4371 }
4372
4373 /*
4374 * Calculate the HMAC-MD5, of a key and some data (raw strings)
4375 */
4376 function rstrHMACMD5(key, data) {
4377 var i;
4378 var bkey = rstr2binl(key);
4379 var ipad = [];
4380 var opad = [];
4381 var hash;
4382 ipad[15] = opad[15] = undefined;
4383 if (bkey.length > 16) {
4384 bkey = binlMD5(bkey, key.length * 8);
4385 }
4386 for (i = 0; i < 16; i += 1) {
4387 ipad[i] = bkey[i] ^ 0x36363636;
4388 opad[i] = bkey[i] ^ 0x5c5c5c5c;
4389 }
4390 hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
4391 return binl2rstr(binlMD5(opad.concat(hash), 512 + 128));
4392 }
4393
4394 /*
4395 * Convert a raw string to a hex string
4396 */
4397 function rstr2hex(input) {
4398 var hexTab = "0123456789abcdef";
4399 var output = "";
4400 var x;
4401 var i;
4402 for (i = 0; i < input.length; i += 1) {
4403 x = input.charCodeAt(i);
4404 output +=
4405 hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f);
4406 }
4407 return output;
4408 }
4409
4410 /*
4411 * Encode a string as utf-8
4412 */
4413 function str2rstrUTF8(input) {
4414 return unescape(encodeURIComponent(input));
4415 }
4416
4417 /*
4418 * Take string arguments and return either raw or hex encoded strings
4419 */
4420 function rawMD5(s) {
4421 return rstrMD5(str2rstrUTF8(s));
4422 }
4423 function hexMD5(s) {
4424 return rstr2hex(rawMD5(s));
4425 }
4426 function rawHMACMD5(k, d) {
4427 return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d));
4428 }
4429 function hexHMACMD5(k, d) {
4430 return rstr2hex(rawHMACMD5(k, d));
4431 }
4432
4433 function md5(string, key, raw) {
4434 if (!key) {
4435 if (!raw) {
4436 return hexMD5(string);
4437 }
4438 return rawMD5(string);
4439 }
4440 if (!raw) {
4441 return hexHMACMD5(key, string);
4442 }
4443 return rawHMACMD5(key, string);
4444 }
4445
4446 module.exports = md5;
4447 },
4448 {},
4449 ],
3538 },4450 },
3539 {},4451 {},
3540 [4],4452 [4],