diff --git a/lib/err-helpers.js b/lib/err-helpers.js index 17b465f..d00c201 100644 --- a/lib/err-helpers.js +++ b/lib/err-helpers.js @@ -12,23 +12,22 @@ const getErrorCause = (err) => { if (!err) return - /** @type {unknown} */ - // @ts-ignore - const cause = err.cause + const cause = evaluateCause(err.cause) - // VError / NError style causes - if (typeof cause === 'function') { - // @ts-ignore - const causeResult = err.cause() + return cause instanceof Error + ? cause + : undefined +} - return causeResult instanceof Error - ? causeResult - : undefined - } else { - return cause instanceof Error - ? cause - : undefined - } +/** + * @param {unknown|(()=>err)} cause + * @returns {Error|undefined} + */ +const evaluateCause = (cause) => { + // VError / NError style causes are functions + return typeof cause === 'function' + ? cause() + : cause } /** @@ -108,6 +107,7 @@ const messageWithCauses = (err) => _messageWithCauses(err, new Set()) module.exports = { getErrorCause, + evaluateCause, stackWithCauses, messageWithCauses } diff --git a/lib/err.js b/lib/err.js index d92fce4..fd63e23 100644 --- a/lib/err.js +++ b/lib/err.js @@ -2,7 +2,7 @@ module.exports = errSerializer -const { messageWithCauses, stackWithCauses } = require('./err-helpers') +const { messageWithCauses, stackWithCauses, evaluateCause } = require('./err-helpers') const { toString } = Object.prototype const seen = Symbol('circular-ref-tag') @@ -66,7 +66,8 @@ function errSerializer (err) { } if (err.cause) { - _err.cause = errSerializer(err.cause) + const cause = evaluateCause(err.cause) + _err.cause = errSerializer(cause) } for (const key in err) { diff --git a/test/err.test.js b/test/err.test.js index d073c06..0ca4a58 100644 --- a/test/err.test.js +++ b/test/err.test.js @@ -218,3 +218,31 @@ test('serializes causes', function (t) { t.equal(serialized.cause.cause.message, 'baz') t.match(serialized.cause.cause.stack, /err\.test\.js:/) }) + +test('serializes causes with VError support', function (t) { + t.plan(11) + + // Fake VError-style setup + const err = Error('foo: bar') + err.cause = () => { + const err = Error('bar') + err.cause = Error('abc') + return err + } + + const serialized = serializer(err) + + t.equal(serialized.type, 'Error') + t.equal(serialized.message, 'foo: bar: abc') // message serialization already walks cause-chain + t.match(serialized.stack, /err\.test\.js:/) + + t.ok(serialized.cause) + t.equal(serialized.cause.type, 'Error') + t.equal(serialized.cause.message, 'bar: abc') + t.match(serialized.cause.stack, /err\.test\.js:/) + + t.ok(serialized.cause.cause) + t.equal(serialized.cause.cause.type, 'Error') + t.equal(serialized.cause.cause.message, 'abc') + t.match(serialized.cause.cause.stack, /err\.test\.js:/) +})