diff --git a/errors.js b/errors.js index 01b073c..036eba7 100644 --- a/errors.js +++ b/errors.js @@ -1,11 +1,39 @@ 'use strict' -const createError = require('errno').create -const LevelUPError = createError('LevelUPError') -const NotFoundError = createError('NotFoundError', LevelUPError) +function createError (type, Proto) { + const Err = function (message, cause) { + if (typeof message === 'object' && message !== null) { + // Can be passed just a cause + cause = cause || message + message = message.message || message.name + } -NotFoundError.prototype.notFound = true -NotFoundError.prototype.status = 404 + 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 }) + + Error.call(this) + + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, Err) + } + } + + Err.prototype = new Proto() + return Err +} + +const LevelUPError = createError('LevelUPError', Error) module.exports = { LevelUPError: LevelUPError, @@ -13,6 +41,9 @@ module.exports = { OpenError: createError('OpenError', LevelUPError), ReadError: createError('ReadError', LevelUPError), WriteError: createError('WriteError', LevelUPError), - NotFoundError: NotFoundError, + NotFoundError: createError('NotFoundError', LevelUPError), EncodingError: createError('EncodingError', LevelUPError) } + +module.exports.NotFoundError.prototype.notFound = true +module.exports.NotFoundError.prototype.status = 404 diff --git a/package.json b/package.json index ee2308a..ea913a4 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,7 @@ "LICENSE.md", "UPGRADING.md" ], - "dependencies": { - "errno": "^1.0.0" - }, + "dependencies": {}, "devDependencies": { "airtap": "^4.0.3", "airtap-playwright": "^1.0.1", diff --git a/test.js b/test.js index 8f3e995..f0cff56 100644 --- a/test.js +++ b/test.js @@ -3,20 +3,100 @@ const test = require('tape') const errors = require('.') -test('all errors are instances of LevelUPError', function (t) { +test('all errors are instances of Error and LevelUPError', function (t) { const LevelUPError = errors.LevelUPError const keys = Object.keys(errors) keys.forEach(function (key) { + t.ok(new errors[key]() instanceof Error) t.ok(new errors[key]() instanceof LevelUPError) }) t.end() }) +test('error with message has expected properties', function (t) { + const error = new errors.ReadError('foo') + + t.is(error.type, 'ReadError') + t.is(error.name, 'ReadError') + t.is(error.cause, undefined) + t.is(error.message, 'foo') + t.end() +}) + +test('error without message has expected properties', function (t) { + const error = new errors.ReadError() + + t.is(error.type, 'ReadError') + t.is(error.name, 'ReadError') + t.is(error.cause, undefined) + t.is(error.message, '') + t.end() +}) + +test('error with cause has expected properties', function (t) { + const cause = new Error('foo') + const error = new errors.ReadError(cause) + + t.is(error.type, 'ReadError') + t.is(error.name, 'ReadError') + t.is(error.cause, cause) + t.is(error.message, 'foo') + t.end() +}) + +test('error with message and cause has expected properties', function (t) { + const cause = new Error('foo') + const error = new errors.ReadError('bar', cause) + + t.is(error.type, 'ReadError') + t.is(error.name, 'ReadError') + t.is(error.cause, cause) + t.is(error.message, 'bar') + t.end() +}) + test('NotFoundError has special properties', function (t) { const error = new errors.NotFoundError() - t.equal(error.notFound, true) - t.equal(error.status, 404) + t.is(error.notFound, true) + t.is(error.status, 404) + t.end() +}) + +test('error message is writable to mirror node core conventions', function (t) { + const error = new errors.WriteError('foo') + error.message = 'Got error: ' + error.message + 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.is(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.isNot(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.is(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.isNot(cause, error, 'new instance') t.end() })