UNPKG

error-ninja

Version:

A handy library for creating custom error constructors that work across async boundaries in Node.js.

213 lines (185 loc) 10 kB
# Error-Ninja A handy library for creating custom error constructors that work across async boundaries in Node.js. JavaScript error handling has never been this nice! ## What's this for? This library is for you if you want to: 1) Maintain error stack traces across async boundaries. 2) Throw and catch errors to indicate problems instead of relying on function return values. 3) Implement custom error constructors e.g. `new DataStreamError()`. 4) Attach data/properties to errors to give them additional context. 5) Understand how bugs are triggered and how they bubble up your call stack. 6) Work with native JavaScript errors as well as your own. 7) Make your debugging life easier. 8) Add an ID property to errors to make them easy to log and trace in production. 9) Print out stack traces for uncaught errors and unhandled promise rejections. 10) Add more power to your errors without altering their native JavaScript and Node.js functionality. ## v1.x Breaking Changes The new v1 of this library is a complete rewrite and is not backwards compatible with previous versions. ## Quick Start See the `./examples/example.js` file for a working example or execute: `npm run example`. ```javascript const setupErrorNinja = require(`error-ninja`); const createErrorClass = setupErrorNinja({ stackTraceLimit: 20, // Expand the maximum number of stack trace frames for all errors. fullInsight: true, // Capture full errors and stack traces instead of just the error name, ID and message. }); const DataStreamError = createErrorClass(`DataStreamError`); const FatalError = createErrorClass(`FatalError`); try { // ...something bad happens... throw new DataStreamError(`NETWORK_DISC`, `The network disconnected.`, { status: 500, uri: `...` }); } catch (err) { const newErr = FatalError.chain(err, `DATA_INTERRUPTED`, `Unable to load the resource.`); console.error(newErr.pretty(true)); } ``` ## Example Code #### Chaining errors together The magic of ErrorNinja occurs when you chain errors together. This allows you to throw and catch as much as you like and obtain a full understanding of how errors propagate through your call stack. ```javascript const someError = new Error(`Something bad happened`); const anotherError = DataStreamError.chain(someError, `DATA_INTERRUPTED`, `Failed to load the resource.`); const newError = FatalError.chain(someError, `FATAL`, `Unable to continue.`); ``` #### Understanding the cause of an error By default err.cause() returns an array of the IDs and messages for the chained errors up to this point. If `true` is passed as the first parameter and the `fullInsight` option is enabled it will return the original error objects. ```javascript const newErr = FatalError.chain(someError, `DATA_INTERRUPTED`, `Unable to load the resource.`); const causes = newErr.cause(); causes.forEach(cause => console.error(`Cause:`, cause)); ``` #### Pretty printing a chain of errors to the terminal By default err.pretty() returns a formatted string containing the IDs and messages for the chained errors up to this point. If `true` is passed as the first parameter and the `fullInsight` option is enabled it will add the stack traces to the output. ```javascript const newErr = FatalError.chain(someError, `DATA_INTERRUPTED`, `Unable to load the resource.`); const prettyOutput = newErr.pretty(true); console.error(prettyOutput); ``` ## Example Output Some sample output from the `examples.js` file. #### Output when calling `err.cause()`: ```javascript [ { name: 'FatalError', id: 'CRASHED', message: 'A fatal error occured.', data: { someProperty: 123 }, __isLite: true }, { name: 'VideoStreamError', id: 'CONNECT_FAILED', message: 'Unable to connect to the video server.', data: {}, __isLite: true }, { name: 'CustomCustomUriError', id: 'INVALID_URI', message: 'The specified URI is not a string.', data: { uri: null, typeOf: 'object' }, __isLite: true } ] ``` #### Output when calling `err.cause(true)`: ```javascript [ { FatalError: [CRASHED] A fatal error occured. at startVideoStream (/Users/josh/Repositories/Personal/Error-Ninja/examples/example.js:71:31) at process._tickCallback (internal/process/next_tick.js:68:7) at Function.Module.runMain (internal/modules/cjs/loader.js:744:11) at startup (internal/bootstrap/node.js:285:19) at bootstrapNodeJSCore (internal/bootstrap/node.js:739:3) __options: { fullInsight: true }, name: 'FatalError', id: 'CRASHED', __originalMsg: 'A fatal error occured.', __chain: [ [NinjaError], [ErrorClass] ], __isErrorNinja: true, __isWrapped: false, __isLite: false, data: { someProperty: 123 } }, { VideoStreamError: [CONNECT_FAILED] Unable to connect to the video server. at startVideoStream (/Users/josh/Repositories/Personal/Error-Ninja/examples/example.js:71:31) at process._tickCallback (internal/process/next_tick.js:68:7) at Function.Module.runMain (internal/modules/cjs/loader.js:744:11) at startup (internal/bootstrap/node.js:285:19) at bootstrapNodeJSCore (internal/bootstrap/node.js:739:3) __options: { fullInsight: true }, name: 'VideoStreamError', id: 'CONNECT_FAILED', __originalMsg: 'Unable to connect to the video server.', __chain: [ [ErrorClass] ], __isErrorNinja: true, __isWrapped: false, __isLite: false, data: undefined }, { CustomUriError: [INVALID_URI] The specified URI is not a string. at connectToVideoServer (/Users/josh/Repositories/Personal/Error-Ninja/examples/example.js:52:26) at startVideoStream (/Users/josh/Repositories/Personal/Error-Ninja/examples/example.js:66:9) at Object.<anonymous> (/Users/josh/Repositories/Personal/Error-Ninja/examples/example.js:97:1) at Module._compile (internal/modules/cjs/loader.js:688:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10) at Module.load (internal/modules/cjs/loader.js:598:32) at tryModuleLoad (internal/modules/cjs/loader.js:537:12) at Function.Module._load (internal/modules/cjs/loader.js:529:3) at Function.Module.runMain (internal/modules/cjs/loader.js:741:12) at startup (internal/bootstrap/node.js:285:19) at bootstrapNodeJSCore (internal/bootstrap/node.js:739:3) __options: { fullInsight: true }, name: 'CustomUriError', id: 'INVALID_URI', __originalMsg: 'The specified URI is not a string.', __chain: [], __isErrorNinja: true, __isWrapped: false, __isLite: false, data: { uri: null, typeOf: 'object' } } ] ``` #### Output when calling `err.pretty()`: ``` =========================================================================== <1> FatalError: [CRASHED] A fatal error occured. --------------------------------------------------------------------------- @property {number} someProperty: <123> =========================================================================== <2> VideoStreamError: [CONNECT_FAILED] Unable to connect to the video server. =========================================================================== <3> CustomUriError: [INVALID_URI] The specified URI is not a string. --------------------------------------------------------------------------- @property {object} uri: <null> @property {string} typeOf: <object> =========================================================================== ``` #### Output when calling `err.pretty(true)`: ``` =========================================================================== <1> FatalError: [CRASHED] A fatal error occured. =========================================================================== @property {number} someProperty: <123> --------------------------------------------------------------------------- at startVideoStream (/Users/josh/Repositories/Personal/Error-Ninja/examples/example.js:71:31) at process._tickCallback (internal/process/next_tick.js:68:7) at Function.Module.runMain (internal/modules/cjs/loader.js:744:11) at startup (internal/bootstrap/node.js:285:19) at bootstrapNodeJSCore (internal/bootstrap/node.js:739:3) =========================================================================== <2> VideoStreamError: [CONNECT_FAILED] Unable to connect to the video server. --------------------------------------------------------------------------- at startVideoStream (/Users/josh/Repositories/Personal/Error-Ninja/examples/example.js:71:31) at process._tickCallback (internal/process/next_tick.js:68:7) at Function.Module.runMain (internal/modules/cjs/loader.js:744:11) at startup (internal/bootstrap/node.js:285:19) at bootstrapNodeJSCore (internal/bootstrap/node.js:739:3) =========================================================================== <3> CustomUriError: [INVALID_URI] The specified URI is not a string. =========================================================================== @property {object} uri: <null> @property {string} typeOf: <object> --------------------------------------------------------------------------- at connectToVideoServer (/Users/josh/Repositories/Personal/Error-Ninja/examples/example.js:52:26) at startVideoStream (/Users/josh/Repositories/Personal/Error-Ninja/examples/example.js:66:9) at Object.<anonymous> (/Users/josh/Repositories/Personal/Error-Ninja/examples/example.js:97:1) at Module._compile (internal/modules/cjs/loader.js:688:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10) at Module.load (internal/modules/cjs/loader.js:598:32) at tryModuleLoad (internal/modules/cjs/loader.js:537:12) at Function.Module._load (internal/modules/cjs/loader.js:529:3) at Function.Module.runMain (internal/modules/cjs/loader.js:741:12) at startup (internal/bootstrap/node.js:285:19) at bootstrapNodeJSCore (internal/bootstrap/node.js:739:3) =========================================================================== ```