Skip to content

Release 5.0.0

Compare
Choose a tag to compare
@ehmicky ehmicky released this 19 Nov 14:12
· 582 commits to main since this release

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.