Schema-validated job scheduling with worker thread isolation, automatic retries, and event streaming.
npm install @cleverbrush/schedulerRequires Node.js 16+ (uses worker_threads).
| Feature | Description |
|---|---|
| Worker thread isolation | Each job runs in its own Worker thread — crashes never affect the scheduler |
| 5 schedule types | Minute, day, week, month, year with configurable intervals |
| Automatic retries | maxRetries for immediate retries; maxConsequentFails to auto-disable flaky jobs |
| Concurrency control | noConcurrentRuns: true prevents overlapping executions |
| Event-driven | job:start, job:end, job:error, job:timeout, job:message events with stdout/stderr streams |
| Timeout handling | Per-job timeout with automatic worker termination |
| Pluggable persistence | Implement IJobRepository for database-backed storage (default: in-memory) |
| Schema-validated | All schedules validated with @cleverbrush/schema — export Schemas.ScheduleSchema for API validation |
import { JobScheduler } from '@cleverbrush/scheduler';
const scheduler = new JobScheduler({
rootFolder: '/path/to/jobs'
});
scheduler.addJob({
id: 'cleanup',
path: 'cleanup.js',
schedule: { every: 'day', interval: 1, hour: 3, minute: 0 }
});
scheduler.addJob({
id: 'weekly-report',
path: 'report.js',
schedule: {
every: 'week',
interval: 1,
dayOfWeek: [1], // Monday
hour: 9,
minute: 0
},
timeout: 1000 * 60 * 5, // 5 min timeout
maxRetries: 3,
noConcurrentRuns: true
});
scheduler.start();scheduler.on('job:start', ({ jobId, instanceId, startDate, stdout, stderr }) => {
console.log(`Job ${jobId} started at ${startDate}`);
stdout.on('data', (chunk) => process.stdout.write(chunk));
});
scheduler.on('job:end', ({ jobId, endDate }) => {
console.log(`Job ${jobId} completed at ${endDate}`);
});
scheduler.on('job:error', ({ jobId, error }) => {
console.error(`Job ${jobId} failed:`, error);
});
scheduler.on('job:timeout', ({ jobId }) => {
console.warn(`Job ${jobId} timed out — worker terminated`);
});
scheduler.on('job:message', ({ jobId, value }) => {
console.log(`Message from ${jobId}:`, value);
});| Type | Example | Runs |
|---|---|---|
minute | { every: "minute", interval: 15 } | Every 15 minutes |
day | { every: "day", interval: 1, hour: 3, minute: 0 } | Daily at 03:00 |
week | { every: "week", interval: 1, dayOfWeek: [1, 5], hour: 9, minute: 0 } | Mon & Fri at 09:00 |
month | { every: "month", interval: 1, dayOfMonth: 1, hour: 0, minute: 0 } | 1st of each month at midnight |
year | { every: "year", interval: 1, month: 1, dayOfMonth: 1, hour: 0, minute: 0 } | Jan 1 each year at midnight |
Use ScheduleCalculator standalone to preview or test schedule dates without running jobs:
import { ScheduleCalculator } from '@cleverbrush/scheduler';
const calc = new ScheduleCalculator(
{ every: 'week', interval: 1, dayOfWeek: [1], hour: 9, minute: 0 },
new Date('2025-01-01')
);
while (calc.hasNext()) {
const next = calc.next();
console.log(next); // 2025-01-06T09:00:00, 2025-01-13T09:00:00, ...
if (next > someCutoff) break;
}By default jobs are stored in memory. Implement IJobRepository for durable storage:
const scheduler = new JobScheduler({
rootFolder: '/path/to/jobs',
persistRepository: myDatabaseRepository
});