"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"); 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); Object.assign(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); } Object.assign(this, options.context); }; ErrorConstructor.prototype = Object.create(options.inheritsFrom.prototype); ErrorConstructor.prototype._errorChainError = true; ErrorConstructor.prototype.name = name; ErrorConstructor.prototype.constructor = ErrorConstructor; return ErrorConstructor; };