@carno.js/core 1.1.1 → 1.2.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.
Files changed (124) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +188 -188
  3. package/dist/Carno.js +45 -26
  4. package/dist/Carno.mjs +45 -26
  5. package/dist/bun/index.js +4 -4
  6. package/dist/bun/index.js.map +30 -29
  7. package/dist/compression/CompressionMiddleware.js +110 -0
  8. package/dist/compression/CompressionMiddleware.mjs +90 -0
  9. package/dist/index.js +3 -1
  10. package/dist/index.mjs +2 -0
  11. package/package.json +2 -2
  12. package/src/Carno.ts +728 -673
  13. package/src/DefaultRoutes.ts +34 -34
  14. package/src/cache/CacheDriver.ts +50 -50
  15. package/src/cache/CacheService.ts +139 -139
  16. package/src/cache/MemoryDriver.ts +104 -104
  17. package/src/cache/RedisDriver.ts +116 -116
  18. package/src/compiler/JITCompiler.ts +167 -167
  19. package/src/compression/CompressionMiddleware.ts +221 -0
  20. package/src/container/Container.ts +168 -168
  21. package/src/context/Context.ts +130 -130
  22. package/src/cors/CorsHandler.ts +145 -145
  23. package/src/decorators/Controller.ts +63 -63
  24. package/src/decorators/Inject.ts +16 -16
  25. package/src/decorators/Middleware.ts +22 -22
  26. package/src/decorators/Service.ts +18 -18
  27. package/src/decorators/methods.ts +58 -58
  28. package/src/decorators/params.ts +47 -47
  29. package/src/events/Lifecycle.ts +97 -97
  30. package/src/exceptions/HttpException.ts +99 -99
  31. package/src/index.ts +99 -95
  32. package/src/metadata.ts +46 -46
  33. package/src/middleware/CarnoMiddleware.ts +20 -14
  34. package/src/router/RadixRouter.ts +225 -225
  35. package/src/testing/TestHarness.ts +185 -185
  36. package/src/utils/Metadata.ts +43 -43
  37. package/src/utils/parseQuery.ts +161 -161
  38. package/src/validation/ValibotAdapter.ts +95 -95
  39. package/src/validation/ValidatorAdapter.ts +69 -69
  40. package/src/validation/ZodAdapter.ts +102 -102
  41. package/dist/Carno.d.js +0 -14
  42. package/dist/Carno.d.mjs +0 -1
  43. package/dist/DefaultRoutes.d.js +0 -13
  44. package/dist/DefaultRoutes.d.mjs +0 -0
  45. package/dist/cache/CacheDriver.d.js +0 -13
  46. package/dist/cache/CacheDriver.d.mjs +0 -0
  47. package/dist/cache/CacheService.d.js +0 -13
  48. package/dist/cache/CacheService.d.mjs +0 -0
  49. package/dist/cache/MemoryDriver.d.js +0 -13
  50. package/dist/cache/MemoryDriver.d.mjs +0 -0
  51. package/dist/cache/RedisDriver.d.js +0 -13
  52. package/dist/cache/RedisDriver.d.mjs +0 -0
  53. package/dist/compiler/JITCompiler.d.js +0 -13
  54. package/dist/compiler/JITCompiler.d.mjs +0 -0
  55. package/dist/container/Container.d.js +0 -13
  56. package/dist/container/Container.d.mjs +0 -0
  57. package/dist/context/Context.d.js +0 -13
  58. package/dist/context/Context.d.mjs +0 -0
  59. package/dist/cors/CorsHandler.d.js +0 -13
  60. package/dist/cors/CorsHandler.d.mjs +0 -0
  61. package/dist/decorators/Controller.d.js +0 -13
  62. package/dist/decorators/Controller.d.mjs +0 -0
  63. package/dist/decorators/Inject.d.js +0 -13
  64. package/dist/decorators/Inject.d.mjs +0 -0
  65. package/dist/decorators/Middleware.d.js +0 -13
  66. package/dist/decorators/Middleware.d.mjs +0 -0
  67. package/dist/decorators/Service.d.js +0 -13
  68. package/dist/decorators/Service.d.mjs +0 -0
  69. package/dist/decorators/methods.d.js +0 -13
  70. package/dist/decorators/methods.d.mjs +0 -0
  71. package/dist/decorators/params.d.js +0 -13
  72. package/dist/decorators/params.d.mjs +0 -0
  73. package/dist/events/Lifecycle.d.js +0 -13
  74. package/dist/events/Lifecycle.d.mjs +0 -0
  75. package/dist/exceptions/HttpException.d.js +0 -13
  76. package/dist/exceptions/HttpException.d.mjs +0 -0
  77. package/dist/index.d.js +0 -130
  78. package/dist/index.d.mjs +0 -78
  79. package/dist/metadata.d.js +0 -13
  80. package/dist/metadata.d.mjs +0 -0
  81. package/dist/middleware/CarnoMiddleware.d.js +0 -13
  82. package/dist/middleware/CarnoMiddleware.d.mjs +0 -0
  83. package/dist/router/RadixRouter.d.js +0 -13
  84. package/dist/router/RadixRouter.d.mjs +0 -0
  85. package/dist/testing/TestHarness.d.js +0 -13
  86. package/dist/testing/TestHarness.d.mjs +0 -0
  87. package/dist/utils/Metadata.d.js +0 -13
  88. package/dist/utils/Metadata.d.mjs +0 -0
  89. package/dist/utils/parseQuery.d.js +0 -13
  90. package/dist/utils/parseQuery.d.mjs +0 -0
  91. package/dist/validation/ValibotAdapter.d.js +0 -13
  92. package/dist/validation/ValibotAdapter.d.mjs +0 -0
  93. package/dist/validation/ValidatorAdapter.d.js +0 -13
  94. package/dist/validation/ValidatorAdapter.d.mjs +0 -0
  95. package/dist/validation/ZodAdapter.d.js +0 -13
  96. package/dist/validation/ZodAdapter.d.mjs +0 -0
  97. package/src/Carno.d.ts +0 -135
  98. package/src/DefaultRoutes.d.ts +0 -19
  99. package/src/cache/CacheDriver.d.ts +0 -43
  100. package/src/cache/CacheService.d.ts +0 -89
  101. package/src/cache/MemoryDriver.d.ts +0 -32
  102. package/src/cache/RedisDriver.d.ts +0 -34
  103. package/src/compiler/JITCompiler.d.ts +0 -36
  104. package/src/container/Container.d.ts +0 -38
  105. package/src/context/Context.d.ts +0 -36
  106. package/src/cors/CorsHandler.d.ts +0 -47
  107. package/src/decorators/Controller.d.ts +0 -13
  108. package/src/decorators/Inject.d.ts +0 -6
  109. package/src/decorators/Middleware.d.ts +0 -5
  110. package/src/decorators/Service.d.ts +0 -9
  111. package/src/decorators/methods.d.ts +0 -7
  112. package/src/decorators/params.d.ts +0 -13
  113. package/src/events/Lifecycle.d.ts +0 -54
  114. package/src/exceptions/HttpException.d.ts +0 -43
  115. package/src/index.d.ts +0 -42
  116. package/src/metadata.d.ts +0 -41
  117. package/src/middleware/CarnoMiddleware.d.ts +0 -12
  118. package/src/router/RadixRouter.d.ts +0 -19
  119. package/src/testing/TestHarness.d.ts +0 -71
  120. package/src/utils/Metadata.d.ts +0 -20
  121. package/src/utils/parseQuery.d.ts +0 -23
  122. package/src/validation/ValibotAdapter.d.ts +0 -30
  123. package/src/validation/ValidatorAdapter.d.ts +0 -54
  124. package/src/validation/ZodAdapter.d.ts +0 -35
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 carno.js
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 carno.js
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,188 +1,188 @@
1
- # @carno.js/core
2
-
3
- Ultra-fast, performance-first HTTP framework for Bun.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- bun add @carno.js/core
9
- ```
10
-
11
- ## Prerequisites
12
-
13
- Enable decorators in your `tsconfig.json`:
14
-
15
- ```json
16
- {
17
- "compilerOptions": {
18
- "experimentalDecorators": true,
19
- "emitDecoratorMetadata": true
20
- }
21
- }
22
- ```
23
-
24
- ## Quick Start
25
-
26
- ```typescript
27
- import { Carno, Controller, Get, Service, Param } from '@carno.js/core';
28
-
29
- @Service()
30
- class GreetService {
31
- greet(name: string) {
32
- return `Hello, ${name}!`;
33
- }
34
- }
35
-
36
- @Controller('/greet')
37
- class GreetController {
38
- constructor(private greetService: GreetService) {}
39
-
40
- @Get('/:name')
41
- greet(@Param('name') name: string) {
42
- return { message: this.greetService.greet(name) };
43
- }
44
- }
45
-
46
- const app = new Carno();
47
- app.services([GreetService]);
48
- app.controllers([GreetController]);
49
- app.listen(3000);
50
- ```
51
-
52
- ## API Overview
53
-
54
- ### Application
55
-
56
- ```typescript
57
- const app = new Carno(config?: CarnoConfig);
58
-
59
- app.controllers([...]); // Register controllers
60
- app.services([...]); // Register services
61
- app.middlewares([...]); // Register global middlewares
62
- app.use(plugin); // Use a plugin/module
63
- await app.listen(3000); // Start server
64
- await app.stop(); // Stop server
65
- ```
66
-
67
- ### Decorators
68
-
69
- #### Controllers & Routes
70
-
71
- | Decorator | Description |
72
- | :--- | :--- |
73
- | `@Controller(path?)` | Define a controller with optional base path |
74
- | `@Get(path?)` | Handle GET requests |
75
- | `@Post(path?)` | Handle POST requests |
76
- | `@Put(path?)` | Handle PUT requests |
77
- | `@Delete(path?)` | Handle DELETE requests |
78
- | `@Patch(path?)` | Handle PATCH requests |
79
-
80
- #### Parameters
81
-
82
- | Decorator | Description |
83
- | :--- | :--- |
84
- | `@Param(key?)` | Route parameters |
85
- | `@Query(key?)` | Query string parameters |
86
- | `@Body(key?)` | Request body |
87
- | `@Header(key?)` | Request headers |
88
- | `@Req()` | Raw Request object |
89
- | `@Ctx()` | Full Context object |
90
-
91
- #### Dependency Injection
92
-
93
- | Decorator | Description |
94
- | :--- | :--- |
95
- | `@Service(options?)` | Mark class as injectable service |
96
- | `@Inject(token)` | Inject by token (for interfaces) |
97
-
98
- #### Middleware
99
-
100
- | Decorator | Description |
101
- | :--- | :--- |
102
- | `@Middleware(middleware)` | Apply middleware to controller/route |
103
-
104
- #### Lifecycle
105
-
106
- | Decorator | Description |
107
- | :--- | :--- |
108
- | `@OnApplicationInit()` | Called after DI container is ready |
109
- | `@OnApplicationBoot()` | Called when server starts listening |
110
- | `@OnApplicationShutdown()` | Called when server is stopping |
111
-
112
- ### Validation
113
-
114
- ```typescript
115
- import { z } from 'zod';
116
- import { Schema, ZodAdapter } from '@carno.js/core';
117
-
118
- @Schema(z.object({
119
- name: z.string().min(2),
120
- email: z.string().email(),
121
- }))
122
- class CreateUserDto {
123
- name!: string;
124
- email!: string;
125
- }
126
-
127
- const app = new Carno({ validation: new ZodAdapter() });
128
- ```
129
-
130
- ### CORS
131
-
132
- ```typescript
133
- const app = new Carno({
134
- cors: {
135
- origins: '*', // or specific origin(s)
136
- methods: ['GET', 'POST'],
137
- headers: ['Content-Type'],
138
- credentials: true,
139
- maxAge: 86400,
140
- }
141
- });
142
- ```
143
-
144
- ### Exceptions
145
-
146
- ```typescript
147
- import {
148
- BadRequestException,
149
- UnauthorizedException,
150
- ForbiddenException,
151
- NotFoundException,
152
- ConflictException,
153
- InternalServerErrorException,
154
- } from '@carno.js/core';
155
-
156
- throw new NotFoundException('User not found');
157
- ```
158
-
159
- ### Testing
160
-
161
- ```typescript
162
- import { withTestApp } from '@carno.js/core';
163
-
164
- await withTestApp(
165
- async (harness) => {
166
- const res = await harness.get('/users');
167
- expect(res.status).toBe(200);
168
- },
169
- { controllers: [UserController], listen: true }
170
- );
171
- ```
172
-
173
- ## Configuration
174
-
175
- ```typescript
176
- interface CarnoConfig {
177
- exports?: (Token | ProviderConfig)[];
178
- globalMiddlewares?: MiddlewareHandler[];
179
- disableStartupLog?: boolean;
180
- cors?: CorsConfig;
181
- validation?: ValidatorAdapter | boolean;
182
- cache?: CacheConfig | boolean;
183
- }
184
- ```
185
-
186
- ## License
187
-
188
- MIT
1
+ # @carno.js/core
2
+
3
+ Ultra-fast, performance-first HTTP framework for Bun.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @carno.js/core
9
+ ```
10
+
11
+ ## Prerequisites
12
+
13
+ Enable decorators in your `tsconfig.json`:
14
+
15
+ ```json
16
+ {
17
+ "compilerOptions": {
18
+ "experimentalDecorators": true,
19
+ "emitDecoratorMetadata": true
20
+ }
21
+ }
22
+ ```
23
+
24
+ ## Quick Start
25
+
26
+ ```typescript
27
+ import { Carno, Controller, Get, Service, Param } from '@carno.js/core';
28
+
29
+ @Service()
30
+ class GreetService {
31
+ greet(name: string) {
32
+ return `Hello, ${name}!`;
33
+ }
34
+ }
35
+
36
+ @Controller('/greet')
37
+ class GreetController {
38
+ constructor(private greetService: GreetService) {}
39
+
40
+ @Get('/:name')
41
+ greet(@Param('name') name: string) {
42
+ return { message: this.greetService.greet(name) };
43
+ }
44
+ }
45
+
46
+ const app = new Carno();
47
+ app.services([GreetService]);
48
+ app.controllers([GreetController]);
49
+ app.listen(3000);
50
+ ```
51
+
52
+ ## API Overview
53
+
54
+ ### Application
55
+
56
+ ```typescript
57
+ const app = new Carno(config?: CarnoConfig);
58
+
59
+ app.controllers([...]); // Register controllers
60
+ app.services([...]); // Register services
61
+ app.middlewares([...]); // Register global middlewares
62
+ app.use(plugin); // Use a plugin/module
63
+ await app.listen(3000); // Start server
64
+ await app.stop(); // Stop server
65
+ ```
66
+
67
+ ### Decorators
68
+
69
+ #### Controllers & Routes
70
+
71
+ | Decorator | Description |
72
+ | :--- | :--- |
73
+ | `@Controller(path?)` | Define a controller with optional base path |
74
+ | `@Get(path?)` | Handle GET requests |
75
+ | `@Post(path?)` | Handle POST requests |
76
+ | `@Put(path?)` | Handle PUT requests |
77
+ | `@Delete(path?)` | Handle DELETE requests |
78
+ | `@Patch(path?)` | Handle PATCH requests |
79
+
80
+ #### Parameters
81
+
82
+ | Decorator | Description |
83
+ | :--- | :--- |
84
+ | `@Param(key?)` | Route parameters |
85
+ | `@Query(key?)` | Query string parameters |
86
+ | `@Body(key?)` | Request body |
87
+ | `@Header(key?)` | Request headers |
88
+ | `@Req()` | Raw Request object |
89
+ | `@Ctx()` | Full Context object |
90
+
91
+ #### Dependency Injection
92
+
93
+ | Decorator | Description |
94
+ | :--- | :--- |
95
+ | `@Service(options?)` | Mark class as injectable service |
96
+ | `@Inject(token)` | Inject by token (for interfaces) |
97
+
98
+ #### Middleware
99
+
100
+ | Decorator | Description |
101
+ | :--- | :--- |
102
+ | `@Middleware(middleware)` | Apply middleware to controller/route |
103
+
104
+ #### Lifecycle
105
+
106
+ | Decorator | Description |
107
+ | :--- | :--- |
108
+ | `@OnApplicationInit()` | Called after DI container is ready |
109
+ | `@OnApplicationBoot()` | Called when server starts listening |
110
+ | `@OnApplicationShutdown()` | Called when server is stopping |
111
+
112
+ ### Validation
113
+
114
+ ```typescript
115
+ import { z } from 'zod';
116
+ import { Schema, ZodAdapter } from '@carno.js/core';
117
+
118
+ @Schema(z.object({
119
+ name: z.string().min(2),
120
+ email: z.string().email(),
121
+ }))
122
+ class CreateUserDto {
123
+ name!: string;
124
+ email!: string;
125
+ }
126
+
127
+ const app = new Carno({ validation: new ZodAdapter() });
128
+ ```
129
+
130
+ ### CORS
131
+
132
+ ```typescript
133
+ const app = new Carno({
134
+ cors: {
135
+ origins: '*', // or specific origin(s)
136
+ methods: ['GET', 'POST'],
137
+ headers: ['Content-Type'],
138
+ credentials: true,
139
+ maxAge: 86400,
140
+ }
141
+ });
142
+ ```
143
+
144
+ ### Exceptions
145
+
146
+ ```typescript
147
+ import {
148
+ BadRequestException,
149
+ UnauthorizedException,
150
+ ForbiddenException,
151
+ NotFoundException,
152
+ ConflictException,
153
+ InternalServerErrorException,
154
+ } from '@carno.js/core';
155
+
156
+ throw new NotFoundException('User not found');
157
+ ```
158
+
159
+ ### Testing
160
+
161
+ ```typescript
162
+ import { withTestApp } from '@carno.js/core';
163
+
164
+ await withTestApp(
165
+ async (harness) => {
166
+ const res = await harness.get('/users');
167
+ expect(res.status).toBe(200);
168
+ },
169
+ { controllers: [UserController], listen: true }
170
+ );
171
+ ```
172
+
173
+ ## Configuration
174
+
175
+ ```typescript
176
+ interface CarnoConfig {
177
+ exports?: (Token | ProviderConfig)[];
178
+ globalMiddlewares?: MiddlewareHandler[];
179
+ disableStartupLog?: boolean;
180
+ cors?: CorsConfig;
181
+ validation?: ValidatorAdapter | boolean;
182
+ cache?: CacheConfig | boolean;
183
+ }
184
+ ```
185
+
186
+ ## License
187
+
188
+ MIT
package/dist/Carno.js CHANGED
@@ -216,37 +216,56 @@ class Carno {
216
216
  }
