Skip to content

Add error contracts: RenderableError and ReportableError #156

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@
- `@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
- 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)
Expand Down
3 changes: 2 additions & 1 deletion packages/application/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
8 changes: 8 additions & 0 deletions packages/contracts/src/core/renderable-error.ts
Original file line number Diff line number Diff line change
@@ -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> | any
}
9 changes: 9 additions & 0 deletions packages/contracts/src/core/reportable-error.ts
Original file line number Diff line number Diff line change
@@ -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> | any
}
2 changes: 2 additions & 0 deletions packages/contracts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
17 changes: 9 additions & 8 deletions packages/core/src/errors/handler.ts
Original file line number Diff line number Diff line change
@@ -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 {
/**
Expand All @@ -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<void>>
protected readonly reportCallbacks: Array<(error: any, ctx: HttpContext) => Promise<void> | void>

/**
* Create a new error handler instance.
Expand Down Expand Up @@ -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 []
}

/**
Expand Down Expand Up @@ -112,15 +113,15 @@ export class ErrorHandler implements ErrorHandlerContract {
/**
* Handle the given error.
*/
async handle (error: any, ctx: HttpContext): Promise<void> {
async handle (error: Error, ctx: HttpContext): Promise<void> {
await this.report(error, ctx)
await this.render(error, ctx)
}

/**
* Report an error.
*/
async report (error: any, ctx: HttpContext): Promise<void> {
async report (error: Error, ctx: HttpContext): Promise<void> {
if (this.shouldNotReport(error)) {
return
}
Expand Down Expand Up @@ -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<unknown> {
async errorReported (error: SetOptional<ReportableError, 'report'>, ctx: HttpContext): Promise<unknown> {
if (typeof error.report !== 'function') {
return false
}
Expand All @@ -161,7 +162,7 @@ export class ErrorHandler implements ErrorHandlerContract {
/**
* Render the error into an HTTP response.
*/
async render (error: any, ctx: HttpContext): Promise<any> {
async render (error: Error, ctx: HttpContext): Promise<any> {
if (await this.errorRendered(error, ctx)) {
return
}
Expand All @@ -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<unknown> {
async errorRendered (error: SetOptional<RenderableError, 'render'>, ctx: HttpContext): Promise<unknown> {
if (typeof error.render !== 'function') {
return false
}
Expand Down
10 changes: 5 additions & 5 deletions packages/core/src/errors/http-error.ts
Original file line number Diff line number Diff line change
@@ -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)
}
Expand All @@ -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)
)

Expand Down