exisjs 0.7.11 → 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 (150) hide show
  1. package/README.md +195 -39
  2. package/dist/cli/commands/dev.d.ts +4 -0
  3. package/dist/cli/commands/dev.d.ts.map +1 -1
  4. package/dist/cli/commands/dev.js +136 -32
  5. package/dist/cli/commands/dev.js.map +1 -1
  6. package/dist/cli/commands/run.d.ts +7 -0
  7. package/dist/cli/commands/run.d.ts.map +1 -0
  8. package/dist/cli/commands/run.js +99 -0
  9. package/dist/cli/commands/run.js.map +1 -0
  10. package/dist/cli/index.js +29 -13
  11. package/dist/cli/index.js.map +1 -1
  12. package/dist/cli/manifest.d.ts.map +1 -1
  13. package/dist/cli/manifest.js +18 -3
  14. package/dist/cli/manifest.js.map +1 -1
  15. package/dist/cli/routes.d.ts +6 -0
  16. package/dist/cli/routes.d.ts.map +1 -1
  17. package/dist/cli/routes.js +224 -135
  18. package/dist/cli/routes.js.map +1 -1
  19. package/dist/config/config.d.ts +2 -1
  20. package/dist/config/config.d.ts.map +1 -1
  21. package/dist/config/config.js.map +1 -1
  22. package/dist/config/types.d.ts +30 -9
  23. package/dist/config/types.d.ts.map +1 -1
  24. package/dist/database/lifecycle.d.ts +6 -1
  25. package/dist/database/lifecycle.d.ts.map +1 -1
  26. package/dist/database/lifecycle.js +15 -4
  27. package/dist/database/lifecycle.js.map +1 -1
  28. package/dist/decorators/params.d.ts +21 -0
  29. package/dist/decorators/params.d.ts.map +1 -1
  30. package/dist/decorators/params.js +21 -1
  31. package/dist/decorators/params.js.map +1 -1
  32. package/dist/di/container.d.ts +15 -6
  33. package/dist/di/container.d.ts.map +1 -1
  34. package/dist/di/container.js +26 -3
  35. package/dist/di/container.js.map +1 -1
  36. package/dist/di/index.d.ts +1 -1
  37. package/dist/di/index.d.ts.map +1 -1
  38. package/dist/di/index.js.map +1 -1
  39. package/dist/di/inject.d.ts +2 -1
  40. package/dist/di/inject.d.ts.map +1 -1
  41. package/dist/di/inject.js +2 -1
  42. package/dist/di/inject.js.map +1 -1
  43. package/dist/docs/BENCHMARKS.md +4 -4
  44. package/dist/docs/content/README.md +43 -44
  45. package/dist/docs/content/boundaries.mdx +141 -48
  46. package/dist/docs/content/cli/commands.mdx +161 -107
  47. package/dist/docs/content/cli/overview.mdx +61 -46
  48. package/dist/docs/content/cli/usage.mdx +107 -30
  49. package/dist/docs/content/config.mdx +145 -147
  50. package/dist/docs/content/controllers.mdx +193 -464
  51. package/dist/docs/content/cron.mdx +183 -69
  52. package/dist/docs/content/decorators.mdx +449 -154
  53. package/dist/docs/content/errors.mdx +202 -305
  54. package/dist/docs/content/exisjs-mcp.mdx +59 -49
  55. package/dist/docs/content/installation.mdx +243 -157
  56. package/dist/docs/content/introduction.mdx +220 -48
  57. package/dist/docs/content/middleware.mdx +171 -497
  58. package/dist/docs/content/modules.mdx +141 -83
  59. package/dist/docs/content/providers.mdx +216 -180
  60. package/dist/docs/content/requests.mdx +168 -488
  61. package/dist/docs/content/responses.mdx +200 -533
  62. package/dist/docs/content/routing.mdx +239 -558
  63. package/dist/docs/content/security.mdx +233 -57
  64. package/dist/docs/content/structure.mdx +151 -240
  65. package/dist/docs/content/swagger.mdx +192 -91
  66. package/dist/docs/content/testing.mdx +205 -74
  67. package/dist/docs/content/validation.mdx +479 -341
  68. package/dist/docs/reference/README.md +34 -2
  69. package/dist/docs/reference/config.mdx +277 -73
  70. package/dist/docs/reference/context.mdx +197 -106
  71. package/dist/docs/reference/cron.mdx +266 -152
  72. package/dist/docs/reference/database.mdx +135 -53
  73. package/dist/docs/reference/decorators.mdx +234 -116
  74. package/dist/docs/reference/di.mdx +146 -112
  75. package/dist/docs/reference/error.mdx +155 -178
  76. package/dist/docs/reference/exis.mdx +141 -70
  77. package/dist/docs/reference/logger.mdx +110 -126
  78. package/dist/docs/reference/middleware.mdx +235 -158
  79. package/dist/docs/reference/plugin.mdx +136 -64
  80. package/dist/docs/reference/router.mdx +277 -251
  81. package/dist/docs/reference/sanitize.mdx +91 -87
  82. package/dist/docs/reference/swagger.mdx +280 -100
  83. package/dist/docs/reference/testing.mdx +223 -89
  84. package/dist/docs/reference/validator.mdx +194 -206
  85. package/dist/lib/inspect-routes.d.ts +2 -0
  86. package/dist/lib/inspect-routes.d.ts.map +1 -0
  87. package/dist/lib/inspect-routes.js +148 -0
  88. package/dist/lib/inspect-routes.js.map +1 -0
  89. package/dist/lib/start-server.js +13 -8
  90. package/dist/lib/start-server.js.map +1 -1
  91. package/dist/module/module.d.ts +6 -3
  92. package/dist/module/module.d.ts.map +1 -1
  93. package/dist/module/module.js +17 -3
  94. package/dist/module/module.js.map +1 -1
  95. package/dist/router/controller-registrar.d.ts.map +1 -1
  96. package/dist/router/controller-registrar.js +26 -4
  97. package/dist/router/controller-registrar.js.map +1 -1
  98. package/dist/router/scanner/cron-mounter.d.ts.map +1 -1
  99. package/dist/router/scanner/cron-mounter.js +9 -2
  100. package/dist/router/scanner/cron-mounter.js.map +1 -1
  101. package/dist/server/app.d.ts +3 -1
  102. package/dist/server/app.d.ts.map +1 -1
  103. package/dist/server/app.js +29 -8
  104. package/dist/server/app.js.map +1 -1
  105. package/dist/server/define.d.ts.map +1 -1
  106. package/dist/server/define.js +5 -0
  107. package/dist/server/define.js.map +1 -1
  108. package/dist/server/request.d.ts +2 -0
  109. package/dist/server/request.d.ts.map +1 -1
  110. package/dist/server/request.js +11 -0
  111. package/dist/server/request.js.map +1 -1
  112. package/dist/swagger/generator.d.ts.map +1 -1
  113. package/dist/swagger/generator.js +130 -10
  114. package/dist/swagger/generator.js.map +1 -1
  115. package/dist/telemetry/index.d.ts +12 -0
  116. package/dist/telemetry/index.d.ts.map +1 -0
  117. package/dist/telemetry/index.js +121 -0
  118. package/dist/telemetry/index.js.map +1 -0
  119. package/dist/testing/client.d.ts +1 -0
  120. package/dist/testing/client.d.ts.map +1 -1
  121. package/dist/testing/client.js +4 -1
  122. package/dist/testing/client.js.map +1 -1
  123. package/dist/testing/index.d.ts.map +1 -1
  124. package/dist/testing/index.js +20 -4
  125. package/dist/testing/index.js.map +1 -1
  126. package/dist/validator/builder.d.ts +26 -1
  127. package/dist/validator/builder.d.ts.map +1 -1
  128. package/dist/validator/builder.js +31 -0
  129. package/dist/validator/builder.js.map +1 -1
  130. package/dist/validator/engine.d.ts +23 -0
  131. package/dist/validator/engine.d.ts.map +1 -1
  132. package/dist/validator/engine.js +228 -31
  133. package/dist/validator/engine.js.map +1 -1
  134. package/dist/validator/index.d.ts +8 -4
  135. package/dist/validator/index.d.ts.map +1 -1
  136. package/dist/validator/index.js +4 -1
  137. package/dist/validator/index.js.map +1 -1
  138. package/dist/validator/tex-types.d.ts +15 -0
  139. package/dist/validator/tex-types.d.ts.map +1 -1
  140. package/dist/validator/tex-types.js +102 -0
  141. package/dist/validator/tex-types.js.map +1 -1
  142. package/dist/validator/tex.d.ts +1 -1
  143. package/dist/validator/tex.d.ts.map +1 -1
  144. package/dist/validator/tex.js +3 -1
  145. package/dist/validator/tex.js.map +1 -1
  146. package/dist/validator/types.d.ts +1 -0
  147. package/dist/validator/types.d.ts.map +1 -1
  148. package/package.json +47 -14
  149. package/dist/docs/content/dataloader.mdx +0 -170
  150. package/dist/docs/reference/dataloader.mdx +0 -91
