mini-nest 1.0.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 (236) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +556 -0
  3. package/dist/aop/aopWrapper.d.ts +17 -0
  4. package/dist/aop/aopWrapper.d.ts.map +1 -0
  5. package/dist/aop/aopWrapper.js +112 -0
  6. package/dist/aop/aopWrapper.js.map +1 -0
  7. package/dist/cache/cacheManager.d.ts +11 -0
  8. package/dist/cache/cacheManager.d.ts.map +1 -0
  9. package/dist/cache/cacheManager.js +42 -0
  10. package/dist/cache/cacheManager.js.map +1 -0
  11. package/dist/circuitBreaker/circuitBreakerManager.d.ts +21 -0
  12. package/dist/circuitBreaker/circuitBreakerManager.d.ts.map +1 -0
  13. package/dist/circuitBreaker/circuitBreakerManager.js +60 -0
  14. package/dist/circuitBreaker/circuitBreakerManager.js.map +1 -0
  15. package/dist/circuitBreaker/circuitOpenError.d.ts +4 -0
  16. package/dist/circuitBreaker/circuitOpenError.d.ts.map +1 -0
  17. package/dist/circuitBreaker/circuitOpenError.js +11 -0
  18. package/dist/circuitBreaker/circuitOpenError.js.map +1 -0
  19. package/dist/container.js +33 -0
  20. package/dist/controller.js +10 -0
  21. package/dist/core/app/App.d.ts +31 -0
  22. package/dist/core/app/App.d.ts.map +1 -0
  23. package/dist/core/app/App.js +149 -0
  24. package/dist/core/app/App.js.map +1 -0
  25. package/dist/core/container/container.d.ts +16 -0
  26. package/dist/core/container/container.d.ts.map +1 -0
  27. package/dist/core/container/container.js +61 -0
  28. package/dist/core/container/container.js.map +1 -0
  29. package/dist/core/pipeline/ExecutionContext.d.ts +22 -0
  30. package/dist/core/pipeline/ExecutionContext.d.ts.map +1 -0
  31. package/dist/core/pipeline/ExecutionContext.js +31 -0
  32. package/dist/core/pipeline/ExecutionContext.js.map +1 -0
  33. package/dist/core/pipeline/RequestPipeline.d.ts +9 -0
  34. package/dist/core/pipeline/RequestPipeline.d.ts.map +1 -0
  35. package/dist/core/pipeline/RequestPipeline.js +83 -0
  36. package/dist/core/pipeline/RequestPipeline.js.map +1 -0
  37. package/dist/createMethodDecorator.js +22 -0
  38. package/dist/createParamDecorator.js +46 -0
  39. package/dist/decorators/Injectable.d.ts +3 -0
  40. package/dist/decorators/Injectable.d.ts.map +1 -0
  41. package/dist/decorators/Injectable.js +10 -0
  42. package/dist/decorators/Injectable.js.map +1 -0
  43. package/dist/decorators/UseGuard.d.ts +6 -0
  44. package/dist/decorators/UseGuard.d.ts.map +1 -0
  45. package/dist/decorators/UseGuard.js +20 -0
  46. package/dist/decorators/UseGuard.js.map +1 -0
  47. package/dist/decorators/UseInterceptor.d.ts +5 -0
  48. package/dist/decorators/UseInterceptor.d.ts.map +1 -0
  49. package/dist/decorators/UseInterceptor.js +15 -0
  50. package/dist/decorators/UseInterceptor.js.map +1 -0
  51. package/dist/decorators/aop/Cache.d.ts +6 -0
  52. package/dist/decorators/aop/Cache.d.ts.map +1 -0
  53. package/dist/decorators/aop/Cache.js +13 -0
  54. package/dist/decorators/aop/Cache.js.map +1 -0
  55. package/dist/decorators/aop/CircuitBreaker.d.ts +6 -0
  56. package/dist/decorators/aop/CircuitBreaker.d.ts.map +1 -0
  57. package/dist/decorators/aop/CircuitBreaker.js +10 -0
  58. package/dist/decorators/aop/CircuitBreaker.js.map +1 -0
  59. package/dist/decorators/aop/Retry.d.ts +2 -0
  60. package/dist/decorators/aop/Retry.d.ts.map +1 -0
  61. package/dist/decorators/aop/Retry.js +10 -0
  62. package/dist/decorators/aop/Retry.js.map +1 -0
  63. package/dist/decorators/aop/Timeout.d.ts +2 -0
  64. package/dist/decorators/aop/Timeout.d.ts.map +1 -0
  65. package/dist/decorators/aop/Timeout.js +10 -0
  66. package/dist/decorators/aop/Timeout.js.map +1 -0
  67. package/dist/decorators/http/Controller.d.ts +3 -0
  68. package/dist/decorators/http/Controller.d.ts.map +1 -0
  69. package/dist/decorators/http/Controller.js +11 -0
  70. package/dist/decorators/http/Controller.js.map +1 -0
  71. package/dist/decorators/http/CreateMethodDecorator.d.ts +8 -0
  72. package/dist/decorators/http/CreateMethodDecorator.d.ts.map +1 -0
  73. package/dist/decorators/http/CreateMethodDecorator.js +20 -0
  74. package/dist/decorators/http/CreateMethodDecorator.js.map +1 -0
  75. package/dist/decorators/http/CreateParamDecorator.d.ts +13 -0
  76. package/dist/decorators/http/CreateParamDecorator.d.ts.map +1 -0
  77. package/dist/decorators/http/CreateParamDecorator.js +57 -0
  78. package/dist/decorators/http/CreateParamDecorator.js.map +1 -0
  79. package/dist/decorators/index.d.ts +11 -0
  80. package/dist/decorators/index.d.ts.map +1 -0
  81. package/dist/decorators/index.js +37 -0
  82. package/dist/decorators/index.js.map +1 -0
  83. package/dist/decorators.js +1 -0
  84. package/dist/examples/User.example.js +12 -0
  85. package/dist/examples/container.example.js +31 -0
  86. package/dist/examples/container.example.mjs +42 -0
  87. package/dist/examples/controllers/LogController.js +33 -0
  88. package/dist/examples/controllers/UserController.js +68 -0
  89. package/dist/examples/createTestApp.js +23 -0
  90. package/dist/examples/interceptors/LoggerInterceptor.js +12 -0
  91. package/dist/examples/log.example.js +6 -0
  92. package/dist/examples/services/LoggerService.js +19 -0
  93. package/dist/examples/services/UserService.js +40 -0
  94. package/dist/exceptions/HTTPExceptions.d.ts +36 -0
  95. package/dist/exceptions/HTTPExceptions.d.ts.map +1 -0
  96. package/dist/exceptions/HTTPExceptions.js +62 -0
  97. package/dist/exceptions/HTTPExceptions.js.map +1 -0
  98. package/dist/exceptions/baseHTTPException.d.ts +16 -0
  99. package/dist/exceptions/baseHTTPException.d.ts.map +1 -0
  100. package/dist/exceptions/baseHTTPException.js +25 -0
  101. package/dist/exceptions/baseHTTPException.js.map +1 -0
  102. package/dist/exceptions/defaultExceptionFilter.d.ts +10 -0
  103. package/dist/exceptions/defaultExceptionFilter.d.ts.map +1 -0
  104. package/dist/exceptions/defaultExceptionFilter.js +65 -0
  105. package/dist/exceptions/defaultExceptionFilter.js.map +1 -0
  106. package/dist/exceptions/exceptionFilter.d.ts +6 -0
  107. package/dist/exceptions/exceptionFilter.d.ts.map +1 -0
  108. package/dist/exceptions/exceptionFilter.js +3 -0
  109. package/dist/exceptions/exceptionFilter.js.map +1 -0
  110. package/dist/exceptions/exceptionHandler.d.ts +12 -0
  111. package/dist/exceptions/exceptionHandler.d.ts.map +1 -0
  112. package/dist/exceptions/exceptionHandler.js +50 -0
  113. package/dist/exceptions/exceptionHandler.js.map +1 -0
  114. package/dist/exceptions/index.d.ts +6 -0
  115. package/dist/exceptions/index.d.ts.map +1 -0
  116. package/dist/exceptions/index.js +22 -0
  117. package/dist/exceptions/index.js.map +1 -0
  118. package/dist/guards/Guard.d.ts +5 -0
  119. package/dist/guards/Guard.d.ts.map +1 -0
  120. package/dist/guards/Guard.js +3 -0
  121. package/dist/guards/Guard.js.map +1 -0
  122. package/dist/guards/applyGuard.d.ts +5 -0
  123. package/dist/guards/applyGuard.d.ts.map +1 -0
  124. package/dist/guards/applyGuard.js +15 -0
  125. package/dist/guards/applyGuard.js.map +1 -0
  126. package/dist/http/HttpRequest.d.ts +52 -0
  127. package/dist/http/HttpRequest.d.ts.map +1 -0
  128. package/dist/http/HttpRequest.js +103 -0
  129. package/dist/http/HttpRequest.js.map +1 -0
  130. package/dist/http/HttpResponse.d.ts +20 -0
  131. package/dist/http/HttpResponse.d.ts.map +1 -0
  132. package/dist/http/HttpResponse.js +63 -0
  133. package/dist/http/HttpResponse.js.map +1 -0
  134. package/dist/http/adapters/expressAdapter.d.ts +17 -0
  135. package/dist/http/adapters/expressAdapter.d.ts.map +1 -0
  136. package/dist/http/adapters/expressAdapter.js +91 -0
  137. package/dist/http/adapters/expressAdapter.js.map +1 -0
  138. package/dist/http/adapters/fastifyAdapter.d.ts +17 -0
  139. package/dist/http/adapters/fastifyAdapter.d.ts.map +1 -0
  140. package/dist/http/adapters/fastifyAdapter.js +91 -0
  141. package/dist/http/adapters/fastifyAdapter.js.map +1 -0
  142. package/dist/http/adapters/httpAdapter.d.ts +14 -0
  143. package/dist/http/adapters/httpAdapter.d.ts.map +1 -0
  144. package/dist/http/adapters/httpAdapter.js +3 -0
  145. package/dist/http/adapters/httpAdapter.js.map +1 -0
  146. package/dist/http/client/httpClient.d.ts +51 -0
  147. package/dist/http/client/httpClient.d.ts.map +1 -0
  148. package/dist/http/client/httpClient.js +163 -0
  149. package/dist/http/client/httpClient.js.map +1 -0
  150. package/dist/index.d.ts +22 -0
  151. package/dist/index.d.ts.map +1 -0
  152. package/dist/index.js +65 -0
  153. package/dist/index.js.map +1 -0
  154. package/dist/injectable.js +12 -0
  155. package/dist/interceptor.js +44 -0
  156. package/dist/interceptors/Interceptor.d.ts +4 -0
  157. package/dist/interceptors/Interceptor.d.ts.map +1 -0
  158. package/dist/interceptors/Interceptor.js +3 -0
  159. package/dist/interceptors/Interceptor.js.map +1 -0
  160. package/dist/interceptors/applyInterceptor.d.ts +5 -0
  161. package/dist/interceptors/applyInterceptor.d.ts.map +1 -0
  162. package/dist/interceptors/applyInterceptor.js +22 -0
  163. package/dist/interceptors/applyInterceptor.js.map +1 -0
  164. package/dist/lifecycle/lifecycle.d.ts +23 -0
  165. package/dist/lifecycle/lifecycle.d.ts.map +1 -0
  166. package/dist/lifecycle/lifecycle.js +3 -0
  167. package/dist/lifecycle/lifecycle.js.map +1 -0
  168. package/dist/main.js +6 -0
  169. package/dist/metadata.js +83 -0
  170. package/dist/middleware/middleware.d.ts +14 -0
  171. package/dist/middleware/middleware.d.ts.map +1 -0
  172. package/dist/middleware/middleware.js +41 -0
  173. package/dist/middleware/middleware.js.map +1 -0
  174. package/dist/middleware/type.d.ts +4 -0
  175. package/dist/middleware/type.d.ts.map +1 -0
  176. package/dist/middleware/type.js +3 -0
  177. package/dist/middleware/type.js.map +1 -0
  178. package/dist/paramRegistry.js +4 -0
  179. package/dist/request/createMethodDecorator.js +22 -0
  180. package/dist/request/createParamDecorator.js +46 -0
  181. package/dist/request/interceptor.js +44 -0
  182. package/dist/request/metadata.js +83 -0
  183. package/dist/request/paramRegistry.js +4 -0
  184. package/dist/request/resolveHandlerArgument.js +108 -0
  185. package/dist/request/routeMatch.js +24 -0
  186. package/dist/request/routeRegistry.js +107 -0
  187. package/dist/request/utils/normalizePath.js +16 -0
  188. package/dist/request/validation/rule.js +44 -0
  189. package/dist/request/validation/validationErrorException.js +8 -0
  190. package/dist/resolveHandlerArgument.js +33 -0
  191. package/dist/routeMatch.js +24 -0
  192. package/dist/routeRegistry.js +108 -0
  193. package/dist/routes.js +1 -0
  194. package/dist/routing/metadata.d.ts +19 -0
  195. package/dist/routing/metadata.d.ts.map +1 -0
  196. package/dist/routing/metadata.js +65 -0
  197. package/dist/routing/metadata.js.map +1 -0
  198. package/dist/routing/metadataKeys.d.ts +22 -0
  199. package/dist/routing/metadataKeys.d.ts.map +1 -0
  200. package/dist/routing/metadataKeys.js +19 -0
  201. package/dist/routing/metadataKeys.js.map +1 -0
  202. package/dist/routing/paramTypes.d.ts +18 -0
  203. package/dist/routing/paramTypes.d.ts.map +1 -0
  204. package/dist/routing/paramTypes.js +3 -0
  205. package/dist/routing/paramTypes.js.map +1 -0
  206. package/dist/routing/routeRegistry.d.ts +31 -0
  207. package/dist/routing/routeRegistry.d.ts.map +1 -0
  208. package/dist/routing/routeRegistry.js +99 -0
  209. package/dist/routing/routeRegistry.js.map +1 -0
  210. package/dist/simulateRequest.js +52 -0
  211. package/dist/utils/decoratorCheck.d.ts +3 -0
  212. package/dist/utils/decoratorCheck.d.ts.map +1 -0
  213. package/dist/utils/decoratorCheck.js +37 -0
  214. package/dist/utils/decoratorCheck.js.map +1 -0
  215. package/dist/utils/log.d.ts +7 -0
  216. package/dist/utils/log.d.ts.map +1 -0
  217. package/dist/utils/log.js +20 -0
  218. package/dist/utils/log.js.map +1 -0
  219. package/dist/utils/metadataKey.d.ts +9 -0
  220. package/dist/utils/metadataKey.d.ts.map +1 -0
  221. package/dist/utils/metadataKey.js +28 -0
  222. package/dist/utils/metadataKey.js.map +1 -0
  223. package/dist/utils/normalizePath.js +16 -0
  224. package/dist/validation/resolveHandlerArgument.d.ts +10 -0
  225. package/dist/validation/resolveHandlerArgument.d.ts.map +1 -0
  226. package/dist/validation/resolveHandlerArgument.js +112 -0
  227. package/dist/validation/resolveHandlerArgument.js.map +1 -0
  228. package/dist/validation/rule.d.ts +35 -0
  229. package/dist/validation/rule.d.ts.map +1 -0
  230. package/dist/validation/rule.js +45 -0
  231. package/dist/validation/rule.js.map +1 -0
  232. package/dist/validation/validationErrorException.d.ts +6 -0
  233. package/dist/validation/validationErrorException.d.ts.map +1 -0
  234. package/dist/validation/validationErrorException.js +9 -0
  235. package/dist/validation/validationErrorException.js.map +1 -0
  236. package/package.json +84 -0
