exisjs 0.7.9 → 0.7.11

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 (140) hide show
  1. package/dist/cli/commands/dev.d.ts.map +1 -1
  2. package/dist/cli/commands/dev.js +8 -1
  3. package/dist/cli/commands/dev.js.map +1 -1
  4. package/dist/config/config.d.ts +2 -1
  5. package/dist/config/config.d.ts.map +1 -1
  6. package/dist/config/config.js +1 -1
  7. package/dist/config/config.js.map +1 -1
  8. package/dist/config/types.d.ts +7 -0
  9. package/dist/config/types.d.ts.map +1 -1
  10. package/dist/database/lifecycle.d.ts +2 -1
  11. package/dist/database/lifecycle.d.ts.map +1 -1
  12. package/dist/database/lifecycle.js +6 -2
  13. package/dist/database/lifecycle.js.map +1 -1
  14. package/dist/decorators/constants.d.ts +2 -0
  15. package/dist/decorators/constants.d.ts.map +1 -1
  16. package/dist/decorators/constants.js +3 -1
  17. package/dist/decorators/constants.js.map +1 -1
  18. package/dist/decorators/core.d.ts +14 -0
  19. package/dist/decorators/core.d.ts.map +1 -1
  20. package/dist/decorators/core.js +156 -41
  21. package/dist/decorators/core.js.map +1 -1
  22. package/dist/decorators/filters.d.ts +2 -10
  23. package/dist/decorators/filters.d.ts.map +1 -1
  24. package/dist/decorators/filters.js.map +1 -1
  25. package/dist/decorators/index.d.ts +1 -0
  26. package/dist/decorators/index.d.ts.map +1 -1
  27. package/dist/decorators/index.js +1 -0
  28. package/dist/decorators/index.js.map +1 -1
  29. package/dist/decorators/lifecycle.d.ts +29 -0
  30. package/dist/decorators/lifecycle.d.ts.map +1 -1
  31. package/dist/decorators/params.d.ts +3 -7
  32. package/dist/decorators/params.d.ts.map +1 -1
  33. package/dist/decorators/params.js.map +1 -1
  34. package/dist/decorators/pipes.d.ts +91 -0
  35. package/dist/decorators/pipes.d.ts.map +1 -0
  36. package/dist/decorators/pipes.js +283 -0
  37. package/dist/decorators/pipes.js.map +1 -0
  38. package/dist/di/container.d.ts +8 -4
  39. package/dist/di/container.d.ts.map +1 -1
  40. package/dist/di/container.js +186 -68
  41. package/dist/di/container.js.map +1 -1
  42. package/dist/di/decorators.d.ts +23 -5
  43. package/dist/di/decorators.d.ts.map +1 -1
  44. package/dist/di/decorators.js +83 -20
  45. package/dist/di/decorators.js.map +1 -1
  46. package/dist/di/forward-ref.d.ts +23 -0
  47. package/dist/di/forward-ref.d.ts.map +1 -0
  48. package/dist/di/forward-ref.js +30 -0
  49. package/dist/di/forward-ref.js.map +1 -0
  50. package/dist/di/index.d.ts +3 -2
  51. package/dist/di/index.d.ts.map +1 -1
  52. package/dist/di/index.js +8 -1
  53. package/dist/di/index.js.map +1 -1
  54. package/dist/docs/BENCHMARKS.md +5 -5
  55. package/dist/docs/content/config.mdx +16 -6
  56. package/dist/docs/content/controllers.mdx +230 -0
  57. package/dist/docs/content/errors.mdx +16 -9
  58. package/dist/docs/content/modules.mdx +23 -4
  59. package/dist/docs/content/providers.mdx +42 -6
  60. package/dist/docs/content/requests.mdx +9 -3
  61. package/dist/docs/content/responses.mdx +19 -5
  62. package/dist/docs/content/security.mdx +48 -35
  63. package/dist/docs/content/structure.mdx +119 -93
  64. package/dist/docs/reference/config.mdx +29 -18
  65. package/dist/docs/reference/decorators.mdx +15 -4
  66. package/dist/docs/reference/di.mdx +23 -16
  67. package/dist/docs/reference/router.mdx +75 -0
  68. package/dist/docs/reference/validator.mdx +2 -2
  69. package/dist/error/errors.d.ts.map +1 -1
  70. package/dist/error/errors.js +12 -0
  71. package/dist/error/errors.js.map +1 -1
  72. package/dist/middleware/security.d.ts +18 -0
  73. package/dist/middleware/security.d.ts.map +1 -1
  74. package/dist/middleware/security.js +32 -0
  75. package/dist/middleware/security.js.map +1 -1
  76. package/dist/router/controller-registrar.d.ts.map +1 -1
  77. package/dist/router/controller-registrar.js +142 -87
  78. package/dist/router/controller-registrar.js.map +1 -1
  79. package/dist/router/index.d.ts.map +1 -1
  80. package/dist/router/index.js.map +1 -1
  81. package/dist/router/radix.d.ts +1 -0
  82. package/dist/router/radix.d.ts.map +1 -1
  83. package/dist/router/radix.js +21 -0
  84. package/dist/router/radix.js.map +1 -1
  85. package/dist/router/route-builder.d.ts +160 -29
  86. package/dist/router/route-builder.d.ts.map +1 -1
  87. package/dist/router/route-builder.js +105 -31
  88. package/dist/router/route-builder.js.map +1 -1
  89. package/dist/router/route-scanner.d.ts +3 -2
  90. package/dist/router/route-scanner.d.ts.map +1 -1
  91. package/dist/router/route-scanner.js.map +1 -1
  92. package/dist/router/router.d.ts.map +1 -1
  93. package/dist/router/router.js +44 -0
  94. package/dist/router/router.js.map +1 -1
  95. package/dist/router/scanner/functional-compiler.d.ts +1 -1
  96. package/dist/router/scanner/functional-compiler.d.ts.map +1 -1
  97. package/dist/router/scanner/functional-compiler.js +403 -64
  98. package/dist/router/scanner/functional-compiler.js.map +1 -1
  99. package/dist/router/types.d.ts +4 -2
  100. package/dist/router/types.d.ts.map +1 -1
  101. package/dist/server/adapters/uws-adapter.d.ts +17 -0
  102. package/dist/server/adapters/uws-adapter.d.ts.map +1 -1
  103. package/dist/server/adapters/uws-adapter.js +101 -7
  104. package/dist/server/adapters/uws-adapter.js.map +1 -1
  105. package/dist/server/app.d.ts +2 -0
  106. package/dist/server/app.d.ts.map +1 -1
  107. package/dist/server/app.js +38 -0
  108. package/dist/server/app.js.map +1 -1
  109. package/dist/server/helpers/etag.d.ts +1 -0
  110. package/dist/server/helpers/etag.d.ts.map +1 -1
  111. package/dist/server/helpers/etag.js +168 -9
  112. package/dist/server/helpers/etag.js.map +1 -1
  113. package/dist/server/helpers/ip.d.ts.map +1 -1
  114. package/dist/server/helpers/ip.js +75 -17
  115. package/dist/server/helpers/ip.js.map +1 -1
  116. package/dist/server/request-handler.d.ts.map +1 -1
  117. package/dist/server/request-handler.js +35 -19
  118. package/dist/server/request-handler.js.map +1 -1
  119. package/dist/server/request.d.ts +4 -3
  120. package/dist/server/request.d.ts.map +1 -1
  121. package/dist/server/request.js +26 -8
  122. package/dist/server/request.js.map +1 -1
  123. package/dist/server/response.d.ts +1 -0
  124. package/dist/server/response.d.ts.map +1 -1
  125. package/dist/server/response.js +48 -22
  126. package/dist/server/response.js.map +1 -1
  127. package/dist/swagger/generator.d.ts.map +1 -1
  128. package/dist/swagger/generator.js +66 -22
  129. package/dist/swagger/generator.js.map +1 -1
  130. package/dist/testing/client.d.ts +15 -11
  131. package/dist/testing/client.d.ts.map +1 -1
  132. package/dist/testing/client.js +32 -11
  133. package/dist/testing/client.js.map +1 -1
  134. package/dist/utils/logger.d.ts.map +1 -1
  135. package/dist/utils/logger.js +2 -0
  136. package/dist/utils/logger.js.map +1 -1
  137. package/dist/validator/engine.d.ts.map +1 -1
  138. package/dist/validator/engine.js +45 -0
  139. package/dist/validator/engine.js.map +1 -1
  140. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  ---