@@ -1,259 +1,278 @@
1
1
  ---
2
- title: "Router & Controllers"
3
- description: "API Reference for defining routes and controllers in ExisJS."
2
+ title: "exisjs/router"
3
+ description: "API Reference for ExisJS routing engines: Functional routing (controller, route.*, defineBoundary) and Class-Based OOP routing (decorators, parameter injectors)."
4
+ nextUrl: "/docs/reference/validator"
5
+ nextTitle: "Validator Reference"
4
6
  ---
5
7
 
6
- # Router & Controllers
8
+ # `exisjs/router`
7
9
 
8
- ExisJS provides two distinct ways to build your API: **Functional Routing** (using `route` and `controller`) and **Class-Based Routing** (using standard decorators). You can use either paradigm, or even mix both in the same application.
10
+ ExisJS provides two routing paradigms with full feature parity: **Functional Routing** (`exisjs/router`) and **Class-Based OOP Routing** (`exisjs/decorators`).
9
11
 
10
12
  ---
11
13
 
12
- ## 1. Functional Routing
14
+ ## 1. Functional Routing API
13
15
 
14
- The functional router provides a lightweight, type-safe API via the `route` and `controller` exports from `exisjs/router`.
15
-
16
- These utilities allow you to define HTTP endpoints with built-in validation schemas, automatically typed execution contexts, and file-level configuration.
17
-
18
- ### `controller()`
19
-
20
- The `controller` function wraps a collection of routes and configuration options (like middleware or CORS) inside a single file.
21
-
22
- ### Usage
16
+ The functional router allows defining endpoints and boundaries using composable objects and functions.
23
17
 
