UNPKG

@bugsnag/node

Version:

Bugsnag error reporter for Node.js

1,492 lines (1,425 loc) 92.6 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; var Breadcrumb = /*#__PURE__*/function () { function Breadcrumb(message, metadata, type, timestamp) { if (timestamp === void 0) { timestamp = new Date(); } this.type = type; this.message = message; this.metadata = metadata; this.timestamp = timestamp; } var _proto = Breadcrumb.prototype; _proto.toJSON = function toJSON() { return { type: this.type, name: this.message, timestamp: this.timestamp, metaData: this.metadata }; }; return Breadcrumb; }(); var _$Breadcrumb_7 = Breadcrumb; var _$breadcrumbTypes_12 = ['navigation', 'request', 'process', 'log', 'user', 'state', 'error', 'manual']; // Array#reduce var _$reduce_22 = 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; }; /* removed: var _$reduce_22 = require('./reduce'); */; // Array#filter var _$filter_17 = function (arr, fn) { return _$reduce_22(arr, function (accum, item, i, arr) { return !fn(item, i, arr) ? accum : accum.concat(item); }, []); }; /* removed: var _$reduce_22 = require('./reduce'); */; // Array#includes var _$includes_18 = function (arr, x) { return _$reduce_22(arr, function (accum, item, i, arr) { return accum === true || item === x; }, false); }; // Array#isArray var _$isArray_19 = function (obj) { return Object.prototype.toString.call(obj) === '[object Array]'; }; /* eslint-disable-next-line no-prototype-builtins */ var _hasDontEnumBug = !{ toString: null }.propertyIsEnumerable('toString'); var _dontEnums = ['toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor']; // Object#keys var _$keys_20 = 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; }; var _$intRange_31 = 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; }; }; /* removed: var _$filter_17 = require('../es-utils/filter'); */; /* removed: var _$isArray_19 = require('../es-utils/is-array'); */; var _$listOfFunctions_32 = function (value) { return typeof value === 'function' || _$isArray_19(value) && _$filter_17(value, function (f) { return typeof f === 'function'; }).length === value.length; }; var _$stringWithLength_33 = function (value) { return typeof value === 'string' && !!value.length; }; var _$config_9 = {}; /* removed: var _$filter_17 = require('./lib/es-utils/filter'); */; /* removed: var _$reduce_22 = require('./lib/es-utils/reduce'); */; /* removed: var _$keys_20 = require('./lib/es-utils/keys'); */; /* removed: var _$isArray_19 = require('./lib/es-utils/is-array'); */; /* removed: var _$includes_18 = require('./lib/es-utils/includes'); */; /* removed: var _$intRange_31 = require('./lib/validators/int-range'); */; /* removed: var _$stringWithLength_33 = require('./lib/validators/string-with-length'); */; /* removed: var _$listOfFunctions_32 = require('./lib/validators/list-of-functions'); */; /* removed: var _$breadcrumbTypes_12 = require('./lib/breadcrumb-types'); */; var defaultErrorTypes = function () { return { unhandledExceptions: true, unhandledRejections: true }; }; _$config_9.schema = { apiKey: { defaultValue: function () { return null; }, message: 'is required', validate: _$stringWithLength_33 }, appVersion: { defaultValue: function () { return undefined; }, message: 'should be a string', validate: function (value) { return value === undefined || _$stringWithLength_33(value); } }, appType: { defaultValue: function () { return undefined; }, message: 'should be a string', validate: function (value) { return value === undefined || _$stringWithLength_33(value); } }, autoDetectErrors: { defaultValue: function () { return true; }, message: 'should be true|false', validate: function (value) { return value === true || value === false; } }, enabledErrorTypes: { defaultValue: function () { return defaultErrorTypes(); }, message: 'should be an object containing the flags { unhandledExceptions:true|false, unhandledRejections:true|false }', allowPartialObject: true, validate: function (value) { // ensure we have an object if (typeof value !== 'object' || !value) return false; var providedKeys = _$keys_20(value); var defaultKeys = _$keys_20(defaultErrorTypes()); // ensure it only has a subset of the allowed keys if (_$filter_17(providedKeys, function (k) { return _$includes_18(defaultKeys, k); }).length < providedKeys.length) return false; // ensure all of the values are boolean if (_$filter_17(_$keys_20(value), function (k) { return typeof value[k] !== 'boolean'; }).length > 0) return false; return true; } }, onError: { defaultValue: function () { return []; }, message: 'should be a function or array of functions', validate: _$listOfFunctions_32 }, onSession: { defaultValue: function () { return []; }, message: 'should be a function or array of functions', validate: _$listOfFunctions_32 }, onBreadcrumb: { defaultValue: function () { return []; }, message: 'should be a function or array of functions', validate: _$listOfFunctions_32 }, endpoints: { defaultValue: function (endpoints) { // only apply the default value if no endpoints have been provided, otherwise prevent delivery by setting to null if (typeof endpoints === 'undefined') { return { notify: 'https://notify.bugsnag.com', sessions: 'https://sessions.bugsnag.com' }; } else { return { notify: null, sessions: null }; } }, message: 'should be an object containing endpoint URLs { notify, sessions }', validate: function (val) { return ( // first, ensure it's an object val && typeof val === 'object' && // notify and sessions must always be set _$stringWithLength_33(val.notify) && _$stringWithLength_33(val.sessions) && // ensure no keys other than notify/session are set on endpoints object _$filter_17(_$keys_20(val), function (k) { return !_$includes_18(['notify', 'sessions'], k); }).length === 0 ); } }, autoTrackSessions: { defaultValue: function (val) { return true; }, message: 'should be true|false', validate: function (val) { return val === true || val === false; } }, enabledReleaseStages: { defaultValue: function () { return null; }, message: 'should be an array of strings', validate: function (value) { return value === null || _$isArray_19(value) && _$filter_17(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 25; }, message: 'should be a number ≤100', validate: function (value) { return _$intRange_31(0, 100)(value); } }, enabledBreadcrumbTypes: { defaultValue: function () { return _$breadcrumbTypes_12; }, message: "should be null or a list of available breadcrumb types (" + _$breadcrumbTypes_12.join(',') + ")", validate: function (value) { return value === null || _$isArray_19(value) && _$reduce_22(value, function (accum, maybeType) { if (accum === false) return accum; return _$includes_18(_$breadcrumbTypes_12, maybeType); }, true); } }, context: { defaultValue: function () { return undefined; }, message: 'should be a string', validate: function (value) { return value === undefined || typeof value === 'string'; } }, user: { defaultValue: function () { return {}; }, message: 'should be an object with { id, email, name } properties', validate: function (value) { return value === null || value && _$reduce_22(_$keys_20(value), function (accum, key) { return accum && _$includes_18(['id', 'email', 'name'], key); }, true); } }, metadata: { defaultValue: function () { return {}; }, message: 'should be an object', validate: function (value) { return typeof value === 'object' && value !== null; } }, 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_22(['debug', 'info', 'warn', 'error'], function (accum, method) { return accum && typeof value[method] === 'function'; }, true); } }, redactedKeys: { defaultValue: function () { return ['password']; }, message: 'should be an array of strings|regexes', validate: function (value) { return _$isArray_19(value) && value.length === _$filter_17(value, function (s) { return typeof s === 'string' || s && typeof s.test === 'function'; }).length; } }, plugins: { defaultValue: function () { return []; }, message: 'should be an array of plugin objects', validate: function (value) { return _$isArray_19(value) && value.length === _$filter_17(value, function (p) { return p && typeof p === 'object' && typeof p.load === 'function'; }).length; } }, featureFlags: { defaultValue: function () { return []; }, message: 'should be an array of objects that have a "name" property', validate: function (value) { return _$isArray_19(value) && value.length === _$filter_17(value, function (feature) { return feature && typeof feature === 'object' && typeof feature.name === 'string'; }).length; } }, reportUnhandledPromiseRejectionsAsHandled: { defaultValue: function () { return false; }, message: 'should be true|false', validate: function (value) { return value === true || value === false; } }, sendPayloadChecksums: { defaultValue: function () { return false; }, message: 'should be true|false', validate: function (value) { return value === true || value === false; } } }; var _$errorStackParser_15 = require("error-stack-parser"); // extends helper from babel // https://github.com/babel/babel/blob/916429b516e6466fd06588ee820e40e025d7f3a3/packages/babel-helpers/src/helpers.js#L377-L393 var _$assign_16 = 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; }; /* removed: var _$reduce_22 = require('./reduce'); */; // Array#map var _$map_21 = function (arr, fn) { return _$reduce_22(arr, function (accum, item, i, arr) { return accum.concat(fn(item, i, arr)); }, []); }; var _$safeJsonStringify_5 = function (data, replacer, space, opts) { var redactedKeys = opts && opts.redactedKeys ? opts.redactedKeys : []; var redactedPaths = opts && opts.redactedPaths ? opts.redactedPaths : []; return JSON.stringify(prepareObjForSerialization(data, redactedKeys, redactedPaths), replacer, space); }; var MAX_DEPTH = 20; var MAX_EDGES = 25000; var MIN_PRESERVED_DEPTH = 8; var REPLACEMENT_NODE = '...'; function isError(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 shouldRedact(patterns, key) { for (var i = 0, len = patterns.length; i < len; i++) { if (typeof patterns[i] === 'string' && patterns[i].toLowerCase() === key.toLowerCase()) return true; if (patterns[i] && typeof patterns[i].test === 'function' && patterns[i].test(key)) return true; } return false; } function __isArray_5(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, redactedKeys, redactedPaths) { 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(obj); if (er) { edges--; var eResult = visit({ name: obj.name, message: obj.message }, path); seen.pop(); return eResult; } if (__isArray_5(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(redactedPaths, path.join('.')) && shouldRedact(redactedKeys, prop)) { result[prop] = '[REDACTED]'; 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, []); } /* removed: var _$map_21 = require('./es-utils/map'); */; /* removed: var _$filter_17 = require('./es-utils/filter'); */; /* removed: var _$isArray_19 = require('./es-utils/is-array'); */; /* removed: var _$safeJsonStringify_5 = require('@bugsnag/safe-json-stringify'); */; function add(existingFeatures, existingFeatureKeys, name, variant) { if (typeof name !== 'string') { return; } if (variant === undefined) { variant = null; } else if (variant !== null && typeof variant !== 'string') { variant = _$safeJsonStringify_5(variant); } var existingIndex = existingFeatureKeys[name]; if (typeof existingIndex === 'number') { existingFeatures[existingIndex] = { name: name, variant: variant }; return; } existingFeatures.push({ name: name, variant: variant }); existingFeatureKeys[name] = existingFeatures.length - 1; } function merge(existingFeatures, newFeatures, existingFeatureKeys) { if (!_$isArray_19(newFeatures)) { return; } for (var i = 0; i < newFeatures.length; ++i) { var feature = newFeatures[i]; if (feature === null || typeof feature !== 'object') { continue; } // 'add' will handle if 'name' doesn't exist & 'variant' is optional add(existingFeatures, existingFeatureKeys, feature.name, feature.variant); } return existingFeatures; } // convert feature flags from a map of 'name -> variant' into the format required // by the Bugsnag Event API: // [{ featureFlag: 'name', variant: 'variant' }, { featureFlag: 'name 2' }] function toEventApi(featureFlags) { return _$map_21(_$filter_17(featureFlags, Boolean), function (_ref) { var name = _ref.name, variant = _ref.variant; var flag = { featureFlag: name }; // don't add a 'variant' property unless there's actually a value if (typeof variant === 'string') { flag.variant = variant; } return flag; }); } function clear(features, featuresIndex, name) { var existingIndex = featuresIndex[name]; if (typeof existingIndex === 'number') { features[existingIndex] = null; delete featuresIndex[name]; } } var _$featureFlagDelegate_23 = { add: add, clear: clear, merge: merge, toEventApi: toEventApi }; // Given `err` which may be an error, does it have a stack property which is a string? var _$hasStack_24 = 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; }; var _$iserror_25 = require("iserror"); /* removed: var _$assign_16 = require('./es-utils/assign'); */; var __add_27 = function (state, section, keyOrObj, maybeVal) { var _updates; if (!section) return; var updates; // addMetadata("section", null) -> clears section if (keyOrObj === null) return __clear_27(state, section); // normalise the two supported input types into object form if (typeof keyOrObj === 'object') updates = keyOrObj; if (typeof keyOrObj === 'string') updates = (_updates = {}, _updates[keyOrObj] = maybeVal, _updates); // exit if we don't have an updates object at this point if (!updates) return; // preventing the __proto__ property from being used as a key if (section === '__proto__' || section === 'constructor' || section === 'prototype') { return; } // ensure a section with this name exists if (!state[section]) state[section] = {}; // merge the updates with the existing section state[section] = _$assign_16({}, state[section], updates); }; var get = function (state, section, key) { if (typeof section !== 'string') return undefined; if (!key) { return state[section]; } if (state[section]) { return state[section][key]; } return undefined; }; var __clear_27 = function (state, section, key) { if (typeof section !== 'string') return; // clear an entire section if (!key) { delete state[section]; return; } // preventing the __proto__ property from being used as a key if (section === '__proto__' || section === 'constructor' || section === 'prototype') { return; } // clear a single value from a section if (state[section]) { delete state[section][key]; } }; var _$metadataDelegate_27 = { add: __add_27, get: get, clear: __clear_27 }; function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } /* removed: var _$errorStackParser_15 = require('./lib/error-stack-parser'); */; var StackGenerator = require("stack-generator"); /* removed: var _$hasStack_24 = require('./lib/has-stack'); */; /* removed: var _$map_21 = require('./lib/es-utils/map'); */; /* removed: var _$reduce_22 = require('./lib/es-utils/reduce'); */; /* removed: var _$filter_17 = require('./lib/es-utils/filter'); */; /* removed: var _$assign_16 = require('./lib/es-utils/assign'); */; /* removed: var _$metadataDelegate_27 = require('./lib/metadata-delegate'); */; /* removed: var _$featureFlagDelegate_23 = require('./lib/feature-flag-delegate'); */; /* removed: var _$iserror_25 = require('./lib/iserror'); */; var Event = /*#__PURE__*/function () { function Event(errorClass, errorMessage, stacktrace, handledState, originalError) { if (stacktrace === void 0) { stacktrace = []; } if (handledState === void 0) { handledState = defaultHandledState(); } this.apiKey = undefined; this.context = undefined; this.groupingHash = undefined; this.originalError = originalError; this._handledState = handledState; this.severity = this._handledState.severity; this.unhandled = this._handledState.unhandled; this.app = {}; this.device = {}; this.request = {}; this.response = {}; this.breadcrumbs = []; this.threads = []; this._metadata = {}; this._features = []; this._featuresIndex = {}; this._user = {}; this._session = undefined; this._correlation = undefined; this._groupingDiscriminator = undefined; this.errors = [createBugsnagError(errorClass, errorMessage, Event.__type, stacktrace)]; // Flags. // Note these are not initialised unless they are used // to save unnecessary bytes in the browser bundle /* this.attemptImmediateDelivery, default: true */ } var _proto = Event.prototype; _proto.addMetadata = function addMetadata(section, keyOrObj, maybeVal) { return _$metadataDelegate_27.add(this._metadata, section, keyOrObj, maybeVal); } /** * Associate this event with a specific trace. This is usually done automatically when * using bugsnag-js-performance, but can also be set manually if required. * * @param traceId the ID of the trace the event occurred within * @param spanId the ID of the span that the event occurred within */; _proto.setTraceCorrelation = function setTraceCorrelation(traceId, spanId) { if (typeof traceId === 'string') { this._correlation = _extends({ traceId: traceId }, typeof spanId === 'string' ? { spanId: spanId } : {}); } }; _proto.getGroupingDiscriminator = function getGroupingDiscriminator() { return this._groupingDiscriminator; }; _proto.setGroupingDiscriminator = function setGroupingDiscriminator(value) { var previousValue = this._groupingDiscriminator; if (typeof value === 'string' || value === null || value === undefined) this._groupingDiscriminator = value; return previousValue; }; _proto.getMetadata = function getMetadata(section, key) { return _$metadataDelegate_27.get(this._metadata, section, key); }; _proto.clearMetadata = function clearMetadata(section, key) { return _$metadataDelegate_27.clear(this._metadata, section, key); }; _proto.addFeatureFlag = function addFeatureFlag(name, variant) { if (variant === void 0) { variant = null; } _$featureFlagDelegate_23.add(this._features, this._featuresIndex, name, variant); }; _proto.addFeatureFlags = function addFeatureFlags(featureFlags) { _$featureFlagDelegate_23.merge(this._features, featureFlags, this._featuresIndex); }; _proto.getFeatureFlags = function getFeatureFlags() { return _$featureFlagDelegate_23.toEventApi(this._features); }; _proto.clearFeatureFlag = function clearFeatureFlag(name) { _$featureFlagDelegate_23.clear(this._features, this._featuresIndex, name); }; _proto.clearFeatureFlags = function clearFeatureFlags() { this._features = []; this._featuresIndex = {}; }; _proto.getUser = function getUser() { return this._user; }; _proto.setUser = function setUser(id, email, name) { this._user = { id: id, email: email, name: name }; }; _proto.toJSON = function toJSON() { return { payloadVersion: '4', exceptions: _$map_21(this.errors, function (er) { return _$assign_16({}, er, { message: er.errorMessage }); }), severity: this.severity, unhandled: this._handledState.unhandled, severityReason: this._handledState.severityReason, app: this.app, device: this.device, request: this.request, response: this.response, breadcrumbs: this.breadcrumbs, context: this.context, groupingHash: this.groupingHash, groupingDiscriminator: this._groupingDiscriminator, metaData: this._metadata, user: this._user, session: this._session, featureFlags: this.getFeatureFlags(), correlation: this._correlation }; }; return Event; }(); // 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 ensureString = function (str) { return typeof str === 'string' ? str : ''; }; function createBugsnagError(errorClass, errorMessage, type, stacktrace) { return { errorClass: ensureString(errorClass), errorMessage: ensureString(errorMessage), type: type, 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; } }, []) }; } function getCauseStack(error) { if (error.cause) { return [error].concat(getCauseStack(error.cause)); } else { return [error]; } } // Helpers Event.getStacktrace = function (error, errorFramesToSkip, backtraceFramesToSkip) { if (_$hasStack_24(error)) return _$errorStackParser_15.parse(error).slice(errorFramesToSkip); // error wasn't provided or didn't have a stacktrace so try to walk the callstack try { return _$filter_17(StackGenerator.backtrace(), function (frame) { return (frame.functionName || '').indexOf('StackGenerator$$') === -1; }).slice(1 + backtraceFramesToSkip); } catch (e) { return []; } }; Event.create = function (maybeError, tolerateNonErrors, handledState, component, errorFramesToSkip, logger) { if (errorFramesToSkip === void 0) { errorFramesToSkip = 0; } var _normaliseError = normaliseError(maybeError, tolerateNonErrors, component, logger), error = _normaliseError[0], internalFrames = _normaliseError[1]; var event; try { var stacktrace = Event.getStacktrace(error, // if an error was created/throw in the normaliseError() function, we need to // tell the getStacktrace() function to skip the number of frames we know will // be from our own functions. This is added to the number of frames deep we // were told about internalFrames > 0 ? 1 + internalFrames + errorFramesToSkip : 0, // if there's no stacktrace, the callstack may be walked to generated one. // this is how many frames should be removed because they come from our library 1 + errorFramesToSkip); event = new Event(error.name, error.message, stacktrace, handledState, maybeError); } catch (e) { event = new Event(error.name, error.message, [], handledState, maybeError); } if (error.name === 'InvalidError') { event.addMetadata("" + component, 'non-error parameter', makeSerialisable(maybeError)); } if (error.cause) { var _event$errors; var causes = getCauseStack(error).slice(1); var normalisedCauses = _$map_21(causes, function (cause) { // Only get stacktrace for error causes that are a valid JS Error and already have a stack var stacktrace = _$iserror_25(cause) && _$hasStack_24(cause) ? _$errorStackParser_15.parse(cause) : []; var _normaliseError2 = normaliseError(cause, true, 'error cause'), error = _normaliseError2[0]; if (error.name === 'InvalidError') event.addMetadata('error cause', makeSerialisable(cause)); return createBugsnagError(error.name, error.message, Event.__type, stacktrace); }); (_event$errors = event.errors).push.apply(_event$errors, normalisedCauses); } return event; }; var makeSerialisable = function (err) { if (err === null) return 'null'; if (err === undefined) return 'undefined'; return err; }; var normaliseError = function (maybeError, tolerateNonErrors, component, logger) { var error; var internalFrames = 0; var createAndLogInputError = function (reason) { var verb = component === 'error cause' ? 'was' : 'received'; if (logger) logger.warn(component + " " + verb + " a non-error: \"" + reason + "\""); var err = new Error(component + " " + verb + " a non-error. See \"" + component + "\" tab for more detail."); err.name = 'InvalidError'; return err; }; // In some cases: // // - the promise rejection handler (both in the browser and node) // - the node uncaughtException handler // // We are really limited in what we can do to get a stacktrace. So we use the // tolerateNonErrors option to ensure that the resulting error communicates as // such. if (!tolerateNonErrors) { if (_$iserror_25(maybeError)) { error = maybeError; } else { error = createAndLogInputError(typeof maybeError); internalFrames += 2; } } else { switch (typeof maybeError) { case 'string': case 'number': case 'boolean': error = new Error(String(maybeError)); internalFrames += 1; break; case 'function': error = createAndLogInputError('function'); internalFrames += 2; break; case 'object': if (maybeError !== null && _$iserror_25(maybeError)) { error = maybeError; } else if (maybeError !== null && hasNecessaryFields(maybeError)) { error = new Error(maybeError.message || maybeError.errorMessage); error.name = maybeError.name || maybeError.errorClass; internalFrames += 1; } else { error = createAndLogInputError(maybeError === null ? 'null' : 'unsupported object'); internalFrames += 2; } break; default: error = createAndLogInputError('nothing'); internalFrames += 2; } } if (!_$hasStack_24(error)) { // in IE10/11 a new Error() doesn't have a stacktrace until you throw it, so try that here try { throw error; } catch (e) { if (_$hasStack_24(e)) { error = e; // if the error only got a stacktrace after we threw it here, we know it // will only have one extra internal frame from this function, regardless // of whether it went through createAndLogInputError() or not internalFrames = 1; } } } return [error, internalFrames]; }; // default value for stacktrace.type Event.__type = 'browserjs'; var hasNecessaryFields = function (error) { return (typeof error.name === 'string' || typeof error.errorClass === 'string') && (typeof error.message === 'string' || typeof error.errorMessage === 'string'); }; var _$Event_10 = Event; // This is a heavily modified/simplified version of // https://github.com/othiym23/async-some // with the logic flipped so that it is akin to the // synchronous "every" method instead of "some". // run the asynchronous test function (fn) over each item in the array (arr) // in series until: // - fn(item, cb) => calls cb(null, false) // - or the end of the array is reached // the callback (cb) will be passed (null, false) if any of the items in arr // caused fn to call back with false, otherwise it will be passed (null, true) var _$asyncEvery_11 = function (arr, fn, cb) { var index = 0; var next = function () { if (index >= arr.length) return cb(null, true); fn(arr[index], function (err, result) { if (err) return cb(err); if (result === false) return cb(null, false); index++; next(); }); }; next(); }; /* removed: var _$asyncEvery_11 = require('./async-every'); */; var _$callbackRunner_13 = function (callbacks, event, onCallbackError, cb) { // This function is how we support different kinds of callback: // - synchronous - return value // - node-style async with callback - cb(err, value) // - promise/thenable - resolve(value) // It normalises each of these into the lowest common denominator – a node-style callback var runMaybeAsyncCallback = function (fn, cb) { if (typeof fn !== 'function') return cb(null); try { // if function appears sync… if (fn.length !== 2) { var ret = fn(event); // 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, val); }); }, // reject function (err) { setTimeout(function () { onCallbackError(err); return cb(null, true); }); }); } return cb(null, ret); } // if function is async… fn(event, function (err, result) { if (err) { onCallbackError(err); return cb(null); } cb(null, result); }); } catch (e) { onCallbackError(e); cb(null); } }; _$asyncEvery_11(callbacks, runMaybeAsyncCallback, cb); }; var _$syncCallbackRunner_30 = function (callbacks, callbackArg, callbackType, logger) { var ignore = false; var cbs = callbacks.slice(); while (!ignore) { if (!cbs.length) break; try { ignore = cbs.pop()(callbackArg) === false; } catch (e) { logger.error("Error occurred in " + callbackType + " callback, continuing anyway\u2026"); logger.error(e); } } return ignore; }; var _$pad_4 = function pad(num, size) { var s = '000000000' + num; return s.substr(s.length - size); }; /* removed: var _$pad_4 = require('./pad.js'); */; var os = require("os"); function getHostname() { try { return os.hostname(); } catch (e) { /** * This is most likely Windows 7 which is known to cause os.hostname() to break * @see https://github.com/nodejs/node/issues/41297 * @see https://github.com/libuv/libuv/issues/3260 * * Fallback to take hostname from environment variables * @see https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/hostname#notes */ // eslint-disable-next-line no-underscore-dangle return process.env._CLUSTER_NETWORK_NAME_ || process.env.COMPUTERNAME || 'hostname'; } } var padding = 2, pid = _$pad_4(process.pid.toString(36), padding), hostname = getHostname(), length = hostname.length, hostId = _$pad_4(hostname.split('').reduce(function (prev, char) { return +prev + char.charCodeAt(0); }, +length + 36).toString(36), padding); var _$fingerprint_2 = function fingerprint() { return pid + hostId; }; /** * Check the provided value is a valid device id * @param {unknown} value * @returns */ var _$isCuid_3 = function isCuid(value) { return typeof value === 'string' && /^c[a-z0-9]{20,32}$/.test(value); }; /** * 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_2 = require('./lib/fingerprint.js'); */; /* removed: var _$isCuid_3 = require('./lib/is-cuid.js'); */; /* removed: var _$pad_4 = require('./lib/pad.js'); */; var c = 0, blockSize = 4, base = 36, discreteValues = Math.pow(base, blockSize); function randomBlock() { return _$pad_4((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_4(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_2(), // Grab some more chars from Math.random() random = randomBlock() + randomBlock(); return letter + timestamp + counter + print + random; } cuid.fingerprint = _$fingerprint_2; cuid.isCuid = _$isCuid_3; var _$cuid_1 = cuid; /* removed: var _$cuid_1 = require('@bugsnag/cuid'); */; var Session = /*#__PURE__*/function () { function Session() { this.id = _$cuid_1(); this.startedAt = new Date(); this._handled = 0; this._unhandled = 0; this._user = {}; this.app = {}; this.device = {}; } var _proto = Session.prototype; _proto.getUser = function getUser() { return this._user; }; _proto.setUser = function setUser(id, email, name) { this._user = { id: id, email: email, name: name }; }; _proto.toJSON = function toJSON() { return { id: this.id, startedAt: this.startedAt, events: { handled: this._handled, unhandled: this._unhandled } }; }; _proto._track = function _track(event) { this[event._handledState.unhandled ? '_unhandled' : '_handled'] += 1; }; return Session; }(); var _$Session_34 = Session; /* removed: var _$config_9 = require('./config'); */; /* removed: var _$Event_10 = require('./event'); */; /* removed: var _$Breadcrumb_7 = require('./breadcrumb'); */; /* removed: var _$Session_34 = require('./session'); */; /* removed: var _$map_21 = require('./lib/es-utils/map'); */; /* removed: var _$includes_18 = require('./lib/es-utils/includes'); */; /* removed: var _$filter_17 = require('./lib/es-utils/filter'); */; /* removed: var _$reduce_22 = require('./lib/es-utils/reduce'); */; /* removed: var _$keys_20 = require('./lib/es-utils/keys'); */; /* removed: var _$assign_16 = require('./lib/es-utils/assign'); */; /* removed: var _$callbackRunner_13 = require('./lib/callback-runner'); */; /* removed: var _$metadataDelegate_27 = require('./lib/metadata-delegate'); */; /* removed: var _$syncCallbackRunner_30 = require('./lib/sync-callback-runner'); */; /* removed: var _$breadcrumbTypes_12 = require('./lib/breadcrumb-types'); */; var __add_8 = _$featureFlagDelegate_23.add, __clear_8 = _$featureFlagDelegate_23.clear, __merge_8 = _$featureFlagDelegate_23.merge; var SECONDARY_ENDPOINT_API_KEY_PREFIX = '00000'; var SECONDARY_NOTIFY_ENDPOINT = 'https://notify.bugsnag.smartbear.com'; var SECONDARY_SESSIONS_ENDPOINT = 'https://sessions.bugsnag.smartbear.com'; var noop = function () {}; var Client = /*#__PURE__*/function () { function Client(configuration, schema, internalPlugins, notifier) { var _this = this; if (schema === void 0) { schema = _$config_9.schema; } if (internalPlugins === void 0) { internalPlugins = []; } // notifier id this._notifier = notifier; // intialise opts and config this._config = {}; this._schema = schema; // i/o this._delivery = { sendSession: noop, sendEvent: noop }; this._logger = { debug: noop, info: noop, warn: noop, error: noop }; // plugins this._plugins = {}; // state this._breadcrumbs = []; this._session = null; this._metadata = {}; this._featuresIndex = {}; this._features = []; this._context = undefined; this._user = {}; this._groupingDiscriminator = undefined; // callbacks: // e: onError // s: onSession // sp: onSessionPayload // b: onBreadcrumb // (note these names are minified by hand because object // properties are not safe to minify automatically) this._cbs = { e: [], s: [], sp: [], b: [] }; // expose internal constructors this.Client = Client; this.Event = _$Event_10; this.Breadcrumb = _$Breadcrumb_7; this.Session = _$Session_34; this._config = this._configure(configuration, internalPlugins); _$map_21(internalPlugins.concat(this._config.plugins), function (pl) { if (pl) _this._loadPlugin(pl); }); // when notify() is called we need to know how many frames are from our own source // this inital value is 1 not 0 because we wrap notify() to ensure it is always // bound to have the client as its `this` value – see below. this._depth = 1; var self = this; var notify = this.notify; this.notify = function () { return notify.apply(self, arguments); }; } var _proto = Client.prototype; _proto.addMetadata = function addMetadata(section, keyOrObj, maybeVal) { return _$metadataDelegate_27.add(this._metadata, section, keyOrObj, maybeVal); }; _proto.getMetadata = function getMetadata(section, key) { return _$metadataDelegate_27.get(this._metadata, section, key); }; _proto.clearMetadata = function clearMetadata(section, key) { return _$metadataDelegate_27.clear(this._metadata, section, key); }; _proto.addFeatureFlag = function addFeatureFlag(name, variant) { if (variant === void 0) { variant = null; } __add_8(this._features, this._featuresIndex, name, variant); }; _proto.addFeatureFlags = function addFeatureFlags(featureFlags) { __merge_8(this._features, featureFlags, this._featuresIndex); }; _proto.clearFeatureFlag = function clearFeatureFlag(name) { __clear_8(this._features, this._featuresIndex, name); }; _proto.clearFeatureFlags = function clearFeatureFlags() { this._features = []; this._featuresIndex = {}; }; _proto.getContext = function getContext() { return this._context; }; _proto.setContext = function setContext(c) { this._context = c; }; _proto.getGroupingDiscriminator = function getGroupingDiscriminator() { return this._groupingDiscriminator; }; _proto.setGroupingDiscriminator = function setGroupingDiscriminator(value) { var previousValue = this._groupingDiscriminator; if (typeof value === 'string' || value === null || value === undefined) this._groupingDiscriminator = value; return previousValue; }; _proto._configure = function _configure(opts, internalPlugins) { var schema = _$reduce_22(internalPlugins, function (schema, plugin) { if (plugin && plugin.configSchema) return _$assign_16({}, schema, plugin.configSchema); return schema; }, this._schema); // sendPayloadChecksums is false by default unless custom endpoints are not specified if (!opts.endpoints) { opts.sendPayloadChecksums = 'sendPayloadChecksums' in opts ? opts.sendPayloadChecksums : true; } // accumulate configuration and error messages var _reduce = _$reduce_22(_$keys_20(schema), function (accum, key) { var defaultValue = schema[key].defaultValue(opts[key]); if (opts[key] !== undefined) { var valid = schema[key].validate(opts[key]); if (!valid) { accum.errors[key] = schema[key].message; accum.config[key] = defaultValue; } else { if (schema[key].allowPartialObject) { accum.config[key] = _$assign_16(defaultValue, opts[key]); } else { accum.config[key] = opts[key]; } } } else { accum.config[key] = defaultValue; } return accum; }, { errors: {}, config: {} }), errors = _reduce.errors, config = _reduce.config; if (schema.apiKey) { // missing api key is the only fatal error if (!config.apiKey) throw new Error('No Bugsnag API Key set'); // warn about an apikey that is not of the expected format if (!/^[0-9a-f]{32}$/i.test(config.apiKey)) errors.apiKey = 'should be a string of 32 hexadecimal characters'; if (opts.endpoints === undefined && config.apiKey.indexOf(SECONDARY_ENDPOINT_API_KEY_PREFIX) === 0) { config.endpoints = { notify: SECONDARY_NOTIFY_ENDPOINT, sessions: SECONDARY_SESSIONS_ENDPOINT }; } } // update and elevate some options this._metadata = _$assign_16({}, config.metadata); __merge_8(this._features, config.featureFlags, this._featuresIndex); this._user = _$assign_16({}, config.user); this._context = config.context; if (config.logger) this._logger = config.logger; // add callbacks if (config.onError) this._cbs.e = this._cbs.e.concat(config.onError); if (config.onBreadcrumb) this._cbs.b = this._cbs.b.concat(config.onBreadcrumb); if (config.onSession) this._cbs.s = this._cbs.s.concat(config.onSession); // finally warn about any invalid config where we fell back to the default if (_$keys_20(errors).length) { this._logger.warn(generateConfigErrorMessage(errors, opts)); } return config; }; _proto.getUser = function getUser() { return this._user; }; _proto.setUser = function setUser(id, email, name) { this._user = { id: id, email: email, name: name }; }; _proto._loadPlugin = function _loadPlugin(plugin) { var result = plugin.load(this); // 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; }; _proto.getPlugin = function getPlugin(name) { return this._plugins["~" + name + "~"]; }; _proto._setDelivery = function _setDelivery(d) { this._delivery = d(this); }; _proto.startSession = function startSession() { var session = new _$Session_34(); session.app.releaseStage = this._config.releaseStage; session.app.version = this._config.appVersion; session.app.type = this._config.appType; session._user = _$assign_16({}, this._user); // run onSession callbacks var ignore = _$syncCallbackRunner_30(this._cbs.s, session, 'onSession', this._logger); if (ignore) { this._logger.debug('Session not started due to onSession callback'); return this; } return this._sessionDelegate.startSession(this, session); }; _proto.addOnError = function addOnError(fn, front) { if (front === void 0) { front = false; } this._cbs.e[front ? 'unshift' : 'push'](fn); }; _proto.removeOnError = function removeOnError(fn) { this._cbs.e = _$filter_17(this._cbs.e, function (f) { return f !== fn; }); }; _proto._addOnSessionPayload = function _addOnSessionPayload(fn) { this._cbs.sp.push(fn); }; _proto.addOnSession = function addOnSession(fn) { this._cbs.s.push(fn); }; _proto.removeOnSession = function removeOnSession(fn) { this._cbs.s = _$filter_17(this._cbs.s, function (f) { return f !== fn; }); }; _proto.addOnBreadcrumb = function addOnBreadcrumb(fn, front) { if (front === void 0) { front = false; } this._cbs.b[front ? 'unshift' : 'push'](fn); }; _proto.removeOnBreadcrumb = function removeOnBreadcrumb(fn) { this._cbs.b = _$filter_17(this._cbs.b, function (f) { return f !== fn; }); }; _proto.pauseSession = function pauseSession() { return this._sessionDelegate.pauseSession(this); }; _proto.resumeSession = function resumeSession() { return this._sessionDelegate.resumeSession(this); }; _proto.leaveBreadcrumb = function leaveBreadcrumb(message, metadata, type) { // coerce bad values so that the defaults get set message = typeof message === 'string' ? message : ''; type = typeof type === 'string' && _$includes_18(_$breadcrumbTypes_12, type) ? type : 'manual'; metadata = typeof metadata === 'object' && m