exisjs 0.7.12 → 0.7.13

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 (116) hide show
  1. package/README.md +195 -39
  2. package/dist/config/types.d.ts +2 -2
  3. package/dist/config/types.d.ts.map +1 -1
  4. package/dist/decorators/params.d.ts +21 -0
  5. package/dist/decorators/params.d.ts.map +1 -1
  6. package/dist/decorators/params.js +21 -1
  7. package/dist/decorators/params.js.map +1 -1
  8. package/dist/di/container.d.ts +15 -6
  9. package/dist/di/container.d.ts.map +1 -1
  10. package/dist/di/container.js +26 -3
  11. package/dist/di/container.js.map +1 -1
  12. package/dist/di/index.d.ts +1 -1
  13. package/dist/di/index.d.ts.map +1 -1
  14. package/dist/di/index.js.map +1 -1
  15. package/dist/di/inject.d.ts +2 -1
  16. package/dist/di/inject.d.ts.map +1 -1
  17. package/dist/di/inject.js +2 -1
  18. package/dist/di/inject.js.map +1 -1
  19. package/dist/docs/BENCHMARKS.md +4 -4
  20. package/dist/docs/content/README.md +43 -44
  21. package/dist/docs/content/boundaries.mdx +138 -61
  22. package/dist/docs/content/cli/commands.mdx +151 -128
  23. package/dist/docs/content/cli/overview.mdx +61 -47
  24. package/dist/docs/content/cli/usage.mdx +107 -30
  25. package/dist/docs/content/config.mdx +145 -158
  26. package/dist/docs/content/controllers.mdx +193 -464
  27. package/dist/docs/content/cron.mdx +183 -69
  28. package/dist/docs/content/decorators.mdx +449 -154
  29. package/dist/docs/content/errors.mdx +202 -305
  30. package/dist/docs/content/exisjs-mcp.mdx +59 -49
  31. package/dist/docs/content/installation.mdx +243 -157
  32. package/dist/docs/content/introduction.mdx +220 -48
  33. package/dist/docs/content/middleware.mdx +171 -497
  34. package/dist/docs/content/modules.mdx +141 -83
  35. package/dist/docs/content/providers.mdx +216 -180
  36. package/dist/docs/content/requests.mdx +168 -488
  37. package/dist/docs/content/responses.mdx +200 -533
  38. package/dist/docs/content/routing.mdx +235 -598
  39. package/dist/docs/content/security.mdx +233 -57
  40. package/dist/docs/content/structure.mdx +151 -240
  41. package/dist/docs/content/swagger.mdx +192 -91
  42. package/dist/docs/content/testing.mdx +205 -74
  43. package/dist/docs/content/validation.mdx +479 -341
  44. package/dist/docs/reference/README.md +34 -2
  45. package/dist/docs/reference/config.mdx +277 -75
  46. package/dist/docs/reference/context.mdx +197 -106
  47. package/dist/docs/reference/cron.mdx +266 -152
  48. package/dist/docs/reference/database.mdx +135 -53
  49. package/dist/docs/reference/decorators.mdx +234 -116
  50. package/dist/docs/reference/di.mdx +146 -112
  51. package/dist/docs/reference/error.mdx +155 -178
  52. package/dist/docs/reference/exis.mdx +141 -70
  53. package/dist/docs/reference/logger.mdx +110 -126
  54. package/dist/docs/reference/middleware.mdx +235 -158
  55. package/dist/docs/reference/plugin.mdx +136 -64
  56. package/dist/docs/reference/router.mdx +275 -293
  57. package/dist/docs/reference/sanitize.mdx +91 -87
  58. package/dist/docs/reference/swagger.mdx +280 -100
  59. package/dist/docs/reference/testing.mdx +223 -89
  60. package/dist/docs/reference/validator.mdx +194 -206
  61. package/dist/lib/start-server.js +3 -8
  62. package/dist/lib/start-server.js.map +1 -1
  63. package/dist/module/module.d.ts +6 -3
  64. package/dist/module/module.d.ts.map +1 -1
  65. package/dist/module/module.js +17 -3
  66. package/dist/module/module.js.map +1 -1
  67. package/dist/router/controller-registrar.d.ts.map +1 -1
  68. package/dist/router/controller-registrar.js +26 -4
  69. package/dist/router/controller-registrar.js.map +1 -1
  70. package/dist/server/app.d.ts +3 -1
  71. package/dist/server/app.d.ts.map +1 -1
  72. package/dist/server/app.js +24 -8
  73. package/dist/server/app.js.map +1 -1
  74. package/dist/server/request.d.ts +2 -0
  75. package/dist/server/request.d.ts.map +1 -1
  76. package/dist/server/request.js +11 -0
  77. package/dist/server/request.js.map +1 -1
  78. package/dist/swagger/generator.d.ts.map +1 -1
  79. package/dist/swagger/generator.js +130 -10
  80. package/dist/swagger/generator.js.map +1 -1
  81. package/dist/telemetry/index.d.ts +12 -0
  82. package/dist/telemetry/index.d.ts.map +1 -0
  83. package/dist/telemetry/index.js +121 -0
  84. package/dist/telemetry/index.js.map +1 -0
  85. package/dist/testing/client.d.ts +1 -0
  86. package/dist/testing/client.d.ts.map +1 -1
  87. package/dist/testing/client.js +4 -1
  88. package/dist/testing/client.js.map +1 -1
  89. package/dist/testing/index.d.ts.map +1 -1
  90. package/dist/testing/index.js +11 -4
  91. package/dist/testing/index.js.map +1 -1
  92. package/dist/validator/builder.d.ts +26 -1
  93. package/dist/validator/builder.d.ts.map +1 -1
  94. package/dist/validator/builder.js +31 -0
  95. package/dist/validator/builder.js.map +1 -1
  96. package/dist/validator/engine.d.ts +23 -0
  97. package/dist/validator/engine.d.ts.map +1 -1
  98. package/dist/validator/engine.js +228 -31
  99. package/dist/validator/engine.js.map +1 -1
  100. package/dist/validator/index.d.ts +8 -4
  101. package/dist/validator/index.d.ts.map +1 -1
  102. package/dist/validator/index.js +4 -1
  103. package/dist/validator/index.js.map +1 -1
  104. package/dist/validator/tex-types.d.ts +15 -0
  105. package/dist/validator/tex-types.d.ts.map +1 -1
  106. package/dist/validator/tex-types.js +102 -0
  107. package/dist/validator/tex-types.js.map +1 -1
  108. package/dist/validator/tex.d.ts +1 -1
  109. package/dist/validator/tex.d.ts.map +1 -1
  110. package/dist/validator/tex.js +3 -1
  111. package/dist/validator/tex.js.map +1 -1
  112. package/dist/validator/types.d.ts +1 -0
  113. package/dist/validator/types.d.ts.map +1 -1
  114. package/package.json +46 -14
  115. package/dist/docs/content/dataloader.mdx +0 -170
  116. package/dist/docs/reference/dataloader.mdx +0 -91
