modern-errors
Advanced tools
Comparing version 1.4.1 to 1.5.0
@@ -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 @@ |
{ | ||
"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": { |
@@ -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
7
270
599
6
+ Addederror-serializer@^1.3.2
+ Addedfilter-obj@^5.1.0
+ Addederror-serializer@1.4.0(transitive)
+ Addedfilter-obj@5.1.0(transitive)
+ Addedis-error-instance@1.6.0(transitive)
+ Addednormalize-exception@2.11.0(transitive)
+ Addedsafe-json-value@1.11.0(transitive)
Updatedcreate-error-types@^1.0.7