You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

78 lines
2.9 KiB
JavaScript

"use strict";
const { validateArguments, validateValue } = require("@validatem/core");
const required = require("@validatem/required");
const isString = require("@validatem/is-string");
const dynamic = require("@validatem/dynamic");
const defaultTo = require("@validatem/default-to");
const isFunction = require("@validatem/is-function");
const forbidSpecialProperties = require("./validators/forbid-special-properties");
const isErrorConstructor = require("./validators/is-error-constructor");
const isContextSchema = require("./validators/is-context-schema");
function safeAssign(a, ... rest) {
let previousPrototype = a.__proto__;
Object.assign(a, ... rest);
a.__proto__ = previousPrototype;
return a;
}
module.exports = function createCustomErrorType(_name, _options) {
let [ name, options ] = validateArguments(arguments, {
name: [ required, isString ],
options: [ defaultTo({}), dynamic((options) => ({
inheritsFrom: [ isErrorConstructor, defaultTo.literal(Error) ],
context: [ forbidSpecialProperties, options.contextSchema ],
contextSchema: [ isContextSchema ],
populate: [ isFunction, defaultTo.literal((message, context) => {
return { message, context };
}) ]
}))]
});
// NOTE: Intentionally not using `function ErrorConstructor` since this mucks up the default stacktrace display.
let ErrorConstructor = function (... args) {
let populateResult = options.populate(...args);
let { message, context } = validateValue(populateResult, {
message: [ required, isString ],
context: [ forbidSpecialProperties, options.contextSchema, defaultTo({}) ]
});
// We want this to ideally show at the top of any debug/inspect output for the error object
this._help = "https://www.npmjs.com/package/error-chain";
// We cannot just `Error.call(this, message)` as that will result in the wrong error name in the .stack property
this.message = message;
// TODO: Apparently Safari doesn't have a stack at all? https://stackoverflow.com/a/5251506
if (typeof Error.captureStackTrace !== "undefined") {
Error.captureStackTrace(this, this.constructor);
} else {
// TODO: Figure out whether this has the same wrong-error-name issue
this.stack = (new Error()).stack;
}
ErrorConstructor._assignDefaultProperties(this);
safeAssign(this, context);
};
ErrorConstructor._assignDefaultProperties = function (errorObject) {
/* This is split out into a separate method, to make it possible to call it recursively up the parent error type tree, without invoking a full constructor for each. */
if (options.inheritsFrom._assignDefaultProperties != null) {
options.inheritsFrom._assignDefaultProperties(errorObject);
}
safeAssign(errorObject, options.context);
};
ErrorConstructor.prototype = Object.create(options.inheritsFrom.prototype);
ErrorConstructor.prototype._errorChainError = true;
ErrorConstructor.prototype.name = name;
ErrorConstructor.prototype.constructor = ErrorConstructor;
return ErrorConstructor;
};