/**
* Adds additional contextual variables to an error and
* modifies its stacktrace and toString output to include
* that context.
*
* @param {Error} in_error
* @param {object} context
* @returns {Error}
*/
const error_context = (in_error, context = {}) => {
// If the incoming error already has context, merge the two
if ( typeof in_error.context === 'object' ) {
in_error.context = {...in_error.context, ...context}
} else { // Otherwise, assign the context
in_error.context = context
}
// Preserve the original stacktrace
if ( !in_error.original_stack ) {
in_error.original_stack = in_error.stack
}
in_error.toString = () => {
// Get the JS stacktrace
let stack = in_error.original_stack
// Build the context display
const context_lines = []
if ( typeof in_error.context === 'object' ) {
for ( const name in in_error.context ) {
const val = in_error.context[name]
try {
const string = JSON.stringify(val)
context_lines.push(` - ${name}: ${string}`)
} catch (e) {
if ( typeof val.toString === 'function' ) {
context_lines.push(` - ${name}: ${val}`)
}
}
}
}
if ( context_lines.length > 0 ) {
stack += `\n\nWith the following context:\n${context_lines.join('\n')}\n`
}
return stack
}
in_error.stack = in_error.toString()
return in_error
}
module.exports = exports = error_context