2019-07-01 06:34:15 -04:00
|
|
|
/**
|
2020-04-28 15:40:20 -04:00
|
|
|
* Light-weight helpers for handling JavaScript Errors in node.js and the
|
2020-04-29 15:58:10 -04:00
|
|
|
* browser.
|
2019-07-01 06:34:15 -04:00
|
|
|
*/
|
2019-07-05 09:24:20 -04:00
|
|
|
class OError extends Error {
|
2019-07-01 06:34:15 -04:00
|
|
|
/**
|
|
|
|
* @param {string} message as for built-in Error
|
2020-04-28 15:40:20 -04:00
|
|
|
* @param {Object} [info] extra data to attach to the error
|
2020-04-29 15:58:10 -04:00
|
|
|
* @param {Error} [cause] the internal error that caused this error
|
2019-07-01 06:34:15 -04:00
|
|
|
*/
|
2020-04-28 15:40:20 -04:00
|
|
|
constructor(message, info, cause) {
|
2019-07-01 06:34:15 -04:00
|
|
|
super(message)
|
|
|
|
this.name = this.constructor.name
|
2020-04-28 15:40:20 -04:00
|
|
|
if (info) this.info = info
|
|
|
|
if (cause) this.cause = cause
|
|
|
|
|
2020-04-29 15:58:10 -04:00
|
|
|
/** @private @type {Array<TaggedError>} */
|
2020-04-28 15:40:20 -04:00
|
|
|
this._oErrorTags // eslint-disable-line
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the extra info object for this error.
|
|
|
|
*
|
2020-04-29 15:58:10 -04:00
|
|
|
* @param {Object | null | undefined} info extra data to attach to the error
|
2020-04-28 15:40:20 -04:00
|
|
|
* @return {this}
|
|
|
|
*/
|
|
|
|
withInfo(info) {
|
|
|
|
this.info = info
|
|
|
|
return this
|
2019-07-01 06:34:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wrap the given error, which caused this error.
|
|
|
|
*
|
2020-04-29 15:58:10 -04:00
|
|
|
* @param {Error} cause the internal error that caused this error
|
2019-07-01 06:34:15 -04:00
|
|
|
* @return {this}
|
|
|
|
*/
|
2020-04-17 03:44:50 -04:00
|
|
|
withCause(cause) {
|
2019-07-01 06:34:15 -04:00
|
|
|
this.cause = cause
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2020-04-28 15:40:20 -04:00
|
|
|
/**
|
|
|
|
* Tag debugging information onto any error (whether an OError or not) and
|
|
|
|
* return it.
|
|
|
|
*
|
2020-04-29 15:58:10 -04:00
|
|
|
* @param {Error} error the error to tag
|
|
|
|
* @param {string} [message] message with which to tag `error`
|
|
|
|
* @param {Object} [info] extra data with wich to tag `error`
|
2020-04-28 15:40:20 -04:00
|
|
|
* @return {Error} the modified `error` argument
|
|
|
|
*/
|
|
|
|
static tag(error, message, info) {
|
|
|
|
const oError = /** @type{OError} */ (error)
|
2019-07-01 06:34:15 -04:00
|
|
|
|
2020-04-28 15:40:20 -04:00
|
|
|
if (!oError._oErrorTags) oError._oErrorTags = []
|
|
|
|
|
2020-05-05 04:02:11 -04:00
|
|
|
let tag
|
|
|
|
if (Error.captureStackTrace) {
|
2020-05-05 04:06:15 -04:00
|
|
|
// Hide this function in the stack trace, and avoid capturing it twice.
|
|
|
|
tag = /** @type TaggedError */ ({ name: 'TaggedError', message, info })
|
2020-05-05 04:02:11 -04:00
|
|
|
Error.captureStackTrace(tag, OError.tag)
|
|
|
|
} else {
|
|
|
|
tag = new TaggedError(message, info)
|
|
|
|
}
|
2020-04-28 15:40:20 -04:00
|
|
|
|
|
|
|
oError._oErrorTags.push(tag)
|
|
|
|
|
|
|
|
return error
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The merged info from any `tag`s on the given error.
|
|
|
|
*
|
|
|
|
* If an info property is repeated, the last one wins.
|
|
|
|
*
|
2020-04-29 15:58:10 -04:00
|
|
|
* @param {Error | null | undefined} error any errror (may or may not be an `OError`)
|
2020-04-28 15:40:20 -04:00
|
|
|
* @return {Object}
|
|
|
|
*/
|
|
|
|
static getFullInfo(error) {
|
|
|
|
const info = {}
|
|
|
|
|
|
|
|
if (!error) return info
|
|
|
|
|
|
|
|
const oError = /** @type{OError} */ (error)
|
|
|
|
|
|
|
|
if (typeof oError.info === 'object') Object.assign(info, oError.info)
|
|
|
|
|
|
|
|
if (oError._oErrorTags) {
|
|
|
|
for (const tag of oError._oErrorTags) {
|
|
|
|
Object.assign(info, tag.info)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return info
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the `stack` property from `error`, including the `stack`s for any
|
|
|
|
* tagged errors added with `OError.tag` and for any `cause`s.
|
|
|
|
*
|
2020-04-29 15:58:10 -04:00
|
|
|
* @param {Error | null | undefined} error any error (may or may not be an `OError`)
|
2020-04-28 15:40:20 -04:00
|
|
|
* @return {string}
|
|
|
|
*/
|
|
|
|
static getFullStack(error) {
|
|
|
|
if (!error) return ''
|
|
|
|
|
|
|
|
const oError = /** @type{OError} */ (error)
|
|
|
|
|
|
|
|
let stack = oError.stack
|
|
|
|
|
|
|
|
if (oError._oErrorTags) {
|
|
|
|
for (const tag of oError._oErrorTags) {
|
|
|
|
stack += `\n${tag.stack}`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const causeStack = oError.cause && OError.getFullStack(oError.cause)
|
|
|
|
if (causeStack) {
|
|
|
|
stack += '\ncaused by:\n' + indent(causeStack)
|
|
|
|
}
|
|
|
|
|
|
|
|
return stack
|
|
|
|
}
|
2019-07-01 06:34:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-04-28 15:40:20 -04:00
|
|
|
* Used to record a stack trace every time we tag info onto an Error.
|
2019-07-01 06:34:15 -04:00
|
|
|
*
|
2020-04-28 15:40:20 -04:00
|
|
|
* @private
|
|
|
|
* @extends OError
|
2019-07-01 06:34:15 -04:00
|
|
|
*/
|
2020-04-28 15:40:20 -04:00
|
|
|
class TaggedError extends OError {}
|
2019-07-01 06:34:15 -04:00
|
|
|
|
2020-04-28 15:40:20 -04:00
|
|
|
function indent(string) {
|
|
|
|
return string.replace(/^/gm, ' ')
|
|
|
|
}
|
2019-07-01 06:34:15 -04:00
|
|
|
|
2019-07-05 12:45:43 -04:00
|
|
|
module.exports = OError
|