UNPKG

@honeybadger-io/core

Version:
655 lines 23.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.logDeprecatedMethod = exports.globalThisOrWindow = exports.isBrowserConfig = exports.clone = exports.formatCGIData = exports.filterUrl = exports.filter = exports.generateStackTrace = exports.endpoint = exports.instrumentConsole = exports.instrument = exports.isErrorObject = exports.makeNotice = exports.logger = exports.sanitize = exports.shallowClone = exports.runAfterNotifyHandlers = exports.runBeforeNotifyHandlers = exports.getSourceForBacktrace = exports.getCauses = exports.calculateBacktraceShift = exports.DEFAULT_BACKTRACE_SHIFT = exports.makeBacktrace = exports.objectIsExtensible = exports.objectIsEmpty = exports.mergeNotice = exports.merge = void 0; /* eslint-disable prefer-rest-params */ var stackTraceParser = __importStar(require("stacktrace-parser")); function merge(obj1, obj2) { var result = {}; for (var k in obj1) { result[k] = obj1[k]; } for (var k in obj2) { result[k] = obj2[k]; } return result; } exports.merge = merge; function mergeNotice(notice1, notice2) { var result = merge(notice1, notice2); if (notice1.context && notice2.context) { result.context = merge(notice1.context, notice2.context); } return result; } exports.mergeNotice = mergeNotice; function objectIsEmpty(obj) { for (var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) { return false; } } return true; } exports.objectIsEmpty = objectIsEmpty; function objectIsExtensible(obj) { if (typeof Object.isExtensible !== 'function') { return true; } return Object.isExtensible(obj); } exports.objectIsExtensible = objectIsExtensible; function makeBacktrace(stack, filterHbSourceCode, logger) { if (filterHbSourceCode === void 0) { filterHbSourceCode = false; } if (logger === void 0) { logger = console; } if (!stack) { return []; } try { var backtrace = stackTraceParser .parse(stack) .map(function (line) { return { file: line.file, method: line.methodName, number: line.lineNumber, column: line.column }; }); if (filterHbSourceCode) { backtrace.splice(0, calculateBacktraceShift(backtrace)); } return backtrace; } catch (err) { logger.debug(err); return []; } } exports.makeBacktrace = makeBacktrace; function isFrameFromHbSourceCode(frame) { var hasHbFile = false; var hasHbMethod = false; if (frame.file) { hasHbFile = frame.file.toLowerCase().indexOf('@honeybadger-io') > -1; } if (frame.method) { hasHbMethod = frame.method.toLowerCase().indexOf('@honeybadger-io') > -1; } return hasHbFile || hasHbMethod; } exports.DEFAULT_BACKTRACE_SHIFT = 3; /** * If {@link generateStackTrace} is used, we want to exclude frames that come from * Honeybadger's source code. * * Logic: * - For each frame, increment the shift if source code is from Honeybadger * - If a frame from an <anonymous> file is encountered increment the shift ONLY if between Honeybadger source code * (i.e. previous and next frames are from Honeybadger) * - Exit when frame encountered is not from Honeybadger source code * * Note: this will not always work, especially in browser versions where code * is minified, uglified and bundled. * For those cases we default to 3: * - generateStackTrace * - makeNotice * - notify */ function calculateBacktraceShift(backtrace) { var shift = 0; for (var i = 0; i < backtrace.length; i++) { var frame = backtrace[i]; if (isFrameFromHbSourceCode(frame)) { shift++; continue; } if (!frame.file || frame.file === '<anonymous>') { var nextFrame = backtrace[i + 1]; if (nextFrame && isFrameFromHbSourceCode(nextFrame)) { shift++; continue; } } break; } return shift || exports.DEFAULT_BACKTRACE_SHIFT; } exports.calculateBacktraceShift = calculateBacktraceShift; function getCauses(notice, logger) { if (notice.cause) { var causes = []; var cause = notice; // @ts-ignore this throws an error if tsconfig.json has strict: true while (causes.length < 3 && (cause = cause.cause)) { causes.push({ class: cause.name, message: cause.message, backtrace: typeof cause.stack == 'string' ? makeBacktrace(cause.stack, false, logger) : null }); } return causes; } return []; } exports.getCauses = getCauses; function getSourceForBacktrace(backtrace, getSourceFileHandler) { return __awaiter(this, void 0, void 0, function () { var result, index, trace, fileContent; return __generator(this, function (_a) { switch (_a.label) { case 0: result = []; if (!getSourceFileHandler || !backtrace || !backtrace.length) { return [2 /*return*/, result]; } index = 0; _a.label = 1; case 1: if (!backtrace.length) return [3 /*break*/, 3]; trace = backtrace.splice(0)[index]; return [4 /*yield*/, getSourceFileHandler(trace.file)]; case 2: fileContent = _a.sent(); result[index] = getSourceCodeSnippet(fileContent, trace.number, trace.column, 2); index++; return [3 /*break*/, 1]; case 3: return [2 /*return*/, result]; } }); }); } exports.getSourceForBacktrace = getSourceForBacktrace; function runBeforeNotifyHandlers(notice, handlers) { var results = []; var result = true; for (var i = 0, len = handlers.length; i < len; i++) { var handler = handlers[i]; var handlerResult = handler(notice); if (handlerResult === false) { result = false; } results.push(handlerResult); } return { results: results, result: result }; } exports.runBeforeNotifyHandlers = runBeforeNotifyHandlers; function runAfterNotifyHandlers(notice, handlers, error) { if (notice && notice.afterNotify) { notice.afterNotify(error, notice); } for (var i = 0, len = handlers.length; i < len; i++) { handlers[i](error, notice); } return true; } exports.runAfterNotifyHandlers = runAfterNotifyHandlers; // Returns a new object with properties from other object. function shallowClone(obj) { if (typeof (obj) !== 'object' || obj === null) { return {}; } var result = {}; for (var k in obj) { result[k] = obj[k]; } return result; } exports.shallowClone = shallowClone; function sanitize(obj, maxDepth) { if (maxDepth === void 0) { maxDepth = 8; } var seenObjects = []; function seen(obj) { if (!obj || typeof (obj) !== 'object') { return false; } for (var i = 0; i < seenObjects.length; i++) { var value = seenObjects[i]; if (value === obj) { return true; } } seenObjects.push(obj); return false; } function canSerialize(obj) { var typeOfObj = typeof obj; // Functions are TMI if (/function/.test(typeOfObj)) { // Let special toJSON method pass as it's used by JSON.stringify (#722) return obj.name === 'toJSON'; } // Symbols can't convert to strings. if (/symbol/.test(typeOfObj)) { return false; } if (obj === null) { return false; } // No prototype, likely created with `Object.create(null)`. if (typeof obj === 'object' && typeof obj.hasOwnProperty === 'undefined') { return false; } return true; } function serialize(obj, depth) { if (depth === void 0) { depth = 0; } if (depth >= maxDepth) { return '[DEPTH]'; } // Inspect invalid types if (!canSerialize(obj)) { return Object.prototype.toString.call(obj); } // Halt circular references if (seen(obj)) { return '[RECURSION]'; } // Serialize inside arrays if (Array.isArray(obj)) { return obj.map(function (o) { return safeSerialize(o, depth + 1); }); } // Serialize inside objects if (typeof (obj) === 'object') { var ret = {}; for (var k in obj) { var v = obj[k]; if (Object.prototype.hasOwnProperty.call(obj, k) && (k != null) && (v != null)) { ret[k] = safeSerialize(v, depth + 1); } } return ret; } // Return everything else untouched return obj; } function safeSerialize(obj, depth) { if (depth === void 0) { depth = 0; } try { return serialize(obj, depth); } catch (e) { return "[ERROR] ".concat(e); } } return safeSerialize(obj); } exports.sanitize = sanitize; function logger(client) { var log = function (method) { return function () { var _a; var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (method === 'debug') { if (!client.config.debug) { return; } // Log at default level so that you don't need to also enable verbose // logging in Chrome. method = 'log'; } args.unshift('[Honeybadger]'); (_a = client.config.logger)[method].apply(_a, args); }; }; return { log: log('log'), info: log('info'), debug: log('debug'), warn: log('warn'), error: log('error') }; } exports.logger = logger; /** * Converts any object into a notice object (which at minimum has the same * properties as Error, but supports additional Honeybadger properties.) */ function makeNotice(thing) { var notice; if (!thing) { notice = {}; } else if (isErrorObject(thing)) { var e = thing; notice = merge(thing, { name: e.name, message: e.message, stack: e.stack, cause: e.cause }); } else if (typeof thing === 'object') { notice = shallowClone(thing); } else { var m = String(thing); notice = { message: m }; } return notice; } exports.makeNotice = makeNotice; function isErrorObject(thing) { return thing instanceof Error || Object.prototype.toString.call(thing) === '[object Error]'; // Important for cross-realm objects } exports.isErrorObject = isErrorObject; /** * Instrument an existing function inside an object (usually global). * @param {!Object} object * @param {!String} name * @param {!Function} replacement */ // eslint-disable-next-line @typescript-eslint/no-explicit-any function instrument(object, name, replacement) { if (!object || !name || !replacement || !(name in object)) { return; } try { var original = object[name]; while (original && original.__hb_original) { original = original.__hb_original; } object[name] = replacement(original); object[name].__hb_original = original; } catch (_e) { // Ignores errors where "original" is a restricted object (see #1001) // Uncaught Error: Permission denied to access property "__hb_original" // Also ignores: // Error: TypeError: Cannot set property onunhandledrejection of [object Object] which has only a getter // User-Agent: Mozilla/5.0 (Linux; Android 10; SAMSUNG SM-G960F) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/12.1 Chrome/79.0.3945.136 Mobile Safari/537.36 } } exports.instrument = instrument; var _consoleAlreadyInstrumented = false; var listeners = []; // eslint-disable-next-line @typescript-eslint/no-explicit-any function instrumentConsole(_window, handler) { if (!_window || !_window.console || !handler) { return; } listeners.push(handler); if (_consoleAlreadyInstrumented) { return; } _consoleAlreadyInstrumented = true; ['debug', 'info', 'warn', 'error', 'log'].forEach(function (level) { instrument(_window.console, level, function hbLogger(original) { return function () { var args = Array.prototype.slice.call(arguments); listeners.forEach(function (listener) { try { listener(level, args); } catch (_e) { // ignore // should never reach here because instrument method already wraps with try/catch block } }); if (typeof original === 'function') { Function.prototype.apply.call(original, _window.console, arguments); } }; }); }); } exports.instrumentConsole = instrumentConsole; function endpoint(base, path) { var endpoint = base.trim().replace(/\/$/, ''); path = path.trim().replace(/(^\/|\/$)/g, ''); return "".concat(endpoint, "/").concat(path); } exports.endpoint = endpoint; function generateStackTrace() { try { throw new Error(''); } catch (e) { if (e.stack) { return e.stack; } } var maxStackSize = 10; var stack = []; var curr = arguments.callee; while (curr && stack.length < maxStackSize) { if (/function(?:\s+([\w$]+))+\s*\(/.test(curr.toString())) { stack.push(RegExp.$1 || '<anonymous>'); } else { stack.push('<anonymous>'); } try { curr = curr.caller; } catch (e) { break; } } return stack.join('\n'); } exports.generateStackTrace = generateStackTrace; function filter(obj, filters) { if (!is('Object', obj)) { return; } if (!is('Array', filters)) { filters = []; } var seen = []; function filter(obj) { var k, newObj; if (is('Object', obj) || is('Array', obj)) { if (seen.indexOf(obj) !== -1) { return '[CIRCULAR DATA STRUCTURE]'; } seen.push(obj); } if (is('Object', obj)) { newObj = {}; for (k in obj) { if (filterMatch(k, filters)) { newObj[k] = '[FILTERED]'; } else { newObj[k] = filter(obj[k]); } } return newObj; } if (is('Array', obj)) { return obj.map(function (v) { return filter(v); }); } if (is('Function', obj)) { return '[FUNC]'; } return obj; } return filter(obj); } exports.filter = filter; function filterMatch(key, filters) { for (var i = 0; i < filters.length; i++) { if (key.toLowerCase().indexOf(filters[i].toLowerCase()) !== -1) { return true; } } return false; } function is(type, obj) { var klass = Object.prototype.toString.call(obj).slice(8, -1); return obj !== undefined && obj !== null && klass === type; } function filterUrl(url, filters) { if (!filters) { return url; } if (typeof url !== 'string') { return url; } var query = url.split(/\?/, 2)[1]; if (!query) { return url; } var result = url; query.split(/[&]\s?/).forEach(function (pair) { var _a = pair.split('=', 2), key = _a[0], value = _a[1]; if (filterMatch(key, filters)) { result = result.replace("".concat(key, "=").concat(value), "".concat(key, "=[FILTERED]")); } }); return result; } exports.filterUrl = filterUrl; function formatCGIData(vars, prefix) { if (prefix === void 0) { prefix = ''; } var formattedVars = {}; Object.keys(vars).forEach(function (key) { var formattedKey = prefix + key.replace(/\W/g, '_').toUpperCase(); formattedVars[formattedKey] = vars[key]; }); return formattedVars; } exports.formatCGIData = formatCGIData; function clone(obj) { return JSON.parse(JSON.stringify(obj)); } exports.clone = clone; var THRESHOLD_COLUMN_NUMBER = 10000; var THRESHOLD_LINE_LENGTH = 10000; var THRESHOLD_FILE_SIZE = 200000; // 200KB threshold function getThresholdExceededSnippet(lineNumber) { var _a; return _a = {}, _a[lineNumber] = 'SOURCE_SIZE_TOO_LARGE', _a; } function getSourceCodeSnippet(fileData, lineNumber, columnNumber, sourceRadius) { if (sourceRadius === void 0) { sourceRadius = 2; } if (!fileData) { return null; } // If column number is provided and very high, it's likely a bundled/minified file if (columnNumber && columnNumber > THRESHOLD_COLUMN_NUMBER) { return getThresholdExceededSnippet(lineNumber); } // If file is very large, it's likely bundled if (fileData.length > THRESHOLD_FILE_SIZE) { return getThresholdExceededSnippet(lineNumber); } var lines = fileData.split('\n'); // add one empty line because array index starts from 0, but error line number is counted from 1 lines.unshift(''); // Check if the target line is extremely long var targetLine = lines[lineNumber]; if (targetLine && targetLine.length > THRESHOLD_LINE_LENGTH) { return getThresholdExceededSnippet(lineNumber); } var start = lineNumber - sourceRadius; var end = lineNumber + sourceRadius; var result = {}; for (var i = start; i <= end; i++) { var line = lines[i]; if (typeof line === 'string') { result[i] = line; } } return result; } function isBrowserConfig(config) { return config.async !== undefined; } exports.isBrowserConfig = isBrowserConfig; /** globalThis has fairly good support. But just in case, lets check its defined. * @see {https://caniuse.com/?search=globalThis} */ function globalThisOrWindow() { if (typeof globalThis !== 'undefined') { return globalThis; } if (typeof self !== 'undefined') { return self; } return window; } exports.globalThisOrWindow = globalThisOrWindow; var _deprecatedMethodCalls = {}; /** * Logs a deprecation warning, every X calls to the method. */ function logDeprecatedMethod(logger, oldMethod, newMethod, callCountThreshold) { if (callCountThreshold === void 0) { callCountThreshold = 100; } var key = "".concat(oldMethod, "-").concat(newMethod); if (typeof _deprecatedMethodCalls[key] === 'undefined') { _deprecatedMethodCalls[key] = 0; } if (_deprecatedMethodCalls[key] % callCountThreshold !== 0) { _deprecatedMethodCalls[key]++; return; } var msg = "Deprecation warning: ".concat(oldMethod, " has been deprecated; please use ").concat(newMethod, " instead."); logger.warn(msg); _deprecatedMethodCalls[key]++; } exports.logDeprecatedMethod = logDeprecatedMethod; //# sourceMappingURL=util.js.map