exisjs 0.7.10 → 0.7.12
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.
- package/dist/cli/commands/dev.d.ts +4 -0
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +140 -29
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/run.d.ts +7 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/run.js +99 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/index.js +29 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/manifest.d.ts.map +1 -1
- package/dist/cli/manifest.js +18 -3
- package/dist/cli/manifest.js.map +1 -1
- package/dist/cli/routes.d.ts +6 -0
- package/dist/cli/routes.d.ts.map +1 -1
- package/dist/cli/routes.js +224 -135
- package/dist/cli/routes.js.map +1 -1
- package/dist/config/config.d.ts +3 -1
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +28 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/database/lifecycle.d.ts +8 -2
- package/dist/database/lifecycle.d.ts.map +1 -1
- package/dist/database/lifecycle.js +21 -6
- package/dist/database/lifecycle.js.map +1 -1
- package/dist/di/container.d.ts +2 -1
- package/dist/di/container.d.ts.map +1 -1
- package/dist/di/container.js.map +1 -1
- package/dist/di/index.d.ts +1 -1
- package/dist/di/index.d.ts.map +1 -1
- package/dist/di/index.js.map +1 -1
- package/dist/docs/BENCHMARKS.md +5 -5
- package/dist/docs/content/boundaries.mdx +19 -3
- package/dist/docs/content/cli/commands.mdx +31 -0
- package/dist/docs/content/cli/overview.mdx +1 -0
- package/dist/docs/content/config.mdx +27 -6
- package/dist/docs/content/routing.mdx +44 -0
- package/dist/docs/content/security.mdx +48 -35
- package/dist/docs/content/structure.mdx +119 -93
- package/dist/docs/reference/config.mdx +31 -19
- package/dist/docs/reference/database.mdx +7 -7
- package/dist/docs/reference/router.mdx +45 -1
- package/dist/lib/inspect-routes.d.ts +2 -0
- package/dist/lib/inspect-routes.d.ts.map +1 -0
- package/dist/lib/inspect-routes.js +148 -0
- package/dist/lib/inspect-routes.js.map +1 -0
- package/dist/lib/start-server.js +10 -0
- package/dist/lib/start-server.js.map +1 -1
- package/dist/middleware/security.d.ts +18 -0
- package/dist/middleware/security.d.ts.map +1 -1
- package/dist/middleware/security.js +32 -0
- package/dist/middleware/security.js.map +1 -1
- package/dist/router/radix.d.ts +1 -0
- package/dist/router/radix.d.ts.map +1 -1
- package/dist/router/radix.js +21 -0
- package/dist/router/radix.js.map +1 -1
- package/dist/router/scanner/cron-mounter.d.ts.map +1 -1
- package/dist/router/scanner/cron-mounter.js +9 -2
- package/dist/router/scanner/cron-mounter.js.map +1 -1
- package/dist/server/adapters/uws-adapter.d.ts +17 -0
- package/dist/server/adapters/uws-adapter.d.ts.map +1 -1
- package/dist/server/adapters/uws-adapter.js +101 -7
- package/dist/server/adapters/uws-adapter.js.map +1 -1
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/app.js +5 -0
- package/dist/server/app.js.map +1 -1
- package/dist/server/define.d.ts.map +1 -1
- package/dist/server/define.js +5 -0
- package/dist/server/define.js.map +1 -1
- package/dist/server/helpers/etag.d.ts.map +1 -1
- package/dist/server/helpers/etag.js +51 -3
- package/dist/server/helpers/etag.js.map +1 -1
- package/dist/server/request-handler.d.ts.map +1 -1
- package/dist/server/request-handler.js +35 -19
- package/dist/server/request-handler.js.map +1 -1
- package/dist/server/request.d.ts +4 -3
- package/dist/server/request.d.ts.map +1 -1
- package/dist/server/request.js +26 -8
- package/dist/server/request.js.map +1 -1
- package/dist/server/response.d.ts +1 -0
- package/dist/server/response.d.ts.map +1 -1
- package/dist/server/response.js +24 -13
- package/dist/server/response.js.map +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +9 -0
- package/dist/testing/index.js.map +1 -1
- package/package.json +2 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
nextUrl:
|
|
3
|
-
nextTitle:
|
|
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
|
-
│ │ │
|
|
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
|
|
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
|
-
│ ├──
|
|
44
|
-
│
|
|
45
|
-
│ ├──
|
|
46
|
-
│
|
|
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/
|
|
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
|
|
71
|
-
|
|
|
72
|
-
| `route.ts` / `*.route.ts`
|
|
73
|
-
| `schema.ts` / `*.schema.ts`
|
|
74
|
-
| `service.ts` / `*.service.ts`
|
|
75
|
-
| `boundary.ts` / `*.boundary.ts` | Folder-scoped config + request pipeline for everything in this folder and below.
|
|
76
|
-
| `error.ts` / `*.error.ts`
|
|
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
|
|
93
|
-
import { UserParamsSchema } from
|
|
94
|
-
import { fetchUser } from
|
|
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(
|
|
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
|
|
112
|
-
import { UserParamsSchema } from
|
|
113
|
-
import { UserService } from
|
|
118
|
+
import { Controller, Get, Param } from 'exisjs/decorators'
|
|
119
|
+
import { UserParamsSchema } from './schema'
|
|
120
|
+
import { UserService } from './service'
|
|
114
121
|
|
|
115
|
-
@Controller(
|
|
122
|
+
@Controller('/users')
|
|
116
123
|
export default class UserRoute {
|
|
117
124
|
constructor(private userService: UserService) {}
|
|
118
125
|
|
|
119
|
-
@Get(
|
|
120
|
-
async getUser(@Param(
|
|
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
|
|
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
|
|
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
|
|
182
|
-
|
|
|
183
|
-
| `(req, res, next)`
|
|
184
|
-
| `beforeHandle(req, res)`
|
|
185
|
-
| `afterHandle(req, res, data)`
|
|
186
|
-
| `(ctx, next)` — default export (functional) or `handle(ctx, next)` (OOP) | Pipeline Wrapper
|
|
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
|
|
192
|
-
import type { Request, Response, Next, BoundaryContext } from
|
|
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: [
|
|
196
|
-
headers: {
|
|
197
|
-
exclude: [{ path:
|
|
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[
|
|
202
|
-
res.status(403).json({ error:
|
|
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(
|
|
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
|
|
232
|
-
import type { Request, Response, Next, BoundaryContext } from
|
|
238
|
+
import { Boundary } from 'exisjs/decorators'
|
|
239
|
+
import type { Request, Response, Next, BoundaryContext } from 'exisjs/router'
|
|
233
240
|
|
|
234
241
|
@Boundary({
|
|
235
|
-
cors: { origin: [
|
|
236
|
-
headers: {
|
|
237
|
-
exclude: [
|
|
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[
|
|
243
|
-
res.status(403).json({ error:
|
|
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
|
|
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 ||
|
|
289
|
-
code: err.code ||
|
|
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
|
|
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 ||
|
|
308
|
-
code: err.code ||
|
|
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
|
|
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
|
|
348
|
+
```typescript filename="src/config/env.ts"
|
|
349
|
+
import { tex } from 'exisjs/validator'
|
|
343
350
|
|
|
344
351
|
export const env = tex
|
|
345
|
-
.
|
|
346
|
-
PORT: tex.number({
|
|
347
|
-
NODE_ENV: tex.enum([
|
|
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
|
-
|
|
366
|
-
|
|
367
|
-
|
|
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:
|
|
3
|
-
description:
|
|
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,21 +99,33 @@ 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
|
-
|
|
103
|
-
- **`
|
|
104
|
-
- **`
|
|
105
|
-
- **`
|
|
106
|
-
- **`
|
|
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
|
-
|
|
110
|
-
- **`
|
|
111
|
-
- **`
|
|
112
|
-
- **`
|
|
113
|
-
- **`
|
|
114
|
-
- **`
|
|
110
|
+
|
|
111
|
+
- **`cors`** _(boolean | object)_: Enable and configure Cross-Origin Resource Sharing globally. Acts as the base configuration; can be overridden per directory via `boundary.ts` (`defineBoundary({ cors })`) or per endpoint via `route.ts` (`route.get({ cors })` / `@Cors()`).
|
|
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 (`watch` & `dev`)
|
|
119
|
+
|
|
120
|
+
- **`watch.paths`** / **`dev.watch`** _(string[])_: Custom directories or files to watch during development (default: `['src', ...configFiles]`).
|
|
121
|
+
- **`watch.ignore`** / **`dev.ignored`** _((string | RegExp)[])_: Additional files, extensions, or regex patterns to ignore during hot reload.
|
|
122
|
+
- **`watch.extensions`** / **`dev.extensions`** _(string[])_: Explicit file extensions that trigger reloads (defaults to `['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']`).
|
|
123
|
+
- **`watch.debounceMs`** / **`dev.debounceMs`** _(number)_: Debounce interval in ms before restarting the server (default: `250ms`).
|
|
124
|
+
- **`dev.usePolling`** _(boolean)_: Enables chokidar polling fallback (default: `true` on Windows).
|
|
125
|
+
- **`dev.interval`** _(number)_: Polling file system interval in milliseconds (default: `100`).
|
|
115
126
|
|
|
116
127
|
### Extended Integrations
|
|
128
|
+
|
|
117
129
|
- **`queue`**: Define your background queue driver (`memory` or `redis`) and configure `maxConcurrent` processing limits.
|
|
118
130
|
- **`plugins`**: An array of `ExisPlugin` instances to register natively into the framework.
|
|
119
131
|
- **`test`**: Configure testing environments (`include`, `exclude`, `setupFiles`, `coverage`).
|
|
@@ -11,15 +11,15 @@ ExisJS provides a unified, database-agnostic lifecycle and transaction layer. It
|
|
|
11
11
|
|
|
12
12
|
## 1. Lifecycle Management
|
|
13
13
|
|
|
14
|
-
### `registerDatabase(app, options)`
|
|
14
|
+
### `registerDatabase(app, options)` or `registerDatabase(options)`
|
|
15
15
|
|
|
16
|
-
Registers a database connection into the application lifecycle. Automatically
|
|
16
|
+
Registers a database connection into the application lifecycle. Automatically coordinates `connect()` during server boot and `disconnect()` during graceful shutdown (`SIGTERM`, `SIGINT`, or `app.close()`).
|
|
17
17
|
|
|
18
|
-
#### Options (`
|
|
18
|
+
#### Options (`DatabaseRegistration`)
|
|
19
19
|
- `name` (`string`): Unique identifier for the database connection (e.g. `'postgres'`, `'mongodb'`).
|
|
20
|
-
- `connect` (`() => Promise<void
|
|
21
|
-
- `disconnect` (`() => Promise<void
|
|
22
|
-
- `healthCheck` (`() => Promise<boolean
|
|
20
|
+
- `connect` (`() => Promise<void> | void`): Async callback to establish connections and connection pools.
|
|
21
|
+
- `disconnect` (`() => Promise<void> | void`): Async callback to close connections gracefully.
|
|
22
|
+
- `isHealthy` / `healthCheck` (`() => Promise<boolean | DatabaseHealthInfo> | boolean | DatabaseHealthInfo`): Optional health check callback returning `true` / `false` or detailed health info (`{ status: 'connected' | 'disconnected' | 'error', latencyMs?, error?, details? }`).
|
|
23
23
|
|
|
24
24
|
```typescript filename="src/http/server.ts"
|
|
25
25
|
import { exis } from 'exisjs'
|
|
@@ -56,7 +56,7 @@ Singleton registry managing all registered database adapters.
|
|
|
56
56
|
- `DatabaseManager.register(options)`: Registers a database adapter.
|
|
57
57
|
- `DatabaseManager.connectAll()`: Connects all registered databases concurrently.
|
|
58
58
|
- `DatabaseManager.disconnectAll()`: Gracefully disconnects all databases concurrently.
|
|
59
|
-
- `DatabaseManager.checkHealth()`: Runs health checks across all registered databases and returns `{ healthy: boolean, databases: Record<string,
|
|
59
|
+
- `DatabaseManager.checkHealth()`: Runs health checks across all registered databases and returns `{ healthy: boolean, databases: Record<string, DatabaseHealthInfo> }`.
|
|
60
60
|
- `DatabaseManager.clear()`: Clears all registered database adapters (useful for test tear-downs).
|
|
61
61
|
|
|
62
62
|
```typescript filename="src/http/health/route.ts"
|
|
@@ -335,7 +335,50 @@ The response object supports universal streaming with backpressure management ac
|
|
|
335
335
|
|
|
336
336
|
---
|
|
337
337
|
|
|
338
|
-
## 4.
|
|
338
|
+
## 4. Boundaries & Folder-Scoped Pipelines (`boundary.ts`)
|
|
339
|
+
|
|
340
|
+
ExisJS introduces **Boundaries** (`boundary.ts`) to manage folder-scoped middleware, CORS policies, security probes, guards, interceptors, and default headers without having to wire them manually across every route file.
|
|
341
|
+
|
|
342
|
+
### How Boundaries Work
|
|
343
|
+
|
|
344
|
+
Place a `boundary.ts` file in any route directory (e.g., `src/http/boundary.ts` or `src/http/api/v1/boundary.ts`):
|
|
345
|
+
|
|
346
|
+
```typescript filename="src/http/api/boundary.ts"
|
|
347
|
+
import { defineBoundary } from 'exisjs/router'
|
|
348
|
+
import { authGuard } from '../../guards/auth'
|
|
349
|
+
|
|
350
|
+
export default defineBoundary({
|
|
351
|
+
// Applied to all routes in src/http/api/* and subdirectories
|
|
352
|
+
cors: {
|
|
353
|
+
origin: 'https://app.example.com',
|
|
354
|
+
credentials: true,
|
|
355
|
+
},
|
|
356
|
+
guards: [authGuard],
|
|
357
|
+
headers: {
|
|
358
|
+
'X-API-Version': 'v1',
|
|
359
|
+
},
|
|
360
|
+
// Exclude public endpoints from this boundary's pipeline
|
|
361
|
+
exclude: ['/api/health', '/api/public/*'],
|
|
362
|
+
})
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Cascading & Precedence Rules
|
|
366
|
+
|
|
367
|
+
When route slices are nested in subdirectories (e.g. `src/http/boundary.ts` -> `src/http/api/boundary.ts` -> `src/http/api/users/route.ts`), ExisJS evaluates boundary configurations using the following rules:
|
|
368
|
+
|
|
369
|
+
1. **Middlewares, Guards, Filters, & Interceptors (Cascading / Additive)**:
|
|
370
|
+
- Evaluated **top-down** from parent boundary -> child boundary -> route-level controller.
|
|
371
|
+
2. **CORS Configuration (Innermost Precedence)**:
|
|
372
|
+
- **Route-Level `cors`** (in `route.ts` / `controller({ cors: ... })`) overrides any boundary CORS.
|
|
373
|
+
- **Child Boundary `cors`** (in `src/http/api/boundary.ts`) overrides parent boundary `cors` (in `src/http/boundary.ts`).
|
|
374
|
+
- **Parent Boundary `cors`** applies if no child boundary or route overrides it.
|
|
375
|
+
- **Global `app.options.cors`** acts as the root fallback.
|
|
376
|
+
3. **Response Headers (Deep Merge with Child Precedence)**:
|
|
377
|
+
- Headers defined in child boundaries or route definitions overwrite identical header keys from parent boundaries.
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## 5. `AppRouter` Client Manifest Contract
|
|
339
382
|
|
|
340
383
|
ExisJS generates a compile-time route manifest in `.exis/types.d.ts` and `.exis/types/index.d.ts`:
|
|
341
384
|
|
|
@@ -345,3 +388,4 @@ ExisJS generates a compile-time route manifest in `.exis/types.d.ts` and `.exis/
|
|
|
345
388
|
- `RouteDefinition.__type`: Phantom type property on every route definition preserving exact inferred return types, DTO schemas, and Drizzle/Prisma models.
|
|
346
389
|
|
|
347
390
|
|
|
391
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inspect-routes.d.ts","sourceRoot":"","sources":["../../src/lib/inspect-routes.ts"],"names":[],"mappings":""}
|