diff --git a/CHANGELOG.md b/CHANGELOG.md index e8257df6..71700e96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,12 @@ - allow users to define only selected hashing driver constructros in `HashConfig#drivers` - export a `ViteConfig` interface - extend `ViewConfigBuilder` interface: add `withoutLayout` method + - 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 + - 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) 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 new file mode 100644 index 00000000..edf38990 --- /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 | any +} diff --git a/packages/contracts/src/core/reportable-error.ts b/packages/contracts/src/core/reportable-error.ts new file mode 100644 index 00000000..82dd35aa --- /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 93616554..792eb3c0 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/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 e7356164..8b10cf79 100644 --- a/packages/core/src/errors/handler.ts +++ b/packages/core/src/errors/handler.ts @@ -1,9 +1,10 @@ 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' -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 +20,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 +68,7 @@ export class ErrorHandler implements ErrorHandlerContract { * Returns an array of errors that should not be reported. */ dontReport (): ErrorConstructor[] { - return ([] as ErrorConstructor[]) + return [] } /** @@ -112,7 +113,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 +121,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 +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: any, ctx: HttpContext): Promise { + async errorReported (error: SetOptional, ctx: HttpContext): Promise { if (typeof error.report !== 'function') { return false } @@ -161,7 +162,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 +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: any, ctx: HttpContext): Promise { + async errorRendered (error: SetOptional, 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) )