diff --git a/assets/javascripts/vendor/raven.js b/assets/javascripts/vendor/raven.js
index a4d42a9b..7ed8e6bd 100644
--- a/assets/javascripts/vendor/raven.js
+++ b/assets/javascripts/vendor/raven.js
@@ -1,10 +1,10 @@
-/*! Raven.js 3.20.1 (42adaf5) | github.com/getsentry/raven-js */
+/*! Raven.js 3.25.2 (30b6d4e) | github.com/getsentry/raven-js */
/*
* Includes TraceKit
* https://github.com/getsentry/TraceKit
*
- * Copyright 2017 Matt Robenolt and other contributors
+ * Copyright 2018 Matt Robenolt and other contributors
* Released under the BSD license
* https://github.com/getsentry/raven-js/blob/master/LICENSE
*
@@ -77,6 +77,8 @@
],
2: [
function (_dereq_, module, exports) {
+ var utils = _dereq_(5);
+
var wrapMethod = function (console, level, callback) {
var originalConsoleLevel = console[level];
var originalConsole = console;
@@ -90,7 +92,7 @@
console[level] = function () {
var args = [].slice.call(arguments);
- var msg = "" + args.join(" ");
+ var msg = utils.safeJoin(args, " ");
var data = {
level: sentryLevel,
logger: "console",
@@ -102,7 +104,7 @@
// Default browsers message
msg =
"Assertion failed: " +
- (args.slice(1).join(" ") || "console.assert");
+ (utils.safeJoin(args.slice(1), " ") || "console.assert");
data.extra.arguments = args.slice(1);
callback && callback(msg, data);
}
@@ -127,7 +129,7 @@
wrapMethod: wrapMethod,
};
},
- {},
+ { 5: 5 },
],
3: [
function (_dereq_, module, exports) {
@@ -136,12 +138,16 @@
var TraceKit = _dereq_(6);
var stringify = _dereq_(7);
+ var md5 = _dereq_(8);
var RavenConfigError = _dereq_(1);
var utils = _dereq_(5);
+ var isErrorEvent = utils.isErrorEvent;
+ var isDOMError = utils.isDOMError;
+ var isDOMException = utils.isDOMException;
var isError = utils.isError;
var isObject = utils.isObject;
- var isErrorEvent = utils.isErrorEvent;
+ var isPlainObject = utils.isPlainObject;
var isUndefined = utils.isUndefined;
var isFunction = utils.isFunction;
var isString = utils.isString;
@@ -160,6 +166,11 @@
var isSameStacktrace = utils.isSameStacktrace;
var parseUrl = utils.parseUrl;
var fill = utils.fill;
+ var supportsFetch = utils.supportsFetch;
+ var supportsReferrerPolicy = utils.supportsReferrerPolicy;
+ var serializeKeysForMessage = utils.serializeKeysForMessage;
+ var serializeException = utils.serializeException;
+ var sanitize = utils.sanitize;
var wrapConsoleMethod = _dereq_(2).wrapMethod;
@@ -207,20 +218,33 @@
this._globalProject = null;
this._globalContext = {};
this._globalOptions = {
+ // SENTRY_RELEASE can be injected by https://github.com/getsentry/sentry-webpack-plugin
+ release: _window.SENTRY_RELEASE && _window.SENTRY_RELEASE.id,
logger: "javascript",
ignoreErrors: [],
ignoreUrls: [],
whitelistUrls: [],
includePaths: [],
+ headers: null,
collectWindowErrors: true,
+ captureUnhandledRejections: true,
maxMessageLength: 0,
-
// By default, truncates URL values to 250 chars
maxUrlLength: 250,
stackTraceLimit: 50,
autoBreadcrumbs: true,
instrument: true,
sampleRate: 1,
+ sanitizeKeys: [],
+ };
+ this._fetchDefaults = {
+ method: "POST",
+ keepalive: true,
+ // Despite all stars in the sky saying that Edge supports old draft syntax, aka 'never', 'always', 'origin' and 'default
+ // https://caniuse.com/#feat=referrer-policy
+ // It doesn't. And it throw exception instead of ignoring this parameter...
+ // REF: https://github.com/getsentry/raven-js/issues/1233
+ referrerPolicy: supportsReferrerPolicy() ? "origin" : "",
};
this._ignoreOnError = 0;
this._isRavenInstalled = false;
@@ -257,7 +281,7 @@
// webpack (using a build step causes webpack #1617). Grunt verifies that
// this value matches package.json during build.
// See: https://github.com/getsentry/raven-js/issues/465
- VERSION: "3.20.1",
+ VERSION: "3.25.2",
debug: false,
@@ -376,6 +400,10 @@
self._handleOnErrorStackInfo.apply(self, arguments);
});
+ if (self._globalOptions.captureUnhandledRejections) {
+ self._attachPromiseRejectionHandler();
+ }
+
self._patchFunctionToString();
if (
@@ -538,7 +566,7 @@
return wrapped;
},
- /*
+ /**
* Uninstalls the global error handler.
*
* @return {Raven}
@@ -546,8 +574,10 @@
uninstall: function () {
TraceKit.report.uninstall();
+ this._detachPromiseRejectionHandler();
this._unpatchFunctionToString();
this._restoreBuiltIns();
+ this._restoreConsole();
Error.stackTraceLimit = this._originalErrorStackTraceLimit;
this._isRavenInstalled = false;
@@ -555,7 +585,58 @@
return this;
},
- /*
+ /**
+ * Callback used for `unhandledrejection` event
+ *
+ * @param {PromiseRejectionEvent} event An object containing
+ * promise: the Promise that was rejected
+ * reason: the value with which the Promise was rejected
+ * @return void
+ */
+ _promiseRejectionHandler: function (event) {
+ this._logDebug(
+ "debug",
+ "Raven caught unhandled promise rejection:",
+ event,
+ );
+ this.captureException(event.reason, {
+ extra: {
+ unhandledPromiseRejection: true,
+ },
+ });
+ },
+
+ /**
+ * Installs the global promise rejection handler.
+ *
+ * @return {raven}
+ */
+ _attachPromiseRejectionHandler: function () {
+ this._promiseRejectionHandler =
+ this._promiseRejectionHandler.bind(this);
+ _window.addEventListener &&
+ _window.addEventListener(
+ "unhandledrejection",
+ this._promiseRejectionHandler,
+ );
+ return this;
+ },
+
+ /**
+ * Uninstalls the global promise rejection handler.
+ *
+ * @return {raven}
+ */
+ _detachPromiseRejectionHandler: function () {
+ _window.removeEventListener &&
+ _window.removeEventListener(
+ "unhandledrejection",
+ this._promiseRejectionHandler,
+ );
+ return this;
+ },
+
+ /**
* Manually capture an exception and send it over to Sentry
*
* @param {error} ex An exception to be logged
@@ -563,30 +644,60 @@
* @return {Raven}
*/
captureException: function (ex, options) {
- // Cases for sending ex as a message, rather than an exception
- var isNotError = !isError(ex);
- var isNotErrorEvent = !isErrorEvent(ex);
- var isErrorEventWithoutError = isErrorEvent(ex) && !ex.error;
+ options = objectMerge(
+ { trimHeadFrames: 0 },
+ options ? options : {},
+ );
- if (
- (isNotError && isNotErrorEvent) ||
- isErrorEventWithoutError
- ) {
+ if (isErrorEvent(ex) && ex.error) {
+ // If it is an ErrorEvent with `error` property, extract it to get actual Error
+ ex = ex.error;
+ } else if (isDOMError(ex) || isDOMException(ex)) {
+ // If it is a DOMError or DOMException (which are legacy APIs, but still supported in some browsers)
+ // then we just extract the name and message, as they don't provide anything else
+ // https://developer.mozilla.org/en-US/docs/Web/API/DOMError
+ // https://developer.mozilla.org/en-US/docs/Web/API/DOMException
+ var name =
+ ex.name || (isDOMError(ex) ? "DOMError" : "DOMException");
+ var message = ex.message ? name + ": " + ex.message : name;
+
+ return this.captureMessage(
+ message,
+ objectMerge(options, {
+ // neither DOMError or DOMException provide stack trace and we most likely wont get it this way as well
+ // but it's barely any overhead so we may at least try
+ stacktrace: true,
+ trimHeadFrames: options.trimHeadFrames + 1,
+ }),
+ );
+ } else if (isError(ex)) {
+ // we have a real Error object
+ ex = ex;
+ } else if (isPlainObject(ex)) {
+ // If it is plain Object, serialize it manually and extract options
+ // This will allow us to group events based on top-level keys
+ // which is much better than creating new group when any key/value change
+ options = this._getCaptureExceptionOptionsFromPlainObject(
+ options,
+ ex,
+ );
+ ex = new Error(options.message);
+ } else {
+ // If none of previous checks were valid, then it means that
+ // it's not a DOMError/DOMException
+ // it's not a plain Object
+ // it's not a valid ErrorEvent (one with an error property)
+ // it's not an Error
+ // So bail out and capture it as a simple message:
return this.captureMessage(
ex,
- objectMerge(
- {
- trimHeadFrames: 1,
- stacktrace: true, // if we fall back to captureMessage, default to attempting a new trace
- },
- options,
- ),
+ objectMerge(options, {
+ stacktrace: true, // if we fall back to captureMessage, default to attempting a new trace
+ trimHeadFrames: options.trimHeadFrames + 1,
+ }),
);
}
- // Get actual Error from ErrorEvent
- if (isErrorEvent(ex)) ex = ex.error;
-
// Store the raw exception object for potential debugging and introspection
this._lastCapturedException = ex;
@@ -607,6 +718,23 @@
return this;
},
+ _getCaptureExceptionOptionsFromPlainObject: function (
+ currentOptions,
+ ex,
+ ) {
+ var exKeys = Object.keys(ex).sort();
+ var options = objectMerge(currentOptions, {
+ message:
+ "Non-Error exception captured with keys: " +
+ serializeKeysForMessage(exKeys),
+ fingerprint: [md5(exKeys)],
+ extra: currentOptions.extra || {},
+ });
+ options.extra.__serialized__ = serializeException(ex);
+
+ return options;
+ },
+
/*
* Manually send a message to Sentry
*
@@ -626,10 +754,11 @@
}
options = options || {};
+ msg = msg + ""; // Make sure it's actually a string
var data = objectMerge(
{
- message: msg + "", // Make sure it's actually a string
+ message: msg,
},
options,
);
@@ -651,6 +780,17 @@
// stack[0] is `throw new Error(msg)` call itself, we are interested in the frame that was just before that, stack[1]
var initialCall = isArray(stack.stack) && stack.stack[1];
+
+ // if stack[1] is `Raven.captureException`, it means that someone passed a string to it and we redirected that call
+ // to be handled by `captureMessage`, thus `initialCall` is the 3rd one, not 2nd
+ // initialCall => captureException(string) => captureMessage(string)
+ if (
+ initialCall &&
+ initialCall.func === "Raven.captureException"
+ ) {
+ initialCall = stack.stack[2];
+ }
+
var fileurl = (initialCall && initialCall.url) || "";
if (
@@ -671,18 +811,21 @@
this._globalOptions.stacktrace ||
(options && options.stacktrace)
) {
+ // fingerprint on msg, not stack trace (legacy behavior, could be revisited)
+ data.fingerprint =
+ data.fingerprint == null ? msg : data.fingerprint;
+
options = objectMerge(
{
- // fingerprint on msg, not stack trace (legacy behavior, could be
- // revisited)
- fingerprint: msg,
- // since we know this is a synthetic trace, the top N-most frames
- // MUST be from Raven.js, so mark them as in_app later by setting
- // trimHeadFrames
- trimHeadFrames: (options.trimHeadFrames || 0) + 1,
+ trimHeadFrames: 0,
},
options,
);
+ // Since we know this is a synthetic trace, the top frame (this function call)
+ // MUST be from Raven.js, so mark it for trimming
+ // We add to the trim counter so that callers can choose to trim extra frames, such
+ // as utility functions.
+ options.trimHeadFrames += 1;
var frames = this._prepareFrames(stack, options);
data.stacktrace = {
@@ -691,6 +834,13 @@
};
}
+ // Make sure that fingerprint is always wrapped in an array
+ if (data.fingerprint) {
+ data.fingerprint = isArray(data.fingerprint)
+ ? data.fingerprint
+ : [data.fingerprint];
+ }
+
// Fire away!
this._send(data);
@@ -976,7 +1126,7 @@
},
_triggerEvent: function (eventType, options) {
- // NOTE: `event` is a native browser thing, so let's avoid conflicting with it
+ // NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it
var evt, key;
if (!this._hasDocument) return;
@@ -1290,7 +1440,7 @@
fill(_window, "setTimeout", wrapTimeFn, wrappedBuiltIns);
fill(_window, "setInterval", wrapTimeFn, wrappedBuiltIns);
- if (_requestAnimationFrame) {
+ if (_window.requestAnimationFrame) {
fill(
_window,
"requestAnimationFrame",
@@ -1365,7 +1515,8 @@
}
if (autoBreadcrumbs.xhr && "XMLHttpRequest" in _window) {
- var xhrproto = XMLHttpRequest.prototype;
+ var xhrproto =
+ _window.XMLHttpRequest && _window.XMLHttpRequest.prototype;
fill(
xhrproto,
"open",
@@ -1395,7 +1546,7 @@
xhrproto,
"send",
function (origSend) {
- return function (data) {
+ return function () {
// preserve arity
var xhr = this;
@@ -1450,12 +1601,12 @@
);
}
- if (autoBreadcrumbs.xhr && "fetch" in _window) {
+ if (autoBreadcrumbs.xhr && supportsFetch()) {
fill(
_window,
"fetch",
function (origFetch) {
- return function (fn, t) {
+ return function () {
// preserve arity
// Make a copy of the arguments to prevent deoptimization
// https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments
@@ -1482,6 +1633,11 @@
url = "" + fetchInput;
}
+ // if Sentry key appears in URL, don't capture, as it's our own request
+ if (url.indexOf(self._globalKey) !== -1) {
+ return origFetch.apply(this, args);
+ }
+
if (args[1] && args[1].method) {
method = args[1].method;
}
@@ -1492,18 +1648,29 @@
status_code: null,
};
- self.captureBreadcrumb({
- type: "http",
- category: "fetch",
- data: fetchData,
- });
-
return origFetch
.apply(this, args)
.then(function (response) {
fetchData.status_code = response.status;
+ self.captureBreadcrumb({
+ type: "http",
+ category: "fetch",
+ data: fetchData,
+ });
+
return response;
+ })
+ ["catch"](function (err) {
+ // if there is an error performing the request
+ self.captureBreadcrumb({
+ type: "http",
+ category: "fetch",
+ data: fetchData,
+ level: "error",
+ });
+
+ throw err;
});
};
},
@@ -1525,7 +1692,7 @@
self._keypressEventHandler(),
false,
);
- } else {
+ } else if (_document.attachEvent) {
// IE8 Compatibility
_document.attachEvent(
"onclick",
@@ -1548,8 +1715,8 @@
var hasPushAndReplaceState =
!isChromePackagedApp &&
_window.history &&
- history.pushState &&
- history.replaceState;
+ _window.history.pushState &&
+ _window.history.replaceState;
if (autoBreadcrumbs.location && hasPushAndReplaceState) {
// TODO: remove onpopstate handler on uninstall()
var oldOnPopState = _window.onpopstate;
@@ -1579,13 +1746,13 @@
};
fill(
- history,
+ _window.history,
"pushState",
historyReplacementFunction,
wrappedBuiltIns,
);
fill(
- history,
+ _window.history,
"replaceState",
historyReplacementFunction,
wrappedBuiltIns,
@@ -1629,6 +1796,14 @@
}
},
+ _restoreConsole: function () {
+ // eslint-disable-next-line guard-for-in
+ for (var method in this._originalConsoleMethods) {
+ this._originalConsole[method] =
+ this._originalConsoleMethods[method];
+ }
+ },
+
_drainPlugins: function () {
var self = this;
@@ -1822,7 +1997,7 @@
},
],
},
- culprit: fileurl,
+ transaction: fileurl,
},
options,
);
@@ -1905,18 +2080,18 @@
if (this._hasNavigator && _navigator.userAgent) {
httpData.headers = {
- "User-Agent": navigator.userAgent,
+ "User-Agent": _navigator.userAgent,
};
}
- if (this._hasDocument) {
- if (_document.location && _document.location.href) {
- httpData.url = _document.location.href;
- }
- if (_document.referrer) {
- if (!httpData.headers) httpData.headers = {};
- httpData.headers.Referer = _document.referrer;
- }
+ // Check in `window` instead of `document`, as we may be in ServiceWorker environment
+ if (_window.location && _window.location.href) {
+ httpData.url = _window.location.href;
+ }
+
+ if (this._hasDocument && _document.referrer) {
+ if (!httpData.headers) httpData.headers = {};
+ httpData.headers.Referer = _document.referrer;
}
return httpData;
@@ -1949,7 +2124,7 @@
if (
!last ||
current.message !== last.message || // defined for captureMessage
- current.culprit !== last.culprit // defined for captureException/onerror
+ current.transaction !== last.transaction // defined for captureException/onerror
)
return false;
@@ -1982,8 +2157,14 @@
try {
// If Retry-After is not in Access-Control-Expose-Headers, most
// browsers will throw an exception trying to access it
- retry = request.getResponseHeader("Retry-After");
- retry = parseInt(retry, 10) * 1000; // Retry-After is returned in seconds
+ if (supportsFetch()) {
+ retry = request.headers.get("Retry-After");
+ } else {
+ retry = request.getResponseHeader("Retry-After");
+ }
+
+ // Retry-After is returned in seconds
+ retry = parseInt(retry, 10) * 1000;
} catch (e) {
/* eslint no-empty:0 */
}
@@ -2037,9 +2218,6 @@
};
}
- // If there are no tags/extra, strip the key from the payload alltogther.
- if (isEmptyObject(data.tags)) delete data.tags;
-
if (this._globalContext.user) {
// sentry.interfaces.User
data.user = this._globalContext.user;
@@ -2056,6 +2234,19 @@
if (globalOptions.serverName)
data.server_name = globalOptions.serverName;
+ data = this._sanitizeData(data);
+
+ // Cleanup empty properties before sending them to the server
+ Object.keys(data).forEach(function (key) {
+ if (
+ data[key] == null ||
+ data[key] === "" ||
+ isEmptyObject(data[key])
+ ) {
+ delete data[key];
+ }
+ });
+
if (isFunction(globalOptions.dataCallback)) {
data = globalOptions.dataCallback(data) || data;
}
@@ -2093,6 +2284,10 @@
}
},
+ _sanitizeData: function (data) {
+ return sanitize(data, this._globalOptions.sanitizeKeys);
+ },
+
_getUuid: function () {
return uuid4();
},
@@ -2197,6 +2392,61 @@
},
_makeRequest: function (opts) {
+ // Auth is intentionally sent as part of query string (NOT as custom HTTP header) to avoid preflight CORS requests
+ var url = opts.url + "?" + urlencode(opts.auth);
+
+ var evaluatedHeaders = null;
+ var evaluatedFetchParameters = {};
+
+ if (opts.options.headers) {
+ evaluatedHeaders = this._evaluateHash(opts.options.headers);
+ }
+
+ if (opts.options.fetchParameters) {
+ evaluatedFetchParameters = this._evaluateHash(
+ opts.options.fetchParameters,
+ );
+ }
+
+ if (supportsFetch()) {
+ evaluatedFetchParameters.body = stringify(opts.data);
+
+ var defaultFetchOptions = objectMerge(
+ {},
+ this._fetchDefaults,
+ );
+ var fetchOptions = objectMerge(
+ defaultFetchOptions,
+ evaluatedFetchParameters,
+ );
+
+ if (evaluatedHeaders) {
+ fetchOptions.headers = evaluatedHeaders;
+ }
+
+ return _window
+ .fetch(url, fetchOptions)
+ .then(function (response) {
+ if (response.ok) {
+ opts.onSuccess && opts.onSuccess();
+ } else {
+ var error = new Error(
+ "Sentry error code: " + response.status,
+ );
+ // It's called request only to keep compatibility with XHR interface
+ // and not add more redundant checks in setBackoffState method
+ error.request = response;
+ opts.onError && opts.onError(error);
+ }
+ })
+ ["catch"](function () {
+ opts.onError &&
+ opts.onError(
+ new Error("Sentry error code: network unavailable"),
+ );
+ });
+ }
+
var request =
_window.XMLHttpRequest && new _window.XMLHttpRequest();
if (!request) return;
@@ -2208,8 +2458,6 @@
if (!hasCORS) return;
- var url = opts.url;
-
if ("withCredentials" in request) {
request.onreadystatechange = function () {
if (request.readyState !== 4) {
@@ -2243,14 +2491,37 @@
}
}
- // NOTE: auth is intentionally sent as part of query string (NOT as custom
- // HTTP header) so as to avoid preflight CORS requests
- request.open("POST", url + "?" + urlencode(opts.auth));
+ request.open("POST", url);
+
+ if (evaluatedHeaders) {
+ each(evaluatedHeaders, function (key, value) {
+ request.setRequestHeader(key, value);
+ });
+ }
+
request.send(stringify(opts.data));
},
+ _evaluateHash: function (hash) {
+ var evaluated = {};
+
+ for (var key in hash) {
+ if (hash.hasOwnProperty(key)) {
+ var value = hash[key];
+ evaluated[key] =
+ typeof value === "function" ? value() : value;
+ }
+ }
+
+ return evaluated;
+ },
+
_logDebug: function (level) {
- if (this._originalConsoleMethods[level] && this.debug) {
+ // We allow `Raven.debug` and `Raven.config(DSN, { debug: true })` to not make backward incompatible API change
+ if (
+ this._originalConsoleMethods[level] &&
+ (this.debug || this._globalOptions.debug)
+ ) {
// In IE<10 console methods do not have their own 'apply' method
Function.prototype.apply.call(
this._originalConsoleMethods[level],
@@ -2288,7 +2559,7 @@
: {},
);
},
- { 1: 1, 2: 2, 5: 5, 6: 6, 7: 7 },
+ { 1: 1, 2: 2, 5: 5, 6: 6, 7: 7, 8: 8 },
],
4: [
function (_dereq_, module, exports) {
@@ -2328,6 +2599,42 @@
Raven.afterLoad();
module.exports = Raven;
+
+ /**
+ * DISCLAIMER:
+ *
+ * Expose `Client` constructor for cases where user want to track multiple "sub-applications" in one larger app.
+ * 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.
+ * Accidentally calling `install` multiple times, may result in an unexpected behavior that's very hard to debug.
+ *
+ * It's called `Client' to be in-line with Raven Node implementation.
+ *
+ * HOWTO:
+ *
+ * import Raven from 'raven-js';
+ *
+ * const someAppReporter = new Raven.Client();
+ * const someOtherAppReporter = new Raven.Client();
+ *
+ * someAppReporter.config('__DSN__', {
+ * ...config goes here
+ * });
+ *
+ * someOtherAppReporter.config('__OTHER_DSN__', {
+ * ...config goes here
+ * });
+ *
+ * someAppReporter.captureMessage(...);
+ * someAppReporter.captureException(...);
+ * someAppReporter.captureBreadcrumb(...);
+ *
+ * someOtherAppReporter.captureMessage(...);
+ * someOtherAppReporter.captureException(...);
+ * someOtherAppReporter.captureBreadcrumb(...);
+ *
+ * It should "just work".
+ */
+ module.exports.Client = RavenConstructor;
}).call(
this,
typeof global !== "undefined"
@@ -2344,6 +2651,8 @@
5: [
function (_dereq_, module, exports) {
(function (global) {
+ var stringify = _dereq_(7);
+
var _window =
typeof window !== "undefined"
? window
@@ -2360,7 +2669,7 @@
// Yanked from https://git.io/vS8DV re-used under CC0
// with some tiny modifications
function isError(value) {
- switch ({}.toString.call(value)) {
+ switch (Object.prototype.toString.call(value)) {
case "[object Error]":
return true;
case "[object Exception]":
@@ -2374,8 +2683,20 @@
function isErrorEvent(value) {
return (
- supportsErrorEvent() &&
- {}.toString.call(value) === "[object ErrorEvent]"
+ Object.prototype.toString.call(value) === "[object ErrorEvent]"
+ );
+ }
+
+ function isDOMError(value) {
+ return (
+ Object.prototype.toString.call(value) === "[object DOMError]"
+ );
+ }
+
+ function isDOMException(value) {
+ return (
+ Object.prototype.toString.call(value) ===
+ "[object DOMException]"
);
}
@@ -2387,6 +2708,10 @@
return typeof what === "function";
}
+ function isPlainObject(what) {
+ return Object.prototype.toString.call(what) === "[object Object]";
+ }
+
function isString(what) {
return Object.prototype.toString.call(what) === "[object String]";
}
@@ -2396,6 +2721,8 @@
}
function isEmptyObject(what) {
+ if (!isPlainObject(what)) return false;
+
for (var _ in what) {
if (what.hasOwnProperty(_)) {
return false;
@@ -2413,6 +2740,59 @@
}
}
+ function supportsDOMError() {
+ try {
+ new DOMError(""); // eslint-disable-line no-new
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+
+ function supportsDOMException() {
+ try {
+ new DOMException(""); // eslint-disable-line no-new
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+
+ function supportsFetch() {
+ if (!("fetch" in _window)) return false;
+
+ try {
+ new Headers(); // eslint-disable-line no-new
+ new Request(""); // eslint-disable-line no-new
+ new Response(); // eslint-disable-line no-new
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+
+ // Despite all stars in the sky saying that Edge supports old draft syntax, aka 'never', 'always', 'origin' and 'default
+ // https://caniuse.com/#feat=referrer-policy
+ // It doesn't. And it throw exception instead of ignoring this parameter...
+ // REF: https://github.com/getsentry/raven-js/issues/1233
+ function supportsReferrerPolicy() {
+ if (!supportsFetch()) return false;
+
+ try {
+ // eslint-disable-next-line no-new
+ new Request("pickleRick", {
+ referrerPolicy: "origin",
+ });
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+
+ function supportsPromiseRejectionEvent() {
+ return typeof PromiseRejectionEvent === "function";
+ }
+
function wrappedCallback(callback) {
function dataCallback(data, original) {
var normalizedData = callback(data) || data;
@@ -2470,9 +2850,15 @@
}
function truncate(str, max) {
- return !max || str.length <= max
- ? str
- : str.substr(0, max) + "\u2026";
+ if (typeof max !== "number") {
+ throw new Error(
+ "2nd argument to `truncate` function should be a number",
+ );
+ }
+ if (typeof str !== "string" || max === 0) {
+ return str;
+ }
+ return str.length <= max ? str : str.substr(0, max) + "\u2026";
}
/**
@@ -2521,14 +2907,14 @@
return pairs.join("&");
}
- // borrowed from https://datatracker.ietf.org/doc/html/rfc3986#appendix-B
+ // borrowed from https://tools.ietf.org/html/rfc3986#appendix-B
// intentionally using regex and not href parsing trick because React Native and other
// environments where DOM might not be available
function parseUrl(url) {
+ if (typeof url !== "string") return {};
var match = url.match(
/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/,
);
- if (!match) return {};
// coerce to undefined values to empty string so we don't get 'undefined'
var query = match[6] || "";
@@ -2675,6 +3061,13 @@
return !!(!!a ^ !!b);
}
+ /**
+ * Returns true if both parameters are undefined
+ */
+ function isBothUndefined(a, b) {
+ return isUndefined(a) && isUndefined(b);
+ }
+
/**
* Returns true if the two input exception interfaces have the same content
*/
@@ -2687,6 +3080,9 @@
if (ex1.type !== ex2.type || ex1.value !== ex2.value)
return false;
+ // in case both stacktraces are undefined, we can't decide so default to false
+ if (isBothUndefined(ex1.stacktrace, ex2.stacktrace)) return false;
+
return isSameStacktrace(ex1.stacktrace, ex2.stacktrace);
}
@@ -2726,6 +3122,7 @@
* @param track {optional} record instrumentation to an array
*/
function fill(obj, name, replacement, track) {
+ if (obj == null) return;
var orig = obj[name];
obj[name] = replacement(orig);
obj[name].__raven__ = true;
@@ -2735,16 +3132,190 @@
}
}
+ /**
+ * Join values in array
+ * @param input array of values to be joined together
+ * @param delimiter string to be placed in-between values
+ * @returns {string}
+ */
+ function safeJoin(input, delimiter) {
+ if (!isArray(input)) return "";
+
+ var output = [];
+
+ for (var i = 0; i < input.length; i++) {
+ try {
+ output.push(String(input[i]));
+ } catch (e) {
+ output.push("[value cannot be serialized]");
+ }
+ }
+
+ return output.join(delimiter);
+ }
+
+ // Default Node.js REPL depth
+ var MAX_SERIALIZE_EXCEPTION_DEPTH = 3;
+ // 50kB, as 100kB is max payload size, so half sounds reasonable
+ var MAX_SERIALIZE_EXCEPTION_SIZE = 50 * 1024;
+ var MAX_SERIALIZE_KEYS_LENGTH = 40;
+
+ function utf8Length(value) {
+ return ~-encodeURI(value).split(/%..|./).length;
+ }
+
+ function jsonSize(value) {
+ return utf8Length(JSON.stringify(value));
+ }
+
+ function serializeValue(value) {
+ if (typeof value === "string") {
+ var maxLength = 40;
+ return truncate(value, maxLength);
+ } else if (
+ typeof value === "number" ||
+ typeof value === "boolean" ||
+ typeof value === "undefined"
+ ) {
+ return value;
+ }
+
+ var type = Object.prototype.toString.call(value);
+
+ // Node.js REPL notation
+ if (type === "[object Object]") return "[Object]";
+ if (type === "[object Array]") return "[Array]";
+ if (type === "[object Function]")
+ return value.name
+ ? "[Function: " + value.name + "]"
+ : "[Function]";
+
+ return value;
+ }
+
+ function serializeObject(value, depth) {
+ if (depth === 0) return serializeValue(value);
+
+ if (isPlainObject(value)) {
+ return Object.keys(value).reduce(function (acc, key) {
+ acc[key] = serializeObject(value[key], depth - 1);
+ return acc;
+ }, {});
+ } else if (Array.isArray(value)) {
+ return value.map(function (val) {
+ return serializeObject(val, depth - 1);
+ });
+ }
+
+ return serializeValue(value);
+ }
+
+ function serializeException(ex, depth, maxSize) {
+ if (!isPlainObject(ex)) return ex;
+
+ depth =
+ typeof depth !== "number"
+ ? MAX_SERIALIZE_EXCEPTION_DEPTH
+ : depth;
+ maxSize =
+ typeof depth !== "number"
+ ? MAX_SERIALIZE_EXCEPTION_SIZE
+ : maxSize;
+
+ var serialized = serializeObject(ex, depth);
+
+ if (jsonSize(stringify(serialized)) > maxSize) {
+ return serializeException(ex, depth - 1);
+ }
+
+ return serialized;
+ }
+
+ function serializeKeysForMessage(keys, maxLength) {
+ if (typeof keys === "number" || typeof keys === "string")
+ return keys.toString();
+ if (!Array.isArray(keys)) return "";
+
+ keys = keys.filter(function (key) {
+ return typeof key === "string";
+ });
+ if (keys.length === 0) return "[object has no keys]";
+
+ maxLength =
+ typeof maxLength !== "number"
+ ? MAX_SERIALIZE_KEYS_LENGTH
+ : maxLength;
+ if (keys[0].length >= maxLength) return keys[0];
+
+ for (var usedKeys = keys.length; usedKeys > 0; usedKeys--) {
+ var serialized = keys.slice(0, usedKeys).join(", ");
+ if (serialized.length > maxLength) continue;
+ if (usedKeys === keys.length) return serialized;
+ return serialized + "\u2026";
+ }
+
+ return "";
+ }
+
+ function sanitize(input, sanitizeKeys) {
+ if (
+ !isArray(sanitizeKeys) ||
+ (isArray(sanitizeKeys) && sanitizeKeys.length === 0)
+ )
+ return input;
+
+ var sanitizeRegExp = joinRegExp(sanitizeKeys);
+ var sanitizeMask = "********";
+ var safeInput;
+
+ try {
+ safeInput = JSON.parse(stringify(input));
+ } catch (o_O) {
+ return input;
+ }
+
+ function sanitizeWorker(workerInput) {
+ if (isArray(workerInput)) {
+ return workerInput.map(function (val) {
+ return sanitizeWorker(val);
+ });
+ }
+
+ if (isPlainObject(workerInput)) {
+ return Object.keys(workerInput).reduce(function (acc, k) {
+ if (sanitizeRegExp.test(k)) {
+ acc[k] = sanitizeMask;
+ } else {
+ acc[k] = sanitizeWorker(workerInput[k]);
+ }
+ return acc;
+ }, {});
+ }
+
+ return workerInput;
+ }
+
+ return sanitizeWorker(safeInput);
+ }
+
module.exports = {
isObject: isObject,
isError: isError,
isErrorEvent: isErrorEvent,
+ isDOMError: isDOMError,
+ isDOMException: isDOMException,
isUndefined: isUndefined,
isFunction: isFunction,
+ isPlainObject: isPlainObject,
isString: isString,
isArray: isArray,
isEmptyObject: isEmptyObject,
supportsErrorEvent: supportsErrorEvent,
+ supportsDOMError: supportsDOMError,
+ supportsDOMException: supportsDOMException,
+ supportsFetch: supportsFetch,
+ supportsReferrerPolicy: supportsReferrerPolicy,
+ supportsPromiseRejectionEvent: supportsPromiseRejectionEvent,
wrappedCallback: wrappedCallback,
each: each,
objectMerge: objectMerge,
@@ -2760,6 +3331,10 @@
isSameStacktrace: isSameStacktrace,
parseUrl: parseUrl,
fill: fill,
+ safeJoin: safeJoin,
+ serializeException: serializeException,
+ serializeKeysForMessage: serializeKeysForMessage,
+ sanitize: sanitize,
};
}).call(
this,
@@ -2772,7 +3347,7 @@
: {},
);
},
- {},
+ { 7: 7 },
],
6: [
function (_dereq_, module, exports) {
@@ -2780,14 +3355,14 @@
var utils = _dereq_(5);
/*
- TraceKit - Cross brower stack traces
-
- This was originally forked from github.com/occ/TraceKit, but has since been
- largely re-written and is now maintained as part of raven-js. Tests for
- this are in test/vendor.
-
- MIT license
-*/
+ TraceKit - Cross brower stack traces
+
+ This was originally forked from github.com/occ/TraceKit, but has since been
+ largely re-written and is now maintained as part of raven-js. Tests for
+ this are in test/vendor.
+
+ MIT license
+ */
var TraceKit = {
collectWindowErrors: true,
@@ -2815,10 +3390,25 @@
function getLocationHref() {
if (typeof document === "undefined" || document.location == null)
return "";
-
return document.location.href;
}
+ function getLocationOrigin() {
+ if (typeof document === "undefined" || document.location == null)
+ return "";
+
+ // Oh dear IE10...
+ if (!document.location.origin) {
+ document.location.origin =
+ document.location.protocol +
+ "//" +
+ document.location.hostname +
+ (document.location.port ? ":" + document.location.port : "");
+ }
+
+ return document.location.origin;
+ }
+
/**
* TraceKit.report: cross-browser processing of unhandled exceptions
*
@@ -2925,7 +3515,7 @@
/**
* Ensures all global unhandled exceptions are recorded.
* Supported by Gecko and IE.
- * @param {string} message Error message.
+ * @param {string} msg Error message.
* @param {string} url URL of script that generated the exception.
* @param {(number|string)} lineNo The line number at which the error
* occurred.
@@ -2933,8 +3523,12 @@
* occurred.
* @param {?Error} ex The actual Error object.
*/
- function traceKitWindowOnError(message, url, lineNo, colNo, ex) {
+ function traceKitWindowOnError(msg, url, lineNo, colNo, ex) {
var stack = null;
+ // If 'ex' is ErrorEvent, get real Error from inside
+ var exception = utils.isErrorEvent(ex) ? ex.error : ex;
+ // If 'msg' is ErrorEvent, get real message from inside
+ var message = utils.isErrorEvent(msg) ? msg.message : msg;
if (lastExceptionStack) {
TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(
@@ -2944,13 +3538,13 @@
message,
);
processLastException();
- } else if (ex && utils.isError(ex)) {
- // non-string `ex` arg; attempt to extract stack trace
+ } else if (exception && utils.isError(exception)) {
+ // non-string `exception` arg; attempt to extract stack trace
// New chrome and blink send along a real error object
// Let's just report that like a normal error.
// See: https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror
- stack = TraceKit.computeStackTrace(ex);
+ stack = TraceKit.computeStackTrace(exception);
notifyHandlers(stack, true);
} else {
var location = {
@@ -2960,13 +3554,13 @@
};
var name = undefined;
- var msg = message; // must be new var or will modify original `arguments`
var groups;
+
if ({}.toString.call(message) === "[object String]") {
var groups = message.match(ERROR_TYPES_RE);
if (groups) {
name = groups[1];
- msg = groups[2];
+ message = groups[2];
}
}
@@ -2974,7 +3568,7 @@
stack = {
name: name,
- message: msg,
+ message: message,
url: getLocationHref(),
stack: [location],
};
@@ -3163,20 +3757,22 @@
if (typeof ex.stack === "undefined" || !ex.stack) return;
var chrome =
- /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack||[a-z]:|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,
- gecko =
- /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i,
- winjs =
- /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i,
- // Used to additionally parse URL/line/column from eval frames
- geckoEval = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i,
- chromeEval = /\((\S*)(?::(\d+))(?::(\d+))\)/,
- lines = ex.stack.split("\n"),
- stack = [],
- submatch,
- parts,
- element,
- reference = /^(.*) is undefined$/.exec(ex.message);
+ /^\s*at (?:(.*?) ?\()?((?:file|https?|blob|chrome-extension|native|eval|webpack||[a-z]:|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;
+ var winjs =
+ /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx(?:-web)|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i;
+ // NOTE: blob urls are now supposed to always have an origin, therefore it's format
+ // which is `blob:http://url/path/with-some-uuid`, is matched by `blob.*?:\/` as well
+ var gecko =
+ /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|moz-extension).*?:\/.*?|\[native code\]|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i;
+ // Used to additionally parse URL/line/column from eval frames
+ var geckoEval = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i;
+ var chromeEval = /\((\S*)(?::(\d+))(?::(\d+))\)/;
+ var lines = ex.stack.split("\n");
+ var stack = [];
+ var submatch;
+ var parts;
+ var element;
+ var reference = /^(.*) is undefined$/.exec(ex.message);
for (var i = 0, j = lines.length; i < j; ++i) {
if ((parts = chrome.exec(lines[i]))) {
@@ -3236,6 +3832,47 @@
element.func = UNKNOWN_FUNCTION;
}
+ if (element.url && element.url.substr(0, 5) === "blob:") {
+ // Special case for handling JavaScript loaded into a blob.
+ // We use a synchronous AJAX request here as a blob is already in
+ // memory - it's not making a network request. This will generate a warning
+ // in the browser console, but there has already been an error so that's not
+ // that much of an issue.
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", element.url, false);
+ xhr.send(null);
+
+ // If we failed to download the source, skip this patch
+ if (xhr.status === 200) {
+ var source = xhr.responseText || "";
+
+ // We trim the source down to the last 300 characters as sourceMappingURL is always at the end of the file.
+ // Why 300? To be in line with: https://github.com/getsentry/sentry/blob/4af29e8f2350e20c28a6933354e4f42437b4ba42/src/sentry/lang/javascript/processor.py#L164-L175
+ source = source.slice(-300);
+
+ // Now we dig out the source map URL
+ var sourceMaps = source.match(
+ /\/\/# sourceMappingURL=(.*)$/,
+ );
+
+ // If we don't find a source map comment or we find more than one, continue on to the next element.
+ if (sourceMaps) {
+ var sourceMapAddress = sourceMaps[1];
+
+ // Now we check to see if it's a relative URL.
+ // If it is, convert it to an absolute one.
+ if (sourceMapAddress.charAt(0) === "~") {
+ sourceMapAddress =
+ getLocationOrigin() + sourceMapAddress.slice(1);
+ }
+
+ // Now we strip the '.map' off of the end of the URL and update the
+ // element so that Sentry can match the map to the blob.
+ element.url = sourceMapAddress.slice(0, -4);
+ }
+ }
+ }
+
stack.push(element);
}
@@ -3449,15 +4086,15 @@
7: [
function (_dereq_, module, exports) {
/*
- json-stringify-safe
- Like JSON.stringify, but doesn't throw on circular references.
-
- Originally forked from https://github.com/isaacs/json-stringify-safe
- version 5.0.1 on 3/8/2017 and modified to handle Errors serialization
- and IE8 compatibility. Tests for this are in test/vendor.
-
- ISC license: https://github.com/isaacs/json-stringify-safe/blob/master/LICENSE
-*/
+ json-stringify-safe
+ Like JSON.stringify, but doesn't throw on circular references.
+
+ Originally forked from https://github.com/isaacs/json-stringify-safe
+ version 5.0.1 on 3/8/2017 and modified to handle Errors serialization
+ and IE8 compatibility. Tests for this are in test/vendor.
+
+ ISC license: https://github.com/isaacs/json-stringify-safe/blob/master/LICENSE
+ */
exports = module.exports = stringify;
exports.getSerialize = serializer;
@@ -3535,6 +4172,281 @@
},
{},
],
+ 8: [
+ function (_dereq_, module, exports) {
+ /*
+ * JavaScript MD5
+ * https://github.com/blueimp/JavaScript-MD5
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * https://opensource.org/licenses/MIT
+ *
+ * Based on
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for more info.
+ */
+
+ /*
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+ function safeAdd(x, y) {
+ var lsw = (x & 0xffff) + (y & 0xffff);
+ var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+ return (msw << 16) | (lsw & 0xffff);
+ }
+
+ /*
+ * Bitwise rotate a 32-bit number to the left.
+ */
+ function bitRotateLeft(num, cnt) {
+ return (num << cnt) | (num >>> (32 - cnt));
+ }
+
+ /*
+ * These functions implement the four basic operations the algorithm uses.
+ */
+ function md5cmn(q, a, b, x, s, t) {
+ return safeAdd(
+ bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s),
+ b,
+ );
+ }
+ function md5ff(a, b, c, d, x, s, t) {
+ return md5cmn((b & c) | (~b & d), a, b, x, s, t);
+ }
+ function md5gg(a, b, c, d, x, s, t) {
+ return md5cmn((b & d) | (c & ~d), a, b, x, s, t);
+ }
+ function md5hh(a, b, c, d, x, s, t) {
+ return md5cmn(b ^ c ^ d, a, b, x, s, t);
+ }
+ function md5ii(a, b, c, d, x, s, t) {
+ return md5cmn(c ^ (b | ~d), a, b, x, s, t);
+ }
+
+ /*
+ * Calculate the MD5 of an array of little-endian words, and a bit length.
+ */
+ function binlMD5(x, len) {
+ /* append padding */
+ x[len >> 5] |= 0x80 << len % 32;
+ x[(((len + 64) >>> 9) << 4) + 14] = len;
+
+ var i;
+ var olda;
+ var oldb;
+ var oldc;
+ var oldd;
+ var a = 1732584193;
+ var b = -271733879;
+ var c = -1732584194;
+ var d = 271733878;
+
+ for (i = 0; i < x.length; i += 16) {
+ olda = a;
+ oldb = b;
+ oldc = c;
+ oldd = d;
+
+ a = md5ff(a, b, c, d, x[i], 7, -680876936);
+ d = md5ff(d, a, b, c, x[i + 1], 12, -389564586);
+ c = md5ff(c, d, a, b, x[i + 2], 17, 606105819);
+ b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330);
+ a = md5ff(a, b, c, d, x[i + 4], 7, -176418897);
+ d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426);
+ c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341);
+ b = md5ff(b, c, d, a, x[i + 7], 22, -45705983);
+ a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416);
+ d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417);
+ c = md5ff(c, d, a, b, x[i + 10], 17, -42063);
+ b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162);
+ a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682);
+ d = md5ff(d, a, b, c, x[i + 13], 12, -40341101);
+ c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290);
+ b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329);
+
+ a = md5gg(a, b, c, d, x[i + 1], 5, -165796510);
+ d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632);
+ c = md5gg(c, d, a, b, x[i + 11], 14, 643717713);
+ b = md5gg(b, c, d, a, x[i], 20, -373897302);
+ a = md5gg(a, b, c, d, x[i + 5], 5, -701558691);
+ d = md5gg(d, a, b, c, x[i + 10], 9, 38016083);
+ c = md5gg(c, d, a, b, x[i + 15], 14, -660478335);
+ b = md5gg(b, c, d, a, x[i + 4], 20, -405537848);
+ a = md5gg(a, b, c, d, x[i + 9], 5, 568446438);
+ d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690);
+ c = md5gg(c, d, a, b, x[i + 3], 14, -187363961);
+ b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501);
+ a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467);
+ d = md5gg(d, a, b, c, x[i + 2], 9, -51403784);
+ c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473);
+ b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734);
+
+ a = md5hh(a, b, c, d, x[i + 5], 4, -378558);
+ d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463);
+ c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562);
+ b = md5hh(b, c, d, a, x[i + 14], 23, -35309556);
+ a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060);
+ d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353);
+ c = md5hh(c, d, a, b, x[i + 7], 16, -155497632);
+ b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640);
+ a = md5hh(a, b, c, d, x[i + 13], 4, 681279174);
+ d = md5hh(d, a, b, c, x[i], 11, -358537222);
+ c = md5hh(c, d, a, b, x[i + 3], 16, -722521979);
+ b = md5hh(b, c, d, a, x[i + 6], 23, 76029189);
+ a = md5hh(a, b, c, d, x[i + 9], 4, -640364487);
+ d = md5hh(d, a, b, c, x[i + 12], 11, -421815835);
+ c = md5hh(c, d, a, b, x[i + 15], 16, 530742520);
+ b = md5hh(b, c, d, a, x[i + 2], 23, -995338651);
+
+ a = md5ii(a, b, c, d, x[i], 6, -198630844);
+ d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415);
+ c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905);
+ b = md5ii(b, c, d, a, x[i + 5], 21, -57434055);
+ a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571);
+ d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606);
+ c = md5ii(c, d, a, b, x[i + 10], 15, -1051523);
+ b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799);
+ a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359);
+ d = md5ii(d, a, b, c, x[i + 15], 10, -30611744);
+ c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380);
+ b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649);
+ a = md5ii(a, b, c, d, x[i + 4], 6, -145523070);
+ d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379);
+ c = md5ii(c, d, a, b, x[i + 2], 15, 718787259);
+ b = md5ii(b, c, d, a, x[i + 9], 21, -343485551);
+
+ a = safeAdd(a, olda);
+ b = safeAdd(b, oldb);
+ c = safeAdd(c, oldc);
+ d = safeAdd(d, oldd);
+ }
+ return [a, b, c, d];
+ }
+
+ /*
+ * Convert an array of little-endian words to a string
+ */
+ function binl2rstr(input) {
+ var i;
+ var output = "";
+ var length32 = input.length * 32;
+ for (i = 0; i < length32; i += 8) {
+ output += String.fromCharCode((input[i >> 5] >>> i % 32) & 0xff);
+ }
+ return output;
+ }
+
+ /*
+ * Convert a raw string to an array of little-endian words
+ * Characters >255 have their high-byte silently ignored.
+ */
+ function rstr2binl(input) {
+ var i;
+ var output = [];
+ output[(input.length >> 2) - 1] = undefined;
+ for (i = 0; i < output.length; i += 1) {
+ output[i] = 0;
+ }
+ var length8 = input.length * 8;
+ for (i = 0; i < length8; i += 8) {
+ output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << i % 32;
+ }
+ return output;
+ }
+
+ /*
+ * Calculate the MD5 of a raw string
+ */
+ function rstrMD5(s) {
+ return binl2rstr(binlMD5(rstr2binl(s), s.length * 8));
+ }
+
+ /*
+ * Calculate the HMAC-MD5, of a key and some data (raw strings)
+ */
+ function rstrHMACMD5(key, data) {
+ var i;
+ var bkey = rstr2binl(key);
+ var ipad = [];
+ var opad = [];
+ var hash;
+ ipad[15] = opad[15] = undefined;
+ if (bkey.length > 16) {
+ bkey = binlMD5(bkey, key.length * 8);
+ }
+ for (i = 0; i < 16; i += 1) {
+ ipad[i] = bkey[i] ^ 0x36363636;
+ opad[i] = bkey[i] ^ 0x5c5c5c5c;
+ }
+ hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
+ return binl2rstr(binlMD5(opad.concat(hash), 512 + 128));
+ }
+
+ /*
+ * Convert a raw string to a hex string
+ */
+ function rstr2hex(input) {
+ var hexTab = "0123456789abcdef";
+ var output = "";
+ var x;
+ var i;
+ for (i = 0; i < input.length; i += 1) {
+ x = input.charCodeAt(i);
+ output +=
+ hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f);
+ }
+ return output;
+ }
+
+ /*
+ * Encode a string as utf-8
+ */
+ function str2rstrUTF8(input) {
+ return unescape(encodeURIComponent(input));
+ }
+
+ /*
+ * Take string arguments and return either raw or hex encoded strings
+ */
+ function rawMD5(s) {
+ return rstrMD5(str2rstrUTF8(s));
+ }
+ function hexMD5(s) {
+ return rstr2hex(rawMD5(s));
+ }
+ function rawHMACMD5(k, d) {
+ return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d));
+ }
+ function hexHMACMD5(k, d) {
+ return rstr2hex(rawHMACMD5(k, d));
+ }
+
+ function md5(string, key, raw) {
+ if (!key) {
+ if (!raw) {
+ return hexMD5(string);
+ }
+ return rawMD5(string);
+ }
+ if (!raw) {
+ return hexHMACMD5(key, string);
+ }
+ return rawHMACMD5(key, string);
+ }
+
+ module.exports = md5;
+ },
+ {},
+ ],
},
{},
[4],