exisjs 0.7.8 → 0.7.10
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.map +1 -1
- package/dist/cli/commands/dev.js +14 -4
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/resolve-aliases.js +1 -1
- package/dist/cli/resolve-aliases.js.map +1 -1
- package/dist/config/config.js +1 -1
- package/dist/config/config.js.map +1 -1
- package/dist/decorators/caching.d.ts +53 -0
- package/dist/decorators/caching.d.ts.map +1 -0
- package/dist/decorators/caching.js +154 -0
- package/dist/decorators/caching.js.map +1 -0
- package/dist/decorators/constants.d.ts +6 -0
- package/dist/decorators/constants.d.ts.map +1 -1
- package/dist/decorators/constants.js +7 -1
- package/dist/decorators/constants.js.map +1 -1
- package/dist/decorators/core.d.ts +14 -0
- package/dist/decorators/core.d.ts.map +1 -1
- package/dist/decorators/core.js +156 -41
- package/dist/decorators/core.js.map +1 -1
- package/dist/decorators/filters.d.ts +29 -0
- package/dist/decorators/filters.d.ts.map +1 -0
- package/dist/decorators/filters.js +30 -0
- package/dist/decorators/filters.js.map +1 -0
- package/dist/decorators/index.d.ts +7 -0
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js +7 -0
- package/dist/decorators/index.js.map +1 -1
- package/dist/decorators/lifecycle.d.ts +66 -0
- package/dist/decorators/lifecycle.d.ts.map +1 -0
- package/dist/decorators/lifecycle.js +3 -0
- package/dist/decorators/lifecycle.js.map +1 -0
- package/dist/decorators/middleware.d.ts +24 -3
- package/dist/decorators/middleware.d.ts.map +1 -1
- package/dist/decorators/middleware.js +24 -3
- package/dist/decorators/middleware.js.map +1 -1
- package/dist/decorators/params.d.ts +3 -7
- package/dist/decorators/params.d.ts.map +1 -1
- package/dist/decorators/params.js +1 -7
- package/dist/decorators/params.js.map +1 -1
- package/dist/decorators/pipes.d.ts +91 -0
- package/dist/decorators/pipes.d.ts.map +1 -0
- package/dist/decorators/pipes.js +283 -0
- package/dist/decorators/pipes.js.map +1 -0
- package/dist/decorators/profile.d.ts +32 -0
- package/dist/decorators/profile.d.ts.map +1 -0
- package/dist/decorators/profile.js +61 -0
- package/dist/decorators/profile.js.map +1 -0
- package/dist/decorators/response.d.ts +0 -1
- package/dist/decorators/response.d.ts.map +1 -1
- package/dist/decorators/response.js +1 -18
- package/dist/decorators/response.js.map +1 -1
- package/dist/decorators/routing.d.ts +22 -12
- package/dist/decorators/routing.d.ts.map +1 -1
- package/dist/decorators/routing.js +49 -3
- package/dist/decorators/routing.js.map +1 -1
- package/dist/decorators/security.d.ts +42 -0
- package/dist/decorators/security.d.ts.map +1 -0
- package/dist/decorators/security.js +110 -0
- package/dist/decorators/security.js.map +1 -0
- package/dist/decorators/transaction.d.ts +29 -0
- package/dist/decorators/transaction.d.ts.map +1 -0
- package/dist/decorators/transaction.js +91 -0
- package/dist/decorators/transaction.js.map +1 -0
- package/dist/di/container.d.ts +8 -3
- package/dist/di/container.d.ts.map +1 -1
- package/dist/di/container.js +214 -66
- package/dist/di/container.js.map +1 -1
- package/dist/di/decorators.d.ts +23 -5
- package/dist/di/decorators.d.ts.map +1 -1
- package/dist/di/decorators.js +83 -20
- package/dist/di/decorators.js.map +1 -1
- package/dist/di/forward-ref.d.ts +23 -0
- package/dist/di/forward-ref.d.ts.map +1 -0
- package/dist/di/forward-ref.js +30 -0
- package/dist/di/forward-ref.js.map +1 -0
- package/dist/di/index.d.ts +2 -1
- package/dist/di/index.d.ts.map +1 -1
- package/dist/di/index.js +8 -1
- package/dist/di/index.js.map +1 -1
- package/dist/docs/content/controllers.mdx +230 -0
- package/dist/docs/content/decorators.mdx +89 -4
- package/dist/docs/content/errors.mdx +44 -21
- package/dist/docs/content/modules.mdx +23 -4
- package/dist/docs/content/providers.mdx +90 -6
- package/dist/docs/content/requests.mdx +9 -3
- package/dist/docs/content/responses.mdx +19 -5
- package/dist/docs/reference/config.mdx +3 -2
- package/dist/docs/reference/decorators.mdx +42 -8
- package/dist/docs/reference/di.mdx +45 -12
- package/dist/docs/reference/router.mdx +75 -0
- package/dist/docs/reference/validator.mdx +2 -2
- package/dist/error/errors.d.ts +24 -12
- package/dist/error/errors.d.ts.map +1 -1
- package/dist/error/errors.js +48 -12
- package/dist/error/errors.js.map +1 -1
- package/dist/router/controller-registrar.d.ts.map +1 -1
- package/dist/router/controller-registrar.js +217 -94
- package/dist/router/controller-registrar.js.map +1 -1
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js.map +1 -1
- package/dist/router/route-builder.d.ts +160 -29
- package/dist/router/route-builder.d.ts.map +1 -1
- package/dist/router/route-builder.js +105 -31
- package/dist/router/route-builder.js.map +1 -1
- package/dist/router/route-scanner.d.ts +3 -2
- package/dist/router/route-scanner.d.ts.map +1 -1
- package/dist/router/route-scanner.js.map +1 -1
- package/dist/router/router.d.ts.map +1 -1
- package/dist/router/router.js +44 -0
- package/dist/router/router.js.map +1 -1
- package/dist/router/scanner/functional-compiler.d.ts +1 -1
- package/dist/router/scanner/functional-compiler.d.ts.map +1 -1
- package/dist/router/scanner/functional-compiler.js +403 -64
- package/dist/router/scanner/functional-compiler.js.map +1 -1
- package/dist/router/types.d.ts +4 -2
- package/dist/router/types.d.ts.map +1 -1
- package/dist/server/app.d.ts +2 -0
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/app.js +40 -0
- package/dist/server/app.js.map +1 -1
- package/dist/server/helpers/etag.d.ts +1 -0
- package/dist/server/helpers/etag.d.ts.map +1 -1
- package/dist/server/helpers/etag.js +121 -10
- package/dist/server/helpers/etag.js.map +1 -1
- package/dist/server/helpers/ip.d.ts.map +1 -1
- package/dist/server/helpers/ip.js +75 -17
- package/dist/server/helpers/ip.js.map +1 -1
- package/dist/server/response.d.ts.map +1 -1
- package/dist/server/response.js +24 -9
- package/dist/server/response.js.map +1 -1
- package/dist/swagger/generator.d.ts.map +1 -1
- package/dist/swagger/generator.js +66 -22
- package/dist/swagger/generator.js.map +1 -1
- package/dist/testing/client.d.ts +16 -12
- package/dist/testing/client.d.ts.map +1 -1
- package/dist/testing/client.js +51 -15
- package/dist/testing/client.js.map +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +10 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +2 -0
- package/dist/utils/logger.js.map +1 -1
- package/dist/validator/engine.d.ts.map +1 -1
- package/dist/validator/engine.js +45 -0
- package/dist/validator/engine.js.map +1 -1
- package/package.json +1 -1
|
@@ -64,7 +64,9 @@ export default class UsersController {
|
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
<Info title="Note on Dependency Injection">
|
|
67
|
-
ExisJS
|
|
67
|
+
ExisJS provides two elegant ways to inject dependencies:
|
|
68
|
+
1. **Property-Level / Functional Injection**: Use `inject(UsersService)` for concise, boilerplate-free injection.
|
|
69
|
+
2. **Constructor Injection**: In OOP controllers and services, ExisJS inspects TypeScript's emitted `design:paramtypes` and `INJECTABLE_REGISTRY` to automatically inject service instances into constructor parameters (`constructor(private usersService: UsersService)`) without requiring explicit `@Inject()` decorators on every parameter!
|
|
68
70
|
</Info>
|
|
69
71
|
|
|
70
72
|
### Extended: Manual Resolution
|
|
@@ -78,6 +80,54 @@ const app = getActiveApp()
|
|
|
78
80
|
const usersService = app.container.resolve(UsersService)
|
|
79
81
|
```
|
|
80
82
|
|
|
83
|
+
## Service Lifecycle Hooks
|
|
84
|
+
|
|
85
|
+
ExisJS `@Injectable()` services can implement lifecycle interfaces. When the application starts up or shuts down gracefully, ExisJS automatically triggers these hooks on all managed singletons:
|
|
86
|
+
|
|
87
|
+
```typescript filename="src/http/database/service.ts"
|
|
88
|
+
import { Injectable, type OnInit, type OnDestroy } from 'exisjs/decorators'
|
|
89
|
+
|
|
90
|
+
@Injectable()
|
|
91
|
+
export class DatabaseService implements OnInit, OnDestroy {
|
|
92
|
+
async onInit(): Promise<void> {
|
|
93
|
+
// Invoked automatically during app startup (app.create / app.listen)
|
|
94
|
+
await this.connectPool()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async onDestroy(): Promise<void> {
|
|
98
|
+
// Invoked automatically during graceful termination (app.close / SIGINT / SIGTERM)
|
|
99
|
+
await this.drainPool()
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Service Method Enhancements
|
|
105
|
+
|
|
106
|
+
You can supercharge service methods with off-heap caching, automated transaction wrapping, and SLA performance profiling:
|
|
107
|
+
|
|
108
|
+
```typescript filename="src/http/users/service.ts"
|
|
109
|
+
import { Injectable, Cacheable, CacheEvict, Transactional, Profile } from 'exisjs/decorators'
|
|
110
|
+
|
|
111
|
+
@Injectable()
|
|
112
|
+
export class UsersService {
|
|
113
|
+
constructor(private db: DatabaseService) {}
|
|
114
|
+
|
|
115
|
+
// Caches return values off-heap in Rust LRU cache
|
|
116
|
+
@Cacheable({ ttl: 300, key: (id: string) => `user:${id}` })
|
|
117
|
+
@Profile({ thresholdMs: 100, warnSlow: true })
|
|
118
|
+
async findById(id: string) {
|
|
119
|
+
return this.db.query('SELECT * FROM users WHERE id = $1', [id])
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Runs in an atomic transaction; automatically rolls back on error
|
|
123
|
+
@Transactional()
|
|
124
|
+
@CacheEvict({ key: (id: string) => `user:${id}` })
|
|
125
|
+
async updateUser(id: string, data: any) {
|
|
126
|
+
return this.db.update('users', id, data)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
81
131
|
## Custom Providers
|
|
82
132
|
|
|
83
133
|
ExisJS's DI container is flexible. By default, when you register a class, ExisJS instantiates it for you. However, you often need more control. You can register standard classes, raw values, factories, or aliased classes natively using `app.provide()`.
|
|
@@ -134,20 +184,31 @@ import { UsersService } from './users/service'
|
|
|
134
184
|
export default class RootServer {}
|
|
135
185
|
```
|
|
136
186
|
|
|
137
|
-
### Constructor Injection (`@Inject
|
|
187
|
+
### Constructor & Property Injection (`@Inject`, `@Optional`, and `forwardRef`)
|
|
138
188
|
|
|
139
|
-
In addition to property-level `inject()`, ExisJS provides full OOP constructor
|
|
189
|
+
In addition to property-level `inject()`, ExisJS provides full OOP constructor and property field injection with `@Inject(token)`, `@Optional()`, and `forwardRef()`:
|
|
140
190
|
|
|
141
191
|
```typescript filename="src/http/users/route.ts"
|
|
142
|
-
import { Controller, Get, Param
|
|
192
|
+
import { Controller, Get, Param } from 'exisjs/decorators'
|
|
193
|
+
import { Inject, Optional, forwardRef } from 'exisjs/di'
|
|
143
194
|
import { UsersService } from './service'
|
|
195
|
+
import { ConfigService } from '../config/service'
|
|
144
196
|
|
|
145
197
|
@Controller('/users')
|
|
146
198
|
export default class UsersController {
|
|
199
|
+
// 1. Property-level decorator injection
|
|
200
|
+
@Inject(ConfigService)
|
|
201
|
+
private config!: ConfigService
|
|
202
|
+
|
|
203
|
+
// 2. Optional token injection
|
|
204
|
+
@Inject('METRICS')
|
|
205
|
+
@Optional()
|
|
206
|
+
private metrics?: any
|
|
207
|
+
|
|
208
|
+
// 3. Constructor injection with forwardRef for circular dependencies
|
|
147
209
|
constructor(
|
|
148
210
|
private usersService: UsersService,
|
|
149
|
-
@Inject(
|
|
150
|
-
@Optional() @Inject('METRICS') private metrics?: any
|
|
211
|
+
@Inject(forwardRef(() => UsersService)) private circularRef: UsersService
|
|
151
212
|
) {}
|
|
152
213
|
|
|
153
214
|
@Get('/:id')
|
|
@@ -157,6 +218,29 @@ export default class UsersController {
|
|
|
157
218
|
}
|
|
158
219
|
```
|
|
159
220
|
|
|
221
|
+
### Forward References (`forwardRef`) & Circular Dependencies
|
|
222
|
+
|
|
223
|
+
When two services mutually depend on each other (`ServiceA` requires `ServiceB` and `ServiceB` requires `ServiceA`), circular references normally lead to `ReferenceError` during module evaluation. ExisJS solves this using `forwardRef(() => Service)` alongside lazy ES6 proxy resolution:
|
|
224
|
+
|
|
225
|
+
```typescript filename="src/services/order.service.ts"
|
|
226
|
+
import { Injectable } from 'exisjs/decorators'
|
|
227
|
+
import { Inject, forwardRef } from 'exisjs/di'
|
|
228
|
+
import { InventoryService } from './inventory.service'
|
|
229
|
+
|
|
230
|
+
@Injectable()
|
|
231
|
+
export class OrderService {
|
|
232
|
+
constructor(
|
|
233
|
+
@Inject(forwardRef(() => InventoryService))
|
|
234
|
+
private inventory: InventoryService
|
|
235
|
+
) {}
|
|
236
|
+
|
|
237
|
+
processOrder(id: string) {
|
|
238
|
+
return this.inventory.reserveStock(id)
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
|
|
160
244
|
## Injection Scopes
|
|
161
245
|
|
|
162
246
|
For people coming from different programming language backgrounds, it might be unexpected to learn that in ExisJS, almost everything is shared across incoming requests. We have a connection pool to the database, singleton services with global state, etc. This is because Node.js doesn't follow the request/response Multi-Threaded Stateless Model.
|
|
@@ -451,11 +451,17 @@ const language = req.acceptsLanguages('en', 'es', 'fr')
|
|
|
451
451
|
|
|
452
452
|
---
|
|
453
453
|
|
|
454
|
-
## 7. Network Information &
|
|
454
|
+
## 7. Network Information & Reverse Proxy IP Resolution
|
|
455
455
|
|
|
456
|
-
ExisJS handles
|
|
456
|
+
ExisJS handles reverse proxies, CDNs, and dual-stack IP networks automatically. When deployed behind Cloudflare, AWS ALB, NGINX, Render, or GCP, set `trustProxy: true` in your `exis.config.ts` or define `TRUST_PROXY=true` in your environment variables.
|
|
457
457
|
|
|
458
|
-
|
|
458
|
+
When proxy trust is enabled, ExisJS evaluates industry-standard proxy and CDN headers in priority order:
|
|
459
|
+
1. `CF-Connecting-IP` (Cloudflare)
|
|
460
|
+
2. `True-Client-IP` (Akamai / Cloudflare Enterprise)
|
|
461
|
+
3. `X-Real-IP` / `X-Client-IP` (NGINX / Reverse Proxies)
|
|
462
|
+
4. `X-Forwarded-For` (Standard multi-hop proxy chains)
|
|
463
|
+
|
|
464
|
+
- **`req.ip`**: The real client's IP address. Automatically strips proxy hops, normalizes IPv6-mapped IPv4 dual-stack addresses (e.g., `::ffff:192.168.1.50` $\to$ `192.168.1.50`), and converts IPv6 loopback `::1` $\to$ `127.0.0.1` for consistent rate limiting and geo-IP lookups.
|
|
459
465
|
- **`req.ips`**: An array of IPs if multiple proxies were traversed, each normalized cleanly.
|
|
460
466
|
- **`req.hostname`**: The resolved hostname (from `Host` or `X-Forwarded-Host`).
|
|
461
467
|
- **`req.protocol`**: Returns `'http'` or `'https'`.
|
|
@@ -575,17 +575,31 @@ export default class AuthController {
|
|
|
575
575
|
|
|
576
576
|
---
|
|
577
577
|
|
|
578
|
-
## 6.
|
|
578
|
+
## 6. Safe Response Serialization (BSON, Dates & Prototype Auto-Unwrapping)
|
|
579
579
|
|
|
580
|
-
ExisJS
|
|
580
|
+
ExisJS includes a robust, zero-crash serialization engine that automatically prepares complex objects before sending them down the wire:
|
|
581
581
|
|
|
582
|
-
|
|
582
|
+
- **Mongoose / ORM Documents**: Automatically unwraps documents by invoking `.toObject({ virtuals: true, getters: true })` or `.toJSON()`. You no longer need to call `.lean()` or sanitize documents manually.
|
|
583
|
+
- **MongoDB BSON Types**: Automatically converts `ObjectId`, `Decimal128`, `Long`, and `Binary` buffers to their clean string / base64 representations.
|
|
584
|
+
- **BigInt & Dates**: Converts native `BigInt` values to strings and `Date` instances to ISO 8601 strings.
|
|
585
|
+
- **Maps & Sets**: Automatically converts `Map` to plain objects and `Set` to arrays.
|
|
586
|
+
- **Circular Reference Guard**: Circular object references are safely detected and replaced with `'[Circular]'` rather than throwing fatal V8 JSON serialization exceptions.
|
|
587
|
+
- **Enhanced Diagnostics**: If an unhandled serialization issue occurs, ExisJS logs the exact property path (e.g. `'recentTransactions[0].accountId'`) for immediate debugging clarity.
|
|
583
588
|
|
|
584
|
-
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
## 7. ETags & Conditional Requests (304 Not Modified)
|
|
592
|
+
|
|
593
|
+
ExisJS features native ETag generation.
|
|
594
|
+
|
|
595
|
+
By default, ETags are disabled (`etag: false`) on dynamic REST API endpoints to minimize hashing CPU overhead and provide transparent logging timings.
|
|
596
|
+
|
|
597
|
+
When enabled globally (`etag: true` in `exis.config.ts`) or on a per-route basis (`res.etagEnabled = true`):
|
|
598
|
+
- If the client sends an `If-None-Match` header matching the response ETag, ExisJS immediately sends `304 Not Modified` with an empty body, saving bandwidth.
|
|
585
599
|
|
|
586
600
|
---
|
|
587
601
|
|
|
588
|
-
##
|
|
602
|
+
## 8. Server-Sent Events (SSE) & AI Streaming
|
|
589
603
|
|
|
590
604
|
ExisJS provides a first-class `res.sse()` method for real-time live events and AI chat completions (OpenAI, Anthropic, Ollama, LangChain).
|
|
591
605
|
|
|
@@ -103,14 +103,15 @@ Here is the full list of options supported natively by the ExisJS configuration
|
|
|
103
103
|
- **`host`** *(string)*: The host interface to bind to (default: `'0.0.0.0'`).
|
|
104
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
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
|
|
106
|
+
- **`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
107
|
|
|
108
108
|
### Security & Middlewares
|
|
109
109
|
- **`cors`** *(boolean | object)*: Enable and configure Cross-Origin Resource Sharing globally.
|
|
110
110
|
- **`helmet`** *(boolean | object)*: Enable strict security headers. Enabled by default.
|
|
111
111
|
- **`logger`** *(boolean | object)*: Configure the efficient Pino request logger.
|
|
112
112
|
- **`compression`** *(boolean | object)*: Enable gzip/brotli response compression.
|
|
113
|
-
- **`
|
|
113
|
+
- **`etag`** *(boolean)*: Automatic ETag generation for HTTP conditional requests (default: `false` for dynamic API routes).
|
|
114
|
+
- **`bodyLimit`** *(number)*: Maximum JSON/URL-encoded payload size in bytes (default: `10MB`).
|
|
114
115
|
|
|
115
116
|
### Extended Integrations
|
|
116
117
|
- **`queue`**: Define your background queue driver (`memory` or `redis`) and configure `maxConcurrent` processing limits.
|
|
@@ -83,9 +83,21 @@ Parameter decorators automatically extract data from the incoming request and in
|
|
|
83
83
|
|
|
84
84
|
---
|
|
85
85
|
|
|
86
|
-
## 3.
|
|
87
|
-
|
|
86
|
+
## 3. Transformation Pipes & Middleware Decorators
|
|
87
|
+
|
|
88
|
+
- `@UsePipes(...pipes)`: Binds transformation and validation pipes to a controller class or route method.
|
|
89
|
+
- **Built-in Pipes**:
|
|
90
|
+
- `ParseIntPipe`: Parses numeric strings to integers (`options?: { errorHttpStatusCode, exceptionFactory }`).
|
|
91
|
+
- `ParseFloatPipe`: Parses numeric float strings (`options?: { errorHttpStatusCode, exceptionFactory }`).
|
|
92
|
+
- `ParseBoolPipe`: Parses boolean strings (`'true'`, `'false'`, `1`, `0`).
|
|
93
|
+
- `ParseUUIDPipe`: Validates UUID format (`options?: { version: '3' | '4' | '5' | 'all' }`).
|
|
94
|
+
- `ParseArrayPipe`: Splits comma-separated strings and applies item-level transformation pipes.
|
|
95
|
+
- `ValidationPipe`: Validates DTO classes and schemas (`options?: { transform: boolean }`).
|
|
88
96
|
- `@Use(...middlewaresOrGuardsOrInterceptorsOrFilters)`: Unified decorator that binds middlewares, guards (`canActivate`), interceptors (`intercept`), or exception filters (`catch`) to a controller class or method.
|
|
97
|
+
- `@UseGuards(...guards)`: Dedicated decorator to apply guards to a class or method.
|
|
98
|
+
- `@UseInterceptors(...interceptors)`: Dedicated decorator to apply interceptors to a class or method.
|
|
99
|
+
- `@UseFilters(...filters)`: Dedicated decorator to apply exception filters to a class or method.
|
|
100
|
+
- `@Catch(...exceptions)`: Class decorator for `ExceptionFilter` implementations declaring handled exception types.
|
|
89
101
|
- `@Upload(fieldName, options?)` / `@UseUpload(...)`: Configures multipart file upload handling for a method, populating `@UploadedFile()` or `@UploadedFiles()`.
|
|
90
102
|
- `@Timeout(ms: number | TimeoutOptions)`: Overrides the request timeout for a class or method (e.g. `@Timeout(120000)`). Passing `0` disables timeouts for long-running streaming or exports.
|
|
91
103
|
- `@Idempotent(options?)`: Caches responses based on the `Idempotency-Key` header using native in-memory off-heap storage. Works at class and method level.
|
|
@@ -98,13 +110,32 @@ Parameter decorators automatically extract data from the incoming request and in
|
|
|
98
110
|
|
|
99
111
|
---
|
|
100
112
|
|
|
101
|
-
## 4.
|
|
113
|
+
## 4. Security & Access Control Decorators
|
|
114
|
+
|
|
115
|
+
- `@Permissions(...permissions: string[])`: Sets required permission keys for a controller or route method (automatically bypassed if `req.user.isSuperAdmin` is true).
|
|
116
|
+
- `@Roles(...roles: string[])`: Sets required role names for a controller or route method.
|
|
117
|
+
- `@Public()` / `@IsPublic()` / `@AllowAnonymous()`: Marks a controller or route as public, instructing global authentication guards to bypass credential checks.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 5. Service Enhancements: Caching, Transactions & Profiling
|
|
122
|
+
|
|
123
|
+
- `@Cacheable({ ttl?: number, key?: string | ((...args) => string) })`: Caches method return values in ExisJS's off-heap native memory LRU cache.
|
|
124
|
+
- `@CacheEvict({ key: string | ((...args) => string) })`: Evicts cached entries matching the key when the method completes.
|
|
125
|
+
- `@Transactional(options?)`: Automatically wraps service operations in an atomic database transaction.
|
|
126
|
+
- `@Profile({ thresholdMs?: number, warnSlow?: boolean, log?: Function })`: Profiles method execution time with sub-millisecond precision.
|
|
127
|
+
- `OnInit` (`onInit()`): Invoked when the application or service boots up.
|
|
128
|
+
- `OnDestroy` (`onDestroy()`): Invoked when the application gracefully terminates.
|
|
129
|
+
- `OnModuleInit`, `OnModuleDestroy`, `OnApplicationBootstrap`: Modular lifecycle hooks.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 6. Response & Metadata Modifiers
|
|
102
134
|
|
|
103
135
|
- `@HttpCode(code: number)`: Enforces a custom HTTP status code on success.
|
|
104
136
|
- `@Header(name: string, value: string)`: Appends an HTTP header to the response.
|
|
105
137
|
- `@Returns(schema)`: Attaches response schema for OpenAPI documentation.
|
|
106
138
|
- `@Redirect(url: string, statusCode?: number)`: Redirects request to target URL.
|
|
107
|
-
- `@Permissions(...permissions: string[])`: Sets required permissions on the route.
|
|
108
139
|
- `@Hosts(...hosts: string[])`: Restricts routes to specific Host headers.
|
|
109
140
|
- `@SetMetadata(key, value)`: Attaches arbitrary custom metadata to a class or handler method, retrievable via `Reflector`.
|
|
110
141
|
- `Reflector`: Utility class to inspect metadata attached by `@SetMetadata`:
|
|
@@ -114,20 +145,23 @@ Parameter decorators automatically extract data from the incoming request and in
|
|
|
114
145
|
|
|
115
146
|
---
|
|
116
147
|
|
|
117
|
-
##
|
|
148
|
+
## 7. Dependency Injection & Modules
|
|
118
149
|
|
|
150
|
+
- `@Global()`: Marks a `@Module()` as global, automatically making its exported providers available across the entire application without explicit re-importing.
|
|
119
151
|
- `@Module({ imports?, controllers?, providers?, exports? })`: OOP class decorator equivalent to `defineModule()` for structuring application modules.
|
|
120
152
|
- `@Injectable(options?)`: Marks a service class as an IoC provider managed by the container. Supports `{ scope: 'singleton' | 'request' | 'transient' }`.
|
|
121
|
-
- `@Inject(token)`: Constructor parameter decorator from `exisjs/di` for injecting dependencies by class, string, or symbol token.
|
|
122
|
-
- `@Optional()`:
|
|
153
|
+
- `@Inject(token)`: Constructor parameter and class property decorator from `exisjs/di` for injecting dependencies by class, string, or symbol token.
|
|
154
|
+
- `@Optional()`: Parameter and property decorator from `exisjs/di` resolving missing dependencies to `undefined` without throwing.
|
|
155
|
+
- `forwardRef(() => Target)`: Creates a lazy forward reference for resolving circular dependencies in constructor or property injection.
|
|
123
156
|
- `@Boundary(config)`: Marks a class as a folder-scoped Boundary configuring CORS, headers, exclusions, and pipeline steps.
|
|
124
157
|
- `@Server(config)`: Marks a class as a root server definition.
|
|
125
158
|
|
|
126
159
|
---
|
|
127
160
|
|
|
128
|
-
##
|
|
161
|
+
## 8. Cron & Task Scheduling
|
|
129
162
|
|
|
130
163
|
- `@Cron(schedule: string, options?: CronJobOptions)`: Schedules a class method as a recurring cron job on any `@Injectable()` service.
|
|
131
164
|
- `@Interval(ms: number, options?: IntervalOptions)`: Schedules a class method to execute repeatedly every `ms` milliseconds.
|
|
132
165
|
- `@TimeoutTask(ms: number, options?: TimeoutOptions)` (alias `@ScheduledTimeout`): Schedules a class method to execute once after `ms` milliseconds following application bootstrap.
|
|
133
166
|
|
|
167
|
+
|
|
@@ -109,20 +109,39 @@ export default controller({
|
|
|
109
109
|
> [!NOTE]
|
|
110
110
|
> `inject()` relies on the active execution context. If you call `inject()` outside of an active HTTP request or application lifecycle hook (e.g., in a background `setInterval` without context), it will throw an error.
|
|
111
111
|
|
|
112
|
-
### 2. Constructor Injection in Classes (OOP)
|
|
112
|
+
### 2. Constructor & Property Injection in Classes (OOP)
|
|
113
113
|
|
|
114
|
-
ExisJS supports full OOP constructor parameter
|
|
114
|
+
ExisJS supports full OOP constructor parameter and property field injection with **automatic parameter type reflection**:
|
|
115
|
+
- **Automatic Type Reflection**: If `emitDecoratorMetadata: true` is enabled in `tsconfig.json`, ExisJS inspects `design:paramtypes` and automatically maps constructor parameter classes directly to dependencies registered in `INJECTABLE_REGISTRY`. You do not need `@Inject()` on standard service classes!
|
|
116
|
+
- **Property-Level `@Inject()`**: Inject dependencies directly onto class properties (`@Inject(Token) public service!: Service`) with automatic `design:type` reflection fallback.
|
|
117
|
+
- **Token Injection (`@Inject(token)`)**: Use `@Inject('TOKEN')` when injecting custom strings, config objects, or symbols.
|
|
118
|
+
- **Optional Dependencies (`@Optional()`)**: Mark dependencies that can safely resolve to `undefined` if not registered.
|
|
119
|
+
- **Forward References (`forwardRef(() => Service)`)**: Resolve circular dependencies between services using lazy forward references and ES6 Proxy delegates.
|
|
115
120
|
|
|
116
121
|
```typescript filename="src/http/users/route.ts"
|
|
117
|
-
import { Controller, Get, Param
|
|
122
|
+
import { Controller, Get, Param } from 'exisjs/decorators'
|
|
123
|
+
import { Inject, Optional, forwardRef } from 'exisjs/di'
|
|
118
124
|
import { UserService } from './service'
|
|
125
|
+
import { ConfigService } from '../config/service'
|
|
119
126
|
|
|
120
127
|
@Controller('/users')
|
|
121
128
|
export default class UserController {
|
|
129
|
+
// 1. Property-level injection
|
|
130
|
+
@Inject(ConfigService)
|
|
131
|
+
private config!: ConfigService
|
|
132
|
+
|
|
133
|
+
// 2. Optional property injection
|
|
134
|
+
@Inject('ANALYTICS')
|
|
135
|
+
@Optional()
|
|
136
|
+
private analytics?: any
|
|
137
|
+
|
|
122
138
|
constructor(
|
|
139
|
+
// 3. Automatically resolved from INJECTABLE_REGISTRY via type reflection!
|
|
123
140
|
private userService: UserService,
|
|
141
|
+
// 4. Explicit token injection
|
|
124
142
|
@Inject('DATABASE_URL') private dbUrl: string,
|
|
125
|
-
|
|
143
|
+
// 5. Circular dependency resolution via forwardRef
|
|
144
|
+
@Inject(forwardRef(() => UserService)) private circularRef: UserService
|
|
126
145
|
) {}
|
|
127
146
|
|
|
128
147
|
@Get('/:id')
|
|
@@ -132,16 +151,30 @@ export default class UserController {
|
|
|
132
151
|
}
|
|
133
152
|
```
|
|
134
153
|
|
|
135
|
-
You can also use property-level `inject()` inside your Controller methods:
|
|
136
154
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Lifecycle Hooks (`OnInit`, `OnDestroy`)
|
|
158
|
+
|
|
159
|
+
Providers can implement lifecycle interfaces. When the application container initializes or gracefully closes, ExisJS automatically triggers these hooks:
|
|
160
|
+
|
|
161
|
+
- **`OnInit` (`onInit()` / `onModuleInit()`)**: Executed when the container initializes during `app.create()` or `app.listen()`.
|
|
162
|
+
- **`OnDestroy` (`onDestroy()` / `onModuleDestroy()`)**: Executed when the application receives a termination signal or `app.close()` is called.
|
|
163
|
+
- **`OnApplicationBootstrap` (`onApplicationBootstrap()`)**: Executed after all modules are initialized and before the server begins listening for incoming requests.
|
|
164
|
+
|
|
165
|
+
```typescript filename="src/http/database/service.ts"
|
|
166
|
+
import { Injectable, type OnInit, type OnDestroy } from 'exisjs/decorators'
|
|
167
|
+
|
|
168
|
+
@Injectable()
|
|
169
|
+
export class DatabaseService implements OnInit, OnDestroy {
|
|
170
|
+
async onInit(): Promise<void> {
|
|
171
|
+
await this.pool.connect()
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async onDestroy(): Promise<void> {
|
|
175
|
+
await this.pool.end()
|
|
144
176
|
}
|
|
145
177
|
}
|
|
146
178
|
```
|
|
147
179
|
|
|
180
|
+
|
|
@@ -47,6 +47,81 @@ export default controller({
|
|
|
47
47
|
- `onResponse` (`HookResponse`): A file-level response hook.
|
|
48
48
|
- `[key: string]` (`RouteDefinition`): **Any other key** is treated as a route definition (e.g. `getUser: route.get(...)`). This is how you bind routes to the controller!
|
|
49
49
|
|
|
50
|
+
### `controller.merge(...controllers)`
|
|
51
|
+
|
|
52
|
+
Merges multiple functional controllers into a single unified controller definition. This enables splitting large `route.ts` files into modular, domain-specific controller pieces while exporting a single composite controller from the route file.
|
|
53
|
+
|
|
54
|
+
```typescript filename="src/http/auth/route.ts"
|
|
55
|
+
import { controller, route } from 'exisjs/router'
|
|
56
|
+
|
|
57
|
+
// Sub-controller 1: credentials & sessions
|
|
58
|
+
const authRoutes = controller({
|
|
59
|
+
login: route.post('/login', {
|
|
60
|
+
async handle({ body }) {
|
|
61
|
+
return { token: 'jwt-token' }
|
|
62
|
+
}
|
|
63
|
+
}),
|
|
64
|
+
logout: route.post('/logout', {
|
|
65
|
+
async handle() {
|
|
66
|
+
return { success: true }
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
// Sub-controller 2: OAuth integrations
|
|
72
|
+
const oauthRoutes = controller({
|
|
73
|
+
google: route.get('/google', {
|
|
74
|
+
async handle() {
|
|
75
|
+
return { redirectUrl: 'https://accounts.google.com/o/oauth2/auth' }
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
// Export combined controller as default
|
|
81
|
+
export default controller.merge(authRoutes, oauthRoutes)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### `controller.extend(base, overrides)`
|
|
85
|
+
|
|
86
|
+
Inherits all route definitions, middlewares, filters, and configuration from a `base` controller and applies overrides or adds new route endpoints. This provides the functional equivalent of class-based controller inheritance for reusable CRUD templates.
|
|
87
|
+
|
|
88
|
+
```typescript filename="src/http/products/route.ts"
|
|
89
|
+
import { controller, route } from 'exisjs/router'
|
|
90
|
+
|
|
91
|
+
// Reusable generic CRUD controller factory
|
|
92
|
+
function createBaseCrud(resource: string) {
|
|
93
|
+
return controller({
|
|
94
|
+
list: route.get('/', {
|
|
95
|
+
async handle() {
|
|
96
|
+
return { resource, items: [] }
|
|
97
|
+
}
|
|
98
|
+
}),
|
|
99
|
+
getById: route.get('/:id', {
|
|
100
|
+
async handle({ params }) {
|
|
101
|
+
return { resource, id: params.id }
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Inherit base endpoints and add specialized or overridden routes
|
|
108
|
+
export default controller.extend(createBaseCrud('products'), {
|
|
109
|
+
// Additional specialized route
|
|
110
|
+
featured: route.get('/featured', {
|
|
111
|
+
async handle() {
|
|
112
|
+
return { featured: ['Product 1', 'Product 2'] }
|
|
113
|
+
}
|
|
114
|
+
}),
|
|
115
|
+
|
|
116
|
+
// Override base endpoint
|
|
117
|
+
getById: route.get('/:id', {
|
|
118
|
+
async handle({ params }) {
|
|
119
|
+
return { customProduct: true, id: params.id }
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
```
|
|
124
|
+
|
|
50
125
|
---
|
|
51
126
|
|
|
52
127
|
## `route`
|
|
@@ -14,8 +14,8 @@ ExisJS ships with a efficient, integrated validation library out of the box, exp
|
|
|
14
14
|
The `tex` object provides factory functions for every common primitive and data structure:
|
|
15
15
|
|
|
16
16
|
- `tex.string(options?)`: Validates a string (supports min, max, trim, sanitizers, masking, SQL injection prevention, defaults).
|
|
17
|
-
- `tex.number(options?)`: Validates a numeric value (supports min, max,
|
|
18
|
-
- `tex.boolean(options?)`: Validates a boolean value (supports
|
|
17
|
+
- `tex.number(options?)`: Validates a numeric value. Automatically coerces numeric strings in query parameters (e.g., `?limit=100` $\to$ `100`) and supports `{ coerce: true, min, max, default }`.
|
|
18
|
+
- `tex.boolean(options?)`: Validates a boolean value. Automatically coerces boolean strings in query parameters (e.g., `?active=true` $\to$ `true`, `"1"`/`"0"`, `"yes"`/`"no"`) and supports `{ coerce: true, default }`.
|
|
19
19
|
- `tex.email(options?)`: Validates standard email formatting.
|
|
20
20
|
- `tex.uuid(options?)`: Validates UUID v1 or v4 strings (`{ version: 1 | 4 }`).
|
|
21
21
|
- `tex.cuid(options?)`: Validates CUID identifiers.
|
package/dist/error/errors.d.ts
CHANGED
|
@@ -51,18 +51,30 @@ export declare class ServiceUnavailableError extends HttpError {
|
|
|
51
51
|
export declare class GatewayTimeoutError extends HttpError {
|
|
52
52
|
constructor(message?: string);
|
|
53
53
|
}
|
|
54
|
-
export declare
|
|
55
|
-
|
|
56
|
-
export declare
|
|
57
|
-
|
|
58
|
-
export declare
|
|
59
|
-
|
|
60
|
-
export declare
|
|
61
|
-
|
|
62
|
-
export declare
|
|
63
|
-
|
|
64
|
-
export declare
|
|
65
|
-
|
|
54
|
+
export declare class HttpException extends HttpError {
|
|
55
|
+
}
|
|
56
|
+
export declare class BadRequestException extends BadRequestError {
|
|
57
|
+
}
|
|
58
|
+
export declare class UnauthorizedException extends UnauthorizedError {
|
|
59
|
+
}
|
|
60
|
+
export declare class ForbiddenException extends ForbiddenError {
|
|
61
|
+
}
|
|
62
|
+
export declare class NotFoundException extends NotFoundError {
|
|
63
|
+
}
|
|
64
|
+
export declare class ConflictException extends ConflictError {
|
|
65
|
+
}
|
|
66
|
+
export declare class PayloadTooLargeException extends PayloadTooLargeError {
|
|
67
|
+
}
|
|
68
|
+
export declare class UnprocessableException extends UnprocessableError {
|
|
69
|
+
}
|
|
70
|
+
export declare class RateLimitException extends RateLimitError {
|
|
71
|
+
}
|
|
72
|
+
export declare class InternalException extends InternalError {
|
|
73
|
+
}
|
|
74
|
+
export declare class ServiceUnavailableException extends ServiceUnavailableError {
|
|
75
|
+
}
|
|
76
|
+
export declare class GatewayTimeoutException extends GatewayTimeoutError {
|
|
77
|
+
}
|
|
66
78
|
export declare function renderValidationTable(rows: {
|
|
67
79
|
field: string;
|
|
68
80
|
received: string;
|
|
@@ -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;AAKrD,qBAAa,SAAU,SAAQ,KAAK;IAClC,SAAgB,UAAU,EAAE,MAAM,CAAA;IAClC,SAAgB,IAAI,EAAE,MAAM,CAAA;IAC5B,SAAgB,aAAa,EAAE,OAAO,CAAA;IACtC,SAAgB,OAAO,CAAC,EAAE,OAAO,CAAA;gBAG/B,OAAO,EAAE,MAAM,EACf,UAAU,SAAM,EAChB,IAAI,SAAmB,EACvB,OAAO,CAAC,EAAE,OAAO;IAenB,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS;IAIhE,MAAM,CAAC,YAAY,CAAC,OAAO,SAAiB,GAAG,SAAS;IAIxD,MAAM,CAAC,SAAS,CAAC,OAAO,SAAc,GAAG,SAAS;IAIlD,MAAM,CAAC,QAAQ,CAAC,QAAQ,SAAa,GAAG,SAAS;IAIjD,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS;IAI3C,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS;IAInE,MAAM,CAAC,eAAe,CACpB,OAAO,SAAsB,EAC7B,OAAO,CAAC,EAAE,OAAO,GAChB,SAAS;IAIZ,MAAM,CAAC,eAAe,CAAC,OAAO,SAAsB,GAAG,SAAS;IAIhE,MAAM,CAAC,QAAQ,CAAC,OAAO,SAA0B,GAAG,SAAS;IAI7D,MAAM,CAAC,kBAAkB,CAAC,OAAO,SAAwB,GAAG,SAAS;IAIrE,MAAM,CAAC,cAAc,CAAC,OAAO,SAAoB,GAAG,SAAS;IAI7D,MAAM,IAAI,MAAM;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/error/errors.ts"],"names":[],"mappings":"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"}
|
package/dist/error/errors.js
CHANGED
|
@@ -63,6 +63,7 @@ class HttpError extends Error {
|
|
|
63
63
|
message: this.message,
|
|
64
64
|
...(this.details !== undefined && { details: this.details }),
|
|
65
65
|
},
|
|
66
|
+
...(this.details && typeof this.details === 'object' ? this.details : {}),
|
|
66
67
|
};
|
|
67
68
|
}
|
|
68
69
|
}
|
|
@@ -145,18 +146,42 @@ class GatewayTimeoutError extends HttpError {
|
|
|
145
146
|
}
|
|
146
147
|
exports.GatewayTimeoutError = GatewayTimeoutError;
|
|
147
148
|
// ─── Exception Aliases ────────────────────────────────────────────────────────
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
exports.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
exports.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
exports.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
exports.
|
|
149
|
+
class HttpException extends HttpError {
|
|
150
|
+
}
|
|
151
|
+
exports.HttpException = HttpException;
|
|
152
|
+
class BadRequestException extends BadRequestError {
|
|
153
|
+
}
|
|
154
|
+
exports.BadRequestException = BadRequestException;
|
|
155
|
+
class UnauthorizedException extends UnauthorizedError {
|
|
156
|
+
}
|
|
157
|
+
exports.UnauthorizedException = UnauthorizedException;
|
|
158
|
+
class ForbiddenException extends ForbiddenError {
|
|
159
|
+
}
|
|
160
|
+
exports.ForbiddenException = ForbiddenException;
|
|
161
|
+
class NotFoundException extends NotFoundError {
|
|
162
|
+
}
|
|
163
|
+
exports.NotFoundException = NotFoundException;
|
|
164
|
+
class ConflictException extends ConflictError {
|
|
165
|
+
}
|
|
166
|
+
exports.ConflictException = ConflictException;
|
|
167
|
+
class PayloadTooLargeException extends PayloadTooLargeError {
|
|
168
|
+
}
|
|
169
|
+
exports.PayloadTooLargeException = PayloadTooLargeException;
|
|
170
|
+
class UnprocessableException extends UnprocessableError {
|
|
171
|
+
}
|
|
172
|
+
exports.UnprocessableException = UnprocessableException;
|
|
173
|
+
class RateLimitException extends RateLimitError {
|
|
174
|
+
}
|
|
175
|
+
exports.RateLimitException = RateLimitException;
|
|
176
|
+
class InternalException extends InternalError {
|
|
177
|
+
}
|
|
178
|
+
exports.InternalException = InternalException;
|
|
179
|
+
class ServiceUnavailableException extends ServiceUnavailableError {
|
|
180
|
+
}
|
|
181
|
+
exports.ServiceUnavailableException = ServiceUnavailableException;
|
|
182
|
+
class GatewayTimeoutException extends GatewayTimeoutError {
|
|
183
|
+
}
|
|
184
|
+
exports.GatewayTimeoutException = GatewayTimeoutException;
|
|
160
185
|
// ─── Global Error Handler ─────────────────────────────────────────────────────
|
|
161
186
|
function escapeHtml(str) {
|
|
162
187
|
if (typeof str !== 'string')
|
|
@@ -478,11 +503,14 @@ function createErrorHandler(isDev = false) {
|
|
|
478
503
|
httpPart: err.httpPart,
|
|
479
504
|
}));
|
|
480
505
|
res.status(400).json({
|
|
506
|
+
success: false,
|
|
481
507
|
statusCode: 400,
|
|
482
508
|
error: 'Bad Request',
|
|
483
509
|
message: message || 'Validation Error',
|
|
510
|
+
validationErrors: errorsMap,
|
|
484
511
|
errors: errorsMap,
|
|
485
512
|
details: responseDetails,
|
|
513
|
+
timestamp: new Date().toISOString(),
|
|
486
514
|
});
|
|
487
515
|
return;
|
|
488
516
|
}
|
|
@@ -490,10 +518,14 @@ function createErrorHandler(isDev = false) {
|
|
|
490
518
|
if (err instanceof SyntaxError && 'body' in err) {
|
|
491
519
|
res.status(400).json({
|
|
492
520
|
success: false,
|
|
521
|
+
statusCode: 400,
|
|
493
522
|
error: {
|
|
494
523
|
code: 'INVALID_JSON',
|
|
495
524
|
message: 'Invalid JSON in request body',
|
|
496
525
|
},
|
|
526
|
+
message: 'Invalid JSON in request body',
|
|
527
|
+
validationErrors: null,
|
|
528
|
+
timestamp: new Date().toISOString(),
|
|
497
529
|
});
|
|
498
530
|
return;
|
|
499
531
|
}
|
|
@@ -514,11 +546,15 @@ function createErrorHandler(isDev = false) {
|
|
|
514
546
|
}
|
|
515
547
|
res.status(500).json({
|
|
516
548
|
success: false,
|
|
549
|
+
statusCode: 500,
|
|
517
550
|
error: {
|
|
518
551
|
code: 'INTERNAL_ERROR',
|
|
519
552
|
message: isDev ? err.message : 'An unexpected error occurred',
|
|
520
553
|
...(isDev && { stack: err.stack }),
|
|
521
554
|
},
|
|
555
|
+
message: isDev ? err.message : 'An unexpected error occurred',
|
|
556
|
+
validationErrors: null,
|
|
557
|
+
timestamp: new Date().toISOString(),
|
|
522
558
|
});
|
|
523
559
|
};
|
|
524
560
|
}
|