@onebun/core 0.2.6 → 0.2.8
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/package.json +6 -6
- package/src/application/application.test.ts +350 -7
- package/src/application/application.ts +537 -254
- package/src/application/multi-service-application.test.ts +15 -0
- package/src/application/multi-service-application.ts +2 -0
- package/src/application/multi-service.types.ts +7 -1
- package/src/decorators/decorators.ts +213 -0
- package/src/docs-examples.test.ts +386 -3
- package/src/exception-filters/exception-filters.test.ts +172 -0
- package/src/exception-filters/exception-filters.ts +129 -0
- package/src/exception-filters/http-exception.ts +22 -0
- package/src/exception-filters/index.ts +2 -0
- package/src/file/onebun-file.ts +8 -2
- package/src/http-guards/http-guards.test.ts +230 -0
- package/src/http-guards/http-guards.ts +173 -0
- package/src/http-guards/index.ts +1 -0
- package/src/index.ts +10 -0
- package/src/module/module.test.ts +78 -0
- package/src/module/module.ts +55 -7
- package/src/queue/docs-examples.test.ts +72 -12
- package/src/queue/index.ts +4 -0
- package/src/queue/queue-service-proxy.test.ts +82 -0
- package/src/queue/queue-service-proxy.ts +114 -0
- package/src/queue/types.ts +2 -2
- package/src/security/cors-middleware.ts +212 -0
- package/src/security/index.ts +19 -0
- package/src/security/rate-limit-middleware.ts +276 -0
- package/src/security/security-headers-middleware.ts +188 -0
- package/src/security/security.test.ts +285 -0
- package/src/testing/index.ts +1 -0
- package/src/testing/testing-module.test.ts +199 -0
- package/src/testing/testing-module.ts +252 -0
- package/src/types.ts +153 -3
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TestingModule — lightweight harness for testing OneBun controllers and services
|
|
3
|
+
* without the full application startup overhead.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* ```typescript
|
|
7
|
+
* const module = await TestingModule
|
|
8
|
+
* .create({ controllers: [UserController], providers: [UserService] })
|
|
9
|
+
* .overrideProvider(UserService).useValue(mockUserService)
|
|
10
|
+
* .compile();
|
|
11
|
+
*
|
|
12
|
+
* const response = await module.inject('GET', '/users/1');
|
|
13
|
+
* expect(response.ok).toBe(true);
|
|
14
|
+
*
|
|
15
|
+
* await module.close();
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { OneBunApplication } from '../application/application';
|
|
20
|
+
import type { HttpMethod, OneBunResponse } from '../types';
|
|
21
|
+
import type { Context } from 'effect';
|
|
22
|
+
|
|
23
|
+
import { Module } from '../decorators/decorators';
|
|
24
|
+
import { getServiceTag } from '../module/service';
|
|
25
|
+
|
|
26
|
+
import { makeMockLoggerLayer } from './test-utils';
|
|
27
|
+
|
|
28
|
+
// Re-export for convenience
|
|
29
|
+
export { makeMockLoggerLayer };
|
|
30
|
+
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// Types
|
|
33
|
+
// ============================================================================
|
|
34
|
+
|
|
35
|
+
interface TestingModuleCreateOptions {
|
|
36
|
+
/** Module classes to import (already decorated with @Module) */
|
|
37
|
+
imports?: Function[];
|
|
38
|
+
/** Controller classes to include */
|
|
39
|
+
controllers?: Function[];
|
|
40
|
+
/** Service/provider classes to include */
|
|
41
|
+
providers?: Function[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface InjectOptions {
|
|
45
|
+
/** Request body (will be JSON-serialised) */
|
|
46
|
+
body?: unknown;
|
|
47
|
+
/** Extra headers */
|
|
48
|
+
headers?: Record<string, string>;
|
|
49
|
+
/** Query parameters */
|
|
50
|
+
query?: Record<string, string>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type OverrideBuilder = {
|
|
54
|
+
/** Replace the service with a plain value / mock instance */
|
|
55
|
+
useValue(val: unknown): TestingModule;
|
|
56
|
+
/** Replace the service with an instance of the given class */
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
|
+
useClass(cls: new (...args: any[]) => unknown): TestingModule;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// CompiledTestingModule
|
|
63
|
+
// ============================================================================
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Result of `TestingModule.compile()`.
|
|
67
|
+
* Provides `inject()` for HTTP calls and `get()` for service access.
|
|
68
|
+
* Call `close()` when done to release the underlying server.
|
|
69
|
+
*/
|
|
70
|
+
export class CompiledTestingModule {
|
|
71
|
+
constructor(
|
|
72
|
+
private readonly app: OneBunApplication,
|
|
73
|
+
private readonly port: number,
|
|
74
|
+
) {}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get a service instance by its class constructor.
|
|
78
|
+
* Useful for asserting service state after a request.
|
|
79
|
+
*
|
|
80
|
+
* @param serviceClass - The `@Service()`-decorated class
|
|
81
|
+
* @returns The service instance registered in the module
|
|
82
|
+
* @throws If the service is not found
|
|
83
|
+
*/
|
|
84
|
+
get<T>(serviceClass: new (...args: unknown[]) => T): T {
|
|
85
|
+
return this.app.getService(serviceClass) as T;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Send a fake HTTP request to the testing application.
|
|
90
|
+
* No real network call is made — the request goes through the full
|
|
91
|
+
* middleware → guards → filters → handler pipeline.
|
|
92
|
+
*
|
|
93
|
+
* @param method - HTTP method (GET, POST, PUT, DELETE, etc.)
|
|
94
|
+
* @param path - URL path (e.g. '/users/1')
|
|
95
|
+
* @param options - Optional body, headers, query params
|
|
96
|
+
*/
|
|
97
|
+
async inject(
|
|
98
|
+
method: HttpMethod | string,
|
|
99
|
+
path: string,
|
|
100
|
+
options?: InjectOptions,
|
|
101
|
+
): Promise<OneBunResponse> {
|
|
102
|
+
let url = `http://localhost:${this.port}${path}`;
|
|
103
|
+
|
|
104
|
+
if (options?.query && Object.keys(options.query).length > 0) {
|
|
105
|
+
const qs = new URLSearchParams(options.query).toString();
|
|
106
|
+
url = `${url}?${qs}`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const headers = new Headers(options?.headers);
|
|
110
|
+
if (!headers.has('content-type')) {
|
|
111
|
+
headers.set('content-type', 'application/json');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Use the native undici fetch to bypass any global `fetch` mock that may be set by
|
|
115
|
+
// other concurrent test files. This ensures real network calls reach the test server.
|
|
116
|
+
|
|
117
|
+
const nativeFetch = (require('undici') as { fetch: typeof fetch }).fetch ?? globalThis.fetch;
|
|
118
|
+
|
|
119
|
+
return await nativeFetch(url, {
|
|
120
|
+
method,
|
|
121
|
+
headers,
|
|
122
|
+
body: options?.body !== undefined ? JSON.stringify(options.body) : undefined,
|
|
123
|
+
}) as OneBunResponse;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Stop the test server and release resources.
|
|
128
|
+
* Call this in `afterEach` / `afterAll` to prevent port leaks.
|
|
129
|
+
*/
|
|
130
|
+
async close(): Promise<void> {
|
|
131
|
+
await this.app.stop?.({ closeSharedRedis: false });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ============================================================================
|
|
136
|
+
// TestingModule
|
|
137
|
+
// ============================================================================
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Fluent builder for creating isolated test environments for OneBun modules.
|
|
141
|
+
*
|
|
142
|
+
* @example Basic usage
|
|
143
|
+
* ```typescript
|
|
144
|
+
* describe('UserController', () => {
|
|
145
|
+
* let module: CompiledTestingModule;
|
|
146
|
+
*
|
|
147
|
+
* beforeEach(async () => {
|
|
148
|
+
* module = await TestingModule
|
|
149
|
+
* .create({ controllers: [UserController], providers: [UserService] })
|
|
150
|
+
* .compile();
|
|
151
|
+
* });
|
|
152
|
+
*
|
|
153
|
+
* afterEach(() => module.close());
|
|
154
|
+
*
|
|
155
|
+
* it('returns 200 for GET /users', async () => {
|
|
156
|
+
* const res = await module.inject('GET', '/users');
|
|
157
|
+
* expect(res.ok).toBe(true);
|
|
158
|
+
* });
|
|
159
|
+
* });
|
|
160
|
+
* ```
|
|
161
|
+
*
|
|
162
|
+
* @example With provider overrides
|
|
163
|
+
* ```typescript
|
|
164
|
+
* const mockService = { getUser: () => ({ id: 1, name: 'Test' }) };
|
|
165
|
+
*
|
|
166
|
+
* module = await TestingModule
|
|
167
|
+
* .create({ controllers: [UserController], providers: [UserService] })
|
|
168
|
+
* .overrideProvider(UserService).useValue(mockService)
|
|
169
|
+
* .compile();
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
export class TestingModule {
|
|
173
|
+
private readonly options: TestingModuleCreateOptions;
|
|
174
|
+
private readonly overrides: Array<{
|
|
175
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
176
|
+
tag: Context.Tag<any, any>;
|
|
177
|
+
value: unknown;
|
|
178
|
+
}> = [];
|
|
179
|
+
|
|
180
|
+
private constructor(options: TestingModuleCreateOptions) {
|
|
181
|
+
this.options = options;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Create a new TestingModule builder.
|
|
186
|
+
*
|
|
187
|
+
* @param options - Module options (controllers, providers, imports)
|
|
188
|
+
*/
|
|
189
|
+
static create(options: TestingModuleCreateOptions): TestingModule {
|
|
190
|
+
return new TestingModule(options);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Override a provider with a mock value or class.
|
|
195
|
+
* Overrides are applied before `setup()` so controllers receive mocks at construction time.
|
|
196
|
+
*
|
|
197
|
+
* @param serviceClass - The `@Service()`-decorated class to override
|
|
198
|
+
*/
|
|
199
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
200
|
+
overrideProvider<T>(serviceClass: new (...args: any[]) => T): OverrideBuilder {
|
|
201
|
+
return {
|
|
202
|
+
useValue: (val: unknown): TestingModule => {
|
|
203
|
+
this.overrides.push({ tag: getServiceTag(serviceClass), value: val });
|
|
204
|
+
|
|
205
|
+
return this;
|
|
206
|
+
},
|
|
207
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
208
|
+
useClass: (cls: new (...args: any[]) => unknown): TestingModule => {
|
|
209
|
+
this.overrides.push({ tag: getServiceTag(serviceClass), value: new cls() });
|
|
210
|
+
|
|
211
|
+
return this;
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Compile the testing module and start the test server.
|
|
218
|
+
* Returns a `CompiledTestingModule` with `inject()` and `get()` methods.
|
|
219
|
+
*
|
|
220
|
+
* @returns Compiled module ready for testing
|
|
221
|
+
*/
|
|
222
|
+
async compile(): Promise<CompiledTestingModule> {
|
|
223
|
+
// Lazily import to avoid circular dependencies at module parse time
|
|
224
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
225
|
+
const { OneBunApplication } = await import('../application/application');
|
|
226
|
+
|
|
227
|
+
// Build a synthetic module class from the provided options
|
|
228
|
+
const { controllers = [], providers = [], imports = [] } = this.options;
|
|
229
|
+
|
|
230
|
+
class _TestingAppModule {}
|
|
231
|
+
Module({
|
|
232
|
+
controllers,
|
|
233
|
+
providers: providers as unknown[],
|
|
234
|
+
imports,
|
|
235
|
+
})(_TestingAppModule);
|
|
236
|
+
|
|
237
|
+
// Create the application with:
|
|
238
|
+
// - port 0 → OS picks a free port
|
|
239
|
+
// - silent logger
|
|
240
|
+
// - test provider overrides injected before setup()
|
|
241
|
+
const app = new OneBunApplication(_TestingAppModule, {
|
|
242
|
+
port: 0,
|
|
243
|
+
loggerLayer: makeMockLoggerLayer() as import('effect').Layer.Layer<import('@onebun/logger').Logger>,
|
|
244
|
+
gracefulShutdown: false,
|
|
245
|
+
_testProviders: this.overrides,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
await app.start();
|
|
249
|
+
|
|
250
|
+
return new CompiledTestingModule(app, app.getPort());
|
|
251
|
+
}
|
|
252
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import type { BaseMiddleware } from './module/middleware';
|
|
2
|
+
import type { QueueAdapterConstructor } from './queue/types';
|
|
2
3
|
import type { Type } from 'arktype';
|
|
3
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
Context,
|
|
6
|
+
Effect,
|
|
7
|
+
Layer,
|
|
8
|
+
} from 'effect';
|
|
4
9
|
|
|
5
10
|
import type { Logger, LoggerOptions } from '@onebun/logger';
|
|
6
11
|
|
|
12
|
+
|
|
7
13
|
/**
|
|
8
14
|
* HTTP Request type used in OneBun controllers.
|
|
9
15
|
* Extends standard Web API Request with:
|
|
@@ -175,6 +181,11 @@ export interface ModuleInstance {
|
|
|
175
181
|
* using this module's DI scope (services + logger + config).
|
|
176
182
|
*/
|
|
177
183
|
resolveMiddleware?(classes: Function[]): Function[];
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Register a service instance by tag (e.g. before setup() for application-provided services like QueueService proxy).
|
|
187
|
+
*/
|
|
188
|
+
registerService?<T>(tag: Context.Tag<unknown, T>, instance: T): void;
|
|
178
189
|
}
|
|
179
190
|
|
|
180
191
|
/**
|
|
@@ -422,6 +433,12 @@ export interface ApplicationOptions {
|
|
|
422
433
|
*/
|
|
423
434
|
queue?: QueueApplicationOptions;
|
|
424
435
|
|
|
436
|
+
/**
|
|
437
|
+
* Static file serving: serve files from a directory for requests not matched by API routes.
|
|
438
|
+
* Optional path prefix and fallback file (e.g. index.html) for SPA-style routing.
|
|
439
|
+
*/
|
|
440
|
+
static?: StaticApplicationOptions;
|
|
441
|
+
|
|
425
442
|
/**
|
|
426
443
|
* Documentation configuration (OpenAPI/Swagger)
|
|
427
444
|
*/
|
|
@@ -454,6 +471,71 @@ export interface ApplicationOptions {
|
|
|
454
471
|
* @defaultValue true
|
|
455
472
|
*/
|
|
456
473
|
gracefulShutdown?: boolean;
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Global exception filters applied to all routes.
|
|
477
|
+
* Route-level and controller-level filters take priority over global ones.
|
|
478
|
+
* If no filters match, the built-in default filter is used.
|
|
479
|
+
*
|
|
480
|
+
* @example
|
|
481
|
+
* ```typescript
|
|
482
|
+
* const app = new OneBunApplication(AppModule, {
|
|
483
|
+
* filters: [new GlobalExceptionFilter()],
|
|
484
|
+
* });
|
|
485
|
+
* ```
|
|
486
|
+
*/
|
|
487
|
+
filters?: import('./exception-filters/exception-filters').ExceptionFilter[];
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* CORS configuration. When provided, `CorsMiddleware` is automatically prepended
|
|
491
|
+
* to the global middleware chain.
|
|
492
|
+
*
|
|
493
|
+
* @example
|
|
494
|
+
* ```typescript
|
|
495
|
+
* const app = new OneBunApplication(AppModule, {
|
|
496
|
+
* cors: { origin: 'https://my-frontend.example.com', credentials: true },
|
|
497
|
+
* });
|
|
498
|
+
* ```
|
|
499
|
+
*/
|
|
500
|
+
cors?: import('./security/cors-middleware').CorsOptions | true;
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Rate limiting configuration. When provided, `RateLimitMiddleware` is automatically
|
|
504
|
+
* prepended to the global middleware chain (after CORS, before other middleware).
|
|
505
|
+
*
|
|
506
|
+
* @example
|
|
507
|
+
* ```typescript
|
|
508
|
+
* const app = new OneBunApplication(AppModule, {
|
|
509
|
+
* rateLimit: { windowMs: 60_000, max: 100 },
|
|
510
|
+
* });
|
|
511
|
+
* ```
|
|
512
|
+
*/
|
|
513
|
+
rateLimit?: import('./security/rate-limit-middleware').RateLimitOptions | true;
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Security headers configuration. When provided, `SecurityHeadersMiddleware` is
|
|
517
|
+
* automatically appended to the global middleware chain.
|
|
518
|
+
* Pass `true` to use all defaults (equivalent to no options).
|
|
519
|
+
*
|
|
520
|
+
* @example
|
|
521
|
+
* ```typescript
|
|
522
|
+
* const app = new OneBunApplication(AppModule, {
|
|
523
|
+
* security: { strictTransportSecurity: false }, // disable HSTS for local dev
|
|
524
|
+
* });
|
|
525
|
+
* ```
|
|
526
|
+
*/
|
|
527
|
+
security?: import('./security/security-headers-middleware').SecurityHeadersOptions | true;
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Pre-register service instances for testing.
|
|
531
|
+
* These are injected before `setup()` so controllers receive mocks at construction time.
|
|
532
|
+
* @internal Use `TestingModule` instead of setting this directly.
|
|
533
|
+
*/
|
|
534
|
+
_testProviders?: Array<{
|
|
535
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
536
|
+
tag: Context.Tag<any, any>;
|
|
537
|
+
value: unknown;
|
|
538
|
+
}>;
|
|
457
539
|
}
|
|
458
540
|
|
|
459
541
|
/**
|
|
@@ -467,8 +549,10 @@ export type QueueAdapterType = 'memory' | 'redis';
|
|
|
467
549
|
export interface QueueApplicationOptions {
|
|
468
550
|
/** Enable/disable queue (default: auto - enabled if handlers exist) */
|
|
469
551
|
enabled?: boolean;
|
|
470
|
-
/** Adapter type or custom adapter
|
|
471
|
-
adapter?: QueueAdapterType;
|
|
552
|
+
/** Adapter type, or custom adapter constructor (e.g. for NATS JetStream) */
|
|
553
|
+
adapter?: QueueAdapterType | QueueAdapterConstructor;
|
|
554
|
+
/** Options passed to the custom adapter constructor when adapter is a class */
|
|
555
|
+
options?: unknown;
|
|
472
556
|
/** Redis-specific options (only used when adapter is 'redis') */
|
|
473
557
|
redis?: {
|
|
474
558
|
/** Use shared Redis provider instead of dedicated connection */
|
|
@@ -528,6 +612,39 @@ export interface WebSocketApplicationOptions {
|
|
|
528
612
|
maxPayload?: number;
|
|
529
613
|
}
|
|
530
614
|
|
|
615
|
+
/**
|
|
616
|
+
* Static file serving configuration for OneBunApplication.
|
|
617
|
+
* Serves files from a directory for requests not matched by API/ws/docs/metrics routes.
|
|
618
|
+
*/
|
|
619
|
+
export interface StaticApplicationOptions {
|
|
620
|
+
/**
|
|
621
|
+
* Filesystem path to the directory to serve (static root).
|
|
622
|
+
* Can be absolute or relative to the process cwd.
|
|
623
|
+
*/
|
|
624
|
+
root: string;
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* URL path prefix under which static files are served.
|
|
628
|
+
* Empty or '/' means "everything not matched by API routes".
|
|
629
|
+
* Example: '/app' — only paths starting with /app are served from static root
|
|
630
|
+
* (the prefix is stripped when resolving the file path).
|
|
631
|
+
*/
|
|
632
|
+
pathPrefix?: string;
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Fallback file name (e.g. 'index.html') for SPA-style client-side routing.
|
|
636
|
+
* When the requested file is not found, this file under static root is returned instead.
|
|
637
|
+
*/
|
|
638
|
+
fallbackFile?: string;
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* TTL in milliseconds for caching file existence checks.
|
|
642
|
+
* Reduces disk I/O for repeated requests. Use 0 to disable caching.
|
|
643
|
+
* @defaultValue 60000
|
|
644
|
+
*/
|
|
645
|
+
fileExistenceCacheTtlMs?: number;
|
|
646
|
+
}
|
|
647
|
+
|
|
531
648
|
/**
|
|
532
649
|
* Documentation configuration for OneBunApplication
|
|
533
650
|
* Enables automatic OpenAPI spec generation and Swagger UI
|
|
@@ -712,6 +829,35 @@ export interface RouteOptions {
|
|
|
712
829
|
timeout?: number;
|
|
713
830
|
}
|
|
714
831
|
|
|
832
|
+
/**
|
|
833
|
+
* HTTP Execution Context provided to HTTP guards.
|
|
834
|
+
* Gives read-only access to the incoming request and routing information.
|
|
835
|
+
*/
|
|
836
|
+
export interface HttpExecutionContext {
|
|
837
|
+
/** Returns the incoming request object */
|
|
838
|
+
getRequest(): OneBunRequest;
|
|
839
|
+
/** Returns the handler method name (e.g. 'getUser') */
|
|
840
|
+
getHandler(): string;
|
|
841
|
+
/** Returns the controller class name (e.g. 'UserController') */
|
|
842
|
+
getController(): string;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* HTTP Guard interface — implement to protect routes via `@UseGuards()`.
|
|
847
|
+
*
|
|
848
|
+
* @example
|
|
849
|
+
* ```typescript
|
|
850
|
+
* class MyGuard implements HttpGuard {
|
|
851
|
+
* canActivate(ctx: HttpExecutionContext): boolean {
|
|
852
|
+
* return ctx.getRequest().headers.get('x-api-key') === 'secret';
|
|
853
|
+
* }
|
|
854
|
+
* }
|
|
855
|
+
* ```
|
|
856
|
+
*/
|
|
857
|
+
export interface HttpGuard {
|
|
858
|
+
canActivate(context: HttpExecutionContext): boolean | Promise<boolean>;
|
|
859
|
+
}
|
|
860
|
+
|
|
715
861
|
/**
|
|
716
862
|
* Route metadata
|
|
717
863
|
*/
|
|
@@ -721,6 +867,10 @@ export interface RouteMetadata {
|
|
|
721
867
|
handler: string;
|
|
722
868
|
params?: ParamMetadata[];
|
|
723
869
|
middleware?: Function[];
|
|
870
|
+
/** Guards to execute before the route handler. Supports class constructors and instances. */
|
|
871
|
+
guards?: (Function | HttpGuard)[];
|
|
872
|
+
/** Exception filters to apply when the route handler throws. */
|
|
873
|
+
filters?: import('./exception-filters/exception-filters').ExceptionFilter[];
|
|
724
874
|
/**
|
|
725
875
|
* Response schemas for validation
|
|
726
876
|
* Key is HTTP status code (e.g., 200, 201, 404)
|