exisjs 0.7.7 → 0.7.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/cli/commands/dev.d.ts.map +1 -1
  2. package/dist/cli/commands/dev.js +14 -4
  3. package/dist/cli/commands/dev.js.map +1 -1
  4. package/dist/cli/resolve-aliases.js +1 -1
  5. package/dist/cli/resolve-aliases.js.map +1 -1
  6. package/dist/decorators/caching.d.ts +53 -0
  7. package/dist/decorators/caching.d.ts.map +1 -0
  8. package/dist/decorators/caching.js +154 -0
  9. package/dist/decorators/caching.js.map +1 -0
  10. package/dist/decorators/constants.d.ts +4 -0
  11. package/dist/decorators/constants.d.ts.map +1 -1
  12. package/dist/decorators/constants.js +5 -1
  13. package/dist/decorators/constants.js.map +1 -1
  14. package/dist/decorators/filters.d.ts +37 -0
  15. package/dist/decorators/filters.d.ts.map +1 -0
  16. package/dist/decorators/filters.js +30 -0
  17. package/dist/decorators/filters.js.map +1 -0
  18. package/dist/decorators/index.d.ts +6 -0
  19. package/dist/decorators/index.d.ts.map +1 -1
  20. package/dist/decorators/index.js +6 -0
  21. package/dist/decorators/index.js.map +1 -1
  22. package/dist/decorators/lifecycle.d.ts +37 -0
  23. package/dist/decorators/lifecycle.d.ts.map +1 -0
  24. package/dist/decorators/lifecycle.js +3 -0
  25. package/dist/decorators/lifecycle.js.map +1 -0
  26. package/dist/decorators/middleware.d.ts +24 -3
  27. package/dist/decorators/middleware.d.ts.map +1 -1
  28. package/dist/decorators/middleware.js +24 -3
  29. package/dist/decorators/middleware.js.map +1 -1
  30. package/dist/decorators/params.d.ts.map +1 -1
  31. package/dist/decorators/params.js +1 -7
  32. package/dist/decorators/params.js.map +1 -1
  33. package/dist/decorators/profile.d.ts +32 -0
  34. package/dist/decorators/profile.d.ts.map +1 -0
  35. package/dist/decorators/profile.js +61 -0
  36. package/dist/decorators/profile.js.map +1 -0
  37. package/dist/decorators/response.d.ts +0 -1
  38. package/dist/decorators/response.d.ts.map +1 -1
  39. package/dist/decorators/response.js +1 -18
  40. package/dist/decorators/response.js.map +1 -1
  41. package/dist/decorators/routing.d.ts +22 -12
  42. package/dist/decorators/routing.d.ts.map +1 -1
  43. package/dist/decorators/routing.js +49 -3
  44. package/dist/decorators/routing.js.map +1 -1
  45. package/dist/decorators/security.d.ts +42 -0
  46. package/dist/decorators/security.d.ts.map +1 -0
  47. package/dist/decorators/security.js +110 -0
  48. package/dist/decorators/security.js.map +1 -0
  49. package/dist/decorators/transaction.d.ts +29 -0
  50. package/dist/decorators/transaction.d.ts.map +1 -0
  51. package/dist/decorators/transaction.js +91 -0
  52. package/dist/decorators/transaction.js.map +1 -0
  53. package/dist/di/container.d.ts +2 -0
  54. package/dist/di/container.d.ts.map +1 -1
  55. package/dist/di/container.js +30 -0
  56. package/dist/di/container.js.map +1 -1
  57. package/dist/docs/content/decorators.mdx +89 -4
  58. package/dist/docs/content/errors.mdx +29 -13
  59. package/dist/docs/content/providers.mdx +48 -0
  60. package/dist/docs/content/structure.mdx +1 -1
  61. package/dist/docs/content/swagger.mdx +94 -33
  62. package/dist/docs/reference/decorators.mdx +27 -4
  63. package/dist/docs/reference/di.mdx +26 -0
  64. package/dist/docs/reference/swagger.mdx +118 -35
  65. package/dist/docs/reference/validator.mdx +1 -1
  66. package/dist/error/errors.d.ts +24 -12
  67. package/dist/error/errors.d.ts.map +1 -1
  68. package/dist/error/errors.js +36 -12
  69. package/dist/error/errors.js.map +1 -1
  70. package/dist/router/controller-registrar.d.ts.map +1 -1
  71. package/dist/router/controller-registrar.js +75 -7
  72. package/dist/router/controller-registrar.js.map +1 -1
  73. package/dist/server/app.d.ts.map +1 -1
  74. package/dist/server/app.js +2 -0
  75. package/dist/server/app.js.map +1 -1
  76. package/dist/testing/client.d.ts +1 -1
  77. package/dist/testing/client.d.ts.map +1 -1
  78. package/dist/testing/client.js +30 -15
  79. package/dist/testing/client.js.map +1 -1
  80. package/dist/testing/index.d.ts.map +1 -1
  81. package/dist/testing/index.js +10 -0
  82. package/dist/testing/index.js.map +1 -1
  83. package/package.json +5 -1
