index.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. 'use strict'
  2. var util = require('util')
  3. /**
  4. * Make custom error types that pass `instanceof` checks, have stack traces,
  5. * support custom messages and properties, and support wrapping errors (causes).
  6. *
  7. * @module
  8. */
  9. //
  10. // For ES6+ Classes
  11. //
  12. /**
  13. * A base class for custom errors that handles:
  14. *
  15. * 1. Wrapping an optional 'cause'.
  16. * 2. Storing an 'info' object with additional data.
  17. * 3. Setting the name to the subclass name.
  18. *
  19. * @extends Error
  20. */
  21. class ErrorTypeError extends Error {
  22. /**
  23. * @param {string} message as for built-in Error
  24. * @param {?object} info extra data to attach to the error
  25. */
  26. constructor ({ message, info }) {
  27. super(message)
  28. this.name = this.constructor.name
  29. if (info) {
  30. this.info = info
  31. }
  32. }
  33. /**
  34. * Wrap the given error, which caused this error.
  35. *
  36. * @param {Error} cause
  37. * @return {this}
  38. */
  39. withCause (cause) {
  40. this.cause = cause
  41. if (this.message && cause.message) {
  42. this.message += ': ' + cause.message
  43. }
  44. return this
  45. }
  46. }
  47. /**
  48. * Base class for errors with a corresponding HTTP status code.
  49. *
  50. * @extends ErrorTypeError
  51. */
  52. class ErrorWithStatusCode extends ErrorTypeError {
  53. /**
  54. * @param {?number} statusCode an HTTP status code
  55. * @param {object} options as for ErrorTypeError
  56. */
  57. constructor ({ statusCode, ...options }) {
  58. super(options)
  59. this.statusCode = statusCode || 500
  60. }
  61. }
  62. exports.ErrorWithStatusCode = ErrorWithStatusCode
  63. /**
  64. * Return the `info` property from `error` and recursively merge the `info`
  65. * properties from the error's causes, if any.
  66. *
  67. * If a property is repeated, the first one in the cause chain wins.
  68. *
  69. * @param {?Error} error assumed not to have circular causes
  70. * @return {Object}
  71. */
  72. function getFullInfo (error) {
  73. if (!error) return {}
  74. const info = getFullInfo(error.cause)
  75. if (typeof error.info === 'object') Object.assign(info, error.info)
  76. return info
  77. }
  78. /**
  79. * Return the `stack` property from `error` and recursively append the `stack`
  80. * properties from the error's causes, if any.
  81. *
  82. * @param {?Error} error assumed not to have circular causes
  83. * @return {string}
  84. */
  85. function getFullStack (error) {
  86. if (!error) return ''
  87. const causeStack = getFullStack(error.cause)
  88. if (causeStack) return (error.stack + '\ncaused by: ' + causeStack)
  89. return error.stack
  90. }
  91. /**
  92. * Is `error` or one of its causes an instance of `klass`?
  93. *
  94. * @param {?Error} error assumed not to have circular causes
  95. * @param {function} klass
  96. * @return {Boolean}
  97. */
  98. function hasCauseInstanceOf (error, klass) {
  99. if (!error) return false
  100. return error instanceof klass || hasCauseInstanceOf(error.cause, klass)
  101. }
  102. exports.Error = ErrorTypeError
  103. exports.getFullInfo = getFullInfo
  104. exports.getFullStack = getFullStack
  105. exports.hasCauseInstanceOf = hasCauseInstanceOf
  106. //
  107. // For ES5
  108. //
  109. function extendErrorType (base, name, builder) {
  110. var errorConstructor = function () {
  111. Error.captureStackTrace && Error.captureStackTrace(this, this.constructor)
  112. if (builder) builder.apply(this, arguments)
  113. this.name = name
  114. }
  115. util.inherits(errorConstructor, base)
  116. errorConstructor.prototype.withCause = function (cause) {
  117. this.cause = cause
  118. if (this.message && cause.message) {
  119. this.message += ': ' + cause.message
  120. }
  121. return this
  122. }
  123. return errorConstructor
  124. }
  125. function defineErrorType (name, builder) {
  126. return extendErrorType(Error, name, builder)
  127. }
  128. function extendErrorTypeIn (container, base, name, builder) {
  129. container[name] = extendErrorType(base, name, builder)
  130. }
  131. function defineErrorTypeIn (container, name, builder) {
  132. extendErrorTypeIn(container, Error, name, builder)
  133. }
  134. exports.extend = extendErrorType
  135. exports.define = defineErrorType
  136. exports.extendIn = extendErrorTypeIn
  137. exports.defineIn = defineErrorTypeIn