exisjs 0.7.13 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +4 -37
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +3 -1
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +10 -0
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +9 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/database/lifecycle.d.ts +7 -0
- package/dist/database/lifecycle.d.ts.map +1 -1
- package/dist/database/lifecycle.js +30 -7
- package/dist/database/lifecycle.js.map +1 -1
- package/dist/decorators/caching.d.ts +2 -5
- package/dist/decorators/caching.d.ts.map +1 -1
- package/dist/decorators/caching.js +7 -61
- package/dist/decorators/caching.js.map +1 -1
- package/dist/decorators/core.d.ts.map +1 -1
- package/dist/decorators/core.js +5 -0
- package/dist/decorators/core.js.map +1 -1
- package/dist/decorators/cron.d.ts +1 -0
- package/dist/decorators/cron.d.ts.map +1 -1
- package/dist/decorators/cron.js +4 -1
- package/dist/decorators/cron.js.map +1 -1
- package/dist/di/container.d.ts +1 -0
- package/dist/di/container.d.ts.map +1 -1
- package/dist/di/container.js +19 -3
- package/dist/di/container.js.map +1 -1
- package/dist/docs/ARCHITECTURE.md +117 -78
- package/dist/docs/BENCHMARKS.md +59 -26
- package/dist/docs/README.md +1 -1
- package/dist/docs/content/boundaries.mdx +2 -0
- package/dist/docs/content/cli/commands.mdx +1 -2
- package/dist/docs/content/cli/overview.mdx +1 -1
- package/dist/docs/content/config.mdx +22 -9
- package/dist/docs/content/cron.mdx +2 -2
- package/dist/docs/content/decorators.mdx +2 -2
- package/dist/docs/content/errors.mdx +22 -6
- package/dist/docs/content/installation.mdx +13 -8
- package/dist/docs/content/introduction.mdx +12 -12
- package/dist/docs/content/middleware.mdx +1 -1
- package/dist/docs/content/providers.mdx +4 -4
- package/dist/docs/content/requests.mdx +12 -1
- package/dist/docs/content/responses.mdx +44 -3
- package/dist/docs/content/routing.mdx +24 -0
- package/dist/docs/content/structure.mdx +4 -1
- package/dist/docs/content/testing.mdx +2 -0
- package/dist/docs/content/validation.mdx +16 -2
- package/dist/docs/reference/README.md +1 -1
- package/dist/docs/reference/config.mdx +1 -1
- package/dist/docs/reference/decorators.mdx +2 -2
- package/dist/docs/reference/di.mdx +1 -1
- package/dist/docs/reference/middleware.mdx +7 -5
- package/dist/docs/reference/sanitize.mdx +4 -4
- package/dist/docs/reference/validator.mdx +1 -1
- package/dist/error/errors.d.ts.map +1 -1
- package/dist/error/errors.js +31 -8
- package/dist/error/errors.js.map +1 -1
- package/dist/lib/start-server.js +24 -1
- package/dist/lib/start-server.js.map +1 -1
- package/dist/middleware/compression.d.ts +0 -12
- package/dist/middleware/compression.d.ts.map +1 -1
- package/dist/middleware/compression.js +52 -41
- package/dist/middleware/compression.js.map +1 -1
- package/dist/middleware/idempotency.d.ts +11 -6
- package/dist/middleware/idempotency.d.ts.map +1 -1
- package/dist/middleware/idempotency.js +5 -35
- package/dist/middleware/idempotency.js.map +1 -1
- package/dist/middleware/ip-filter.d.ts.map +1 -1
- package/dist/middleware/ip-filter.js +0 -16
- package/dist/middleware/ip-filter.js.map +1 -1
- package/dist/middleware/middleware.d.ts +2 -1
- package/dist/middleware/middleware.d.ts.map +1 -1
- package/dist/middleware/middleware.js +92 -56
- package/dist/middleware/middleware.js.map +1 -1
- package/dist/middleware/rate-limit.d.ts.map +1 -1
- package/dist/middleware/rate-limit.js +19 -38
- package/dist/middleware/rate-limit.js.map +1 -1
- package/dist/middleware/security.d.ts.map +1 -1
- package/dist/middleware/security.js +3 -1
- package/dist/middleware/security.js.map +1 -1
- package/dist/middleware/upload.d.ts.map +1 -1
- package/dist/middleware/upload.js +15 -7
- package/dist/middleware/upload.js.map +1 -1
- package/dist/response/index.d.ts +23 -0
- package/dist/response/index.d.ts.map +1 -1
- package/dist/response/index.js +28 -1
- package/dist/response/index.js.map +1 -1
- package/dist/router/index.d.ts +1 -1
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js.map +1 -1
- package/dist/router/radix.d.ts +3 -16
- package/dist/router/radix.d.ts.map +1 -1
- package/dist/router/radix.js +135 -211
- package/dist/router/radix.js.map +1 -1
- package/dist/router/route-builder.d.ts +28 -0
- package/dist/router/route-builder.d.ts.map +1 -1
- package/dist/router/route-builder.js.map +1 -1
- package/dist/router/route-scanner.d.ts +2 -1
- package/dist/router/route-scanner.d.ts.map +1 -1
- package/dist/router/route-scanner.js +37 -44
- package/dist/router/route-scanner.js.map +1 -1
- package/dist/router/router.d.ts.map +1 -1
- package/dist/router/router.js +153 -126
- package/dist/router/router.js.map +1 -1
- package/dist/router/scanner/boundary-loader.d.ts.map +1 -1
- package/dist/router/scanner/boundary-loader.js +7 -2
- package/dist/router/scanner/boundary-loader.js.map +1 -1
- package/dist/router/scanner/fs-scanner.d.ts.map +1 -1
- package/dist/router/scanner/fs-scanner.js +22 -0
- package/dist/router/scanner/fs-scanner.js.map +1 -1
- package/dist/router/scanner/functional-compiler.d.ts.map +1 -1
- package/dist/router/scanner/functional-compiler.js +9 -1
- package/dist/router/scanner/functional-compiler.js.map +1 -1
- package/dist/router/types.d.ts +6 -4
- package/dist/router/types.d.ts.map +1 -1
- package/dist/sanitize/index.d.ts +2 -3
- package/dist/sanitize/index.d.ts.map +1 -1
- package/dist/sanitize/index.js +15 -20
- package/dist/sanitize/index.js.map +1 -1
- package/dist/server/adapters/bun-adapter.d.ts +2 -1
- package/dist/server/adapters/bun-adapter.d.ts.map +1 -1
- package/dist/server/adapters/bun-adapter.js +22 -2
- package/dist/server/adapters/bun-adapter.js.map +1 -1
- package/dist/server/app.d.ts +2 -2
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/app.js +14 -7
- package/dist/server/app.js.map +1 -1
- package/dist/server/bootstrapper.d.ts +2 -2
- package/dist/server/bootstrapper.d.ts.map +1 -1
- package/dist/server/bootstrapper.js +21 -2
- package/dist/server/bootstrapper.js.map +1 -1
- package/dist/server/download.d.ts +22 -0
- package/dist/server/download.d.ts.map +1 -0
- package/dist/server/download.js +18 -0
- package/dist/server/download.js.map +1 -0
- package/dist/server/helpers/body-parser.d.ts +1 -1
- package/dist/server/helpers/body-parser.d.ts.map +1 -1
- package/dist/server/helpers/body-parser.js +126 -48
- package/dist/server/helpers/body-parser.js.map +1 -1
- package/dist/server/helpers/cookie.d.ts +6 -0
- package/dist/server/helpers/cookie.d.ts.map +1 -1
- package/dist/server/helpers/cookie.js +35 -0
- package/dist/server/helpers/cookie.js.map +1 -1
- package/dist/server/helpers/etag.d.ts.map +1 -1
- package/dist/server/helpers/etag.js +7 -2
- package/dist/server/helpers/etag.js.map +1 -1
- package/dist/server/helpers/index.d.ts +2 -1
- package/dist/server/helpers/index.d.ts.map +1 -1
- package/dist/server/helpers/index.js +5 -1
- package/dist/server/helpers/index.js.map +1 -1
- package/dist/server/helpers/ip.d.ts +13 -0
- package/dist/server/helpers/ip.d.ts.map +1 -1
- package/dist/server/helpers/ip.js +65 -54
- package/dist/server/helpers/ip.js.map +1 -1
- package/dist/server/helpers/json.d.ts +3 -0
- package/dist/server/helpers/json.d.ts.map +1 -0
- package/dist/server/helpers/json.js +43 -0
- package/dist/server/helpers/json.js.map +1 -0
- package/dist/server/helpers/upload-name.d.ts +9 -0
- package/dist/server/helpers/upload-name.d.ts.map +1 -0
- package/dist/server/helpers/upload-name.js +39 -0
- package/dist/server/helpers/upload-name.js.map +1 -0
- package/dist/server/request-handler.d.ts +0 -7
- package/dist/server/request-handler.d.ts.map +1 -1
- package/dist/server/request-handler.js +19 -46
- package/dist/server/request-handler.js.map +1 -1
- package/dist/server/request.d.ts +3 -2
- package/dist/server/request.d.ts.map +1 -1
- package/dist/server/request.js +22 -16
- package/dist/server/request.js.map +1 -1
- package/dist/server/response.d.ts +20 -6
- package/dist/server/response.d.ts.map +1 -1
- package/dist/server/response.js +107 -34
- package/dist/server/response.js.map +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +8 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/reporter.d.mts.map +1 -1
- package/dist/testing/reporter.mjs +18 -0
- package/dist/testing/reporter.mjs.map +1 -1
- package/dist/utils/ttl-cache.d.ts +11 -0
- package/dist/utils/ttl-cache.d.ts.map +1 -0
- package/dist/utils/ttl-cache.js +42 -0
- package/dist/utils/ttl-cache.js.map +1 -0
- package/dist/validator/builder.d.ts +2 -2
- package/dist/validator/builder.js +2 -2
- package/dist/validator/compile.d.ts +3 -0
- package/dist/validator/compile.d.ts.map +1 -0
- package/dist/validator/compile.js +254 -0
- package/dist/validator/compile.js.map +1 -0
- package/dist/validator/engine.d.ts +1 -0
- package/dist/validator/engine.d.ts.map +1 -1
- package/dist/validator/engine.js +285 -317
- package/dist/validator/engine.js.map +1 -1
- package/dist/validator/error.d.ts +1 -0
- package/dist/validator/error.d.ts.map +1 -1
- package/dist/validator/error.js +11 -1
- package/dist/validator/error.js.map +1 -1
- package/dist/validator/rules.d.ts +64 -0
- package/dist/validator/rules.d.ts.map +1 -0
- package/dist/validator/rules.js +655 -0
- package/dist/validator/rules.js.map +1 -0
- package/dist/validator/tex-types.d.ts +4 -4
- package/dist/validator/tex-types.d.ts.map +1 -1
- package/dist/validator/text.d.ts +11 -0
- package/dist/validator/text.d.ts.map +1 -0
- package/dist/validator/text.js +69 -0
- package/dist/validator/text.js.map +1 -0
- package/package.json +56 -39
|
@@ -481,7 +481,7 @@ export class OrderService implements OnInit, OnDestroy {
|
|
|
481
481
|
| `OnDestroy` (`onDestroy()`) | Class Interface | Invoked once during graceful shutdown. |
|
|
482
482
|
| `OnModuleInit` / `OnModuleDestroy` | Class Interface | Invoked during parent module initialization and destruction. |
|
|
483
483
|
| `OnApplicationBootstrap` | Class Interface | Invoked when all modules are initialized and server starts listening. |
|
|
484
|
-
| `OnApplicationShutdown` | Class Interface |
|
|
484
|
+
| `OnApplicationShutdown` | Class Interface | `onApplicationShutdown(signal?)` runs during graceful shutdown; `signal` is `'SIGTERM'` or `'SIGINT'` when a signal triggered it. |
|
|
485
485
|
|
|
486
486
|
---
|
|
487
487
|
|
|
@@ -601,7 +601,7 @@ import { UserService } from './service'
|
|
|
601
601
|
@Controller('/users')
|
|
602
602
|
export default class UsersController {
|
|
603
603
|
constructor(
|
|
604
|
-
private userService: UserService,
|
|
604
|
+
@Inject(UserService) private userService: UserService,
|
|
605
605
|
@Inject('DATABASE_URL') private dbUrl: string,
|
|
606
606
|
@Optional() @Inject('ANALYTICS_SERVICE') private analytics?: any
|
|
607
607
|
) {}
|
|
@@ -120,28 +120,44 @@ export default class UsersController {
|
|
|
120
120
|
|
|
121
121
|
## Standard Error Response Format
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
Every error response has the same core shape, whatever caused it:
|
|
124
124
|
|
|
125
125
|
```json filename="HTTP 404 Response"
|
|
126
126
|
{
|
|
127
127
|
"success": false,
|
|
128
128
|
"statusCode": 404,
|
|
129
|
-
"error":
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
"error": {
|
|
130
|
+
"code": "NOT_FOUND",
|
|
131
|
+
"message": "User not found"
|
|
132
|
+
}
|
|
133
133
|
}
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
+
| Field | Description |
|
|
137
|
+
| :--- | :--- |
|
|
138
|
+
| `success` | Always `false` for errors |
|
|
139
|
+
| `statusCode` | Same as the HTTP status |
|
|
140
|
+
| `error.code` | Stable machine-readable code: `NOT_FOUND`, `UNAUTHORIZED`, `VALIDATION_ERROR`, `INTERNAL_ERROR`... |
|
|
141
|
+
| `error.message` | Human-readable message. For unexpected errors in production it is a generic message; internals are never exposed |
|
|
142
|
+
| `error.details` | Optional extra data (validation issues, or the `details` passed to an `HttpError`) |
|
|
143
|
+
|
|
144
|
+
Clients can rely on `error.code` and `error.message` for every failed request.
|
|
145
|
+
|
|
136
146
|
### Validation Error Responses
|
|
137
147
|
|
|
138
148
|
When schema validation fails (via `tex`, Zod, or Yup), ExisJS intercepts the validation failure and generates a structured `400 Bad Request` payload containing field-level error mappings:
|
|
139
149
|
|
|
150
|
+
The top-level `message`, `validationErrors`, `errors` and `details` fields are kept for existing clients; new code should read `error.details`. Values of credential fields (`password`, `token`, `secret`...) are replaced with `[Redacted]` in responses and logs.
|
|
151
|
+
|
|
140
152
|
```json filename="HTTP 400 Validation Error Response"
|
|
141
153
|
{
|
|
142
154
|
"success": false,
|
|
143
155
|
"statusCode": 400,
|
|
144
|
-
"error":
|
|
156
|
+
"error": {
|
|
157
|
+
"code": "VALIDATION_ERROR",
|
|
158
|
+
"message": "Validation Error: email: Invalid email format, age: Minimum age is 18",
|
|
159
|
+
"details": [ /* same entries as "details" below */ ]
|
|
160
|
+
},
|
|
145
161
|
"message": "Validation Error: email: Invalid email format, age: Minimum age is 18",
|
|
146
162
|
"validationErrors": {
|
|
147
163
|
"email": "Invalid email format",
|
|
@@ -119,7 +119,7 @@ my-api/
|
|
|
119
119
|
│ ├── route.ts # Root route handler (/)
|
|
120
120
|
│ └── server.ts # Application lifecycle (onStart / onClose)
|
|
121
121
|
├── tests/
|
|
122
|
-
│ └── users.
|
|
122
|
+
│ └── users.test.ts # End-to-end test suite
|
|
123
123
|
├── .env # Environment variables
|
|
124
124
|
├── .gitignore
|
|
125
125
|
├── .prettierrc
|
|
@@ -178,7 +178,7 @@ Create or update `tsconfig.json` to configure module resolution and metadata ref
|
|
|
178
178
|
```
|
|
179
179
|
|
|
180
180
|
> [!NOTE]
|
|
181
|
-
> `experimentalDecorators`
|
|
181
|
+
> `experimentalDecorators` is required when using the class-based (OOP) paradigm; `emitDecoratorMetadata` is harmless but not used by the ExisJS toolchain, so inject constructor dependencies with `@Inject(Token)`. This applies to the class-based (OOP) paradigm (`@Controller()`, `@Injectable()`).
|
|
182
182
|
|
|
183
183
|
### Step 3: Create `exis.config.ts`
|
|
184
184
|
|
|
@@ -191,18 +191,23 @@ const config: ExisConfig = {
|
|
|
191
191
|
port: Number(process.env.PORT) || 4000,
|
|
192
192
|
host: '0.0.0.0',
|
|
193
193
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
194
|
+
// Credentialed requests need explicit origins (comma-separated CORS_ORIGIN).
|
|
195
|
+
// Without it, any origin may call the API, but without cookies.
|
|
196
|
+
cors: process.env.CORS_ORIGIN
|
|
197
|
+
? { origin: process.env.CORS_ORIGIN.split(','), credentials: true }
|
|
198
|
+
: { origin: '*' },
|
|
198
199
|
|
|
199
200
|
logger: {
|
|
200
|
-
level: 'debug',
|
|
201
|
+
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
|
|
201
202
|
pretty: process.env.NODE_ENV !== 'production',
|
|
202
203
|
},
|
|
203
204
|
|
|
204
205
|
helmet: { enabled: true },
|
|
205
206
|
compression: true,
|
|
207
|
+
|
|
208
|
+
// Swagger UI at /docs; set enabled to
|
|
209
|
+
// process.env.NODE_ENV !== 'production' to hide it in production
|
|
210
|
+
docs: { enabled: true },
|
|
206
211
|
}
|
|
207
212
|
|
|
208
213
|
export default config
|
|
@@ -278,7 +283,7 @@ npm run dev
|
|
|
278
283
|
Output:
|
|
279
284
|
|
|
280
285
|
```text filename="Terminal"
|
|
281
|
-
[exis] starting development server (v0.
|
|
286
|
+
[exis] starting development server (v0.8.0)...
|
|
282
287
|
EXIS ready in 125 ms
|
|
283
288
|
|
|
284
289
|
→ Local: http://localhost:4000/
|
|
@@ -9,7 +9,7 @@ nextTitle: 'Installation'
|
|
|
9
9
|
|
|
10
10
|
**ExisJS** is an opinionated web framework for building TypeScript backends on Node.js. It brings convention-over-configuration and automatic file-system routing to backend API development.
|
|
11
11
|
|
|
12
|
-
Rather than manually assembling, wiring, and maintaining separate third-party libraries for routing, input validation, dependency injection, OpenAPI documentation, rate limiting, and background task scheduling, ExisJS provides
|
|
12
|
+
Rather than manually assembling, wiring, and maintaining separate third-party libraries for routing, input validation, dependency injection, OpenAPI documentation, rate limiting, and background task scheduling, ExisJS provides an integrated standard library in a lightweight, pure TypeScript core with no native add-ons.
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
@@ -20,7 +20,7 @@ TypeScript backend development often faces two approaches:
|
|
|
20
20
|
1. **Micro-frameworks (Express, Fastify)**: Require developers to manually select, wire, and maintain middleware packages for validation, OpenAPI schemas, dependency injection, and security headers. Over time, projects diverge in structure and maintenance overhead increases.
|
|
21
21
|
2. **Heavy enterprise frameworks**: Introduce deep class hierarchies, verbose configuration modules, and steep learning curves.
|
|
22
22
|
|
|
23
|
-
ExisJS combines convention-based file routing, built-in standard modules, and
|
|
23
|
+
ExisJS combines convention-based file routing, built-in standard modules, and a lean runtime with support for both functional and object-oriented paradigms.
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
@@ -38,16 +38,16 @@ ExisJS combines convention-based file routing, built-in standard modules, and na
|
|
|
38
38
|
│ Request Context (ExisRequest/Response) · Hierarchical Boundaries · Swagger │
|
|
39
39
|
│ Rate Limiting · Helmet Headers · CSRF · Idempotency · Telemetry │
|
|
40
40
|
└──────────────────────────────────────┬──────────────────────────────────────┘
|
|
41
|
-
│
|
|
41
|
+
│
|
|
42
42
|
┌──────────────────────────────────────▼──────────────────────────────────────┐
|
|
43
|
-
│
|
|
44
|
-
│
|
|
45
|
-
│
|
|
43
|
+
│ Runtime Core │
|
|
44
|
+
│ Radix-Tree Router │ Compiled tex Validators │ CIDR IP Filter │
|
|
45
|
+
│ Secure Body Parser │ Bounded In-Memory Caches │
|
|
46
46
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
### 1. File-System
|
|
50
|
-
The file-system routing engine scans the `src/http` directory and compiles route paths into a
|
|
49
|
+
### 1. File-System Routing
|
|
50
|
+
The file-system routing engine scans the `src/http` directory and compiles route paths into a radix tree at startup. Purely static paths resolve through an exact-match table; parameterized paths walk the tree with no per-request parsing of the route definitions.
|
|
51
51
|
|
|
52
52
|
### 2. Dual-Paradigm Parity
|
|
53
53
|
ExisJS provides feature parity across both functional and object-oriented paradigms. Both compile down to the same runtime execution pipeline:
|
|
@@ -60,8 +60,8 @@ The built-in `tex` schema builder provides runtime type validation, parameter co
|
|
|
60
60
|
### 4. Hierarchical Security & Error Boundaries
|
|
61
61
|
Directory-based `boundary.ts` files allow you to declare scoped authentication guards, rate limits, CORS configurations, and error handlers that cascade down folder trees without manual middleware repetition.
|
|
62
62
|
|
|
63
|
-
### 5.
|
|
64
|
-
|
|
63
|
+
### 5. No Native Dependencies
|
|
64
|
+
The core is pure TypeScript. There are no platform binaries to download or compile, so ExisJS installs the same way on every OS and CPU architecture that Node.js supports.
|
|
65
65
|
|
|
66
66
|
---
|
|
67
67
|
|
|
@@ -70,7 +70,7 @@ All native Rust bindings (`@exisjs/rs`) include pure TypeScript fallback impleme
|
|
|
70
70
|
| Feature Area | ExisJS Built-In Capability | Benefit |
|
|
71
71
|
| :--- | :--- | :--- |
|
|
72
72
|
| **Routing** | File-system route discovery (`src/http/**/route.ts`) | No manual router mounting or path strings |
|
|
73
|
-
| **Validation** | `tex` Schema Validator (
|
|
73
|
+
| **Validation** | `tex` Schema Validator (compiled once per schema) | Fast parsing, zero-crash sanitization, automatic OpenAPI types |
|
|
74
74
|
| **Documentation** | Automatic Swagger / OpenAPI 3.1 at `/docs` | Self-updating interactive documentation with zero manual sync |
|
|
75
75
|
| **Traffic Control** | Native Token Bucket Rate Limiting & CIDR Filtering | Fast DoS prevention and client throttling |
|
|
76
76
|
| **Security** | Security headers (`helmet`), CSRF, HPP, Body Sanitization | Secure defaults without installing separate packages |
|
|
@@ -178,7 +178,7 @@ Every HTTP request dispatched through ExisJS follows a predictable, high-perform
|
|
|
178
178
|
Incoming Request
|
|
179
179
|
│
|
|
180
180
|
▼
|
|
181
|
-
1.
|
|
181
|
+
1. Radix Path Match
|
|
182
182
|
│
|
|
183
183
|
▼
|
|
184
184
|
2. CIDR IP Filter & Rate Limit Check
|
|
@@ -24,7 +24,7 @@ All standard middlewares can be imported directly from `exisjs/middleware`. Cust
|
|
|
24
24
|
| `requestLogger` | `requestLogger(options?)` | Request logger powered by Pino, injecting `req.log` with automatic timing and child loggers. |
|
|
25
25
|
| `requestId` | `requestId()` | Attaches a unique `X-Request-Id` to requests and responses with distributed tracing headers. |
|
|
26
26
|
| `compress` | `compress()` | Stream-based HTTP response compression supporting Gzip, Brotli, and Deflate. |
|
|
27
|
-
| `rateLimit` | `rateLimit(options?)` |
|
|
27
|
+
| `rateLimit` | `rateLimit(options?)` | In-memory rate limiter returning `429 Too Many Requests` with standard headers. |
|
|
28
28
|
| `idempotent` | `idempotent(options?)` | Duplicate mutation caching keyed by `Idempotency-Key` headers. |
|
|
29
29
|
| `dedupe` | `dedupe(options)` | Merges simultaneous identical in-flight `GET` requests into a single operation. |
|
|
30
30
|
| `backpressure` | `backpressure(options?)` | Queues requests during high load and sheds excess traffic with `503 Service Unavailable`. |
|
|
@@ -70,7 +70,7 @@ export default controller({
|
|
|
70
70
|
|
|
71
71
|
### OOP Constructor and Property Injection
|
|
72
72
|
|
|
73
|
-
In class-based controllers and services, dependencies
|
|
73
|
+
In class-based controllers and services, constructor dependencies are injected by token. Mark each parameter with `@Inject(Token)`; a class is its own token. ExisJS does not rely on TypeScript's emitted type metadata (the `tsx`/esbuild toolchain does not produce it), so an undecorated parameter on a controller or `@Injectable()` class fails with a clear error instead of receiving `undefined`:
|
|
74
74
|
|
|
75
75
|
```typescript filename="src/http/users/route.ts"
|
|
76
76
|
import { Controller, Get, Post, Body } from 'exisjs/decorators'
|
|
@@ -85,8 +85,8 @@ export default class UsersController {
|
|
|
85
85
|
@Optional()
|
|
86
86
|
private metrics?: MetricsService
|
|
87
87
|
|
|
88
|
-
//
|
|
89
|
-
constructor(private usersService: UsersService) {}
|
|
88
|
+
// Constructor injection: the class is the token
|
|
89
|
+
constructor(@Inject(UsersService) private usersService: UsersService) {}
|
|
90
90
|
|
|
91
91
|
@Get('/')
|
|
92
92
|
async list() {
|
|
@@ -287,7 +287,7 @@ import { DatabaseService } from './database.service'
|
|
|
287
287
|
|
|
288
288
|
@Injectable()
|
|
289
289
|
export class RolesService {
|
|
290
|
-
constructor(private db: DatabaseService) {}
|
|
290
|
+
constructor(@Inject(DatabaseService) private db: DatabaseService) {}
|
|
291
291
|
|
|
292
292
|
// Caches method results using an LRU cache
|
|
293
293
|
@Cacheable({ ttl: 300, key: (id: string) => `role:${id}` })
|
|
@@ -155,12 +155,23 @@ When running behind reverse proxies, load balancers, or CDNs (such as AWS ALB, C
|
|
|
155
155
|
import type { ExisConfig } from 'exisjs/config'
|
|
156
156
|
|
|
157
157
|
const config: ExisConfig = {
|
|
158
|
-
|
|
158
|
+
// Exactly one proxy (e.g. NGINX or a load balancer) in front of the app:
|
|
159
|
+
trustProxy: 1,
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
export default config
|
|
162
163
|
```
|
|
163
164
|
|
|
165
|
+
Choose the value that matches your deployment:
|
|
166
|
+
|
|
167
|
+
| Value | Trusts | Use when |
|
|
168
|
+
| :--- | :--- | :--- |
|
|
169
|
+
| `false` (default) | Nothing; `req.ip` is the socket address | The app is reached directly |
|
|
170
|
+
| `N` (number) | The `N` closest hops, counting the socket peer. With `X-Forwarded-For: a, b` and `trustProxy: 1`, `req.ip` is `b`, the address your proxy saw. Values a client prepends are ignored. | You run behind a known number of proxies |
|
|
171
|
+
| `true` | Every hop, plus CDN headers (`CF-Connecting-IP`, `True-Client-IP`, `X-Real-IP`, `X-Client-IP`) | Every request passes through a CDN or proxy that overwrites these headers |
|
|
172
|
+
|
|
173
|
+
> **Warning:** with `trustProxy: true`, any client that can reach the app directly can set these headers and spoof `req.ip`, and with it rate limits and IP filters. Prefer a hop count unless the app is only reachable through your proxy.
|
|
174
|
+
|
|
164
175
|
When enabled:
|
|
165
176
|
- `req.ip` automatically resolves the client IP, normalizes IPv6 loopback (`::1` $\to$ `127.0.0.1`), and strips IPv4-mapped IPv6 prefixes (`::ffff:192.168.1.1` $\to$ `192.168.1.1`).
|
|
166
177
|
- `req.ips` provides the complete upstream proxy chain.
|
|
@@ -248,22 +248,63 @@ res.clearCookie('token', { path: '/' })
|
|
|
248
248
|
|
|
249
249
|
## File Downloads & Streaming
|
|
250
250
|
|
|
251
|
-
### File Downloads
|
|
251
|
+
### File Downloads
|
|
252
252
|
|
|
253
|
-
|
|
253
|
+
Return `download()` from a handler to send generated content (a string, `Buffer` or stream) as an attachment. It sets `Content-Disposition` and infers `Content-Type` from the filename:
|
|
254
254
|
|
|
255
255
|
```typescript filename="src/http/reports/route.ts"
|
|
256
256
|
import { controller, route } from 'exisjs/router'
|
|
257
|
+
import { download } from 'exisjs/response'
|
|
257
258
|
|
|
258
259
|
export default controller({
|
|
259
|
-
|
|
260
|
+
exportCsv: route.get('/export', {
|
|
261
|
+
async handle() {
|
|
262
|
+
const csv = await ReportService.buildCsv()
|
|
263
|
+
return download(csv, 'report.csv')
|
|
264
|
+
},
|
|
265
|
+
}),
|
|
266
|
+
exportXlsx: route.get('/export.xlsx', {
|
|
267
|
+
async handle() {
|
|
268
|
+
const stream = await ReportService.streamWorkbook()
|
|
269
|
+
return download(stream, 'report.xlsx', {
|
|
270
|
+
headers: { 'Cache-Control': 'no-store' },
|
|
271
|
+
})
|
|
272
|
+
},
|
|
273
|
+
}),
|
|
274
|
+
})
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Pass `{ contentType }` to override the inferred MIME type. A handler that returns a plain `Buffer` sends it as `application/octet-stream`.
|
|
278
|
+
|
|
279
|
+
#### Files on disk: `res.download(path)`
|
|
280
|
+
|
|
281
|
+
`res.download()` sends a file from disk. A string argument is **always a file path**, never content; a missing file answers `404`. It also accepts a `Buffer` or stream.
|
|
282
|
+
|
|
283
|
+
```typescript filename="src/http/reports/route.ts"
|
|
284
|
+
import { controller, route } from 'exisjs/router'
|
|
285
|
+
|
|
286
|
+
export default controller({
|
|
287
|
+
annual: route.get('/annual', {
|
|
260
288
|
handle: async ({ res }) => {
|
|
261
289
|
res.download('./storage/annual-report.pdf', 'Annual-Report-2026.pdf')
|
|
262
290
|
},
|
|
263
291
|
}),
|
|
292
|
+
// The filename comes from the request, so confine it to one directory
|
|
293
|
+
byName: route.get('/files/:name', {
|
|
294
|
+
handle: async ({ params, res }) => {
|
|
295
|
+
res.download(params.name, undefined, { root: './storage/exports' })
|
|
296
|
+
},
|
|
297
|
+
}),
|
|
264
298
|
})
|
|
265
299
|
```
|
|
266
300
|
|
|
301
|
+
> **Security:** whenever any part of the path comes from the request (a param, query value or body field), pass `root`. Paths that resolve outside it, such as `../../.env` or an absolute path, answer `404` instead of leaking the file.
|
|
302
|
+
|
|
303
|
+
| | String argument means | Use for |
|
|
304
|
+
| :--- | :--- | :--- |
|
|
305
|
+
| `return download(data, name)` | Content | Generated CSV, JSON, reports |
|
|
306
|
+
| `res.download(path, name?, { root })` | File path | Files stored on disk |
|
|
307
|
+
|
|
267
308
|
---
|
|
268
309
|
|
|
269
310
|
### Stream Piping (`res.stream()` / `res.sendStream()`)
|
|
@@ -362,6 +362,30 @@ export default controller({
|
|
|
362
362
|
|
|
363
363
|
---
|
|
364
364
|
|
|
365
|
+
## Reusing Route Types in Services
|
|
366
|
+
|
|
367
|
+
`InferRouteInput` and `InferRouteOutput` derive types from a route definition, so service methods do not need a second copy of the request shape:
|
|
368
|
+
|
|
369
|
+
```typescript filename="src/http/users/route.ts"
|
|
370
|
+
import { route, type InferRouteInput, type InferRouteOutput } from 'exisjs/router'
|
|
371
|
+
import { CreateUserSchema } from './schema'
|
|
372
|
+
import { UserService } from './service'
|
|
373
|
+
|
|
374
|
+
export const createUser = route.post('/', {
|
|
375
|
+
body: CreateUserSchema,
|
|
376
|
+
async handle({ body, resolve }) {
|
|
377
|
+
return resolve(UserService).create(body)
|
|
378
|
+
},
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
// { body, query, params } as the handler receives them, after validation
|
|
382
|
+
export type CreateUserInput = InferRouteInput<typeof createUser>['body']
|
|
383
|
+
// What the handler resolves to
|
|
384
|
+
export type CreateUserResult = InferRouteOutput<typeof createUser>
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
365
389
|
## Dependency Resolution in Functional Routes
|
|
366
390
|
|
|
367
391
|
Functional routes can resolve dependencies directly from the IoC container using `ctx.resolve()`:
|
|
@@ -110,11 +110,14 @@ export default controller({
|
|
|
110
110
|
```typescript filename="src/http/users/route.ts"
|
|
111
111
|
import { Controller, Get, Param, ApiOperation } from 'exisjs/decorators'
|
|
112
112
|
import { userParamsSchema } from './schema'
|
|
113
|
+
import { Inject } from 'exisjs/di'
|
|
113
114
|
import { UserService } from './service'
|
|
114
115
|
|
|
115
116
|
@Controller()
|
|
116
117
|
export default class UsersController {
|
|
117
|
-
constructor(
|
|
118
|
+
constructor(
|
|
119
|
+
@Inject(UserService) private readonly userService: UserService
|
|
120
|
+
) {}
|
|
118
121
|
|
|
119
122
|
@Get('/:id', { params: userParamsSchema })
|
|
120
123
|
@ApiOperation({ summary: 'Retrieve user by ID' })
|
|
@@ -33,6 +33,8 @@ The `exisjs/testing` module re-exports test runner primitives, assertions, mocki
|
|
|
33
33
|
|
|
34
34
|
`createTestContext` initializes your application definition, executes `onStart` hooks, establishes database connections (Mongoose, Prisma, or DatabaseManager), and tears down connections gracefully in `afterAll`.
|
|
35
35
|
|
|
36
|
+
Tests always load routes from your source files, never from a previous `exisjs build`. `exisjs test` reads `.env.test` on top of `.env`, so point it at a separate database. A test file that crashes while loading is reported as failed, and a run that finds no test files fails (set `EXIS_PASS_WITH_NO_TESTS=1` to allow it).
|
|
37
|
+
|
|
36
38
|
### Functional Paradigm Application Test
|
|
37
39
|
|
|
38
40
|
Given a functional application defined in `src/http/server.ts`:
|
|
@@ -368,7 +368,7 @@ When validating, `tex.discriminatedUnion` matches the discriminator property aga
|
|
|
368
368
|
```json filename="Discriminator Error Example"
|
|
369
369
|
{
|
|
370
370
|
"statusCode": 400,
|
|
371
|
-
"error": "
|
|
371
|
+
"error": { "code": "VALIDATION_ERROR", "message": "Validation Error", "details": [] },
|
|
372
372
|
"message": "Validation Error: type: Invalid discriminator value 'archive' for field 'type'",
|
|
373
373
|
"details": [
|
|
374
374
|
{
|
|
@@ -588,7 +588,7 @@ When incoming requests fail validation, ExisJS returns an HTTP `400 Bad Request`
|
|
|
588
588
|
```json filename="HTTP 400 Bad Request"
|
|
589
589
|
{
|
|
590
590
|
"statusCode": 400,
|
|
591
|
-
"error": "
|
|
591
|
+
"error": { "code": "VALIDATION_ERROR", "message": "Validation Error", "details": [] },
|
|
592
592
|
"message": "Validation Error: username: Field must be at least 4 characters, email: Invalid email address",
|
|
593
593
|
"errors": {
|
|
594
594
|
"username": "Field must be at least 4 characters",
|
|
@@ -643,6 +643,20 @@ export default controller({
|
|
|
643
643
|
})
|
|
644
644
|
```
|
|
645
645
|
|
|
646
|
+
### Query and Param Coercion with Zod
|
|
647
|
+
|
|
648
|
+
Query strings and path parameters always arrive as strings. For Zod object schemas used as `query` or `params`, ExisJS converts fields declared as `z.number()`, `z.boolean()` or `z.date()` before validating, so `z.coerce` and `.transform(Number)` are not needed (they keep working if present). This works with Zod 3 and Zod 4, including fields wrapped in `.optional()`, `.default()` or `.nullable()`.
|
|
649
|
+
|
|
650
|
+
```typescript
|
|
651
|
+
const ListQuery = z.object({
|
|
652
|
+
page: z.number().int().min(1).default(1), // ?page=2 -> 2
|
|
653
|
+
active: z.boolean().optional(), // ?active=true -> true
|
|
654
|
+
from: z.date().optional(), // ?from=2026-10-01 -> Date
|
|
655
|
+
})
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
A value that cannot be converted (`?page=abc`) is left as a string, so the schema rejects it with a normal validation error. Request bodies are never coerced.
|
|
659
|
+
|
|
646
660
|
### Using Zod in OOP Controllers
|
|
647
661
|
|
|
648
662
|
```typescript filename="src/http/users/route.ts"
|
|
@@ -20,7 +20,7 @@ This directory contains complete, information-dense API references, TypeScript i
|
|
|
20
20
|
| `exisjs/middleware` | [`middleware.mdx`](file:///z:/projects/framework/exisjs/docs/reference/middleware.mdx) | Built-in middleware suite: Helmet, CORS, CSRF, rate limiting, IP filtering, and multipart uploads. |
|
|
21
21
|
| `exisjs/context` | [`context.mdx`](file:///z:/projects/framework/exisjs/docs/reference/context.mdx) | `AsyncLocalStorage` request context API (`getRequest`, `getResponse`, `getContext`, `after`). |
|
|
22
22
|
| `exisjs/cron` | [`cron.mdx`](file:///z:/projects/framework/exisjs/docs/reference/cron.mdx) | Task scheduler (`cron()`, `@Cron()`), interval timers, presets, and `CronManager` runtime controls. |
|
|
23
|
-
| `exisjs/sanitize` | [`sanitize.mdx`](file:///z:/projects/framework/exisjs/docs/reference/sanitize.mdx) |
|
|
23
|
+
| `exisjs/sanitize` | [`sanitize.mdx`](file:///z:/projects/framework/exisjs/docs/reference/sanitize.mdx) | Security cleaners (`escapeHtml`, `preventSql`) and data transform utilities. |
|
|
24
24
|
| `exisjs/swagger` | [`swagger.mdx`](file:///z:/projects/framework/exisjs/docs/reference/swagger.mdx) | OpenAPI 3.1 specification generation, Swagger UI / Scalar interactive rendering, and decorators. |
|
|
25
25
|
| `exisjs/testing` | [`testing.mdx`](file:///z:/projects/framework/exisjs/docs/reference/testing.mdx) | Test runner (`node:test`), synthetic HTTP client (`createTestApp`), `expect` matchers, and `ex` mocks. |
|
|
26
26
|
| `exisjs/plugin` | [`plugin.mdx`](file:///z:/projects/framework/exisjs/docs/reference/plugin.mdx) | Modular plugin development (`definePlugin`), lifecycle hooks, and dependency encapsulation. |
|
|
@@ -172,7 +172,7 @@ The root configuration schema accepted by `exis.config.ts`:
|
|
|
172
172
|
| `host` | `string` | `'0.0.0.0'` | Network interface binding. |
|
|
173
173
|
| `server` | `'auto' \| 'node' \| 'bun' \| 'uws'` | `'auto'` | HTTP server backend. Automatically selects Bun or Node when detected. |
|
|
174
174
|
| `cluster` | `{ workers?: number \| 'auto' \| 'safe' \| 'max' }` | `{ workers: 1 }` | Multi-process worker clustering. `'safe'` uses available CPU cores minus 1; `'max'` uses all available cores. |
|
|
175
|
-
| `trustProxy` | `boolean \| number` | `false` |
|
|
175
|
+
| `trustProxy` | `boolean \| number` | `false` | `true`: trust all proxy headers (`X-Forwarded-For`, `CF-Connecting-IP`, `True-Client-IP`, `X-Real-IP`). `N`: trust only the N closest hops in `X-Forwarded-For`, counting the socket peer; CDN headers are ignored. |
|
|
176
176
|
| `bodyLimit` | `number` | `10485760` (10MB) | Maximum payload size in bytes for JSON and URL-encoded request bodies. |
|
|
177
177
|
| `compression` | `boolean` | `false` | Enables gzip and Brotli response compression. |
|
|
178
178
|
| `etag` | `boolean` | `false` | Enables ETag computation for dynamic responses. |
|
|
@@ -219,7 +219,7 @@ export const CurrentUser = createParamDecorator((data: string | undefined, ctx)
|
|
|
219
219
|
| `@RateLimit(options)` | Class / Method | `RateLimitOptions` | Enforces rate limits on endpoints. |
|
|
220
220
|
| `@Cors(options)` | Class / Method | `CorsConfig \| boolean` | Enforces CORS configuration. |
|
|
221
221
|
| `@IpFilter(options)` | Class / Method | `IpFilterOptions` | Enforces CIDR IP allow/deny lists. |
|
|
222
|
-
| `@Idempotent(options?)` | Class / Method | `IdempotencyOptions` | Enables
|
|
222
|
+
| `@Idempotent(options?)` | Class / Method | `IdempotencyOptions` | Enables response caching via `Idempotency-Key`. |
|
|
223
223
|
| `@Dedupe(options?)` | Class / Method | `DedupeOptions` | Deduplicates concurrent in-flight requests with identical keys. |
|
|
224
224
|
| `@BlockProbes(options?)` | Class / Method | `BlockProbesOptions` | Blackholes malicious scanner probes (`.env`, `wp-admin`). |
|
|
225
225
|
| `@Upload(field, options?)` | Method | `string, FileUploadOptions` | Configures multipart file upload streaming on the endpoint. |
|
|
@@ -266,7 +266,7 @@ export const CurrentUser = createParamDecorator((data: string | undefined, ctx)
|
|
|
266
266
|
|
|
267
267
|
| Decorator | Target | Options | Description |
|
|
268
268
|
| :--- | :--- | :--- | :--- |
|
|
269
|
-
| `@Cacheable(options)` | Method | `{ ttl?: number, key?: string \| ((...args) => string) }` | Caches method return value in
|
|
269
|
+
| `@Cacheable(options)` | Method | `{ ttl?: number, key?: string \| ((...args) => string) }` | Caches method return value in a bounded in-memory cache. |
|
|
270
270
|
| `@CacheEvict(options)` | Method | `{ key: string \| ((...args) => string) }` | Evicts cached keys upon successful method execution. |
|
|
271
271
|
| `@Transactional(options?)` | Method | `TransactionOptions` | Wraps database queries within an atomic transaction. |
|
|
272
272
|
| `@Profile(options?)` | Method | `{ thresholdMs?: number, warnSlow?: boolean }` | Measures and logs method execution latency. |
|
|
@@ -131,7 +131,7 @@ export default class UserController {
|
|
|
131
131
|
private config!: Record<string, any>
|
|
132
132
|
|
|
133
133
|
constructor(
|
|
134
|
-
private userService: UserService,
|
|
134
|
+
@Inject(UserService) private userService: UserService,
|
|
135
135
|
@Inject('DATABASE_URL') private dbUrl: string
|
|
136
136
|
) {}
|
|
137
137
|
}
|
|
@@ -7,7 +7,7 @@ nextTitle: "Context Reference"
|
|
|
7
7
|
|
|
8
8
|
# `exisjs/middleware`
|
|
9
9
|
|
|
10
|
-
The `exisjs/middleware` module provides the built-in HTTP middleware pipeline for ExisJS. It includes
|
|
10
|
+
The `exisjs/middleware` module provides the built-in HTTP middleware pipeline for ExisJS. It includes traffic controls, multipart upload handlers, security header suites, and request context enhancers.
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
@@ -78,7 +78,7 @@ function helmet(options?: HelmetOptions): Handler
|
|
|
78
78
|
| `xFrameOptions` | `'DENY' \| 'SAMEORIGIN'` | `'SAMEORIGIN'` | Protects against clickjacking. |
|
|
79
79
|
| `hsts` | `boolean \| HstsOptions` | `true` (in prod) | Strict Transport Security configuration. |
|
|
80
80
|
| `noSniff` | `boolean` | `true` | Sets `X-Content-Type-Options: nosniff`. |
|
|
81
|
-
| `xssFilter` | `boolean` | `true` | Sets `X-XSS-Protection:
|
|
81
|
+
| `xssFilter` | `boolean` | `true` | Sets `X-XSS-Protection: 0`, disabling the legacy browser XSS auditor (current OWASP guidance). |
|
|
82
82
|
|
|
83
83
|
---
|
|
84
84
|
|
|
@@ -145,7 +145,7 @@ function mongoSanitize(options?: MongoSanitizeOptions): Handler
|
|
|
145
145
|
|
|
146
146
|
### `rateLimit(options?)`
|
|
147
147
|
|
|
148
|
-
In-memory
|
|
148
|
+
In-memory fixed-window rate limiter; expired windows are swept automatically.
|
|
149
149
|
|
|
150
150
|
```typescript
|
|
151
151
|
function rateLimit(options?: RateLimitOptions): Handler
|
|
@@ -181,7 +181,7 @@ function routeTimeout(msOrOptions: number | TimeoutOptions): Handler
|
|
|
181
181
|
|
|
182
182
|
### `idempotent(options?)`
|
|
183
183
|
|
|
184
|
-
Caches responses against the `Idempotency-Key` HTTP header in
|
|
184
|
+
Caches responses against the `Idempotency-Key` HTTP header in a bounded in-memory cache (10,000 entries) to prevent duplicate mutations.
|
|
185
185
|
|
|
186
186
|
```typescript
|
|
187
187
|
function idempotent(options?: IdempotencyOptions): Handler
|
|
@@ -269,7 +269,9 @@ fileUpload.none(options?: FileUploadOptions): Handler
|
|
|
269
269
|
| `dest` | `string` | Target directory on disk to stream uploaded files to. |
|
|
270
270
|
| `maxBytes` | `number` | Maximum allowed individual file size in bytes. |
|
|
271
271
|
| `mimeTypes` | `string[]` | Permitted MIME types (e.g. `['image/png', 'image/jpeg']`). |
|
|
272
|
-
| `limits` | `FileUploadLimits` | Detailed limits (`fileSize`, `files`, `fieldSize`, `fields`). |
|
|
272
|
+
| `limits` | `FileUploadLimits` | Detailed limits (`fileSize`, `files`, `fieldSize`, `fields`, `parts`). |
|
|
273
|
+
|
|
274
|
+
**Default limits** (applied when a route does not set its own): `files: 10`, `fields: 100`, `parts: 200`, `fieldSize: 1 MB`, and `fileSize` equal to the app `bodyLimit` (10 MB by default). Exceeding a limit returns `413 Payload Too Large`. Files written to disk are named `<field>-<uuid><ext>`; field names and extensions are sanitized, so client input cannot choose the path.
|
|
273
275
|
|
|
274
276
|
#### Example
|
|
275
277
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "exisjs/sanitize"
|
|
3
|
-
description: "API Reference for ExisJS data sanitization engine:
|
|
3
|
+
description: "API Reference for ExisJS data sanitization engine: string cleaners, safeSanitize, and object transformers."
|
|
4
4
|
nextUrl: "/docs/reference/swagger"
|
|
5
5
|
nextTitle: "Swagger Reference"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# `exisjs/sanitize`
|
|
9
9
|
|
|
10
|
-
The `exisjs/sanitize` module provides pure data sanitization functions for cleaning and normalizing input strings, numbers, dates, arrays, and complex objects before business logic execution.
|
|
10
|
+
The `exisjs/sanitize` module provides pure data sanitization functions for cleaning and normalizing input strings, numbers, dates, arrays, and complex objects before business logic execution. It includes security transforms for XSS escaping, HTML stripping, and basic SQL injection and path traversal probe detection.
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
@@ -19,9 +19,9 @@ import { sanitize } from 'exisjs/sanitize'
|
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
22
|
-
## 1.
|
|
22
|
+
## 1. Security Sanitizers
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
The SQL and path traversal checks reject common probe strings; they complement, and never replace, parameterized queries and resolved-path checks.
|
|
25
25
|
|
|
26
26
|
| Function | Signature | Description |
|
|
27
27
|
| :--- | :--- | :--- |
|
|
@@ -7,7 +7,7 @@ nextTitle: "Decorators Reference"
|
|
|
7
7
|
|
|
8
8
|
# `exisjs/validator`
|
|
9
9
|
|
|
10
|
-
The `exisjs/validator` module provides the `tex` schema builder,
|
|
10
|
+
The `exisjs/validator` module provides the `tex` schema builder, `TexEngine` (schemas compile once into plain JS checks), data transformation modifiers, and pagination helpers for request body, query, and parameter validation.
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/error/errors.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/error/errors.ts"],"names":[],"mappings":"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"}
|