UNPKG

errlop

Version:

An extended Error class that envelops a parent error, such that the stack trace contains the causation

144 lines (143 loc) 4.93 kB
"use strict"; /* eslint-disable @typescript-eslint/no-explicit-any */ Object.defineProperty(exports, "__esModule", { value: true }); /** * Convert to a valid number or null. * @param input */ function getNumber(input) { if (input == null || input === '') return null; const number = Number(input); if (isNaN(number)) return null; return number; } /** * Fetch the exit code from the value * @param value */ function getExitCode(value) { if (value != null) { if (typeof value.exitCode !== 'undefined') return getNumber(value.exitCode); if (typeof value.errno !== 'undefined') return getNumber(value.errno); if (typeof value.code !== 'undefined') return getNumber(value.code); } return null; } /** * Prepend a code to the stack if applicable. * @param code * @param stack */ function prependCode(code, stack) { if (code && typeof code === 'string' && stack.includes(code) === false) return `[${code}]: ${stack}`; return stack; } /** Errlop, an extended Error class that envelops a parent Error to provide ancestry stack inforation. */ class Errlop extends Error { /** * Turn the input and parent into an Errlop instance. * @param input * @param parent */ constructor(input, parent = null) { var _a, _b; if (!input) throw new Error('Attempted to create an Errlop without an input'); // construct with message super(input.message || input); // implements this.parent = null; this.ancestors = []; this.exitCode = null; this.orphanStack = ''; this.code = ''; this.level = ''; /** Duck typing so native classes can work with transpiled classes, as otherwise they would fail instanceof checks. */ this.klass = Errlop; // parent // if override not set, fallback to parent or cause if (!parent) parent = input.parent || input.cause; // if override, or parent/cause was set, then set this.parent if (parent) { if (Errlop.isError(parent)) { this.parent = parent; } else { this.parent = new Errlop(parent); } } // ancestors, assumed to only exist on Errlop if (this.parent) { this.ancestors.push(this.parent); if (Errlop.isErrlop(this.parent)) { this.ancestors.push(...this.parent.ancestors); } } // exitCode, code, level for (const error of [input, this, ...this.ancestors]) { if (this.exitCode == null) { this.exitCode = getExitCode(error); } if (this.code === '' && error.code != null && error.code !== '') { this.code = (_a = getNumber(error.code)) !== null && _a !== void 0 ? _a : error.code.toString(); } if (this.level === '' && error.level != null && error.level !== '') { this.level = (_b = getNumber(error.level)) !== null && _b !== void 0 ? _b : error.level.toString(); } } // orphanStack this.orphanStack = prependCode(this.code, (input.orphanStack || input.stack || this.stack || // this.stack should exist, unless something that extended Errlop broke it this.message || this || '').toString()); // stack this.stack = [this, ...this.ancestors] .map(function (error) { // error is either Errlop or Error, however that doesn't stop extenders from breaking them return prependCode(error.code, (error.orphanStack || error.stack || // those should exist, unless something that extended Error broke it error.message || error || '').toString()); }) .filter((s) => Boolean(s)) // filter out Error instances that have no stack .join(Errlop.stackSeparator); } static isErrlop(value) { return value && (value instanceof this || value.klass === this); } static isError(value) { return value instanceof Error || Errlop.isErrlop(value); } /** * Ensure that the value is an Errlop instance * @param value */ static ensure(value) { return this.isErrlop(value) ? value : this.create(value, null); } /** * Syntactic sugar for Errlop class creation. * Enables `Errlop.create(...)` to achieve `new Errlop(...)` * @param input * @param parent */ static create(input, parent = null) { return new this(input, parent); } } // static methods /** The separator to use for the stack entries */ Errlop.stackSeparator = '\n↳ '; exports.default = Errlop;