@@ -5,7 +5,7 @@ nextTitle: "Swagger API Reference"
5
5
 
6
6
  # Swagger & OpenAPI 3.1 Documentation
7
7
 
8
- ExisJS comes with a native, zero-bloat **Swagger & OpenAPI 3.1** documentation engine. It automatically discovers all registered routes, introspects **Tex** and **Zod** validation schemas, and serves interactive API documentation at `/docs`.
8
+ ExisJS comes with a native, built-in **Swagger & OpenAPI 3.1** documentation engine. It automatically discovers all registered routes, introspects **Tex** and **Zod** validation schemas, and serves interactive API documentation at `/docs`.
9
9
 
10
10
  ---
11
11
 
@@ -44,6 +44,8 @@ The industry-standard interactive Swagger explorer featuring **Try it out**, liv
44
44
  A modern, high-performance, dark-mode-first API reference documentation viewer with integrated code snippets and fast search.
45
45
 
46
46
  ```typescript filename="exis.config.ts"
47
+ import { defineConfig } from 'exisjs/config'
48
+
47
49
  export default defineConfig({
48
50
  docs: {
49
51
  enabled: true,
@@ -56,14 +58,14 @@ export default defineConfig({
56
58
 
57
59
  ## Documenting Functional Routes
58
60
 
59
- When writing functional route controllers (`export default controller({ ... })`), add metadata directly to the route definition:
61
+ In ExisJS folder-based routing, each endpoint folder contains a `route.ts` file that exports a default `controller({ ... })`. You can add OpenAPI metadata directly to each route definition:
60
62
 
61
63
  ```typescript filename="src/http/users/route.ts"
62
64
  import { controller, route } from 'exisjs/router'
63
65
  import { tex, getPaginationSkip } from 'exisjs/validator'
64
66
 
