119 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			119 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
'use strict'
 | 
						|
 | 
						|
// **************************************************************
 | 
						|
// * Code initially copied/adapted from "pony-cause" npm module *
 | 
						|
// * Please upstream improvements there                         *
 | 
						|
// **************************************************************
 | 
						|
 | 
						|
const isErrorLike = (err) => {
 | 
						|
  return err && typeof err.message === 'string'
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @param {Error|{ cause?: unknown|(()=>err)}} err
 | 
						|
 * @returns {Error|Object|undefined}
 | 
						|
 */
 | 
						|
const getErrorCause = (err) => {
 | 
						|
  if (!err) return
 | 
						|
 | 
						|
  /** @type {unknown} */
 | 
						|
  // @ts-ignore
 | 
						|
  const cause = err.cause
 | 
						|
 | 
						|
  // VError / NError style causes
 | 
						|
  if (typeof cause === 'function') {
 | 
						|
    // @ts-ignore
 | 
						|
    const causeResult = err.cause()
 | 
						|
 | 
						|
    return isErrorLike(causeResult)
 | 
						|
      ? causeResult
 | 
						|
      : undefined
 | 
						|
  } else {
 | 
						|
    return isErrorLike(cause)
 | 
						|
      ? cause
 | 
						|
      : undefined
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Internal method that keeps a track of which error we have already added, to avoid circular recursion
 | 
						|
 *
 | 
						|
 * @private
 | 
						|
 * @param {Error} err
 | 
						|
 * @param {Set<Error>} seen
 | 
						|
 * @returns {string}
 | 
						|
 */
 | 
						|
const _stackWithCauses = (err, seen) => {
 | 
						|
  if (!isErrorLike(err)) return ''
 | 
						|
 | 
						|
  const stack = err.stack || ''
 | 
						|
 | 
						|
  // Ensure we don't go circular or crazily deep
 | 
						|
  if (seen.has(err)) {
 | 
						|
    return stack + '\ncauses have become circular...'
 | 
						|
  }
 | 
						|
 | 
						|
  const cause = getErrorCause(err)
 | 
						|
 | 
						|
  if (cause) {
 | 
						|
    seen.add(err)
 | 
						|
    return (stack + '\ncaused by: ' + _stackWithCauses(cause, seen))
 | 
						|
  } else {
 | 
						|
    return stack
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @param {Error} err
 | 
						|
 * @returns {string}
 | 
						|
 */
 | 
						|
const stackWithCauses = (err) => _stackWithCauses(err, new Set())
 | 
						|
 | 
						|
/**
 | 
						|
 * Internal method that keeps a track of which error we have already added, to avoid circular recursion
 | 
						|
 *
 | 
						|
 * @private
 | 
						|
 * @param {Error} err
 | 
						|
 * @param {Set<Error>} seen
 | 
						|
 * @param {boolean} [skip]
 | 
						|
 * @returns {string}
 | 
						|
 */
 | 
						|
const _messageWithCauses = (err, seen, skip) => {
 | 
						|
  if (!isErrorLike(err)) return ''
 | 
						|
 | 
						|
  const message = skip ? '' : (err.message || '')
 | 
						|
 | 
						|
  // Ensure we don't go circular or crazily deep
 | 
						|
  if (seen.has(err)) {
 | 
						|
    return message + ': ...'
 | 
						|
  }
 | 
						|
 | 
						|
  const cause = getErrorCause(err)
 | 
						|
 | 
						|
  if (cause) {
 | 
						|
    seen.add(err)
 | 
						|
 | 
						|
    // @ts-ignore
 | 
						|
    const skipIfVErrorStyleCause = typeof err.cause === 'function'
 | 
						|
 | 
						|
    return (message +
 | 
						|
      (skipIfVErrorStyleCause ? '' : ': ') +
 | 
						|
      _messageWithCauses(cause, seen, skipIfVErrorStyleCause))
 | 
						|
  } else {
 | 
						|
    return message
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @param {Error} err
 | 
						|
 * @returns {string}
 | 
						|
 */
 | 
						|
const messageWithCauses = (err) => _messageWithCauses(err, new Set())
 | 
						|
 | 
						|
module.exports = {
 | 
						|
  isErrorLike,
 | 
						|
  getErrorCause,
 | 
						|
  stackWithCauses,
 | 
						|
  messageWithCauses
 | 
						|
}
 |