@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 +172 -53
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin.d.ts +16 -0
- package/dist/plugin.js +21 -3
- package/package.json +4 -4
package/GUIDE.md
CHANGED
|
@@ -1,31 +1,44 @@
|
|
|
1
1
|
# @veloxts/scheduler Guide
|
|
2
2
|
|
|
3
|
-
|
|
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 {
|
|
14
|
+
import { veloxApp } from '@veloxts/core';
|
|
15
|
+
import { schedulerPlugin, task } from '@veloxts/scheduler';
|
|
7
16
|
|
|
8
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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('
|
|
69
|
-
.
|
|
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
|
-
//
|
|
113
|
-
scheduler.
|
|
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
|
-
|
|
123
|
+
app.register(schedulerPlugin({
|
|
137
124
|
timezone: 'UTC',
|
|
138
|
-
|
|
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
|
-
*
|
|
144
|
-
*
|
|
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
|
|
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.
|
|
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.
|
|
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": "
|
|
29
|
+
"@types/node": "25.0.3",
|
|
30
30
|
"@vitest/coverage-v8": "4.0.16",
|
|
31
31
|
"fastify": "5.6.2",
|
|
32
|
-
"typescript": "5.
|
|
32
|
+
"typescript": "5.9.3",
|
|
33
33
|
"vitest": "4.0.16"
|
|
34
34
|
},
|
|
35
35
|
"publishConfig": {
|