65
67
  export default controller({
66
- listUsers: route.get('/', {
68
+ list: route.get('/', {
67
69
  summary: 'List active users',
68
70
  description: 'Returns a paginated list of all active users in the system.',
69
71
  tags: ['Users'],
@@ -80,11 +82,19 @@ export default controller({
80
82
  },
81
83
  async handle({ query }) {
82
84
  const { skip, limit } = getPaginationSkip(query)
83
- return UserService.find(skip, limit)
85
+ return {
86
+ data: [
87
+ { id: '550e8400-e29b-41d4-a716-446655440000', name: 'Alice' },
88
+ { id: '550e8400-e29b-41d4-a716-446655440001', name: 'Bob' },
89
+ ],
90
+ total: 2,
91
+ skip,
92
+ limit,
93
+ }
84
94
  },
85
95
  }),
86
96
 
87
- createUser: route.post('/', {
97
+ create: route.post('/', {
88
98
  summary: 'Create a new user',
89
99
  tags: ['Users'],
90
100
  body: tex.object({
@@ -96,11 +106,11 @@ export default controller({
96
106
  400: { description: 'Validation error' },
97
107
  },
98
108
  async handle({ body }) {
99
- return UserService.create(body)
109
+ return { id: '550e8400-e29b-41d4-a716-446655440002', ...body }
100
110
  },
101
111
  }),
102
112
 
103
- getUser: route.get('/:id', {
113
+ getById: route.get('/:id', {
104
114
  summary: 'Get user by UUID',
105
115
  tags: ['Users'],
106
116
  params: tex.object({ id: tex.uuid() }),
@@ -109,7 +119,7 @@ export default controller({
109
119
  404: { description: 'User not found' },
110
120
  },
111
121
  async handle({ params }) {
112
- return UserService.findById(params.id)
122
+ return { id: params.id, name: 'Alice' }
113
123
  },
114
124
  }),
115
125
  })
@@ -119,9 +129,9 @@ export default controller({
119
129
 
120
130
  ## Documenting Class-Based Controllers (OOP)
121
131
 
122
- For class-based controllers, use decorators from `exisjs/swagger` or `exisjs/decorators`:
132
+ For class-based controllers in `route.ts`, use OpenAPI decorators from `exisjs/swagger` alongside standard routing decorators:
123
133
 
124
- ```typescript filename="src/http/products/controller.ts"
134
+ ```typescript filename="src/http/products/route.ts"
125
135
  import { Controller, Get, Post, Body, Param, Query } from 'exisjs/decorators'
126
136
  import {
127
137
  ApiTags,
@@ -130,32 +140,42 @@ import {
130
140
  ApiBearerAuth,
131
141
  ApiExclude,
132
142
  } from 'exisjs/swagger'
133
- import { tex } from 'exisjs/validator'
134
143
 
135
144
  @ApiTags('Products')
136
145
  @ApiBearerAuth()
137
- @Controller('/products')
138
- export class ProductController {
146
+ @Controller()
147
+ export default class ProductsController {
139
148
  @Get('/')
140
149
  @ApiOperation({
141
150
  summary: 'List products',
142
151
  description: 'Fetches paginated list of catalog products',
143
152
  })
144
153
  @ApiResponse({ status: 200, description: 'List of products' })
145
- async list(@Query('page') page: number) {
146
- return ProductService.findAll(page)
154
+ async list(@Query('page') page: number = 1) {
155
+ return [
156
+ { id: 1, name: 'Mechanical Keyboard', price: 99 },
157
+ { id: 2, name: 'Ergonomic Mouse', price: 49 },
158
+ ]
147
159
  }
148
160
 
149
161
  @Post('/')
150
162
  @ApiOperation({ summary: 'Create new product' })
151
163
  @ApiResponse({ status: 201, description: 'Product created' })
152
- async create(@Body() dto: any) {
153
- return ProductService.create(dto)
164
+ async create(@Body() dto: { name: string; price: number }) {
165
+ return { id: 3, ...dto }
166
+ }
167
+
168
+ @Get('/:id')
169
+ @ApiOperation({ summary: 'Get product by ID' })
170
+ @ApiResponse({ status: 200, description: 'Product found' })
171
+ @ApiResponse({ status: 404, description: 'Product not found' })
172
+ async getById(@Param('id') id: string) {
173
+ return { id, name: 'Mechanical Keyboard', price: 99 }
154
174
  }
155
175
 
156
- @Get('/internal-stats')
176
+ @Get('/internal/metrics')
157
177
  @ApiExclude() // Excluded from Swagger documentation
158
- async stats() {
178
+ async metrics() {
159
179
  return { uptime: process.uptime() }
160
180
  }
161
181
  }
@@ -165,7 +185,7 @@ export class ProductController {
165
185
 
166
186
  ## Cascading Boundary Metadata
167
187
 
168
- Cascading boundaries (`boundary.ts`) can assign shared tags, descriptions, or security requirements to all nested routes:
188
+ Cascading boundaries (`boundary.ts`) can assign shared tags, descriptions, or security requirements to all nested routes in the directory:
169
189
 
170
190
  ```typescript filename="src/http/admin/boundary.ts"
171
191
  import { defineBoundary } from 'exisjs/router'
@@ -183,6 +203,8 @@ export const config = defineBoundary({
183
203
  Define reusable security schemes (such as Bearer JWT, API Keys, or OAuth2) in `exis.config.ts`:
184
204
 
185
205
  ```typescript filename="exis.config.ts"
206
+ import { defineConfig } from 'exisjs/config'
207
+
186
208
  export default defineConfig({
187
209
  docs: {
188
210
  enabled: true,
@@ -208,21 +230,60 @@ export default defineConfig({
208
230
 
209
231
  ---
210
232
 
211
- ## Programmatic / Custom Setup
233
+ ## Advanced Configuration (`exis.config.ts`)
212
234
 
213
- You can also mount Swagger programmatically on any `App` instance:
235
+ For advanced customization, such as modifying the generated OpenAPI spec before serving or injecting custom styling:
214
236
 
215
- ```typescript filename="src/index.ts"
216
- import { App } from 'exisjs'
217
- import { swagger } from 'exisjs/swagger'
237
+ ```typescript filename="exis.config.ts"
238
+ import { defineConfig } from 'exisjs/config'
218
239
 
219
- const app = new App()
240
+ export default defineConfig({
241
+ docs: {
242
+ enabled: true,
243
+ title: 'Enterprise API Gateway',
244
+ version: '2.0.0',
245
+ description: 'Internal and external microservices API reference',
246
+ ui: 'scalar', // 'swagger-ui' | 'scalar'
247
+ servers: [
248
+ { url: 'https://api.example.com', description: 'Production Server' },
249
+ { url: 'http://localhost:3000', description: 'Local Development' },
250
+ ],
251
+ transformSpec(spec) {
252
+ // Programmatically mutate or enrich OpenAPI specification
253
+ spec.info.contact = { name: 'API Support', email: 'support@example.com' }
254
+ return spec
255
+ },
256
+ },
257
+ })
258
+ ```
259
+
260
+ ---
261
+
262
+ ## Server Lifecycle Hook (`server.ts`)
263
+
264
+ You can also interact with the Swagger subsystem or application instance during server boot in `src/http/server.ts`:
265
+
266
+ ### Functional Paradigm
267
+ ```typescript filename="src/http/server.ts"
268
+ import { exis } from 'exisjs'
269
+
270
+ export default exis({
271
+ onStart(app) {
272
+ console.log('[API] Swagger documentation ready at /docs')
273
+ },
274
+ })
275
+ ```
276
+
277
+ ### Class-Based (OOP) Paradigm
278
+ ```typescript filename="src/http/server.ts"
279
+ import { Server } from 'exisjs/decorators'
280
+ import type { App } from 'exisjs'
220
281
 
221
- // Mount custom swagger documentation
222
- swagger({
223
- title: 'Custom API',
224
- version: '2.0.0',
225
- path: '/api/docs',
226
- specPath: '/api/docs/spec.json',
227
- })(app)
282
+ @Server()
283
+ export default class RootServer {
284
+ async onStart(app: App) {
285
+ console.log('[API] Swagger documentation ready at /docs')
286
+ }
287
+ }
228
288
  ```
289
+
@@ -86,6 +86,10 @@ Parameter decorators automatically extract data from the incoming request and in
86
86
  ## 3. Middleware & Pipeline Decorators
87
87
 
88
88
  - `@Use(...middlewaresOrGuardsOrInterceptorsOrFilters)`: Unified decorator that binds middlewares, guards (`canActivate`), interceptors (`intercept`), or exception filters (`catch`) to a controller class or method.
89
+ - `@UseGuards(...guards)`: Dedicated decorator to apply guards to a class or method.
90
+ - `@UseInterceptors(...interceptors)`: Dedicated decorator to apply interceptors to a class or method.
91
+ - `@UseFilters(...filters)`: Dedicated decorator to apply exception filters to a class or method.
92
+ - `@Catch(...exceptions)`: Class decorator for `ExceptionFilter` implementations declaring handled exception types.
89
93
  - `@Upload(fieldName, options?)` / `@UseUpload(...)`: Configures multipart file upload handling for a method, populating `@UploadedFile()` or `@UploadedFiles()`.
90
94
  - `@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
95
  - `@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 +102,32 @@ Parameter decorators automatically extract data from the incoming request and in
98
102
 
99
103
  ---
100
104
 
101
- ## 4. Response & Metadata Modifiers
105
+ ## 4. Security & Access Control Decorators
106
+
107
+ - `@Permissions(...permissions: string[])`: Sets required permission keys for a controller or route method (automatically bypassed if `req.user.isSuperAdmin` is true).
108
+ - `@Roles(...roles: string[])`: Sets required role names for a controller or route method.
109
+ - `@Public()` / `@IsPublic()` / `@AllowAnonymous()`: Marks a controller or route as public, instructing global authentication guards to bypass credential checks.
110
+
111
+ ---
112
+
113
+ ## 5. Service Enhancements: Caching, Transactions & Profiling
114
+
115
+ - `@Cacheable({ ttl?: number, key?: string | ((...args) => string) })`: Caches method return values in ExisJS's off-heap native memory LRU cache.
116
+ - `@CacheEvict({ key: string | ((...args) => string) })`: Evicts cached entries matching the key when the method completes.
117
+ - `@Transactional(options?)`: Automatically wraps service operations in an atomic database transaction.
118
+ - `@Profile({ thresholdMs?: number, warnSlow?: boolean, log?: Function })`: Profiles method execution time with sub-millisecond precision.
119
+ - `OnInit` (`onInit()`): Invoked when the application or service boots up.
120
+ - `OnDestroy` (`onDestroy()`): Invoked when the application gracefully terminates.
121
+ - `OnModuleInit`, `OnModuleDestroy`, `OnApplicationBootstrap`: Modular lifecycle hooks.
122
+
123
+ ---
124
+
125
+ ## 6. Response & Metadata Modifiers
102
126
 
103
127
  - `@HttpCode(code: number)`: Enforces a custom HTTP status code on success.
104
128
  - `@Header(name: string, value: string)`: Appends an HTTP header to the response.
105
129
  - `@Returns(schema)`: Attaches response schema for OpenAPI documentation.
106
130
  - `@Redirect(url: string, statusCode?: number)`: Redirects request to target URL.
107
- - `@Permissions(...permissions: string[])`: Sets required permissions on the route.
108
131
  - `@Hosts(...hosts: string[])`: Restricts routes to specific Host headers.
109
132
  - `@SetMetadata(key, value)`: Attaches arbitrary custom metadata to a class or handler method, retrievable via `Reflector`.
110
133
  - `Reflector`: Utility class to inspect metadata attached by `@SetMetadata`:
@@ -114,7 +137,7 @@ Parameter decorators automatically extract data from the incoming request and in
114
137
 
115
138
  ---
116
139
 
117
- ## 5. Dependency Injection & Modules
140
+ ## 7. Dependency Injection & Modules
118
141
 
119
142
  - `@Module({ imports?, controllers?, providers?, exports? })`: OOP class decorator equivalent to `defineModule()` for structuring application modules.
120
143
  - `@Injectable(options?)`: Marks a service class as an IoC provider managed by the container. Supports `{ scope: 'singleton' | 'request' | 'transient' }`.
@@ -125,7 +148,7 @@ Parameter decorators automatically extract data from the incoming request and in
125
148
 
126
149
  ---
127
150
 
128
- ## 6. Cron & Task Scheduling
151
+ ## 8. Cron & Task Scheduling
129
152
 
130
153
  - `@Cron(schedule: string, options?: CronJobOptions)`: Schedules a class method as a recurring cron job on any `@Injectable()` service.
131
154
  - `@Interval(ms: number, options?: IntervalOptions)`: Schedules a class method to execute repeatedly every `ms` milliseconds.
@@ -145,3 +145,29 @@ export default class UserController {
145
145
  }
146
146
  ```
147
147
 
148
+ ---
149
+
150
+ ## Lifecycle Hooks (`OnInit`, `OnDestroy`)
151
+
152
+ Providers can implement lifecycle interfaces. When the application container initializes or gracefully closes, ExisJS automatically triggers these hooks:
153
+
154
+ - **`OnInit` (`onInit()` / `onModuleInit()`)**: Executed when the container initializes during `app.create()` or `app.listen()`.
155
+ - **`OnDestroy` (`onDestroy()` / `onModuleDestroy()`)**: Executed when the application receives a termination signal or `app.close()` is called.
156
+ - **`OnApplicationBootstrap` (`onApplicationBootstrap()`)**: Executed after all modules are initialized and before the server begins listening for incoming requests.
157
+
158
+ ```typescript filename="src/http/database/service.ts"
159
+ import { Injectable, type OnInit, type OnDestroy } from 'exisjs/decorators'
160
+
161
+ @Injectable()
162
+ export class DatabaseService implements OnInit, OnDestroy {
163
+ async onInit(): Promise<void> {
164
+ await this.pool.connect()
165
+ }
166
+
167
+ async onDestroy(): Promise<void> {
168
+ await this.pool.end()
169
+ }
170
+ }
171
+ ```
172
+
173
+
@@ -53,91 +53,174 @@ import {
53
53
  Assigns one or more tags to a controller class or method handler.
54
54
  *(Alias: `@Tags()`)*
55
55
 
56
- ```typescript
56
+ ```typescript filename="src/http/users/route.ts"
57
+ import { Controller, Get } from 'exisjs/decorators'
58
+ import { ApiTags } from 'exisjs/swagger'
59
+
57
60
  @ApiTags('Users', 'Authentication')
58
- @Controller('/users')
59
- export class UserController {}
61
+ @Controller()
62
+ export default class UserController {
63
+ @Get('/')
64
+ findAll() {
65
+ return []
66
+ }
67
+ }
60
68
  ```
61
69
 
62
70
  ### `@ApiOperation(options)`
63
71
  Attaches summary, description, operationId, and deprecated status to a route method.
64
72
  *(Alias: `@Operation()`)*
65
73
 
66
- ```typescript
67
- @Get('/:id')
68
- @ApiOperation({
69
- summary: 'Get user by UUID',
70
- description: 'Fetches user details from database',
71
- deprecated: false,
72
- })
73
- getUser() {}
74
+ ```typescript filename="src/http/users/route.ts"
75
+ import { Controller, Get, Param } from 'exisjs/decorators'
76
+ import { ApiOperation } from 'exisjs/swagger'
77
+
78
+ @Controller()
79
+ export default class UserController {
80
+ @Get('/:id')
81
+ @ApiOperation({
82
+ summary: 'Get user by UUID',
83
+ description: 'Fetches user details from database',
84
+ deprecated: false,
85
+ })
86
+ getUser(@Param('id') id: string) {
87
+ return { id, name: 'Alice' }
88
+ }
89
+ }
74
90
  ```
75
91
 
76
92
  ### `@ApiResponse(options)`
77
93
  Defines an expected HTTP response status code and optional schema.
78
94
 
79
- ```typescript
80
- @ApiResponse({
81
- status: 200,
82
- description: 'User details found',
83
- schema: UserResponseSchema,
95
+ ```typescript filename="src/http/users/route.ts"
96
+ import { Controller, Get, Param } from 'exisjs/decorators'
97
+ import { ApiOperation, ApiResponse } from 'exisjs/swagger'
98
+ import { tex } from 'exisjs/validator'
99
+
100
+ const UserSchema = tex.object({
101
+ id: tex.uuid(),
102
+ name: tex.string(),
84
103
  })
85
- @ApiResponse({ status: 404, description: 'User not found' })
86
- getUser() {}
104
+
105
+ @Controller()
106
+ export default class UserController {
107
+ @Get('/:id')
108
+ @ApiResponse({
109
+ status: 200,
110
+ description: 'User details found',
111
+ schema: UserSchema,
112
+ })
113
+ @ApiResponse({ status: 404, description: 'User not found' })
114
+ getUser(@Param('id') id: string) {
115
+ return { id, name: 'Alice' }
116
+ }
117
+ }
87
118
  ```
88
119
 
89
120
  ### `@ApiBearerAuth(name?: string)`
90
121
  Requires Bearer JWT authentication for this endpoint or controller.
91
122
  *(Alias: `@BearerAuth()`)*
92
123
 
93
- ```typescript
124
+ ```typescript filename="src/http/users/route.ts"
125
+ import { Controller, Get } from 'exisjs/decorators'
126
+ import { ApiBearerAuth } from 'exisjs/swagger'
127
+
94
128
  @ApiBearerAuth()
95
- @Get('/me')
96
- getMe() {}
129
+ @Controller()
130
+ export default class UserController {
131
+ @Get('/me')
132
+ getMe() {
133
+ return { id: 'user-123', role: 'admin' }
134
+ }
135
+ }
97
136
  ```
98
137
 
99
138
  ### `@ApiSecurity(name: string, scopes?: string[])`
100
139
  Requires custom security scheme authentication for this endpoint or controller.
101
140
 
102
- ```typescript
103
- @ApiSecurity('apiKeyAuth')
104
- @Get('/metrics')
105
- getMetrics() {}
141
+ ```typescript filename="src/http/metrics/route.ts"
142
+ import { Controller, Get } from 'exisjs/decorators'
143
+ import { ApiSecurity } from 'exisjs/swagger'
144
+
145
+ @Controller()
146
+ export default class MetricsController {
147
+ @Get('/')
148
+ @ApiSecurity('apiKeyAuth')
149
+ getMetrics() {
150
+ return { uptime: process.uptime() }
151
+ }
152
+ }
106
153
  ```
107
154
 
108
155
  ### `@ApiExclude()`
109
156
  Excludes an endpoint or controller class from OpenAPI and Swagger generation.
110
157
 
111
- ```typescript
112
- @Get('/internal')
113
- @ApiExclude()
114
- getInternal() {}
158
+ ```typescript filename="src/http/internal/route.ts"
159
+ import { Controller, Get } from 'exisjs/decorators'
160
+ import { ApiExclude } from 'exisjs/swagger'
161
+
162
+ @Controller()
163
+ export default class InternalController {
164
+ @Get('/health')
165
+ @ApiExclude()
166
+ getHealth() {
167
+ return { status: 'healthy' }
168
+ }
169
+ }
115
170
  ```
116
171
 
117
172
  ### `@ApiProperty(options?: { description?: string; example?: any; required?: boolean })`
118
173
  Annotates a class property with metadata for OpenAPI schema models.
119
174
 
175
+ ```typescript filename="src/http/users/dto.ts"
176
+ import { ApiProperty } from 'exisjs/swagger'
177
+
178
+ export class CreateUserDto {
179
+ @ApiProperty({ description: 'Full name of the user', example: 'Alice Johnson', required: true })
180
+ name!: string
181
+
182
+ @ApiProperty({ description: 'Work email address', example: 'alice@example.com', required: true })
183
+ email!: string
184
+ }
185
+ ```
186
+
120
187
  ---
121
188
 
122
189
  ## Functions
123
190
 
124
191
  ### `swagger(config?: SwaggerConfig)`
125
- Convenience helper to mount Swagger/OpenAPI documentation on an ExisJS App instance.
192
+ Convenience helper to mount Swagger/OpenAPI documentation on an ExisJS App instance (typically inside `onStart` or custom server setups).
126
193
 
127
- ```typescript
128
- import { App } from 'exisjs'
194
+ ```typescript filename="src/http/server.ts"
195
+ import { exis } from 'exisjs'
129
196
  import { swagger } from 'exisjs/swagger'
130
197
 
131
- const app = new App()
132
- swagger({ title: 'My API', path: '/docs' })(app)
198
+ export default exis({
199
+ onStart(app) {
200
+ swagger({ title: 'My API', path: '/docs' })(app)
201
+ },
202
+ })
133
203
  ```
134
204
 
135
205
  ### `generateOpenApiSpec(routesOrApp, config)`
136
- Pure generator function that introspects routes, parameters, Tex/Zod schemas, and returns an `OpenApiSpec` object.
206
+ Pure generator function that introspects routes, parameters, and `TexEngine`/Zod schemas and returns a complete `OpenApiSpec` object.
137
207
 
138
208
  ```typescript
209
+ import { generateOpenApiSpec } from 'exisjs/swagger'
210
+
139
211
  const spec = generateOpenApiSpec(app, { title: 'My API', version: '1.0.0' })
140
212
  ```
141
213
 
142
214
  ### `renderDocumentationHtml(config)`
143
215
  Renders standalone HTML markup for Swagger UI or Scalar API reference.
216
+
217
+ ```typescript
218
+ import { renderDocumentationHtml } from 'exisjs/swagger'
219
+
220
+ const html = renderDocumentationHtml({
221
+ title: 'My API',
222
+ specPath: '/docs/json',
223
+ ui: 'scalar',
224
+ })
225
+ ```
226
+
@@ -234,7 +234,7 @@ type Profile = ResolveSchema<typeof ProfileSchema>
234
234
 
235
235
  ## OpenAPI Generation
236
236
 
237
- The `TexEngine` automatically converts validation schemas into strictly compliant OpenAPI/Swagger JSON schemas when attached to routes. This powers zero-config Swagger documentation once `@exisjs/swagger` is added.
237
+ The `TexEngine` automatically converts validation schemas into strictly compliant OpenAPI/Swagger JSON schemas when attached to routes. This powers zero-config Swagger documentation in `exisjs/swagger`.
238
238
 
239
239
  ---
240
240
 
@@ -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 const HttpException: typeof HttpError;
55
- export declare const BadRequestException: typeof BadRequestError;
56
- export declare const UnauthorizedException: typeof UnauthorizedError;
57
- export declare const ForbiddenException: typeof ForbiddenError;
58
- export declare const NotFoundException: typeof NotFoundError;
59
- export declare const ConflictException: typeof ConflictError;
60
- export declare const PayloadTooLargeException: typeof PayloadTooLargeError;
61
- export declare const UnprocessableException: typeof UnprocessableError;
62
- export declare const RateLimitException: typeof RateLimitError;
63
- export declare const InternalException: typeof InternalError;
64
- export declare const ServiceUnavailableException: typeof ServiceUnavailableError;
65
- export declare const GatewayTimeoutException: typeof GatewayTimeoutError;
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":"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"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/error/errors.ts"],"names":[],"mappings":"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"}
@@ -145,18 +145,42 @@ class GatewayTimeoutError extends HttpError {
145
145
  }
146
146
  exports.GatewayTimeoutError = GatewayTimeoutError;
147
147
  // ─── Exception Aliases ────────────────────────────────────────────────────────
148
- exports.HttpException = HttpError;
149
- exports.BadRequestException = BadRequestError;
150
- exports.UnauthorizedException = UnauthorizedError;
151
- exports.ForbiddenException = ForbiddenError;
152
- exports.NotFoundException = NotFoundError;
153
- exports.ConflictException = ConflictError;
154
- exports.PayloadTooLargeException = PayloadTooLargeError;
155
- exports.UnprocessableException = UnprocessableError;
156
- exports.RateLimitException = RateLimitError;
157
- exports.InternalException = InternalError;
158
- exports.ServiceUnavailableException = ServiceUnavailableError;
159
- exports.GatewayTimeoutException = GatewayTimeoutError;
148
+ class HttpException extends HttpError {
149
+ }
150
+ exports.HttpException = HttpException;
151
+ class BadRequestException extends BadRequestError {
152
+ }
153
+ exports.BadRequestException = BadRequestException;
154
+ class UnauthorizedException extends UnauthorizedError {
155
+ }
156
+ exports.UnauthorizedException = UnauthorizedException;
157
+ class ForbiddenException extends ForbiddenError {
158
+ }
159
+ exports.ForbiddenException = ForbiddenException;
160
+ class NotFoundException extends NotFoundError {
161
+ }
162
+ exports.NotFoundException = NotFoundException;
163
+ class ConflictException extends ConflictError {
164
+ }
165
+ exports.ConflictException = ConflictException;
166
+ class PayloadTooLargeException extends PayloadTooLargeError {
167
+ }
168
+ exports.PayloadTooLargeException = PayloadTooLargeException;
169
+ class UnprocessableException extends UnprocessableError {
170
+ }
171
+ exports.UnprocessableException = UnprocessableException;
172
+ class RateLimitException extends RateLimitError {
173
+ }
174
+ exports.RateLimitException = RateLimitException;
175
+ class InternalException extends InternalError {
176
+ }
177
+ exports.InternalException = InternalException;
178
+ class ServiceUnavailableException extends ServiceUnavailableError {
179
+ }
180
+ exports.ServiceUnavailableException = ServiceUnavailableException;
181
+ class GatewayTimeoutException extends GatewayTimeoutError {
182
+ }
183
+ exports.GatewayTimeoutException = GatewayTimeoutException;
160
184
  // ─── Global Error Handler ─────────────────────────────────────────────────────
161
185
  function escapeHtml(str) {
162
186
  if (typeof str !== 'string')