package/LICENSE ADDED
File without changes
package/README.md ADDED
@@ -0,0 +1,556 @@
1
+ # mini-nest
2
+
3
+ A lightweight, NestJS-inspired BFF (Backend-for-Frontend) framework for Node.js.
4
+
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
6
+ [![Node.js](https://img.shields.io/badge/Node.js-18+-green.svg)](https://nodejs.org/)
7
+ [![Test Coverage](https://img.shields.io/badge/coverage-92.5%25-brightgreen.svg)]()
8
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
+
10
+ English | [中文](README-CN.md)
11
+
12
+ ## Features
13
+
14
+ - 🎯 **Decorator-based** - Familiar NestJS-style decorators
15
+ - 💉 **Dependency Injection** - Automatic constructor injection
16
+ - 🛡️ **Guards & Interceptors** - Request pipeline control
17
+ - ⚡ **AOP Decorators** - `@Cache`, `@Retry`, `@Timeout`, `@CircuitBreaker`
18
+ - 🔗 **HTTP Client** - Built-in client with `aggregate()` for BFF patterns
19
+ - 🌳 **Trie-based Routing** - Fast route matching with params
20
+ - 🔄 **Lifecycle Hooks** - `OnInit`, `OnDestroy`, etc.
21
+ - 📦 **Lightweight** - Minimal dependencies, ~65% Express performance, ~90% fastify performance
22
+
23
+
24
+ ## Design Boundaries
25
+
26
+ mini-nest is intentionally lightweight. Here's what it **does** and **doesn't** do:
27
+
28
+ | Feature | Status | Notes |
29
+ |---------|--------|-------|
30
+ | Singleton DI | ✅ | No request-scope support yet |
31
+ | Static routing | ✅ | No regex/wildcard patterns |
32
+ | JSON API | ✅ | No streaming/multipart built-in |
33
+ | Single-tenant | ✅ | Multi-tenant needs manual handling |
34
+ | Decorator-based | ✅ | No runtime route registration |
35
+
36
+ **Best for:**
37
+ - BFF (Backend-for-Frontend) layers
38
+ - Small to medium APIs
39
+ - Teams familiar with NestJS patterns
40
+ - Projects prioritizing simplicity over features
41
+
42
+ **Not ideal for:**
43
+ - Large monoliths needing module isolation
44
+ - Real-time streaming applications
45
+ - Multi-tenant SaaS (without custom work)
46
+ - Projects requiring request-scoped DI
47
+
48
+ ## Installation
49
+
50
+ ```bash
51
+ npm install mini-nest
52
+ ```
53
+
54
+ ## Quick Start
55
+
56
+ ```typescript
57
+ import 'reflect-metadata';
58
+ import { createMiniNestApp, Controller, Get, Injectable, Param } from 'mini-nest';
59
+
60
+ @Injectable()
61
+ class UserService {
62
+ getUser(id: string) {
63
+ return { id, name: 'Alice' };
64
+ }
65
+ }
66
+
67
+ @Controller('/api/users')
68
+ class UserController {
69
+ constructor(private userService: UserService) {}
70
+
71
+ @Get('/:id')
72
+ getUser(@Param('id') id: string) {
73
+ return this.userService.getUser(id);
74
+ }
75
+ }
76
+
77
+ const app = createMiniNestApp({
78
+ port: 3000,
79
+ controllers: [UserController],
80
+ });
81
+
82
+ app.listen(() => console.log('Server running on http://localhost:3000'));
83
+ ```
84
+
85
+ ## Documentation
86
+
87
+ ### Controllers & Routes
88
+
89
+ ```typescript
90
+ import { Controller, Get, Post, Put, Delete, Patch } from 'mini-nest';
91
+
92
+ @Controller('/api/users')
93
+ class UserController {
94
+ @Get('/')
95
+ findAll() {
96
+ return [];
97
+ }
98
+
99
+ @Get('/:id')
100
+ findOne(@Param('id') id: string) {
101
+ return { id };
102
+ }
103
+
104
+ @Post('/')
105
+ create(@Body() data: any) {
106
+ return data;
107
+ }
108
+
109
+ @Put('/:id')
110
+ update(@Param('id') id: string, @Body() data: any) {
111
+ return { id, ...data };
112
+ }
113
+
114
+ @Delete('/:id')
115
+ remove(@Param('id') id: string) {
116
+ return { deleted: id };
117
+ }
118
+ }
119
+ ```
120
+
121
+ ### Parameter Decorators
122
+
123
+ ```typescript
124
+ import { Body, Query, Param, Header } from 'mini-nest';
125
+
126
+ @Controller('/api')
127
+ class ExampleController {
128
+ @Post('/search')
129
+ search(
130
+ @Body() body: any, // Full body
131
+ @Body('query') query: string, // Specific field
132
+ @Query('page') page: string, // Query param
133
+ @Param('id') id: string, // Route param
134
+ @Header('authorization') auth: string // Header
135
+ ) {
136
+ return { body, query, page, id, auth };
137
+ }
138
+ }
139
+ ```
140
+
141
+ ### Dependency Injection
142
+
143
+ ```typescript
144
+ import { Injectable } from 'mini-nest';
145
+
146
+ @Injectable()
147
+ class DatabaseService {
148
+ query(sql: string) {
149
+ return [{ id: 1 }];
150
+ }
151
+ }
152
+
153
+ @Injectable()
154
+ class UserRepository {
155
+ constructor(private db: DatabaseService) {}
156
+
157
+ findAll() {
158
+ return this.db.query('SELECT * FROM users');
159
+ }
160
+ }
161
+
162
+ @Injectable()
163
+ class UserService {
164
+ constructor(private repo: UserRepository) {}
165
+
166
+ getUsers() {
167
+ return this.repo.findAll();
168
+ }
169
+ }
170
+ ```
171
+
172
+ ### Guards
173
+
174
+ ```typescript
175
+ import { Injectable, Guard, UseGuard, ExecutionContext } from 'mini-nest';
176
+
177
+ @Injectable()
178
+ class AuthGuard implements Guard {
179
+ canActivate(ctx: ExecutionContext): boolean {
180
+ const request = ctx.getRequest();
181
+ const token = request.header('authorization');
182
+ return token === 'Bearer valid-token';
183
+ }
184
+ }
185
+
186
+ @Controller('/api/admin')
187
+ class AdminController {
188
+ @Get('/dashboard')
189
+ @UseGuard([AuthGuard])
190
+ getDashboard() {
191
+ return { data: 'secret' };
192
+ }
193
+ }
194
+ ```
195
+
196
+ ### Interceptors
197
+
198
+ ```typescript
199
+ import { Injectable, Interceptor, UseInterceptor } from 'mini-nest';
200
+
201
+ @Injectable()
202
+ class LoggingInterceptor implements Interceptor {
203
+ async intercept(next: () => Promise<unknown>) {
204
+ console.log('Before...');
205
+ const result = await next();
206
+ console.log('After...');
207
+ return result;
208
+ }
209
+ }
210
+
211
+ @Injectable()
212
+ class TransformInterceptor implements Interceptor {
213
+ async intercept(next: () => Promise<unknown>) {
214
+ const result = await next();
215
+ return { data: result, timestamp: Date.now() };
216
+ }
217
+ }
218
+
219
+ @Controller('/api')
220
+ @UseInterceptor(LoggingInterceptor)
221
+ class ApiController {
222
+ @Get('/data')
223
+ @UseInterceptor(TransformInterceptor)
224
+ getData() {
225
+ return { message: 'Hello' };
226
+ }
227
+ }
228
+ ```
229
+
230
+ ### AOP Decorators
231
+
232
+ #### @Cache
233
+
234
+ ```typescript
235
+ import { Cache } from 'mini-nest';
236
+
237
+ @Injectable()
238
+ class DataService {
239
+ @Cache({ ttl: 60 }) // Cache for 60 seconds
240
+ getExpensiveData() {
241
+ return computeExpensiveOperation();
242
+ }
243
+
244
+ @Cache({ ttl: 300, key: 'custom-key' })
245
+ getWithCustomKey() {
246
+ return data;
247
+ }
248
+ }
249
+ ```
250
+
251
+ #### @Retry
252
+
253
+ ```typescript
254
+ import { Retry } from 'mini-nest';
255
+
256
+ @Injectable()
257
+ class ExternalApiService {
258
+ @Retry(3) // Retry up to 3 times with exponential backoff
259
+ async fetchData() {
260
+ return await fetch('https://api.example.com/data');
261
+ }
262
+ }
263
+ ```
264
+
265
+ #### @Timeout
266
+
267
+ ```typescript
268
+ import { Timeout } from 'mini-nest';
269
+
270
+ @Injectable()
271
+ class SlowService {
272
+ @Timeout(5000) // Timeout after 5 seconds
273
+ async slowOperation() {
274
+ return await longRunningTask();
275
+ }
276
+ }
277
+ ```
278
+
279
+ #### @CircuitBreaker
280
+
281
+ ```typescript
282
+ import { CircuitBreaker } from 'mini-nest';
283
+
284
+ @Injectable()
285
+ class RiskyService {
286
+ @CircuitBreaker({
287
+ failureThreshold: 5, // Open after 5 failures
288
+ resetTimeout: 30000 // Try again after 30s
289
+ })
290
+ async callExternalService() {
291
+ return await externalApi.call();
292
+ }
293
+ }
294
+ ```
295
+
296
+ ### HttpClient
297
+
298
+ Built-in HTTP client with retry, timeout, and aggregation support:
299
+
300
+ ```typescript
301
+ import { Injectable, HttpClient } from 'mini-nest';
302
+
303
+ @Injectable()
304
+ class ApiService {
305
+ constructor(private http: HttpClient) {}
306
+
307
+ async getUser(id: string) {
308
+ const res = await this.http.get(`https://api.example.com/users/${id}`);
309
+ return res.data;
310
+ }
311
+
312
+ async createUser(data: any) {
313
+ const res = await this.http.post('https://api.example.com/users', data);
314
+ return res.data;
315
+ }
316
+ }
317
+ ```
318
+
319
+ #### Aggregate (BFF Pattern)
320
+
321
+ Combine multiple API calls into a single response:
322
+
323
+ ```typescript
324
+ @Injectable()
325
+ class BffService {
326
+ constructor(private http: HttpClient) {}
327
+
328
+ async getUserProfile(userId: string) {
329
+ const { data, errors } = await this.http.aggregate({
330
+ baseUrl: 'https://api.example.com',
331
+ params: { id: userId },
332
+ sources: {
333
+ user: '/users/:id',
334
+ posts: '/users/:id/posts',
335
+ followers: '/users/:id/followers',
336
+ },
337
+ output: (sources) => ({
338
+ id: sources.user.id,
339
+ name: sources.user.name,
340
+ postCount: sources.posts.length,
341
+ followerCount: sources.followers.length,
342
+ }),
343
+ timeout: 5000,
344
+ partial: true, // Continue even if some requests fail
345
+ });
346
+
347
+ return data;
348
+ }
349
+ }
350
+ ```
351
+
352
+ ### Lifecycle Hooks
353
+
354
+ ```typescript
355
+ import { Injectable, OnInit, OnDestroy } from 'mini-nest';
356
+
357
+ @Injectable()
358
+ class DatabaseService implements OnInit, OnDestroy {
359
+ private connection: any;
360
+
361
+ OnInit() {
362
+ console.log('Connecting to database...');
363
+ this.connection = createConnection();
364
+ }
365
+
366
+ OnDestroy() {
367
+ console.log('Closing database connection...');
368
+ this.connection.close();
369
+ }
370
+ }
371
+ ```
372
+
373
+ ### Validation
374
+
375
+ ```typescript
376
+ import { Query, Body, rule } from 'mini-nest';
377
+
378
+ @Controller('/api')
379
+ class ValidationController {
380
+ @Get('/search')
381
+ search(
382
+ @Query({
383
+ key: 'page',
384
+ validator: rule().required().min(1)
385
+ })
386
+ page: number,
387
+
388
+ @Query({
389
+ key: 'email',
390
+ validator: rule().required().pattern(/^[\w-]+@[\w-]+\.\w+$/)
391
+ })
392
+ email: string
393
+ ) {
394
+ return { page, email };
395
+ }
396
+
397
+ @Post('/users')
398
+ createUser(
399
+ @Body({
400
+ key: 'name',
401
+ validator: rule().required().minLength(2).maxLength(50)
402
+ })
403
+ name: string
404
+ ) {
405
+ return { name };
406
+ }
407
+ }
408
+ ```
409
+
410
+ ### Exception Handling
411
+
412
+ ```typescript
413
+ import {
414
+ NotFoundException,
415
+ BadRequestException,
416
+ UnauthorizedException,
417
+ ForbiddenException,
418
+ InternalServerErrorException
419
+ } from 'mini-nest';
420
+
421
+ @Controller('/api/users')
422
+ class UserController {
423
+ @Get('/:id')
424
+ getUser(@Param('id') id: string) {
425
+ const user = findUser(id);
426
+ if (!user) {
427
+ throw new NotFoundException(`User ${id} not found`);
428
+ }
429
+ return user;
430
+ }
431
+ }
432
+ ```
433
+
434
+ Custom exception filter:
435
+
436
+ ```typescript
437
+ import { ExceptionFilter, ExecutionContext } from 'mini-nest';
438
+
439
+ class CustomExceptionFilter implements ExceptionFilter {
440
+ canHandle(exception: unknown): boolean {
441
+ return exception instanceof CustomError;
442
+ }
443
+
444
+ catch(exception: CustomError, context: ExecutionContext) {
445
+ const response = context.getResponse();
446
+ response.status(400).json({
447
+ error: 'CustomError',
448
+ message: exception.message,
449
+ });
450
+ }
451
+ }
452
+ ```
453
+
454
+ ## Configuration
455
+
456
+ ```typescript
457
+ const app = createMiniNestApp({
458
+ port: 3000,
459
+ adapter: 'express' | 'fastify',
460
+ controllers: [UserController, PostController],
461
+ https: { // Optional HTTPS
462
+ key: '/path/to/key.pem',
463
+ cert: '/path/to/cert.pem',
464
+ },
465
+ });
466
+ ```
467
+
468
+ ## Benchmark
469
+
470
+ Compared against Express and Fastify (10s, 100 connections):
471
+
472
+
473
+ | Framework | Requests | Relative |
474
+ |-----------|----------|----------|
475
+ | Fastify (raw) | 2,544,032 | 100% |
476
+ | mini-nest + Fastify | 2,290,571 | 90.0% |
477
+ | Express (raw) | 1,742,452 | 68.5% |
478
+ | mini-nest + Express | 1,739,737 | 68.4% |
479
+
480
+ mini-nest performs comparably to Express while providing:
481
+ - Dependency Injection
482
+ - Decorator-based routing
483
+ - AOP features (Cache, Retry, Timeout, CircuitBreaker)
484
+ - Guards & Interceptors
485
+ - Built-in HTTP client with aggregation
486
+
487
+ Run benchmarks locally:
488
+
489
+ ```bash
490
+ npm run benchmark
491
+ ```
492
+
493
+ ## Project Structure
494
+
495
+ ```
496
+ src/
497
+ ├── core/
498
+ │ ├── app/ # Application bootstrap
499
+ │ ├── container/ # DI container
500
+ │ └── pipeline/ # Request pipeline
501
+ ├── decorators/
502
+ │ ├── http/ # @Controller, @Get, @Post, etc.
503
+ │ └── aop/ # @Cache, @Retry, @Timeout, @CircuitBreaker
504
+ ├── http/
505
+ │ ├── adapters/ # Express and Fastify adapter
506
+ │ └── client/ # HttpClient
507
+ ├── guards/ # Guard system
508
+ ├── interceptors/ # Interceptor system
509
+ ├── exceptions/ # Exception handling
510
+ ├── routing/ # Trie-based router
511
+ ├── validation/ # Parameter validation
512
+ └── lifecycle/ # Lifecycle hooks
513
+ ```
514
+
515
+ ## Requirements
516
+
517
+ - Node.js 18+
518
+ - TypeScript 5.0+
519
+ - `experimentalDecorators: true`
520
+ - `emitDecoratorMetadata: true`
521
+
522
+ tsconfig.json:
523
+
524
+ ```json
525
+ {
526
+ "compilerOptions": {
527
+ "target": "ES2020",
528
+ "module": "CommonJS",
529
+ "experimentalDecorators": true, //very important
530
+ "emitDecoratorMetadata": true, //very important
531
+ "esModuleInterop": true,
532
+ "strict": true
533
+ }
534
+ }
535
+ ```
536
+
537
+ ## Testing
538
+
539
+ ```bash
540
+ # Run tests
541
+ npm test
542
+
543
+ # Run with coverage
544
+ npm run test:coverage
545
+ ```
546
+
547
+ ## Roadmap
548
+
549
+ - [ ] Module system (`@Module()`)
550
+ - [ ] WebSocket support
551
+ - [ ] OpenAPI/Swagger generation
552
+ - [ ] CLI tool
553
+
554
+ ## License
555
+
556
+ MIT
@@ -0,0 +1,17 @@
1
+ import { Constructor } from "../core/container/container";
2
+ import { CacheOptions } from "../decorators/aop/Cache";
3
+ import { CircuitBreakerOptions } from "../decorators/aop/CircuitBreaker";
4
+ export interface AOPContext {
5
+ target: Object;
6
+ method: Function;
7
+ args: any[];
8
+ methodName: string;
9
+ className: string;
10
+ timeout?: number;
11
+ retry?: number;
12
+ cache?: CacheOptions;
13
+ circuitBreaker?: CircuitBreakerOptions;
14
+ }
15
+ export declare function wrapWithAOP<T extends object>(instance: T, token: Constructor<T>): T;
16
+ export declare function executeWithAOP(ctx: AOPContext): Promise<any>;
17
+ //# sourceMappingURL=aopWrapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aopWrapper.d.ts","sourceRoot":"","sources":["../../src/aop/aopWrapper.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAGzE,MAAM,WAAW,UAAU;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,QAAQ,CAAC;IACjB,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,cAAc,CAAC,EAAE,qBAAqB,CAAC;CAC1C;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAkCnF;AAID,wBAAsB,cAAc,CAAC,GAAG,EAAE,UAAU,gBA8DnD"}
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.wrapWithAOP = wrapWithAOP;
4
+ exports.executeWithAOP = executeWithAOP;
5
+ const cacheManager_1 = require("../cache/cacheManager");
6
+ const circuitBreakerManager_1 = require("../circuitBreaker/circuitBreakerManager");
7
+ const circuitOpenError_1 = require("../circuitBreaker/circuitOpenError");
8
+ const metadataKeys_1 = require("../routing/metadataKeys");
9
+ function wrapWithAOP(instance, token) {
10
+ const proto = token.prototype;
11
+ return new Proxy(instance, {
12
+ get(target, prop, receiver) {
13
+ const original = Reflect.get(target, prop, receiver);
14
+ if (typeof original !== 'function') {
15
+ return original;
16
+ }
17
+ const propKey = String(prop);
18
+ const timeout = metadataKeys_1.TIMEOUT.get(proto, propKey);
19
+ const cache = metadataKeys_1.CACHE.get(proto, propKey);
20
+ const retry = metadataKeys_1.RETRY.get(proto, propKey);
21
+ const circuitBreaker = metadataKeys_1.CIRCUITBREAKER.get(proto, propKey);
22
+ if (timeout === undefined && cache === undefined && retry === undefined && circuitBreaker === undefined) {
23
+ return original.bind(target);
24
+ }
25
+ return async function (...args) {
26
+ return executeWithAOP({
27
+ target,
28
+ method: original,
29
+ args,
30
+ methodName: propKey,
31
+ className: token.name,
32
+ timeout,
33
+ cache,
34
+ retry,
35
+ circuitBreaker
36
+ });
37
+ };
38
+ }
39
+ });
40
+ }
41
+ async function executeWithAOP(ctx) {
42
+ const { target, method, args, timeout, className, methodName, retry, cache, circuitBreaker } = ctx;
43
+ const circuitKey = `${className}-${methodName}`;
44
+ if (circuitBreaker) {
45
+ const { failureThreshold = 5, resetTimeout = 30000 } = circuitBreaker;
46
+ const { allowed, reason } = circuitBreakerManager_1.circuitBreakerManager.canExecute(circuitKey, resetTimeout);
47
+ if (!allowed) {
48
+ throw new circuitOpenError_1.CircuitOpenError(reason);
49
+ }
50
+ }
51
+ const cacheKey = cache
52
+ ? (cache.key ?? `${className}:${methodName}:${JSON.stringify(args)}`)
53
+ : null;
54
+ if (cacheKey && cacheManager_1.cacheManager.has(cacheKey)) {
55
+ return cacheManager_1.cacheManager.get(cacheKey);
56
+ }
57
+ //build execution function
58
+ const execute = async () => {
59
+ const promise = method.apply(target, args);
60
+ if (timeout === undefined) {
61
+ return promise;
62
+ }
63
+ return withTimeout(promise, timeout);
64
+ };
65
+ try {
66
+ const result = retry !== undefined ? await withRetry(execute, retry) : await execute();
67
+ if (circuitBreaker) {
68
+ circuitBreakerManager_1.circuitBreakerManager.recordSuccess(circuitKey);
69
+ }
70
+ if (cacheKey && cache) {
71
+ cacheManager_1.cacheManager.set(cacheKey, result, cache.ttl);
72
+ }
73
+ return result;
74
+ }
75
+ catch (error) {
76
+ if (circuitBreaker) {
77
+ const { failureThreshold = 5 } = circuitBreaker;
78
+ circuitBreakerManager_1.circuitBreakerManager.recordFailure(circuitKey, failureThreshold);
79
+ }
80
+ throw error;
81
+ }
82
+ }
83
+ async function withTimeout(promise, ms) {
84
+ return Promise.race([
85
+ promise,
86
+ new Promise((_, reject) => {
87
+ return setTimeout(() => reject(new Error(`Request timeout after ${ms}ms`)), ms);
88
+ })
89
+ ]);
90
+ }
91
+ function delay(ms) {
92
+ return new Promise(resolve => setTimeout(resolve, ms));
93
+ }
94
+ async function withRetry(fn, maxRetryTime) {
95
+ let lastError;
96
+ for (let attempt = 0; attempt <= maxRetryTime; attempt++) {
97
+ try {
98
+ return await fn();
99
+ }
100
+ catch (error) {
101
+ if (!(error instanceof Error)) {
102
+ error = new Error(String(error));
103
+ }
104
+ lastError = error;
105
+ if (attempt < maxRetryTime) {
106
+ await delay(100 * Math.pow(2, attempt));
107
+ }
108
+ }
109
+ }
110
+ throw lastError;
111
+ }
112
+ //# sourceMappingURL=aopWrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aopWrapper.js","sourceRoot":"","sources":["../../src/aop/aopWrapper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ declare class CacheManager {
2
+ private cache;
3
+ get<T>(key: string | symbol): T | undefined;
4
+ set(key: string | symbol, value: any, ttlSeconds: number): void;
5
+ delete(key: string | symbol): void;
6
+ clear(): void;
7
+ has(key: string | symbol): boolean;
8
+ }
9
+ export declare const cacheManager: CacheManager;
10
+ export {};
11
+ //# sourceMappingURL=cacheManager.d.ts.map