UNPKG

@bugsnag/node

Version:

Bugsnag error reporter for Node.js

1,644 lines (1,342 loc) 68.3 kB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.bugsnag = f()}})(function(){var define,module,exports; // minimal implementations of useful ES functionality // all we really need for arrays is reduce – everything else is just sugar! // Array#reduce var reduce = function (arr, fn, accum) { var val = accum; for (var i = 0, len = arr.length; i < len; i++) { val = fn(val, arr[i], i, arr); } return val; }; // Array#filter var filter = function (arr, fn) { return reduce(arr, function (accum, item, i, arr) { return !fn(item, i, arr) ? accum : accum.concat(item); }, []); }; // Array#map var map = function (arr, fn) { return reduce(arr, function (accum, item, i, arr) { return accum.concat(fn(item, i, arr)); }, []); }; // Array#includes var includes = function (arr, x) { return reduce(arr, function (accum, item, i, arr) { return accum === true || item === x; }, false); }; var _hasDontEnumBug = !{ toString: null }.propertyIsEnumerable('toString'); var _dontEnums = ['toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor']; // Object#keys var keys = function (obj) { // stripped down version of // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/Keys var result = []; var prop; for (prop in obj) { if (Object.prototype.hasOwnProperty.call(obj, prop)) result.push(prop); } if (!_hasDontEnumBug) return result; for (var i = 0, len = _dontEnums.length; i < len; i++) { if (Object.prototype.hasOwnProperty.call(obj, _dontEnums[i])) result.push(_dontEnums[i]); } return result; }; // Array#isArray var isArray = function (obj) { return Object.prototype.toString.call(obj) === '[object Array]'; }; var _pad = function (n) { return n < 10 ? "0" + n : n; }; // Date#toISOString var isoDate = function () { // from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString var d = new Date(); return d.getUTCFullYear() + '-' + _pad(d.getUTCMonth() + 1) + '-' + _pad(d.getUTCDate()) + 'T' + _pad(d.getUTCHours()) + ':' + _pad(d.getUTCMinutes()) + ':' + _pad(d.getUTCSeconds()) + '.' + (d.getUTCMilliseconds() / 1000).toFixed(3).slice(2, 5) + 'Z'; }; var _$esUtils_8 = { map: map, reduce: reduce, filter: filter, includes: includes, keys: keys, isArray: isArray, isoDate: isoDate }; var __isoDate_2 = _$esUtils_8.isoDate; var BugsnagBreadcrumb = /*#__PURE__*/ function () { function BugsnagBreadcrumb(name, metaData, type, timestamp) { if (name === void 0) { name = '[anonymous]'; } if (metaData === void 0) { metaData = {}; } if (type === void 0) { type = 'manual'; } if (timestamp === void 0) { timestamp = __isoDate_2(); } this.type = type; this.name = name; this.metaData = metaData; this.timestamp = timestamp; } var _proto = BugsnagBreadcrumb.prototype; _proto.toJSON = function toJSON() { return { type: this.type, name: this.name, timestamp: this.timestamp, metaData: this.metaData }; }; return BugsnagBreadcrumb; }(); var _$BugsnagBreadcrumb_2 = BugsnagBreadcrumb; var _$validators_17 = {}; _$validators_17.intRange = function (min, max) { if (min === void 0) { min = 1; } if (max === void 0) { max = Infinity; } return function (value) { return typeof value === 'number' && parseInt('' + value, 10) === value && value >= min && value <= max; }; }; _$validators_17.stringWithLength = function (value) { return typeof value === 'string' && !!value.length; }; var _$config_4 = {}; var __filter_4 = _$esUtils_8.filter, __reduce_4 = _$esUtils_8.reduce, __keys_4 = _$esUtils_8.keys, __isArray_4 = _$esUtils_8.isArray, __includes_4 = _$esUtils_8.includes; var intRange = _$validators_17.intRange, stringWithLength = _$validators_17.stringWithLength; _$config_4.schema = { apiKey: { defaultValue: function () { return null; }, message: 'is required', validate: stringWithLength }, appVersion: { defaultValue: function () { return null; }, message: 'should be a string', validate: function (value) { return value === null || stringWithLength(value); } }, appType: { defaultValue: function () { return null; }, message: 'should be a string', validate: function (value) { return value === null || stringWithLength(value); } }, autoNotify: { defaultValue: function () { return true; }, message: 'should be true|false', validate: function (value) { return value === true || value === false; } }, beforeSend: { defaultValue: function () { return []; }, message: 'should be a function or array of functions', validate: function (value) { return typeof value === 'function' || __isArray_4(value) && __filter_4(value, function (f) { return typeof f === 'function'; }).length === value.length; } }, endpoints: { defaultValue: function () { return { notify: 'https://notify.bugsnag.com', sessions: 'https://sessions.bugsnag.com' }; }, message: 'should be an object containing endpoint URLs { notify, sessions }. sessions is optional if autoCaptureSessions=false', validate: function (val, obj) { return (// first, ensure it's an object val && typeof val === 'object' && // endpoints.notify must always be set stringWithLength(val.notify) && ( // endpoints.sessions must be set unless session tracking is explicitly off obj.autoCaptureSessions === false || stringWithLength(val.sessions)) && // ensure no keys other than notify/session are set on endpoints object __filter_4(__keys_4(val), function (k) { return !__includes_4(['notify', 'sessions'], k); }).length === 0 ); } }, autoCaptureSessions: { defaultValue: function (val, opts) { return opts.endpoints === undefined || !!opts.endpoints && !!opts.endpoints.sessions; }, message: 'should be true|false', validate: function (val) { return val === true || val === false; } }, notifyReleaseStages: { defaultValue: function () { return null; }, message: 'should be an array of strings', validate: function (value) { return value === null || __isArray_4(value) && __filter_4(value, function (f) { return typeof f === 'string'; }).length === value.length; } }, releaseStage: { defaultValue: function () { return 'production'; }, message: 'should be a string', validate: function (value) { return typeof value === 'string' && value.length; } }, maxBreadcrumbs: { defaultValue: function () { return 20; }, message: 'should be a number ≤40', validate: function (value) { return intRange(0, 40)(value); } }, autoBreadcrumbs: { defaultValue: function () { return true; }, message: 'should be true|false', validate: function (value) { return typeof value === 'boolean'; } }, user: { defaultValue: function () { return null; }, message: '(object) user should be an object', validate: function (value) { return typeof value === 'object'; } }, metaData: { defaultValue: function () { return null; }, message: 'should be an object', validate: function (value) { return typeof value === 'object'; } }, logger: { defaultValue: function () { return undefined; }, message: 'should be null or an object with methods { debug, info, warn, error }', validate: function (value) { return !value || value && __reduce_4(['debug', 'info', 'warn', 'error'], function (accum, method) { return accum && typeof value[method] === 'function'; }, true); } }, filters: { defaultValue: function () { return ['password']; }, message: 'should be an array of strings|regexes', validate: function (value) { return __isArray_4(value) && value.length === __filter_4(value, function (s) { return typeof s === 'string' || s && typeof s.test === 'function'; }).length; } } }; _$config_4.mergeDefaults = function (opts, schema) { if (!opts || !schema) throw new Error('opts and schema objects are required'); return __reduce_4(__keys_4(schema), function (accum, key) { accum[key] = opts[key] !== undefined ? opts[key] : schema[key].defaultValue(opts[key], opts); return accum; }, {}); }; _$config_4.validate = function (opts, schema) { if (!opts || !schema) throw new Error('opts and schema objects are required'); var errors = __reduce_4(__keys_4(schema), function (accum, key) { if (schema[key].validate(opts[key], opts)) return accum; return accum.concat({ key: key, message: schema[key].message, value: opts[key] }); }, []); return { valid: !errors.length, errors: errors }; }; // This is a heavily modified/simplified version of // https://github.com/othiym23/async-some // // We can't use that because: // a) it inflates the bundle size to over 10kB // b) it depends on a module that uses Object.keys() // (which we can't use due to ie8 support) // run the asynchronous test function (fn) over each item in the array (arr) // in series until: // - fn(item, cb) => calls cb(null, true) // - or the end of the array is reached // the callback (cb) will be passed true if any of the items resulted in a true // callback, otherwise false var _$asyncSome_5 = function (arr, fn, cb) { var length = arr.length; var index = 0; var next = function () { if (index >= length) return cb(null, false); fn(arr[index], function (err, result) { if (err) return cb(err, false); if (result === true) return cb(null, true); index++; next(); }); }; next(); }; var _$inferReleaseStage_10 = function (client) { return client.app && typeof client.app.releaseStage === 'string' ? client.app.releaseStage : client.config.releaseStage; }; var _$iserror_11 = require("iserror"); var _$runBeforeSend_16 = function (report, onError) { return function (fn, cb) { if (typeof fn !== 'function') return cb(null, false); try { // if function appears sync… if (fn.length !== 2) { var ret = fn(report); // check if it returned a "thenable" (promise) if (ret && typeof ret.then === 'function') { return ret.then( // resolve function (val) { return setTimeout(function () { return cb(null, shouldPreventSend(report, val)); }, 0); }, // reject function (err) { setTimeout(function () { onError(err); return cb(null, false); }); }); } return cb(null, shouldPreventSend(report, ret)); } // if function is async… fn(report, function (err, result) { if (err) { onError(err); return cb(null, false); } cb(null, shouldPreventSend(report, result)); }); } catch (e) { onError(e); cb(null, false); } }; }; var shouldPreventSend = function (report, value) { return report.isIgnored() || value === false; }; var _$errorStackParser_7 = require("error-stack-parser"); // Given `err` which may be an error, does it have a stack property which is a string? var _$hasStack_9 = function (err) { return !!err && (!!err.stack || !!err.stacktrace || !!err['opera#sourceloc']) && typeof (err.stack || err.stacktrace || err['opera#sourceloc']) === 'string' && err.stack !== err.name + ": " + err.message; }; function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } /* removed: var _$errorStackParser_7 = require('./lib/error-stack-parser'); */; var StackGenerator = require("stack-generator"); /* removed: var _$hasStack_9 = require('./lib/has-stack'); */; var __reduce_22 = _$esUtils_8.reduce, __filter_22 = _$esUtils_8.filter; var BugsnagReport = /*#__PURE__*/ function () { function BugsnagReport(errorClass, errorMessage, stacktrace, handledState, originalError) { if (stacktrace === void 0) { stacktrace = []; } if (handledState === void 0) { handledState = defaultHandledState(); } // duck-typing ftw >_< this.__isBugsnagReport = true; this._ignored = false; // private (un)handled state this._handledState = handledState; // setable props this.app = undefined; this.apiKey = undefined; this.breadcrumbs = []; this.context = undefined; this.device = undefined; this.errorClass = stringOrFallback(errorClass, '[no error class]'); this.errorMessage = stringOrFallback(errorMessage, '[no error message]'); this.groupingHash = undefined; this.metaData = {}; this.request = undefined; this.severity = this._handledState.severity; this.stacktrace = __reduce_22(stacktrace, function (accum, frame) { var f = formatStackframe(frame); // don't include a stackframe if none of its properties are defined try { if (JSON.stringify(f) === '{}') return accum; return accum.concat(f); } catch (e) { return accum; } }, []); this.user = undefined; this.session = undefined; this.originalError = originalError; } var _proto = BugsnagReport.prototype; _proto.ignore = function ignore() { this._ignored = true; }; _proto.isIgnored = function isIgnored() { return this._ignored; }; _proto.updateMetaData = function updateMetaData(section) { var _updates; if (!section) return this; var updates; // updateMetaData("section", null) -> removes section if ((arguments.length <= 1 ? undefined : arguments[1]) === null) return this.removeMetaData(section); // updateMetaData("section", "property", null) -> removes property from section if ((arguments.length <= 2 ? undefined : arguments[2]) === null) return this.removeMetaData(section, arguments.length <= 1 ? undefined : arguments[1], arguments.length <= 2 ? undefined : arguments[2]); // normalise the two supported input types into object form if (typeof (arguments.length <= 1 ? undefined : arguments[1]) === 'object') updates = arguments.length <= 1 ? undefined : arguments[1]; if (typeof (arguments.length <= 1 ? undefined : arguments[1]) === 'string') updates = (_updates = {}, _updates[arguments.length <= 1 ? undefined : arguments[1]] = arguments.length <= 2 ? undefined : arguments[2], _updates); // exit if we don't have an updates object at this point if (!updates) return this; // ensure a section with this name exists if (!this.metaData[section]) this.metaData[section] = {}; // merge the updates with the existing section this.metaData[section] = _extends({}, this.metaData[section], updates); return this; }; _proto.removeMetaData = function removeMetaData(section, property) { if (typeof section !== 'string') return this; // remove an entire section if (!property) { delete this.metaData[section]; return this; } // remove a single property from a section if (this.metaData[section]) { delete this.metaData[section][property]; return this; } return this; }; _proto.toJSON = function toJSON() { return { payloadVersion: '4', exceptions: [{ errorClass: this.errorClass, message: this.errorMessage, stacktrace: this.stacktrace, type: process.env.IS_BROWSER ? 'browserjs' : 'nodejs' }], severity: this.severity, unhandled: this._handledState.unhandled, severityReason: this._handledState.severityReason, app: this.app, device: this.device, breadcrumbs: this.breadcrumbs, context: this.context, user: this.user, metaData: this.metaData, groupingHash: this.groupingHash, request: this.request, session: this.session }; }; return BugsnagReport; }(); // takes a stacktrace.js style stackframe (https://github.com/stacktracejs/stackframe) // and returns a Bugsnag compatible stackframe (https://docs.bugsnag.com/api/error-reporting/#json-payload) var formatStackframe = function (frame) { var f = { file: frame.fileName, method: normaliseFunctionName(frame.functionName), lineNumber: frame.lineNumber, columnNumber: frame.columnNumber, code: undefined, inProject: undefined // Some instances result in no file: // - calling notify() from chrome's terminal results in no file/method. // - non-error exception thrown from global code in FF // This adds one. }; if (f.lineNumber > -1 && !f.file && !f.method) { f.file = 'global code'; } return f; }; var normaliseFunctionName = function (name) { return /^global code$/i.test(name) ? 'global code' : name; }; var defaultHandledState = function () { return { unhandled: false, severity: 'warning', severityReason: { type: 'handledException' } }; }; var stringOrFallback = function (str, fallback) { return typeof str === 'string' && str ? str : fallback; }; // Helpers BugsnagReport.getStacktrace = function (error, errorFramesToSkip, generatedFramesToSkip) { if (errorFramesToSkip === void 0) { errorFramesToSkip = 0; } if (generatedFramesToSkip === void 0) { generatedFramesToSkip = 0; } if (_$hasStack_9(error)) return _$errorStackParser_7.parse(error).slice(errorFramesToSkip); // error wasn't provided or didn't have a stacktrace so try to walk the callstack return __filter_22(StackGenerator.backtrace(), function (frame) { return (frame.functionName || '').indexOf('StackGenerator$$') === -1; }).slice(1 + generatedFramesToSkip); }; BugsnagReport.ensureReport = function (reportOrError, errorFramesToSkip, generatedFramesToSkip) { if (errorFramesToSkip === void 0) { errorFramesToSkip = 0; } if (generatedFramesToSkip === void 0) { generatedFramesToSkip = 0; } // notify() can be called with a Report object. In this case no action is required if (reportOrError.__isBugsnagReport) return reportOrError; try { var stacktrace = BugsnagReport.getStacktrace(reportOrError, errorFramesToSkip, 1 + generatedFramesToSkip); return new BugsnagReport(reportOrError.name, reportOrError.message, stacktrace, undefined, reportOrError); } catch (e) { return new BugsnagReport(reportOrError.name, reportOrError.message, [], undefined, reportOrError); } }; var _$BugsnagReport_22 = BugsnagReport; var _$pad_20 = function pad(num, size) { var s = '000000000' + num; return s.substr(s.length - size); }; /* removed: var _$pad_20 = require('./pad.js'); */; var os = require("os"), padding = 2, pid = _$pad_20(process.pid.toString(36), padding), hostname = os.hostname(), length = hostname.length, hostId = _$pad_20(hostname.split('').reduce(function (prev, char) { return +prev + char.charCodeAt(0); }, +length + 36).toString(36), padding); var _$fingerprint_19 = function fingerprint() { return pid + hostId; }; /** * cuid.js * Collision-resistant UID generator for browsers and node. * Sequential for fast db lookups and recency sorting. * Safe for element IDs and server-side lookups. * * Extracted from CLCTR * * Copyright (c) Eric Elliott 2012 * MIT License */ /* removed: var _$fingerprint_19 = require('./lib/fingerprint.js'); */; /* removed: var _$pad_20 = require('./lib/pad.js'); */; var c = 0, blockSize = 4, base = 36, discreteValues = Math.pow(base, blockSize); function randomBlock() { return _$pad_20((Math.random() * discreteValues << 0).toString(base), blockSize); } function safeCounter() { c = c < discreteValues ? c : 0; c++; // this is not subliminal return c - 1; } function cuid() { // Starting with a lowercase letter makes // it HTML element ID friendly. var letter = 'c', // hard-coded allows for sequential access // timestamp // warning: this exposes the exact date and time // that the uid was created. timestamp = new Date().getTime().toString(base), // Prevent same-machine collisions. counter = _$pad_20(safeCounter().toString(base), blockSize), // A few chars to generate distinct ids for different // clients (so different computers are far less // likely to generate the same id) print = _$fingerprint_19(), // Grab some more chars from Math.random() random = randomBlock() + randomBlock(); return letter + timestamp + counter + print + random; } cuid.fingerprint = _$fingerprint_19; var _$cuid_18 = cuid; var __isoDate_23 = _$esUtils_8.isoDate; /* removed: var _$cuid_18 = require('@bugsnag/cuid'); */; var Session = /*#__PURE__*/ function () { function Session() { this.id = _$cuid_18(); this.startedAt = __isoDate_23(); this._handled = 0; this._unhandled = 0; } var _proto = Session.prototype; _proto.toJSON = function toJSON() { return { id: this.id, startedAt: this.startedAt, events: { handled: this._handled, unhandled: this._unhandled } }; }; _proto.trackError = function trackError(report) { this[report._handledState.unhandled ? '_unhandled' : '_handled'] += 1; }; return Session; }(); var _$Session_23 = Session; function ___extends_3() { ___extends_3 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return ___extends_3.apply(this, arguments); } /* removed: var _$config_4 = require('./config'); */; /* removed: var _$BugsnagReport_22 = require('./report'); */; /* removed: var _$BugsnagBreadcrumb_2 = require('./breadcrumb'); */; /* removed: var _$Session_23 = require('./session'); */; var __map_3 = _$esUtils_8.map, __includes_3 = _$esUtils_8.includes, __isArray_3 = _$esUtils_8.isArray; /* removed: var _$inferReleaseStage_10 = require('./lib/infer-release-stage'); */; /* removed: var _$iserror_11 = require('./lib/iserror'); */; /* removed: var _$asyncSome_5 = require('./lib/async-some'); */; /* removed: var _$runBeforeSend_16 = require('./lib/run-before-send'); */; var LOG_USAGE_ERR_PREFIX = "Usage error."; var REPORT_USAGE_ERR_PREFIX = "Bugsnag usage error."; var BugsnagClient = /*#__PURE__*/ function () { function BugsnagClient(notifier) { if (!notifier || !notifier.name || !notifier.version || !notifier.url) { throw new Error('`notifier` argument is required'); } // notifier id this.notifier = notifier; // configure() should be called before notify() this._configured = false; // intialise opts and config this._opts = {}; this.config = {}; // // i/o this._delivery = { sendSession: function () {}, sendReport: function () {} }; this._logger = { debug: function () {}, info: function () {}, warn: function () {}, error: function () {} // plugins }; this._plugins = {}; this._session = null; this.breadcrumbs = []; // setable props this.app = {}; this.context = undefined; this.device = undefined; this.metaData = undefined; this.request = undefined; this.user = {}; // expose internal constructors this.BugsnagClient = BugsnagClient; this.BugsnagReport = _$BugsnagReport_22; this.BugsnagBreadcrumb = _$BugsnagBreadcrumb_2; this.BugsnagSession = _$Session_23; var self = this; var notify = this.notify; this.notify = function () { return notify.apply(self, arguments); }; } var _proto = BugsnagClient.prototype; _proto.setOptions = function setOptions(opts) { this._opts = ___extends_3({}, this._opts, opts); }; _proto.configure = function configure(partialSchema) { if (partialSchema === void 0) { partialSchema = _$config_4.schema; } var conf = _$config_4.mergeDefaults(this._opts, partialSchema); var validity = _$config_4.validate(conf, partialSchema); if (!validity.valid === true) throw new Error(generateConfigErrorMessage(validity.errors)); // update and elevate some special options if they were passed in at this point if (typeof conf.beforeSend === 'function') conf.beforeSend = [conf.beforeSend]; if (conf.appVersion) this.app.version = conf.appVersion; if (conf.appType) this.app.type = conf.appType; if (conf.metaData) this.metaData = conf.metaData; if (conf.user) this.user = conf.user; if (conf.logger) this.logger(conf.logger); // merge with existing config this.config = ___extends_3({}, this.config, conf); this._configured = true; return this; }; _proto.use = function use(plugin) { if (!this._configured) throw new Error('client not configured'); if (plugin.configSchema) this.configure(plugin.configSchema); for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } var result = plugin.init.apply(plugin, [this].concat(args)); // JS objects are not the safest way to store arbitrarily keyed values, // so bookend the key with some characters that prevent tampering with // stuff like __proto__ etc. (only store the result if the plugin had a // name) if (plugin.name) this._plugins["~" + plugin.name + "~"] = result; return this; }; _proto.getPlugin = function getPlugin(name) { return this._plugins["~" + name + "~"]; }; _proto.delivery = function delivery(d) { this._delivery = d; return this; }; _proto.logger = function logger(l, sid) { this._logger = l; return this; }; _proto.sessionDelegate = function sessionDelegate(s) { this._sessionDelegate = s; return this; }; _proto.startSession = function startSession() { if (!this._sessionDelegate) { this._logger.warn('No session implementation is installed'); return this; } return this._sessionDelegate.startSession(this); }; _proto.leaveBreadcrumb = function leaveBreadcrumb(name, metaData, type, timestamp) { if (!this._configured) throw new Error('client not configured'); // coerce bad values so that the defaults get set name = name || undefined; type = typeof type === 'string' ? type : undefined; timestamp = typeof timestamp === 'string' ? timestamp : undefined; metaData = typeof metaData === 'object' && metaData !== null ? metaData : undefined; // if no name and no metaData, usefulness of this crumb is questionable at best so discard if (typeof name !== 'string' && !metaData) return; var crumb = new _$BugsnagBreadcrumb_2(name, metaData, type, timestamp); // push the valid crumb onto the queue and maintain the length this.breadcrumbs.push(crumb); if (this.breadcrumbs.length > this.config.maxBreadcrumbs) { this.breadcrumbs = this.breadcrumbs.slice(this.breadcrumbs.length - this.config.maxBreadcrumbs); } return this; }; _proto.notify = function notify(error, opts, cb) { var _this = this; if (opts === void 0) { opts = {}; } if (cb === void 0) { cb = function () {}; } if (!this._configured) throw new Error('client not configured'); // releaseStage can be set via config.releaseStage or client.app.releaseStage var releaseStage = _$inferReleaseStage_10(this); // ensure we have an error (or a reasonable object representation of an error) var _normaliseError = normaliseError(error, opts, this._logger), err = _normaliseError.err, errorFramesToSkip = _normaliseError.errorFramesToSkip, _opts = _normaliseError._opts; if (_opts) opts = _opts; // ensure opts is an object if (typeof opts !== 'object' || opts === null) opts = {}; // create a report from the error, if it isn't one already var report = _$BugsnagReport_22.ensureReport(err, errorFramesToSkip, 2); report.app = ___extends_3({}, { releaseStage: releaseStage }, report.app, this.app); report.context = report.context || opts.context || this.context || undefined; report.device = ___extends_3({}, report.device, this.device, opts.device); report.request = ___extends_3({}, report.request, this.request, opts.request); report.user = ___extends_3({}, report.user, this.user, opts.user); report.metaData = ___extends_3({}, report.metaData, this.metaData, opts.metaData); report.breadcrumbs = this.breadcrumbs.slice(0); if (this._session) { this._session.trackError(report); report.session = this._session; } // set severity if supplied if (opts.severity !== undefined) { report.severity = opts.severity; report._handledState.severityReason = { type: 'userSpecifiedSeverity' }; } // exit early if the reports should not be sent on the current releaseStage if (__isArray_3(this.config.notifyReleaseStages) && !__includes_3(this.config.notifyReleaseStages, releaseStage)) { this._logger.warn("Report not sent due to releaseStage/notifyReleaseStages configuration"); return cb(null, report); } var originalSeverity = report.severity; var beforeSend = [].concat(opts.beforeSend).concat(this.config.beforeSend); var onBeforeSendErr = function (err) { _this._logger.error("Error occurred in beforeSend callback, continuing anyway\u2026"); _this._logger.error(err); }; _$asyncSome_5(beforeSend, _$runBeforeSend_16(report, onBeforeSendErr), function (err, preventSend) { if (err) onBeforeSendErr(err); if (preventSend) { _this._logger.debug("Report not sent due to beforeSend callback"); return cb(null, report); } // only leave a crumb for the error if actually got sent if (_this.config.autoBreadcrumbs) { _this.leaveBreadcrumb(report.errorClass, { errorClass: report.errorClass, errorMessage: report.errorMessage, severity: report.severity }, 'error'); } if (originalSeverity !== report.severity) { report._handledState.severityReason = { type: 'userCallbackSetSeverity' }; } _this._delivery.sendReport(_this._logger, _this.config, { apiKey: report.apiKey || _this.config.apiKey, notifier: _this.notifier, events: [report] }, function (err) { return cb(err, report); }); }); }; return BugsnagClient; }(); var normaliseError = function (error, opts, logger) { var synthesizedErrorFramesToSkip = 3; var createAndLogUsageError = function (reason) { var msg = generateNotifyUsageMessage(reason); logger.warn(LOG_USAGE_ERR_PREFIX + " " + msg); return new Error(REPORT_USAGE_ERR_PREFIX + " " + msg); }; var err; var errorFramesToSkip = 0; var _opts; switch (typeof error) { case 'string': if (typeof opts === 'string') { // ≤v3 used to have a notify('ErrorName', 'Error message') interface // report usage/deprecation errors if this function is called like that err = createAndLogUsageError('string/string'); _opts = { metaData: { notifier: { notifyArgs: [error, opts] } } }; } else { err = new Error(String(error)); errorFramesToSkip = synthesizedErrorFramesToSkip; } break; case 'number': case 'boolean': err = new Error(String(error)); break; case 'function': err = createAndLogUsageError('function'); break; case 'object': if (error !== null && (_$iserror_11(error) || error.__isBugsnagReport)) { err = error; } else if (error !== null && hasNecessaryFields(error)) { err = new Error(error.message || error.errorMessage); err.name = error.name || error.errorClass; errorFramesToSkip = synthesizedErrorFramesToSkip; } else { err = createAndLogUsageError(error === null ? 'null' : 'unsupported object'); } break; default: err = createAndLogUsageError('nothing'); } return { err: err, errorFramesToSkip: errorFramesToSkip, _opts: _opts }; }; var hasNecessaryFields = function (error) { return (typeof error.name === 'string' || typeof error.errorClass === 'string') && (typeof error.message === 'string' || typeof error.errorMessage === 'string'); }; var generateConfigErrorMessage = function (errors) { return "Bugsnag configuration error\n" + __map_3(errors, function (err) { return "\"" + err.key + "\" " + err.message + " \n got " + stringify(err.value); }).join('\n\n'); }; var generateNotifyUsageMessage = function (actual) { return "notify() expected error/opts parameters, got " + actual; }; var stringify = function (val) { return typeof val === 'object' ? JSON.stringify(val) : String(val); }; var _$BugsnagClient_3 = BugsnagClient; var _$safeJsonStringify_21 = function (data, replacer, space, opts) { var filterKeys = opts && opts.filterKeys ? opts.filterKeys : []; var filterPaths = opts && opts.filterPaths ? opts.filterPaths : []; return JSON.stringify(prepareObjForSerialization(data, filterKeys, filterPaths), replacer, space); }; var MAX_DEPTH = 20; var MAX_EDGES = 25000; var MIN_PRESERVED_DEPTH = 8; var REPLACEMENT_NODE = '...'; function __isError_21(o) { return o instanceof Error || /^\[object (Error|(Dom)?Exception)\]$/.test(Object.prototype.toString.call(o)); } function throwsMessage(err) { return '[Throws: ' + (err ? err.message : '?') + ']'; } function find(haystack, needle) { for (var i = 0, len = haystack.length; i < len; i++) { if (haystack[i] === needle) return true; } return false; } // returns true if the string `path` starts with any of the provided `paths` function isDescendent(paths, path) { for (var i = 0, len = paths.length; i < len; i++) { if (path.indexOf(paths[i]) === 0) return true; } return false; } function shouldFilter(patterns, key) { for (var i = 0, len = patterns.length; i < len; i++) { if (typeof patterns[i] === 'string' && patterns[i] === key) return true; if (patterns[i] && typeof patterns[i].test === 'function' && patterns[i].test(key)) return true; } return false; } function __isArray_21(obj) { return Object.prototype.toString.call(obj) === '[object Array]'; } function safelyGetProp(obj, prop) { try { return obj[prop]; } catch (err) { return throwsMessage(err); } } function prepareObjForSerialization(obj, filterKeys, filterPaths) { var seen = []; // store references to objects we have seen before var edges = 0; function visit(obj, path) { function edgesExceeded() { return path.length > MIN_PRESERVED_DEPTH && edges > MAX_EDGES; } edges++; if (path.length > MAX_DEPTH) return REPLACEMENT_NODE; if (edgesExceeded()) return REPLACEMENT_NODE; if (obj === null || typeof obj !== 'object') return obj; if (find(seen, obj)) return '[Circular]'; seen.push(obj); if (typeof obj.toJSON === 'function') { try { // we're not going to count this as an edge because it // replaces the value of the currently visited object edges--; var fResult = visit(obj.toJSON(), path); seen.pop(); return fResult; } catch (err) { return throwsMessage(err); } } var er = __isError_21(obj); if (er) { edges--; var eResult = visit({ name: obj.name, message: obj.message }, path); seen.pop(); return eResult; } if (__isArray_21(obj)) { var aResult = []; for (var i = 0, len = obj.length; i < len; i++) { if (edgesExceeded()) { aResult.push(REPLACEMENT_NODE); break; } aResult.push(visit(obj[i], path.concat('[]'))); } seen.pop(); return aResult; } var result = {}; try { for (var prop in obj) { if (!Object.prototype.hasOwnProperty.call(obj, prop)) continue; if (isDescendent(filterPaths, path.join('.')) && shouldFilter(filterKeys, prop)) { result[prop] = '[Filtered]'; continue; } if (edgesExceeded()) { result[prop] = REPLACEMENT_NODE; break; } result[prop] = visit(safelyGetProp(obj, prop), path.concat(prop)); } } catch (e) {} seen.pop(); return result; } return visit(obj, []); } var _$jsonPayload_12 = {}; /* removed: var _$safeJsonStringify_21 = require('@bugsnag/safe-json-stringify'); */; var REPORT_FILTER_PATHS = ['events.[].app', 'events.[].metaData', 'events.[].user', 'events.[].breadcrumbs', 'events.[].request', 'events.[].device']; var SESSION_FILTER_PATHS = ['device', 'app', 'user']; _$jsonPayload_12.report = function (report, filterKeys) { var payload = _$safeJsonStringify_21(report, null, null, { filterPaths: REPORT_FILTER_PATHS, filterKeys: filterKeys }); if (payload.length > 10e5) { delete report.events[0].metaData; report.events[0].metaData = { notifier: "WARNING!\nSerialized payload was " + payload.length / 10e5 + "MB (limit = 1MB)\nmetaData was removed" }; payload = _$safeJsonStringify_21(report, null, null, { filterPaths: REPORT_FILTER_PATHS, filterKeys: filterKeys }); if (payload.length > 10e5) throw new Error('payload exceeded 1MB limit'); } return payload; }; _$jsonPayload_12.session = function (report, filterKeys) { var payload = _$safeJsonStringify_21(report, null, null, { filterPaths: SESSION_FILTER_PATHS, filterKeys: filterKeys }); if (payload.length > 10e5) throw new Error('payload exceeded 1MB limit'); return payload; }; var http = require("http"); var https = require("https"); var ___require_25 = require("url"), parse = ___require_25.parse; var _$request_25 = function (_ref, cb) { var url = _ref.url, headers = _ref.headers, body = _ref.body, agent = _ref.agent; var didError = false; var onError = function (err) { if (didError) return; didError = true; cb(err); }; var parsedUrl = parse(url); var secure = parsedUrl.protocol === 'https:'; var transport = secure ? https : http; var req = transport.request({ method: 'POST', hostname: parsedUrl.hostname, port: parsedUrl.port, path: parsedUrl.path, headers: headers }); req.on('error', onError); req.on('response', function (res) { bufferResponse(res, function (err, body) { if (err) return onError(err); if (res.statusCode < 200 || res.statusCode >= 300) { return onError(new Error("Bad statusCode from API: " + res.statusCode + "\n" + body)); } cb(null, body); }); }); req.write(body); req.end(); }; var bufferResponse = function (stream, cb) { var data = ''; stream.on('error', cb); stream.setEncoding('utf8'); stream.on('data', function (d) { data += d; }); stream.on('end', function () { return cb(null, data); }); }; /* removed: var _$jsonPayload_12 = require('@bugsnag/core/lib/json-payload'); */; var __isoDate_24 = _$esUtils_8.isoDate; /* removed: var _$request_25 = require('./request'); */; var _$delivery_24 = function () { return { sendReport: function (logger, config, report, cb) { if (cb === void 0) { cb = function () {}; } var _cb = function (err) { if (err) logger.error("Report failed to send\u2026\n" + (err && err.stack ? err.stack : err), err); cb(err); }; try { _$request_25({ url: config.endpoints.notify, headers: { 'Content-Type': 'application/json', 'Bugsnag-Api-Key': report.apiKey || config.apiKey, 'Bugsnag-Payload-Version': '4', 'Bugsnag-Sent-At': __isoDate_24() }, body: _$jsonPayload_12.report(report, config.filters), agent: config.agent }, function (err, body) { return _cb(err); }); } catch (e) { _cb(e); } }, sendSession: function (logger, config, session, cb) { if (cb === void 0) { cb = function () {}; } var _cb = function (err) { if (err) logger.error("Session failed to send\u2026\n" + (err && err.stack ? err.stack : err), err); cb(err); }; try { _$request_25({ url: config.endpoints.sessions, headers: { 'Content-Type': 'application/json', 'Bugsnag-Api-Key': config.apiKey, 'Bugsnag-Payload-Version': '1', 'Bugsnag-Sent-At': __isoDate_24() }, body: _$jsonPayload_12.session(session, config.filters), agent: config.agent }, function (err) { return _cb(err); }); } catch (e) { _cb(e); } } }; }; // for now just expose the builtin process global from node.js var _$process_1 = global.process; function ___extends_26() { ___extends_26 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return ___extends_26.apply(this, arguments); } var schema = _$config_4.schema; var __reduce_26 = _$esUtils_8.reduce; var __stringWithLength_26 = _$validators_17.stringWithLength; var __os_26 = require("os"); /* removed: var _$process_1 = require('process'); */; var _$config_26 = { projectRoot: { defaultValue: function () { return _$process_1.cwd(); }, validate: function (value) { return value === null || __stringWithLength_26(value); }, message: 'should be string' }, hostname: { defaultValue: function () { return __os_26.hostname(); }, message: 'should be a string', validate: function (value) { return value === null || __stringWithLength_26(value); } }, logger: ___extends_26({}, schema.logger, { defaultValue: function () { return getPrefixedConsole(); } }), releaseStage: ___extends_26({}, schema.releaseStage, { defaultValue: function () { return _$process_1.env.NODE_ENV || 'production'; } }), agent: { defaultValue: function () { return undefined; }, message: 'should be an HTTP(s) agent', validate: function (value) { return value === undefined || isAgent(value); } }, onUncaughtException: { defaultValue: function () { return function (err, report, logger) { logger.error("Uncaught exception" + getContext(report) + ", the process will now terminate\u2026\n" + (err && err.stack ? err.stack : err)); _$process_1.exit(1); }; }, message: 'should be a function', validate: function (value) { return typeof value === 'function'; } }, onUnhandledRejection: { defaultValue: function () { return function (err, report, logger) { logger.error("Unhandled rejection" + getContext(report) + "\u2026\n" + (err && err.stack ? err.stack : err)); }; }, message: 'should be a function', validate: function (value) { return typeof value === 'function'; } } }; var getPrefixedConsole = function () { return __reduce_26(['debug', 'info', 'warn', 'error'], function (accum, method) { var consoleMethod = console[method] || console.log; accum[method] = consoleMethod.bind(console, '[bugsnag]'); return accum; }, {}); }; var getContext = function (report) { return report.request && Object.keys(report.request).length ? " at " + report.request.httpMethod + " " + (report.request.path || report.request.url) : ""; }; var isAgent = function (value) { return typeof value === 'object' && value !== null || typeof value === 'boolean'; }; var _$nodeFallbackStack_13 = {}; // The utilities in this file are used to save the stackframes from a known execution context // to use when a subsequent error has no stack frames. This happens with a lot of // node's builtin async callbacks when they return from the native layer with no context // for example: // // fs.readFile('does not exist', (err) => { // /* node 8 */ // err.stack = "ENOENT: no such file or directory, open 'nope'" // /* node 4,6 */ // err.stack = "Error: ENOENT: no such file or directory, open 'nope'\n at Error (native)" // }) // Gets the stack string for the current execution context _$nodeFallbackStack_13.getStack = function () { // slice(3) removes the first line + this function's frame + the caller's frame, // so the stack begins with the caller of this function return new Error().stack.split('\n').slice(3).join('\n'); }; // Given an Error and a fallbackStack from getStack(), use the fallbackStack // if error.stack has no genuine stackframes (according to the example above) _$nodeFallbackStack_13.maybeUseFallbackStack = function (err, fallbackStack) { var lines = err.stack.split('\n'); if (lines.length === 1 || lines.length === 2 && /at Error \(native\)/.test(lines[1])) { err.stack = lines[0] + "\n" + fallbackStack; } return err; }; /* removed: var _$BugsnagReport_22 = require('../report'); */; /* removed: var _$iserror_11 = require('./iserror'); */; var _$reportFromError_15 = function (maybeError, handledState) { var actualError = _$iserror_11(maybeError) ? maybeError : new Error('Handled a non-error. See "error" tab for more detail.'); var report = new _$BugsnagReport_22(actualError.name, actualError.message, _$BugsnagReport_22.getStacktrace(actualError), handledState, maybeError); if (maybeError !== actualError) report.updateMetaData('error', 'non-error value', String(maybeError)); return report; }; /* eslint node/no-deprecated-api: [error, {ignoreModuleItems: ["domain"]}] */ var domain = require("domain"); /* removed: var _$reportFromError_15 = require('@bugsnag/core/lib/report-from-error'); */; var getStack = _$nodeFallbackStack_13.getStack, maybeUseFallbackStack = _$nodeFallbackStack_13.maybeUseFallbackStack; var _$contextualize_28 = { name: 'contextualize', init: function (client) { var contextualize = function (fn, opts) { // capture a stacktrace in case a resulting error has nothing var fallbackStack = getStack(); var dom = domain.create(); dom.on('error', function (err) { // check if the stacktrace has no context, if so, if so append the frames we created earlier if (err.stack) maybeUseFallbackStack(err, fallbackStack); var report = _$reportFromError_15(err, { severity: 'error', unhandled: true, severityReason: { type: 'unhandledException' } }); client.notify(report, opts, function (e, report) { if (e) client._logger.error('Failed to send report to Bugsnag'); client.config.onUncaughtException(err, report, client._logger); }); }); process.nextTick(function () { return dom.run(fn); }); }; return contextualize; } }; /* removed: var _$reportFromError_15 = require('@bugsnag/core/lib/report-from-error'); */; var __getStack_29 = _$nodeFallbackStack_13.getStack, __maybeUseFallbackStack_29 = _$nodeFallbackStack_13.maybeUseFallbackStack; var _$intercept_29 = { name: 'intercept', init: function (client) { var intercept = function (opts, cb) { if (cb === void 0) { cb = function () {}; } if (typeof opts === 'function') { cb = opts; opts = {}; } // capture a stacktrace in case a resulting error has nothing var fallbackStack = __getStack_29(); return function (err) { if (err) { // check if the stacktrace has no context, if so, if so append the frames we created earlier if (err.stack) __maybeUseFallbackStack_29(err, fallbackStack); var report = _$reportFromError_15(err, { severity: 'warning', unhandled: false, severityReason: { type: 'callbackErrorIntercept' } }); client.notify(report, opts); return; } for (var _len = arguments.length, data = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { data[_key - 1] = arguments[_key]; } cb.apply(void 0, data); // eslint-disable-line }; }; return intercept; } }; function ___extends_30() { ___extends_30 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return ___extends_30.apply(this, arguments); } var __isoDate_30 = _$esUtils_8.isoDate; /* * Automatically detects browser device details */ var _$device_30 = { init: function (client) { var device = { hostname: client.config.hostname // merge with anything already set on the client }; client.device = ___e