From 66722c2b226fcd07c1a587c63da9c672ee84ddaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20P=C3=B6hls?= Date: Fri, 8 Dec 2023 06:44:27 +0100 Subject: [PATCH 1/4] wip --- packages/contracts/src/core/renderable-error.ts | 8 ++++++++ packages/contracts/src/core/reportable-error.ts | 9 +++++++++ packages/contracts/src/index.ts | 2 ++ packages/core/src/errors/handler.ts | 16 ++++++++-------- packages/core/src/errors/http-error.ts | 10 +++++----- 5 files changed, 32 insertions(+), 13 deletions(-) create mode 100644 packages/contracts/src/core/renderable-error.ts create mode 100644 packages/contracts/src/core/reportable-error.ts diff --git a/packages/contracts/src/core/renderable-error.ts b/packages/contracts/src/core/renderable-error.ts new file mode 100644 index 00000000..4d69f9c3 --- /dev/null +++ b/packages/contracts/src/core/renderable-error.ts @@ -0,0 +1,8 @@ +import { HttpContext } from '../index.js' + +export interface RenderableError extends Error { + /** + * Render an error into an HTTP response. + */ + render?(error: Error, ctx: HttpContext): Promise +} diff --git a/packages/contracts/src/core/reportable-error.ts b/packages/contracts/src/core/reportable-error.ts new file mode 100644 index 00000000..92d59e64 --- /dev/null +++ b/packages/contracts/src/core/reportable-error.ts @@ -0,0 +1,9 @@ + +import { HttpContext } from '../index.js' + +export interface ReportableError extends Error { + /** + * Report an error, to a 3rd-party service, the console, a file, or somewhere else. + */ + report?(error: Error, ctx: HttpContext): Promise | any +} diff --git a/packages/contracts/src/index.ts b/packages/contracts/src/index.ts index c073e31d..8ef114fb 100644 --- a/packages/contracts/src/index.ts +++ b/packages/contracts/src/index.ts @@ -19,6 +19,8 @@ export { EnvStore } from './env/env.js' export { Bootstrapper, BootstrapperCtor } from './core/bootstrapper.js' export { ErrorHandler, ErrorHandlerCtor } from './core/error-handler.js' +export { RenderableError } from './core/renderable-error.js' +export { ReportableError } from './core/reportable-error.js' export { HashBuilder, HashBuilderCallback, HashBuilderConfig } from './hashing/hash-builder.js' export { HashConfig } from './hashing/config.js' diff --git a/packages/core/src/errors/handler.ts b/packages/core/src/errors/handler.ts index e7356164..bb8ae485 100644 --- a/packages/core/src/errors/handler.ts +++ b/packages/core/src/errors/handler.ts @@ -3,7 +3,7 @@ import Youch from 'youch' import { tap } from '@supercharge/goodies' import { HttpError } from './http-error.js' import { Collect } from '@supercharge/collections' -import { Application, ErrorHandler as ErrorHandlerContract, HttpContext, Logger, ViewEngine } from '@supercharge/contracts' +import { Application, ErrorHandler as ErrorHandlerContract, HttpContext, Logger, RenderableError, ReportableError, ViewEngine } from '@supercharge/contracts' export class ErrorHandler implements ErrorHandlerContract { /** @@ -19,7 +19,7 @@ export class ErrorHandler implements ErrorHandlerContract { /** * Stores the list of report callbacks. */ - protected readonly reportCallbacks: Array<(error: HttpError, ctx: HttpContext) => void | Promise> + protected readonly reportCallbacks: Array<(error: any, ctx: HttpContext) => Promise | void> /** * Create a new error handler instance. @@ -67,7 +67,7 @@ export class ErrorHandler implements ErrorHandlerContract { * Returns an array of errors that should not be reported. */ dontReport (): ErrorConstructor[] { - return ([] as ErrorConstructor[]) + return [] } /** @@ -112,7 +112,7 @@ export class ErrorHandler implements ErrorHandlerContract { /** * Handle the given error. */ - async handle (error: any, ctx: HttpContext): Promise { + async handle (error: Error, ctx: HttpContext): Promise { await this.report(error, ctx) await this.render(error, ctx) } @@ -120,7 +120,7 @@ export class ErrorHandler implements ErrorHandlerContract { /** * Report an error. */ - async report (error: any, ctx: HttpContext): Promise { + async report (error: Error, ctx: HttpContext): Promise { if (this.shouldNotReport(error)) { return } @@ -150,7 +150,7 @@ export class ErrorHandler implements ErrorHandlerContract { * Determine whether the given `error` is implementing a `report` method and * that `report` method returns a truthy value, like a valid HTTP response. */ - async errorReported (error: any, ctx: HttpContext): Promise { + async errorReported (error: ReportableError, ctx: HttpContext): Promise { if (typeof error.report !== 'function') { return false } @@ -161,7 +161,7 @@ export class ErrorHandler implements ErrorHandlerContract { /** * Render the error into an HTTP response. */ - async render (error: any, ctx: HttpContext): Promise { + async render (error: Error, ctx: HttpContext): Promise { if (await this.errorRendered(error, ctx)) { return } @@ -183,7 +183,7 @@ export class ErrorHandler implements ErrorHandlerContract { * Determine whether the given `error` is implementing a `render` method and * that `render` method returns a truthy value, like a valid HTTP response. */ - async errorRendered (error: any, ctx: HttpContext): Promise { + async errorRendered (error: RenderableError, ctx: HttpContext): Promise { if (typeof error.render !== 'function') { return false } diff --git a/packages/core/src/errors/http-error.ts b/packages/core/src/errors/http-error.ts index b911f298..9c2f92bf 100644 --- a/packages/core/src/errors/http-error.ts +++ b/packages/core/src/errors/http-error.ts @@ -1,13 +1,13 @@ -import { HttpContext } from '@supercharge/contracts' import { HttpError as BaseHttpError } from '@supercharge/errors' +import { HttpContext, RenderableError, ReportableError } from '@supercharge/contracts' -export class HttpError extends BaseHttpError { +export class HttpError extends BaseHttpError implements RenderableError, ReportableError { /** * Create a new HTTP error instance. */ - constructor (message: string) { - super(message) + constructor (message: string, cause?: any) { + super(message, { cause }) this.withStatus(500) } @@ -16,7 +16,7 @@ export class HttpError extends BaseHttpError { * Returns a new HTTP error instance wrapping the given `error`. */ static wrap (error: Error): HttpError { - const err = new this(error.message).withStatus( + const err = new this(error.message, error).withStatus( this.retrieveStatusFrom(error) ) From 0cba632265e46d6eb9d1c3a40297dffbd8251aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20P=C3=B6hls?= Date: Mon, 11 Dec 2023 09:08:50 +0100 Subject: [PATCH 2/4] make error render and error report methods required --- packages/application/package.json | 3 ++- packages/contracts/src/core/renderable-error.ts | 2 +- packages/contracts/src/core/reportable-error.ts | 2 +- packages/core/package.json | 1 + packages/core/src/errors/handler.ts | 5 +++-- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/application/package.json b/packages/application/package.json index f3bed9b6..3bdb6673 100644 --- a/packages/application/package.json +++ b/packages/application/package.json @@ -28,7 +28,8 @@ "@supercharge/map": "~1.5.0", "@supercharge/strings": "~2.0.0", "globby": "~14.0.0", - "normalize-path": "~3.0.0" + "normalize-path": "~3.0.0", + "type-fest": "~4.8.3" }, "devDependencies": { "@types/normalize-path": "~3.0.2", diff --git a/packages/contracts/src/core/renderable-error.ts b/packages/contracts/src/core/renderable-error.ts index 4d69f9c3..edf38990 100644 --- a/packages/contracts/src/core/renderable-error.ts +++ b/packages/contracts/src/core/renderable-error.ts @@ -4,5 +4,5 @@ export interface RenderableError extends Error { /** * Render an error into an HTTP response. */ - render?(error: Error, ctx: HttpContext): Promise + render(error: Error, ctx: HttpContext): Promise | any } diff --git a/packages/contracts/src/core/reportable-error.ts b/packages/contracts/src/core/reportable-error.ts index 92d59e64..82dd35aa 100644 --- a/packages/contracts/src/core/reportable-error.ts +++ b/packages/contracts/src/core/reportable-error.ts @@ -5,5 +5,5 @@ export interface ReportableError extends Error { /** * Report an error, to a 3rd-party service, the console, a file, or somewhere else. */ - report?(error: Error, ctx: HttpContext): Promise | any + report(error: Error, ctx: HttpContext): Promise | any } diff --git a/packages/core/package.json b/packages/core/package.json index 6048472f..36182b14 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -27,6 +27,7 @@ "@supercharge/set": "~2.2.1", "@supercharge/support": "^4.0.0-alpha.1", "dotenv": "~16.3.1", + "type-fest": "~4.8.3", "youch": "~3.3.3", "youch-terminal": "~2.2.3" }, diff --git a/packages/core/src/errors/handler.ts b/packages/core/src/errors/handler.ts index bb8ae485..8b10cf79 100644 --- a/packages/core/src/errors/handler.ts +++ b/packages/core/src/errors/handler.ts @@ -1,5 +1,6 @@ import Youch from 'youch' +import { SetOptional } from 'type-fest' import { tap } from '@supercharge/goodies' import { HttpError } from './http-error.js' import { Collect } from '@supercharge/collections' @@ -150,7 +151,7 @@ export class ErrorHandler implements ErrorHandlerContract { * Determine whether the given `error` is implementing a `report` method and * that `report` method returns a truthy value, like a valid HTTP response. */ - async errorReported (error: ReportableError, ctx: HttpContext): Promise { + async errorReported (error: SetOptional, ctx: HttpContext): Promise { if (typeof error.report !== 'function') { return false } @@ -183,7 +184,7 @@ export class ErrorHandler implements ErrorHandlerContract { * Determine whether the given `error` is implementing a `render` method and * that `render` method returns a truthy value, like a valid HTTP response. */ - async errorRendered (error: RenderableError, ctx: HttpContext): Promise { + async errorRendered (error: SetOptional, ctx: HttpContext): Promise { if (typeof error.render !== 'function') { return false } From ff79319038b626ca8d8bdf99a994c898d201dbd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20P=C3=B6hls?= Date: Mon, 11 Dec 2023 09:10:25 +0100 Subject: [PATCH 3/4] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b170d9d..bdc9bb23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ - `@supercharge/contracts` - allow users to define only selected hashing driver constructros in `HashConfig#drivers` - export a `ViteConfig` interface + - export `RenderableError` and `ReportableError` interfaces + - `RenderableError` defines the `render(error, httpContext)` method + - `ReportableError` defines the `report(error, httpContext)` method - `@supercharge/core` - bypass import cache when dynamically importing routes from file path - `@supercharge/vite` From 3b568557b401b87651dcff6fa766b45a572ccb45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20P=C3=B6hls?= Date: Mon, 11 Dec 2023 09:13:41 +0100 Subject: [PATCH 4/4] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdc9bb23..c0155673 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - `ReportableError` defines the `report(error, httpContext)` method - `@supercharge/core` - bypass import cache when dynamically importing routes from file path + - keep the original error as the `cause` when wrapping that error into an `HttpError` - `@supercharge/vite` - create `vite` container binding - add a `ViteConfig` instance wrapping a Vite configuration JS object (will be used by a config/vite.ts file)