Release 5.0.0
Breaking changes
Top-level error class
The default export is now the top-level error class ModernError
.
Also, the base error class is now documented as BaseError
instead of AnyError
.
Before:
import modernErrors from 'modern-errors'
export const AnyError = modernErrors(plugins, options)
After:
import ModernError from 'modern-errors'
export const BaseError = ModernError.subclass('BaseError', {
...options,
plugins,
})
Error normalization
Creating an UnknownError
class is now optional, although still recommended. To normalize unknown errors, UnknownError
must now be explicitly passed as a second argument to BaseError.normalize()
.
Before:
export const main = function () {
try {
// ...
} catch (error) {
throw BaseError.normalize(error)
}
}
After:
export const main = function () {
try {
// ...
} catch (error) {
throw BaseError.normalize(error, UnknownError)
}
}
When UnknownError
is not passed as a second argument, BaseError.normalize()
now converts unknown errors to BaseError
instances instead.
Before:
const error = new Error('example')
assert(BaseError.normalize(error) instanceof UnknownError)
After:
const error = new Error('example')
assert(BaseError.normalize(error) instanceof BaseError)
assert(!(BaseError.normalize(error) instanceof UnknownError))
assert(BaseError.normalize(error, UnknownError) instanceof UnknownError)
Wrap error options
When wrapping errors, the outer and inner error's options are now always merged.
Before:
try {
throw new AuthError('...', innerOptions)
} catch (cause) {
// Options are now `outerOptions`. `innerOptions` are discarded.
throw new InputError('...', { ...outerOptions, cause })
}
After:
try {
throw new AuthError('...', innerOptions)
} catch (cause) {
// `outerOptions` are merged with `innerOptions`
throw new InputError('...', { ...outerOptions, cause })
}
Wrap error class
When wrapping errors, the inner error's class is now kept if the outer error's class is a parent (including BaseError
).
export const ParentError = BaseError.subclass('ParentError')
export const ChildError = ParentError.subclass('ChildError')
Before:
try {
throw new ChildError('...')
} catch (cause) {
// Now a ParentError
throw new ParentError('...', { cause })
}
After:
try {
throw new ChildError('...')
} catch (cause) {
// Still a ChildError, because that is a subclass of ParentError
throw new ParentError('...', { cause })
}
Aggregate errors
Aggregate errors must now be explicitly normalized by BaseError.normalize()
instead of being automatically normalized on creation.
Features
Global custom logic
Global custom logic can now be specified by passing the custom
option to the BaseError
. Previously, only class-specific custom logic could be specified.
Class-specific plugins
Plugins can now be specific to an error class (and its subclasses) by using the plugins
option. Previously plugins had to be applied to all error classes.
Optional wrapping
The BaseError
can now be instantiated without wrapping an error. The cause
option is now optional.
Missing stack trace
The stack trace produced when wrapping an error that does not have one has been improved.
Plugins
The following changes only impact authors of custom plugins.
info.ErrorClass
Static methods (including ErrorClass.normalize()
) can now be called on any error class, not only on BaseError
. As a consequence, info.AnyError
has been renamed to info.ErrorClass
.
info.ErrorClasses
info.ErrorClasses
is now an array instead of an object. This array might contain error classes with duplicate names.
info.errorInfo
info.errorInfo(error)
now returns the error's ErrorClass
and ErrorClasses
.