@@ -1,36 +1,70 @@
1
- # Decorators
2
-
3
- ExisJS provides standard ECMAScript decorators for developers who prefer the Object-Oriented Programming (OOP) paradigm.
1
+ ---
2
+ title: "Decorators"
3
+ description: "Reference guide for Object-Oriented decorators in ExisJS covering controllers, routing, parameters, pipes, middleware, security, background tasks, caching, transactions, OpenAPI, and JavaScript helpers."
4
+ nextUrl: "/docs/requests"
5
+ nextTitle: "Requests"
6
+ ---
4
7
 
5
- Because ExisJS does not rely on reflection libraries like `reflect-metadata`, these decorators are fast, lightweight, and memory-efficient.
8
+ # Decorators
6
9
 
7
- All decorators can be imported directly from `exisjs/decorators`.
10
+ ExisJS provides ECMAScript and TypeScript decorators for building backend applications in the **Object-Oriented Programming (OOP)** paradigm.
11
+
12
+ Decorators attach metadata to classes and methods using JavaScript `Symbol` keys. They do not depend on external reflection packages such as `reflect-metadata`.
13
+
14
+ Import decorators directly from `exisjs/decorators`:
15
+
16
+ ```typescript
17
+ import {
18
+ Controller,
19
+ Get,
20
+ Post,
21
+ Put,
22
+ Patch,
23
+ Delete,
24
+ Body,
25
+ Param,
26
+ Query,
27
+ Headers,
28
+ Injectable,
29
+ Use,
30
+ UseGuards,
31
+ UsePipes,
32
+ ParseIntPipe,
33
+ Cron,
34
+ Interval,
35
+ Cacheable,
36
+ Transactional
37
+ } from 'exisjs/decorators'
38
+ ```
8
39
 
9
40
  ---
10
41
 
11
42
  ## 1. Application & Boundary Decorators
12
43
 
13
- ExisJS allows you to configure your root application and directory-scoped boundaries using decorators.
14
-
15
- ### `@Server(config)`
16
- Used in your entry point (e.g. `src/http/server.ts`) to configure plugins and root providers in an OOP style:
44
+ ### `@Server(options)`
45
+ Configures root providers, plugins, cron schedules, and lifecycle hooks on an application entry class:
17
46
 
18
47
  ```typescript filename="src/http/server.ts"
19
48
  import { Server } from 'exisjs/decorators'
20
49
  import type { App } from 'exisjs'
21
50
 
22
51
  @Server({
23
- providers: [['Logger', { useValue: console }]]
52
+ providers: [['LOGGER_TOKEN', { useValue: console }]],
53
+ plugins: []
24
54
  })
25
55
  export default class RootServer {
26
56
  async onStart(app: App) {
27
57
  console.log('Server started')
28
58
  }
59
+
60
+ async onClose(app: App) {
61
+ console.log('Server stopping')
62
+ }
29
63
  }
30
64
  ```
31
65
 
32
- ### `@Boundary(config)`
33
- Used inside any folder (e.g. `src/http/admin/boundary.ts`) to configure folder-scoped CORS, headers, exclusions, and DI providers:
66
+ ### `@Boundary(options)`
67
+ Configures directory-scoped middleware chains, CORS policies, response headers, and route exclusions inside `boundary.ts`:
34
68
 
35
69
  ```typescript filename="src/http/admin/boundary.ts"
36
70
  import { Boundary } from 'exisjs/decorators'
@@ -38,20 +72,20 @@ import type { Request, Response, Next, BoundaryContext } from 'exisjs/router'
38
72
  import { UnauthorizedError } from 'exisjs/error'
39
73
 
40
74
  @Boundary({
41
- cors: { origin: 'https://admin.yourdomain.com', credentials: true },
75
+ cors: { origin: 'https://admin.example.com', credentials: true },
42
76
  headers: { 'X-Admin-Api-Version': '2.0' },
43
- exclude: ['/admin/health'],
77
+ exclude: ['/admin/health']
44
78
  })
45
79
  export default class AdminBoundary {
46
- // Chain step auto-detected by (req, res, next) signature
47
- requireAuth(req: Request, res: Response, next: Next) {
80
+ // Middleware handler identified by (req, res, next) signature
81
+ requireApiKey(req: Request, res: Response, next: Next) {
48
82
  if (!req.headers['x-api-key']) {
49
- throw new UnauthorizedError('Missing API key')
83
+ throw new UnauthorizedError('Missing admin API key')
50
84
  }
51
85
  next()
52
86
  }
53
87
 
54
- // Request wrapper auto-detected by handle(ctx, next)
88
+ // Request wrapper identified by handle(ctx, next) signature
55
89
  async handle(ctx: BoundaryContext, next: Next) {
56
90
  const start = Date.now()
57
91
  const result = await next()
@@ -63,9 +97,10 @@ export default class AdminBoundary {
63
97
 
64
98
  ---
65
99
 
66
- ## 2. Controllers & Routing
100
+ ## 2. Controller & Routing Decorators
67
101
 
68
- To map a class to a route prefix, use the `@Controller()` decorator. HTTP method decorators then map class methods to specific endpoints.
102
+ ### `@Controller(prefixOrOptions)`
103
+ Registers a class as a route controller. Accepts a string path prefix or a configuration object:
69
104
 
70
105
  ```typescript filename="src/http/users/route.ts"
