@buenojs/bueno 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +109 -0
- package/.github/workflows/ci.yml +31 -0
- package/LICENSE +21 -0
- package/README.md +892 -0
- package/architecture.md +652 -0
- package/bun.lock +70 -0
- package/dist/cli/index.js +3233 -0
- package/dist/index.js +9014 -0
- package/package.json +77 -0
- package/src/cache/index.ts +795 -0
- package/src/cli/ARCHITECTURE.md +837 -0
- package/src/cli/bin.ts +10 -0
- package/src/cli/commands/build.ts +425 -0
- package/src/cli/commands/dev.ts +248 -0
- package/src/cli/commands/generate.ts +541 -0
- package/src/cli/commands/help.ts +55 -0
- package/src/cli/commands/index.ts +112 -0
- package/src/cli/commands/migration.ts +355 -0
- package/src/cli/commands/new.ts +804 -0
- package/src/cli/commands/start.ts +208 -0
- package/src/cli/core/args.ts +283 -0
- package/src/cli/core/console.ts +349 -0
- package/src/cli/core/index.ts +60 -0
- package/src/cli/core/prompt.ts +424 -0
- package/src/cli/core/spinner.ts +265 -0
- package/src/cli/index.ts +135 -0
- package/src/cli/templates/deploy.ts +295 -0
- package/src/cli/templates/docker.ts +307 -0
- package/src/cli/templates/index.ts +24 -0
- package/src/cli/utils/fs.ts +428 -0
- package/src/cli/utils/index.ts +8 -0
- package/src/cli/utils/strings.ts +197 -0
- package/src/config/env.ts +408 -0
- package/src/config/index.ts +506 -0
- package/src/config/loader.ts +329 -0
- package/src/config/merge.ts +285 -0
- package/src/config/types.ts +320 -0
- package/src/config/validation.ts +441 -0
- package/src/container/forward-ref.ts +143 -0
- package/src/container/index.ts +386 -0
- package/src/context/index.ts +360 -0
- package/src/database/index.ts +1142 -0
- package/src/database/migrations/index.ts +371 -0
- package/src/database/schema/index.ts +619 -0
- package/src/frontend/api-routes.ts +640 -0
- package/src/frontend/bundler.ts +643 -0
- package/src/frontend/console-client.ts +419 -0
- package/src/frontend/console-stream.ts +587 -0
- package/src/frontend/dev-server.ts +846 -0
- package/src/frontend/file-router.ts +611 -0
- package/src/frontend/frameworks/index.ts +106 -0
- package/src/frontend/frameworks/react.ts +85 -0
- package/src/frontend/frameworks/solid.ts +104 -0
- package/src/frontend/frameworks/svelte.ts +110 -0
- package/src/frontend/frameworks/vue.ts +92 -0
- package/src/frontend/hmr-client.ts +663 -0
- package/src/frontend/hmr.ts +728 -0
- package/src/frontend/index.ts +342 -0
- package/src/frontend/islands.ts +552 -0
- package/src/frontend/isr.ts +555 -0
- package/src/frontend/layout.ts +475 -0
- package/src/frontend/ssr/react.ts +446 -0
- package/src/frontend/ssr/solid.ts +523 -0
- package/src/frontend/ssr/svelte.ts +546 -0
- package/src/frontend/ssr/vue.ts +504 -0
- package/src/frontend/ssr.ts +699 -0
- package/src/frontend/types.ts +2274 -0
- package/src/health/index.ts +604 -0
- package/src/index.ts +410 -0
- package/src/lock/index.ts +587 -0
- package/src/logger/index.ts +444 -0
- package/src/logger/transports/index.ts +969 -0
- package/src/metrics/index.ts +494 -0
- package/src/middleware/built-in.ts +360 -0
- package/src/middleware/index.ts +94 -0
- package/src/modules/filters.ts +458 -0
- package/src/modules/guards.ts +405 -0
- package/src/modules/index.ts +1256 -0
- package/src/modules/interceptors.ts +574 -0
- package/src/modules/lazy.ts +418 -0
- package/src/modules/lifecycle.ts +478 -0
- package/src/modules/metadata.ts +90 -0
- package/src/modules/pipes.ts +626 -0
- package/src/router/index.ts +339 -0
- package/src/router/linear.ts +371 -0
- package/src/router/regex.ts +292 -0
- package/src/router/tree.ts +562 -0
- package/src/rpc/index.ts +1263 -0
- package/src/security/index.ts +436 -0
- package/src/ssg/index.ts +631 -0
- package/src/storage/index.ts +456 -0
- package/src/telemetry/index.ts +1097 -0
- package/src/testing/index.ts +1586 -0
- package/src/types/index.ts +236 -0
- package/src/types/optional-deps.d.ts +219 -0
- package/src/validation/index.ts +276 -0
- package/src/websocket/index.ts +1004 -0
- package/tests/integration/cli.test.ts +1016 -0
- package/tests/integration/fullstack.test.ts +234 -0
- package/tests/unit/cache.test.ts +174 -0
- package/tests/unit/cli-commands.test.ts +892 -0
- package/tests/unit/cli.test.ts +1258 -0
- package/tests/unit/container.test.ts +279 -0
- package/tests/unit/context.test.ts +221 -0
- package/tests/unit/database.test.ts +183 -0
- package/tests/unit/linear-router.test.ts +280 -0
- package/tests/unit/lock.test.ts +336 -0
- package/tests/unit/middleware.test.ts +184 -0
- package/tests/unit/modules.test.ts +142 -0
- package/tests/unit/pubsub.test.ts +257 -0
- package/tests/unit/regex-router.test.ts +265 -0
- package/tests/unit/router.test.ts +373 -0
- package/tests/unit/rpc.test.ts +1248 -0
- package/tests/unit/security.test.ts +174 -0
- package/tests/unit/telemetry.test.ts +371 -0
- package/tests/unit/test-cache.test.ts +110 -0
- package/tests/unit/test-database.test.ts +282 -0
- package/tests/unit/tree-router.test.ts +325 -0
- package/tests/unit/validation.test.ts +794 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exception Filters System
|
|
3
|
+
*
|
|
4
|
+
* Filters catch exceptions thrown during request processing and transform
|
|
5
|
+
* them into appropriate responses. They act as error boundaries in the
|
|
6
|
+
* request pipeline.
|
|
7
|
+
*
|
|
8
|
+
* Execution Order:
|
|
9
|
+
* Incoming Request → Guards → Pipes → Handler → Filters (if error) → Error Response
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { Context } from "../context";
|
|
13
|
+
import type { Token } from "../types";
|
|
14
|
+
import { BuenoError, ValidationError, NotFoundError } from "../types";
|
|
15
|
+
|
|
16
|
+
// ============= Types =============
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Exception filter interface
|
|
20
|
+
* Filters implement this interface to handle specific exception types
|
|
21
|
+
*/
|
|
22
|
+
export interface ExceptionFilter<T = Error> {
|
|
23
|
+
catch(exception: T, context: Context): Response | Promise<Response>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Filter function type for functional filters
|
|
28
|
+
*/
|
|
29
|
+
export type FilterFn<T = Error> = (
|
|
30
|
+
exception: T,
|
|
31
|
+
context: Context,
|
|
32
|
+
) => Response | Promise<Response>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Filter type - can be a token, instance, or function
|
|
36
|
+
*/
|
|
37
|
+
export type Filter<T = Error> =
|
|
38
|
+
| Token<ExceptionFilter<T>>
|
|
39
|
+
| ExceptionFilter<T>
|
|
40
|
+
| FilterFn<T>;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Metadata stored for caught exception types
|
|
44
|
+
*/
|
|
45
|
+
interface CatchMetadata {
|
|
46
|
+
exceptionType: new (...args: unknown[]) => Error;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ============= Metadata Storage =============
|
|
50
|
+
|
|
51
|
+
// Type alias for class constructors
|
|
52
|
+
type Constructor = new (...args: unknown[]) => unknown;
|
|
53
|
+
|
|
54
|
+
// Metadata storage for filters
|
|
55
|
+
const filterMetadataStore = new WeakMap<Constructor, Map<string, unknown>>();
|
|
56
|
+
const catchMetadataStore = new WeakMap<Constructor, CatchMetadata>();
|
|
57
|
+
|
|
58
|
+
// Prototype metadata for method decorators
|
|
59
|
+
const filterPrototypeStore = new WeakMap<object, Map<string, unknown>>();
|
|
60
|
+
|
|
61
|
+
function setFilterMetadata(
|
|
62
|
+
target: Constructor,
|
|
63
|
+
key: string,
|
|
64
|
+
value: unknown,
|
|
65
|
+
): void {
|
|
66
|
+
if (!filterMetadataStore.has(target)) {
|
|
67
|
+
filterMetadataStore.set(target, new Map());
|
|
68
|
+
}
|
|
69
|
+
filterMetadataStore.get(target)?.set(key, value);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function getFilterMetadata<T>(
|
|
73
|
+
target: Constructor,
|
|
74
|
+
key: string,
|
|
75
|
+
): T | undefined {
|
|
76
|
+
return filterMetadataStore.get(target)?.get(key) as T | undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function setFilterPrototypeMetadata(
|
|
80
|
+
target: object,
|
|
81
|
+
key: string,
|
|
82
|
+
value: unknown,
|
|
83
|
+
): void {
|
|
84
|
+
if (!filterPrototypeStore.has(target)) {
|
|
85
|
+
filterPrototypeStore.set(target, new Map());
|
|
86
|
+
}
|
|
87
|
+
filterPrototypeStore.get(target)?.set(key, value);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function getFilterPrototypeMetadata<T>(
|
|
91
|
+
target: object,
|
|
92
|
+
key: string,
|
|
93
|
+
): T | undefined {
|
|
94
|
+
return filterPrototypeStore.get(target)?.get(key) as T | undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ============= Decorators =============
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Decorator to apply filters to a controller class or method
|
|
101
|
+
* Filters are executed in order: global → class → method
|
|
102
|
+
*
|
|
103
|
+
* @param filters - Filters to apply
|
|
104
|
+
* @returns ClassDecorator & MethodDecorator
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* @Controller('users')
|
|
109
|
+
* @UseFilters(CustomExceptionFilter) // Applied to all methods
|
|
110
|
+
* class UsersController {
|
|
111
|
+
* @Get(':id')
|
|
112
|
+
* @UseFilters(NotFoundExceptionFilter) // Additional filter for this method
|
|
113
|
+
* getUser() {}
|
|
114
|
+
* }
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export function UseFilters(...filters: Filter[]): MethodDecorator & ClassDecorator {
|
|
118
|
+
const decorator = (
|
|
119
|
+
target: unknown,
|
|
120
|
+
propertyKey?: string | symbol,
|
|
121
|
+
descriptor?: PropertyDescriptor,
|
|
122
|
+
): PropertyDescriptor | void => {
|
|
123
|
+
if (descriptor && propertyKey !== undefined) {
|
|
124
|
+
// Method decorator
|
|
125
|
+
const targetObj = target as object;
|
|
126
|
+
const existingFilters =
|
|
127
|
+
getFilterPrototypeMetadata<Filter[]>(targetObj, "filters:method") ?? [];
|
|
128
|
+
setFilterPrototypeMetadata(targetObj, "filters:method", [
|
|
129
|
+
...existingFilters,
|
|
130
|
+
...filters,
|
|
131
|
+
]);
|
|
132
|
+
return descriptor;
|
|
133
|
+
} else {
|
|
134
|
+
// Class decorator
|
|
135
|
+
const targetClass = target as Constructor;
|
|
136
|
+
const existingFilters =
|
|
137
|
+
getFilterMetadata<Filter[]>(targetClass, "filters:class") ?? [];
|
|
138
|
+
setFilterMetadata(targetClass, "filters:class", [
|
|
139
|
+
...existingFilters,
|
|
140
|
+
...filters,
|
|
141
|
+
]);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
return decorator as MethodDecorator & ClassDecorator;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Decorator to mark a filter as catching a specific exception type
|
|
149
|
+
*
|
|
150
|
+
* @param exceptionType - The exception class to catch
|
|
151
|
+
* @returns ClassDecorator
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* @Catch(ValidationError)
|
|
156
|
+
* @Injectable()
|
|
157
|
+
* class CustomValidationFilter implements ExceptionFilter<ValidationError> {
|
|
158
|
+
* catch(exception: ValidationError, context: Context): Response {
|
|
159
|
+
* return context.status(422).json({
|
|
160
|
+
* error: 'Validation Failed',
|
|
161
|
+
* issues: exception.issues
|
|
162
|
+
* });
|
|
163
|
+
* }
|
|
164
|
+
* }
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
export function Catch<T extends Error>(
|
|
168
|
+
exceptionType: new (...args: never[]) => T,
|
|
169
|
+
): ClassDecorator {
|
|
170
|
+
const decorator = (target: Constructor): void => {
|
|
171
|
+
catchMetadataStore.set(target, { exceptionType: exceptionType as unknown as new (...args: unknown[]) => Error });
|
|
172
|
+
};
|
|
173
|
+
return decorator as ClassDecorator;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ============= Helper Functions =============
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get class-level filters from a controller
|
|
180
|
+
*/
|
|
181
|
+
export function getClassFilters(target: Constructor): Filter[] | undefined {
|
|
182
|
+
return getFilterMetadata<Filter[]>(target, "filters:class");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Get method-level filters from a controller method
|
|
187
|
+
*/
|
|
188
|
+
export function getMethodFilters(
|
|
189
|
+
target: object,
|
|
190
|
+
propertyKey: string | symbol,
|
|
191
|
+
): Filter[] | undefined {
|
|
192
|
+
// First check method-specific filters
|
|
193
|
+
const methodFilters = getFilterPrototypeMetadata<Filter[]>(
|
|
194
|
+
target,
|
|
195
|
+
`filters:method:${String(propertyKey)}`,
|
|
196
|
+
);
|
|
197
|
+
if (methodFilters) {
|
|
198
|
+
return methodFilters;
|
|
199
|
+
}
|
|
200
|
+
// Fall back to general method filters
|
|
201
|
+
return getFilterPrototypeMetadata<Filter[]>(target, "filters:method");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Get the exception type that a filter catches
|
|
206
|
+
*/
|
|
207
|
+
export function getCatchType(
|
|
208
|
+
filter: Filter,
|
|
209
|
+
): (new (...args: unknown[]) => Error) | undefined {
|
|
210
|
+
// If it's a class constructor, check the catch metadata
|
|
211
|
+
if (typeof filter === "function" && filter.prototype !== undefined) {
|
|
212
|
+
const metadata = catchMetadataStore.get(filter as Constructor);
|
|
213
|
+
return metadata?.exceptionType;
|
|
214
|
+
}
|
|
215
|
+
// If it's an instance, check its constructor
|
|
216
|
+
if (typeof filter === "object" && filter !== null && "catch" in filter) {
|
|
217
|
+
const constructor = (filter as object).constructor as Constructor;
|
|
218
|
+
const metadata = catchMetadataStore.get(constructor);
|
|
219
|
+
return metadata?.exceptionType;
|
|
220
|
+
}
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Check if a filter can handle a specific exception
|
|
226
|
+
*/
|
|
227
|
+
export function canHandleException(
|
|
228
|
+
filter: Filter,
|
|
229
|
+
exception: Error,
|
|
230
|
+
): boolean {
|
|
231
|
+
const catchType = getCatchType(filter);
|
|
232
|
+
if (!catchType) {
|
|
233
|
+
// No specific catch type means it handles all exceptions
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
return exception instanceof catchType;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Type guard to check if a value is an ExceptionFilter instance
|
|
241
|
+
*/
|
|
242
|
+
export function isExceptionFilter(value: unknown): value is ExceptionFilter {
|
|
243
|
+
return (
|
|
244
|
+
typeof value === "object" &&
|
|
245
|
+
value !== null &&
|
|
246
|
+
"catch" in value &&
|
|
247
|
+
typeof (value as ExceptionFilter).catch === "function"
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Type guard to check if a value is a FilterFn
|
|
253
|
+
*/
|
|
254
|
+
export function isFilterFn(value: unknown): value is FilterFn {
|
|
255
|
+
return typeof value === "function" && value.length === 2;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Execute a filter and return the response
|
|
260
|
+
*/
|
|
261
|
+
export async function executeFilter(
|
|
262
|
+
filter: Filter,
|
|
263
|
+
exception: Error,
|
|
264
|
+
context: Context,
|
|
265
|
+
resolveFilter?: (filter: Filter) => ExceptionFilter | null,
|
|
266
|
+
): Promise<Response> {
|
|
267
|
+
let filterInstance: ExceptionFilter | null = null;
|
|
268
|
+
|
|
269
|
+
if (isExceptionFilter(filter)) {
|
|
270
|
+
filterInstance = filter;
|
|
271
|
+
} else if (isFilterFn(filter)) {
|
|
272
|
+
// Convert filter function to response
|
|
273
|
+
return filter(exception, context);
|
|
274
|
+
} else if (typeof filter === "function") {
|
|
275
|
+
// It's a class constructor - try to resolve from container or instantiate
|
|
276
|
+
if (resolveFilter) {
|
|
277
|
+
filterInstance = resolveFilter(filter);
|
|
278
|
+
}
|
|
279
|
+
if (!filterInstance) {
|
|
280
|
+
// Try to instantiate directly (for simple filters without dependencies)
|
|
281
|
+
try {
|
|
282
|
+
const Constructor = filter as new () => ExceptionFilter;
|
|
283
|
+
filterInstance = new Constructor();
|
|
284
|
+
} catch {
|
|
285
|
+
// Cannot instantiate - will fall through to default handling
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
} else if (
|
|
289
|
+
typeof filter === "object" &&
|
|
290
|
+
filter !== null &&
|
|
291
|
+
!isExceptionFilter(filter)
|
|
292
|
+
) {
|
|
293
|
+
// It's a token - try to resolve
|
|
294
|
+
if (resolveFilter) {
|
|
295
|
+
filterInstance = resolveFilter(filter);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (filterInstance && isExceptionFilter(filterInstance)) {
|
|
300
|
+
return filterInstance.catch(exception, context);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Fallback - should not reach here if a catch-all filter is configured
|
|
304
|
+
return createInternalErrorResponse(exception);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Options for executing filters
|
|
309
|
+
*/
|
|
310
|
+
export interface ExecuteFiltersOptions {
|
|
311
|
+
globalFilters: Filter[];
|
|
312
|
+
classFilters: Filter[];
|
|
313
|
+
methodFilters: Filter[];
|
|
314
|
+
resolveFilter?: (filter: Filter) => ExceptionFilter | null;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Find and execute the appropriate filter for an exception
|
|
319
|
+
* Filters are checked in order: method → class → global
|
|
320
|
+
* The first filter that can handle the exception is used
|
|
321
|
+
*/
|
|
322
|
+
export async function findAndExecuteFilter(
|
|
323
|
+
exception: Error,
|
|
324
|
+
context: Context,
|
|
325
|
+
options: ExecuteFiltersOptions,
|
|
326
|
+
): Promise<Response> {
|
|
327
|
+
const { globalFilters, classFilters, methodFilters, resolveFilter } = options;
|
|
328
|
+
|
|
329
|
+
// Combine all filters in execution order (method first, then class, then global)
|
|
330
|
+
const allFilters = [
|
|
331
|
+
...methodFilters,
|
|
332
|
+
...classFilters,
|
|
333
|
+
...globalFilters,
|
|
334
|
+
];
|
|
335
|
+
|
|
336
|
+
// Find the first filter that can handle this exception type
|
|
337
|
+
for (const filter of allFilters) {
|
|
338
|
+
if (canHandleException(filter, exception)) {
|
|
339
|
+
return executeFilter(filter, exception, context, resolveFilter);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// No specific filter found - use default error response
|
|
344
|
+
return createDefaultErrorResponse(exception, context);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// ============= Built-in Filters =============
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* HttpExceptionFilter - Handles BuenoError exceptions
|
|
351
|
+
* Returns appropriate HTTP status codes and error details
|
|
352
|
+
*/
|
|
353
|
+
@Catch(BuenoError)
|
|
354
|
+
export class HttpExceptionFilter implements ExceptionFilter<BuenoError> {
|
|
355
|
+
catch(exception: BuenoError, context: Context): Response {
|
|
356
|
+
return context.status(exception.statusCode).json({
|
|
357
|
+
error: exception.name,
|
|
358
|
+
message: exception.message,
|
|
359
|
+
code: exception.code,
|
|
360
|
+
statusCode: exception.statusCode,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* ValidationFilter - Handles ValidationError exceptions
|
|
367
|
+
* Returns validation error details with issues list
|
|
368
|
+
*/
|
|
369
|
+
@Catch(ValidationError)
|
|
370
|
+
export class ValidationFilter implements ExceptionFilter<ValidationError> {
|
|
371
|
+
catch(exception: ValidationError, context: Context): Response {
|
|
372
|
+
return context.status(422).json({
|
|
373
|
+
error: "Validation Failed",
|
|
374
|
+
message: exception.message,
|
|
375
|
+
code: "VALIDATION_ERROR",
|
|
376
|
+
issues: exception.issues,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* NotFoundFilter - Handles NotFoundError exceptions
|
|
383
|
+
* Returns 404 response with resource not found message
|
|
384
|
+
*/
|
|
385
|
+
@Catch(NotFoundError)
|
|
386
|
+
export class NotFoundFilter implements ExceptionFilter<NotFoundError> {
|
|
387
|
+
catch(exception: NotFoundError, context: Context): Response {
|
|
388
|
+
return context.status(404).json({
|
|
389
|
+
error: "Not Found",
|
|
390
|
+
message: exception.message,
|
|
391
|
+
code: "NOT_FOUND",
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* AllExceptionsFilter - Catch-all filter for any Error
|
|
398
|
+
* This filter handles all unhandled exceptions
|
|
399
|
+
*/
|
|
400
|
+
export class AllExceptionsFilter implements ExceptionFilter<Error> {
|
|
401
|
+
catch(exception: Error, context: Context): Response {
|
|
402
|
+
// Log the error for debugging
|
|
403
|
+
console.error("Unhandled exception:", exception);
|
|
404
|
+
|
|
405
|
+
// Check if it's a known error type
|
|
406
|
+
if (exception instanceof BuenoError) {
|
|
407
|
+
return context.status(exception.statusCode).json({
|
|
408
|
+
error: exception.name,
|
|
409
|
+
message: exception.message,
|
|
410
|
+
code: exception.code,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Generic error response
|
|
415
|
+
return context.status(500).json({
|
|
416
|
+
error: "Internal Server Error",
|
|
417
|
+
message: exception.message || "An unexpected error occurred",
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// ============= Helper Response Functions =============
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Create a default error response when no filter matches
|
|
426
|
+
*/
|
|
427
|
+
export function createDefaultErrorResponse(
|
|
428
|
+
exception: Error,
|
|
429
|
+
context: Context,
|
|
430
|
+
): Response {
|
|
431
|
+
// Log the unhandled exception
|
|
432
|
+
console.error("Unhandled exception (no filter matched):", exception);
|
|
433
|
+
|
|
434
|
+
return context.status(500).json({
|
|
435
|
+
error: "Internal Server Error",
|
|
436
|
+
message: exception.message || "An unexpected error occurred",
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Create an internal error response
|
|
442
|
+
*/
|
|
443
|
+
export function createInternalErrorResponse(exception: Error): Response {
|
|
444
|
+
console.error("Internal server error:", exception);
|
|
445
|
+
|
|
446
|
+
return new Response(
|
|
447
|
+
JSON.stringify({
|
|
448
|
+
error: "Internal Server Error",
|
|
449
|
+
message: "An unexpected error occurred",
|
|
450
|
+
}),
|
|
451
|
+
{
|
|
452
|
+
status: 500,
|
|
453
|
+
headers: {
|
|
454
|
+
"Content-Type": "application/json",
|
|
455
|
+
},
|
|
456
|
+
},
|
|
457
|
+
);
|
|
458
|
+
}
|