@@ -16,6 +16,11 @@ import type {
16
16
} from '../types/rest.js' ;
17
17
import { HttpVerbs } from './constants.js' ;
18
18
import { ErrorHandlerRegistry } from './ErrorHandlerRegistry.js' ;
19
+ import {
20
+ MethodNotAllowedError ,
21
+ NotFoundError ,
22
+ ServiceError ,
23
+ } from './errors.js' ;
19
24
import { Route } from './Route.js' ;
20
25
import { RouteHandlerRegistry } from './RouteHandlerRegistry.js' ;
21
26
@@ -54,13 +59,37 @@ abstract class BaseRouter {
54
59
this . isDev = isDevMode ( ) ;
55
60
}
56
61
62
+ /**
63
+ * Registers a custom error handler for specific error types.
64
+ *
65
+ * @param errorType - The error constructor(s) to handle
66
+ * @param handler - The error handler function that returns an ErrorResponse
67
+ */
57
68
public errorHandler < T extends Error > (
58
69
errorType : ErrorConstructor < T > | ErrorConstructor < T > [ ] ,
59
70
handler : ErrorHandler < T >
60
71
) : void {
61
72
this . errorHandlerRegistry . register ( errorType , handler ) ;
62
73
}
63
74
75
+ /**
76
+ * Registers a custom handler for 404 Not Found errors.
77
+ *
78
+ * @param handler - The error handler function for NotFoundError
79
+ */
80
+ public notFound ( handler : ErrorHandler < NotFoundError > ) : void {
81
+ this . errorHandlerRegistry . register ( NotFoundError , handler ) ;
82
+ }
83
+
84
+ /**
85
+ * Registers a custom handler for 405 Method Not Allowed errors.
86
+ *
87
+ * @param handler - The error handler function for MethodNotAllowedError
88
+ */
89
+ public methodNotAllowed ( handler : ErrorHandler < MethodNotAllowedError > ) : void {
90
+ this . errorHandlerRegistry . register ( MethodNotAllowedError , handler ) ;
91
+ }
92
+
64
93
public abstract resolve (
65
94
event : unknown ,
66
95
context : Context ,
@@ -76,6 +105,62 @@ abstract class BaseRouter {
76
105
}
77
106
}
78
107
108
+ /**
109
+ * Handles errors by finding a registered error handler or falling
110
+ * back to a default handler.
111
+ *
112
+ * @param error - The error to handle
113
+ * @returns A Response object with appropriate status code and error details
114
+ */
115
+ protected async handleError ( error : Error ) : Promise < Response > {
116
+ const handler = this . errorHandlerRegistry . resolve ( error ) ;
117
+ if ( handler !== null ) {
118
+ try {
119
+ const body = await handler ( error ) ;
120
+ return new Response ( JSON . stringify ( body ) , {
121
+ status : body . statusCode ,
122
+ headers : { 'Content-Type' : 'application/json' } ,
123
+ } ) ;
124
+ } catch ( handlerError ) {
125
+ return this . #defaultErrorHandler( handlerError as Error ) ;
126
+ }
127
+ }
128
+
129
+ if ( error instanceof ServiceError ) {
130
+ return new Response ( JSON . stringify ( error . toJSON ( ) ) , {
131
+ status : error . statusCode ,
132
+ headers : { 'Content-Type' : 'application/json' } ,
133
+ } ) ;
134
+ }
135
+
136
+ return this . #defaultErrorHandler( error ) ;
137
+ }
138
+
139
+ /**
140
+ * Default error handler that returns a 500 Internal Server Error response.
141
+ * In development mode, includes stack trace and error details.
142
+ *
143
+ * @param error - The error to handle
144
+ * @returns A Response object with 500 status and error details
145
+ */
146
+ #defaultErrorHandler( error : Error ) : Response {
147
+ return new Response (
148
+ JSON . stringify ( {
149
+ statusCode : 500 ,
150
+ error : 'Internal Server Error' ,
151
+ message : isDevMode ( ) ? error . message : 'Internal Server Error' ,
152
+ ...( isDevMode ( ) && {
153
+ stack : error . stack ,
154
+ details : { errorName : error . name } ,
155
+ } ) ,
156
+ } ) ,
157
+ {
158
+ status : 500 ,
159
+ headers : { 'Content-Type' : 'application/json' } ,
160
+ }
161
+ ) ;
162
+ }
163
+
79
164
#handleHttpMethod(
80
165
method : HttpMethod ,
81
166
path : Path ,
0 commit comments