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
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;
|
|
};
|