Skip to content

Latest commit

 

History

History
733 lines (489 loc) · 15.3 KB

CHANGELOG.md

File metadata and controls

733 lines (489 loc) · 15.3 KB

7.0.1

Types

  • Fix TypeScript types when using the custom option (#18).

7.0.0

Breaking changes

  • Minimal supported Node.js version is now 18.18.0

6.0.0

Breaking changes

  • Minimal supported Node.js version is now 16.17.0

5.5.4

Bug fixes

5.5.3

Bug fixes

  • Fix error.errors TypeScript type (#15)

5.5.2

Bug fixes

  • Fix TypeScript types inference (#18)

5.5.1

Bug fixes

  • Fix TypeScript types for error props (#9)

5.5.0

Features

  • Improve TypeScript types

5.4.0

Features

  • Improve TypeScript types

5.3.1

Bug fixes

  • Fix TypeScript types

5.3.0

Features

5.2.0

Documentation

  • Improve documentation

5.1.1

Bug fixes

  • Fix bug duplicating the stack trace when wrapping the error message

5.1.0

Features

5.0.0

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 = () => {
  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)

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.

4.1.1

Bug fixes

  • Improve how stack traces are printed

4.1.0

Features

  • Add browser support

4.0.0

Major features

Plugins

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])

CLI plugin

The modern-errors-cli plugin handles CLI errors.

Process errors

The modern-errors-process plugin handles process errors.

Clean stack traces

The modern-errors-stack plugin automatically cleans up stack traces.

HTTP responses

The modern-errors-http plugin converts errors to plain objects to use in an HTTP response.

Error logging (Winston)

The modern-errors-winston plugin logs errors with Winston.

Subclasses

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')

Improved options

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])

Aggregate errors

The errors option can now be used to aggregate multiple errors into one, similarly to new AggregateError(errors).

Breaking changes

Creating error classes

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')

Error handler

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)

Custom classes

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

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' } })

Bug reports

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',
})

Serialization/parsing

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)

Error wrapping

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 })

Checking error classes

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 types

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.

Exporting error classes

Error classes should now be exported to be re-used across modules.

License

Switch to MIT license.

3.1.1

Bug fixes

3.1.0

Features

3.0.0

Breaking changes

2.0.2

Bug fixes

  • Fix TypeScript types of parse()

2.0.1

Bug fixes

  • Fix TypeScript types

2.0.0

Breaking changes

1.5.0

Features

1.4.1

Bug fixes

  • Fix using the in operator on the return value

1.4.0

Features

  • Reduce npm package size

1.3.0

Documentation

1.2.0

Features

  • Improve error normalization