overleaf/libraries/o-error/index.js
2019-07-04 10:38:56 +02:00

156 lines
3.7 KiB
JavaScript

'use strict'
var util = require('util')
/**
* Make custom error types that pass `instanceof` checks, have stack traces,
* support custom messages and properties, and support wrapping errors (causes).
*
* @module
*/
//
// For ES6+ Classes
//
/**
* A base class for custom errors that handles:
*
* 1. Wrapping an optional 'cause'.
* 2. Storing an 'info' object with additional data.
* 3. Setting the name to the subclass name.
*
* @extends Error
*/
class ErrorTypeError extends Error {
/**
* @param {string} message as for built-in Error
* @param {?object} info extra data to attach to the error
*/
constructor ({ message, info }) {
super(message)
this.name = this.constructor.name
if (info) {
this.info = info
}
}
/**
* Wrap the given error, which caused this error.
*
* @param {Error} cause
* @return {this}
*/
withCause (cause) {
this.cause = cause
if (this.message && cause.message) {
this.message += ': ' + cause.message
}
return this
}
}
/**
* Base class for errors with a corresponding HTTP status code.
*
* @extends ErrorTypeError
*/
class ErrorWithStatusCode extends ErrorTypeError {
/**
* @param {?number} statusCode an HTTP status code
* @param {object} options as for ErrorTypeError
*/
constructor ({ statusCode, ...options }) {
super(options)
this.statusCode = statusCode || 500
}
}
exports.ErrorWithStatusCode = ErrorWithStatusCode
/**
* Return the `info` property from `error` and recursively merge the `info`
* properties from the error's causes, if any.
*
* If a property is repeated, the first one in the cause chain wins.
*
* @param {?Error} error assumed not to have circular causes
* @return {Object}
*/
function getFullInfo (error) {
if (!error) return {}
const info = getFullInfo(error.cause)
if (typeof error.info === 'object') Object.assign(info, error.info)
return info
}
/**
* Return the `stack` property from `error` and recursively append the `stack`
* properties from the error's causes, if any.
*
* @param {?Error} error assumed not to have circular causes
* @return {string}
*/
function getFullStack (error) {
if (!error) return ''
const causeStack = getFullStack(error.cause)
if (causeStack) return (error.stack + '\ncaused by: ' + causeStack)
return error.stack
}
/**
* Is `error` or one of its causes an instance of `klass`?
*
* @param {?Error} error assumed not to have circular causes
* @param {function} klass
* @return {Boolean}
*/
function hasCauseInstanceOf (error, klass) {
if (!error) return false
return error instanceof klass || hasCauseInstanceOf(error.cause, klass)
}
exports.Error = ErrorTypeError
exports.getFullInfo = getFullInfo
exports.getFullStack = getFullStack
exports.hasCauseInstanceOf = hasCauseInstanceOf
//
// For ES5
//
function extendErrorType (base, name, builder) {
var errorConstructor = function () {
Error.captureStackTrace && Error.captureStackTrace(this, this.constructor)
if (builder) builder.apply(this, arguments)
this.name = name
}
util.inherits(errorConstructor, base)
errorConstructor.prototype.withCause = function (cause) {
this.cause = cause
if (this.message && cause.message) {
this.message += ': ' + cause.message
}
return this
}
return errorConstructor
}
function defineErrorType (name, builder) {
return extendErrorType(Error, name, builder)
}
function extendErrorTypeIn (container, base, name, builder) {
container[name] = extendErrorType(base, name, builder)
}
function defineErrorTypeIn (container, name, builder) {
extendErrorTypeIn(container, Error, name, builder)
}
exports.extend = extendErrorType
exports.define = defineErrorType
exports.extendIn = extendErrorTypeIn
exports.defineIn = defineErrorTypeIn