exisjs 0.7.12 → 0.7.13

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 (116) hide show
  1. package/README.md +195 -39
  2. package/dist/config/types.d.ts +2 -2
  3. package/dist/config/types.d.ts.map +1 -1
  4. package/dist/decorators/params.d.ts +21 -0
  5. package/dist/decorators/params.d.ts.map +1 -1
  6. package/dist/decorators/params.js +21 -1
  7. package/dist/decorators/params.js.map +1 -1
  8. package/dist/di/container.d.ts +15 -6
  9. package/dist/di/container.d.ts.map +1 -1
  10. package/dist/di/container.js +26 -3
  11. package/dist/di/container.js.map +1 -1
  12. package/dist/di/index.d.ts +1 -1
  13. package/dist/di/index.d.ts.map +1 -1
  14. package/dist/di/index.js.map +1 -1
  15. package/dist/di/inject.d.ts +2 -1
  16. package/dist/di/inject.d.ts.map +1 -1
  17. package/dist/di/inject.js +2 -1
  18. package/dist/di/inject.js.map +1 -1
  19. package/dist/docs/BENCHMARKS.md +4 -4
  20. package/dist/docs/content/README.md +43 -44
  21. package/dist/docs/content/boundaries.mdx +138 -61
  22. package/dist/docs/content/cli/commands.mdx +151 -128
  23. package/dist/docs/content/cli/overview.mdx +61 -47
  24. package/dist/docs/content/cli/usage.mdx +107 -30
  25. package/dist/docs/content/config.mdx +145 -158
  26. package/dist/docs/content/controllers.mdx +193 -464
  27. package/dist/docs/content/cron.mdx +183 -69
  28. package/dist/docs/content/decorators.mdx +449 -154
  29. package/dist/docs/content/errors.mdx +202 -305
  30. package/dist/docs/content/exisjs-mcp.mdx +59 -49
  31. package/dist/docs/content/installation.mdx +243 -157
  32. package/dist/docs/content/introduction.mdx +220 -48
  33. package/dist/docs/content/middleware.mdx +171 -497
  34. package/dist/docs/content/modules.mdx +141 -83
  35. package/dist/docs/content/providers.mdx +216 -180
  36. package/dist/docs/content/requests.mdx +168 -488
  37. package/dist/docs/content/responses.mdx +200 -533
  38. package/dist/docs/content/routing.mdx +235 -598
  39. package/dist/docs/content/security.mdx +233 -57
  40. package/dist/docs/content/structure.mdx +151 -240
  41. package/dist/docs/content/swagger.mdx +192 -91
  42. package/dist/docs/content/testing.mdx +205 -74
  43. package/dist/docs/content/validation.mdx +479 -341
  44. package/dist/docs/reference/README.md +34 -2
  45. package/dist/docs/reference/config.mdx +277 -75
  46. package/dist/docs/reference/context.mdx +197 -106
  47. package/dist/docs/reference/cron.mdx +266 -152
  48. package/dist/docs/reference/database.mdx +135 -53
  49. package/dist/docs/reference/decorators.mdx +234 -116
  50. package/dist/docs/reference/di.mdx +146 -112
  51. package/dist/docs/reference/error.mdx +155 -178
  52. package/dist/docs/reference/exis.mdx +141 -70
  53. package/dist/docs/reference/logger.mdx +110 -126
  54. package/dist/docs/reference/middleware.mdx +235 -158
  55. package/dist/docs/reference/plugin.mdx +136 -64
  56. package/dist/docs/reference/router.mdx +275 -293
  57. package/dist/docs/reference/sanitize.mdx +91 -87
  58. package/dist/docs/reference/swagger.mdx +280 -100
  59. package/dist/docs/reference/testing.mdx +223 -89
  60. package/dist/docs/reference/validator.mdx +194 -206
  61. package/dist/lib/start-server.js +3 -8
  62. package/dist/lib/start-server.js.map +1 -1
  63. package/dist/module/module.d.ts +6 -3
  64. package/dist/module/module.d.ts.map +1 -1
  65. package/dist/module/module.js +17 -3
  66. package/dist/module/module.js.map +1 -1
  67. package/dist/router/controller-registrar.d.ts.map +1 -1
  68. package/dist/router/controller-registrar.js +26 -4
  69. package/dist/router/controller-registrar.js.map +1 -1
  70. package/dist/server/app.d.ts +3 -1
  71. package/dist/server/app.d.ts.map +1 -1
  72. package/dist/server/app.js +24 -8
  73. package/dist/server/app.js.map +1 -1
  74. package/dist/server/request.d.ts +2 -0
  75. package/dist/server/request.d.ts.map +1 -1
  76. package/dist/server/request.js +11 -0
  77. package/dist/server/request.js.map +1 -1
  78. package/dist/swagger/generator.d.ts.map +1 -1
  79. package/dist/swagger/generator.js +130 -10
  80. package/dist/swagger/generator.js.map +1 -1
  81. package/dist/telemetry/index.d.ts +12 -0
  82. package/dist/telemetry/index.d.ts.map +1 -0
  83. package/dist/telemetry/index.js +121 -0
  84. package/dist/telemetry/index.js.map +1 -0
  85. package/dist/testing/client.d.ts +1 -0
  86. package/dist/testing/client.d.ts.map +1 -1
  87. package/dist/testing/client.js +4 -1
  88. package/dist/testing/client.js.map +1 -1
  89. package/dist/testing/index.d.ts.map +1 -1
  90. package/dist/testing/index.js +11 -4
  91. package/dist/testing/index.js.map +1 -1
  92. package/dist/validator/builder.d.ts +26 -1
  93. package/dist/validator/builder.d.ts.map +1 -1
  94. package/dist/validator/builder.js +31 -0
  95. package/dist/validator/builder.js.map +1 -1
  96. package/dist/validator/engine.d.ts +23 -0
  97. package/dist/validator/engine.d.ts.map +1 -1
  98. package/dist/validator/engine.js +228 -31
  99. package/dist/validator/engine.js.map +1 -1
  100. package/dist/validator/index.d.ts +8 -4
  101. package/dist/validator/index.d.ts.map +1 -1
  102. package/dist/validator/index.js +4 -1
  103. package/dist/validator/index.js.map +1 -1
  104. package/dist/validator/tex-types.d.ts +15 -0
  105. package/dist/validator/tex-types.d.ts.map +1 -1
  106. package/dist/validator/tex-types.js +102 -0
  107. package/dist/validator/tex-types.js.map +1 -1
  108. package/dist/validator/tex.d.ts +1 -1
  109. package/dist/validator/tex.d.ts.map +1 -1
  110. package/dist/validator/tex.js +3 -1
  111. package/dist/validator/tex.js.map +1 -1
  112. package/dist/validator/types.d.ts +1 -0
  113. package/dist/validator/types.d.ts.map +1 -1
  114. package/package.json +46 -14
  115. package/dist/docs/content/dataloader.mdx +0 -170
  116. package/dist/docs/reference/dataloader.mdx +0 -91
