Skip to content

Commit

Permalink
Merge pull request #156 from supercharge/add-renderable-error-contract
Browse files Browse the repository at this point in the history
Add error contracts: RenderableError and ReportableError
  • Loading branch information
marcuspoehls authored Dec 11, 2023
2 parents 3a35dbb + 3b56855 commit 72a893b
Show file tree
Hide file tree
Showing 8 changed files with 40 additions and 14 deletions.
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

0 comments on commit 72a893b

Please sign in to comment.