exisjs 0.7.6 → 0.7.8

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 (211) hide show
  1. package/dist/cli/commands/build.d.ts.map +1 -1
  2. package/dist/cli/commands/build.js +12 -0
  3. package/dist/cli/commands/build.js.map +1 -1
  4. package/dist/cli/commands/clean.d.ts +5 -0
  5. package/dist/cli/commands/clean.d.ts.map +1 -0
  6. package/dist/cli/commands/clean.js +30 -0
  7. package/dist/cli/commands/clean.js.map +1 -0
  8. package/dist/cli/commands/dev.d.ts.map +1 -1
  9. package/dist/cli/commands/dev.js +15 -1
  10. package/dist/cli/commands/dev.js.map +1 -1
  11. package/dist/cli/commands/generate.d.ts +6 -1
  12. package/dist/cli/commands/generate.d.ts.map +1 -1
  13. package/dist/cli/commands/generate.js +57 -2
  14. package/dist/cli/commands/generate.js.map +1 -1
  15. package/dist/cli/index.js +16 -2
  16. package/dist/cli/index.js.map +1 -1
  17. package/dist/cli/manifest.d.ts +1 -0
  18. package/dist/cli/manifest.d.ts.map +1 -1
  19. package/dist/cli/manifest.js +161 -23
  20. package/dist/cli/manifest.js.map +1 -1
  21. package/dist/config/config.d.ts.map +1 -1
  22. package/dist/config/config.js +9 -0
  23. package/dist/config/config.js.map +1 -1
  24. package/dist/config/types.d.ts +4 -0
  25. package/dist/config/types.d.ts.map +1 -1
  26. package/dist/cron/expressions.d.ts +45 -0
  27. package/dist/cron/expressions.d.ts.map +1 -0
  28. package/dist/cron/expressions.js +87 -0
  29. package/dist/cron/expressions.js.map +1 -0
  30. package/dist/cron/index.d.ts +6 -0
  31. package/dist/cron/index.d.ts.map +1 -0
  32. package/dist/cron/index.js +22 -0
  33. package/dist/cron/index.js.map +1 -0
  34. package/dist/cron/job.d.ts +50 -0
  35. package/dist/cron/job.d.ts.map +1 -0
  36. package/dist/cron/job.js +247 -0
  37. package/dist/cron/job.js.map +1 -0
  38. package/dist/cron/manager.d.ts +105 -0
  39. package/dist/cron/manager.d.ts.map +1 -0
  40. package/dist/cron/manager.js +245 -0
  41. package/dist/cron/manager.js.map +1 -0
  42. package/dist/cron/parser.d.ts +22 -0
  43. package/dist/cron/parser.d.ts.map +1 -0
  44. package/dist/cron/parser.js +269 -0
  45. package/dist/cron/parser.js.map +1 -0
  46. package/dist/cron/types.d.ts +76 -0
  47. package/dist/cron/types.d.ts.map +1 -0
  48. package/dist/cron/types.js +3 -0
  49. package/dist/cron/types.js.map +1 -0
  50. package/dist/decorators/constants.d.ts +2 -0
  51. package/dist/decorators/constants.d.ts.map +1 -1
  52. package/dist/decorators/constants.js +3 -1
  53. package/dist/decorators/constants.js.map +1 -1
  54. package/dist/decorators/core.d.ts +29 -11
  55. package/dist/decorators/core.d.ts.map +1 -1
  56. package/dist/decorators/core.js +49 -11
  57. package/dist/decorators/core.js.map +1 -1
  58. package/dist/decorators/cron.d.ts +57 -0
  59. package/dist/decorators/cron.d.ts.map +1 -0
  60. package/dist/decorators/cron.js +143 -0
  61. package/dist/decorators/cron.js.map +1 -0
  62. package/dist/decorators/index.d.ts +2 -0
  63. package/dist/decorators/index.d.ts.map +1 -1
  64. package/dist/decorators/index.js +2 -0
  65. package/dist/decorators/index.js.map +1 -1
  66. package/dist/decorators/params.d.ts +97 -2
  67. package/dist/decorators/params.d.ts.map +1 -1
  68. package/dist/decorators/params.js +94 -2
  69. package/dist/decorators/params.js.map +1 -1
  70. package/dist/decorators/routing.d.ts +81 -16
  71. package/dist/decorators/routing.d.ts.map +1 -1
  72. package/dist/decorators/routing.js +81 -16
  73. package/dist/decorators/routing.js.map +1 -1
  74. package/dist/docs/content/cron.mdx +196 -0
  75. package/dist/docs/content/errors.mdx +58 -9
  76. package/dist/docs/content/structure.mdx +1 -1
  77. package/dist/docs/content/swagger.mdx +289 -0
  78. package/dist/docs/reference/cron.mdx +300 -0
  79. package/dist/docs/reference/decorators.mdx +8 -0
  80. package/dist/docs/reference/error.mdx +44 -0
  81. package/dist/docs/reference/swagger.mdx +226 -0
  82. package/dist/docs/reference/validator.mdx +1 -1
  83. package/dist/router/boundary.d.ts +24 -2
  84. package/dist/router/boundary.d.ts.map +1 -1
  85. package/dist/router/boundary.js +24 -2
  86. package/dist/router/boundary.js.map +1 -1
  87. package/dist/router/controller-registrar.d.ts.map +1 -1
  88. package/dist/router/controller-registrar.js +52 -3
  89. package/dist/router/controller-registrar.js.map +1 -1
  90. package/dist/router/route-builder.d.ts +196 -33
  91. package/dist/router/route-builder.d.ts.map +1 -1
  92. package/dist/router/route-builder.js +171 -11
  93. package/dist/router/route-builder.js.map +1 -1
  94. package/dist/router/route-scanner.d.ts +6 -0
  95. package/dist/router/route-scanner.d.ts.map +1 -1
  96. package/dist/router/route-scanner.js +69 -812
  97. package/dist/router/route-scanner.js.map +1 -1
  98. package/dist/router/router.d.ts.map +1 -1
  99. package/dist/router/router.js +27 -0
  100. package/dist/router/router.js.map +1 -1
  101. package/dist/router/scanner/boundary-loader.d.ts +18 -0
  102. package/dist/router/scanner/boundary-loader.d.ts.map +1 -0
  103. package/dist/router/scanner/boundary-loader.js +539 -0
  104. package/dist/router/scanner/boundary-loader.js.map +1 -0
  105. package/dist/router/scanner/cron-mounter.d.ts +15 -0
  106. package/dist/router/scanner/cron-mounter.d.ts.map +1 -0
  107. package/dist/router/scanner/cron-mounter.js +216 -0
  108. package/dist/router/scanner/cron-mounter.js.map +1 -0
  109. package/dist/router/scanner/error-mounter.d.ts +14 -0
  110. package/dist/router/scanner/error-mounter.d.ts.map +1 -0
  111. package/dist/router/scanner/error-mounter.js +200 -0
  112. package/dist/router/scanner/error-mounter.js.map +1 -0
  113. package/dist/router/scanner/fs-scanner.d.ts +25 -0
  114. package/dist/router/scanner/fs-scanner.d.ts.map +1 -0
  115. package/dist/router/scanner/fs-scanner.js +115 -0
  116. package/dist/router/scanner/fs-scanner.js.map +1 -0
  117. package/dist/router/scanner/functional-compiler.d.ts +9 -0
  118. package/dist/router/scanner/functional-compiler.d.ts.map +1 -0
  119. package/dist/router/scanner/functional-compiler.js +233 -0
  120. package/dist/router/scanner/functional-compiler.js.map +1 -0
  121. package/dist/router/scanner/index.d.ts +9 -0
  122. package/dist/router/scanner/index.d.ts.map +1 -0
  123. package/dist/router/scanner/index.js +19 -0
  124. package/dist/router/scanner/index.js.map +1 -0
  125. package/dist/router/types.d.ts +15 -0
  126. package/dist/router/types.d.ts.map +1 -1
  127. package/dist/server/app.d.ts +2 -0
  128. package/dist/server/app.d.ts.map +1 -1
  129. package/dist/server/app.js +3 -0
  130. package/dist/server/app.js.map +1 -1
  131. package/dist/server/helpers/body-parser.d.ts +18 -0
  132. package/dist/server/helpers/body-parser.d.ts.map +1 -0
  133. package/dist/server/helpers/body-parser.js +246 -0
  134. package/dist/server/helpers/body-parser.js.map +1 -0
  135. package/dist/server/helpers/cookie.d.ts +4 -0
  136. package/dist/server/helpers/cookie.d.ts.map +1 -0
  137. package/dist/server/helpers/cookie.js +61 -0
  138. package/dist/server/helpers/cookie.js.map +1 -0
  139. package/dist/server/helpers/etag.d.ts +3 -0
  140. package/dist/server/helpers/etag.d.ts.map +1 -0
  141. package/dist/server/helpers/etag.js +25 -0
  142. package/dist/server/helpers/etag.js.map +1 -0
  143. package/dist/server/helpers/index.d.ts +6 -0
  144. package/dist/server/helpers/index.d.ts.map +1 -0
  145. package/dist/server/helpers/index.js +21 -0
  146. package/dist/server/helpers/index.js.map +1 -0
  147. package/dist/server/helpers/ip.d.ts +9 -0
  148. package/dist/server/helpers/ip.d.ts.map +1 -0
  149. package/dist/server/helpers/ip.js +62 -0
  150. package/dist/server/helpers/ip.js.map +1 -0
  151. package/dist/server/helpers/stream.d.ts +4 -0
  152. package/dist/server/helpers/stream.d.ts.map +1 -0
  153. package/dist/server/helpers/stream.js +193 -0
  154. package/dist/server/helpers/stream.js.map +1 -0
  155. package/dist/server/request.d.ts +1 -139
  156. package/dist/server/request.d.ts.map +1 -1
  157. package/dist/server/request.js +19 -433
  158. package/dist/server/request.js.map +1 -1
  159. package/dist/server/response.d.ts +105 -158
  160. package/dist/server/response.d.ts.map +1 -1
  161. package/dist/server/response.js +121 -423
  162. package/dist/server/response.js.map +1 -1
  163. package/dist/swagger/decorators.d.ts +81 -0
  164. package/dist/swagger/decorators.d.ts.map +1 -0
  165. package/dist/swagger/decorators.js +172 -0
  166. package/dist/swagger/decorators.js.map +1 -0
  167. package/dist/swagger/generator.d.ts +19 -0
  168. package/dist/swagger/generator.d.ts.map +1 -0
  169. package/dist/swagger/generator.js +344 -0
  170. package/dist/swagger/generator.js.map +1 -0
  171. package/dist/swagger/index.d.ts +31 -0
  172. package/dist/swagger/index.d.ts.map +1 -0
  173. package/dist/swagger/index.js +53 -0
  174. package/dist/swagger/index.js.map +1 -0
  175. package/dist/swagger/mounter.d.ts +7 -0
  176. package/dist/swagger/mounter.d.ts.map +1 -0
  177. package/dist/swagger/mounter.js +56 -0
  178. package/dist/swagger/mounter.js.map +1 -0
  179. package/dist/swagger/types.d.ts +190 -0
  180. package/dist/swagger/types.d.ts.map +1 -0
  181. package/dist/swagger/types.js +3 -0
  182. package/dist/swagger/types.js.map +1 -0
  183. package/dist/swagger/ui.d.ts +6 -0
  184. package/dist/swagger/ui.d.ts.map +1 -0
  185. package/dist/swagger/ui.js +118 -0
  186. package/dist/swagger/ui.js.map +1 -0
  187. package/dist/validator/builder.d.ts +223 -0
  188. package/dist/validator/builder.d.ts.map +1 -0
  189. package/dist/validator/builder.js +517 -0
  190. package/dist/validator/builder.js.map +1 -0
  191. package/dist/validator/engine.d.ts +19 -0
  192. package/dist/validator/engine.d.ts.map +1 -0
  193. package/dist/validator/engine.js +568 -0
  194. package/dist/validator/engine.js.map +1 -0
  195. package/dist/validator/error.d.ts +12 -0
  196. package/dist/validator/error.d.ts.map +1 -0
  197. package/dist/validator/error.js +63 -0
  198. package/dist/validator/error.js.map +1 -0
  199. package/dist/validator/pagination.d.ts +40 -0
  200. package/dist/validator/pagination.d.ts.map +1 -0
  201. package/dist/validator/pagination.js +60 -0
  202. package/dist/validator/pagination.js.map +1 -0
  203. package/dist/validator/tex.d.ts +6 -158
  204. package/dist/validator/tex.d.ts.map +1 -1
  205. package/dist/validator/tex.js +14 -971
  206. package/dist/validator/tex.js.map +1 -1
  207. package/dist/validator/types.d.ts +83 -0
  208. package/dist/validator/types.d.ts.map +1 -0
  209. package/dist/validator/types.js +3 -0
  210. package/dist/validator/types.js.map +1 -0
  211. package/package.json +9 -1