@@ -1,41 +1,42 @@
1
1
  ---
2
- nextUrl: "/docs/queues"
3
- nextTitle: "Queues & Background Jobs"
2
+ title: "Background Tasks and Cron"
3
+ description: "Schedule background tasks, recurring cron jobs, intervals, and startup timeouts with overlap prevention and drift correction in ExisJS."
4
+ nextUrl: "/docs/swagger"
5
+ nextTitle: "OpenAPI & Swagger"
4
6
  ---
5
7
 
6
- # Cron & Task Scheduling
8
+ # Background Tasks & Cron
7
9
 
8
- ExisJS includes a built-in Cron and Task Scheduling system designed for reliability and developer ergonomic efficiency. It resolves common Node.js scheduling challenges such as timer drift, concurrent execution overlap, unhandled exceptions crashing the process, timezone/DST inaccuracies, and zombie timers during hot reload or process shutdown.
10
+ ExisJS provides an integrated task scheduling system for background jobs, recurring intervals, and delayed timeouts. It eliminates common Node.js scheduling hazards including timer drift, execution overlap, unhandled exceptions crashing the process, timezone/DST inaccuracies, and lingering zombie timers during graceful shutdowns.
9
11
 
10
- Both **Functional** and **Class-Based (OOP)** paradigms are fully supported with complete feature parity.
12
+ Both **Functional** (`cron()`) and **Class-Based OOP** (`@Cron()`) paradigms are fully supported with complete feature parity.
11
13
 
12
14
  ---
13
15
 
14
16
  ## File Conventions & Auto-Discovery
15
17
 
16
- ExisJS auto-mounts cron jobs using two straightforward conventions:
18
+ ExisJS automatically discovers, compiles, and mounts scheduled cron tasks during server bootstrap using two flexible layout conventions:
17
19
 