24
18
  ```typescript
25
- import { controller, route } from 'exisjs/router'
26
- import { authMiddleware } from '../../middleware/auth'
27
-
28
- export default controller({
29
- // Global config applied to all routes in this controller
30
- cors: true,
31
- middleware: [authMiddleware],
32
-
33
- // Your individual routes
34
- getUser: route.get('/user', {
35
- async handle({ req }) {
36
- return { user: req.user }
37
- }
38
- })
39
- })
19
+ import {
20
+ route,
21
+ controller,
22
+ createRouter,
23
+ defineMiddleware,
24
+ defineBoundary,
25
+ validate,
26
+ getContext,
27
+ setContext,
28
+ getRequest,
29
+ getResponse,
30
+ after,
31
+ fileUpload,
32
+ SSEStream,
33
+ } from 'exisjs/router'
34
+
35
+ import type {
36
+ RouteConfig,
37
+ RouteDefinition,
38
+ ControllerConfig,
39
+ SuperContext,
40
+ BoundaryConfig,
41
+ BoundaryContext,
42
+ BoundaryExcludeRule,
43
+ Request,
44
+ Response,
45
+ NextFunction,
46
+ } from 'exisjs/router'
40
47
  ```
41
48
 
42
- ### Options (`ControllerConfig`)
43
- - `cors` (`boolean | CorsConfig`): Enables or configures CORS for all routes in this file.
44
- - `middleware` (`Handler[]`): Array of middleware functions applied to all routes in this file.
45
- - `filters` (`any | any[]`): Route filters applied to all routes.
46
- - `onError` (`HookError`): A file-level error handler.
47
- - `onResponse` (`HookResponse`): A file-level response hook.
48
- - `[key: string]` (`RouteDefinition`): **Any other key** is treated as a route definition (e.g. `getUser: route.get(...)`). This is how you bind routes to the controller!
49
-
50
- ### `controller.merge(...controllers)`
51
-
52
- Merges multiple functional controllers into a single unified controller definition. This enables splitting large `route.ts` files into modular, domain-specific controller pieces while exporting a single composite controller from the route file.
49
+ ### `controller(config)`
53
50
 
54
- ```typescript filename="src/http/auth/route.ts"
55
- import { controller, route } from 'exisjs/router'
51
+ Wraps route definitions, file-scoped middlewares, guards, and CORS policies into an exportable controller slice.
56
52
 
57
- // Sub-controller 1: credentials & sessions
58
- const authRoutes = controller({
59
- login: route.post('/login', {
60
- async handle({ body }) {
61
- return { token: 'jwt-token' }
62
- }
63
- }),
64
- logout: route.post('/logout', {
65
- async handle() {
66
- return { success: true }
67
- }
68
- })
69
- })
53
+ ```typescript
54
+ function controller<T extends Record<string, any>>(
55
+ config: ControllerConfig<T>
56
+ ): ControllerDefinition<T>
57
+ ```
70
58
 
71
- // Sub-controller 2: OAuth integrations
72
- const oauthRoutes = controller({
73
- google: route.get('/google', {
74
- async handle() {
75
- return { redirectUrl: 'https://accounts.google.com/o/oauth2/auth' }
76
- }
77
- })
78
- })
59
+ #### `ControllerConfig` Options
79
60
 
80
- // Export combined controller as default
81
- export default controller.merge(authRoutes, oauthRoutes)
82
- ```
61
+ | Property | Type | Description |
62
+ | :--- | :--- | :--- |
63
+ | `cors` | `boolean \| CorsConfig` | CORS configuration applied to all routes in this controller file. |
64
+ | `middleware` / `middlewares` | `Handler \| Handler[]` | Middleware functions executed prior to controller route handlers. |
65
+ | `guards` | `Guard[]` | Authorization guards executed before controller endpoints. |
66
+ | `interceptors` | `Interceptor[]` | Response interceptors applied to this controller. |
67
+ | `filters` | `Filter[]` | Exception filters applied across this controller. |
68
+ | `onError` | `(err: Error, req: Request, res: Response) => any` | Controller-scoped error hook. |
69
+ | `onResponse` | `(data: any, req: Request, res: Response) => any` | Controller-scoped response hook. |
70
+ | `[key: string]` | `RouteDefinition` | Route builder definitions (e.g. `list: route.get(...)`). |
83
71
 