@@ -0,0 +1,289 @@
1
+ ---
2
+ nextUrl: "/docs/reference/swagger"
3
+ nextTitle: "Swagger API Reference"
4
+ ---
5
+
6
+ # Swagger & OpenAPI 3.1 Documentation
7
+
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
+
10
+ ---
11
+
12
+ ## Quick Start
13
+
14
+ Enable Swagger documentation in your `exis.config.ts`:
15
+
16
+ ```typescript filename="exis.config.ts"
17
+ import { defineConfig } from 'exisjs/config'
18
+
19
+ export default defineConfig({
20
+ docs: {
21
+ enabled: true, // Mounts UI at /docs and spec at /docs/json
22
+ title: 'Store API Documentation',
23
+ version: '1.0.0',
24
+ description: 'Auto-generated API documentation with native schema validation',
25
+ ui: 'swagger-ui', // 'swagger-ui' | 'scalar'
26
+ }
27
+ })
28
+ ```
29
+
30
+ Once enabled:
31
+ * Visit `http://localhost:3000/docs` to view the interactive documentation UI.
32
+ * Visit `http://localhost:3000/docs/json` (or `/docs/openapi.json`) for the raw OpenAPI 3.1 specification.
33
+
34
+ ---
35
+
36
+ ## Interactive Documentation UIs
37
+
38
+ ExisJS supports two built-in UI renderers:
39
+
40
+ ### 1. Swagger UI (`ui: 'swagger-ui'`, default)
41
+ The industry-standard interactive Swagger explorer featuring **Try it out**, live request execution, and parameter inspection.
42
+
43
+ ### 2. Scalar API Reference (`ui: 'scalar'`)
44
+ A modern, high-performance, dark-mode-first API reference documentation viewer with integrated code snippets and fast search.
45
+
46
+ ```typescript filename="exis.config.ts"
47
+ import { defineConfig } from 'exisjs/config'
48
+
49
+ export default defineConfig({
50
+ docs: {
51
+ enabled: true,
52
+ ui: 'scalar', // Switches UI to modern Scalar
53
+ }
54
+ })
55
+ ```
56
+
57
+ ---
58
+
59
+ ## Documenting Functional Routes
60
+
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:
62
+
63
+ ```typescript filename="src/http/users/route.ts"
64
+ import { controller, route } from 'exisjs/router'
65
+ import { tex, getPaginationSkip } from 'exisjs/validator'
66
+
67
+ export default controller({
68
+ list: route.get('/', {
69
+ summary: 'List active users',
70
+ description: 'Returns a paginated list of all active users in the system.',
71
+ tags: ['Users'],
72
+ query: tex.pagination({ defaultLimit: 25, maxLimit: 100 }),
73
+ responses: {
74
+ 200: {
75
+ description: 'Paginated user list',
76
+ schema: tex.object({
77
+ data: tex.array(tex.object({ id: tex.uuid(), name: tex.string() })),
78
+ total: tex.number(),
79
+ }),
80
+ },
81
+ 400: { description: 'Invalid query parameters' },
82
+ },
83
+ async handle({ query }) {
84
+ const { skip, limit } = getPaginationSkip(query)
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
+ }
94
+ },
95
+ }),
96
+
97
+ create: route.post('/', {
98
+ summary: 'Create a new user',
99
+ tags: ['Users'],
100
+ body: tex.object({
101
+ name: tex.string({ min: 2 }),
102
+ email: tex.email(),
103
+ }),
104
+ responses: {
105
+ 201: { description: 'User successfully created' },
106
+ 400: { description: 'Validation error' },
107
+ },
108
+ async handle({ body }) {
109
+ return { id: '550e8400-e29b-41d4-a716-446655440002', ...body }
110
+ },
111
+ }),
112
+
113
+ getById: route.get('/:id', {
114
+ summary: 'Get user by UUID',
115
+ tags: ['Users'],
116
+ params: tex.object({ id: tex.uuid() }),
117
+ responses: {
118
+ 200: { description: 'User details found' },
119
+ 404: { description: 'User not found' },
120
+ },
121
+ async handle({ params }) {
122
+ return { id: params.id, name: 'Alice' }
123
+ },
124
+ }),
125
+ })
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Documenting Class-Based Controllers (OOP)
131
+
132
+ For class-based controllers in `route.ts`, use OpenAPI decorators from `exisjs/swagger` alongside standard routing decorators:
133
+
134
+ ```typescript filename="src/http/products/route.ts"
135
+ import { Controller, Get, Post, Body, Param, Query } from 'exisjs/decorators'
136
+ import {
137
+ ApiTags,
138
+ ApiOperation,
139
+ ApiResponse,
140
+ ApiBearerAuth,
141
+ ApiExclude,
142
+ } from 'exisjs/swagger'
143
+
144
+ @ApiTags('Products')
145
+ @ApiBearerAuth()
146
+ @Controller()
147
+ export default class ProductsController {
148
+ @Get('/')
149
+ @ApiOperation({
150
+ summary: 'List products',
151
+ description: 'Fetches paginated list of catalog products',
152
+ })
153
+ @ApiResponse({ status: 200, description: 'List of products' })
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
+ ]
159
+ }
160
+
161
+ @Post('/')
162
+ @ApiOperation({ summary: 'Create new product' })
163
+ @ApiResponse({ status: 201, description: 'Product created' })
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 }
174
+ }
175
+
176
+ @Get('/internal/metrics')
177
+ @ApiExclude() // Excluded from Swagger documentation
178
+ async metrics() {
179
+ return { uptime: process.uptime() }
180
+ }
181
+ }
182
+ ```
183
+
184
+ ---
185
+
186
+ ## Cascading Boundary Metadata
187
+
188
+ Cascading boundaries (`boundary.ts`) can assign shared tags, descriptions, or security requirements to all nested routes in the directory:
189
+
190
+ ```typescript filename="src/http/admin/boundary.ts"
191
+ import { defineBoundary } from 'exisjs/router'
192
+
193
+ export const config = defineBoundary({
194
+ tags: ['Admin Portal'],
195
+ security: [{ bearerAuth: [] }],
196
+ })
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Security Schemes & Authentication
202
+
203
+ Define reusable security schemes (such as Bearer JWT, API Keys, or OAuth2) in `exis.config.ts`:
204
+
205
+ ```typescript filename="exis.config.ts"
206
+ import { defineConfig } from 'exisjs/config'
207
+
208
+ export default defineConfig({
209
+ docs: {
210
+ enabled: true,
211
+ securitySchemes: {
212
+ bearerAuth: {
213
+ type: 'http',
214
+ scheme: 'bearer',
215
+ bearerFormat: 'JWT',
216
+ description: 'Enter your JWT token: Bearer <token>',
217
+ },
218
+ apiKeyAuth: {
219
+ type: 'apiKey',
220
+ in: 'header',
221
+ name: 'X-API-KEY',
222
+ },
223
+ },
224
+ security: [
225
+ { bearerAuth: [] },
226
+ ],
227
+ },
228
+ })
229
+ ```
230
+
231
+ ---
232
+
233
+ ## Advanced Configuration (`exis.config.ts`)
234
+
235
+ For advanced customization, such as modifying the generated OpenAPI spec before serving or injecting custom styling:
236
+
237
+ ```typescript filename="exis.config.ts"
238
+ import { defineConfig } from 'exisjs/config'
239
+
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'
281
+
282
+ @Server()
283
+ export default class RootServer {
284
+ async onStart(app: App) {
285
+ console.log('[API] Swagger documentation ready at /docs')
286
+ }
287
+ }
288
+ ```
289
+
@@ -0,0 +1,300 @@
1
+ ---
2
+ title: "Cron & Scheduling"
3
+ description: "API Reference for ExisJS Task Scheduling, Cron Expressions, and Job Management."
4
+ ---
5
+
6
+ # Cron & Scheduling (`exisjs/cron`, `exisjs/decorators`)
7
+
8
+ The `exisjs/cron` module provides task scheduling, cron expression parsing, interval timers, and runtime job management.
9
+
10
+ ---
11
+
12
+ ## 1. Functional Definition Helpers
13
+
14
+ ### `cron(scheduleOrOptions, handler?)`
15
+
16
+ Defines a functional cron job.
17
+
18
+ ```typescript
19
+ import { cron, CronExpression } from 'exisjs/cron'
20
+
21
+ // Signature 1: Options object with inline run handler
22
+ export default cron({
23
+ name: 'sync-users',
24
+ schedule: CronExpression.EVERY_10_MINUTES,
25
+ timezone: 'UTC',
26
+ preventOverlap: true,
27
+ retries: 2,
28
+ retryDelayMs: 1000,
29
+ async run({ app, log }) {
30
+ // ...
31
+ }
32
+ })
33
+
34
+ // Signature 2: Schedule string with handler
35
+ export const purgeJob = cron('0 0 * * *', async ({ log }) => {
36
+ // ...
37
+ })
38
+ ```
39
+
40
+ ### `cron.every(duration, handler, options?)`
41
+
42
+ Shorthand helper accepting human-readable duration strings (e.g., `'10 seconds'`, `'5 minutes'`, `'2 hours'`).
43
+
44
+ ```typescript
45
+ import { cron } from 'exisjs/cron'
46
+
47
+ export const healthCheck = cron.every('30 seconds', async ({ log }) => {
48
+ log.info('Checking external dependencies...')
49
+ }, {
50
+ name: 'ext-health-check'
51
+ })
52
+ ```
53
+
54
+ ### `cron.interval(ms, handler, options?)`
55
+
56
+ Shorthand helper for millisecond recurring intervals.
57
+
58
+ ```typescript
59
+ import { cron } from 'exisjs/cron'
60
+
61
+ export const heartbeat = cron.interval(60_000, async ({ log }) => {
62
+ log.info('Server heartbeat tick')
63
+ })
64
+ ```
65
+
66
+ ### `cron.timeout(ms, handler, options?)`
67
+
68
+ Shorthand helper for one-time delayed startup tasks (executes once after application boot).
69
+
70
+ ```typescript
71
+ import { cron } from 'exisjs/cron'
72
+
73
+ export const cacheWarmup = cron.timeout(5_000, async ({ app, log }) => {
74
+ log.info('Warming up Redis cache after boot')
75
+ })
76
+ ```
77
+
78
+ ---
79
+
80
+ ## 2. OOP Decorators (`exisjs/decorators`)
81
+
82
+ ### `@Cron(schedule, options?)`
83
+
84
+ Method decorator that marks a class method as a scheduled cron job. Can be used on any `@Injectable()` service.
85
+
86
+ | Parameter | Type | Description |
87
+ | :--- | :--- | :--- |
88
+ | `schedule` | `string` | Standard 5 or 6 field cron expression or `CronExpression` constant. |
89
+ | `options` | `CronJobOptions` | Optional job configuration (name, timezone, preventOverlap, retries, etc.). |
90
+
91
+ ```typescript
92
+ import { Injectable, Cron, CronExpression } from 'exisjs/decorators'
93
+
94
+ @Injectable()
95
+ export default class ReportService {
96
+ @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT, {
97
+ name: 'nightly-report',
98
+ timezone: 'UTC',
99
+ preventOverlap: true,
100
+ })
101
+ async generateDailyReport() {
102
+ // ...
103
+ }
104
+ }
105
+ ```
106
+
107
+ ### `@Interval(ms, options?)`
108
+
109
+ Method decorator for recurring millisecond tasks.
110
+
111
+ ```typescript
112
+ import { Injectable, Interval } from 'exisjs/decorators'
113
+
114
+ @Injectable()
115
+ export default class QueueMonitor {
116
+ @Interval(15_000, { name: 'queue-poll' })
117
+ async pollQueue() {
118
+ // ...
119
+ }
120
+ }
121
+ ```
122
+
123
+ ### `@TimeoutTask(ms, options?)` (alias `@ScheduledTimeout`)
124
+
125
+ Method decorator for one-time startup tasks executed after the specified millisecond delay.
126
+
127
+ ```typescript
128
+ import { Injectable, TimeoutTask } from 'exisjs/decorators'
129
+
130
+ @Injectable()
131
+ export default class CacheBootstrap {
132
+ @TimeoutTask(3_000, { name: 'seed-cache' })
133
+ async seedCache() {
134
+ // ...
135
+ }
136
+ }
137
+ ```
138
+
139
+ ---
140
+
141
+ ## 3. Configuration & Options
142
+
143
+ ### `CronJobOptions`
144
+
145
+ Configuration object passed to `cron()` or decorator options:
146
+
147
+ | Property | Type | Default | Description |
148
+ | :--- | :--- | :--- | :--- |
149
+ | `name` | `string` | `auto-generated` | Unique identifier for the job. Used in logs and runtime APIs. |
150
+ | `schedule` | `string` | `undefined` | 5 or 6 field cron expression (e.g. `'0 0 * * *'`). |
151
+ | `interval` | `number` | `undefined` | Millisecond interval for recurring execution. |
152
+ | `timeout` | `number` | `undefined` | Millisecond delay for one-time execution. |
153
+ | `timezone` | `string` | `'UTC'` | Valid IANA timezone string (e.g. `'America/New_York'`, `'Asia/Kolkata'`). |
154
+ | `preventOverlap` | `boolean` | `true` | Skips execution if the previous tick is still running. |
155
+ | `overlapPolicy` | `'skip' \| 'queue'` | `'skip'` | Action taken when execution overlap occurs. |
156
+ | `retries` | `number` | `0` | Number of retry attempts upon failure. |
157
+ | `retryDelayMs` | `number` | `1000` | Milliseconds to wait before executing a retry. |
158
+ | `autoStart` | `boolean` | `true` | Whether the job automatically starts on application bootstrap. |
159
+ | `run` / `handle` | `(ctx: CronContext) => Promise<any> \| any` | `undefined` | The execution callback function. |
160
+ | `onError` | `(err: Error, ctx: CronContext) => void` | `undefined` | Hook called when the job fails after all retries. |
161
+
162
+ ### `CronContext`
163
+
164
+ The context object provided as the first argument to job execution handlers:
165
+
166
+ | Property | Type | Description |
167
+ | :--- | :--- | :--- |
168
+ | `name` | `string` | The unique name of the active job. |
169
+ | `scheduledTime` | `Date` | The theoretical timestamp the job was scheduled to run. |
170
+ | `executionTime` | `Date` | The actual timestamp execution began. |
171
+ | `iteration` | `number` | Total number of executions completed by this job instance. |
172
+ | `app` | `App` | Reference to the current ExisJS application instance. |
173
+ | `log` | `Logger` | Scoped logger instance with `job: name` context. |
174
+
175
+ ---
176
+
177
+ ## 4. `CronExpression` Presets
178
+
179
+ ExisJS exports pre-validated standard cron expression constants:
180
+
181
+ | Constant | Value | Description |
182
+ | :--- | :--- | :--- |
183
+ | `CronExpression.EVERY_SECOND` | `* * * * * *` | Every second (6-field) |
184
+ | `CronExpression.EVERY_5_SECONDS` | `*/5 * * * * *` | Every 5 seconds (6-field) |
185
+ | `CronExpression.EVERY_10_SECONDS` | `*/10 * * * * *` | Every 10 seconds (6-field) |
186
+ | `CronExpression.EVERY_30_SECONDS` | `*/30 * * * * *` | Every 30 seconds (6-field) |
187
+ | `CronExpression.EVERY_MINUTE` | `* * * * *` | Every minute |
188
+ | `CronExpression.EVERY_5_MINUTES` | `*/5 * * * *` | Every 5 minutes |
189
+ | `CronExpression.EVERY_10_MINUTES` | `*/10 * * * *` | Every 10 minutes |
190
+ | `CronExpression.EVERY_15_MINUTES` | `*/15 * * * *` | Every 15 minutes |
191
+ | `CronExpression.EVERY_30_MINUTES` | `*/30 * * * *` | Every 30 minutes |
192
+ | `CronExpression.EVERY_HOUR` | `0 * * * *` | Top of every hour |
193
+ | `CronExpression.EVERY_2_HOURS` | `0 */2 * * *` | Every 2 hours |
194
+ | `CronExpression.EVERY_6_HOURS` | `0 */6 * * *` | Every 6 hours |
195
+ | `CronExpression.EVERY_12_HOURS` | `0 */12 * * *` | Every 12 hours |
196
+ | `CronExpression.EVERY_DAY_AT_MIDNIGHT` | `0 0 * * *` | Daily at 00:00 (Midnight) |
197
+ | `CronExpression.EVERY_DAY_AT_1AM` | `0 1 * * *` | Daily at 01:00 AM |
198
+ | `CronExpression.EVERY_DAY_AT_2AM` | `0 2 * * *` | Daily at 02:00 AM |
199
+ | `CronExpression.EVERY_DAY_AT_NOON` | `0 12 * * *` | Daily at 12:00 PM |
200
+ | `CronExpression.EVERY_WEEK` | `0 0 * * 0` | Every Sunday at midnight |
201
+ | `CronExpression.EVERY_MONTH` | `0 0 1 * *` | First day of every month at midnight |
202
+ | `CronExpression.EVERY_YEAR` | `0 0 1 1 *` | January 1st at midnight |
203
+
204
+ ---
205
+
206
+ ## 5. Runtime Management API (`app.cron`)
207
+
208
+ Access the cron manager via `app.cron`:
209
+
210
+ ### `app.cron.schedule(options)`
211
+ Registers and schedules a new cron job dynamically.
212
+
213
+ ```typescript
214
+ const job = app.cron.schedule({
215
+ name: 'dynamic-sync',
216
+ schedule: '0 */3 * * *',
217
+ run: async ({ log }) => {
218
+ log.info('Dynamic sync executed')
219
+ }
220
+ })
221
+ ```
222
+
223
+ ### `app.cron.get(name)`
224
+ Returns the `CronJob` instance by name, or `undefined` if not found.
225
+
226
+ ```typescript
227
+ const job = app.cron.get('nightly-sync')
228
+ ```
229
+
230
+ ### `app.cron.has(name)`
231
+ Returns `true` if a job with the specified name exists.
232
+
233
+ ```typescript
234
+ if (app.cron.has('nightly-sync')) {
235
+ // ...
236
+ }
237
+ ```
238
+
239
+ ### `app.cron.list()`
240
+ Returns an array of all registered `CronJob` instances.
241
+
242
+ ```typescript
243
+ const allJobs = app.cron.list()
244
+ ```
245
+
246
+ ### `app.cron.status()`
247
+ Returns an array of status and metric descriptors for all registered jobs.
248
+
249
+ ```typescript
250
+ const stats = app.cron.status()
251
+ // [
252
+ // {
253
+ // name: 'nightly-sync',
254
+ // status: 'running',
255
+ // running: false,
256
+ // totalExecutions: 14,
257
+ // successCount: 14,
258
+ // failureCount: 0,
259
+ // lastExecution: '2026-09-22T00:00:00.000Z',
260
+ // nextExecution: '2026-09-23T00:00:00.000Z',
261
+ // lastDurationMs: 142
262
+ // }
263
+ // ]
264
+ ```
265
+
266
+ ### `app.cron.trigger(name)`
267
+ Manually triggers an immediate execution of the specified job, returning the handler result.
268
+
269
+ ```typescript
270
+ const result = await app.cron.trigger('nightly-sync')
271
+ ```
272
+
273
+ ### `app.cron.pause(name)` / `app.cron.resume(name)`
274
+ Pauses or resumes a job's automatic scheduling without unregistering it.
275
+
276
+ ```typescript
277
+ app.cron.pause('nightly-sync')
278
+ app.cron.resume('nightly-sync')
279
+ ```
280
+
281
+ ### `app.cron.remove(name)`
282
+ Stops and unregisters a job by name.
283
+
284
+ ```typescript
285
+ app.cron.remove('nightly-sync')
286
+ ```
287
+
288
+ ### `app.cron.stopAll()`
289
+ Stops all scheduled timers across all registered jobs.
290
+
291
+ ```typescript
292
+ app.cron.stopAll()
293
+ ```
294
+
295
+ ### `app.cron.drain(timeoutMs?)`
296
+ Stops new ticks and awaits any currently running job executions to complete. Called automatically during graceful server shutdown.
297
+
298
+ ```typescript
299
+ await app.cron.drain(5000)
300
+ ```
@@ -123,3 +123,11 @@ Parameter decorators automatically extract data from the incoming request and in
123
123
  - `@Boundary(config)`: Marks a class as a folder-scoped Boundary configuring CORS, headers, exclusions, and pipeline steps.