217
217
  createHandler(compiled, params, middlewares, bodyDtoType) {
218
218
  const handler = compiled.fn, hasMiddlewares = middlewares.length > 0, hasParams = params.length > 0, applyCors = this.hasCors ? this.applyCors.bind(this) : null, validator = bodyDtoType ? this.validator : null, hasMiddlewaresOrValidation = hasMiddlewares || !!validator;
219
- return !hasMiddlewaresOrValidation && !hasParams ? compiled.isAsync ? async (req) => {
220
- const ctx = new import_Context.Context(req), result = await handler(ctx), response = this.buildResponse(result);
221
- return applyCors ? applyCors(response, req) : response;
222
- } : (req) => {
223
- const ctx = new import_Context.Context(req), result = handler(ctx), response = this.buildResponse(result);
224
- return applyCors ? applyCors(response, req) : response;
225
- } : !hasMiddlewaresOrValidation && hasParams ? compiled.isAsync ? async (req) => {
226
- const ctx = new import_Context.Context(req, req.params), result = await handler(ctx), response = this.buildResponse(result);
227
- return applyCors ? applyCors(response, req) : response;
228
- } : (req) => {
229
- const ctx = new import_Context.Context(req, req.params), result = handler(ctx), response = this.buildResponse(result);
230
- return applyCors ? applyCors(response, req) : response;
231
- } : async (req) => {
232
- const ctx = new import_Context.Context(req, req.params || {});
233
- for (const middleware of middlewares) {
234
- const result2 = await middleware(ctx);
235
- if (result2 instanceof Response)
236
- return applyCors ? applyCors(result2, req) : result2;
237
- }
238
- validator && bodyDtoType && (await ctx.parseBody(), validator.validateOrThrow(bodyDtoType, ctx.body));
239
- const result = compiled.isAsync ? await handler(ctx) : handler(ctx), response = this.buildResponse(result);
219
+ if (!hasMiddlewaresOrValidation && !hasParams)
220
+ return compiled.isAsync ? async (req) => {
221
+ const ctx = new import_Context.Context(req), result = await handler(ctx), response = this.buildResponse(result);
222
+ return applyCors ? applyCors(response, req) : response;
223
+ } : (req) => {
224
+ const ctx = new import_Context.Context(req), result = handler(ctx), response = this.buildResponse(result);
225
+ return applyCors ? applyCors(response, req) : response;
226
+ };
227
+ if (!hasMiddlewaresOrValidation && hasParams)
228
+ return compiled.isAsync ? async (req) => {
229
+ const ctx = new import_Context.Context(req, req.params), result = await handler(ctx), response = this.buildResponse(result);
230
+ return applyCors ? applyCors(response, req) : response;
231
+ } : (req) => {
232
+ const ctx = new import_Context.Context(req, req.params), result = handler(ctx), response = this.buildResponse(result);
233
+ return applyCors ? applyCors(response, req) : response;
234
+ };
235
+ const coreHandler = async (ctx) => (validator && bodyDtoType && (await ctx.parseBody(), validator.validateOrThrow(bodyDtoType, ctx.body)), compiled.isAsync ? await handler(ctx) : handler(ctx)), chain = this.buildMiddlewareChain(
236
+ middlewares,
237
+ coreHandler,
238
+ this.buildResponse.bind(this)
239
+ );
240
+ return async (req) => {
241
+ const ctx = new import_Context.Context(req, req.params || {}), response = await chain(ctx);
240
242
  return applyCors ? applyCors(response, req) : response;
241
243
  };
242
244
  }
243
245
  resolveMiddleware(middleware) {
244
- if (typeof middleware == "function" && middleware.prototype?.handle) {
245
- const instance = this.container.get(middleware);
246
- return (ctx) => instance.handle(ctx, () => {
247
- });
246
+ return typeof middleware == "function" && middleware.prototype?.handle ? { kind: "class", instance: this.container.get(middleware) } : typeof middleware == "object" && middleware !== null && "handle" in middleware ? { kind: "class", instance: middleware } : { kind: "function", handler: middleware };
247
+ }
248
+ /**
249
+ * Build an onion-style middleware chain.
250
+ * Wraps from inside-out so each middleware can run code before and after next().
251
+ */
252
+ buildMiddlewareChain(middlewares, coreHandler, buildResponseFn) {
253
+ let chain = async (ctx) => {
254
+ const result = await coreHandler(ctx);
255
+ return buildResponseFn(result);
256
+ };
257
+ for (let i = middlewares.length - 1; i >= 0; i--) {
258
+ const mw = middlewares[i], nextLayer = chain;
259
+ mw.kind === "function" ? chain = async (ctx) => {
260
+ const result = await mw.handler(ctx);
261
+ return result instanceof Response ? result : nextLayer(ctx);
262
+ } : chain = async (ctx) => {
263
+ let response;
264
+ const result = await mw.instance.handle(ctx, async () => (response = await nextLayer(ctx), response));
265
+ return result instanceof Response ? result : response ?? new Response(null, { status: 200 });
266
+ };
248
267
  }
249
- return middleware;
268
+ return chain;
250
269
  }
251
270
  /**
252
271
  * Apply CORS headers to a response.
package/dist/Carno.mjs CHANGED
@@ -208,37 +208,56 @@ class Carno {
208
208
  }
209
209
  createHandler(compiled, params, middlewares, bodyDtoType) {
210
210
  const handler = compiled.fn, hasMiddlewares = middlewares.length > 0, hasParams = params.length > 0, applyCors = this.hasCors ? this.applyCors.bind(this) : null, validator = bodyDtoType ? this.validator : null, hasMiddlewaresOrValidation = hasMiddlewares || !!validator;
211
- return !hasMiddlewaresOrValidation && !hasParams ? compiled.isAsync ? async (req) => {
212
- const ctx = new Context(req), result = await handler(ctx), response = this.buildResponse(result);
213
- return applyCors ? applyCors(response, req) : response;
214
- } : (req) => {
215
- const ctx = new Context(req), result = handler(ctx), response = this.buildResponse(result);
216
- return applyCors ? applyCors(response, req) : response;
217
- } : !hasMiddlewaresOrValidation && hasParams ? compiled.isAsync ? async (req) => {
218
- const ctx = new Context(req, req.params), result = await handler(ctx), response = this.buildResponse(result);
219
- return applyCors ? applyCors(response, req) : response;
220
- } : (req) => {
221
- const ctx = new Context(req, req.params), result = handler(ctx), response = this.buildResponse(result);
222
- return applyCors ? applyCors(response, req) : response;
223
- } : async (req) => {
224
- const ctx = new Context(req, req.params || {});
225
- for (const middleware of middlewares) {
226
- const result2 = await middleware(ctx);
227
- if (result2 instanceof Response)
228
- return applyCors ? applyCors(result2, req) : result2;
229
- }
230
- validator && bodyDtoType && (await ctx.parseBody(), validator.validateOrThrow(bodyDtoType, ctx.body));
231
- const result = compiled.isAsync ? await handler(ctx) : handler(ctx), response = this.buildResponse(result);
211
+ if (!hasMiddlewaresOrValidation && !hasParams)
212
+ return compiled.isAsync ? async (req) => {
213
+ const ctx = new Context(req), result = await handler(ctx), response = this.buildResponse(result);
214
+ return applyCors ? applyCors(response, req) : response;
215
+ } : (req) => {
216
+ const ctx = new Context(req), result = handler(ctx), response = this.buildResponse(result);
217
+ return applyCors ? applyCors(response, req) : response;
218
+ };
219
+ if (!hasMiddlewaresOrValidation && hasParams)
220
+ return compiled.isAsync ? async (req) => {
221
+ const ctx = new Context(req, req.params), result = await handler(ctx), response = this.buildResponse(result);
222
+ return applyCors ? applyCors(response, req) : response;
223
+ } : (req) => {
224
+ const ctx = new Context(req, req.params), result = handler(ctx), response = this.buildResponse(result);
225
+ return applyCors ? applyCors(response, req) : response;
226
+ };
227
+ const coreHandler = async (ctx) => (validator && bodyDtoType && (await ctx.parseBody(), validator.validateOrThrow(bodyDtoType, ctx.body)), compiled.isAsync ? await handler(ctx) : handler(ctx)), chain = this.buildMiddlewareChain(
228
+ middlewares,
229
+ coreHandler,
230
+ this.buildResponse.bind(this)
231
+ );
232
+ return async (req) => {
233
+ const ctx = new Context(req, req.params || {}), response = await chain(ctx);
232
234
  return applyCors ? applyCors(response, req) : response;
233
235
  };
234
236
  }
235
237
  resolveMiddleware(middleware) {
236
- if (typeof middleware == "function" && middleware.prototype?.handle) {
237
- const instance = this.container.get(middleware);
238
- return (ctx) => instance.handle(ctx, () => {
239
- });
238
+ return typeof middleware == "function" && middleware.prototype?.handle ? { kind: "class", instance: this.container.get(middleware) } : typeof middleware == "object" && middleware !== null && "handle" in middleware ? { kind: "class", instance: middleware } : { kind: "function", handler: middleware };
239
+ }
240
+ /**
241
+ * Build an onion-style middleware chain.
242
+ * Wraps from inside-out so each middleware can run code before and after next().
243
+ */
244
+ buildMiddlewareChain(middlewares, coreHandler, buildResponseFn) {
245
+ let chain = async (ctx) => {
246
+ const result = await coreHandler(ctx);
247
+ return buildResponseFn(result);
248
+ };
249
+ for (let i = middlewares.length - 1; i >= 0; i--) {
250
+ const mw = middlewares[i], nextLayer = chain;
251
+ mw.kind === "function" ? chain = async (ctx) => {
252
+ const result = await mw.handler(ctx);
253
+ return result instanceof Response ? result : nextLayer(ctx);
254
+ } : chain = async (ctx) => {
255
+ let response;
256
+ const result = await mw.instance.handle(ctx, async () => (response = await nextLayer(ctx), response));
257
+ return result instanceof Response ? result : response ?? new Response(null, { status: 200 });
258
+ };
240
259
  }
241
- return middleware;
260
+ return chain;
242
261
  }
243
262
  /**
244
263
  * Apply CORS headers to a response.