18
- 1. **Single-File Management (`src/http/cron.ts` or `src/http/cron.js`)**:
19
- Define and manage all your scheduled tasks in a single file located at `src/http/cron.ts` (or inside route slices such as `src/http/admin/cron.ts`). Multiple jobs can be exported using named exports, a default export, or an array of jobs.
20
-
21
- 2. **Dedicated Directory (`src/cron/*`)**:
22
- Organize distinct tasks into separate files inside `src/cron/` (e.g. `src/cron/sync.ts`, `src/cron/reports.ts`). Every file inside `src/cron/` is automatically registered upon application boot.
20
+ 1. **Single-File / Root HTTP Convention (`src/http/cron.ts` or `src/http/cron.js`)**:
21
+ Define and manage all your application's background jobs in a single root file at `src/http/cron.ts`. You can also co-locate cron files within route slices (e.g., `src/http/admin/cron.ts` or `src/http/billing/cron.ts`). Multiple jobs can be exported using named exports, default exports, or an array of job definitions (`export default [job1, job2]`).
22
+ 2. **Dedicated Task Directory (`src/cron/*`)**:
23
+ Organize individual tasks into separate files inside `src/cron/` (e.g., `src/cron/cleanup.ts`, `src/cron/sync.ts`). Every file inside `src/cron/` is automatically registered upon boot.
23
24
 
24
25
  ---
25
26
 
26
27
  ## 1. Functional Paradigm (`cron`)
27
28
 
28
- Import `cron` from `exisjs/cron` to define scheduled tasks.
29
+ Import `cron` and `CronExpression` from `exisjs/cron` to define scheduled background tasks.
29
30
 
30
- ### Single-File Multi-Job Example
31
+ ### Root Single-File Management (`src/http/cron.ts`)
31
32
 
32
- Manage multiple jobs within `src/http/cron.ts`:
33
+ You can define and export all your application's background tasks directly within `src/http/cron.ts`:
33
34
 
34
35
  ```typescript filename="src/http/cron.ts"
35
36
  import { cron, CronExpression } from 'exisjs/cron'
36
37
 
37
- // Job 1: Nightly synchronization
38
- export const syncJob = cron({
38
+ // Task 1: Nightly synchronization at midnight UTC
39
+ export const nightlySync = cron({
39
40
  name: 'nightly-sync',
40
41
  schedule: CronExpression.EVERY_DAY_AT_MIDNIGHT,
41
42
  timezone: 'UTC',
@@ -43,30 +44,58 @@ export const syncJob = cron({
43
44
  retries: 2,
44
45
  retryDelayMs: 3000,
45
46
  async run({ app, log }) {
46
- log.info('Executing nightly sync...')
47
+ log.info('Running nightly data synchronization...')
47
48
  },
48
49
  onError(err, { log }) {
49
- log.error({ err }, 'Nightly sync failed after retries')
50
+ log.error({ err }, 'Nightly sync failed after retry attempts')
50
51
  }
51
52
  })
52
53
 
53
- // Job 2: Frequent polling using human-readable duration
54
- export const pollJob = cron.every('5 minutes', async ({ log }) => {
55
- log.info('Checking external queue...')
54
+ // Task 2: Human-readable duration schedule
55
+ export const queuePoller = cron.every('5 minutes', async ({ log }) => {
56
+ log.info('Polling external queue...')
57
+ }, {
58
+ name: 'queue-poller',
59
+ preventOverlap: true
56
60
  })
57
61
 
58
- // Job 3: Recurring millisecond interval
62
+ // Task 3: Fixed millisecond interval
59
63
  export const heartbeat = cron.interval(30_000, async ({ log }) => {
60
64
  log.info('System heartbeat active')
65
+ }, {
66
+ name: 'health-heartbeat'
61
67
  })
62
68
 
63
- // Job 4: Startup delayed task (runs 5 seconds after server boots)
64
- export const warmup = cron.timeout(5_000, async ({ app, log }) => {
65
- log.info('Pre-warming cache...')
69
+ // Task 4: One-time startup delayed task (runs 5s after server boots)
70
+ export const cacheWarmup = cron.timeout(5_000, async ({ log }) => {
71
+ log.info('Pre-warming cache in memory...')
72
+ }, {
73
+ name: 'cache-warmup'
66
74
  })
67
75
  ```
68
76
 
