- Fix
is-error-instancebeing incorrectly listed indevDependenciesinstead ofdependencies(#40).
- Fix
BaseError.normalize(error, NewErrorClass).erroris not converted toNewErrorClassif it is an instance of aBaseError's subclass. However,errorwas converted when it was an instance ofBaseErroritself, which was incorrect.
- When wrapping an error with a different class but the same
error.name, do not repeat that name in the error message
- Fix TypeScript error (#35).
- The core of the plugin
modern-errors-switchhas been split into a separate moduleswitch-functional. If you like functional programming, this might interest you.
- Add new plugin
modern-errors-beautifulwhich prettifies an error's message and stack.
- Improve documentation in
README.md
- Fix
error.messagerepeating inerror.stack, if an invaliderror.causeis used (#33)
- Fix TypeScript types when using the
customoption (#18)
- Minimal supported Node.js version is now
18.18.0
- Minimal supported Node.js version is now
16.17.0
- Fix TypeScript types inference (#18 (comment))
- Fix
error.errorsTypeScript type (#15)
- Fix TypeScript types inference (#18)
- Fix TypeScript types for error
props(#9)
- Improve TypeScript types
- Improve TypeScript types
- Fix TypeScript types
- Error
propsnow preserve property descriptors - Error
propscan be marked as non-enumerable by prefixing their name with_ plugin.properties()can now also return non-enumerable properties by prefixing their name with_
- Improve documentation
- Fix bug duplicating the stack trace when wrapping the error message
- Any
error[exampleMethod](...)plugin method can now also be called usingErrorClass[exampleMethod](error, ...). We recommend using the latter since it does not fail whenerroris unknown or invalid.modern-errors-cli:error.exit()->BaseError.exit(error)modern-errors-serialize:error.toJSON()->BaseError.toJSON(error)modern-errors-http:error.httpResponse()->BaseError.httpResponse(error)
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,
})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 = () => {
try {
// ...
} catch (error) {
throw BaseError.normalize(error)
}
}After:
export const main = () => {
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)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 })
}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 must now be explicitly
normalized by
BaseError.normalize()
instead of being automatically normalized on creation.
Global custom logic can now be specified by passing
the custom option to the
BaseError. Previously, only
class-specific custom logic could be specified.
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.
The BaseError can now be instantiated without
wrapping an error. The
cause option is now optional.
The stack trace produced when wrapping an error that does not have one has been improved.
The following changes only impact authors of custom plugins.
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 is now an array instead of
an object. This array might contain error classes with duplicate names.
info.errorInfo(error) now returns the error's
ErrorClass and
ErrorClasses.
- Improve how stack traces are printed
- Add browser support
Features can now be extended using plugins.
import modernErrors from 'modern-errors'
import modernErrorsBugs from 'modern-errors-bugs'
import modernErrorsCli from 'modern-errors-cli'
export const AnyError = modernErrors([modernErrorsBugs, modernErrorsCli])The modern-errors-cli plugin
handles CLI errors.
The
modern-errors-process plugin
handles process errors.
The
modern-errors-stack plugin
automatically cleans up stack traces.
The modern-errors-http plugin
converts errors to plain objects to use in an HTTP response.
The
modern-errors-winston plugin
logs errors with Winston.
Error subclasses can now be created using ErrorClass.subclass() to share
custom logic and options between classes.
const SharedError = AnyError.subclass('SharedError', {
custom: class extends AnyError {
// ...
},
})
export const InputError = SharedError.subclass('InputError')
export const AuthError = SharedError.subclass('AuthError')Options can now be applied to any error.
export const AnyError = modernErrors(plugins, options)Or to any error of a specific class.
export const InputError = AnyError.subclass('InputError', options)Or to multiple classes.
export const SharedError = AnyError.subclass('SharedError', options)
export const InputError = SharedError.subclass('InputError')
export const AuthError = SharedError.subclass('AuthError')Or to a specific error.
throw new InputError('...', options)Or to a specific plugin method call, passing only that plugin's options.
AnyError[methodName](...args, options[pluginName])error[methodName](...args, options[pluginName])The errors option can now be used to aggregate
multiple errors into one, similarly to new AggregateError(errors).
The main function now returns the base error class AnyError
AnyError.subclass(name) must be used to create each error class. The first one
must now be named UnknownError.
Before:
export const {
// Custom error classes
InputError,
AuthError,
DatabaseError,
// Error handler
errorHandler,
} = modernErrors(['InputError', 'AuthError', 'DatabaseError'])After:
// Base error class
export const AnyError = modernErrors()
export const UnknownError = AnyError.subclass('UnknownError')
export const InputError = AnyError.subclass('InputError')
export const AuthError = AnyError.subclass('AuthError')
export const DatabaseError = AnyError.subclass('DatabaseError')errorHandler() has been renamed to AnyError.normalize()
Before:
const { errorHandler } = modernErrors(errorNames)
const normalizedError = errorHandler(error)After:
const AnyError = modernErrors()
const normalizedError = AnyError.normalize(error)Error classes can now be fully customized using the
custom option: constructors, methods, etc. This
replaces the previous onCreate option.
Before:
modernErrors({
onCreate: (error, options) => {
const { filePath } = options
if (typeof filePath !== 'string') {
throw new TypeError('filePath must be a string.')
}
error.filePath = filePath
},
})After:
export const InputError = AnyError.subclass('InputError', {
custom: class extends AnyError {
constructor(message, options = {}) {
super(message, options)
const { filePath } = options
if (typeof filePath !== 'string') {
throw new TypeError('filePath must be a string.')
}
this.filePath = filePath
}
},
})Error properties must now be set using
props.{propName} instead of
{propName}.
Before:
throw new InputError('...', { filePath: '/path' })After:
throw new InputError('...', { props: { filePath: '/path' } })The bugsUrl option has been renamed to
bugs. It cannot be a function
anymore. It also requires adding the
modern-errors-bugs
plugin.
A few bug fixes related to using the bugs option twice have also been fixed.
Before:
throw new InputError('...', {
bugsUrl: 'https://github.com/my-name/my-project/issues',
})After:
throw new InputError('...', {
bugs: 'https://github.com/my-name/my-project/issues',
})parse() has been renamed to
AnyError.parse().
AnyError.parse() and error.toJSON() also require adding the
modern-errors-serialize
plugin.
Serialization and parsing now recurse deeply over objects and arrays.
Before:
const { parse } = modernErrors(errorNames)
const errorObject = JSON.parse(errorString)
const error = parse(errorObject)After:
import modernErrorsSerialize from 'modern-errors-serialize'
const AnyError = modernErrors([modernErrorsSerialize])
const errorObject = JSON.parse(errorString)
const error = AnyError.parse(errorObject)To wrap an error without changing its class, AnyError must now be used instead
of Error. When wrapping an error, its cause and bugs are now
merged right away, instead of when
AnyError.normalize() is called.
Before:
throw new Error('Could not read the file.', { cause })After:
throw new AnyError('Could not read the file.', { cause })We now recommend using instanceof instead of error.name to
check error classes.
Before:
if (error.name === 'InputError') {
// ...
}After:
if (error instanceof InputError) {
// ...
}AnyError can now be used to check for
any errors from a specific library.
if (error instanceof AnyError) {
// ...
}TypeScript support has been greatly improved and is now fully tested. Most types have changed: if you were using them, please check the new documentation here.
Error classes should now be exported to be re-used across modules.
Switch to MIT license.
- Fix the
bugsUrloption when the error has a known type
- Allow the
bugsUrloption to be a function
- The
InternalErrortype has been renamed toUnknownError
- Fix TypeScript types of
parse()
- Fix TypeScript types
- The error names must now be passed as argument
- Serialize/parse errors
- Fix using the
inoperator on the return value
- Reduce npm package size
- Add documentation about CLI errors
- Improve error normalization