71
106
  import { Controller, Get, Post, Put, Patch, Delete } from 'exisjs/decorators'
@@ -80,7 +115,7 @@ export default class UsersController {
80
115
 
81
116
  @Get('/:id', UserParamsSchema)
82
117
  async getUser() {
83
- return { id: 1 }
118
+ return { id: 1, name: 'Alice' }
84
119
  }
85
120
 
86
121
  @Post('/', CreateUserSchema)
@@ -89,155 +124,324 @@ export default class UsersController {
89
124
  }
90
125
 
91
126
  @Delete('/:id')
92
- async deleteUser() {}
127
+ async deleteUser() {
128
+ return { deleted: true }
129
+ }
93
130
  }
94
131
  ```
95
132
 
96
- ### Available Method Decorators
97
- - `@Get(path?, schema?)`
98
- - `@Post(path?, schema?)`
99
- - `@Put(path?, schema?)`
100
- - `@Patch(path?, schema?)`
101
- - `@Delete(path?, schema?)`
102
- - `@Options(path?, schema?)`
103
- - `@Head(path?, schema?)`
104
- - `@Connect(path?, schema?)`
105
- - `@Trace(path?, schema?)`
106
- - `@All(path?, schema?)`
133
+ ### Routing Methods Table
134
+
135
+ | Decorator | HTTP Method | Arguments | Description |
136
+ | :--- | :--- | :--- | :--- |
137
+ | `@Get(path?, schema?)` | `GET` | `path?: string`, `schema?: RouteSchema` | Maps handler to GET requests. |
138
+ | `@Post(path?, schema?)` | `POST` | `path?: string`, `schema?: RouteSchema` | Maps handler to POST requests. |
139
+ | `@Put(path?, schema?)` | `PUT` | `path?: string`, `schema?: RouteSchema` | Maps handler to PUT requests. |
140
+ | `@Patch(path?, schema?)` | `PATCH` | `path?: string`, `schema?: RouteSchema` | Maps handler to PATCH requests. |
141
+ | `@Delete(path?, schema?)` | `DELETE` | `path?: string`, `schema?: RouteSchema` | Maps handler to DELETE requests. |
142
+ | `@Options(path?, schema?)` | `OPTIONS` | `path?: string`, `schema?: RouteSchema` | Maps handler to OPTIONS requests. |
143
+ | `@Head(path?, schema?)` | `HEAD` | `path?: string`, `schema?: RouteSchema` | Maps handler to HEAD requests. |
144
+ | `@All(path?, schema?)` | Any Verb | `path?: string`, `schema?: RouteSchema` | Catches any HTTP verb matching the path. |
145
+ | `@Connect(path?, schema?)` | `CONNECT` | `path?: string`, `schema?: RouteSchema` | Maps handler to CONNECT requests. |
146
+ | `@Trace(path?, schema?)` | `TRACE` | `path?: string`, `schema?: RouteSchema` | Maps handler to TRACE requests. |
147
+ | `@Query(path?, schema?)` | `QUERY` | `path?: string`, `schema?: RouteSchema` | Maps handler to RFC QUERY requests. |
107
148
 
108
149
  ---
109
150
 
110
- ## 3. Request Parameters
151
+ ## 3. Parameter Decorators
111
152
 
112
- Parameter decorators extract request properties and inject them directly into your method parameters:
153
+ Parameter decorators extract values from the incoming request and pass them into method arguments:
113
154
 
114
155
  ```typescript filename="src/http/users/route.ts"
115
- import { Controller, Post, Body, Param, Headers, Req, Res } from 'exisjs/decorators'
156
+ import {
157
+ Controller,
158
+ Post,
159
+ Body,
160
+ Param,
161
+ Query,
162
+ Headers,
163
+ Req,
164
+ Res,
165
+ Ip,
166
+ Session,
167
+ UploadedFile
168
+ } from 'exisjs/decorators'
169
+ import type { Request, Response, ExisFile } from 'exisjs'
116
170
 
117
171
  @Controller('/users')
