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