@cosmneo/onion-lasagna 0.1.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/dist/backend/core/global.cjs +283 -0
- package/dist/backend/core/global.cjs.map +1 -0
- package/dist/backend/core/global.d.cts +294 -0
- package/dist/backend/core/global.d.ts +294 -0
- package/dist/backend/core/global.js +39 -0
- package/dist/backend/core/global.js.map +1 -0
- package/dist/backend/core/onion-layers.cjs +2302 -0
- package/dist/backend/core/onion-layers.cjs.map +1 -0
- package/dist/backend/core/onion-layers.d.cts +1675 -0
- package/dist/backend/core/onion-layers.d.ts +1675 -0
- package/dist/backend/core/onion-layers.js +1158 -0
- package/dist/backend/core/onion-layers.js.map +1 -0
- package/dist/backend/core/presentation.cjs +573 -0
- package/dist/backend/core/presentation.cjs.map +1 -0
- package/dist/backend/core/presentation.d.cts +5 -0
- package/dist/backend/core/presentation.d.ts +5 -0
- package/dist/backend/core/presentation.js +28 -0
- package/dist/backend/core/presentation.js.map +1 -0
- package/dist/backend/core/validators/arktype.cjs +947 -0
- package/dist/backend/core/validators/arktype.cjs.map +1 -0
- package/dist/backend/core/validators/arktype.d.cts +188 -0
- package/dist/backend/core/validators/arktype.d.ts +188 -0
- package/dist/backend/core/validators/arktype.js +287 -0
- package/dist/backend/core/validators/arktype.js.map +1 -0
- package/dist/backend/core/validators/typebox.cjs +939 -0
- package/dist/backend/core/validators/typebox.cjs.map +1 -0
- package/dist/backend/core/validators/typebox.d.cts +189 -0
- package/dist/backend/core/validators/typebox.d.ts +189 -0
- package/dist/backend/core/validators/typebox.js +281 -0
- package/dist/backend/core/validators/typebox.js.map +1 -0
- package/dist/backend/core/validators/valibot.cjs +942 -0
- package/dist/backend/core/validators/valibot.cjs.map +1 -0
- package/dist/backend/core/validators/valibot.d.cts +160 -0
- package/dist/backend/core/validators/valibot.d.ts +160 -0
- package/dist/backend/core/validators/valibot.js +294 -0
- package/dist/backend/core/validators/valibot.js.map +1 -0
- package/dist/backend/core/validators/zod.cjs +934 -0
- package/dist/backend/core/validators/zod.cjs.map +1 -0
- package/dist/backend/core/validators/zod.d.cts +188 -0
- package/dist/backend/core/validators/zod.d.ts +188 -0
- package/dist/backend/core/validators/zod.js +278 -0
- package/dist/backend/core/validators/zod.js.map +1 -0
- package/dist/backend/frameworks/elysia.cjs +715 -0
- package/dist/backend/frameworks/elysia.cjs.map +1 -0
- package/dist/backend/frameworks/elysia.d.cts +208 -0
- package/dist/backend/frameworks/elysia.d.ts +208 -0
- package/dist/backend/frameworks/elysia.js +251 -0
- package/dist/backend/frameworks/elysia.js.map +1 -0
- package/dist/backend/frameworks/fastify.cjs +677 -0
- package/dist/backend/frameworks/fastify.cjs.map +1 -0
- package/dist/backend/frameworks/fastify.d.cts +201 -0
- package/dist/backend/frameworks/fastify.d.ts +201 -0
- package/dist/backend/frameworks/fastify.js +213 -0
- package/dist/backend/frameworks/fastify.js.map +1 -0
- package/dist/backend/frameworks/hono.cjs +715 -0
- package/dist/backend/frameworks/hono.cjs.map +1 -0
- package/dist/backend/frameworks/hono.d.cts +163 -0
- package/dist/backend/frameworks/hono.d.ts +163 -0
- package/dist/backend/frameworks/hono.js +249 -0
- package/dist/backend/frameworks/hono.js.map +1 -0
- package/dist/backend/frameworks/nestjs.cjs +260 -0
- package/dist/backend/frameworks/nestjs.cjs.map +1 -0
- package/dist/backend/frameworks/nestjs.d.cts +168 -0
- package/dist/backend/frameworks/nestjs.d.ts +168 -0
- package/dist/backend/frameworks/nestjs.js +193 -0
- package/dist/backend/frameworks/nestjs.js.map +1 -0
- package/dist/base-dto.class-D7W9iqoU.d.cts +146 -0
- package/dist/base-dto.class-D7W9iqoU.d.ts +146 -0
- package/dist/base-uuid-v7.vo-BPGEIWLM.d.ts +799 -0
- package/dist/base-uuid-v7.vo-BjqKX44G.d.cts +799 -0
- package/dist/chunk-74IKUOSE.js +116 -0
- package/dist/chunk-74IKUOSE.js.map +1 -0
- package/dist/chunk-BKZOLGQW.js +29 -0
- package/dist/chunk-BKZOLGQW.js.map +1 -0
- package/dist/chunk-CGZBV6BD.js +54 -0
- package/dist/chunk-CGZBV6BD.js.map +1 -0
- package/dist/chunk-DDAHJZVK.js +258 -0
- package/dist/chunk-DDAHJZVK.js.map +1 -0
- package/dist/chunk-MQD5GXMT.js +171 -0
- package/dist/chunk-MQD5GXMT.js.map +1 -0
- package/dist/chunk-OKFXZHBC.js +43 -0
- package/dist/chunk-OKFXZHBC.js.map +1 -0
- package/dist/chunk-RLLWYFPI.js +168 -0
- package/dist/chunk-RLLWYFPI.js.map +1 -0
- package/dist/chunk-VCHFXT5W.js +425 -0
- package/dist/chunk-VCHFXT5W.js.map +1 -0
- package/dist/chunk-ZWLYNGO3.js +40 -0
- package/dist/chunk-ZWLYNGO3.js.map +1 -0
- package/dist/http-response-BAhi8lF4.d.cts +124 -0
- package/dist/http-response-BAhi8lF4.d.ts +124 -0
- package/dist/index-DingXh7B.d.cts +1187 -0
- package/dist/index-tOH7XBa3.d.ts +1187 -0
- package/dist/routing.type-DB4pt-d9.d.ts +184 -0
- package/dist/routing.type-DF2BIL7x.d.cts +184 -0
- package/dist/validation-error.type-kD4_qNZ9.d.cts +199 -0
- package/dist/validation-error.type-kD4_qNZ9.d.ts +199 -0
- package/package.json +191 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { ExceptionFilter, ArgumentsHost, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
export { C as HttpController, H as HttpRequest, a as HttpResponse } from '../../http-response-BAhi8lF4.cjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Base controller for NestJS that auto-wires onion-lasagna integration.
|
|
7
|
+
*
|
|
8
|
+
* Extending this class automatically applies:
|
|
9
|
+
* - {@link OnionLasagnaExceptionFilter} - Maps domain errors to HTTP responses
|
|
10
|
+
* - {@link OnionLasagnaResponseInterceptor} - Transforms HttpResponse to NestJS response
|
|
11
|
+
*
|
|
12
|
+
* This reduces boilerplate when creating controllers that work with
|
|
13
|
+
* onion-lasagna's architecture.
|
|
14
|
+
*
|
|
15
|
+
* @example Basic usage
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { Controller, Get, Post } from '@nestjs/common';
|
|
18
|
+
* import { BaseNestController, OnionLasagnaRequest } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';
|
|
19
|
+
* import type { HttpRequest } from '@cosmneo/onion-lasagna/backend/core/presentation';
|
|
20
|
+
*
|
|
21
|
+
* @Controller('users')
|
|
22
|
+
* export class UsersController extends BaseNestController {
|
|
23
|
+
* constructor(
|
|
24
|
+
* private readonly getUserController: GetUserController,
|
|
25
|
+
* private readonly createUserController: CreateUserController,
|
|
26
|
+
* ) {
|
|
27
|
+
* super();
|
|
28
|
+
* }
|
|
29
|
+
*
|
|
30
|
+
* @Get(':id')
|
|
31
|
+
* getUser(@OnionLasagnaRequest() request: HttpRequest) {
|
|
32
|
+
* return this.getUserController.execute(request);
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* @Post()
|
|
36
|
+
* createUser(@OnionLasagnaRequest() request: HttpRequest) {
|
|
37
|
+
* return this.createUserController.execute(request);
|
|
38
|
+
* }
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @example With NestJS guards
|
|
43
|
+
* ```typescript
|
|
44
|
+
* @Controller('admin')
|
|
45
|
+
* @UseGuards(JwtAuthGuard, RolesGuard)
|
|
46
|
+
* export class AdminController extends BaseNestController {
|
|
47
|
+
* @Get('dashboard')
|
|
48
|
+
* @Roles('admin')
|
|
49
|
+
* getDashboard(@OnionLasagnaRequest() request: HttpRequest) {
|
|
50
|
+
* return this.dashboardController.execute(request);
|
|
51
|
+
* }
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
declare abstract class BaseNestController {
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Parameter decorator that extracts the request from NestJS context
|
|
60
|
+
* and transforms it into onion-lasagna's `HttpRequest` format.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* import { Controller, Get } from '@nestjs/common';
|
|
65
|
+
* import { OnionLasagnaRequest } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';
|
|
66
|
+
* import type { HttpRequest } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';
|
|
67
|
+
*
|
|
68
|
+
* @Controller('users')
|
|
69
|
+
* export class UsersController {
|
|
70
|
+
* @Get(':id')
|
|
71
|
+
* getUser(@OnionLasagnaRequest() request: HttpRequest) {
|
|
72
|
+
* return getUserController.execute(request);
|
|
73
|
+
* }
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
declare const OnionLasagnaRequest: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* NestJS exception filter that maps onion-lasagna errors to HTTP responses.
|
|
81
|
+
*
|
|
82
|
+
* Mapping strategy:
|
|
83
|
+
* - `ObjectValidationError` → 400 Bad Request
|
|
84
|
+
* - `InvalidRequestError` → 400 Bad Request
|
|
85
|
+
* - `AccessDeniedError` → 403 Forbidden
|
|
86
|
+
* - `NotFoundError` → 404 Not Found
|
|
87
|
+
* - `ConflictError` → 409 Conflict
|
|
88
|
+
* - `UnprocessableError` → 422 Unprocessable Entity
|
|
89
|
+
* - `UseCaseError` (other) → 400 Bad Request
|
|
90
|
+
* - `DomainError` → 500 Internal Server Error (masked)
|
|
91
|
+
* - `InfraError` → 500 Internal Server Error (masked)
|
|
92
|
+
* - `ControllerError` → 500 Internal Server Error (masked)
|
|
93
|
+
* - Unknown → 500 Internal Server Error (masked)
|
|
94
|
+
*
|
|
95
|
+
* **Security Note:** Domain and infrastructure errors are masked to avoid
|
|
96
|
+
* leaking internal implementation details.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* import { Controller, UseFilters } from '@nestjs/common';
|
|
101
|
+
* import { OnionLasagnaExceptionFilter } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';
|
|
102
|
+
*
|
|
103
|
+
* @Controller('users')
|
|
104
|
+
* @UseFilters(OnionLasagnaExceptionFilter)
|
|
105
|
+
* export class UsersController {
|
|
106
|
+
* // ...
|
|
107
|
+
* }
|
|
108
|
+
* ```
|
|
109
|
+
*
|
|
110
|
+
* @example Global registration in main.ts
|
|
111
|
+
* ```typescript
|
|
112
|
+
* import { NestFactory } from '@nestjs/core';
|
|
113
|
+
* import { OnionLasagnaExceptionFilter } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';
|
|
114
|
+
*
|
|
115
|
+
* const app = await NestFactory.create(AppModule);
|
|
116
|
+
* app.useGlobalFilters(new OnionLasagnaExceptionFilter());
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
declare class OnionLasagnaExceptionFilter implements ExceptionFilter {
|
|
120
|
+
catch(exception: unknown, host: ArgumentsHost): void;
|
|
121
|
+
private mapError;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* NestJS interceptor that transforms onion-lasagna `HttpResponse` objects
|
|
126
|
+
* into proper NestJS responses.
|
|
127
|
+
*
|
|
128
|
+
* When a controller returns an `HttpResponse` object (with `statusCode`, `body`, and optional `headers`),
|
|
129
|
+
* this interceptor:
|
|
130
|
+
* 1. Sets the HTTP status code from `response.statusCode`
|
|
131
|
+
* 2. Sets any custom headers from `response.headers`
|
|
132
|
+
* 3. Returns only `response.body` to NestJS for serialization
|
|
133
|
+
*
|
|
134
|
+
* If the response is not an `HttpResponse` object, it passes through unchanged.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* import { Controller, Get, UseInterceptors, UseFilters } from '@nestjs/common';
|
|
139
|
+
* import { OnionLasagnaRequest, OnionLasagnaExceptionFilter, OnionLasagnaResponseInterceptor } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';
|
|
140
|
+
* import type { HttpRequest } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';
|
|
141
|
+
*
|
|
142
|
+
* @Controller('users')
|
|
143
|
+
* @UseFilters(OnionLasagnaExceptionFilter)
|
|
144
|
+
* @UseInterceptors(OnionLasagnaResponseInterceptor)
|
|
145
|
+
* export class UsersController {
|
|
146
|
+
* @Get(':id')
|
|
147
|
+
* getUser(@OnionLasagnaRequest() request: HttpRequest) {
|
|
148
|
+
* return getUserController.execute(request);
|
|
149
|
+
* }
|
|
150
|
+
* }
|
|
151
|
+
* ```
|
|
152
|
+
*
|
|
153
|
+
* @example Global registration in main.ts
|
|
154
|
+
* ```typescript
|
|
155
|
+
* import { NestFactory } from '@nestjs/core';
|
|
156
|
+
* import { OnionLasagnaExceptionFilter, OnionLasagnaResponseInterceptor } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';
|
|
157
|
+
*
|
|
158
|
+
* const app = await NestFactory.create(AppModule);
|
|
159
|
+
* app.useGlobalFilters(new OnionLasagnaExceptionFilter());
|
|
160
|
+
* app.useGlobalInterceptors(new OnionLasagnaResponseInterceptor());
|
|
161
|
+
* await app.listen(3000);
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
declare class OnionLasagnaResponseInterceptor implements NestInterceptor {
|
|
165
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export { BaseNestController, OnionLasagnaExceptionFilter, OnionLasagnaRequest, OnionLasagnaResponseInterceptor };
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { ExceptionFilter, ArgumentsHost, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
export { C as HttpController, H as HttpRequest, a as HttpResponse } from '../../http-response-BAhi8lF4.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Base controller for NestJS that auto-wires onion-lasagna integration.
|
|
7
|
+
*
|
|
8
|
+
* Extending this class automatically applies:
|
|
9
|
+
* - {@link OnionLasagnaExceptionFilter} - Maps domain errors to HTTP responses
|
|
10
|
+
* - {@link OnionLasagnaResponseInterceptor} - Transforms HttpResponse to NestJS response
|
|
11
|
+
*
|
|
12
|
+
* This reduces boilerplate when creating controllers that work with
|
|
13
|
+
* onion-lasagna's architecture.
|
|
14
|
+
*
|
|
15
|
+
* @example Basic usage
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { Controller, Get, Post } from '@nestjs/common';
|
|
18
|
+
* import { BaseNestController, OnionLasagnaRequest } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';
|
|
19
|
+
* import type { HttpRequest } from '@cosmneo/onion-lasagna/backend/core/presentation';
|
|
20
|
+
*
|
|
21
|
+
* @Controller('users')
|
|
22
|
+
* export class UsersController extends BaseNestController {
|
|
23
|
+
* constructor(
|
|
24
|
+
* private readonly getUserController: GetUserController,
|
|
25
|
+
* private readonly createUserController: CreateUserController,
|
|
26
|
+
* ) {
|
|
27
|
+
* super();
|
|
28
|
+
* }
|
|
29
|
+
*
|
|
30
|
+
* @Get(':id')
|
|
31
|
+
* getUser(@OnionLasagnaRequest() request: HttpRequest) {
|
|
32
|
+
* return this.getUserController.execute(request);
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* @Post()
|
|
36
|
+
* createUser(@OnionLasagnaRequest() request: HttpRequest) {
|
|
37
|
+
* return this.createUserController.execute(request);
|
|
38
|
+
* }
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @example With NestJS guards
|
|
43
|
+
* ```typescript
|
|
44
|
+
* @Controller('admin')
|
|
45
|
+
* @UseGuards(JwtAuthGuard, RolesGuard)
|
|
46
|
+
* export class AdminController extends BaseNestController {
|
|
47
|
+
* @Get('dashboard')
|
|
48
|
+
* @Roles('admin')
|
|
49
|
+
* getDashboard(@OnionLasagnaRequest() request: HttpRequest) {
|
|
50
|
+
* return this.dashboardController.execute(request);
|
|
51
|
+
* }
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
declare abstract class BaseNestController {
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Parameter decorator that extracts the request from NestJS context
|
|
60
|
+
* and transforms it into onion-lasagna's `HttpRequest` format.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* import { Controller, Get } from '@nestjs/common';
|
|
65
|
+
* import { OnionLasagnaRequest } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';
|
|
66
|
+
* import type { HttpRequest } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';
|
|
67
|
+
*
|
|
68
|
+
* @Controller('users')
|
|
69
|
+
* export class UsersController {
|
|
70
|
+
* @Get(':id')
|
|
71
|
+
* getUser(@OnionLasagnaRequest() request: HttpRequest) {
|
|
72
|
+
* return getUserController.execute(request);
|
|
73
|
+
* }
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
declare const OnionLasagnaRequest: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* NestJS exception filter that maps onion-lasagna errors to HTTP responses.
|
|
81
|
+
*
|
|
82
|
+
* Mapping strategy:
|
|
83
|
+
* - `ObjectValidationError` → 400 Bad Request
|
|
84
|
+
* - `InvalidRequestError` → 400 Bad Request
|
|
85
|
+
* - `AccessDeniedError` → 403 Forbidden
|
|
86
|
+
* - `NotFoundError` → 404 Not Found
|
|
87
|
+
* - `ConflictError` → 409 Conflict
|
|
88
|
+
* - `UnprocessableError` → 422 Unprocessable Entity
|
|
89
|
+
* - `UseCaseError` (other) → 400 Bad Request
|
|
90
|
+
* - `DomainError` → 500 Internal Server Error (masked)
|
|
91
|
+
* - `InfraError` → 500 Internal Server Error (masked)
|
|
92
|
+
* - `ControllerError` → 500 Internal Server Error (masked)
|
|
93
|
+
* - Unknown → 500 Internal Server Error (masked)
|
|
94
|
+
*
|
|
95
|
+
* **Security Note:** Domain and infrastructure errors are masked to avoid
|
|
96
|
+
* leaking internal implementation details.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* import { Controller, UseFilters } from '@nestjs/common';
|
|
101
|
+
* import { OnionLasagnaExceptionFilter } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';
|
|
102
|
+
*
|
|
103
|
+
* @Controller('users')
|
|
104
|
+
* @UseFilters(OnionLasagnaExceptionFilter)
|
|
105
|
+
* export class UsersController {
|
|
106
|
+
* // ...
|
|
107
|
+
* }
|
|
108
|
+
* ```
|
|
109
|
+
*
|
|
110
|
+
* @example Global registration in main.ts
|
|
111
|
+
* ```typescript
|
|
112
|
+
* import { NestFactory } from '@nestjs/core';
|
|
113
|
+
* import { OnionLasagnaExceptionFilter } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';
|
|
114
|
+
*
|
|
115
|
+
* const app = await NestFactory.create(AppModule);
|
|
116
|
+
* app.useGlobalFilters(new OnionLasagnaExceptionFilter());
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
declare class OnionLasagnaExceptionFilter implements ExceptionFilter {
|
|
120
|
+
catch(exception: unknown, host: ArgumentsHost): void;
|
|
121
|
+
private mapError;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* NestJS interceptor that transforms onion-lasagna `HttpResponse` objects
|
|
126
|
+
* into proper NestJS responses.
|
|
127
|
+
*
|
|
128
|
+
* When a controller returns an `HttpResponse` object (with `statusCode`, `body`, and optional `headers`),
|
|
129
|
+
* this interceptor:
|
|
130
|
+
* 1. Sets the HTTP status code from `response.statusCode`
|
|
131
|
+
* 2. Sets any custom headers from `response.headers`
|
|
132
|
+
* 3. Returns only `response.body` to NestJS for serialization
|
|
133
|
+
*
|
|
134
|
+
* If the response is not an `HttpResponse` object, it passes through unchanged.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* import { Controller, Get, UseInterceptors, UseFilters } from '@nestjs/common';
|
|
139
|
+
* import { OnionLasagnaRequest, OnionLasagnaExceptionFilter, OnionLasagnaResponseInterceptor } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';
|
|
140
|
+
* import type { HttpRequest } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';
|
|
141
|
+
*
|
|
142
|
+
* @Controller('users')
|
|
143
|
+
* @UseFilters(OnionLasagnaExceptionFilter)
|
|
144
|
+
* @UseInterceptors(OnionLasagnaResponseInterceptor)
|
|
145
|
+
* export class UsersController {
|
|
146
|
+
* @Get(':id')
|
|
147
|
+
* getUser(@OnionLasagnaRequest() request: HttpRequest) {
|
|
148
|
+
* return getUserController.execute(request);
|
|
149
|
+
* }
|
|
150
|
+
* }
|
|
151
|
+
* ```
|
|
152
|
+
*
|
|
153
|
+
* @example Global registration in main.ts
|
|
154
|
+
* ```typescript
|
|
155
|
+
* import { NestFactory } from '@nestjs/core';
|
|
156
|
+
* import { OnionLasagnaExceptionFilter, OnionLasagnaResponseInterceptor } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';
|
|
157
|
+
*
|
|
158
|
+
* const app = await NestFactory.create(AppModule);
|
|
159
|
+
* app.useGlobalFilters(new OnionLasagnaExceptionFilter());
|
|
160
|
+
* app.useGlobalInterceptors(new OnionLasagnaResponseInterceptor());
|
|
161
|
+
* await app.listen(3000);
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
declare class OnionLasagnaResponseInterceptor implements NestInterceptor {
|
|
165
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export { BaseNestController, OnionLasagnaExceptionFilter, OnionLasagnaRequest, OnionLasagnaResponseInterceptor };
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__decorateElement,
|
|
3
|
+
__decoratorStart,
|
|
4
|
+
__runInitializers
|
|
5
|
+
} from "../../chunk-CGZBV6BD.js";
|
|
6
|
+
|
|
7
|
+
// src/backend/frameworks/nestjs/classes/base-nest-controller.class.ts
|
|
8
|
+
import { UseFilters, UseInterceptors } from "@nestjs/common";
|
|
9
|
+
|
|
10
|
+
// src/backend/frameworks/nestjs/filters/onion-lasagna-exception.filter.ts
|
|
11
|
+
import { Catch, HttpStatus } from "@nestjs/common";
|
|
12
|
+
function isErrorType(error, typeName) {
|
|
13
|
+
if (!error || typeof error !== "object") return false;
|
|
14
|
+
const constructor = error.constructor;
|
|
15
|
+
const name = constructor?.name;
|
|
16
|
+
return name === typeName || name === `_${typeName}`;
|
|
17
|
+
}
|
|
18
|
+
function hasValidationErrors(error) {
|
|
19
|
+
if (!error || typeof error !== "object") return false;
|
|
20
|
+
return "validationErrors" in error && Array.isArray(error.validationErrors);
|
|
21
|
+
}
|
|
22
|
+
var INTERNAL_ERROR_TYPES = [
|
|
23
|
+
"DomainError",
|
|
24
|
+
"InfraError",
|
|
25
|
+
"ControllerError",
|
|
26
|
+
"NetworkError",
|
|
27
|
+
"PersistenceError",
|
|
28
|
+
"ExternalServiceError",
|
|
29
|
+
"InvariantViolationError"
|
|
30
|
+
];
|
|
31
|
+
var _OnionLasagnaExceptionFilter_decorators, _init;
|
|
32
|
+
_OnionLasagnaExceptionFilter_decorators = [Catch()];
|
|
33
|
+
var OnionLasagnaExceptionFilter = class {
|
|
34
|
+
catch(exception, host) {
|
|
35
|
+
const ctx = host.switchToHttp();
|
|
36
|
+
const response = ctx.getResponse();
|
|
37
|
+
const { status, body } = this.mapError(exception);
|
|
38
|
+
response.status(status).json(body);
|
|
39
|
+
}
|
|
40
|
+
mapError(error) {
|
|
41
|
+
if (isErrorType(error, "ObjectValidationError") && hasValidationErrors(error)) {
|
|
42
|
+
return {
|
|
43
|
+
status: HttpStatus.BAD_REQUEST,
|
|
44
|
+
body: {
|
|
45
|
+
message: error.message,
|
|
46
|
+
errorCode: error.code,
|
|
47
|
+
errorItems: error.validationErrors.map((e) => ({
|
|
48
|
+
item: e.field,
|
|
49
|
+
message: e.message
|
|
50
|
+
}))
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (isErrorType(error, "InvalidRequestError") && hasValidationErrors(error)) {
|
|
55
|
+
return {
|
|
56
|
+
status: HttpStatus.BAD_REQUEST,
|
|
57
|
+
body: {
|
|
58
|
+
message: error.message,
|
|
59
|
+
errorCode: error.code,
|
|
60
|
+
errorItems: error.validationErrors.map((e) => ({
|
|
61
|
+
item: e.field,
|
|
62
|
+
message: e.message
|
|
63
|
+
}))
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (isErrorType(error, "AccessDeniedError")) {
|
|
68
|
+
return {
|
|
69
|
+
status: HttpStatus.FORBIDDEN,
|
|
70
|
+
body: { message: error.message, errorCode: error.code }
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (isErrorType(error, "NotFoundError")) {
|
|
74
|
+
return {
|
|
75
|
+
status: HttpStatus.NOT_FOUND,
|
|
76
|
+
body: { message: error.message, errorCode: error.code }
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (isErrorType(error, "ConflictError")) {
|
|
80
|
+
return {
|
|
81
|
+
status: HttpStatus.CONFLICT,
|
|
82
|
+
body: { message: error.message, errorCode: error.code }
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (isErrorType(error, "UnprocessableError")) {
|
|
86
|
+
return {
|
|
87
|
+
status: HttpStatus.UNPROCESSABLE_ENTITY,
|
|
88
|
+
body: { message: error.message, errorCode: error.code }
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (isErrorType(error, "UseCaseError")) {
|
|
92
|
+
return {
|
|
93
|
+
status: HttpStatus.BAD_REQUEST,
|
|
94
|
+
body: { message: error.message, errorCode: error.code }
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
for (const errorType of INTERNAL_ERROR_TYPES) {
|
|
98
|
+
if (isErrorType(error, errorType)) {
|
|
99
|
+
return {
|
|
100
|
+
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
|
101
|
+
body: { message: "An unexpected error occurred", errorCode: "INTERNAL_ERROR" }
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (isErrorType(error, "CodedError")) {
|
|
106
|
+
return {
|
|
107
|
+
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
|
108
|
+
body: { message: "An unexpected error occurred", errorCode: "INTERNAL_ERROR" }
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
|
113
|
+
body: { message: "An unexpected error occurred", errorCode: "INTERNAL_ERROR" }
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
_init = __decoratorStart(null);
|
|
118
|
+
OnionLasagnaExceptionFilter = __decorateElement(_init, 0, "OnionLasagnaExceptionFilter", _OnionLasagnaExceptionFilter_decorators, OnionLasagnaExceptionFilter);
|
|
119
|
+
__runInitializers(_init, 1, OnionLasagnaExceptionFilter);
|
|
120
|
+
|
|
121
|
+
// src/backend/frameworks/nestjs/interceptors/onion-lasagna-response.interceptor.ts
|
|
122
|
+
import {
|
|
123
|
+
Injectable
|
|
124
|
+
} from "@nestjs/common";
|
|
125
|
+
import { map } from "rxjs/operators";
|
|
126
|
+
function isHttpResponse(value) {
|
|
127
|
+
if (!value || typeof value !== "object") return false;
|
|
128
|
+
const obj = value;
|
|
129
|
+
return typeof obj.statusCode === "number" && "body" in obj;
|
|
130
|
+
}
|
|
131
|
+
var _OnionLasagnaResponseInterceptor_decorators, _init2;
|
|
132
|
+
_OnionLasagnaResponseInterceptor_decorators = [Injectable()];
|
|
133
|
+
var OnionLasagnaResponseInterceptor = class {
|
|
134
|
+
intercept(context, next) {
|
|
135
|
+
return next.handle().pipe(
|
|
136
|
+
map((data) => {
|
|
137
|
+
if (!isHttpResponse(data)) {
|
|
138
|
+
return data;
|
|
139
|
+
}
|
|
140
|
+
const res = context.switchToHttp().getResponse();
|
|
141
|
+
res.status(data.statusCode);
|
|
142
|
+
if (data.headers) {
|
|
143
|
+
for (const [key, value] of Object.entries(data.headers)) {
|
|
144
|
+
if (value != null) {
|
|
145
|
+
res.setHeader(key, String(value));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return data.body;
|
|
150
|
+
})
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
_init2 = __decoratorStart(null);
|
|
155
|
+
OnionLasagnaResponseInterceptor = __decorateElement(_init2, 0, "OnionLasagnaResponseInterceptor", _OnionLasagnaResponseInterceptor_decorators, OnionLasagnaResponseInterceptor);
|
|
156
|
+
__runInitializers(_init2, 1, OnionLasagnaResponseInterceptor);
|
|
157
|
+
|
|
158
|
+
// src/backend/frameworks/nestjs/classes/base-nest-controller.class.ts
|
|
159
|
+
var _BaseNestController_decorators, _init3;
|
|
160
|
+
_BaseNestController_decorators = [UseFilters(OnionLasagnaExceptionFilter), UseInterceptors(OnionLasagnaResponseInterceptor)];
|
|
161
|
+
var BaseNestController = class {
|
|
162
|
+
};
|
|
163
|
+
_init3 = __decoratorStart(null);
|
|
164
|
+
BaseNestController = __decorateElement(_init3, 0, "BaseNestController", _BaseNestController_decorators, BaseNestController);
|
|
165
|
+
__runInitializers(_init3, 1, BaseNestController);
|
|
166
|
+
|
|
167
|
+
// src/backend/frameworks/nestjs/decorators/onion-lasagna-request.decorator.ts
|
|
168
|
+
import { createParamDecorator } from "@nestjs/common";
|
|
169
|
+
function normalizeHeaders(headers) {
|
|
170
|
+
const normalized = {};
|
|
171
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
172
|
+
normalized[key.toLowerCase()] = String(value);
|
|
173
|
+
}
|
|
174
|
+
return normalized;
|
|
175
|
+
}
|
|
176
|
+
var OnionLasagnaRequest = createParamDecorator(
|
|
177
|
+
(_data, ctx) => {
|
|
178
|
+
const request = ctx.switchToHttp().getRequest();
|
|
179
|
+
return {
|
|
180
|
+
body: request.body,
|
|
181
|
+
headers: normalizeHeaders(request.headers),
|
|
182
|
+
queryParams: request.query,
|
|
183
|
+
pathParams: request.params
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
export {
|
|
188
|
+
BaseNestController,
|
|
189
|
+
OnionLasagnaExceptionFilter,
|
|
190
|
+
OnionLasagnaRequest,
|
|
191
|
+
OnionLasagnaResponseInterceptor
|
|
192
|
+
};
|
|
193
|
+
//# sourceMappingURL=nestjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/backend/frameworks/nestjs/classes/base-nest-controller.class.ts","../../../src/backend/frameworks/nestjs/filters/onion-lasagna-exception.filter.ts","../../../src/backend/frameworks/nestjs/interceptors/onion-lasagna-response.interceptor.ts","../../../src/backend/frameworks/nestjs/decorators/onion-lasagna-request.decorator.ts"],"sourcesContent":["import { UseFilters, UseInterceptors } from '@nestjs/common';\nimport { OnionLasagnaExceptionFilter } from '../filters/onion-lasagna-exception.filter';\nimport { OnionLasagnaResponseInterceptor } from '../interceptors/onion-lasagna-response.interceptor';\n\n/**\n * Base controller for NestJS that auto-wires onion-lasagna integration.\n *\n * Extending this class automatically applies:\n * - {@link OnionLasagnaExceptionFilter} - Maps domain errors to HTTP responses\n * - {@link OnionLasagnaResponseInterceptor} - Transforms HttpResponse to NestJS response\n *\n * This reduces boilerplate when creating controllers that work with\n * onion-lasagna's architecture.\n *\n * @example Basic usage\n * ```typescript\n * import { Controller, Get, Post } from '@nestjs/common';\n * import { BaseNestController, OnionLasagnaRequest } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';\n * import type { HttpRequest } from '@cosmneo/onion-lasagna/backend/core/presentation';\n *\n * @Controller('users')\n * export class UsersController extends BaseNestController {\n * constructor(\n * private readonly getUserController: GetUserController,\n * private readonly createUserController: CreateUserController,\n * ) {\n * super();\n * }\n *\n * @Get(':id')\n * getUser(@OnionLasagnaRequest() request: HttpRequest) {\n * return this.getUserController.execute(request);\n * }\n *\n * @Post()\n * createUser(@OnionLasagnaRequest() request: HttpRequest) {\n * return this.createUserController.execute(request);\n * }\n * }\n * ```\n *\n * @example With NestJS guards\n * ```typescript\n * @Controller('admin')\n * @UseGuards(JwtAuthGuard, RolesGuard)\n * export class AdminController extends BaseNestController {\n * @Get('dashboard')\n * @Roles('admin')\n * getDashboard(@OnionLasagnaRequest() request: HttpRequest) {\n * return this.dashboardController.execute(request);\n * }\n * }\n * ```\n */\n@UseFilters(OnionLasagnaExceptionFilter)\n@UseInterceptors(OnionLasagnaResponseInterceptor)\n// eslint-disable-next-line @typescript-eslint/no-extraneous-class -- Abstract base with decorator inheritance only\nexport abstract class BaseNestController {}\n","import { Catch, HttpStatus, type ExceptionFilter, type ArgumentsHost } from '@nestjs/common';\nimport type { Response } from 'express';\n\n/**\n * Error item for field-level validation errors.\n */\ninterface ErrorItem {\n item: string;\n message: string;\n}\n\n/**\n * Standard error response body.\n */\ninterface ErrorResponseBody {\n message: string;\n errorCode: string;\n errorItems?: ErrorItem[];\n}\n\n/**\n * Validation error structure.\n */\ninterface ValidationErrorItem {\n field: string;\n message: string;\n}\n\n/**\n * Interface for errors with validation items.\n */\ninterface ErrorWithValidation {\n message: string;\n code: string;\n validationErrors: ValidationErrorItem[];\n}\n\n/**\n * Interface for coded errors.\n */\ninterface CodedErrorLike {\n message: string;\n code: string;\n}\n\n/**\n * Checks if error matches a specific error type by checking its constructor name.\n * This approach avoids issues with multiple class instances in bundled code.\n * Handles both original names and tsup's mangled names (prefixed with _).\n */\nfunction isErrorType(error: unknown, typeName: string): error is CodedErrorLike {\n if (!error || typeof error !== 'object') return false;\n const constructor = (error as object).constructor;\n const name = constructor?.name;\n // Check both the original name and the mangled name (tsup prefixes with _)\n return name === typeName || name === `_${typeName}`;\n}\n\n/**\n * Checks if error has validation errors array.\n */\nfunction hasValidationErrors(error: unknown): error is ErrorWithValidation {\n if (!error || typeof error !== 'object') return false;\n return (\n 'validationErrors' in error && Array.isArray((error as ErrorWithValidation).validationErrors)\n );\n}\n\n/**\n * Known internal error type names that should be masked.\n */\nconst INTERNAL_ERROR_TYPES = [\n 'DomainError',\n 'InfraError',\n 'ControllerError',\n 'NetworkError',\n 'PersistenceError',\n 'ExternalServiceError',\n 'InvariantViolationError',\n];\n\n/**\n * NestJS exception filter that maps onion-lasagna errors to HTTP responses.\n *\n * Mapping strategy:\n * - `ObjectValidationError` → 400 Bad Request\n * - `InvalidRequestError` → 400 Bad Request\n * - `AccessDeniedError` → 403 Forbidden\n * - `NotFoundError` → 404 Not Found\n * - `ConflictError` → 409 Conflict\n * - `UnprocessableError` → 422 Unprocessable Entity\n * - `UseCaseError` (other) → 400 Bad Request\n * - `DomainError` → 500 Internal Server Error (masked)\n * - `InfraError` → 500 Internal Server Error (masked)\n * - `ControllerError` → 500 Internal Server Error (masked)\n * - Unknown → 500 Internal Server Error (masked)\n *\n * **Security Note:** Domain and infrastructure errors are masked to avoid\n * leaking internal implementation details.\n *\n * @example\n * ```typescript\n * import { Controller, UseFilters } from '@nestjs/common';\n * import { OnionLasagnaExceptionFilter } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';\n *\n * @Controller('users')\n * @UseFilters(OnionLasagnaExceptionFilter)\n * export class UsersController {\n * // ...\n * }\n * ```\n *\n * @example Global registration in main.ts\n * ```typescript\n * import { NestFactory } from '@nestjs/core';\n * import { OnionLasagnaExceptionFilter } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';\n *\n * const app = await NestFactory.create(AppModule);\n * app.useGlobalFilters(new OnionLasagnaExceptionFilter());\n * ```\n */\n@Catch()\nexport class OnionLasagnaExceptionFilter implements ExceptionFilter {\n catch(exception: unknown, host: ArgumentsHost) {\n const ctx = host.switchToHttp();\n const response = ctx.getResponse<Response>();\n\n const { status, body } = this.mapError(exception);\n response.status(status).json(body);\n }\n\n private mapError(error: unknown): { status: number; body: ErrorResponseBody } {\n // Validation errors → 400 Bad Request\n if (isErrorType(error, 'ObjectValidationError') && hasValidationErrors(error)) {\n return {\n status: HttpStatus.BAD_REQUEST,\n body: {\n message: error.message,\n errorCode: error.code,\n errorItems: error.validationErrors.map((e) => ({\n item: e.field,\n message: e.message,\n })),\n },\n };\n }\n\n if (isErrorType(error, 'InvalidRequestError') && hasValidationErrors(error)) {\n return {\n status: HttpStatus.BAD_REQUEST,\n body: {\n message: error.message,\n errorCode: error.code,\n errorItems: error.validationErrors.map((e) => ({\n item: e.field,\n message: e.message,\n })),\n },\n };\n }\n\n // Access control → 403 Forbidden\n if (isErrorType(error, 'AccessDeniedError')) {\n return {\n status: HttpStatus.FORBIDDEN,\n body: { message: error.message, errorCode: error.code },\n };\n }\n\n // Use case errors → specific HTTP status codes\n if (isErrorType(error, 'NotFoundError')) {\n return {\n status: HttpStatus.NOT_FOUND,\n body: { message: error.message, errorCode: error.code },\n };\n }\n\n if (isErrorType(error, 'ConflictError')) {\n return {\n status: HttpStatus.CONFLICT,\n body: { message: error.message, errorCode: error.code },\n };\n }\n\n if (isErrorType(error, 'UnprocessableError')) {\n return {\n status: HttpStatus.UNPROCESSABLE_ENTITY,\n body: { message: error.message, errorCode: error.code },\n };\n }\n\n // Other use case errors → 400 Bad Request\n if (isErrorType(error, 'UseCaseError')) {\n return {\n status: HttpStatus.BAD_REQUEST,\n body: { message: error.message, errorCode: error.code },\n };\n }\n\n // Internal errors → 500 (masked)\n for (const errorType of INTERNAL_ERROR_TYPES) {\n if (isErrorType(error, errorType)) {\n return {\n status: HttpStatus.INTERNAL_SERVER_ERROR,\n body: { message: 'An unexpected error occurred', errorCode: 'INTERNAL_ERROR' },\n };\n }\n }\n\n // CodedError (catch-all for known errors) → 500 (masked)\n if (isErrorType(error, 'CodedError')) {\n return {\n status: HttpStatus.INTERNAL_SERVER_ERROR,\n body: { message: 'An unexpected error occurred', errorCode: 'INTERNAL_ERROR' },\n };\n }\n\n // Unknown errors → 500 Internal Server Error (masked)\n return {\n status: HttpStatus.INTERNAL_SERVER_ERROR,\n body: { message: 'An unexpected error occurred', errorCode: 'INTERNAL_ERROR' },\n };\n }\n}\n","import {\n Injectable,\n type NestInterceptor,\n type ExecutionContext,\n type CallHandler,\n} from '@nestjs/common';\nimport type { Observable } from 'rxjs';\nimport { map } from 'rxjs/operators';\nimport type { Response } from 'express';\n\n/**\n * Interface matching onion-lasagna's HttpResponse type.\n */\ninterface HttpResponseLike {\n statusCode: number;\n body: unknown;\n headers?: Record<string, string | number | boolean | undefined>;\n}\n\n/**\n * Type guard to check if a value is an HttpResponse-like object.\n */\nfunction isHttpResponse(value: unknown): value is HttpResponseLike {\n if (!value || typeof value !== 'object') return false;\n const obj = value as HttpResponseLike;\n return typeof obj.statusCode === 'number' && 'body' in obj;\n}\n\n/**\n * NestJS interceptor that transforms onion-lasagna `HttpResponse` objects\n * into proper NestJS responses.\n *\n * When a controller returns an `HttpResponse` object (with `statusCode`, `body`, and optional `headers`),\n * this interceptor:\n * 1. Sets the HTTP status code from `response.statusCode`\n * 2. Sets any custom headers from `response.headers`\n * 3. Returns only `response.body` to NestJS for serialization\n *\n * If the response is not an `HttpResponse` object, it passes through unchanged.\n *\n * @example\n * ```typescript\n * import { Controller, Get, UseInterceptors, UseFilters } from '@nestjs/common';\n * import { OnionLasagnaRequest, OnionLasagnaExceptionFilter, OnionLasagnaResponseInterceptor } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';\n * import type { HttpRequest } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';\n *\n * @Controller('users')\n * @UseFilters(OnionLasagnaExceptionFilter)\n * @UseInterceptors(OnionLasagnaResponseInterceptor)\n * export class UsersController {\n * @Get(':id')\n * getUser(@OnionLasagnaRequest() request: HttpRequest) {\n * return getUserController.execute(request);\n * }\n * }\n * ```\n *\n * @example Global registration in main.ts\n * ```typescript\n * import { NestFactory } from '@nestjs/core';\n * import { OnionLasagnaExceptionFilter, OnionLasagnaResponseInterceptor } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';\n *\n * const app = await NestFactory.create(AppModule);\n * app.useGlobalFilters(new OnionLasagnaExceptionFilter());\n * app.useGlobalInterceptors(new OnionLasagnaResponseInterceptor());\n * await app.listen(3000);\n * ```\n */\n@Injectable()\nexport class OnionLasagnaResponseInterceptor implements NestInterceptor {\n intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {\n return next.handle().pipe(\n map((data: unknown) => {\n // If not an HttpResponse, pass through unchanged\n if (!isHttpResponse(data)) {\n return data;\n }\n\n const res = context.switchToHttp().getResponse<Response>();\n\n // Set status code\n res.status(data.statusCode);\n\n // Set custom headers\n if (data.headers) {\n for (const [key, value] of Object.entries(data.headers)) {\n if (value != null) {\n res.setHeader(key, String(value));\n }\n }\n }\n\n // Return only the body for NestJS serialization\n return data.body;\n }),\n );\n }\n}\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport type { HttpRequest } from '../../../core/onion-layers/presentation/interfaces/types/http/http-request';\n\n/**\n * Normalizes HTTP headers to lowercase keys with string values.\n */\nfunction normalizeHeaders(headers: Record<string, unknown>): Record<string, string> {\n const normalized: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n normalized[key.toLowerCase()] = String(value);\n }\n return normalized;\n}\n\n/**\n * Parameter decorator that extracts the request from NestJS context\n * and transforms it into onion-lasagna's `HttpRequest` format.\n *\n * @example\n * ```typescript\n * import { Controller, Get } from '@nestjs/common';\n * import { OnionLasagnaRequest } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';\n * import type { HttpRequest } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';\n *\n * @Controller('users')\n * export class UsersController {\n * @Get(':id')\n * getUser(@OnionLasagnaRequest() request: HttpRequest) {\n * return getUserController.execute(request);\n * }\n * }\n * ```\n */\nexport const OnionLasagnaRequest = createParamDecorator(\n (_data: unknown, ctx: ExecutionContext): HttpRequest => {\n const request = ctx.switchToHttp().getRequest();\n\n return {\n body: request.body,\n headers: normalizeHeaders(request.headers),\n queryParams: request.query,\n pathParams: request.params,\n };\n },\n);\n"],"mappings":"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","names":["_init","_init"]}
|