diff --git a/lib/index.js b/lib/index.js index 15897b2..bf497bf 100755 --- a/lib/index.js +++ b/lib/index.js @@ -76,6 +76,8 @@ exports.Boom = class Boom extends Error { return this.output.statusCode >= 500; } + set isServer(value) {} // Allow for compatiblity with legacy boom + constructor(message, options = {}) { const { statusCode = 500, data, headers, ctor = exports.Boom } = options; @@ -83,7 +85,7 @@ exports.Boom = class Boom extends Error { super(message ?? internals.codes.get(statusCode) ?? 'Unknown', options); Error.captureStackTrace(this, ctor); // Filter the stack to our external API - this.#apply(data, statusCode, headers); + internals.apply(this, data, statusCode, headers); } static [Symbol.hasInstance](instance) { @@ -102,53 +104,58 @@ exports.Boom = class Boom extends Error { this.output.payload = new internals.PayloadObject(this, this.output.statusCode, debug); } - #apply(data, statusCode, headers, message) { + static { + Object.defineProperty(this.prototype, 'name', { value: 'Boom', writable: true, configurable: true }); + Object.defineProperty(this.prototype, 'isBoom', { value: true, writable: true, configurable: true }); - if (data !== undefined) { - this.data = data; - } + exports.isBoom = function (err, statusCode) { - if (statusCode) { - const numberCode = parseInt(statusCode, 10); - if (isNaN(numberCode) || numberCode < 400) { - throw new TypeError(`statusCode must be a number (400+): ${statusCode}`); - } + return err instanceof Error && !!err.isBoom && (!statusCode || err.output.statusCode === statusCode); + }; - if (message) { - this.message = `${message}: ${this.message}`; - } + } +}; - const payload = new internals.PayloadObject(this, numberCode, false); - this.output = new internals.BoomOutput(numberCode, payload, headers); - } + +exports.boomify = function (err, options = {}) { + + const { override, data, statusCode, message } = options; + + if (!err?.isBoom) { + return new exports.Boom(message, { statusCode, cause: err, data }); } - static { - Object.defineProperty(this.prototype, 'name', { value: 'Boom', writable: true, configurable: true }); - Object.defineProperty(this.prototype, 'isBoom', { value: true, configurable: true }); + if (override === false) { // Defaults to true + internals.apply(err, data); + } + else { + internals.apply(err, data, statusCode ?? err.output.statusCode, {}, message); + } - exports.isBoom = function (err, statusCode) { + err.isServer = err.output.statusCode >= 500; // Assign, in case it is a legacy boom object - return err instanceof Error && !!err.isBoom && (!statusCode || err.output.statusCode === statusCode); - }; + return err; +}; - exports.boomify = function (err, options = {}) { - const { override, data, statusCode, message } = options; +internals.apply = function (boom, data, statusCode, headers, message) { - if (!err?.isBoom) { - return new exports.Boom(message, { statusCode, cause: err, data }); - } + if (data !== undefined) { + boom.data = data; + } - if (override === false) { // Defaults to true - err.#apply(data); - } - else { - err.#apply(data, statusCode ?? err.output.statusCode, {}, message); - } + if (statusCode) { + const numberCode = parseInt(statusCode, 10); + if (isNaN(numberCode) || numberCode < 400) { + throw new TypeError(`statusCode must be a number (400+): ${statusCode}`); + } - return err; - }; + if (message) { + boom.message = `${message}: ${boom.message}`; + } + + const payload = new internals.PayloadObject(boom, numberCode, false); + boom.output = new internals.BoomOutput(numberCode, payload, headers); } }; diff --git a/package.json b/package.json index 3bae608..1fea125 100644 --- a/package.json +++ b/package.json @@ -25,10 +25,11 @@ "@hapi/eslint-plugin": "*", "@hapi/lab": "^25.1.0", "@types/node": "^17.0.31", - "typescript": "~4.6.4" + "typescript": "~4.6.4", + "@hapi/boom10": "npm:@hapi/boom@^10.0.1" }, "scripts": { - "test": "lab -a @hapi/code -t 100 -L -Y", + "test": "lab -a @hapi/code -t 100 -L", "test-cov-html": "lab -a @hapi/code -t 100 -L -r html -o coverage.html" }, "license": "BSD-3-Clause" diff --git a/test/index.js b/test/index.js index 34152a5..ee4bc60 100755 --- a/test/index.js +++ b/test/index.js @@ -1,6 +1,7 @@ 'use strict'; const Boom = require('..'); +const Boom10 = require('@hapi/boom10'); const Code = require('@hapi/code'); const Lab = require('@hapi/lab'); @@ -107,6 +108,7 @@ describe('Boom', () => { const BadaBoom = class extends Boom.Boom {}; expect(new Boom.Boom('oops')).to.be.instanceOf(Boom.Boom); + expect(new Boom10.Boom('oops')).to.be.instanceOf(Boom.Boom); expect(new BadaBoom('oops')).to.be.instanceOf(Boom.Boom); expect(Boom.badRequest('oops')).to.be.instanceOf(Boom.Boom); expect(new Error('oops')).to.not.be.instanceOf(Boom.Boom); @@ -128,6 +130,12 @@ describe('Boom', () => { expect(new Boom.Boom('oops')).to.not.be.instanceOf(BadaBoom); expect(Boom.badRequest('oops')).to.not.be.instanceOf(BadaBoom); }); + + it('works from legacy boom', () => { + + expect(new Boom.Boom('oops')).to.be.instanceOf(Boom10.Boom); + expect(new Boom10.Boom('oops')).to.be.instanceOf(Boom10.Boom); + }); }); describe('isBoom()', () => { @@ -137,6 +145,7 @@ describe('Boom', () => { // Success expect(Boom.isBoom(new Boom.Boom('oops'))).to.be.true(); + expect(Boom.isBoom(new Boom10.Boom('oops'))).to.be.true(); // Fail @@ -148,11 +157,19 @@ describe('Boom', () => { it('returns true for valid boom object and valid status code', () => { expect(Boom.isBoom(Boom.notFound(),404)).to.be.true(); + expect(Boom.isBoom(Boom10.notFound(), 404)).to.be.true(); }); it('returns false for valid boom object and wrong status code', () => { - expect(Boom.isBoom(Boom.notFound(),503)).to.be.false(); + expect(Boom.isBoom(Boom.notFound(), 503)).to.be.false(); + expect(Boom.isBoom(Boom10.notFound(), 503)).to.be.false(); + }); + + it('works from legacy boom', () => { + + expect(Boom10.isBoom(new Boom.Boom('oops'))).to.be.true(); + expect(Boom10.isBoom(new Boom10.Boom('oops'))).to.be.true(); }); }); @@ -272,6 +289,23 @@ describe('Boom', () => { expect(boom.output.payload.message).to.equal('Hello: 123'); expect(boom.output.statusCode).to.equal(400); }); + + it('works with legacy boom', () => { + + const boom = Boom.boomify(new Boom10.Boom(null, { statusCode: 404 }), { statusCode: 501, message: 'Override' }); + + expect(boom.cause).to.be.undefined(); + expect(boom.message).to.equal('Override: Not Found'); + expect(boom.isServer).to.be.true(); + expect(boom.output.statusCode).to.equal(501); + + const boom10 = Boom10.boomify(new Boom.Boom(null, { statusCode: 404 }), { statusCode: 501, message: 'Override' }); + + expect(boom10.cause).to.be.undefined(); + expect(boom10.message).to.equal('Override: Not Found'); + expect(boom10.isServer).to.be.true(); + expect(boom10.output.statusCode).to.equal(501); + }); }); describe('create()', () => {