![Oracle Drags Its Feet in the JavaScript Trademark Dispute](https://cdn.sanity.io/images/cgdhsj6q/production/919c3b22c24f93884c548d60cbb338e819ff2435-1024x1024.webp?w=400&fit=max&auto=format)
Security News
Oracle Drags Its Feet in the JavaScript Trademark Dispute
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
modern-errors
Advanced tools
Handle errors like it's 2023 🔮
Error handling framework that is pluggable, minimalist yet featureful.
Error
instance,
missing stack
, etc.)throw
,
try/catch
, new Error()
,
error.cause
, instanceof
,
class
,
toJSON()
modern-errors-cli
: Handle
errors in CLI modulesmodern-errors-process
:
Handle process errorsmodern-errors-bugs
: Print
where to report bugsmodern-errors-serialize
:
Serialize/parse errorsmodern-errors-clean
: Clean
stack tracesmodern-errors-http
: Create
HTTP error responsesmodern-errors-winston
:
Log errors with WinstonCreate error classes.
import ModernError from 'modern-errors'
export const BaseError = ModernError.subclass('BaseError')
export const UnknownError = BaseError.subclass('UnknownError')
export const InputError = BaseError.subclass('InputError')
export const AuthError = BaseError.subclass('AuthError')
export const DatabaseError = BaseError.subclass('DatabaseError')
Throw errors.
throw new InputError('Missing file path.')
Wrap errors.
try {
// ...
} catch (cause) {
throw new InputError('Could not read the file.', { cause })
}
Normalize errors.
try {
throw 'Missing file path.'
} catch (error) {
// Normalized from a string to a `BaseError` instance
throw BaseError.normalize(error)
}
Use plugins.
import ModernError from 'modern-errors'
import modernErrorsSerialize from 'modern-errors-serialize'
export const BaseError = ModernError.subclass('BaseError', {
plugins: [modernErrorsSerialize],
})
// ...
// Serialize error as JSON, then back to identical error instance
const error = new InputError('Missing file path.')
const errorString = JSON.stringify(error)
const identicalError = BaseError.parse(JSON.parse(errorString))
npm install modern-errors
If any plugin is used, it must also be installed.
npm install modern-errors-{pluginName}
This package works in both Node.js >=14.18.0 and
browsers.
It is an ES module and must be loaded using
an import
or import()
statement,
not require()
.
import ModernError from 'modern-errors'
export const BaseError = ModernError.subclass('BaseError')
export const UnknownError = BaseError.subclass('UnknownError')
export const InputError = BaseError.subclass('InputError')
export const AuthError = BaseError.subclass('AuthError')
export const DatabaseError = BaseError.subclass('DatabaseError')
Exporting and documenting all error classes (except for
ModernError
) allows consumers to check them. This also enables
sharing error classes between modules.
if (error instanceof InputError) {
// ...
}
ErrorClass.subclass()
returns a
subclass.
Parent classes' options are merged to their subclasses.
export const BaseError = ModernError.subclass('BaseError', {
props: { isError: true },
})
export const InputError = BaseError.subclass('InputError', {
props: { isUserError: true },
})
const error = new InputError('...')
console.log(error.isError) // true
console.log(error.isUserError) // true
console.log(error instanceof BaseError) // true
console.log(error instanceof InputError) // true
throw new InputError('Missing file path.')
const error = new InputError('...', { props: { isUserError: true } })
console.log(error.isUserError) // true
const InputError = BaseError.subclass('InputError', {
props: { isUserError: true },
})
const error = new InputError('...')
console.log(error.isUserError) // true
The errors
option aggregates multiple errors into one. This
is like
new AggregateError(errors)
except that it works with any error class.
const databaseError = new DatabaseError('...')
const authError = new AuthError('...')
throw new InputError('...', { errors: [databaseError, authError] })
// InputError: ... {
// [errors]: [
// DatabaseError: ...
// AuthError: ...
// ]
// }
Any error's message, class and
options can be wrapped using the
standard
cause
option.
Instead of being set as a cause
property, the inner error is directly
merged to the outer error,
including its
message
,
stack
,
name
,
AggregateError.errors
and any additional property.
try {
// ...
} catch (cause) {
throw new InputError('Could not read the file.', { cause })
}
The outer error message is appended, unless it is empty. If the outer error
message ends with :
or :\n
, it is prepended instead.
const cause = new InputError('File does not exist.')
// InputError: File does not exist.
throw new InputError('', { cause })
// InputError: File does not exist.
// Could not read the file.
throw new InputError('Could not read the file.', { cause })
// InputError: Could not read the file: File does not exist.
throw new InputError(`Could not read the file:`, { cause })
// InputError: Could not read the file:
// File does not exist.
throw new InputError(`Could not read the file:\n`, { cause })
The outer error's class replaces the inner one.
try {
throw new AuthError('...')
} catch (cause) {
// Now an InputError
throw new InputError('...', { cause })
}
Except when the outer error's class is a parent class, such as
BaseError
.
try {
throw new AuthError('...')
} catch (cause) {
// Still an AuthError
throw new BaseError('...', { cause })
}
The outer error's props
and
plugin options are merged.
try {
throw new AuthError('...', innerOptions)
} catch (cause) {
// `outerOptions` are merged with `innerOptions`
throw new BaseError('...', { ...outerOptions, cause })
}
Any error can be directly passed to the cause
option,
even if it is invalid, unknown or not
normalized.
try {
// ...
} catch (cause) {
throw new InputError('...', { cause })
}
Manipulating errors that are not
Error
instances
or that have
invalid properties
can lead to unexpected bugs.
BaseError.normalize()
fixes that.
try {
throw 'Missing file path.'
} catch (invalidError) {
// This fails: `invalidError.message` is `undefined`
console.log(invalidError.message.trim())
}
try {
throw 'Missing file path.'
} catch (invalidError) {
const normalizedError = BaseError.normalize(invalidError)
// This works: 'Missing file path.'
// `normalizedError` is a `BaseError` instance.
console.log(normalizedError.message.trim())
}
Wrapping a module's main functions with
BaseError.normalize(error, UnknownError)
ensures every error being thrown is valid, applies
plugins, and has a class that is either
known or UnknownError
.
export const main = function () {
try {
// ...
} catch (error) {
throw BaseError.normalize(error, UnknownError)
}
}
An error is unknown if its class is not a subclass of the
BaseError
. This indicates an unexpected exception,
usually a bug.
BaseError.normalize(error, UnknownError)
assigns the UnknownError
class to those errors. This is usually performed in
the top-level error handler.
export const UnknownError = BaseError.subclass('UnknownError')
try {
return regExp.test(value)
} catch (error) {
// Now an `UnknownError` instance
throw BaseError.normalize(error, UnknownError)
}
Unknown errors should be handled in a try {} catch {}
block and
wrapped with a known class
instead. That block should only cover the statement that might throw in order to
prevent catching other unrelated unknown errors.
try {
return regExp.test(value)
} catch (error) {
// Now an `InputError` instance
throw new InputError('Invalid regular expression:', { cause: error })
}
BaseError.normalize()
is required
for unknown errors to use plugins.
try {
return regExp.test(value)
} catch (error) {
error.examplePluginMethod() // This throws
const normalizedError = BaseError.normalize(error)
normalizedError.examplePluginMethod() // This works
}
Plugins extend modern-errors
features. All available plugins are
listed here.
To use a plugin, please install it, then pass it to the
plugins
option.
npm install modern-errors-{pluginName}
import ModernError from 'modern-errors'
import modernErrorsBugs from 'modern-errors-bugs'
import modernErrorsSerialize from 'modern-errors-serialize'
export const BaseError = ModernError.subclass('BaseError', {
plugins: [modernErrorsBugs, modernErrorsSerialize],
})
// ...
Please see the following documentation to create your own plugin.
Most plugins can be configured with options. The option's name is the same as the plugin.
const options = {
// `modern-errors-bugs` options
bugs: 'https://github.com/my-name/my-project/issues',
// `props` can be configured and modified like plugin options
props: { userId: 5 },
}
Plugin options can apply to (in priority order):
ModernError.subclass()
export const BaseError = ModernError.subclass('BaseError', options)
ErrorClass.subclass()
export const InputError = BaseError.subclass('InputError', options)
new ErrorClass()
throw new InputError('...', options)
ErrorClass[methodName](...args, options[pluginName])
error[methodName](...args, options[pluginName])
The custom
option can be used to provide an error class
with additional methods, constructor
or properties.
export const InputError = BaseError.subclass('InputError', {
// The `class` must extend from the parent error class
custom: class extends BaseError {
// If a `constructor` is defined, its parameters must be (message, options)
constructor(message, options) {
message += message.endsWith('.') ? '' : '.'
super(message, options)
}
isUserInput() {
// ...
}
},
})
const error = new InputError('Wrong user name')
console.log(error.message) // 'Wrong user name.'
console.log(error.isUserInput())
Please see the following documentation for information about TypeScript types.
Top-level ErrorClass
.
name
: string
options
: ClassOptions?
Creates and returns a child ErrorClass
.
Type: object
Type: Plugin[]
Type: class extends ErrorClass {}
Custom class to add any methods, constructor
or properties.
Any plugin options can also be specified.
message
: string
options
: InstanceOptions?
Return value: Error
Type: object
Type: any
Inner error being wrapped.
Type: any[]
Array of errors being aggregated.
Any plugin options can also be specified.
error
: any
NewErrorClass
: subclass of ErrorClass
Return value: Error
Normalizes invalid errors.
If the error
's class is a subclass of ErrorClass
, it is left as is.
Otherwise, it is converted to NewErrorClass
,
which defaults to ErrorClass
itself.
This framework brings together a collection of modules which can also be used individually:
error-custom-class
: Create
one error classerror-class-utils
: Utilities
to properly create error classeserror-serializer
: Convert
errors to/from plain objectsnormalize-exception
:
Normalize exceptions/errorsis-error-instance
: Check if
a value is an Error
instancemerge-error-cause
: Merge an
error with its cause
set-error-class
: Properly
update an error's classset-error-message
: Properly
update an error's messageset-error-props
: Properly
update an error's propertieshandle-cli-error
: 💣 Error
handler for CLI applications 💥log-process-errors
: Show
some ❤ to Node.js process errorsFor any question, don't hesitate to submit an issue on GitHub.
Everyone is welcome regardless of personal background. We enforce a Code of conduct in order to promote a positive and inclusive environment.
This project was made with ❤️. The simplest way to give back is by starring and sharing it online.
If the documentation is unclear or has a typo, please click on the page's Edit
button (pencil icon) and suggest a correction.
If you would like to help us fix a bug or add a new feature, please check our guidelines. Pull requests are welcome!
5.0.0
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.
FAQs
Handle errors in a simple, stable, consistent way
The npm package modern-errors receives a total of 1,938 weekly downloads. As such, modern-errors popularity was classified as popular.
We found that modern-errors demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
Security News
The Linux Foundation is warning open source developers that compliance with global sanctions is mandatory, highlighting legal risks and restrictions on contributions.
Security News
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.