UNPKG

@bugsnag/web-worker

Version:

BugSnag error reporter for JavaScript web workers and service workers

1,565 lines (1,320 loc) 87.9 kB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports["Bugsnag"] = factory(); else root["Bugsnag"] = factory(); })(self, () => { return /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ /***/ 99: /***/ ((module, __unused_webpack_exports, __webpack_require__) => { /** * 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 */ var fingerprint = __webpack_require__(98); var isCuid = __webpack_require__(225); var pad = __webpack_require__(182); var c = 0, blockSize = 4, base = 36, discreteValues = Math.pow(base, blockSize); function randomBlock () { return pad((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(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(), // Grab some more chars from Math.random() random = randomBlock() + randomBlock(); return letter + timestamp + counter + print + random; } cuid.fingerprint = fingerprint; cuid.isCuid = isCuid; module.exports = cuid; /***/ }), /***/ 98: /***/ ((module, __unused_webpack_exports, __webpack_require__) => { var pad = __webpack_require__(182); var env = typeof window === 'object' ? window : self; var globalCount = 0; for (var prop in env) { if (Object.hasOwnProperty.call(env, prop)) globalCount++; } var mimeTypesLength = navigator.mimeTypes ? navigator.mimeTypes.length : 0; var clientId = pad((mimeTypesLength + navigator.userAgent.length).toString(36) + globalCount.toString(36), 4); module.exports = function fingerprint () { return clientId; }; /***/ }), /***/ 225: /***/ ((module) => { /** * Check the provided value is a valid device id * @param {unknown} value * @returns */ module.exports = function isCuid (value) { return typeof value === 'string' && (/^c[a-z0-9]{20,32}$/).test(value); }; /***/ }), /***/ 182: /***/ ((module) => { module.exports = function pad (num, size) { var s = '000000000' + num; return s.substr(s.length - size); }; /***/ }), /***/ 876: /***/ ((module) => { module.exports = 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 (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(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, []) } /***/ }), /***/ 482: /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function(root, factory) { 'use strict'; // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, Rhino, and browsers. /* istanbul ignore next */ if (true) { !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(550)], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); } else {} }(this, function ErrorStackParser(StackFrame) { 'use strict'; var FIREFOX_SAFARI_STACK_REGEXP = /(^|@)\S+:\d+/; var CHROME_IE_STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m; var SAFARI_NATIVE_CODE_REGEXP = /^(eval@)?(\[native code])?$/; return { /** * Given an Error object, extract the most information from it. * * @param {Error} error object * @return {Array} of StackFrames */ parse: function ErrorStackParser$$parse(error) { if (typeof error.stacktrace !== 'undefined' || typeof error['opera#sourceloc'] !== 'undefined') { return this.parseOpera(error); } else if (error.stack && error.stack.match(CHROME_IE_STACK_REGEXP)) { return this.parseV8OrIE(error); } else if (error.stack) { return this.parseFFOrSafari(error); } else { throw new Error('Cannot parse given Error object'); } }, // Separate line and column numbers from a string of the form: (URI:Line:Column) extractLocation: function ErrorStackParser$$extractLocation(urlLike) { // Fail-fast but return locations like "(native)" if (urlLike.indexOf(':') === -1) { return [urlLike]; } var regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/; var parts = regExp.exec(urlLike.replace(/[()]/g, '')); return [parts[1], parts[2] || undefined, parts[3] || undefined]; }, parseV8OrIE: function ErrorStackParser$$parseV8OrIE(error) { var filtered = error.stack.split('\n').filter(function(line) { return !!line.match(CHROME_IE_STACK_REGEXP); }, this); return filtered.map(function(line) { if (line.indexOf('(eval ') > -1) { // Throw away eval information until we implement stacktrace.js/stackframe#8 line = line.replace(/eval code/g, 'eval').replace(/(\(eval at [^()]*)|(\),.*$)/g, ''); } var sanitizedLine = line.replace(/^\s+/, '').replace(/\(eval code/g, '('); // capture and preseve the parenthesized location "(/foo/my bar.js:12:87)" in // case it has spaces in it, as the string is split on \s+ later on var location = sanitizedLine.match(/ (\((.+):(\d+):(\d+)\)$)/); // remove the parenthesized location from the line, if it was matched sanitizedLine = location ? sanitizedLine.replace(location[0], '') : sanitizedLine; var tokens = sanitizedLine.split(/\s+/).slice(1); // if a location was matched, pass it to extractLocation() otherwise pop the last token var locationParts = this.extractLocation(location ? location[1] : tokens.pop()); var functionName = tokens.join(' ') || undefined; var fileName = ['eval', '<anonymous>'].indexOf(locationParts[0]) > -1 ? undefined : locationParts[0]; return new StackFrame({ functionName: functionName, fileName: fileName, lineNumber: locationParts[1], columnNumber: locationParts[2], source: line }); }, this); }, parseFFOrSafari: function ErrorStackParser$$parseFFOrSafari(error) { var filtered = error.stack.split('\n').filter(function(line) { return !line.match(SAFARI_NATIVE_CODE_REGEXP); }, this); return filtered.map(function(line) { // Throw away eval information until we implement stacktrace.js/stackframe#8 if (line.indexOf(' > eval') > -1) { line = line.replace(/ line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g, ':$1'); } if (line.indexOf('@') === -1 && line.indexOf(':') === -1) { // Safari eval frames only have function names and nothing else return new StackFrame({ functionName: line }); } else { var functionNameRegex = /((.*".+"[^@]*)?[^@]*)(?:@)/; var matches = line.match(functionNameRegex); var functionName = matches && matches[1] ? matches[1] : undefined; var locationParts = this.extractLocation(line.replace(functionNameRegex, '')); return new StackFrame({ functionName: functionName, fileName: locationParts[0], lineNumber: locationParts[1], columnNumber: locationParts[2], source: line }); } }, this); }, parseOpera: function ErrorStackParser$$parseOpera(e) { if (!e.stacktrace || (e.message.indexOf('\n') > -1 && e.message.split('\n').length > e.stacktrace.split('\n').length)) { return this.parseOpera9(e); } else if (!e.stack) { return this.parseOpera10(e); } else { return this.parseOpera11(e); } }, parseOpera9: function ErrorStackParser$$parseOpera9(e) { var lineRE = /Line (\d+).*script (?:in )?(\S+)/i; var lines = e.message.split('\n'); var result = []; for (var i = 2, len = lines.length; i < len; i += 2) { var match = lineRE.exec(lines[i]); if (match) { result.push(new StackFrame({ fileName: match[2], lineNumber: match[1], source: lines[i] })); } } return result; }, parseOpera10: function ErrorStackParser$$parseOpera10(e) { var lineRE = /Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i; var lines = e.stacktrace.split('\n'); var result = []; for (var i = 0, len = lines.length; i < len; i += 2) { var match = lineRE.exec(lines[i]); if (match) { result.push( new StackFrame({ functionName: match[3] || undefined, fileName: match[2], lineNumber: match[1], source: lines[i] }) ); } } return result; }, // Opera 10.65+ Error.stack very similar to FF/Safari parseOpera11: function ErrorStackParser$$parseOpera11(error) { var filtered = error.stack.split('\n').filter(function(line) { return !!line.match(FIREFOX_SAFARI_STACK_REGEXP) && !line.match(/^Error created at/); }, this); return filtered.map(function(line) { var tokens = line.split('@'); var locationParts = this.extractLocation(tokens.pop()); var functionCall = (tokens.shift() || ''); var functionName = functionCall .replace(/<anonymous function(: (\w+))?>/, '$2') .replace(/\([^)]*\)/g, '') || undefined; var argsRaw; if (functionCall.match(/\(([^)]*)\)/)) { argsRaw = functionCall.replace(/^[^(]+\(([^)]*)\)$/, '$1'); } var args = (argsRaw === undefined || argsRaw === '[arguments not available]') ? undefined : argsRaw.split(','); return new StackFrame({ functionName: functionName, args: args, fileName: locationParts[0], lineNumber: locationParts[1], columnNumber: locationParts[2], source: line }); }, this); } }; })); /***/ }), /***/ 468: /***/ ((module) => { /** * Expose `isError`. */ module.exports = isError; /** * Test whether `value` is error object. * * @param {*} value * @returns {boolean} */ function isError(value) { switch (Object.prototype.toString.call(value)) { case '[object Error]': return true; case '[object Exception]': return true; case '[object DOMException]': return true; default: return value instanceof Error; } } /***/ }), /***/ 14: /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function(root, factory) { 'use strict'; // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, Rhino, and browsers. /* istanbul ignore next */ if (true) { !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(550)], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); } else {} }(this, function(StackFrame) { return { backtrace: function StackGenerator$$backtrace(opts) { var stack = []; var maxStackSize = 10; if (typeof opts === 'object' && typeof opts.maxStackSize === 'number') { maxStackSize = opts.maxStackSize; } var curr = arguments.callee; while (curr && stack.length < maxStackSize && curr['arguments']) { // Allow V8 optimizations var args = new Array(curr['arguments'].length); for (var i = 0; i < args.length; ++i) { args[i] = curr['arguments'][i]; } if (/function(?:\s+([\w$]+))+\s*\(/.test(curr.toString())) { stack.push(new StackFrame({functionName: RegExp.$1 || undefined, args: args})); } else { stack.push(new StackFrame({args: args})); } try { curr = curr.caller; } catch (e) { break; } } return stack; } }; })); /***/ }), /***/ 550: /***/ (function(module, exports) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function(root, factory) { 'use strict'; // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, Rhino, and browsers. /* istanbul ignore next */ if (true) { !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); } else {} }(this, function() { 'use strict'; function _isNumber(n) { return !isNaN(parseFloat(n)) && isFinite(n); } function _capitalize(str) { return str.charAt(0).toUpperCase() + str.substring(1); } function _getter(p) { return function() { return this[p]; }; } var booleanProps = ['isConstructor', 'isEval', 'isNative', 'isToplevel']; var numericProps = ['columnNumber', 'lineNumber']; var stringProps = ['fileName', 'functionName', 'source']; var arrayProps = ['args']; var objectProps = ['evalOrigin']; var props = booleanProps.concat(numericProps, stringProps, arrayProps, objectProps); function StackFrame(obj) { if (!obj) return; for (var i = 0; i < props.length; i++) { if (obj[props[i]] !== undefined) { this['set' + _capitalize(props[i])](obj[props[i]]); } } } StackFrame.prototype = { getArgs: function() { return this.args; }, setArgs: function(v) { if (Object.prototype.toString.call(v) !== '[object Array]') { throw new TypeError('Args must be an Array'); } this.args = v; }, getEvalOrigin: function() { return this.evalOrigin; }, setEvalOrigin: function(v) { if (v instanceof StackFrame) { this.evalOrigin = v; } else if (v instanceof Object) { this.evalOrigin = new StackFrame(v); } else { throw new TypeError('Eval Origin must be an Object or StackFrame'); } }, toString: function() { var fileName = this.getFileName() || ''; var lineNumber = this.getLineNumber() || ''; var columnNumber = this.getColumnNumber() || ''; var functionName = this.getFunctionName() || ''; if (this.getIsEval()) { if (fileName) { return '[eval] (' + fileName + ':' + lineNumber + ':' + columnNumber + ')'; } return '[eval]:' + lineNumber + ':' + columnNumber; } if (functionName) { return functionName + ' (' + fileName + ':' + lineNumber + ':' + columnNumber + ')'; } return fileName + ':' + lineNumber + ':' + columnNumber; } }; StackFrame.fromString = function StackFrame$$fromString(str) { var argsStartIndex = str.indexOf('('); var argsEndIndex = str.lastIndexOf(')'); var functionName = str.substring(0, argsStartIndex); var args = str.substring(argsStartIndex + 1, argsEndIndex).split(','); var locationString = str.substring(argsEndIndex + 1); if (locationString.indexOf('@') === 0) { var parts = /@(.+?)(?::(\d+))?(?::(\d+))?$/.exec(locationString, ''); var fileName = parts[1]; var lineNumber = parts[2]; var columnNumber = parts[3]; } return new StackFrame({ functionName: functionName, args: args || undefined, fileName: fileName, lineNumber: lineNumber || undefined, columnNumber: columnNumber || undefined }); }; for (var i = 0; i < booleanProps.length; i++) { StackFrame.prototype['get' + _capitalize(booleanProps[i])] = _getter(booleanProps[i]); StackFrame.prototype['set' + _capitalize(booleanProps[i])] = (function(p) { return function(v) { this[p] = Boolean(v); }; })(booleanProps[i]); } for (var j = 0; j < numericProps.length; j++) { StackFrame.prototype['get' + _capitalize(numericProps[j])] = _getter(numericProps[j]); StackFrame.prototype['set' + _capitalize(numericProps[j])] = (function(p) { return function(v) { if (!_isNumber(v)) { throw new TypeError(p + ' must be a Number'); } this[p] = Number(v); }; })(numericProps[j]); } for (var k = 0; k < stringProps.length; k++) { StackFrame.prototype['get' + _capitalize(stringProps[k])] = _getter(stringProps[k]); StackFrame.prototype['set' + _capitalize(stringProps[k])] = (function(p) { return function(v) { this[p] = String(v); }; })(stringProps[k]); } return StackFrame; })); /***/ }), /***/ 112: /***/ ((module) => { class Breadcrumb { constructor (message, metadata, type, timestamp = new Date()) { this.type = type this.message = message this.metadata = metadata this.timestamp = timestamp } toJSON () { return { type: this.type, name: this.message, timestamp: this.timestamp, metaData: this.metadata } } } module.exports = Breadcrumb /***/ }), /***/ 478: /***/ ((module, __unused_webpack_exports, __webpack_require__) => { const config = __webpack_require__(223) const Event = __webpack_require__(717) const Breadcrumb = __webpack_require__(112) const Session = __webpack_require__(669) const map = __webpack_require__(628) const includes = __webpack_require__(957) const filter = __webpack_require__(978) const reduce = __webpack_require__(850) const keys = __webpack_require__(188) const assign = __webpack_require__(465) const runCallbacks = __webpack_require__(969) const metadataDelegate = __webpack_require__(670) const runSyncCallbacks = __webpack_require__(171) const BREADCRUMB_TYPES = __webpack_require__(496) const { add, clear, merge } = __webpack_require__(472) const HUB_PREFIX = '00000' const HUB_NOTIFY = 'https://notify.insighthub.smartbear.com' const HUB_SESSION = 'https://sessions.insighthub.smartbear.com' const noop = () => { } class Client { constructor (configuration, schema = config.schema, internalPlugins = [], notifier) { // 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 = {} // 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 this.Breadcrumb = Breadcrumb this.Session = Session this._config = this._configure(configuration, internalPlugins) map(internalPlugins.concat(this._config.plugins), 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 const self = this const notify = this.notify this.notify = function () { return notify.apply(self, arguments) } } addMetadata (section, keyOrObj, maybeVal) { return metadataDelegate.add(this._metadata, section, keyOrObj, maybeVal) } getMetadata (section, key) { return metadataDelegate.get(this._metadata, section, key) } clearMetadata (section, key) { return metadataDelegate.clear(this._metadata, section, key) } addFeatureFlag (name, variant = null) { add(this._features, this._featuresIndex, name, variant) } addFeatureFlags (featureFlags) { merge(this._features, featureFlags, this._featuresIndex) } clearFeatureFlag (name) { clear(this._features, this._featuresIndex, name) } clearFeatureFlags () { this._features = [] this._featuresIndex = {} } getContext () { return this._context } setContext (c) { this._context = c } _configure (opts, internalPlugins) { const schema = reduce(internalPlugins, (schema, plugin) => { if (plugin && plugin.configSchema) return assign({}, 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 const { errors, config } = reduce(keys(schema), (accum, key) => { const defaultValue = schema[key].defaultValue(opts[key]) if (opts[key] !== undefined) { const 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(defaultValue, opts[key]) } else { accum.config[key] = opts[key] } } } else { accum.config[key] = defaultValue } return accum }, { errors: {}, 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.startsWith(HUB_PREFIX)) { config.endpoints = { notify: HUB_NOTIFY, sessions: HUB_SESSION } } } // update and elevate some options this._metadata = assign({}, config.metadata) merge(this._features, config.featureFlags, this._featuresIndex) this._user = assign({}, 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(errors).length) { this._logger.warn(generateConfigErrorMessage(errors, opts)) } return config } getUser () { return this._user } setUser (id, email, name) { this._user = { id, email, name } } _loadPlugin (plugin) { const 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 } getPlugin (name) { return this._plugins[`~${name}~`] } _setDelivery (d) { this._delivery = d(this) } startSession () { const session = new Session() session.app.releaseStage = this._config.releaseStage session.app.version = this._config.appVersion session.app.type = this._config.appType session._user = assign({}, this._user) // run onSession callbacks const ignore = runSyncCallbacks(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) } addOnError (fn, front = false) { this._cbs.e[front ? 'unshift' : 'push'](fn) } removeOnError (fn) { this._cbs.e = filter(this._cbs.e, f => f !== fn) } _addOnSessionPayload (fn) { this._cbs.sp.push(fn) } addOnSession (fn) { this._cbs.s.push(fn) } removeOnSession (fn) { this._cbs.s = filter(this._cbs.s, f => f !== fn) } addOnBreadcrumb (fn, front = false) { this._cbs.b[front ? 'unshift' : 'push'](fn) } removeOnBreadcrumb (fn) { this._cbs.b = filter(this._cbs.b, f => f !== fn) } pauseSession () { return this._sessionDelegate.pauseSession(this) } resumeSession () { return this._sessionDelegate.resumeSession(this) } leaveBreadcrumb (message, metadata, type) { // coerce bad values so that the defaults get set message = typeof message === 'string' ? message : '' type = (typeof type === 'string' && includes(BREADCRUMB_TYPES, type)) ? type : 'manual' metadata = typeof metadata === 'object' && metadata !== null ? metadata : {} // if no message, discard if (!message) return const crumb = new Breadcrumb(message, metadata, type) // run onBreadcrumb callbacks const ignore = runSyncCallbacks(this._cbs.b, crumb, 'onBreadcrumb', this._logger) if (ignore) { this._logger.debug('Breadcrumb not attached due to onBreadcrumb callback') return } // 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) } } _isBreadcrumbTypeEnabled (type) { const types = this._config.enabledBreadcrumbTypes return types === null || includes(types, type) } notify (maybeError, onError, postReportCallback = noop) { const event = Event.create(maybeError, true, undefined, 'notify()', this._depth + 1, this._logger) this._notify(event, onError, postReportCallback) } _notify (event, onError, postReportCallback = noop) { event.app = assign({}, event.app, { releaseStage: this._config.releaseStage, version: this._config.appVersion, type: this._config.appType }) event.context = event.context || this._context event._metadata = assign({}, event._metadata, this._metadata) event._user = assign({}, event._user, this._user) event.breadcrumbs = this._breadcrumbs.slice() merge(event._features, this._features, event._featuresIndex) // exit early if events should not be sent on the current releaseStage if (this._config.enabledReleaseStages !== null && !includes(this._config.enabledReleaseStages, this._config.releaseStage)) { this._logger.warn('Event not sent due to releaseStage/enabledReleaseStages configuration') return postReportCallback(null, event) } const originalSeverity = event.severity const onCallbackError = err => { // errors in callbacks are tolerated but we want to log them out this._logger.error('Error occurred in onError callback, continuing anyway…') this._logger.error(err) } const callbacks = [].concat(this._cbs.e).concat(onError) runCallbacks(callbacks, event, onCallbackError, (err, shouldSend) => { if (err) onCallbackError(err) if (!shouldSend) { this._logger.debug('Event not sent due to onError callback') return postReportCallback(null, event) } if (this._isBreadcrumbTypeEnabled('error')) { // only leave a crumb for the error if actually got sent Client.prototype.leaveBreadcrumb.call(this, event.errors[0].errorClass, { errorClass: event.errors[0].errorClass, errorMessage: event.errors[0].errorMessage, severity: event.severity }, 'error') } if (originalSeverity !== event.severity) { event._handledState.severityReason = { type: 'userCallbackSetSeverity' } } if (event.unhandled !== event._handledState.unhandled) { event._handledState.severityReason.unhandledOverridden = true event._handledState.unhandled = event.unhandled } if (this._session) { this._session._track(event) event._session = this._session } this._delivery.sendEvent({ apiKey: event.apiKey || this._config.apiKey, notifier: this._notifier, events: [event] }, (err) => postReportCallback(err, event)) }) } } const generateConfigErrorMessage = (errors, rawInput) => { const er = new Error( `Invalid configuration\n${map(keys(errors), key => ` - ${key} ${errors[key]}, got ${stringify(rawInput[key])}`).join('\n\n')}`) return er } const stringify = val => { switch (typeof val) { case 'string': case 'number': case 'object': return JSON.stringify(val) default: return String(val) } } module.exports = Client /***/ }), /***/ 223: /***/ ((module, __unused_webpack_exports, __webpack_require__) => { const filter = __webpack_require__(978) const reduce = __webpack_require__(850) const keys = __webpack_require__(188) const isArray = __webpack_require__(424) const includes = __webpack_require__(957) const intRange = __webpack_require__(960) const stringWithLength = __webpack_require__(142) const listOfFunctions = __webpack_require__(483) const BREADCRUMB_TYPES = __webpack_require__(496) const defaultErrorTypes = () => ({ unhandledExceptions: true, unhandledRejections: true }) module.exports.schema = { apiKey: { defaultValue: () => null, message: 'is required', validate: stringWithLength }, appVersion: { defaultValue: () => undefined, message: 'should be a string', validate: value => value === undefined || stringWithLength(value) }, appType: { defaultValue: () => undefined, message: 'should be a string', validate: value => value === undefined || stringWithLength(value) }, autoDetectErrors: { defaultValue: () => true, message: 'should be true|false', validate: value => value === true || value === false }, enabledErrorTypes: { defaultValue: () => defaultErrorTypes(), message: 'should be an object containing the flags { unhandledExceptions:true|false, unhandledRejections:true|false }', allowPartialObject: true, validate: value => { // ensure we have an object if (typeof value !== 'object' || !value) return false const providedKeys = keys(value) const defaultKeys = keys(defaultErrorTypes()) // ensure it only has a subset of the allowed keys if (filter(providedKeys, k => includes(defaultKeys, k)).length < providedKeys.length) return false // ensure all of the values are boolean if (filter(keys(value), k => typeof value[k] !== 'boolean').length > 0) return false return true } }, onError: { defaultValue: () => [], message: 'should be a function or array of functions', validate: listOfFunctions }, onSession: { defaultValue: () => [], message: 'should be a function or array of functions', validate: listOfFunctions }, onBreadcrumb: { defaultValue: () => [], message: 'should be a function or array of functions', validate: listOfFunctions }, endpoints: { defaultValue: (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: (val) => // first, ensure it's an object (val && typeof val === 'object') && ( // notify and sessions must always be set stringWithLength(val.notify) && stringWithLength(val.sessions) ) && // ensure no keys other than notify/session are set on endpoints object filter(keys(val), k => !includes(['notify', 'sessions'], k)).length === 0 }, autoTrackSessions: { defaultValue: val => true, message: 'should be true|false', validate: val => val === true || val === false }, enabledReleaseStages: { defaultValue: () => null, message: 'should be an array of strings', validate: value => value === null || (isArray(value) && filter(value, f => typeof f === 'string').length === value.length) }, releaseStage: { defaultValue: () => 'production', message: 'should be a string', validate: value => typeof value === 'string' && value.length }, maxBreadcrumbs: { defaultValue: () => 25, message: 'should be a number ≤100', validate: value => intRange(0, 100)(value) }, enabledBreadcrumbTypes: { defaultValue: () => BREADCRUMB_TYPES, message: `should be null or a list of available breadcrumb types (${BREADCRUMB_TYPES.join(',')})`, validate: value => value === null || (isArray(value) && reduce(value, (accum, maybeType) => { if (accum === false) return accum return includes(BREADCRUMB_TYPES, maybeType) }, true)) }, context: { defaultValue: () => undefined, message: 'should be a string', validate: value => value === undefined || typeof value === 'string' }, user: { defaultValue: () => ({}), message: 'should be an object with { id, email, name } properties', validate: value => (value === null) || (value && reduce( keys(value), (accum, key) => accum && includes(['id', 'email', 'name'], key), true )) }, metadata: { defaultValue: () => ({}), message: 'should be an object', validate: (value) => typeof value === 'object' && value !== null }, logger: { defaultValue: () => undefined, message: 'should be null or an object with methods { debug, info, warn, error }', validate: value => (!value) || (value && reduce( ['debug', 'info', 'warn', 'error'], (accum, method) => accum && typeof value[method] === 'function', true )) }, redactedKeys: { defaultValue: () => ['password'], message: 'should be an array of strings|regexes', validate: value => isArray(value) && value.length === filter(value, s => (typeof s === 'string' || (s && typeof s.test === 'function')) ).length }, plugins: { defaultValue: () => ([]), message: 'should be an array of plugin objects', validate: value => isArray(value) && value.length === filter(value, p => (p && typeof p === 'object' && typeof p.load === 'function') ).length }, featureFlags: { defaultValue: () => [], message: 'should be an array of objects that have a "name" property', validate: value => isArray(value) && value.length === filter(value, feature => feature && typeof feature === 'object' && typeof feature.name === 'string' ).length }, reportUnhandledPromiseRejectionsAsHandled: { defaultValue: () => false, message: 'should be true|false', validate: value => value === true || value === false }, sendPayloadChecksums: { defaultValue: () => false, message: 'should be true|false', validate: value => value === true || value === false } } /***/ }), /***/ 717: /***/ ((module, __unused_webpack_exports, __webpack_require__) => { const ErrorStackParser = __webpack_require__(480) const StackGenerator = __webpack_require__(14) const hasStack = __webpack_require__(744) const map = __webpack_require__(628) const reduce = __webpack_require__(850) const filter = __webpack_require__(978) const assign = __webpack_require__(465) const metadataDelegate = __webpack_require__(670) const featureFlagDelegate = __webpack_require__(472) const isError = __webpack_require__(43) class Event { constructor (errorClass, errorMessage, stacktrace = [], handledState = defaultHandledState(), originalError) { 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.breadcrumbs = [] this.threads = [] this._metadata = {} this._features = [] this._featuresIndex = {} this._user = {} this._session = undefined this._correlation = 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 */ } addMetadata (section, keyOrObj, maybeVal) { return metadataDelegate.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 */ setTraceCorrelation (traceId, spanId) { if (typeof traceId === 'string') { this._correlation = { traceId, ...typeof spanId === 'string' ? { spanId } : { } } } } getMetadata (section, key) { return metadataDelegate.get(this._metadata, section, key) } clearMetadata (section, key) { return metadataDelegate.clear(this._metadata, section, key) } addFeatureFlag (name, variant = null) { featureFlagDelegate.add(this._features, this._featuresIndex, name, variant) } addFeatureFlags (featureFlags) { featureFlagDelegate.merge(this._features, featureFlags, this._featuresIndex) } getFeatureFlags () { return featureFlagDelegate.toEventApi(this._features) } clearFeatureFlag (name) { featureFlagDelegate.clear(this._features, this._featuresIndex, name) } clearFeatureFlags () { this._features = [] this._featuresIndex = {} } getUser () { return this._user } setUser (id, email, name) { this._user = { id, email, name } } toJSON () { return { payloadVersion: '4', exceptions: map(this.errors, er => assign({}, er, { message: er.errorMessage })), severity: this.severity, unhandled: this._handledState.unhandled, severityReason: this._handledState.severityReason, app: this.app, device: this.device, request: this.request, breadcrumbs: this.breadcrumbs, context: this.context, groupingHash: this.groupingHash, metaData: this._metadata, user: this._user, session: this._session, featureFlags: this.getFeatureFlags(), correlation: this._correlation } } } // 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) const formatStackframe = frame => { const 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 } const normaliseFunctionName = name => /^global code$/i.test(name) ? 'global code' : name const defaultHandledState = () => ({ unhandled: false, severity: 'warning', severityReason: { type: 'handledException' } }) const ensureString = (str) => typeof str === 'string' ? str : '' function createBugsnagError (errorClass, errorMessage, type, stacktrace) { return { errorClass: ensureString(errorClass), errorMessage: ensureString(errorMessage), type, stacktrace: reduce(stacktrace, (accum, frame) => { const 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, ...getCauseStack(error.cause)] } else { return [error] } } // Helpers Event.getStacktrace = function (error, errorFramesToSkip, backtraceFramesToSkip) { if (hasStack(error)) return ErrorStackParser.parse(error).slice(errorFramesToSkip) // error wasn't provided or didn't have a stacktrace so try to walk the callstack try { return filter(StackGenerator.backtrace(), frame => (frame.functionName || '').indexOf('StackGenerator$$') === -1 ).slice(1 + backtraceFramesToSkip) } catch (e) { return [] } } Event.create = function (maybeError, tolerateNonErrors, handledState, component, errorFramesToSkip = 0, logger) { const [error, internalFrames] = normaliseError(maybeError, tolerateNonErrors, component, logger) let event try { const 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) { const causes = getCauseStack(error).slice(1) const normalisedCauses = map(causes, (cause) => { // Only get stacktrace for error causes that are a valid JS Error and already have a stack const stacktrace = (isError(cause) && hasStack(cause)) ? ErrorStackParser.parse(cause) : [] const [error] = normaliseError(cause, true, 'error cause') if (error.name === 'InvalidError') event.addMetadata('error cause', makeSerialisable(cause)) return createBugsnagError(error.name, error.message, Event.__type, stacktrace) }) event.errors.push(...normalisedCauses) } return event } const makeSerialisable = (err) => { if (err === null) return 'null' if (err === undefined) return 'undefined' return err } const normaliseError = (maybeError, tolerateNonErrors, component, logger) => { let error let internalFrames = 0 const createAndLogInputError = (reason) => { const verb = (com