errlop
Version:
An extended Error class that envelops a parent error, such that the stack trace contains the causation
211 lines (210 loc) • 7.89 kB
JavaScript
;
/* eslint-disable @typescript-eslint/no-explicit-any */
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Convert to a valid number or null.
* @param input
*/
function getNumber(input) {
if (input == null || input === '')
return null;
var 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 "[".concat(code, "]: ").concat(stack);
return stack;
}
/** Errlop, an extended Error class that envelops a parent Error to provide ancestry stack inforation. */
var Errlop = /** @class */ (function (_super) {
__extends(Errlop, _super);
/**
* Turn the input and parent into an Errlop instance.
* @param input
* @param parent
*/
function Errlop(input, parent) {
var _a, e_1, _b;
if (parent === void 0) { parent = null; }
var _this = this;
var _c, _d;
if (!input)
throw new Error('Attempted to create an Errlop without an input');
// construct with message
_this = _super.call(this, input.message || input) || this;
// 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)) {
(_a = _this.ancestors).push.apply(_a, __spreadArray([], __read(_this.parent.ancestors), false));
}
}
try {
// exitCode, code, level
for (var _e = __values(__spreadArray([input, _this], __read(_this.ancestors), false)), _f = _e.next(); !_f.done; _f = _e.next()) {
var error = _f.value;
if (_this.exitCode == null) {
_this.exitCode = getExitCode(error);
}
if (_this.code === '' && error.code != null && error.code !== '') {
_this.code = (_c = getNumber(error.code)) !== null && _c !== void 0 ? _c : error.code.toString();
}
if (_this.level === '' && error.level != null && error.level !== '') {
_this.level = (_d = getNumber(error.level)) !== null && _d !== void 0 ? _d : error.level.toString();
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
}
finally { if (e_1) throw e_1.error; }
}
// 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 = __spreadArray([_this], __read(_this.ancestors), false).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(function (s) { return Boolean(s); }) // filter out Error instances that have no stack
.join(Errlop.stackSeparator);
return _this;
}
Errlop.isErrlop = function (value) {
return value && (value instanceof this || value.klass === this);
};
Errlop.isError = function (value) {
return value instanceof Error || Errlop.isErrlop(value);
};
/**
* Ensure that the value is an Errlop instance
* @param value
*/
Errlop.ensure = function (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
*/
Errlop.create = function (input, parent) {
if (parent === void 0) { parent = null; }
return new this(input, parent);
};
// static methods
/** The separator to use for the stack entries */
Errlop.stackSeparator = '\n↳ ';
return Errlop;
}(Error));
exports.default = Errlop;