@bugsnag/web-worker
Version:
BugSnag error reporter for JavaScript web workers and service workers
1,565 lines (1,320 loc) • 87.9 kB
JavaScript
(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