69
- ### Dedicated File Example
77
+ ### Exporting an Array of Jobs (`export default [...]`)
78
+
79
+ In both `src/http/cron.ts` and `src/cron/*.ts`, you can also export an array of jobs as the default export:
80
+
81
+ ```typescript filename="src/http/cron.ts"
82
+ import { cron, CronExpression } from 'exisjs/cron'
83
+
84
+ export default [
85
+ cron({
86
+ name: 'session-cleanup',
87
+ schedule: CronExpression.EVERY_HOUR,
88
+ async run({ log }) {
89
+ log.info('Purging expired sessions...')
90
+ }
91
+ }),
92
+ cron.interval(60_000, async ({ log }) => {
93
+ log.info('Checking worker status...')
94
+ }, { name: 'worker-status' })
95
+ ]
96
+ ```
97
+
98
+ ### Dedicated File Convention (`src/cron/cleanup.ts`)
70
99
 
71
100
  ```typescript filename="src/cron/cleanup.ts"
72
101
  import { cron, CronExpression } from 'exisjs/cron'
@@ -86,111 +115,196 @@ export default cron({
86
115
 
87
116
  ## 2. Class-Based (OOP) Paradigm (`@Cron`)
88
117
 
89
- In the OOP paradigm, methods on any `@Injectable()` service or class can be scheduled using decorators from `exisjs/decorators` with full **Dependency Injection** support.
118
+ In class-based applications, schedule methods on `@Injectable()` service classes using decorators from `exisjs/decorators` with full **Dependency Injection** support.
90
119
 
91
- ```typescript filename="src/cron/cleanup.ts"
120
+ ```typescript filename="src/cron/maintenance.service.ts"
92
121
  import { Injectable, Cron, Interval, TimeoutTask, CronExpression } from 'exisjs/decorators'
93
- import { DatabaseService } from '../services/database.service'
94
- import { LoggerService } from '../services/logger.service'
122
+ import { DatabaseService } from '../database/database.service'
123
+ import { LoggerService } from '../common/logger.service'
95
124
 
96
125
  @Injectable()
97
- export default class CleanupService {
126
+ export default class MaintenanceService {
98
127
  constructor(
99
- private db: DatabaseService,
100
- private logger: LoggerService
128
+ private readonly db: DatabaseService,
129
+ private readonly logger: LoggerService
101
130
  ) {}
102
131
 
103
132
  @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT, {
104
133
  name: 'nightly-purge',
105
134
  timezone: 'UTC',
106
135
  preventOverlap: true,
136
+ retries: 2,
137
+ retryDelayMs: 5000,
107
138
  })
108
- async purgeOldSessions() {
139
+ async purgeExpiredSessions() {
109
140
  this.logger.info('Purging expired user sessions...')
110
141
  await this.db.deleteExpiredSessions()
111
142
  }
112
143
 
113
- @Interval(60_000, { name: 'health-ping' })
114
- async checkDatabaseHealth() {
144
+ @Interval(60_000, {
145
+ name: 'db-health-check',
146
+ preventOverlap: true,
147
+ })
148
+ async pingDatabase() {
115
149
  await this.db.ping()
116
150
  }
117
151
 
118
- @TimeoutTask(5_000, { name: 'cache-warmup' })
152
+ @TimeoutTask(5_000, {
153
+ name: 'cache-init',
154
+ })
119
155
  async warmupCache() {
120
- this.logger.info('Pre-warming cache...')
156
+ this.logger.info('Pre-warming cache after server boot...')
121
157
  }
122
158
  }
123
159
  ```
124
160
 
125
161
  ---
126
162
 
