From 8c2de6b5561f6df9717f09ecddd24f8b8381b0a8 Mon Sep 17 00:00:00 2001 From: Vincent Weevers Date: Sat, 18 Sep 2021 16:08:04 +0200 Subject: [PATCH] Don't wrap existing errors So that when `abstract-leveldown` starts using `level-errors`, and such a db is wrapped with `levelup`, `levelup` will not rewrap errors and lose stack trace information in the process. I.e.: ``` // Error created by abstract-leveldown const err = new ReadError('example') // Error created by levelup const wrapped = new ReadError(err) assert(wrapped === err) ``` Ref https://github.com/Level/community/issues/58 --- errors.js | 13 +++++++++++-- test.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/errors.js b/errors.js index 2648bb1..2aeddf9 100644 --- a/errors.js +++ b/errors.js @@ -2,16 +2,25 @@ function createError (type, Proto) { const Err = function (message, cause) { - if (message && typeof message !== 'string') { + if (typeof message === 'object' && message !== null) { // Can be passed just a cause cause = cause || message message = message.message || message.name } + message = message || '' + cause = cause || undefined + + // If input is already of type, return as-is to keep its stack trace. + // Avoid instanceof, for when node_modules has multiple copies of level-errors. + if (typeof cause === 'object' && cause.type === type && cause.message === message) { + return cause + } + Object.defineProperty(this, 'type', { value: type, enumerable: false, writable: true, configurable: true }) Object.defineProperty(this, 'name', { value: type, enumerable: false, writable: true, configurable: true }) Object.defineProperty(this, 'cause', { value: cause, enumerable: false, writable: true, configurable: true }) - Object.defineProperty(this, 'message', { value: message || '', enumerable: false, writable: true, configurable: true }) + Object.defineProperty(this, 'message', { value: message, enumerable: false, writable: true, configurable: true }) Error.call(this) diff --git a/test.js b/test.js index c46d807..da5fa54 100644 --- a/test.js +++ b/test.js @@ -69,3 +69,33 @@ test('error message is writable for flexibility', function (t) { t.is(error.message, 'Got error: foo') t.end() }) + +test('returns original instance if cause is the same type', function (t) { + const cause = new errors.NotFoundError('Key not found in database [foo]') + const error = new errors.NotFoundError(cause) + t.ok(cause === error, 'same instance') + t.is(error.message, 'Key not found in database [foo]') + t.end() +}) + +test('returns new instance if cause prototype is different', function (t) { + const cause = new errors.NotFoundError('Key not found in database [foo]') + const error = new errors.WriteError(cause) + t.ok(cause !== error, 'new instance') + t.is(error.message, 'Key not found in database [foo]') + t.end() +}) + +test('returns original instance if message and cause are the same', function (t) { + const cause = new errors.NotFoundError('Key not found in database [foo]') + const error = new errors.NotFoundError('Key not found in database [foo]', cause) + t.ok(cause === error, 'same instance') + t.end() +}) + +test('returns new instance if message is different', function (t) { + const cause = new errors.NotFoundError('Key not found in database [foo]') + const error = new errors.NotFoundError('Key not found in database [bar]', cause) + t.ok(cause !== error, 'new instance') + t.end() +})