index.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. /**
  2. * Light-weight helpers for handling JavaScript Errors in node.js and the
  3. * browser.
  4. */
  5. class OError extends Error {
  6. /**
  7. * @param {string} message as for built-in Error
  8. * @param {Object} [info] extra data to attach to the error
  9. * @param {Error} [cause] the internal error that caused this error
  10. */
  11. constructor(message, info, cause) {
  12. super(message)
  13. this.name = this.constructor.name
  14. if (info) this.info = info
  15. if (cause) this.cause = cause
  16. /** @private @type {Array<TaggedError>} */
  17. this._oErrorTags // eslint-disable-line
  18. }
  19. /**
  20. * Set the extra info object for this error.
  21. *
  22. * @param {Object | null | undefined} info extra data to attach to the error
  23. * @return {this}
  24. */
  25. withInfo(info) {
  26. this.info = info
  27. return this
  28. }
  29. /**
  30. * Wrap the given error, which caused this error.
  31. *
  32. * @param {Error} cause the internal error that caused this error
  33. * @return {this}
  34. */
  35. withCause(cause) {
  36. this.cause = cause
  37. return this
  38. }
  39. /**
  40. * Tag debugging information onto any error (whether an OError or not) and
  41. * return it.
  42. *
  43. * @param {Error} error the error to tag
  44. * @param {string} [message] message with which to tag `error`
  45. * @param {Object} [info] extra data with wich to tag `error`
  46. * @return {Error} the modified `error` argument
  47. */
  48. static tag(error, message, info) {
  49. const oError = /** @type{OError} */ (error)
  50. if (!oError._oErrorTags) oError._oErrorTags = []
  51. const tag = new TaggedError(message, info)
  52. // Hide this function in the stack trace.
  53. if (Error.captureStackTrace) Error.captureStackTrace(tag, OError.tag)
  54. oError._oErrorTags.push(tag)
  55. return error
  56. }
  57. /**
  58. * The merged info from any `tag`s on the given error.
  59. *
  60. * If an info property is repeated, the last one wins.
  61. *
  62. * @param {Error | null | undefined} error any errror (may or may not be an `OError`)
  63. * @return {Object}
  64. */
  65. static getFullInfo(error) {
  66. const info = {}
  67. if (!error) return info
  68. const oError = /** @type{OError} */ (error)
  69. if (typeof oError.info === 'object') Object.assign(info, oError.info)
  70. if (oError._oErrorTags) {
  71. for (const tag of oError._oErrorTags) {
  72. Object.assign(info, tag.info)
  73. }
  74. }
  75. return info
  76. }
  77. /**
  78. * Return the `stack` property from `error`, including the `stack`s for any
  79. * tagged errors added with `OError.tag` and for any `cause`s.
  80. *
  81. * @param {Error | null | undefined} error any error (may or may not be an `OError`)
  82. * @return {string}
  83. */
  84. static getFullStack(error) {
  85. if (!error) return ''
  86. const oError = /** @type{OError} */ (error)
  87. let stack = oError.stack
  88. if (oError._oErrorTags) {
  89. for (const tag of oError._oErrorTags) {
  90. stack += `\n${tag.stack}`
  91. }
  92. }
  93. const causeStack = oError.cause && OError.getFullStack(oError.cause)
  94. if (causeStack) {
  95. stack += '\ncaused by:\n' + indent(causeStack)
  96. }
  97. return stack
  98. }
  99. }
  100. /**
  101. * Used to record a stack trace every time we tag info onto an Error.
  102. *
  103. * @private
  104. * @extends OError
  105. */
  106. class TaggedError extends OError {}
  107. function indent(string) {
  108. return string.replace(/^/gm, ' ')
  109. }
  110. module.exports = OError