Cron Scheduler
The Cron Scheduler module provides task scheduling with full cron expression support, retries, and error handling.
Overview
Schedule recurring tasks using familiar cron syntax:
- 5-field expressions - minute, hour, day, month, weekday
- 6-field expressions - adds seconds
- Named values -
MON,JAN, etc. - Ranges and steps -
1-5,*/15 - Automatic retries - configurable retry policy
Installation
typescript
import {
Scheduler,
createScheduler,
parseCron,
JobConfig,
} from '@aiversum/aiv-agents/cron';Basic Usage
Creating a Scheduler
typescript
const scheduler = createScheduler({
timezone: 'Europe/Warsaw',
defaultRetries: 3,
});
// Add a job
scheduler.addJob({
id: 'daily-report',
schedule: '0 9 * * *', // Every day at 9:00 AM
handler: async () => {
console.log('Generating daily report...');
// Your logic here
},
});
// Start the scheduler
scheduler.start();One-Time Execution
typescript
// Execute a job immediately
await scheduler.runJob('daily-report');
// Execute with custom context
await scheduler.runJob('daily-report', { force: true });Cron Expression Syntax
5-Field Format (Standard)
┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12 or JAN-DEC)
│ │ │ │ ┌───────────── day of week (0-6 or SUN-SAT)
│ │ │ │ │
* * * * *6-Field Format (With Seconds)
┌───────────── second (0-59)
│ ┌───────────── minute (0-59)
│ │ ┌───────────── hour (0-23)
│ │ │ ┌───────────── day of month (1-31)
│ │ │ │ ┌───────────── month (1-12 or JAN-DEC)
│ │ │ │ │ ┌───────────── day of week (0-6 or SUN-SAT)
│ │ │ │ │ │
* * * * * *Examples
| Expression | Description |
|---|---|
* * * * * | Every minute |
0 * * * * | Every hour at minute 0 |
0 9 * * * | Every day at 9:00 AM |
0 9 * * MON-FRI | Weekdays at 9:00 AM |
*/15 * * * * | Every 15 minutes |
0 0 1 * * | First day of each month |
30 4 * * SUN | Sundays at 4:30 AM |
0 0,12 * * * | Midnight and noon |
0 9-17 * * * | Every hour 9 AM to 5 PM |
Special Characters
| Character | Description | Example |
|---|---|---|
* | Any value | * * * * * |
, | List of values | 1,15,30 * * * * |
- | Range | 9-17 * * * * |
/ | Step | */5 * * * * |
Named Values
Days of Week:SUN, MON, TUE, WED, THU, FRI, SAT
Months:JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC
Job Configuration
Full Configuration
typescript
interface JobConfig {
// Required
id: string; // Unique job identifier
schedule: string; // Cron expression
handler: JobHandler; // Async function to execute
// Optional
name?: string; // Human-readable name
description?: string; // Job description
enabled?: boolean; // Enable/disable (default: true)
timezone?: string; // Override scheduler timezone
// Retry policy
retries?: number; // Max retry attempts
retryDelay?: number; // Delay between retries (ms)
backoff?: 'linear' | 'exponential';
// Timeout
timeout?: number; // Max execution time (ms)
// Concurrency
allowConcurrent?: boolean; // Allow parallel runs
// Metadata
tags?: string[]; // For filtering/grouping
priority?: number; // Execution priority
}Example: Complex Job
typescript
scheduler.addJob({
id: 'email-digest',
name: 'Daily Email Digest',
description: 'Send daily summary email to subscribers',
schedule: '0 8 * * MON-FRI',
timezone: 'Europe/Warsaw',
handler: async (context) => {
const { jobId, runId, attempt } = context;
console.log(`Running job ${jobId} (attempt ${attempt})`);
// Fetch data
const data = await fetchDailyData();
// Send emails
await sendDigest(data);
return { sent: data.length };
},
// Retry 3 times with exponential backoff
retries: 3,
retryDelay: 5000,
backoff: 'exponential',
// Timeout after 5 minutes
timeout: 300000,
// Don't allow parallel runs
allowConcurrent: false,
tags: ['email', 'daily'],
priority: 10,
});Scheduler Events
typescript
scheduler.on('job:start', ({ jobId, runId }) => {
console.log(`Job ${jobId} started (run: ${runId})`);
});
scheduler.on('job:complete', ({ jobId, runId, result, duration }) => {
console.log(`Job ${jobId} completed in ${duration}ms`);
});
scheduler.on('job:error', ({ jobId, runId, error, attempt, willRetry }) => {
console.error(`Job ${jobId} failed:`, error);
if (willRetry) {
console.log(`Will retry (attempt ${attempt})`);
}
});
scheduler.on('job:retry', ({ jobId, attempt, delay }) => {
console.log(`Retrying job ${jobId} in ${delay}ms (attempt ${attempt})`);
});
scheduler.on('scheduler:start', () => {
console.log('Scheduler started');
});
scheduler.on('scheduler:stop', () => {
console.log('Scheduler stopped');
});Managing Jobs
List Jobs
typescript
const jobs = scheduler.listJobs();
// Returns array of job configs
const activeJobs = scheduler.listJobs({ enabled: true });
const emailJobs = scheduler.listJobs({ tags: ['email'] });Get Job Status
typescript
const status = scheduler.getJobStatus('daily-report');
// {
// id: 'daily-report',
// enabled: true,
// lastRun: Date,
// lastResult: 'success' | 'failure',
// nextRun: Date,
// runCount: 42,
// failureCount: 2,
// }Update Job
typescript
// Disable a job
scheduler.updateJob('daily-report', { enabled: false });
// Change schedule
scheduler.updateJob('daily-report', { schedule: '0 10 * * *' });Remove Job
typescript
scheduler.removeJob('daily-report');Cron Parser
Use the parser directly for validation:
typescript
import { parseCron, getNextRun, isValidCron } from '@aiversum/aiv-agents/cron';
// Validate expression
const valid = isValidCron('0 9 * * MON');
console.log(valid); // true
// Parse expression
const parsed = parseCron('0 9 * * MON');
console.log(parsed);
// {
// minute: [0],
// hour: [9],
// dayOfMonth: [1-31],
// month: [1-12],
// dayOfWeek: [1], // Monday
// }
// Get next run time
const next = getNextRun('0 9 * * MON');
console.log(next); // Date object for next Monday 9:00Execution History
typescript
// Get recent executions
const history = scheduler.getHistory('daily-report', { limit: 10 });
// Each entry:
// {
// runId: string,
// startTime: Date,
// endTime: Date,
// duration: number,
// result: 'success' | 'failure',
// error?: string,
// attempt: number,
// }Best Practices
1. Use Meaningful IDs
typescript
// Good
scheduler.addJob({ id: 'send-weekly-report', ... });
scheduler.addJob({ id: 'cleanup-temp-files', ... });
// Bad
scheduler.addJob({ id: 'job1', ... });
scheduler.addJob({ id: 'cron', ... });2. Handle Errors Gracefully
typescript
scheduler.addJob({
id: 'important-job',
schedule: '0 * * * *',
handler: async () => {
try {
await riskyOperation();
} catch (error) {
// Log and optionally notify
console.error('Job failed:', error);
await sendAlert(error);
throw error; // Re-throw to trigger retry
}
},
retries: 3,
});3. Use Timeouts
typescript
scheduler.addJob({
id: 'api-call',
schedule: '*/5 * * * *',
timeout: 30000, // 30 seconds max
handler: async () => {
// Will be cancelled if takes > 30s
await longRunningApiCall();
},
});4. Avoid Concurrent Runs (When Needed)
typescript
scheduler.addJob({
id: 'database-backup',
schedule: '0 2 * * *',
allowConcurrent: false, // Prevent overlapping runs
handler: async () => {
await performBackup();
},
});API Reference
See the Scheduler API Reference for complete documentation.
Testing
bash
# Run cron module tests
npm test -- --grep "cron"
# Test count: 86 tests