Handle errors in a simple, stable, consistent way
MIT License
custom
option (https://github.com/ehmicky/modern-errors/issues/18).Published by ehmicky 12 months ago
18.18.0
Published by ehmicky over 1 year ago
16.17.0
Published by ehmicky over 1 year ago
Published by ehmicky over 1 year ago
error.errors
TypeScript type (https://github.com/ehmicky/modern-errors/issues/15)Published by ehmicky over 1 year ago
Published by ehmicky over 1 year ago
props
(https://github.com/ehmicky/modern-errors/issues/9)Published by ehmicky almost 2 years ago
Published by ehmicky almost 2 years ago
Published by ehmicky almost 2 years ago
Published by ehmicky almost 2 years ago
props
now preserve property descriptors
props
can be marked as non-enumerable by prefixing their name with _
plugin.properties()
can now also return non-enumerable properties by prefixing their name with _
Published by ehmicky almost 2 years ago
Published by ehmicky almost 2 years ago
Published by ehmicky almost 2 years ago
error[exampleMethod](...)
plugin method can now also be called using ErrorClass[exampleMethod](error, ...)
. We recommend using the latter since it does not fail when error
is 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)
Published by ehmicky almost 2 years ago
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 = 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)
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.
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
.
Published by ehmicky almost 2 years ago
Published by ehmicky almost 2 years ago
Published by ehmicky almost 2 years ago
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.
Published by ehmicky about 2 years ago
bugsUrl
option when the error has a known typePublished by ehmicky about 2 years ago
bugsUrl
option to be a function