modern-errors
Advanced tools
| import{serialize,parse as parseLib}from"error-serializer"; | ||
| import{excludeKeys}from"filter-obj"; | ||
| export const setErrorTypeToJSON=function(ErrorType){ | ||
| Object.defineProperty(ErrorType.prototype,"toJSON",{ | ||
| value:toJSON, | ||
| enumerable:false, | ||
| writable:true, | ||
| configurable:true}); | ||
| }; | ||
| const toJSON=function(){ | ||
| return serialize(this); | ||
| }; | ||
| export const parse=function(proxy,value){ | ||
| const types=excludeKeys(proxy,NON_ERROR_TYPES); | ||
| return parseLib(value,{types,loose:true}); | ||
| }; | ||
| const NON_ERROR_TYPES=["errorHandler","parse"]; | ||
| //# sourceMappingURL=serialize.js.map |
+135
-13
@@ -1,4 +0,17 @@ | ||
| import { ErrorName, OnCreate, ErrorType, ErrorParams } from 'error-type' | ||
| import { | ||
| ErrorName, | ||
| ErrorConstructor as RawErrorConstructor, | ||
| ErrorInstance as RawErrorInstance, | ||
| ErrorParams, | ||
| Options as CreateErrorTypesOptions, | ||
| } from 'create-error-types' | ||
| import { serialize, parse, ErrorObject } from 'error-serializer' | ||
| export interface Options<T extends ErrorParams = ErrorParams> { | ||
| /** | ||
| * `modern-errors` options | ||
| */ | ||
| export interface Options< | ||
| ErrorNamesArg extends ErrorName = ErrorName, | ||
| ErrorParamsArg extends ErrorParams = ErrorParams, | ||
| > { | ||
| /** | ||
@@ -9,3 +22,3 @@ * URL where users should report internal errors/bugs. | ||
| */ | ||
| bugsUrl?: string | URL | ||
| readonly bugsUrl?: CreateErrorTypesOptions['bugsUrl'] | ||
@@ -33,3 +46,14 @@ /** | ||
| */ | ||
| onCreate?: OnCreate<T> | ||
| readonly onCreate?: ( | ||
| error: ErrorInstance<ErrorNamesArg>, | ||
| params: Parameters< | ||
| NonNullable< | ||
| CreateErrorTypesOptions<ErrorNamesArg, ErrorParamsArg>['onCreate'] | ||
| > | ||
| >[1], | ||
| ) => ReturnType< | ||
| NonNullable< | ||
| CreateErrorTypesOptions<ErrorNamesArg, ErrorParamsArg>['onCreate'] | ||
| > | ||
| > | ||
| } | ||
@@ -45,2 +69,3 @@ | ||
| * errorHandler, | ||
| * parse, | ||
| * InputError, | ||
@@ -52,4 +77,7 @@ * AuthError, | ||
| */ | ||
| export type Result<T extends ErrorParams = ErrorParams> = { | ||
| [errorName in ErrorName]: ErrorType<T> | ||
| export type Result< | ||
| ErrorNamesArg extends ErrorName = ErrorName, | ||
| ErrorParamsArg extends ErrorParams = ErrorParams, | ||
| > = { | ||
| [errorName in ErrorNamesArg]: ErrorConstructor<errorName, ErrorParamsArg> | ||
| } & { | ||
@@ -71,11 +99,101 @@ /** | ||
| */ | ||
| errorHandler: ErrorHandler | ||
| errorHandler: ErrorHandler<ErrorNamesArg> | ||
| /** | ||
| * Convert an error plain object into an Error instance. | ||
| * | ||
| * @example | ||
| * ```js | ||
| * try { | ||
| * await readFile(filePath) | ||
| * } catch (cause) { | ||
| * const error = new InputError('Could not read the file.', { | ||
| * cause, | ||
| * filePath: '/path', | ||
| * }) | ||
| * const errorObject = error.toJSON() | ||
| * // { | ||
| * // name: 'InputError', | ||
| * // message: 'Could not read the file', | ||
| * // stack: '...', | ||
| * // cause: { name: 'Error', ... }, | ||
| * // filePath: '/path' | ||
| * // } | ||
| * const errorString = JSON.stringify(error) | ||
| * // '{"name":"InputError",...}' | ||
| * const newErrorObject = JSON.parse(errorString) | ||
| * const newError = parse(newErrorObject) | ||
| * // InputError: Could not read the file. | ||
| * // filePath: '/path' | ||
| * // [cause]: Error: ... | ||
| * } | ||
| * ``` | ||
| */ | ||
| parse: Parse | ||
| } | ||
| export type ErrorHandler = (error: unknown) => Error | ||
| export type ErrorConstructor< | ||
| ErrorNamesArg extends ErrorName = ErrorName, | ||
| ErrorParamsArg extends ErrorParams = ErrorParams, | ||
| > = new ( | ||
| ...args: ConstructorParameters< | ||
| RawErrorConstructor<ErrorNamesArg, ErrorParamsArg> | ||
| > | ||
| ) => ErrorInstance<ErrorNamesArg> | ||
| export type { ErrorName, OnCreate, ErrorType } | ||
| export type ErrorInstance<ErrorNamesArg extends ErrorName = ErrorName> = | ||
| RawErrorInstance<ErrorNamesArg> & { | ||
| /** | ||
| * Convert errors to plain objects that are | ||
| * [always safe](https://github.com/ehmicky/error-serializer#json-safety) to | ||
| * serialize with JSON | ||
| * ([or YAML](https://github.com/ehmicky/error-serializer#custom-serializationparsing), | ||
| * etc.). All error properties | ||
| * [are kept](https://github.com/ehmicky/error-serializer#additional-error-properties), | ||
| * including | ||
| * [`cause`](https://github.com/ehmicky/error-serializer#errorcause-and-aggregateerror). | ||
| * | ||
| * @example | ||
| * ```js | ||
| * try { | ||
| * await readFile(filePath) | ||
| * } catch (cause) { | ||
| * const error = new InputError('Could not read the file.', { | ||
| * cause, | ||
| * filePath: '/path', | ||
| * }) | ||
| * const errorObject = error.toJSON() | ||
| * // { | ||
| * // name: 'InputError', | ||
| * // message: 'Could not read the file', | ||
| * // stack: '...', | ||
| * // cause: { name: 'Error', ... }, | ||
| * // filePath: '/path' | ||
| * // } | ||
| * const errorString = JSON.stringify(error) | ||
| * // '{"name":"InputError",...}' | ||
| * } | ||
| * ``` | ||
| */ | ||
| toJSON: () => ReturnType<typeof serialize<RawErrorInstance<ErrorNamesArg>>> | ||
| } | ||
| /** | ||
| * Creates the error types and handler. | ||
| * Type of `errorHandler()` | ||
| */ | ||
| export type ErrorHandler<ErrorNamesArg extends ErrorName = ErrorName> = ( | ||
| error: unknown, | ||
| ) => ErrorInstance<ErrorNamesArg> | ||
| /** | ||
| * Type of `parse()` | ||
| */ | ||
| export type Parse = <ArgType>( | ||
| value: ArgType, | ||
| ) => ReturnType<typeof parse<ArgType, { loose: true }>> | ||
| export type { ErrorName, ErrorObject, ErrorParams } | ||
| /** | ||
| * Creates custom error types. | ||
| * | ||
@@ -86,2 +204,3 @@ * @example | ||
| * errorHandler, | ||
| * parse, | ||
| * InputError, | ||
@@ -93,4 +212,7 @@ * AuthError, | ||
| */ | ||
| export default function modernErrors<T extends ErrorParams = ErrorParams>( | ||
| options?: Options<T>, | ||
| ): Result<T> | ||
| export default function modernErrors< | ||
| ErrorNamesArg extends ErrorName = ErrorName, | ||
| ErrorParamsArg extends ErrorParams = ErrorParams, | ||
| >( | ||
| options?: Options<ErrorNamesArg, ErrorParamsArg>, | ||
| ): Result<ErrorNamesArg, ErrorParamsArg> |
@@ -7,2 +7,3 @@ import createErrorTypes from"create-error-types"; | ||
| import{proxyProps}from"./proxy.js"; | ||
| import{parse}from"./serialize.js"; | ||
@@ -18,3 +19,6 @@ | ||
| const errorHandler=callErrorHandler.bind(undefined,innerProxy); | ||
| return proxyProps({errorHandler},innerProxy); | ||
| const proxy=proxyProps({errorHandler},innerProxy); | ||
| proxy.parse=parse.bind(undefined,proxy); | ||
| return proxy; | ||
| } | ||
@@ -21,0 +25,0 @@ |
@@ -0,3 +1,5 @@ | ||
| import{setErrorTypeToJSON}from"./serialize.js"; | ||
| export const proxyProps=function(returnValue,innerProxy){ | ||
@@ -28,3 +30,5 @@ | ||
| if(!(propName in target)&&propName in innerProxy){ | ||
| Reflect.set(target,propName,innerProxy[propName],...args); | ||
| const ErrorType=innerProxy[propName]; | ||
| setErrorTypeToJSON(ErrorType); | ||
| Reflect.set(target,propName,ErrorType,...args); | ||
| } | ||
@@ -31,0 +35,0 @@ |
+5
-3
| { | ||
| "name": "modern-errors", | ||
| "version": "1.4.1", | ||
| "version": "1.5.0", | ||
| "type": "module", | ||
@@ -49,4 +49,6 @@ "exports": "./build/src/main.js", | ||
| "dependencies": { | ||
| "create-error-types": "^1.0.2", | ||
| "create-error-types": "^1.0.7", | ||
| "error-cause-polyfill": "^1.1.0", | ||
| "error-serializer": "^1.3.2", | ||
| "filter-obj": "^5.1.0", | ||
| "is-plain-obj": "^4.1.0", | ||
@@ -57,3 +59,3 @@ "merge-error-cause": "^1.3.0" | ||
| "@ehmicky/dev-tasks": "^1.0.84", | ||
| "test-each": "^5.2.0" | ||
| "test-each": "^5.2.1" | ||
| }, | ||
@@ -60,0 +62,0 @@ "engines": { |
+76
-23
@@ -18,18 +18,16 @@ <picture> | ||
| - [Create custom error types](#create-error-types-and-handler) | ||
| - Wrap any error's [message](#wrap-error-message), [type](#set-error-type), or | ||
| - Create [custom error types](#create-error-types-and-handler) | ||
| - Handle errors from both [programmatic](#error-handler) and [CLI](#cli-errors) | ||
| modules | ||
| - Wrap inner errors' [message](#wrap-error-message), [type](#set-error-type), or | ||
| [properties](#wrap-error-properties) | ||
| - Set properties on [individual errors](#set-error-properties), or on | ||
| [all errors of the same type](#error-type-properties) | ||
| - Automatically separate (unhandled) [internal errors](#internal-errors) from | ||
| (handled) user errors | ||
| - Internal errors indicate where to [report bugs](#bug-reports) | ||
| - [Serialize/parse](#serializationparsing) errors | ||
| - Set properties on [individual errors](#set-error-properties), or on | ||
| [all errors of the same type](#error-type-properties) | ||
| - Handle [invalid errors](#invalid-errors) (not an `Error` instance, missing | ||
| stack, etc.) | ||
| The framework integrates with [other libraries](#miscellaneous) to also: | ||
| - Handle [CLI errors](#cli-errors) | ||
| - Use [source maps](#source-maps) on stack traces | ||
| # Example | ||
@@ -45,2 +43,3 @@ | ||
| errorHandler, | ||
| parse, | ||
| // Those error types are examples. | ||
@@ -99,3 +98,3 @@ // Any name ending with "Error" can be specified. | ||
| Creates the [error types](#any-error-type) and [handler](#errorhandler). | ||
| Creates custom [error types](#any-error-type). | ||
@@ -118,2 +117,8 @@ ### Return value | ||
| #### parse | ||
| _Type_: `(errorObject) => Error` | ||
| Convert an [error plain object](#serialize) into [an Error instance](#parse-1). | ||
| ### Options | ||
@@ -461,6 +466,4 @@ | ||
| ## Miscellaneous | ||
| ## CLI errors | ||
| ### CLI errors | ||
| CLI applications can assign a different exit code and log verbosity per error | ||
@@ -495,15 +498,63 @@ type by using [`handle-cli-error`](https://github.com/ehmicky/handle-cli-error). | ||
| ### Source maps | ||
| ## Serialization/parsing | ||
| When using a build step (Babel, TypeScript, etc.), the error stack traces refer | ||
| to the built files/lines instead of the source. This can be fixed by using | ||
| source maps: | ||
| ### Serialize | ||
| - Node.js: | ||
| [`--enable-source-maps` CLI flag](https://nodejs.org/api/cli.html#--enable-source-maps) | ||
| - Chrome: | ||
| [`node-source-map-support`](https://github.com/evanw/node-source-map-support) | ||
| - Other browsers: | ||
| [`stacktrace.js`](https://github.com/stacktracejs/stacktrace.js) | ||
| `error.toJSON()` converts errors to plain objects that are | ||
| [always safe](https://github.com/ehmicky/error-serializer#json-safety) to | ||
| serialize with JSON | ||
| ([or YAML](https://github.com/ehmicky/error-serializer#custom-serializationparsing), | ||
| etc.). All error properties | ||
| [are kept](https://github.com/ehmicky/error-serializer#additional-error-properties), | ||
| including | ||
| [`cause`](https://github.com/ehmicky/error-serializer#errorcause-and-aggregateerror). | ||
| ```js | ||
| try { | ||
| await readFile(filePath) | ||
| } catch (cause) { | ||
| const error = new InputError('Could not read the file.', { | ||
| cause, | ||
| filePath: '/path', | ||
| }) | ||
| const errorObject = error.toJSON() | ||
| // { | ||
| // name: 'InputError', | ||
| // message: 'Could not read the file', | ||
| // stack: '...', | ||
| // cause: { name: 'Error', ... }, | ||
| // filePath: '/path' | ||
| // } | ||
| const errorString = JSON.stringify(error) | ||
| // '{"name":"InputError",...}' | ||
| } | ||
| ``` | ||
| ### Parse | ||
| [`parse(errorObject)`](#parse) converts those error plain objects back to | ||
| identical error instances. | ||
| ```js | ||
| const newErrorObject = JSON.parse(errorString) | ||
| const newError = parse(newErrorObject) | ||
| // InputError: Could not read the file. | ||
| // filePath: '/path' | ||
| // [cause]: Error: ... | ||
| ``` | ||
| ### Deep serialization/parsing | ||
| Objects and arrays containing custom errors can be deeply serialized to JSON. | ||
| They can then be deeply parsed back using | ||
| [`JSON.parse()`'s reviver](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#using_the_reviver_parameter). | ||
| ```js | ||
| const error = new InputError('Could not read the file.') | ||
| const deepObject = [{}, { error }] | ||
| const jsonString = JSON.stringify(deepObject) | ||
| const newDeepObject = JSON.parse(jsonString, (key, value) => parse(value)) | ||
| console.log(newDeepObject[1].error) // InputError: Could not read the file. | ||
| ``` | ||
| # Related projects | ||
@@ -524,2 +575,4 @@ | ||
| handler for CLI applications 💥 | ||
| - [`log-process-errors`](https://github.com/ehmicky/log-process-errors): Show | ||
| some ❤ to Node.js process errors | ||
@@ -526,0 +579,0 @@ # Support |
37283
18.85%7
16.67%270
109.3%599
9.71%6
50%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
Updated