2
- nextUrl: "/docs/structure"
3
- nextTitle: "Project Structure"
2
+ nextUrl: '/docs/structure'
3
+ nextTitle: 'Project Structure'
4
4
  ---
5
5
 
6
6
  # Security & Edge Middleware
@@ -33,7 +33,7 @@ export default exis({
33
33
  app.use(timeout(10000))
34
34
  app.use(hpp())
35
35
  app.use(mongoSanitize())
36
- }
36
+ },
37
37
  })
38
38
  ```
39
39
 
@@ -52,9 +52,9 @@ export const config = defineBoundary({})
52
52
  // Apply rate limiting chain step across the boundary
53
53
  export const limiter = rateLimit({
54
54
  windowMs: 60 * 1000, // 1 minute window
55
- max: 100, // 100 requests per window
55
+ max: 100, // 100 requests per window
56
56
  message: 'Too many requests from this IP, please try again after a minute',
57
- keyGenerator: (req) => req.ip // Uses normalized IPv4/IPv6 client IP
57
+ keyGenerator: (req) => req.ip, // Uses normalized IPv4/IPv6 client IP
58
58
  })
59
59
  ```
60
60
 
@@ -62,13 +62,13 @@ export const limiter = rateLimit({
62
62
 
63
63
  `rateLimit` automatically emits standard IETF draft and legacy rate-limiting headers on every response:
64
64
 
65
- | Header | Description | Example |
66
- | :--- | :--- | :--- |
67
- | `RateLimit-Limit` | Maximum request quota for the configured window. | `100` |
68
- | `RateLimit-Remaining` | Remaining requests permitted within the current window. | `42` |
69
- | `RateLimit-Reset` | Time remaining in seconds until the window quota resets. | `18` |
70
- | `Retry-After` | Included on `429 Too Many Requests` (RFC 6585) indicating seconds to wait. | `18` |
71
- | `X-RateLimit-*` | Legacy header equivalents for backwards compatibility. | `X-RateLimit-Reset: 18` |
65
+ | Header | Description | Example |
66
+ | :-------------------- | :------------------------------------------------------------------------- | :---------------------- |
67
+ | `RateLimit-Limit` | Maximum request quota for the configured window. | `100` |
68
+ | `RateLimit-Remaining` | Remaining requests permitted within the current window. | `42` |
69
+ | `RateLimit-Reset` | Time remaining in seconds until the window quota resets. | `18` |
70
+ | `Retry-After` | Included on `429 Too Many Requests` (RFC 6585) indicating seconds to wait. | `18` |
71
+ | `X-RateLimit-*` | Legacy header equivalents for backwards compatibility. | `X-RateLimit-Reset: 18` |
72
72
 
73
73
  > [!NOTE]
74
74
  > The core `rateLimit` middleware runs using an in-memory native off-heap store. For distributed, multi-instance rate limiting backed by Redis, use `@exisjs/cache`.
@@ -77,34 +77,47 @@ export const limiter = rateLimit({
77
77
 
78
78
  ## 3. Role-Based Access Control (RBAC) Metadata
79
79
 
80
- ExisJS allows declarative permission metadata to be attached to routes:
81
-
82
- #### Functional Paradigm
80
+ ---
83
81
 
84
- ```typescript filename="src/http/admin/route.ts"
85
- import { controller, route } from 'exisjs/router'
82
+ ## 4. Timing-Safe Operations & Authentication
86
83
 
87
- export default controller({
88
- deleteItem: route.delete('/:id', {
89
- permissions: ['admin'],
90
- handle({ params }) {
91
- return { success: true, deleted: params.id }
92
- }
93
- })
94
- })
95
- ```
84
+ To prevent side-channel timing attacks when validating authentication tokens, API keys, HMAC signatures, or webhook secrets, ExisJS provides the `timingSafeEqual` helper:
96
85
 
97
- #### Class-Based (OOP) Paradigm
86
+ ```typescript filename="src/http/common/guards/auth.guard.ts"
87
+ import { timingSafeEqual } from 'exisjs/middleware'
88
+ import { UnauthorizedException } from 'exisjs/error'
98
89
 
99
- ```typescript filename="src/http/admin/route.ts"
100
- import { Controller, Delete, Param, Permissions } from 'exisjs/decorators'
90
+ export function apiKeyGuard(req: any) {
91
+ const apiKey = req.headers['x-api-key']
92
+ const secretKey = process.env.API_KEY
101
93
 
102
- @Controller()
103
- export default class AdminController {
104
- @Permissions('admin')
105
- @Delete('/:id')
106
- async deleteItem(@Param('id') id: string) {
107
- return { success: true, deleted: id }
94
+ if (!timingSafeEqual(apiKey, secretKey)) {
95
+ throw new UnauthorizedException('Invalid API Key')
108
96
  }
109
97
  }
110
98
  ```
99
+
100
+ `timingSafeEqual` executes in constant time regardless of string lengths or partial matching prefixes, neutralizing timing analysis exploits.
101
+
102
+ ---
103
+
104
+ ## 5. Security Scanner Noise Suppression & Blackhole Handler
105
+
106
+ ExisJS includes built-in protection against automated vulnerability bots scanning for `.env`, `/.git`, `/.DS_Store`, WordPress/PHP configs, and Java actuator dumps:
107
+
108
+ ```typescript filename="src/http/server.ts"
109
+ import { exis } from 'exisjs'
110
+ import { blockSuspiciousProbes } from 'exisjs/middleware'
111
+
112
+ export default exis({
113
+ async onStart(app) {
114
+ // Intercepts scanner probes and blackholes them without triggering downstream error logs
115
+ app.use(
116
+ blockSuspiciousProbes({
117
+ blackhole: true, // Terminate immediately with 404/empty body
118
+ silent: true, // Suppress 404 noise from production logs
119
+ })
120
+ )
121
+ },
122
+ })
123
+ ```
@@ -1,6 +1,6 @@
1
1
  ---
2
- nextUrl: "/docs/config"
3
- nextTitle: "Configuration"
2
+ nextUrl: '/docs/config'
3
+ nextTitle: 'Configuration'
4
4
  ---
5
5
 
6
6
  # Project Structure
@@ -22,17 +22,18 @@ my-backend/
22
22
  ├── .exis/ # Internal framework cache and build artifacts
23
23
  ├── src/
24
24
  │ ├── http/
25
- │ │ ├── server.ts # Application entry point
25
+ │ │ ├── server.ts # Application entry point & lifecycle hooks
26
26
  │ │ ├── error.ts # Centralized framework exception handler (onError)
27
27
  │ │ ├── route.ts # Root routes (e.g. GET /api)
28
28
  │ │ ├── boundary.ts # Root boundary (config + request pipeline)
29
29
  │ │ ├── users/
30
30
  │ │ │ ├── route.ts # User endpoints
31
- │ │ │ └── schema.ts # Validation schemas (tex) — mandatory alongside route.ts
31
+ │ │ │ ├── schema.ts # Validation schemas (tex) — mandatory alongside route.ts
32
+ │ │ │ └── service.ts # Business logic / data access, DI-injectable
32
33
  │ │ ├── posts/
33
34
  │ │ │ ├── route.ts
34
35
  │ │ │ ├── schema.ts
35
- │ │ │ └── service.ts # Business logic / data access, DI-injectable
36
+ │ │ │ └── service.ts
36
37
  │ │ └── admin/
37
38
  │ │ ├── boundary.ts # Applies to /admin/* and everything nested
38
39
  │ │ ├── route.ts
@@ -40,10 +41,16 @@ my-backend/
40
41
  │ │ ├── route.ts
41
42
  │ │ ├── schema.ts
42
43
  │ │ └── service.ts
43
- │ ├── models/ # Free-form. Populated once you add a data package.
44
- │ ├── services/ # Free-form. Shared business logic.
45
- │ ├── dto/ # Free-form. Request/response types.
46
- │ └── config/env.ts # Type-safe environment schema (tex)
44
+ │ ├── cron/ # Scheduled tasks & background cron jobs (auto-discovered)
45
+ │ │ └── cleanup.ts
46
+ │ ├── database/ # Database connection, models/schemas, migrations, or repositories
47
+ │ │ ├── db.ts # Connection client & lifecycle registration (registerDatabase)
48
+ │ │ └── models/ # Database entity models / ORM schemas (e.g. user.model.ts)
49
+ │ ├── common/ # Shared cross-cutting guards, interceptors, or filters
50
+ │ │ └── guards/
51
+ │ │ └── auth.guard.ts
52
+ │ └── config/
53
+ │ └── env.ts # Type-safe environment schema (tex.env)
47
54
  ├── tests/
48
55
  ├── .env
49
56
  ├── exis.config.ts
@@ -51,8 +58,7 @@ my-backend/
51
58
  └── tsconfig.json
52
59
  ```
53
60
 
54
- > `src/jobs/` is **not** part of the core structure. It only appears once you
55
- > install `@exisjs/queue` — see that package's docs for its convention.
61
+ > `src/cron/` is natively auto-discovered by the framework at boot. All files inside `src/cron/` or colocated at `src/http/**/cron.ts` are automatically mounted. Database connections and models reside under `src/database/` (or `src/models/`).
56
62
 
57
63
  ---
58
64
 
@@ -62,18 +68,19 @@ A file's name declares its role. Functional vs. class-based (OOP) only changes
62
68
  _syntax_, never _responsibility_.
63
69
 
64
70
  ExisJS supports two naming conventions seamlessly:
71
+
65
72
  1. **Standard Flat Names** (default): `route.ts`, `schema.ts`, `service.ts`, `boundary.ts`.
66
73
  2. **Feature-Prefixed Named Files**: `<name>.route.ts`, `<name>.schema.ts`, `<name>.service.ts`, `<name>.boundary.ts` (e.g., `user.route.ts`, `user.schema.ts`, `user.service.ts`).
67
74
 
68
75
  Both styles are automatically detected by the route scanner, CLI manifest generator, and development watcher with 100% parity.
69
76
 
70
- | File Pattern | Role | Never contains |
71
- | :--- | :--- | :--- |
72
- | `route.ts` / `*.route.ts` | Binds a URL + method to a handler. | Business logic, validation logic |
73
- | `schema.ts` / `*.schema.ts` | `tex` validation schemas, imported by route files. Mandatory for every route accepting input. | Handlers, routing |
74
- | `service.ts` / `*.service.ts` | Business logic and data access. DI-injectable. | Routing, response shaping |
75
- | `boundary.ts` / `*.boundary.ts` | Folder-scoped config + request pipeline for everything in this folder and below. | — |
76
- | `error.ts` / `*.error.ts` | Centralized exception and validation error handler (`onError`). Auto-mounted by the framework. | Business logic, routes |
77
+ | File Pattern | Role | Never contains |
78
+ | :------------------------------ | :--------------------------------------------------------------------------------------------- | :------------------------------- |
79
+ | `route.ts` / `*.route.ts` | Binds a URL + method to a handler. | Business logic, validation logic |
80
+ | `schema.ts` / `*.schema.ts` | `tex` validation schemas, imported by route files. Mandatory for every route accepting input. | Handlers, routing |
81
+ | `service.ts` / `*.service.ts` | Business logic and data access. DI-injectable. | Routing, response shaping |
82
+ | `boundary.ts` / `*.boundary.ts` | Folder-scoped config + request pipeline for everything in this folder and below. | — |
83
+ | `error.ts` / `*.error.ts` | Centralized exception and validation error handler (`onError`). Auto-mounted by the framework. | Business logic, routes |
77
84
 
78
85
  ---
79
86
 
@@ -89,18 +96,18 @@ Any `route.ts` (or `.js`) under `src/http/` maps to a URL from its folder path:
89
96
  ### Functional
90
97
 
91
98
  ```typescript filename="src/http/users/route.ts"
92
- import { controller, route } from "exisjs/router";
93
- import { UserParamsSchema } from "./schema";
94
- import { fetchUser } from "./service";
99
+ import { controller, route } from 'exisjs/router'
100
+ import { UserParamsSchema } from './schema'
101
+ import { fetchUser } from './service'
95
102
 
96
103
  export default controller({
97
- getUser: route.get("/users/:id", {
104
+ getUser: route.get('/users/:id', {
98
105
  params: UserParamsSchema,
99
106
  async handle({ params }) {
100
- return fetchUser(params.id);
107
+ return fetchUser(params.id)
101
108
  },
102
109
  }),
103
- });
110
+ })
104
111
  ```
105
112
 
106
113
  ### Class-based (OOP)
@@ -108,17 +115,17 @@ export default controller({
108
115
  The decorated class **is** the route file — no separate controller file:
109
116
 
110
117
  ```typescript filename="src/http/users/route.ts"
111
- import { Controller, Get, Param } from "exisjs/decorators";
112
- import { UserParamsSchema } from "./schema";
113
- import { UserService } from "./service";
118
+ import { Controller, Get, Param } from 'exisjs/decorators'
119
+ import { UserParamsSchema } from './schema'
120
+ import { UserService } from './service'
114
121
 
115
- @Controller("/users")
122
+ @Controller('/users')
116
123
  export default class UserRoute {
117
124
  constructor(private userService: UserService) {}
118
125
 
119
- @Get("/:id", UserParamsSchema)
120
- async getUser(@Param("id") id: string) {
121
- return this.userService.fetchUser(id);
126
+ @Get('/:id', UserParamsSchema)
127
+ async getUser(@Param('id') id: string) {
128
+ return this.userService.fetchUser(id)
122
129
  }
123
130
  }
124
131
  ```
@@ -132,9 +139,9 @@ export default class UserRoute {
132
139
  ## `src/http/**/schema.ts`
133
140
 
134
141
  ```typescript filename="src/http/users/schema.ts"
135
- import { tex } from "exisjs/validator";
142
+ import { tex } from 'exisjs/validator'
136
143
 
137
- export const UserParamsSchema = tex.object({ id: tex.string() });
144
+ export const UserParamsSchema = tex.object({ id: tex.string() })
138
145
  ```
139
146
 
140
147
  `tex` is the only validation engine. Schemas here are also the source for
@@ -156,7 +163,7 @@ export async function fetchUser(id: string) {
156
163
 
157
164
  ```typescript filename="src/http/users/service.ts"
158
165
  // OOP
159
- import { Injectable } from "exisjs/decorators";
166
+ import { Injectable } from 'exisjs/decorators'
160
167
 
161
168
  @Injectable()
162
169
  export class UserService {
@@ -178,49 +185,49 @@ two independent halves:
178
185
  2. **Pipeline & Hooks** — real code, auto-detected by function signature. No imports,
179
186
  no manual registration required.
180
187
 
181
- | Signature | Detected as | Runs |
182
- | :--- | :--- | :--- |
183
- | `(req, res, next)` | Chain Step | In file/declaration order, before the route handler |
184
- | `beforeHandle(req, res)` | Boundary Guard Hook | Immediately before route execution. Short-circuits on `false` or when headers sent |
185
- | `afterHandle(req, res, data)` | Response Hook | Immediately after route execution. Receives returned data to transform or audit |
186
- | `(ctx, next)` — default export (functional) or `handle(ctx, next)` (OOP) | Pipeline Wrapper | Around all chain steps and the nested route/boundary |
188
+ | Signature | Detected as | Runs |
189
+ | :----------------------------------------------------------------------- | :------------------ | :--------------------------------------------------------------------------------- |
190
+ | `(req, res, next)` | Chain Step | In file/declaration order, before the route handler |
191
+ | `beforeHandle(req, res)` | Boundary Guard Hook | Immediately before route execution. Short-circuits on `false` or when headers sent |
192
+ | `afterHandle(req, res, data)` | Response Hook | Immediately after route execution. Receives returned data to transform or audit |
193
+ | `(ctx, next)` — default export (functional) or `handle(ctx, next)` (OOP) | Pipeline Wrapper | Around all chain steps and the nested route/boundary |
187
194
 
188
195
  ### Functional
189
196
 
190
197
  ```typescript filename="src/http/admin/boundary.ts"
191
- import { defineBoundary } from "exisjs/router";
192
- import type { Request, Response, Next, BoundaryContext } from "exisjs/router";
198
+ import { defineBoundary } from 'exisjs/router'
199
+ import type { Request, Response, Next, BoundaryContext } from 'exisjs/router'
193
200
 
194
201
  export const config = defineBoundary({
195
- cors: { origin: ["https://myapp.com"], credentials: true },
196
- headers: { "X-Powered-By": "ExisJS" },
197
- exclude: [{ path: "/admin/health", method: "GET" }],
202
+ cors: { origin: ['https://myapp.com'], credentials: true },
203
+ headers: { 'X-Powered-By': 'ExisJS' },
204
+ exclude: [{ path: '/admin/health', method: 'GET' }],
198
205
 
199
206
  // Pre-handler hook (returns false to block with 403, or sends response)
200
207
  beforeHandle(req, res) {
201
- if (req.headers["x-role"] !== "admin") {
202
- res.status(403).json({ error: "Admin role required" });
203
- return false;
208
+ if (req.headers['x-role'] !== 'admin') {
209
+ res.status(403).json({ error: 'Admin role required' })
210
+ return false
204
211
  }
205
212
  },
206
213
 
207
214
  // Post-handler hook (transforms returned payload or performs async audit)
208
215
  afterHandle(req, res, data) {
209
- res.setHeader("X-Audited-By", "AdminBoundary");
210
- return { ...data, auditedAt: Date.now() };
216
+ res.setHeader('X-Audited-By', 'AdminBoundary')
217
+ return { ...data, auditedAt: Date.now() }
211
218
  },
212
- });
219
+ })
213
220
 
214
221
  export function logStep(req: Request, res: Response, next: Next) {
215
- console.log(`[Admin] ${req.method} ${req.path}`);
216
- next();
222
+ console.log(`[Admin] ${req.method} ${req.path}`)
223
+ next()
217
224
  }
218
225
 
219
226
  export default async function (ctx: BoundaryContext, next: Next) {
220
227
  try {
221
- return { success: true, data: await next() };
228
+ return { success: true, data: await next() }
222
229
  } catch (err) {
223
- return { success: false, error: { message: (err as Error).message } };
230
+ return { success: false, error: { message: (err as Error).message } }
224
231
  }
225
232
  }
226
233
  ```
@@ -228,40 +235,40 @@ export default async function (ctx: BoundaryContext, next: Next) {
228
235
  ### Class-based (OOP)
229
236
 
230
237
  ```typescript filename="src/http/admin/boundary.ts"
231
- import { Boundary } from "exisjs/decorators";
232
- import type { Request, Response, Next, BoundaryContext } from "exisjs/router";
238
+ import { Boundary } from 'exisjs/decorators'
239
+ import type { Request, Response, Next, BoundaryContext } from 'exisjs/router'
233
240
 
234
241
  @Boundary({
235
- cors: { origin: ["https://myapp.com"], credentials: true },
236
- headers: { "X-Powered-By": "ExisJS" },
237
- exclude: ["/admin/health"],
242
+ cors: { origin: ['https://myapp.com'], credentials: true },
243
+ headers: { 'X-Powered-By': 'ExisJS' },
244
+ exclude: ['/admin/health'],
238
245
  })
239
246
  export default class AdminBoundary {
240
247
  // Pre-handler hook
241
248
  beforeHandle(req: Request, res: Response) {
242
- if (req.headers["x-role"] !== "admin") {
243
- res.status(403).json({ error: "Admin role required" });
244
- return false;
249
+ if (req.headers['x-role'] !== 'admin') {
250
+ res.status(403).json({ error: 'Admin role required' })
251
+ return false
245
252
  }
246
253
  }
247
254
 
248
255
  // Post-handler hook
249
256
  afterHandle(req: Request, res: Response, data: any) {
250
- return { ...data, auditedAt: Date.now() };
257
+ return { ...data, auditedAt: Date.now() }
251
258
  }
252
259
 
253
260
  // Chain step
254
261
  logStep(req: Request, res: Response, next: Next) {
255
- console.log(`[Admin] ${req.method} ${req.path}`);
256
- next();
262
+ console.log(`[Admin] ${req.method} ${req.path}`)
263
+ next()
257
264
  }
258
265
 
259
266
  // Wrapper
260
267
  async handle(ctx: BoundaryContext, next: Next) {
261
268
  try {
262
- return { success: true, data: await next() };
269
+ return { success: true, data: await next() }
263
270
  } catch (err) {
264
- return { success: false, error: { message: (err as Error).message } };
271
+ return { success: false, error: { message: (err as Error).message } }
265
272
  }
266
273
  }
267
274
  }
@@ -276,39 +283,39 @@ ExisJS natively discovers and mounts `src/http/error.ts` (or `error.js`) at boot
276
283
  ### Functional
277
284
 
278
285
  ```typescript filename="src/http/error.ts"
279
- import type { Request, Response } from "exisjs";
286
+ import type { Request, Response } from 'exisjs'
280
287
 
281
288
  export function onError(err: any, req: Request, res: Response) {
282
- const status = err.statusCode || err.status || 500;
289
+ const status = err.statusCode || err.status || 500
283
290
 
284
291
  // Format consistent error response across the entire API
285
292
  res.status(status).json({
286
293
  success: false,
287
294
  error: {
288
- message: err.message || "Internal Server Error",
289
- code: err.code || "INTERNAL_SERVER_ERROR",
295
+ message: err.message || 'Internal Server Error',
296
+ code: err.code || 'INTERNAL_SERVER_ERROR',
290
297
  path: req.path,
291
298
  },
292
- });
299
+ })
293
300
  }
294
301
  ```
295
302
 
296
303
  ### Class-Based (OOP)
297
304
 
298
305
  ```typescript filename="src/http/error.ts"
299
- import type { Request, Response } from "exisjs";
306
+ import type { Request, Response } from 'exisjs'
300
307
 
301
308
  export default class GlobalErrorHandler {
302
309
  onError(err: any, req: Request, res: Response) {
303
- const status = err.statusCode || err.status || 500;
310
+ const status = err.statusCode || err.status || 500
304
311
  res.status(status).json({
305
312
  success: false,
306
313
  error: {
307
- message: err.message || "Internal Server Error",
308
- code: err.code || "INTERNAL_ERROR",
314
+ message: err.message || 'Internal Server Error',
315
+ code: err.code || 'INTERNAL_ERROR',
309
316
  path: req.path,
310
317
  },
311
- });
318
+ })
312
319
  }
313
320
  }
314
321
  ```
@@ -324,32 +331,46 @@ Global infrastructure: port, SSL, CORS defaults, security headers, and
324
331
  official package registration.
325
332
 
326
333
  ```typescript filename="exis.config.ts"
327
- import { defineConfig } from "exisjs";
334
+ import { defineConfig } from 'exisjs'
328
335
 
329
336
  export default defineConfig({
330
337
  port: 3000,
331
- cors: { origin: "*" },
338
+ cors: { origin: '*' },
332
339
  // plugins: [] — official packages (@exisjs/database, @exisjs/auth, etc.)
333
340
  // are registered here once installed. None are required by default.
334
- });
341
+ })
335
342
  ```
336
343
 
337
344
  ---
338
345
 
339
- ## `src/env.ts`
346
+ ## `src/config/env.ts`
340
347
 
341
- ```typescript filename="src/env.ts"
342
- import { tex } from "exisjs/validator";
348
+ ```typescript filename="src/config/env.ts"
349
+ import { tex } from 'exisjs/validator'
343
350
 
344
351
  export const env = tex
345
- .object({
346
- PORT: tex.number({ coerce: true, optional: true }),
347
- NODE_ENV: tex.enum(["development", "production", "test"]),
352
+ .env({
353
+ PORT: tex.number({ default: 4000 }),
354
+ NODE_ENV: tex.enum(['development', 'production', 'test'] as const, {
355
+ default: 'development',
356
+ }),
357
+ CORS_ORIGIN: tex.string({ default: '*' }),
348
358
  })
349
- .parse(process.env);
359
+ .parse(process.env)
350
360
  ```
351
361
 
352
- Boot fails immediately if a required variable is missing.
362
+ Boot fails immediately with clear diagnostics if a required environment variable is missing or malformed.
363
+
364
+ ---
365
+
366
+ ## `src/database/`
367
+
368
+ The `src/database/` directory organizes your persistent storage layer:
369
+
370
+ - `src/database/db.ts`: Connection instance, client initialization, and lifecycle registration via `registerDatabase(app, { connect, disconnect, healthCheck })` (or ORMs like Prisma, Drizzle, Mongoose).
371
+ - `src/database/models/` (or `src/models/`): Data models, entity definitions, and database schemas.
372
+ - `src/database/repositories/`: Abstracted data access objects and custom query builders.
373
+ - `src/database/migrations/`: Database migration scripts.
353
374
 
354
375
  ---
355
376
 
@@ -360,11 +381,16 @@ production. Add to `.gitignore`.
360
381
 
361
382
  ---
362
383
 
363
- ## Custom Directories
384
+ ## Auto-Discovered vs Custom Directories
364
385
 
365
- Only `src/http/` (routing) and, if `@exisjs/queue` is installed, `src/jobs/`
366
- are enforced. Everything else — `src/models/`, `src/services/`, `src/dto/` —
367
- is free-form.
386
+ - **Auto-Discovered by Framework**:
387
+ - `src/http/`: Route handlers (`route.ts`), request schemas (`schema.ts`), boundaries (`boundary.ts`), and global error handler (`error.ts`).
388
+ - `src/cron/`: Scheduled tasks and background cron jobs (`cleanup.ts`, `reports.ts`).
389
+ - **Standard Recommended Conventions**:
390
+ - `src/database/`: Database client (`db.ts`), models (`models/`), and migrations.
391
+ - `src/common/`: Shared guards (`guards/`), interceptors, and exception filters.
392
+ - `src/config/`: Environment configuration (`config/env.ts`).
393
+ - `src/services/` or `src/dto/`: Optional shared business logic or global DTOs across multiple routes.
368
394
 
369
395
  ---
370
396
 
@@ -1,6 +1,6 @@
1
1
  ---
2
- title: "Configuration"
3
- description: "API Reference for the global exis.config.ts file (`exisjs/config`)."
2
+ title: 'Configuration'
3
+ description: 'API Reference for the global exis.config.ts file (`exisjs/config`).'
4
4
  ---
5
5
 
6
6
  # Configuration (`exisjs/config`)
@@ -61,22 +61,22 @@ export default async (phase: string, { defaultConfig }) => {
61
61
 
62
62
  return defineConfig({
63
63
  port: Number(process.env.PORT) || 4000,
64
-
64
+
65
65
  // Auto-spawn Worker threads based on CPU cores!
66
- workers: isDev ? 1 : 'safe',
67
-
66
+ workers: isDev ? 1 : 'safe',
67
+
68
68
  // Globally configure native middleware
69
69
  cors: {
70
70
  origin: isDev ? '*' : 'https://myapp.com',
71
- credentials: true
71
+ credentials: true,
72
72
  },
73
73
  helmet: { enabled: true },
74
74
  logger: isDev ? { level: 'debug', pretty: true } : false,
75
-
75
+
76
76
  // Native plugin registration
77
77
  plugins: [
78
78
  // ... your plugins here
79
- ]
79
+ ],
80
80
  })
81
81
  }
82
82
  ```
@@ -99,20 +99,31 @@ If you export a function from `exis.config.ts`, ExisJS will inject the current "
99
99
  Here is the full list of options supported natively by the ExisJS configuration engine:
100
100
 
101
101
  ### Server & Network
102
- - **`port`** *(number)*: The port the server listens on (default: `4000`).
103
- - **`host`** *(string)*: The host interface to bind to (default: `'0.0.0.0'`).
104
- - **`server`** *(`'node' | 'uws' | 'auto'`)*: ExisJS supports running on standard Node `http`, or incredibly fast C++ `uWebSockets.js` (`uws`). Default is `'auto'` (uses `uws` if installed, falls back to `node`).
105
- - **`workers`** *(number | `'safe'` | `'max'`)*: Number of Node.js Cluster workers to spawn. `'max'` uses all CPU cores. `'safe'` uses all cores minus 1 (to leave room for the OS).
106
- - **`trustProxy`** *(boolean)*: Enable if running behind a reverse proxy (like Nginx or AWS ALB) to trust the `X-Forwarded-For` headers.
102
+
103
+ - **`port`** _(number)_: The port the server listens on (default: `4000`).
104
+ - **`host`** _(string)_: The host interface to bind to (default: `'0.0.0.0'`).
105
+ - **`server`** _(`'node' | 'uws' | 'auto'`)_: ExisJS supports running on standard Node `http`, or incredibly fast C++ `uWebSockets.js` (`uws`). Default is `'auto'` (uses `uws` if installed, falls back to `node`).
106
+ - **`workers`** _(number | `'safe'` | `'max'`)_: Number of Node.js Cluster workers to spawn. `'max'` uses all CPU cores. `'safe'` uses all cores minus 1 (to leave room for the OS).
107
+ - **`trustProxy`** _(boolean | number)_: Enable if running behind reverse proxies or CDNs (Cloudflare, AWS ALB, NGINX, Render) to evaluate `CF-Connecting-IP`, `True-Client-IP`, `X-Real-IP`, and `X-Forwarded-For`. (Can also be enabled via `TRUST_PROXY=true`).
107
108
 
108
109
  ### Security & Middlewares
109
- - **`cors`** *(boolean | object)*: Enable and configure Cross-Origin Resource Sharing globally.
110
- - **`helmet`** *(boolean | object)*: Enable strict security headers. Enabled by default.
111
- - **`logger`** *(boolean | object)*: Configure the efficient Pino request logger.
112
- - **`compression`** *(boolean | object)*: Enable gzip/brotli response compression.
113
- - **`bodyLimit`** *(number)*: Maximum JSON/URL-encoded payload size in bytes (default: `1MB`).
110
+
111
+ - **`cors`** _(boolean | object)_: Enable and configure Cross-Origin Resource Sharing globally.
112
+ - **`helmet`** _(boolean | object)_: Enable strict security headers. Enabled by default.
113
+ - **`logger`** _(boolean | object)_: Configure the efficient Pino request logger.
114
+ - **`compression`** _(boolean | object)_: Enable gzip/brotli response compression.
115
+ - **`etag`** _(boolean)_: Automatic ETag generation for HTTP conditional requests (default: `false` for dynamic API routes).
116
+ - **`bodyLimit`** _(number)_: Maximum JSON/URL-encoded payload size in bytes (default: `10MB`).
117
+
118
+ ### Development Server & File Watcher (`dev`)
119
+
120
+ - **`dev.watch`** _(string[])_: Custom directories or files to watch during development (default: `[cwd]`).
121
+ - **`dev.ignored`** _((string | RegExp)[])_: Additional files, extensions, or regex patterns to ignore during hot reload.
122
+ - **`dev.usePolling`** _(boolean)_: Enables chokidar polling fallback (default: `true` on Windows).
123
+ - **`dev.interval`** _(number)_: Polling file system interval in milliseconds (default: `100`).
114
124
 
115
125
  ### Extended Integrations
126
+
116
127
  - **`queue`**: Define your background queue driver (`memory` or `redis`) and configure `maxConcurrent` processing limits.
117
128
  - **`plugins`**: An array of `ExisPlugin` instances to register natively into the framework.
118
129
  - **`test`**: Configure testing environments (`include`, `exclude`, `setupFiles`, `coverage`).