@veloxts/scheduler 0.6.87 → 0.6.89

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/GUIDE.md CHANGED
@@ -1,31 +1,44 @@
1
1
  # @veloxts/scheduler Guide
2
2
 
3
- ## Creating a Scheduler
3
+ Cron task scheduling for VeloxTS applications with a fluent API for defining scheduled tasks.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @veloxts/scheduler
9
+ ```
10
+
11
+ ## Quick Start
4
12
 
5
13
  ```typescript
6
- import { createScheduler, task } from '@veloxts/scheduler';
14
+ import { veloxApp } from '@veloxts/core';
15
+ import { schedulerPlugin, task } from '@veloxts/scheduler';
7
16
 
8
- const scheduler = createScheduler([
9
- task('cleanup-tokens', async () => {
10
- await db.token.deleteMany({ where: { expiresAt: { lt: new Date() } } });
11
- })
12
- .daily()
13
- .at('02:00')
14
- .build(),
17
+ const app = veloxApp();
15
18
 
16
- task('send-digest', async () => {
17
- await sendDailyDigest();
18
- })
19
- .weekdays()
20
- .at('09:00')
21
- .timezone('America/New_York')
22
- .build(),
23
- ], {
19
+ app.register(schedulerPlugin({
24
20
  timezone: 'UTC',
25
- debug: true,
26
- });
21
+ tasks: [
22
+ task('cleanup-tokens', async (ctx) => {
23
+ await ctx.db.token.deleteMany({
24
+ where: { expiresAt: { lt: new Date() } },
25
+ });
26
+ })
27
+ .daily()
28
+ .at('02:00')
29
+ .build(),
27
30
 
28
- scheduler.start();
31
+ task('send-digest', async (ctx) => {
32
+ await sendDailyDigest();
33
+ })
34
+ .weekdays()
35
+ .at('09:00')
36
+ .timezone('America/New_York')
37
+ .build(),
38
+ ],
39
+ }));
40
+
41
+ await app.start();
29
42
  ```
30
43
 
31
44
  ## Schedule Frequencies
@@ -65,9 +78,8 @@ task('name', handler)
65
78
  ## Task Options
66
79
 
67
80
  ```typescript
68
- task('name', handler)
69
- .daily()
70
- .at('09:00')
81
+ task('sync-data', syncData)
82
+ .hourly()
71
83
  .timezone('America/New_York') // Task-specific timezone
72
84
  .withoutOverlapping() // Skip if still running
73
85
  .withoutOverlapping(30) // Max lock time in minutes
@@ -82,36 +94,11 @@ task('name', handler)
82
94
  .build()
83
95
  ```
84
96
 
85
- ## Running the Scheduler
86
-
87
- ### In Your App
88
-
89
- ```typescript
90
- // Start with your app
91
- const scheduler = createScheduler(tasks);
92
- scheduler.start();
93
-
94
- // Graceful shutdown
95
- process.on('SIGTERM', async () => {
96
- await scheduler.stop();
97
- process.exit(0);
98
- });
99
- ```
100
-
101
- ### From System Cron
102
-
103
- Add to your crontab to run every minute:
104
-
105
- ```bash
106
- * * * * * cd /path/to/app && node scheduler.js >> /dev/null 2>&1
107
- ```
108
-
109
97
  ## Scheduler API
110
98
 
111
99
  ```typescript
112
- // Start/stop
113
- scheduler.start();
114
- await scheduler.stop();
100
+ // Access scheduler from context
101
+ const scheduler = ctx.scheduler;
115
102
 
116
103
  // Check status
117
104
  scheduler.isRunning();
@@ -130,12 +117,12 @@ const nextRun = scheduler.getNextRun('cleanup-tokens');
130
117
  const history = scheduler.getHistory('cleanup-tokens');
131
118
  ```
132
119
 
133
- ## Callbacks
120
+ ## Global Callbacks
134
121
 
135
122
  ```typescript