118
172
  export default class UsersController {
119
- @Post('/:id')
120
- async update(
173
+ @Post('/:id/avatar')
174
+ async uploadAvatar(
121
175
  @Param('id') id: string,
122
- @Body() payload: any,
176
+ @Query('quality') quality: string,
177
+ @Body() metadata: any,
123
178
  @Headers('authorization') token: string,
124
- @Req() req: any,
125
- @Res() res: any
179
+ @UploadedFile() file: ExisFile,
180
+ @Ip() clientIp: string,
181
+ @Req() req: Request,
182
+ @Res({ passthrough: true }) res: Response
183
+ ) {
184
+ return { id, quality, metadata, clientIp, file: file?.filename }
185
+ }
186
+ }
187
+ ```
188
+
189
+ ### Parameter Decorators Reference
190
+
191
+ | Decorator | Injected Value | Example Usage |
192
+ | :--- | :--- | :--- |
193
+ | `@Body(fieldOrPipe?)` | Request body or specific property | `@Body() dto: CreateUserDto`, `@Body('email') email: string` |
194
+ | `@Param(nameOrPipe?)` | Path parameter string | `@Param('id', ParseIntPipe) id: number` |
195
+ | `@Query(nameOrPipe?)` / `@QueryParam(...)` | Parsed query parameter or query map | `@Query('search') search: string` |
196
+ | `@Headers(nameOrPipe?)` | Request header value (case-insensitive) | `@Headers('x-api-key') apiKey: string` |
197
+ | `@Cookies(nameOrPipe?)` / `@Cookie(...)` | Parsed cookie map or specific cookie string | `@Cookie('session_id') sessionId: string` |
198
+ | `@Req()` | The incoming `ExisRequest` instance | `@Req() req: Request` |
199
+ | `@Res(options?)` | The outgoing `ExisResponse` instance (`{ passthrough?: boolean }`) | `@Res({ passthrough: true }) res: Response` |
200
+ | `@Ip()` | Client IP address string (reads `trustProxy` setting) | `@Ip() ip: string` |
201
+ | `@Session()` | Active session state object | `@Session() session: any` |
202
+ | `@Next()` | The `next()` pipeline callback | `@Next() next: Next` |
203
+ | `@State(keyOrPipe?)` | Request context state | `@State('user') user: AuthUser` |
204
+ | `@UploadedFile(nameOrPipe?)` | Single parsed file object | `@UploadedFile('avatar') file: ExisFile` |
205
+ | `@UploadedFiles(nameOrPipe?)` | Array of parsed file objects | `@UploadedFiles('photos') files: ExisFile[]` |
206
+ | `@Fields(nameOrPipe?)` | Non-file multipart form fields | `@Fields() fields: Record<string, any>` |
207
+ | `@HostParam(nameOrPipe?)` | Subdomain or host parameter | `@HostParam('subdomain') domain: string` |
208
+ | `@AppCtx()` | Root Application instance | `@AppCtx() app: App` |
209
+
210
+ ### Custom Parameter Decorators (`createParamDecorator`)
211
+
212
+ ```typescript
213
+ import { createParamDecorator } from 'exisjs/decorators'
214
+
215
+ export const CurrentUser = createParamDecorator((data: string | undefined, ctx) => {
216
+ return data ? ctx.req.user?.[data] : ctx.req.user
217
+ })
218
+ ```
219
+
220
+ ---
221
+
222
+ ## 4. Transformation & Validation Pipes
223
+
224
+ Pipes transform parameter inputs or validate input types:
225
+
226
+ ```typescript filename="src/http/users/route.ts"
227
+ import {
228
+ Controller,
229
+ Get,
230
+ Param,
231
+ Query,
232
+ UsePipes,
233
+ ParseIntPipe,
234
+ ParseFloatPipe,
235
+ ParseBoolPipe,
236
+ ParseUUIDPipe,
237
+ ParseArrayPipe,
238
+ ValidationPipe
239
+ } from 'exisjs/decorators'
240
+
241
+ @Controller('/users')
242
+ @UsePipes(new ValidationPipe({ transform: true }))
243
+ export default class UsersController {
244
+ @Get('/:id')
245
+ async getUser(@Param('id', ParseIntPipe) id: number) {
246
+ return { id, isNumber: typeof id === 'number' }
247
+ }
248
+
249
+ @Get('/filter')
250
+ async filter(
251
+ @Query('page', ParseIntPipe) page: number,
252
+ @Query('active', ParseBoolPipe) active: boolean,
253
+ @Query('tags', new ParseArrayPipe({ separator: ',' })) tags: string[]
126
254
  ) {
127
- return { success: true, id, payload }
255
+ return { page, active, tags }
256
+ }
257
+
258
+ @Get('/uuid/:uuid')
259
+ async getByUuid(@Param('uuid', new ParseUUIDPipe({ version: '4' })) uuid: string) {
260
+ return { uuid }
128
261
  }
129
262
  }
130
263
  ```
131
264
 
132
- ### Available Parameter Decorators
133
- - `@Body(nameOrPipe?)` — Extracts `req.body` or runs through a validation schema/pipe.
134
- - `@Param(nameOrPipe?)` — Extracts a route path param (e.g. `:id`).
135
- - `@QueryParam(nameOrPipe?)` / `@Query(nameOrPipe?)` — Extracts a query parameter from the URL query string.
136
- - `@Headers(nameOrPipe?)` — Extracts a request header by name.
137
- - `@Cookies(nameOrPipe?)` / `@Cookie(nameOrPipe?)` — Injects incoming parsed cookies or a specific cookie value.
138
- - `@State(keyOrPipe?)` — Injects request state from execution context or middleware.
139
- - `@AppCtx()` — Injects the root ExisJS Application instance.
140
- - `@Fields(nameOrPipe?)` — Injects non-file multipart form fields.
141
- - `@UploadedFile(nameOrPipe?)` — Injects a single uploaded file.
142
- - `@UploadedFiles(nameOrPipe?)` — Injects multiple uploaded files.
143
- - `@HostParam(nameOrPipe?)` — Extracts a host subdomain parameter.
144
- - `@Req()` — Injects the raw request object.
145
- - `@Res(options?)` — Injects the response object (e.g. `{ passthrough: true }`).
146
- - `@Ip()` — Injects the client IP address.
147
- - `@Next()` — Injects the `next` function.
148
- - `createParamDecorator(factory)` — Creates custom reusable route parameter decorators:
149
- ```typescript
150
- export const CurrentUser = createParamDecorator((data: string | undefined, ctx) => {
151
- return data ? ctx.req.user?.[data] : ctx.req.user
152
- })
153
- ```
265
+ ### Pipes Reference
266
+
267
+ | Pipe Class | Description | Options |
268
+ | :--- | :--- | :--- |
269
+ | `ParseIntPipe` | Converts input string or number to integer using `Math.trunc` | `{ errorHttpStatusCode?: number, exceptionFactory?: (msg) => any }` |
270
+ | `ParseFloatPipe` | Converts input string or number to floating point | `{ errorHttpStatusCode?: number, exceptionFactory?: (msg) => any }` |
271
+ | `ParseBoolPipe` | Converts `'true'`, `'1'`, `true` to `true`, `'false'`, `'0'`, `false` to `false` | `{ errorHttpStatusCode?: number, exceptionFactory?: (msg) => any }` |
272
+ | `ParseUUIDPipe` | Validates UUID strings | `{ version?: '3' \| '4' \| '5' \| 'all', errorHttpStatusCode?: number }` |
273
+ | `ParseArrayPipe` | Parses comma-separated strings or arrays with sub-item transformation | `{ items?: any, separator?: string, optional?: boolean }` |
274
+ | `ValidationPipe` | Validates DTO classes or schemas implementing `validate()` or Standard Schema | `{ transform?: boolean, whitelist?: boolean, errorHttpStatusCode?: number }` |
275
+ | `@UsePipes(...pipes)` | Attaches one or more pipes to a controller class or method handler | Array of Pipe instances or Pipe classes |
154
276
 
155
277
  ---
156
278
 
157
- ## 4. Middleware & Pipeline Decorators
279
+ ## 5. Middleware, Guards & Lifecycle Pipeline Decorators
158
280
 
159
- You can bind middlewares, guards, interceptors, and exception filters to controllers or route methods using first-class decorators:
281
+ Attach guards, interceptors, exception filters, timeouts, and traffic policies to classes or route methods:
160
282
 
161
283
  ```typescript filename="src/http/checkout/route.ts"
162
- import { Controller, Post, Use, Idempotent, RateLimit, Cors, Timeout } from 'exisjs/decorators'
163
- import { requireAuth } from '../../middleware/auth'
284
+ import {
285
+ Controller,
286
+ Post,
287
+ UseGuards,
288
+ UseInterceptors,
289
+ UseFilters,
290
+ RateLimit,
291
+ Timeout,
292
+ Idempotent,
293
+ Cors,
294
+ Compress,
295
+ Dedupe,
296
+ IpFilter,
297
+ BlockProbes
298
+ } from 'exisjs/decorators'
299
+ import { AuthGuard } from '../../common/guards/auth.guard'
300
+ import { LoggingInterceptor } from '../../common/interceptors/logging'
301
+ import { HttpExceptionFilter } from '../../common/filters/http.filter'
164
302
 
165
303
  @Controller('/checkout')
166
- @Use(requireAuth) // Applies to all routes in this controller
167
- @Cors({ origin: 'https://app.yourdomain.com' })
168
- @RateLimit({ max: 10, windowMs: 60000 })
304
+ @UseGuards(AuthGuard)
305
+ @UseInterceptors(LoggingInterceptor)
306
+ @UseFilters(HttpExceptionFilter)
307
+ @Cors({ origin: 'https://pay.example.com', credentials: true })
308
+ @RateLimit({ max: 20, windowMs: 60000 })
309
+ @IpFilter({ allow: ['10.0.0.0/8', '127.0.0.1'] })
310
+ @BlockProbes()
169
311
  export default class CheckoutController {
170
312
  @Post('/')
171
- @Timeout(30000)
172
- @Idempotent({ ttlMs: 86400000 }) // In-memory off-heap idempotency protection
313
+ @Timeout(15000)
314
+ @Idempotent({ ttlMs: 86400000 })
315
+ @Dedupe({ ttlMs: 5000 })
316
+ @Compress()
173
317
  async processPayment() {
174
- return { success: true }
318
+ return { status: 'processed' }
175
319
  }
176
320
  }
177
321
  ```
178
322
 
179
- ### Available Lifecycle & Middleware Decorators
180
- - `@Use(...items)` — Unified decorator that attaches middlewares, guards (`canActivate`), interceptors (`intercept`), or exception filters (`catch`) to a class or method.
181
- - `@UseGuards(...guards)` — Explicit alias for `@Use` dedicated to route guards.
182
- - `@UseInterceptors(...interceptors)` — Explicit alias for `@Use` dedicated to interceptors.
183
- - `@UseFilters(...filters)` — Explicit alias for `@Use` dedicated to exception filters.
184
- - `@Catch(...exceptions)` — Declares exception classes handled by an Exception Filter.
185
- - `@Timeout(ms: number | TimeoutOptions)` — Overrides the request timeout for a class or method.
186
- - `@Idempotent(options?)` — Caches responses based on the `Idempotency-Key` header using native in-memory off-heap storage.
187
- - `@RateLimit(options?)` — Enforces request rate limits on a controller class or route method.
188
- - `@Cors(options?)` — Configures Cross-Origin Resource Sharing rules on a controller class or route method.
189
- - `@IpFilter(options)` — Enforces IP/CIDR allowlist and denylist.
190
- - `@Compress()` — Enables automatic response compression (Gzip/Brotli).
191
- - `@Dedupe(options)` — Deduplicates concurrent identical requests sharing the same key.
192
- - `@UseUpload(options)` / `@Upload(options)` — Configures multipart file upload handling.
193
- - `@BlockProbes(options?)` — Intercepts scanner exploit probes (`.env`, `/.git`, `/.DS_Store`) and blackholes them without log noise.
323
+ ### Traffic & Middleware Decorators
324
+
325
+ | Decorator | Scope | Description |
326
+ | :--- | :--- | :--- |
327
+ | `@Use(...items)` | Class / Method | Attaches middlewares, guards (`canActivate`), interceptors (`intercept`), or filters (`catch`). |
328
+ | `@UseGuards(...guards)` | Class / Method | Attaches route guards implementing the `CanActivate` interface. |
329
+ | `@UseInterceptors(...interceptors)` | Class / Method | Attaches request/response interceptors implementing `Interceptor`. |
330
+ | `@UseFilters(...filters)` | Class / Method | Attaches exception filters implementing `ExceptionFilter`. |
331
+ | `@Catch(...exceptions)` | Class | Declares the exception types handled by an `ExceptionFilter` class. |
332
+ | `@Timeout(msOrOptions)` | Class / Method | Sets request execution timeout in milliseconds. |
333
+ | `@Idempotent(options?)` | Method / Class | Caches responses matching the `Idempotency-Key` header in an LRU memory store. |
334
+ | `@RateLimit(options?)` | Class / Method | Enforces rate limits using a token bucket algorithm. |
335
+ | `@Cors(options?)` | Class / Method | Applies CORS headers to a specific controller or method. |
336
+ | `@IpFilter(options)` | Class / Method | Restricts access using CIDR IP allowlists and denylists. |
337
+ | `@Compress()` | Class / Method | Enables Gzip and Brotli response compression. |
338
+ | `@Dedupe(options)` | Method / Class | Deduplicates parallel in-flight requests matching the same key. |
339
+ | `@BlockProbes(options?)` / `@BlockSuspiciousProbes()` | Class / Method | Drops vulnerability scanner probes (`.env`, `wp-admin`, `/.git`). |
340
+ | `@Upload(fieldNameOrOptions)` / `@UseUpload(options)` | Method / Parameter | Configures multipart file upload middleware. |
194
341
 
195
342
  ---
196
343
 
197
- ## 5. Security & Authorization Decorators
344
+ ## 6. Security & RBAC Decorators
198
345
 
199
- Declare access control rules directly on controller classes and route methods:
346
+ Declare role requirements, permission checks, and authentication bypasses:
200
347
 
201
- ```typescript filename="src/http/roles/route.ts"
202
- import { Controller, Get, Post, Body, Use, Permissions, Roles, Public } from 'exisjs/decorators'
203
- import { AuthGuard } from '@/common/guards/auth.guard'
348
+ ```typescript filename="src/http/admin/roles/route.ts"
349
+ import {
350
+ Controller,
351
+ Get,
352
+ Post,
353
+ Body,
354
+ UseGuards,
355
+ Roles,
356
+ Permissions,
357
+ Public
358
+ } from 'exisjs/decorators'
359
+ import { AuthGuard } from '../../common/guards/auth.guard'
204
360
 
205
361
  @Controller('/roles')
206
- @Use(AuthGuard) // Enforced on all routes in this controller
362
+ @UseGuards(AuthGuard)
207
363
  export default class RolesController {
208
364
  @Get('/public')
209
- @Public() // Bypasses auth guard for public inspection
365
+ @Public() // Instructs AuthGuard to skip credential verification
210
366
  async getPublicInfo() {
211
- return { status: 'public' }
367
+ return { status: 'open' }
212
368
  }
213
369
 
214
370
  @Get('/')
215
- @Roles('admin', 'manager') // Requires user to have admin or manager role
216
- async getRoles() {
217
- return { roles: [] }
371
+ @Roles('admin', 'security_officer')
372
+ async listRoles() {
373
+ return { roles: ['admin', 'manager', 'viewer'] }
218
374
  }
219
375
 
220
376
  @Post('/')
221
- @Permissions('role:create', 'role:manage') // Granular permission check
377
+ @Permissions('roles:create', 'roles:write')
222
378
  async createRole(@Body() body: any) {
223
- return { success: true }
379
+ return { created: true }
224
380
  }
225
381
  }
226
382
  ```
227
383
 
228
- ### Available Security Decorators
229
- - `@Permissions(...permissions: string[])` — Declares required permissions (auto-bypassed when `req.user.isSuperAdmin` is true).
230
- - `@Roles(...roles: string[])` — Declares required role names.
231
- - `@Public()` / `@IsPublic()` / `@AllowAnonymous()` — Marks an endpoint or controller as public, instructing global auth guards to skip credential verification.
384
+ ### Security Decorators Reference
385
+
386
+ | Decorator | Target | Description |
387
+ | :--- | :--- | :--- |
388
+ | `@Roles(...roles: string[])` | Class / Method | Stores required role names in route metadata (`ROLES_METADATA`). |
389
+ | `@Permissions(...perms: string[])` | Class / Method | Stores granular required permissions in route metadata (`PERMISSIONS_METADATA`). |
390
+ | `@Public()` / `@IsPublic()` / `@AllowAnonymous()` | Class / Method | Marks route as unauthenticated. |
232
391
 
233
392
  ---
234
393
 
235
- ## 6. Service Enhancements: Caching, Transactions & Profiling
394
+ ## 7. Cron & Scheduled Background Tasks
395
+
396
+ Schedule recurring tasks directly on `@Injectable()` service classes:
397
+
398
+ ```typescript filename="src/http/cron/cleanup.service.ts"
399
+ import { Injectable, Cron, Interval, TimeoutTask } from 'exisjs/decorators'
400
+
401
+ @Injectable()
402
+ export class CleanupService {
403
+ // Executes daily at midnight UTC with overlap protection
404
+ @Cron('0 0 * * *', { name: 'nightly-purge', timezone: 'UTC', preventOverlap: true })
405
+ async purgeOldSessions() {
406
+ console.log('Purging expired sessions')
407
+ }
408
+
409
+ // Executes every 60 seconds
410
+ @Interval(60_000, { name: 'health-heartbeat' })
411
+ async pingHeartbeat() {
412
+ console.log('Heartbeat ping')
413
+ }
414
+
415
+ // Executes once 5 seconds after server startup
416
+ @TimeoutTask(5_000, { name: 'cache-warmup' })
417
+ async warmupCaches() {
418
+ console.log('Caches warmed up')
419
+ }
420
+ }
421
+ ```
422
+
423
+ ### Cron Decorators Table
236
424
 
237
- ExisJS provides high-performance method decorators for `@Injectable()` services:
425
+ | Decorator | Schedule Parameter | Description |
426
+ | :--- | :--- | :--- |
427
+ | `@Cron(schedule, options?)` / `@Scheduled(...)` | 5-field cron expression string | Schedules a recurring cron task. |
428
+ | `@Interval(ms, options?)` | Milliseconds interval | Runs repeatedly at the specified interval. |
429
+ | `@TimeoutTask(ms, options?)` / `@ScheduledTimeout(...)` | Milliseconds delay | Runs once after a delay following server start. |
430
+
431
+ ---
432
+
433
+ ## 8. Service Enhancements: Caching, Transactions & Profiling
238
434
 
239
435
  ```typescript filename="src/http/orders/service.ts"
240
- import { Injectable, Cacheable, CacheEvict, Transactional, Profile, type OnInit, type OnDestroy } from 'exisjs/decorators'
436
+ import {
437
+ Injectable,
438
+ Cacheable,
439
+ CacheEvict,
440
+ Transactional,
441
+ Profile,
442
+ type OnInit,
443
+ type OnDestroy
444
+ } from 'exisjs/decorators'
241
445
 
242
446
  @Injectable()
243
447
  export class OrderService implements OnInit, OnDestroy {
@@ -249,14 +453,14 @@ export class OrderService implements OnInit, OnDestroy {
249
453
  console.log('OrderService destroyed')
250
454
  }
251
455
 
252
- // Caches result in off-heap Rust cache for 300 seconds
456
+ // Caches result in memory for 300 seconds
253
457
  @Cacheable({ ttl: 300, key: (id: string) => `order:${id}` })
254
458
  @Profile({ thresholdMs: 50, warnSlow: true })
255
459
  async getOrder(id: string) {
256
460
  return this.db.query('SELECT * FROM orders WHERE id = $1', [id])
257
461
  }
258
462
 
259
- // Automatically runs in a database transaction and evicts cached entries
463
+ // Executes inside a database transaction and evicts the cached key
260
464
  @Transactional()
261
465
  @CacheEvict({ key: (id: string) => `order:${id}` })
262
466
  async updateOrder(id: string, data: any) {
@@ -265,69 +469,129 @@ export class OrderService implements OnInit, OnDestroy {
265
469
  }
266
470
  ```
267
471
 
268
- ### Available Service Decorators & Lifecycle Interfaces
269
- - `@Cacheable({ ttl?: number, key?: string | ((...args) => string) })` — Caches method return values in off-heap memory.
270
- - `@CacheEvict({ key: string | ((...args) => string) })` — Evicts cached keys on mutation.
271
- - `@Transactional(options?)` — Wraps service operations in an atomic database transaction.
272
- - `@Profile({ thresholdMs?: number, warnSlow?: boolean, log?: Function })` — Measures method execution time with sub-millisecond precision.
273
- - `OnInit` (`onInit()`) & `OnDestroy` (`onDestroy()`) — Service startup and shutdown lifecycle hooks.
274
- - `OnModuleInit`, `OnModuleDestroy`, `OnApplicationBootstrap` — Module lifecycle hooks.
472
+ ### Service & Lifecycle Reference
473
+
474
+ | Decorator / Interface | Target | Description |
475
+ | :--- | :--- | :--- |
476
+ | `@Cacheable(options?)` | Method | Caches return value. Options: `{ ttl?: number, key?: string \| ((...args) => string) }`. |
477
+ | `@CacheEvict(options)` | Method | Evicts cache keys on method completion. Options: `{ key: string \| ((...args) => string) }`. |
478
+ | `@Transactional(options?)` | Method | Wraps execution in a database transaction (Prisma, Mongoose, Drizzle, SQL). |
479
+ | `@Profile(options?)` | Method | Measures execution time using `performance.now()`. |
480
+ | `OnInit` (`onInit()`) | Class Interface | Invoked once after service instantiation. |
481
+ | `OnDestroy` (`onDestroy()`) | Class Interface | Invoked once during graceful shutdown. |
482
+ | `OnModuleInit` / `OnModuleDestroy` | Class Interface | Invoked during parent module initialization and destruction. |
483
+ | `OnApplicationBootstrap` | Class Interface | Invoked when all modules are initialized and server starts listening. |
484
+ | `OnApplicationShutdown` | Class Interface | Invoked on `SIGTERM` or `SIGINT` signals. |
275
485
 
276
486
  ---
277
487
 
278
- ## 7. Response & Metadata Modifiers
488
+ ## 9. Response, Header & OpenAPI Decorators
489
+
490
+ ### Response & Host Modifiers
491
+
492
+ ```typescript filename="src/http/items/route.ts"
493
+ import {
494
+ Controller,
495
+ Post,
496
+ Get,
497
+ HttpCode,
498
+ Header,
499
+ Redirect,
500
+ Hosts,
501
+ Returns
502
+ } from 'exisjs/decorators'
503
+ import { ItemResponseSchema } from './schema'
504
+
505
+ @Controller('/items')
506
+ @Hosts('api.example.com', 'internal.example.com')
507
+ export default class ItemsController {
508
+ @Post('/')
509
+ @HttpCode(201)
510
+ @Header('X-Resource-Version', '1.0')
511
+ @Returns(ItemResponseSchema)
512
+ async create() {
513
+ return { created: true }
514
+ }
279
515
 
280
- Modify HTTP response status codes, redirect clients, or set headers declaratively:
516
+ @Get('/legacy')
517
+ @Redirect('/items', 301)
518
+ async legacyRedirect() {}
519
+ }
520
+ ```
281
521
 
282
- ```typescript filename="src/http/api/route.ts"
283
- import { Controller, Post, Get, HttpCode, Header, Redirect, SetMetadata, Reflector } from 'exisjs/decorators'
522
+ ### OpenAPI & Swagger Decorators
284
523
 
285
- @Controller('/api')
286
- @SetMetadata('roles', ['admin'])
287
- export default class ApiController {
288
- @Post('/create')
289
- @HttpCode(201) // Forces 201 Created
290
- @Header('X-Custom-Version', '2.0')
291
- @SetMetadata('permissions', ['write:api'])
292
- async create() {
293
- return { success: true }
524
+ All OpenAPI decorators are available from `exisjs/decorators`:
525
+
526
+ ```typescript filename="src/http/users/route.ts"
527
+ import {
528
+ Controller,
529
+ Get,
530
+ ApiTags,
531
+ ApiOperation,
532
+ ApiResponse,
533
+ ApiBearerAuth,
534
+ ApiSecurity,
535
+ ApiExclude,
536
+ ApiProperty
537
+ } from 'exisjs/decorators'
538
+
539
+ export class UserDto {
540
+ @ApiProperty({ description: 'User ID', example: 'usr_123', required: true })
541
+ id: string
542
+
543
+ @ApiProperty({ description: 'Full name', example: 'Alice Smith' })
544
+ name: string
545
+ }
546
+
547
+ @ApiTags('Users')
548
+ @ApiBearerAuth()
549
+ @Controller('/users')
550
+ export default class UsersController {
551
+ @Get('/:id')
552
+ @ApiOperation({ summary: 'Get User By ID', description: 'Returns a user record' })
553
+ @ApiResponse({ status: 200, description: 'User retrieved successfully' })
554
+ @ApiResponse({ status: 404, description: 'User not found' })
555
+ async getUser() {
556
+ return { id: 'usr_123', name: 'Alice Smith' }
294
557
  }
295
558
 
296
- @Get('/legacy')
297
- @Redirect('/new-api', 301)
298
- async legacy() {}
559
+ @Get('/internal/health')
560
+ @ApiExclude()
561
+ async health() {
562
+ return { ok: true }
563
+ }
299
564
  }
300
565
  ```
301
566
 
302
- ### Available Response & Metadata Decorators
303
- - `@HttpCode(code: number)` — Sets the response HTTP status code.
304
- - `@Header(name: string, value: string)` — Injects a response header.
305
- - `@Returns(schema)` — Attaches response schema for OpenAPI/Swagger generation.
306
- - `@Redirect(url: string, statusCode?: number)` — Redirects the client.
307
- - `@Hosts(...hosts: string[])` — Restricts route/controller to specific Host headers.
308
- - `@SetMetadata(key, value)` — Attaches custom metadata to a class or method.
309
- - `Reflector` — Static utility to inspect metadata attached via `@SetMetadata` across classes and methods.
567
+ | Decorator | Scope | Description |
568
+ | :--- | :--- | :--- |
569
+ | `@ApiTags(...tags)` / `@Tags(...)` | Class / Method | Assigns OpenAPI documentation tags. |
570
+ | `@ApiOperation(options)` / `@Operation(...)` | Method | Configures `summary`, `description`, `operationId`, `deprecated`, `externalDocs`. |
571
+ | `@ApiResponse(options)` | Method | Defines status code documentation (`status`, `description`, `schema`). |
572
+ | `@ApiBearerAuth(name?)` / `@BearerAuth(...)` | Class / Method | Marks route as requiring Bearer JWT authentication in OpenAPI output. |
573
+ | `@ApiSecurity(name, scopes?)` | Class / Method | Declares custom security scheme requirement. |
574
+ | `@ApiExclude()` | Class / Method | Hides route or controller from OpenAPI / Swagger output. |
575
+ | `@ApiProperty(options)` | Property | Annotates class DTO properties with description, examples, types, and enums. |
310
576
 
311
577
  ---
312
578
 
313
- ## 8. Dependency Injection & Modules
579
+ ## 10. Dependency Injection & Modules
314
580
 
315
- To mark a service class as an injectable provider for the IoC container, use the `@Injectable()` decorator. ExisJS supports `singleton` (default), `request`, and `transient` scopes:
581
+ ### `@Injectable(options?)`
316
582
 
317
583
  ```typescript filename="src/http/users/service.ts"
318
584
  import { Injectable } from 'exisjs/decorators'
319
585
 
320
- @Injectable({ scope: 'transient' })
586
+ @Injectable({ scope: 'singleton' }) // 'singleton' | 'transient' | 'request'
321
587
  export class UserService {
322
- async fetchUser(id: string) {
588
+ async findOne(id: string) {
323
589
  return { id, name: 'Alice' }
324
590
  }
325
591
  }
326
592
  ```
327
593
 
328
- ### Constructor Parameter Injection (`@Inject` & `@Optional`)
329
-
330
- You can inject dependencies using tokens or classes, and mark optional dependencies with `@Optional()`:
594
+ ### `@Inject(token)` & `@Optional()`
331
595
 
332
596
  ```typescript filename="src/http/users/route.ts"
333
597
  import { Controller, Get, Param } from 'exisjs/decorators'
@@ -344,20 +608,19 @@ export default class UsersController {
344
608
 
345
609
  @Get('/:id')
346
610
  async getUser(@Param('id') id: string) {
347
- return this.userService.fetchUser(id)
611
+ return this.userService.findOne(id)
348
612
  }
349
613
  }
350
614
  ```
351
615
 
352
- ### Application Modules (`@Module`)
353
-
354
- You can group controllers, providers, and imports into high-level OOP modules using `@Module`:
616
+ ### `@Module(options)` & `@Global()`
355
617
 
356
618
  ```typescript filename="src/http/users/users.module.ts"
357
- import { Module } from 'exisjs/module' // or 'exisjs/decorators'
619
+ import { Module, Global } from 'exisjs/decorators'
358
620
  import UsersController from './route'
359
621
  import { UserService } from './service'
360
622
 
623
+ @Global()
361
624
  @Module({
362
625
  controllers: [UsersController],
363
626
  providers: [UserService],
@@ -366,3 +629,35 @@ import { UserService } from './service'
366
629
  export class UsersModule {}
367
630
  ```
368
631
 
632
+ ---
633
+
634
+ ## 11. JavaScript Runtime Utilities (`Decorate` & `BindParam`)
635
+
636
+ For JavaScript projects without a build step or TypeScript compiler, use `Decorate` and `BindParam` to apply decorator metadata programmatically:
637
+
638
+ ```javascript filename="src/http/users/route.js"
639
+ import { Decorate, BindParam, Controller, Get, Post, Body, Param } from 'exisjs/decorators'
640
+
641
+ class UsersController {
642
+ list() {
643
+ return []
644
+ }
645
+
646
+ create(body) {
647
+ return { created: body.name }
648
+ }
649
+ }
650
+
651
+ export default Decorate(
652
+ UsersController,
653
+ [Controller('/users')],
654
+ {
655
+ list: [Get('/')],
656
+ create: [
657
+ Post('/'),
658
+ BindParam(0, Body()) // Injects @Body() into the first argument (index 0)
659
+ ]
660
+ }
661
+ )
662
+ ```
663
+