Security News
38% of CISOs Fear Theyโre Not Moving Fast Enough on AI
CISOs are racing to adopt AI for cybersecurity, but hurdles in budgets and governance may leave some falling behind in the fight against cyber threats.
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-stack
: Clean
stack tracesmodern-errors-http
: Create
HTTP error responsesmodern-errors-winston
:
Log errors with WinstonCreate error classes.
import modernErrors from 'modern-errors'
// 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')
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 an `Error` instance
throw AnyError.normalize(error)
}
Use plugins.
import modernErrors from 'modern-errors'
import modernErrorsSerialize from 'modern-errors-serialize'
// Use a plugin to serialize errors as JSON
export const AnyError = modernErrors([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 = AnyError.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()
.
plugins
: Plugin[]?
options
: object?
Creates and returns AnyError
.
Options:
props
: error propertiesBase error class.
name
: string
options
: object?
Return value: class extends AnyError {}
Creates and returns an error subclass. The first one must be named
UnknownError
.
Subclasses can also call ErrorClass.subclass()
themselves.
Options:
props
: error propertiescustom
: custom class to add any methods, constructor
or
propertiesanyException
: any
Return value: AnyError
Normalizes invalid errors and assigns the UnknownError
class to unknown errors.
message
: string
options
: object?
Return value: AnyError
Options:
props
: error propertiescause
: inner error being wrapped. Required with
AnyError
, optional with its
subclasses.errors
: array of errors being aggregated// Base error class
export const AnyError = modernErrors()
// The first error class must be named "UnknownError"
export const UnknownError = AnyError.subclass('UnknownError')
export const InputError = AnyError.subclass('InputError')
export const AuthError = AnyError.subclass('AuthError')
export const DatabaseError = AnyError.subclass('DatabaseError')
Exporting and documenting error classes (including AnyError
and
UnknownError
) allows consumers to check them. This also
enables sharing error classes between modules.
// Known `InputError`
if (error instanceof InputError) {
// ...
}
// Unknown error (from a specific library)
if (error instanceof UnknownError) {
// ...
}
// Any error (from a specific library)
if (error instanceof AnyError) {
// ...
}
throw new InputError('Missing file path.')
const error = new InputError('...', { props: { isUserError: true } })
console.log(error.isUserError) // true
const InputError = AnyError.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's, unless the outer error's class
is AnyError
.
try {
throw new AuthError('...')
} catch (cause) {
// Now an InputError
throw new InputError('...', { cause })
}
try {
throw new AuthError('...')
} catch (cause) {
// Still an AuthError
throw new AnyError('...', { cause })
}
The outer error's options (props
and
plugin options) replace the inner one's. If the outer error's
class is AnyError
, those are merged instead.
try {
throw new AuthError('...', innerOptions)
} catch (cause) {
// Options are now `outerOptions`. `innerOptions` are discarded.
throw new InputError('...', { ...outerOptions, cause })
}
try {
throw new AuthError('...', innerOptions)
} catch (cause) {
// `outerOptions` are merged with `innerOptions`
throw new AnyError('...', { ...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.
AnyError.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 = AnyError.normalize(invalidError)
// This works: 'Missing file path.'
// `normalizedError` is an `Error` instance.
console.log(normalizedError.message.trim())
}
Wrapping a module's main functions with
AnyError.normalize()
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 AnyError.normalize(error)
}
}
An error is unknown if its class was not created by
AnyError.subclass()
. This indicates an
unexpected exception, usually a bug.
AnyError.normalize()
assigns the
UnknownError
class to any unknown error.
try {
return regExp.test(value)
} catch (unknownError) {
// Now an `UnknownError` instance
throw AnyError.normalize(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 (unknownError) {
// Now an `InputError` instance
throw new InputError('Invalid regular expression:', { cause: unknownError })
}
AnyError.normalize()
is required for
unknown errors to use plugins.
try {
return regExp.test(value)
} catch (unknownError) {
unknownError.examplePluginMethod() // This throws
const normalizedError = AnyError.normalize(unknownError)
normalizedError.examplePluginMethod() // This works
}
The custom
option can be used to provide an
error class
with additional methods, constructor
or properties.
export const InputError = AnyError.subclass('InputError', {
// The `class` must extend from `AnyError`
custom: class extends AnyError {
// If a `constructor` is defined, its parameters must be (message, options)
// like `AnyError`
constructor(message, options) {
// Modifying `message` or `options` should be done before `super()`
message += message.endsWith('.') ? '' : '.'
// All arguments should be forwarded to `super()`, including any
// custom `options` or additional `constructor` parameters
super(message, options)
// `name` is automatically added, so this is not necessary
// this.name = 'InputError'
}
isUserInput() {
// ...
}
},
})
const error = new InputError('Wrong user name')
console.log(error.message) // 'Wrong user name.'
console.log(error.isUserInput())
ErrorClass.subclass()
can be used to share
logic between error classes.
const SharedError = AnyError.subclass('SharedError', {
custom: class extends AnyError {
// ...
},
})
export const InputError = SharedError.subclass('InputError')
export const AuthError = SharedError.subclass('AuthError')
Plugins extend modern-errors
features. All available plugins are
listed here.
To use a plugin, please install it, then pass it to
modernErrors()
's first argument.
npm install modern-errors-{pluginName}
import modernErrors from 'modern-errors'
import modernErrorsBugs from 'modern-errors-bugs'
import modernErrorsSerialize from 'modern-errors-serialize'
export const AnyError = modernErrors([modernErrorsBugs, modernErrorsSerialize])
// ...
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):
modernErrors()
export const AnyError = modernErrors(plugins, options)
ErrorClass.subclass()
export const SharedError = AnyError.subclass('SharedError', options)
export const InputError = SharedError.subclass('InputError')
export const AuthError = SharedError.subclass('AuthError')
AnyError.subclass()
export const InputError = AnyError.subclass('InputError', options)
throw new InputError('...', options)
AnyError[methodName](...args, options[pluginName])
error[methodName](...args, options[pluginName])
Please see the following documentation to create your own plugin.
Please see the following documentation for information about TypeScript types.
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!
4.1.1
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
CISOs are racing to adopt AI for cybersecurity, but hurdles in budgets and governance may leave some falling behind in the fight against cyber threats.
Research
Security News
Socket researchers uncovered a backdoored typosquat of BoltDB in the Go ecosystem, exploiting Go Module Proxy caching to persist undetected for years.
Security News
Company News
Socket is joining TC54 to help develop standards for software supply chain security, contributing to the evolution of SBOMs, CycloneDX, and Package URL specifications.