diff --git a/package-lock.json b/package-lock.json index 15baeb1..6cafef9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@athenna/common", - "version": "4.27.0", + "version": "4.28.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@athenna/common", - "version": "4.27.0", + "version": "4.28.0", "license": "MIT", "dependencies": { "@fastify/formbody": "^7.4.0", @@ -16,7 +16,7 @@ "change-case": "^4.1.2", "collect.js": "^4.36.1", "execa": "^8.0.1", - "fastify": "^4.25.1", + "fastify": "^4.25.2", "got": "^12.6.1", "http-status-codes": "^2.2.0", "is-wsl": "^2.2.0", @@ -130,7 +130,6 @@ "reflect-metadata": "^0.1.13", "rimraf": "^5.0.5", "ts-node": "^10.9.1", - "typescript": "^5.2.2" } }, @@ -4413,9 +4412,9 @@ "integrity": "sha512-cIusKBIt/R/oI6z/1nyfe2FvGKVTohVRfvkOhvx0nCEW+xf5NoCXjAHcWp93uOUBchzYcsvPlrapAdX1uW+YGg==" }, "node_modules/fastify": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.25.1.tgz", - "integrity": "sha512-D8d0rv61TwqoAS7lom2tvIlgVMlx88lLsiwXyWNjA7CU/LC/mx/Gp2WAlC0S/ABq19U+y/aRvYFG5xLUu2aMrg==", + "version": "4.25.2", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.25.2.tgz", + "integrity": "sha512-SywRouGleDHvRh054onj+lEZnbC1sBCLkR0UY3oyJwjD4BdZJUrxBqfkfCaqn74pVCwBaRHGuL3nEWeHbHzAfw==", "dependencies": { "@fastify/ajv-compiler": "^3.5.0", "@fastify/error": "^3.4.0", diff --git a/package.json b/package.json index 75497c5..d9da340 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@athenna/common", - "version": "4.27.0", + "version": "4.28.0", "description": "The Athenna common helpers to use in any Node.js ESM project.", "license": "MIT", "author": "João Lenon ", @@ -61,7 +61,7 @@ "change-case": "^4.1.2", "collect.js": "^4.36.1", "execa": "^8.0.1", - "fastify": "^4.25.1", + "fastify": "^4.25.2", "got": "^12.6.1", "http-status-codes": "^2.2.0", "is-wsl": "^2.2.0", diff --git a/src/globals/Error.ts b/src/globals/Error.ts index b30e0be..9960dbb 100644 --- a/src/globals/Error.ts +++ b/src/globals/Error.ts @@ -30,6 +30,7 @@ Error.prototype.toAthennaException = function (options: ExceptionJson = {}) { options.stack = options.stack || this.stack options.message = options.message || this.message options.code = options.code || changeCase.constantCase(options.name) + options.details = options.details || this.details || this.errors return new Exception(options) } diff --git a/src/helpers/Exception.ts b/src/helpers/Exception.ts index 3f183b7..5f28e01 100644 --- a/src/helpers/Exception.ts +++ b/src/helpers/Exception.ts @@ -15,11 +15,13 @@ import YouchTerminal from 'youch-terminal' import { Color } from '#src/helpers/Color' import { Options } from '#src/helpers/Options' import type { ExceptionJson } from '#src/types' +import { Is } from '@athenna/common' export class Exception extends Error { public code?: string public help?: any public status?: number + public details?: any[] public isAthennaException = true /** @@ -46,6 +48,10 @@ export class Exception extends Error { this.help = options.help } + if (options.details) { + this.details = options.details + } + if (options.stack) { this.stack = options.stack } else { @@ -65,6 +71,7 @@ export class Exception extends Error { json.message = this.message if (this.help) json.help = this.help + if (this.details) json.details = this.details if (stack) json.stack = this.stack return json @@ -92,11 +99,24 @@ export class Exception extends Error { const separator = Color.cyan('-----') const helpKey = Color.gray.bold.bgGreen(' HELP ') + const detailsKey = Color.gray.bold.bgHex('#f18b0e')(' DETAILS ') const title = Color.gray.bold.bgRed(` ${this.code || this.name} `) let help = '' let message = `${title}\n\n${Color.apply(this.message)}` + if (this.details && this.details.length) { + message = `${message}\n\n${detailsKey}\n\n${this.details + .map(detail => { + if (Is.String(detail)) { + return Color.orange(Color.apply(detail)) + } + + return Color.orange(JSON.stringify(detail, null, 2)) + }) + .join('\n')}` + } + if (this.help && this.help !== '') { help = `${helpKey}\n\n ${Color.green( Color.apply(this.help) @@ -111,7 +131,8 @@ export class Exception extends Error { message, code: this.code, stack: this.stack, - status: this.status + status: this.status, + details: this.details }), {} ).toJSON() diff --git a/src/types/json/ExceptionJson.ts b/src/types/json/ExceptionJson.ts index 18a4bc1..dabb960 100644 --- a/src/types/json/ExceptionJson.ts +++ b/src/types/json/ExceptionJson.ts @@ -13,5 +13,6 @@ export interface ExceptionJson { status?: number message?: string help?: any + details?: any[] stack?: string } diff --git a/tests/unit/ExceptionTest.ts b/tests/unit/ExceptionTest.ts index cba8516..7f57289 100644 --- a/tests/unit/ExceptionTest.ts +++ b/tests/unit/ExceptionTest.ts @@ -75,6 +75,31 @@ export default class ExceptionTest { const prettyError = await exception.prettify() + console.log(prettyError) + + assert.isDefined(prettyError) + assert.typeOf(prettyError, 'string') + } + + @Test() + public async shouldBeAbleToPrettifyTheExceptionWithDetails({ assert }: Context) { + class InternalServerException extends Exception { + constructor(content = 'Internal Server Error.', status = 500) { + super({ + status, + message: content, + details: ['Machine error', 'Runtime error'], + code: 'E_RUNTIME_EXCEPTION', + help: 'Restart your computer, works always. 👍' + }) + } + } + + const exception = new InternalServerException() + const prettyError = await exception.prettify() + + console.log(prettyError) + assert.isDefined(prettyError) assert.typeOf(prettyError, 'string') }