127
- ## 3. Core Reliability Features
163
+ ## Configuration Options (`CronJobOptions`)
164
+
165
+ The following options configure job execution, error recovery, and scheduling policies:
166
+
167
+ | Option | Type | Default | Description |
168
+ | :--- | :--- | :--- | :--- |
169
+ | `name` | `string` | Auto-generated | Unique identifier for the job, used in logs and programmatic management. |
170
+ | `schedule` | `string` | — | Standard 5-part or 6-part cron pattern (e.g., `'0 0 * * *'`). |
171
+ | `interval` | `number` | — | Execution interval in milliseconds (used with interval jobs). |
172
+ | `timeout` | `number` | — | Startup delay in milliseconds before executing once (used with timeout jobs). |
173
+ | `timezone` | `string` | System Local | IANA timezone identifier (e.g., `'UTC'`, `'America/New_York'`, `'Asia/Kolkata'`). |
174
+ | `preventOverlap` | `boolean` | `true` | When `true`, skips execution if the previous tick is still running. |
175
+ | `autoStart` | `boolean` | `true` | Automatically starts the scheduler when the job is registered. |
176
+ | `retries` | `number` | `0` | Number of retry attempts if the handler throws an error. |
177
+ | `retryDelayMs` | `number` | `1000` | Milliseconds to wait between retry attempts. |
178
+ | `run` / `handle` | `(ctx) => Promise<any>` | — | The execution handler function receiving `CronContext`. |
179
+ | `onError` | `(err, ctx) => void` | — | Optional callback triggered when all execution attempts fail. |
180
+
181
+ ---
182
+
183
+ ## Execution Context (`CronContext`)
184
+
185
+ Every job handler receives a `CronContext` object containing execution metadata and framework instances:
186
+
187
+ ```typescript
188
+ export interface CronContext {
189
+ /** The Exis application instance */
190
+ app: App<any>
191
+ /** Structured logger instance */
192
+ log: Logger
193
+ /** Name of the running job */
194
+ jobName: string
195
+ /** Timestamp when this execution iteration started */
196
+ startedAt: Date
197
+ /** Current execution iteration count */
198
+ iteration: number
199
+ /** Signal aborted when graceful server shutdown is initiated */
200
+ signal?: AbortSignal
201
+ }
202
+ ```
203
+
204
+ ---
205
+
206
+ ## Standard Cron Expression Constants (`CronExpression`)
207
+
208
+ ExisJS exports pre-tested cron expressions for common schedules:
209
+
210
+ ```typescript
211
+ import { CronExpression } from 'exisjs/cron'
212
+
213
+ CronExpression.EVERY_SECOND // '* * * * * *'
214
+ CronExpression.EVERY_5_SECONDS // '*/5 * * * * *'
215
+ CronExpression.EVERY_10_SECONDS // '*/10 * * * * *'
216
+ CronExpression.EVERY_30_SECONDS // '*/30 * * * * *'
217
+ CronExpression.EVERY_MINUTE // '* * * * *'
218
+ CronExpression.EVERY_5_MINUTES // '*/5 * * * *'
219
+ CronExpression.EVERY_10_MINUTES // '*/10 * * * *'
220
+ CronExpression.EVERY_30_MINUTES // '*/30 * * * *'
221
+ CronExpression.EVERY_HOUR // '0 * * * *'
222
+ CronExpression.EVERY_2_HOURS // '0 */2 * * *'
223
+ CronExpression.EVERY_6_HOURS // '0 */6 * * *'
224
+ CronExpression.EVERY_DAY_AT_MIDNIGHT // '0 0 * * *'
225
+ CronExpression.EVERY_DAY_AT_1AM // '0 1 * * *'
226
+ CronExpression.EVERY_DAY_AT_2AM // '0 2 * * *'
227
+ CronExpression.EVERY_DAY_AT_NOON // '0 12 * * *'
228
+ CronExpression.EVERY_WEEK // '0 0 * * 0'
229
+ CronExpression.EVERY_WEEKDAY // '0 0 * * 1-5'
230
+ CronExpression.EVERY_WEEKEND // '0 0 * * 6,0'
231
+ CronExpression.EVERY_MONTH // '0 0 1 * *'
232
+ CronExpression.EVERY_YEAR // '0 0 1 1 *'
233
+ ```
234
+
235
+ ---
236
+
237
+ ## Core Reliability Guarantees
238
+
239
+ ### 1. Overlap Prevention (`preventOverlap: true`)
240
+ If a job takes longer than its scheduling frequency (for example, a database migration running for 90 seconds on a 60-second interval), standard Node.js timers cause overlapping concurrent runs. ExisJS locks execution during active runs and skips overlapping ticks until the current run completes.
128
241
 
129
- ### Overlap Prevention (`preventOverlap: true`)
130
- Standard timers trigger subsequent ticks even if the current execution is stalled, leading to connection exhaustion and database lock contention. With `preventOverlap: true` (default: `true`), ExisJS locks execution during active jobs and skips overlapping executions until the current run completes.
242
+ ### 2. Isolated Error Sandbox
243
+ Exceptions thrown inside cron handlers are caught within an isolated execution boundary. An uncaught error in a background task will never crash the HTTP server or prevent subsequent scheduled runs. Errors are logged with execution duration and forwarded to your `onError` handler.
131
244
 