136
- const scheduler = createScheduler(tasks, {
123
+ app.register(schedulerPlugin({
137
124
  timezone: 'UTC',
138
- debug: false,
125
+ tasks: [...],
139
126
  onTaskStart: (task, ctx) => {
140
127
  console.log(`Starting: ${task.name}`);
141
128
  },
@@ -148,5 +135,137 @@ const scheduler = createScheduler(tasks, {
148
135
  onTaskSkip: (task, ctx, reason) => {
149
136
  console.log(`Skipped ${task.name}: ${reason}`);
150
137
  },
138
+ }));
139
+ ```
140
+
141
+ ## Production Deployment
142
+
143
+ ### Key Considerations
144
+
145
+ Unlike other ecosystem packages, the scheduler doesn't require external services like Redis. However, production deployments need special attention:
146
+
147
+ ### 1. Run on Single Instance Only
148
+
149
+ Scheduled tasks should only run on ONE server instance to prevent duplicate execution:
150
+
151
+ ```typescript
152
+ task('send-reports', sendReports)
153
+ .daily()
154
+ .at('09:00')
155
+ .when(() => process.env.SCHEDULER_ENABLED === 'true')
156
+ .build()
157
+ ```
158
+
159
+ Set `SCHEDULER_ENABLED=true` only on one instance.
160
+
161
+ ### 2. Use `withoutOverlapping()` for Long Tasks
162
+
163
+ Prevent task overlap if execution might exceed the schedule interval:
164
+
165
+ ```typescript
166
+ task('sync-inventory', syncInventory)
167
+ .everyFiveMinutes()
168
+ .withoutOverlapping(10) // Lock for max 10 minutes
169
+ .build()
170
+ ```
171
+
172
+ ### 3. Graceful Shutdown
173
+
174
+ Allow running tasks to complete before shutdown:
175
+
176
+ ```typescript
177
+ const app = veloxApp();
178
+
179
+ app.register(schedulerPlugin({ tasks: [...] }));
180
+
181
+ // Graceful shutdown
182
+ process.on('SIGTERM', async () => {
183
+ await app.close(); // Waits for scheduler to stop
184
+ process.exit(0);
151
185
  });
152
186
  ```
187
+
188
+ ### 4. Monitor Task Execution
189
+
190
+ ```typescript
191
+ app.register(schedulerPlugin({
192
+ tasks: [...],
193
+ onTaskError: (task, ctx, error) => {
194
+ // Send to error tracking (Sentry, etc.)
195
+ Sentry.captureException(error, {
196
+ tags: { task: task.name },
197
+ });
198
+ },
199
+ onTaskComplete: (task, ctx, duration) => {
200
+ // Send to metrics (Datadog, etc.)
201
+ metrics.timing(`scheduler.${task.name}`, duration);
202
+ },
203
+ }));
204
+ ```
205
+
206
+ ### Production Checklist
207
+
208
+ 1. **Single instance** - Only enable scheduler on one server
209
+ 2. **Graceful shutdown** - Handle SIGTERM properly
210
+ 3. **Error monitoring** - Track task failures
211
+ 4. **Overlap prevention** - Use `withoutOverlapping()` for long tasks
212
+ 5. **Timezone** - Set explicit timezone for predictable execution
213
+
214
+ ### Environment Variables
215
+
216
+ ```bash
217
+ # .env
218
+ SCHEDULER_ENABLED=true # Only set on scheduler instance
219
+ SCHEDULER_TIMEZONE=UTC # Default timezone
220
+ ```
221
+
222
+ ### Running as Separate Process
223
+
224
+ For better isolation, run scheduler as a separate process:
225
+
226
+ ```typescript
227
+ // scheduler.ts
228
+ import { createScheduler, task } from '@veloxts/scheduler';
229
+
230
+ const scheduler = createScheduler({
231
+ timezone: process.env.SCHEDULER_TIMEZONE || 'UTC',
232
+ tasks: [
233
+ task('cleanup', cleanup).daily().at('02:00').build(),
234
+ task('reports', sendReports).weekdays().at('09:00').build(),
235
+ ],
236
+ });
237
+
238
+ scheduler.start();
239
+
240
+ process.on('SIGTERM', async () => {
241
+ await scheduler.stop();
242
+ process.exit(0);
243
+ });
244
+ ```
245
+
246
+ ```bash
247
+ # Run as separate process
248
+ node scheduler.js
249
+ ```
250
+
251
+ ## Standalone Usage
252
+
253
+ Use scheduler outside of Fastify context:
254
+
255
+ ```typescript
256
+ import { createScheduler, task } from '@veloxts/scheduler';
257
+
258
+ const scheduler = createScheduler({
259
+ timezone: 'UTC',
260
+ tasks: [
261
+ task('my-task', () => console.log('Running!'))
262
+ .everyMinute()
263
+ .build(),
264
+ ],
265
+ });
266
+
267
+ scheduler.start();
268
+
269
+ // Later...
270
+ await scheduler.stop();
271
+ ```
package/dist/index.d.ts CHANGED
@@ -55,7 +55,7 @@
55
55
  * @packageDocumentation
56
56
  */
57
57
  export { createScheduler, scheduler } from './manager.js';
58
- export { _resetStandaloneScheduler, getScheduler, getSchedulerFromInstance, schedulerPlugin, } from './plugin.js';
58
+ export { _resetStandaloneScheduler, closeScheduler, getScheduler, getSchedulerFromInstance, schedulerPlugin, } from './plugin.js';
59
59
  export type { ScheduleInput } from './task.js';
60
60
  export { defineSchedule, defineTask, schedule, task } from './task.js';
61
61
  export type { DayOfWeek, DayOfWeekNumber, ScheduleConstraint, ScheduledTask, SchedulerManager, SchedulerOptions, SchedulerPluginOptions, TaskBuilder, TaskContext, TaskExecution, TaskFailureCallback, TaskHandler, TaskSkipCallback, TaskSuccessCallback, } from './types.js';
package/dist/index.js CHANGED
@@ -57,7 +57,7 @@
57
57
  // Manager
58
58
  export { createScheduler, scheduler } from './manager.js';
59
59
  // Plugin
60
- export { _resetStandaloneScheduler, getScheduler, getSchedulerFromInstance, schedulerPlugin, } from './plugin.js';
60
+ export { _resetStandaloneScheduler, closeScheduler, getScheduler, getSchedulerFromInstance, schedulerPlugin, } from './plugin.js';
61
61
  // Task builder
62
62
  export { defineSchedule, defineTask, schedule, task } from './task.js';
63
63
  // ============================================================================
package/dist/plugin.d.ts CHANGED
@@ -113,8 +113,24 @@ export declare function getSchedulerFromInstance(fastify: FastifyInstance): Sche
113
113
  * ```
114
114
  */
115
115
  export declare function getScheduler(options?: SchedulerPluginOptions): SchedulerManager;
116
+ /**
117
+ * Close the standalone scheduler instance.
118
+ *
119
+ * Call this when shutting down your application to stop all scheduled tasks.
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * import { closeScheduler } from '@veloxts/scheduler';
124
+ *
125
+ * // On shutdown
126
+ * await closeScheduler();
127
+ * ```
128
+ */
129
+ export declare function closeScheduler(): Promise<void>;
116
130
  /**
117
131
  * Reset the standalone scheduler instance.
118
132
  * Primarily used for testing.
133
+ *
134
+ * @deprecated Use `closeScheduler()` instead. Will be removed in v2.0.
119
135
  */
120
136
  export declare function _resetStandaloneScheduler(): Promise<void>;
package/dist/plugin.js CHANGED
@@ -140,12 +140,30 @@ export function getScheduler(options) {
140
140
  return standaloneScheduler;
141
141
  }
142
142
  /**
143
- * Reset the standalone scheduler instance.
144
- * Primarily used for testing.
143
+ * Close the standalone scheduler instance.
144
+ *
145
+ * Call this when shutting down your application to stop all scheduled tasks.
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * import { closeScheduler } from '@veloxts/scheduler';
150
+ *
151
+ * // On shutdown
152
+ * await closeScheduler();
153
+ * ```
145
154
  */
146
- export async function _resetStandaloneScheduler() {
155
+ export async function closeScheduler() {
147
156
  if (standaloneScheduler) {
148
157
  await standaloneScheduler.stop();
149
158
  standaloneScheduler = null;
150
159
  }
151
160
  }
161
+ /**
162
+ * Reset the standalone scheduler instance.
163
+ * Primarily used for testing.
164
+ *
165
+ * @deprecated Use `closeScheduler()` instead. Will be removed in v2.0.
166
+ */
167
+ export async function _resetStandaloneScheduler() {
168
+ await closeScheduler();
169
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veloxts/scheduler",
3
- "version": "0.6.87",
3
+ "version": "0.6.89",
4
4
  "description": "Task scheduling for VeloxTS framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -20,16 +20,16 @@
20
20
  "dependencies": {
21
21
  "cron": "3.5.0",
22
22
  "fastify-plugin": "5.1.0",
23
- "@veloxts/core": "0.6.87"
23
+ "@veloxts/core": "0.6.89"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "fastify": "^5.0.0"
27
27
  },
28
28
  "devDependencies": {
29
- "@types/node": "22.15.29",
29
+ "@types/node": "25.0.3",
30
30
  "@vitest/coverage-v8": "4.0.16",
31
31
  "fastify": "5.6.2",
32
- "typescript": "5.8.3",
32
+ "typescript": "5.9.3",
33
33
  "vitest": "4.0.16"
34
34
  },
35
35
  "publishConfig": {