124
124
  - `@Server(config)`: Marks a class as a root server definition.
125
125
 
126
+ ---
127
+
128
+ ## 6. Cron & Task Scheduling
129
+
130
+ - `@Cron(schedule: string, options?: CronJobOptions)`: Schedules a class method as a recurring cron job on any `@Injectable()` service.
131
+ - `@Interval(ms: number, options?: IntervalOptions)`: Schedules a class method to execute repeatedly every `ms` milliseconds.
132
+ - `@TimeoutTask(ms: number, options?: TimeoutOptions)` (alias `@ScheduledTimeout`): Schedules a class method to execute once after `ms` milliseconds following application bootstrap.
133
+
@@ -133,6 +133,50 @@ export class InsufficientFundsError extends HttpError {
133
133
 
134
134
  ---
135
135
 
136
+ ## Framework Exception File Convention (`src/http/error.ts`)
137
+
138
+ ExisJS natively discovers and auto-mounts `src/http/error.ts` (or `src/http/*.error.ts`) during application bootstrap. It serves as the single source of truth for centralized exception filtering without requiring manual middleware registration in `server.ts`.
139
+
140
+ ### Handler Signatures
141
+
142
+ **Functional (`export function onError`):**
143
+ ```typescript filename="src/http/error.ts"
144
+ import type { Request, Response } from 'exisjs'
145
+
146
+ export function onError(err: any, req: Request, res: Response): void | Promise<void> {
147
+ const status = err.statusCode || err.status || 500
148
+ res.status(status).json({
149
+ success: false,
150
+ error: {
151
+ code: err.code || 'ERROR',
152
+ message: err.message || 'Internal Server Error',
153
+ path: req.path,
154
+ },
155
+ })
156
+ }
157
+ ```
158
+
159
+ **Class-Based OOP (`export default class`):**
160
+ ```typescript filename="src/http/error.ts"
161
+ import type { Request, Response } from 'exisjs'
162
+
163
+ export default class GlobalErrorHandler {
164
+ onError(err: any, req: Request, res: Response): void | Promise<void> {
165
+ const status = err.statusCode || err.status || 500
166
+ res.status(status).json({
167
+ success: false,
168
+ error: {
169
+ code: err.code || 'ERROR',
170
+ message: err.message || 'Internal Server Error',
171
+ path: req.path,
172
+ },
173
+ })
174
+ }
175
+ }
176
+ ```
177
+
178
+ ---
179
+
136
180
  ## Global Error Handler
137
181
 
138
182
  ExisJS uses a highly sophisticated global error handler out of the box, generated via `createErrorHandler(isDev)`.