84
- ### `controller.extend(base, overrides)`
72
+ #### Composition Methods
85
73
 
86
- Inherits all route definitions, middlewares, filters, and configuration from a `base` controller and applies overrides or adds new route endpoints. This provides the functional equivalent of class-based controller inheritance for reusable CRUD templates.
74
+ - **`controller.merge(...controllers)`**: Combines multiple functional controller slices into a single unified controller.
75
+ - **`controller.extend(base, overrides)`**: Inherits routes and configuration from a base controller, allowing overrides and additions.
87
76
 
88
- ```typescript filename="src/http/products/route.ts"
77
+ ```typescript
89
78
  import { controller, route } from 'exisjs/router'
90
79
 
91
- // Reusable generic CRUD controller factory
92
- function createBaseCrud(resource: string) {
93
- return controller({
94
- list: route.get('/', {
95
- async handle() {
96
- return { resource, items: [] }
97
- }
98
- }),
99
- getById: route.get('/:id', {
100
- async handle({ params }) {
101
- return { resource, id: params.id }
102
- }
103
- })
104
- })
105
- }
106
-
107
- // Inherit base endpoints and add specialized or overridden routes
108
- export default controller.extend(createBaseCrud('products'), {
109
- // Additional specialized route
110
- featured: route.get('/featured', {
80
+ const base = controller({
81
+ list: route.get('/', {
111
82
  async handle() {
112
- return { featured: ['Product 1', 'Product 2'] }
113
- }
83
+ return { items: [] }
84
+ },
114
85
  }),
86
+ })
115
87
 
116
- // Override base endpoint
117
- getById: route.get('/:id', {
118
- async handle({ params }) {
119
- return { customProduct: true, id: params.id }
120
- }
121
- })
88
+ export default controller.extend(base, {
89
+ create: route.post('/', {
90
+ async handle({ body }) {
91
+ return { created: true }
92
+ },
93
+ }),
122
94
  })
123
95
  ```
124
96
 
125
97
  ---
126
98
 
127
- ## `route`
99
+ ### `route` Builder
128
100
 
129
- The `route` object provides factory functions for every HTTP method (e.g., `route.get`, `route.post`, `route.patch`, `route.delete`, `route.ws`, `route.sse`).
101
+ Constructs route definitions for standard HTTP methods and streaming protocols:
130
102
 
131
- It takes a path and a `RouteConfig` object.
103
+ - `route.get(path, config)`
104
+ - `route.post(path, config)`
105
+ - `route.put(path, config)`
106
+ - `route.patch(path, config)`
107
+ - `route.delete(path, config)`
108
+ - `route.options(path, config)`
109
+ - `route.head(path, config)`
110
+ - `route.all(path, config)`
111
+ - `route.sse(path, config)`
112
+ - `route.ws(path, config)`
132
113
 
133
- ### Usage
114
+ #### `RouteConfig` Specification
134
115
 
135
116
  ```typescript
136
- import { controller, route } from 'exisjs/router'
137
- import { tex } from 'exisjs/validator'
138
-
139
- export default controller({
140
- createUser: route.post('/users', {
141
- body: tex.object({
142
- email: tex.email(),
143
- password: tex.string({ min: 8 })
144
- }),
145
- async handle(ctx) {
146
- // ctx.body is strictly typed!
147
- const { email, password } = ctx.body
148
- return { success: true, email }
149
- }
150
- })
151
- })
117
+ export interface RouteConfig<B = any, Q = any, P = any, TContext = Record<string, any>, TReturn = any> {
118
+ // Input Validation Schemas
119
+ body?: Schema<B>
120
+ query?: Schema<Q>
121
+ params?: Schema<P>
122
+ response?: Schema<TReturn>
123
+ returns?: Schema<TReturn>
124
+
125
+ // Execution Handler
126
+ handle: (ctx: SuperContext<B, Q, P, TContext>) => TReturn | Promise<TReturn>
127
+
128
+ // Pipeline & Security
129
+ middleware?: Handler<any, any, any, any, TContext> | Handler<any, any, any, any, TContext>[]
130
+ guards?: any | any[]
131
+ interceptors?: any | any[]
132
+ filters?: any | any[]
133
+ roles?: string | string[]
134
+ permissions?: string | string[]
135
+ cors?: boolean | CorsConfig
136
+ host?: string | string[]
137
+ public?: boolean
138
+
139
+ // File Uploads & Tuning
140
+ upload?: string | RouteUploadOptions
141
+ timeout?: number | { ms: number; statusCode?: number; message?: string }
142
+ httpCode?: number
143
+ headers?: Record<string, string>
144
+ redirect?: string | { url: string; statusCode?: number }
145
+
146
+ // OpenAPI Metadata
147
+ summary?: string
148
+ description?: string
149
+ tags?: string[]
150
+ operationId?: string
151
+ deprecated?: boolean
152
+ excludeFromDocs?: boolean
153
+ }
152
154
  ```
153
155
 
154
- ### Options (`RouteConfig`)
155
-
156
- - `body`: A validation schema for the request body (e.g., using `tex` or Zod).
157
- - `query`: A validation schema for the query string (e.g., `tex.pagination()`).
158
- - `params`: A validation schema for the URL path parameters.
159
- - `upload` (`string | RouteUploadOptions`): Declarative multipart upload configuration (e.g. `{ field: 'photo', maxBytes: 10 * 1024 * 1024, mimeTypes: [...] }`). Automatically mounts `fileUpload` middleware and populates `req.file` / `req.files`.
160
- - `timeout` (`number`): Per-route timeout override in milliseconds (e.g. `120000`). Overrides global timeout for this endpoint.
161
- - `response`: A validation schema to serialize/validate the outgoing response.
162
- - `middleware` (`Handler[]`): An array of middleware specific to this route.
163
- - `cors` (`boolean | CorsConfig`): Route-specific CORS overrides.
164
- - `filters` (`any | any[]`): Route-specific filters (like rate limiters or authorization checks).
165
- - `permissions` (`string[]`): An array of required permission flags to access this route.
166
- - `metadata` (`Record<string, any>`): Arbitrary metadata attached to the route for custom plugins/middleware.
167
- - `host` (`string | string[]`): Restrict this route to a specific subdomain/host (e.g. `api.example.com`).
168
- - `handle`: The async function that executes your business logic. It receives a `SuperContext`.
169
-
170
156
  ---
171
157
 
172
- ## `SuperContext`
158
+ ### `SuperContext`
173
159
 
174
- Every `handle` function receives a `SuperContext` object that provides fully-typed access to the request and application environment.
160
+ The strongly-typed execution context supplied to every functional `handle()` function:
175
161
 
176
162
  ```typescript
177
- import { controller, route } from '../router' // Import your custom router (see below)
178
- import { tex } from 'exisjs/validator'
179
-
180
- export default controller({
181
- getUserInfo: route.get('/info/:id', {
182
- params: tex.object({ id: tex.string() }),
183
- query: tex.object({ debug: tex.boolean({ optional: true }) }),
184
- async handle({ body, query, params, headers, req, res, app, state, user, workspace }) {
185
- // 1. `body`, `query`, and `params` are pre-validated and fully typed!
186
- const id = params.id
187
-
188
- // 2. `req` and `res` are the raw ExisJS request/response objects
189
- const ip = req.ip
190
-
191
- // 3. `app` is the global ExisJS application instance
192
- const db = app.resolve('Database')
193
-
194
- // 4. `resolve` is the request-scoped dependency injection resolver!
195
- const usersService = resolve(UsersService)
196
-
197
- // 5. `state` is a mutable object shared across middlewares
198
- const cacheHit = state.cacheHit
199
-
200
- // 6. `user` and `workspace` are strictly typed from your custom context!
201
- return { id, email: user.email, workspace }
202
- }
203
- })
204
- })
163
+ export interface SuperContext<B = any, Q = any, P = any, TContext = Record<string, any>> {
164
+ /** Pre-validated request body */
165
+ body: B
166
+ /** Pre-validated query string parameters */
167
+ query: Q
168
+ /** Pre-validated URL route parameters */
169
+ params: P
170
+ /** Request HTTP headers */
171
+ headers: Record<string, string | string[] | undefined>
172
+ /** ExisRequest instance */
173
+ req: Request<B, Q, P, TContext>
174
+ /** ExisResponse instance */
175
+ res: Response
176
+ /** Exis Application instance */
177
+ app: App
178
+ /** Mutable dictionary shared across route middlewares */
179
+ state: Record<string, any>
180
+ /** Request-scoped Dependency Injection resolver */
181
+ resolve: <T>(token: ProviderToken<T>) => T
182
+ /** Single uploaded file */
183
+ file?: ExisFile
184
+ /** Multiple uploaded files */
185
+ files?: ExisFile[] | Record<string, ExisFile[]>
186
+ /** Multipart text fields */
187
+ fields?: Record<string, any>
188
+ }
205
189
  ```
206
190
 
207
191
  ---
208
192
 
209
- ## `createRouter<T>()`
193
+ ### `defineBoundary(config)`
210
194
 
211
- If your application relies heavily on custom context (e.g., passing a `user` or `workspace` object injected via middleware to every route), you can create a strongly-typed custom router using `createRouter<T>()`.
195
+ Declares hierarchical folder-level middleware, security controls, and response defaults inside `boundary.ts` files.
212
196
 
213
- This removes the need to constantly cast or redefine types.
214
-
215
- ### Usage
216
-
217
- ```typescript filename="src/router.ts"
218
- import { createRouter } from "exisjs/router";
197
+ ```typescript
198
+ function defineBoundary<TContext = Record<string, any>>(
199
+ config: BoundaryConfig<TContext>
200
+ ): BoundaryConfig<TContext>
201
+ ```
219
202
 
220
- interface User {
221
- userId: string;
222
- role: string;
223
- email: string;
224
- }
203
+ #### `BoundaryConfig` Options
204
+
205
+ | Property | Type | Description |
206
+ | :--- | :--- | :--- |
207
+ | `middleware` / `middlewares` | `Handler \| Handler[]` | Middleware cascaded to all routes in this directory and subdirectories. |
208
+ | `cors` | `boolean \| CorsConfig` | Directory-scoped CORS policy. |
209
+ | `headers` | `Record<string, string>` | Default response headers applied to matching routes. |
210
+ | `guards` | `Guard[]` | Authorization guards applied to matching routes. |
211
+ | `interceptors` | `Interceptor[]` | Response interceptors applied to matching routes. |
212
+ | `filters` | `Filter[]` | Exception filters applied to matching routes. |
213
+ | `exclude` | `(string \| BoundaryExcludeRule)[]` | Route patterns or method rules excluded from this boundary. |
214
+ | `timeout` | `number \| ((req: Request) => number)` | Timeout threshold in milliseconds for routes in this directory. |
215
+ | `blockSuspiciousProbes` | `boolean \| BlockProbesOptions` | Suppresses and blackholes malicious scanner probes (`.env`, `wp-admin`, `.git`). |
216
+ | `metadata` | `Record<string, any>` | Metadata (e.g. OpenAPI tags) cascaded to all child routes. |
225
217
 
226
- interface AppContext {
227
- user: User;
228
- workspace: string; // Just an example to prove we can pass any property
229
- }
218
+ ---
230
219
 
231
- // Create a completely customized router scoped to this application
232
- export const { route, controller } = createRouter<AppContext>();
233
- ```
220
+ ## 2. Class-Based OOP Routing API (`exisjs/decorators`)
234
221
 
235
- Now, when you use this custom `route` object, the `SuperContext` will automatically infer your custom context fields!
222
+ The Class-Based paradigm uses standard TypeScript decorators for controller definitions, route methods, parameter injection, and pipeline lifecycle hooks.
236
223
 
237
- ```typescript filename="src/http/users/route.ts"
238
- import { route, controller } from '../../router'
239
-
240
- export default controller({
241
- me: route.get('/me', {
242
- async handle({ user, workspace }) {
243
- // `user` and `workspace` are strictly typed!
244
- return { id: user.userId, workspace }
245
- }
246
- })
247
- })
224
+ ```typescript
225
+ import {
226
+ Controller,
227
+ Get,
228
+ Post,
229
+ Put,
230
+ Patch,
231
+ Delete,
232
+ Options,
233
+ Head,
234
+ All,
235
+ Ws,
236
+ Sse,
237
+ Body,
238
+ Param,
239
+ Query,
240
+ QueryParam,
241
+ Headers,
242
+ Req,
243
+ Res,
244
+ Next,
245
+ Ip,
246
+ Session,
247
+ UploadedFile,
248
+ UploadedFiles,
249
+ createParamDecorator,
250
+ Use,
251
+ Guards,
252
+ Filters,
253
+ Interceptors,
254
+ HttpCode,
255
+ Header,
256
+ Redirect,
257
+ SetMetadata,
258
+ Reflector,
259
+ } from 'exisjs/decorators'
248
260
  ```
249
261
 
250
262
  ---
251
263
 
252
- ## 2. Class-Based Routing (OOP)
264
+ ### `@Controller(prefix?, options?)`
253
265
 
254
- If you prefer an Object-Oriented paradigm, ExisJS provides a robust set of decorators via `exisjs/decorators`. These decorators are built on standard ECMAScript decorators and do not require `reflect-metadata`, making them incredibly fast and lightweight.
266
+ Decorates a class as an HTTP controller slice mounted at the specified route prefix.
255
267
 
256
- ### Usage
268
+ ```typescript
269
+ function Controller(
270
+ prefixOrOptions?: string | ControllerOptions,
271
+ options?: ControllerOptions
272
+ ): ClassDecorator
273
+ ```
274
+
275
+ #### Example
257
276
 
258
277
  ```typescript filename="src/http/users/route.ts"
259
278
  import { Controller, Get, Post, Body, Param } from 'exisjs/decorators'
@@ -261,87 +280,94 @@ import { tex } from 'exisjs/validator'
261
280
 
262
281
  @Controller('/users')
263
282
  export default class UserController {
264
-
265
283
  @Get('/:id')
266
284
  async getUser(@Param('id') id: string) {
267
285
  return { id, name: 'Alice' }
268
286
  }
269
287
 
270
288
  @Post('/', {
271
- body: tex.object({ email: tex.email(), password: tex.string() })
289
+ body: tex.object({
290
+ name: tex.string(),
291
+ email: tex.email(),
292
+ }),
272
293
  })
273
- async createUser(@Body() body: any) {
274
- // body is automatically validated!
275
- return { success: true, email: body.email }
294
+ async createUser(@Body() body: { name: string; email: string }) {
295
+ return { success: true, user: body }
276
296
  }
277
297
  }
278
298
  ```
279
299
 
280
- ### Route Decorators
281
-
282
- - `@Get(path, schema?)`
283
- - `@Post(path, schema?)`
284
- - `@Put(path, schema?)`
285
- - `@Patch(path, schema?)`
286
- - `@Delete(path, schema?)`
287
- - `@Ws(path, schema?)`: Defines a WebSocket route.
288
- - `@Sse(path, schema?)`: Defines a Server-Sent Events stream route.
300
+ ---
289
301
 
290
- *Note: Just like functional routes, you can pass an optional `RouteSchema` to any of these decorators to automatically validate requests!*
302
+ ### HTTP Method Decorators
291
303
 
292
- ### Parameter Injectors
304
+ Method decorators attach HTTP handler logic and validation schemas to controller methods:
293
305
 
294
- Use parameter decorators to automatically extract data from the incoming request and inject them into your method arguments:
306
+ - `@Get(path?, schema?)`
307
+ - `@Post(path?, schema?)`
308
+ - `@Put(path?, schema?)`
309
+ - `@Patch(path?, schema?)`
310
+ - `@Delete(path?, schema?)`
311
+ - `@Options(path?, schema?)`
312
+ - `@Head(path?, schema?)`
313
+ - `@All(path?, schema?)`
314
+ - `@Sse(path?, schema?)`
315
+ - `@Ws(path?, schema?)`
295
316
 
296
- - `@Body()`: Injects the parsed JSON request body.
297
- - `@Param('id')`: Injects a specific URL path parameter.
298
- - `@QueryParam('search')` / `@Query('search')`: Injects a specific query string parameter.
299
- - `@Headers('authorization')`: Injects a specific HTTP header.
300
- - `@Req()`: Injects the raw ExisJS Request object.
301
- - `@Res()`: Injects the raw ExisJS Response object.
302
- - `@Next()`: Injects the next function.
303
- - `@Socket()`: Injects the active WebSocket instance (for `@Ws` routes).
304
- - `@Stream()`: Injects the active SSE instance (for `@Sse` routes).
305
- - `createParamDecorator(factory)`: Factory to construct custom parameter decorators.
317
+ ---
306
318
 
307
- ### Extended Decorators
319
+ ### Parameter Injector Decorators
308
320
 
309
- ExisJS provides a rich ecosystem of decorators to apply middleware, configure responses, and enforce security on both individual routes and entire controllers:
321
+ Parameter decorators extract and inject typed data directly into handler method parameters:
310
322
 
311
- #### Configuration & Pipeline
312
- - `@Use(...items)`: Unified decorator that applies middlewares, Guards (`canActivate`), Interceptors (`intercept`), and Exception Filters (`catch`) to a route or entire controller.
313
- - `@Hosts('api.example.com')`: Restricts the route or controller to specific domains.
323
+ | Decorator | Injected Data | Example |
324
+ | :--- | :--- | :--- |
325
+ | `@Body(property?)` | Request body payload or sub-property | `@Body() data: CreateUserDto` |
326
+ | `@Param(key?)` | URL path parameters dictionary or specific param | `@Param('id') userId: string` |
327
+ | `@Query(key?)` / `@QueryParam(key?)` | Query string parameters dictionary or specific key | `@Query('limit') limit: number` |
328
+ | `@Headers(name?)` | Request headers dictionary or specific header | `@Headers('authorization') auth: string` |
329
+ | `@Req()` | Underlying `ExisRequest` instance | `@Req() req: Request` |
330
+ | `@Res()` | Underlying `ExisResponse` instance | `@Res() res: Response` |
331
+ | `@Next()` | Pipeline `NextFunction` callback | `@Next() next: NextFunction` |
332
+ | `@Ip()` | Client remote IP address | `@Ip() clientIp: string` |
333
+ | `@UploadedFile(field?)` | Single uploaded multipart file object | `@UploadedFile('avatar') file: ExisFile` |
334
+ | `@UploadedFiles(field?)` | Multiple uploaded multipart file objects | `@UploadedFiles('docs') files: ExisFile[]` |
314
335
 
315
- #### Security & Metadata
316
- - `@Permissions('admin')`: Requires specific permission flags.
317
- - `@SetMetadata(key, value)`: Attaches arbitrary metadata to classes and handler methods (retrieved via `Reflector`).
318
- - `Reflector`: Reads and merges metadata attached with `@SetMetadata`.
336
+ ---
319
337
 
320
- #### Responses & Reliability
321
- - `@HttpCode(201)`: Forces a specific HTTP status code for successful responses.
322
- - `@Header('X-Custom', 'Value')`: Automatically injects a specific response header.
323
- - `@Returns(schema)`: Explicitly defines the response schema for OpenAPI generation.
324
- - `@Idempotent()`: Enforces idempotency keys for mutations (e.g., checkout/payments).
338
+ ### `createParamDecorator(factory)`
325
339
 
326
- ---
340
+ Constructs custom parameter decorators to extract specialized values from the request context:
327
341
 
328
- ## 3. Streaming & Backpressure Pipeline
342
+ ```typescript
343
+ import { createParamDecorator } from 'exisjs/decorators'
329
344
 
330
- The response object supports universal streaming with backpressure management across all route types:
345
+ export const CurrentUser = createParamDecorator((data: string | undefined, ctx) => {
346
+ const req = ctx.req
347
+ return data ? req.user?.[data] : req.user
348
+ })
331
349
 
332
- - `res.sendStream(source)`: Streams a Node.js `stream.Readable`, Web `ReadableStream`, or `AsyncIterable` directly to the client socket. Pauses consumption when socket buffers fill up and terminates source readers upon client disconnection.
333
- - `res.stream(source)`: Alias for `sendStream()`.
334
- - **Automatic Handler Auto-Piping**: Returning a Node.js stream, Web standard `ReadableStream`, or `AsyncIterable` generator directly from `handle()` automatically delegates to `res.sendStream()`.
350
+ // Usage in controller:
351
+ @Get('/profile')
352
+ getProfile(@CurrentUser() user: any, @CurrentUser('id') userId: string) {
353
+ return { userId, user }
354
+ }
355
+ ```
335
356
 
336
357
  ---
337
358
 
338
- ## 4. `AppRouter` Client Manifest Contract
359
+ ### Pipeline & Response Decorators
339
360
 
340
- ExisJS generates a compile-time route manifest in `.exis/types.d.ts` and `.exis/types/index.d.ts`:
361
+ | Decorator | Target | Description |
362
+ | :--- | :--- | :--- |
363
+ | `@Use(...middlewares)` | Class / Method | Applies middleware functions to the controller or route. |
364
+ | `@Guards(...guards)` | Class / Method | Attaches authorization guards. |
365
+ | `@Filters(...filters)` | Class / Method | Attaches exception filters. |
366
+ | `@Interceptors(...interceptors)` | Class / Method | Attaches response/request interceptors. |
367
+ | `@HttpCode(status)` | Method | Sets the default HTTP status code for successful responses. |
368
+ | `@Header(name, value)` | Method | Sets static HTTP response headers. |
369
+ | `@Redirect(url, status?)` | Method | Redirects responses to the target URL. |
370
+ | `@SetMetadata(key, value)` | Class / Method | Attaches arbitrary metadata readable via `Reflector`. |
341
371
 
342
- - `AppRouterPaths`: Flat dictionary mapping URL route patterns (e.g. `'/users/:id'`) to their strongly-typed handler contracts (`(payload?: B, options?: { query?: Q; params?: P }) => Promise<R>`).
343
- - `AppRouterTree`: Hierarchical dot-notation tree mapping nested folder paths (`users: { ':id': ... }`) for `@exisjs/fetch`'s `createClient<AppRouter>()`.
344
- - `AppRouter`: Combined intersection type `AppRouterPaths & AppRouterTree`.
345
- - `RouteDefinition.__type`: Phantom type property on every route definition preserving exact inferred return types, DTO schemas, and Drizzle/Prisma models.
346
372
 
347
373