132
- ### Error Isolation Sandbox
133
- Exceptions thrown inside cron handlers are caught within an isolated execution boundary. An error in a background task will never crash the HTTP server or stop subsequent cron triggers. Errors are logged and forwarded to an optional `onError(err, context)` hook for monitoring integration (e.g. Sentry, Datadog).
245
+ ### 3. Accurate Timezone & DST Calculation
246
+ Cron schedules evaluate IANA timezones (e.g. `'America/Chicago'`, `'Europe/Berlin'`, `'UTC'`) using native `Intl.DateTimeFormat` evaluations, adjusting accurately for Daylight Saving Time (DST) transitions.
134
247
 
135
- ### IANA Timezones & Daylight Saving Time (DST)
136
- Supports standard IANA timezones (e.g. `'America/New_York'`, `'Asia/Kolkata'`, `'Europe/London'`, `'UTC'`). Next execution dates are computed accurately across DST transitions using cached native `Intl.DateTimeFormat` evaluations.
248
+ ### 4. Graceful Shutdown & Drain Window
249
+ When the server receives `SIGTERM` or `SIGINT`:
250
+ 1. The scheduler halts all future timer registrations.
251
+ 2. Active timers are unreferenced (`timer.unref()`).
252
+ 3. Running tasks are allowed to complete within the configurable drain window (default: 5000ms) before the process terminates.
137
253
 
138
254
  ---
139
255
 
140
- ## 4. Programmatic Runtime Management (`app.cron`)
256
+ ## Programmatic Management (`app.cron`)
141
257
 
142
- You can inspect, pause, resume, or trigger jobs dynamically at runtime via `app.cron`:
258
+ Manage registered jobs dynamically at runtime from HTTP endpoints or administrative services via `app.cron`:
143
259
 
144
- ```typescript filename="src/http/admin/cron/route.ts"
260
+ ```typescript filename="src/http/admin/tasks/route.ts"
145
261
  import { controller, route } from 'exisjs/router'
146
262
 
147
263
  export default controller({
148
- // Query status and metrics of all registered jobs
149
- listJobs: route.get('/', async ({ app }) => {
264
+ // Get execution statistics and status for all jobs
265
+ listStats: route.get('/', async ({ app }) => {
150
266
  return {
151
- jobs: app.cron.status()
267
+ jobs: app.cron.getStats()
152
268
  }
153
269
  }),
154
270
 
155
- // Manually trigger a scheduled job on demand
271
+ // Manually trigger an immediate execution
156
272
  triggerJob: route.post('/:name/trigger', async ({ app, params }) => {
157
273
  const result = await app.cron.trigger(params.name)
158
274
  return { success: true, result }
159
275
  }),
160
276
 
161
- // Pause a job
277
+ // Pause a running job
162
278
  pauseJob: route.post('/:name/pause', async ({ app, params }) => {
163
- app.cron.pause(params.name)
279
+ const job = app.cron.getJob(params.name)
280
+ if (!job) return { success: false, error: 'Job not found' }
281
+
282
+ job.pause()
164
283
  return { success: true, message: `Job ${params.name} paused` }
165
284
  }),
166
285
 
167
286
  // Resume a paused job
168
287
  resumeJob: route.post('/:name/resume', async ({ app, params }) => {
169
- app.cron.resume(params.name)
288
+ const job = app.cron.getJob(params.name)
289
+ if (!job) return { success: false, error: 'Job not found' }
290
+
291
+ job.resume()
170
292
  return { success: true, message: `Job ${params.name} resumed` }
171
- }),
293
+ })
172
294
  })
173
295
  ```
174
296
 
175
297
  ---
176
298
 
177
- ## 5. Graceful Shutdown & Draining
299
+ ## CLI Generation
178
300
 
179
- When the application receives a shutdown signal (`SIGINT`, `SIGTERM`, or `app.close()`):
180
- 1. The scheduler stops scheduling future ticks immediately.
181
- 2. Active timers are unreferenced (`unref()`).
182
- 3. Running tasks are allowed to finish within the configured drain window (default: 5000ms) before the process terminates.
183
-
184
- ---
185
-
186
- ## 6. CLI Scaffolding
187
-
188
- Generate cron job files using the ExisJS CLI:
301
+ Scaffold new scheduled tasks with the ExisJS CLI:
189
302
 
190
303
  ```bash
191
- # Functional Job (generates src/cron/cleanup.ts)
304
+ # Functional job (generates src/cron/cleanup.ts)
192
305
  npx exis generate cron cleanup
193
306
 
194
- # Class-Based Job (generates src/cron/cleanup.ts with @Injectable & @Cron)
307
+ # Class-based OOP service (generates src/cron/cleanup.service.ts with @Injectable & @Cron)
195
308
  npx exis generate cron cleanup --oop
196
309
  ```
310
+