index.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  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. let tag
  52. if (Error.captureStackTrace) {
  53. // Hide this function in the stack trace.
  54. tag = { name: 'TaggedError', message, info }
  55. Error.captureStackTrace(tag, OError.tag)
  56. } else {
  57. tag = new TaggedError(message, info)
  58. }
  59. oError._oErrorTags.push(tag)
  60. return error
  61. }
  62. /**
  63. * The merged info from any `tag`s on the given error.
  64. *
  65. * If an info property is repeated, the last one wins.
  66. *
  67. * @param {Error | null | undefined} error any errror (may or may not be an `OError`)
  68. * @return {Object}
  69. */
  70. static getFullInfo(error) {
  71. const info = {}
  72. if (!error) return info
  73. const oError = /** @type{OError} */ (error)
  74. if (typeof oError.info === 'object') Object.assign(info, oError.info)
  75. if (oError._oErrorTags) {
  76. for (const tag of oError._oErrorTags) {
  77. Object.assign(info, tag.info)
  78. }
  79. }
  80. return info
  81. }
  82. /**
  83. * Return the `stack` property from `error`, including the `stack`s for any
  84. * tagged errors added with `OError.tag` and for any `cause`s.
  85. *
  86. * @param {Error | null | undefined} error any error (may or may not be an `OError`)
  87. * @return {string}
  88. */
  89. static getFullStack(error) {
  90. if (!error) return ''
  91. const oError = /** @type{OError} */ (error)
  92. let stack = oError.stack
  93. if (oError._oErrorTags) {
  94. for (const tag of oError._oErrorTags) {
  95. stack += `\n${tag.stack}`
  96. }
  97. }
  98. const causeStack = oError.cause && OError.getFullStack(oError.cause)
  99. if (causeStack) {
  100. stack += '\ncaused by:\n' + indent(causeStack)
  101. }
  102. return stack
  103. }
  104. }
  105. /**
  106. * Used to record a stack trace every time we tag info onto an Error.
  107. *
  108. * @private
  109. * @extends OError
  110. */
  111. class TaggedError extends OError {}
  112. function indent(string) {
  113. return string.replace(/^/gm, ' ')
  114. }
  115. module.exports = OError