@jamx-framework/scheduler
v1.0.0
Published
JAMX Framework — Cron job scheduler
Downloads
130
Maintainers
Readme
@jamx-framework/scheduler
Descripción
Sistema de programación de tareas (scheduler) para JAMX Framework. Permite ejecutar tareas programadas usando expresiones cron o intervalos de tiempo fijos. Ideal para jobs de mantenimiento, limpieza, reportes, sincronización de datos, y cualquier proceso que necesite ejecutarse automáticamente en segundo plano.
Cómo funciona
El scheduler soporta dos tipos de tareas:
- Cron tasks: Se ejecutan según expresiones cron (ej:
"0 0 * * *"para diario a medianoche) - Interval tasks: Se ejecutan cada N milisegundos (ej: cada 5 minutos)
El scheduler mantiene un ticker interno que verifica cada segundo si alguna tarea cron debe ejecutarse, y usa setInterval para tareas con intervalo fijo.
Componentes principales
Scheduler (src/scheduler.ts)
Clase principal para gestionar tareas programadas:
add(definition): Registra una nueva tarearemove(name): Elimina una tareastart(): Inicia la ejecución de tareasstop(): Detiene todas las tareasrun(name): Ejecuta una tarea manualmentestatusAll(): Retorna estado de todas las tareastaskStatus(name): Retorna estado de una tarea específica
Cron Parser (src/cron-parser.ts)
Parseo y evaluación de expresiones cron:
parseCron(expression): Convierte string cron aCronExpressionmatchesCron(expr, date): Verifica si una fecha coincide con la expresiónnextMatch(expr, from): Calcula próxima ejecución
Uso básico
Crear un scheduler
import { Scheduler } from '@jamx-framework/scheduler';
const scheduler = new Scheduler();Añadir tareas cron
// Tarea que se ejecuta cada día a las 3:00 AM
scheduler.add({
name: 'daily-cleanup',
cron: '0 3 * * *',
handler: async () => {
console.log('Running daily cleanup...');
await cleanupOldRecords();
},
onError: (err) => {
console.error('Cleanup failed:', err);
},
runOnInit: false, // no ejecutar al iniciar
});
// Tarea cada lunes a las 8:00 AM
scheduler.add({
name: 'weekly-report',
cron: '0 8 * * 1', // 1 = lunes
handler: async () => {
await generateWeeklyReport();
},
});
// Usar alias
scheduler.add({
name: 'midnight-task',
cron: '@daily', // equivalente a '0 0 * * *'
handler: () => {
console.log('Running at midnight');
},
});Añadir tareas de intervalo
// Cada 5 minutos
scheduler.add({
name: 'cache-refresh',
every: 5 * 60 * 1000, // 5 minutos en ms
handler: async () => {
await refreshCache();
},
onError: (err) => {
console.error('Cache refresh failed:', err);
},
});
// Cada 30 segundos
scheduler.add({
name: 'health-check',
every: 30_000,
handler: async () => {
const healthy = await checkHealth();
if (!healthy) {
await sendAlert();
}
},
runOnInit: true, // ejecutar inmediatamente al iniciar
});Iniciar y detener
// Iniciar scheduler
scheduler.start();
console.log('Scheduler started');
// Detener scheduler
await scheduler.stop();
console.log('Scheduler stopped');Ejecutar tareas manualmente
// Ejecutar una tarea específica
await scheduler.run('daily-cleanup');
// Ver estado de todas las tareas
const allStatus = scheduler.statusAll();
console.log(allStatus);
/*
[
{
name: 'daily-cleanup',
lastRun: 1700000000000,
nextRun: 1700086400000,
runs: 5,
errors: 0,
},
...
]
*/
// Ver estado de una tarea
const status = scheduler.taskStatus('cache-refresh');
console.log(status);
/*
{
name: 'cache-refresh',
lastRun: 1700000000000,
nextRun: 170000300000,
runs: 100,
errors: 2,
}
*/Eliminar tareas
const removed = scheduler.remove('old-task');
if (removed) {
console.log('Task removed');
}API Reference
Tipos
TaskHandler
type TaskHandler = () => void | Promise<void>;Función que se ejecuta cuando la tarea dispara.
CronTask
interface CronTask {
name: string;
cron: string;
handler: TaskHandler;
onError?: (err: unknown) => void;
runOnInit?: boolean;
}Tarea basada en expresión cron.
IntervalTask
interface IntervalTask {
name: string;
every: number; // milisegundos
handler: TaskHandler;
onError?: (err: unknown) => void;
runOnInit?: boolean;
}Tarea basada en intervalo.
TaskDefinition
type TaskDefinition = CronTask | IntervalTask;Definición de tarea (cron o intervalo).
TaskStatus
interface TaskStatus {
name: string;
lastRun: number | null; // timestamp de última ejecución
nextRun: number | null; // timestamp de próxima ejecución
runs: number; // número de ejecuciones
errors: number; // número de errores
}Estado de una tarea.
SchedulerStatus
type SchedulerStatus = "idle" | "running" | "stopped";Estado del scheduler.
Clase Scheduler
Constructor
new Scheduler()Crea un scheduler vacío.
add()
add(definition: TaskDefinition): thisRegistra una nueva tarea. Lanza error si ya existe una tarea con el mismo nombre.
Ejemplo:
scheduler.add({
name: 'my-task',
cron: '0 * * * *', // cada hora
handler: async () => {
await doSomething();
},
});remove()
remove(name: string): booleanElimina una tarea. Retorna true si la tarea existía y fue eliminada, false si no existía.
start()
start(): voidInicia el scheduler:
- Ejecuta todas las tareas con
runOnInit: true - Inicia ticker cada segundo para tareas cron
- Inicia intervalos para tareas de intervalo
Nota: Si el scheduler ya está running, no hace nada.
stop()
stop(): voidDetiene el scheduler:
- Cancela ticker de cron
- Cancela todos los intervalos
- Cambia estado a "stopped"
run()
async run(name: string): Promise<void>Ejecuta una tarea manualmente por nombre. Lanza error si la tarea no existe.
statusAll()
statusAll(): TaskStatus[]Retorna array con el estado de todas las tareas registradas.
taskStatus()
taskStatus(name: string): TaskStatus | nullRetorna el estado de una tarea específica, o null si no existe.
isRunning
readonly isRunning: booleantrue si el scheduler está en ejecución.
Funciones de Cron Parser
parseCron()
function parseCron(expression: string): CronExpressionParsea una expresión cron y retorna un objeto CronExpression con arrays de valores para cada campo.
Formato cron: minute hour day-of-month month day-of-week
Ejemplos:
parseCron('0 0 * * *'); // cada día a medianoche
parseCron('*/5 * * * *'); // cada 5 minutos
parseCron('0 9-17 * * 1-5'); // cada hora de 9 a 17, de lunes a viernes
parseCron('@daily'); // alias para '0 0 * * *'CronExpression:
interface CronExpression {
minutes: number[]; // 0-59
hours: number[]; // 0-23
daysOfMonth: number[]; // 1-31
months: number[]; // 1-12
daysOfWeek: number[]; // 0-7 (0 y 7 = domingo)
}matchesCron()
function matchesCron(expr: CronExpression, date: Date): booleanVerifica si una fecha específica coincide con la expresión cron.
Ejemplo:
const expr = parseCron('0 0 * * *'); // medianoche
const now = new Date('2024-01-15T00:00:00');
matchesCron(expr, now); // truenextMatch()
function nextMatch(expr: CronExpression, from: Date): DateCalcula la próxima fecha/hora que coincide con la expresión cron, a partir de from.
Ejemplo:
const expr = parseCron('0 0 * * *'); // medianoche
const now = new Date('2024-01-15T14:30:00');
const next = nextMatch(expr, now); // 2024-01-16T00:00:00Expresiones Cron
Formato estándar
* * * * *
│ │ │ │ │
│ │ │ │ └─ Día de la semana (0-7, 0 y 7 = domingo)
│ │ │ └─── Mes (1-12)
│ │ └───── Día del mes (1-31)
│ └─────── Hora (0-23)
└───────── Minuto (0-59)Comodines y rangos
*: Cualquier valor*/n: Cada n unidades (ej:*/5en minutos = cada 5 minutos)a-b: Rango (ej:9-17= de 9 a 17)a,b,c: Lista (ej:1,15,30= días 1, 15 y 30)*/n+a-b: Combinación (ej:*/2 9-17 * * 1-5)
Ejemplos comunes
| Expresión | Descripción |
|-----------|-------------|
| 0 0 * * * | Diario a medianoche |
| 0 3 * * * | Diario a las 3:00 AM |
| 0 0 * * 0 | Cada domingo a medianoche |
| 0 9 * * 1 | Cada lunes a las 9:00 AM |
| */15 * * * * | Cada 15 minutos |
| 0 */2 * * * | Cada 2 horas |
| 0 0 1 * * | El primer día de cada mes |
| 0 0 1 1 * | El 1 de enero (Año Nuevo) |
| @hourly | Cada hora (alias) |
| @daily | Diario a medianoche (alias) |
| @weekly | Semanalmente el domingo a medianoche (alias) |
| @monthly | Mensualmente el día 1 a medianoche (alias) |
| @yearly | Anualmente el 1 de enero a medianoche (alias) |
Días de la semana
0o7= Domingo1= Lunes2= Martes3= Miércoles4= Jueves5= Viernes6= Sábado
Meses
1= Enero2= Febrero- ...
12= Diciembre
Flujo interno
Inicialización
const scheduler = new Scheduler();
scheduler.add({ name: 'task1', cron: '0 0 * * *', handler: () => {} });
scheduler.start();Dentro de start():
start() {
this.status = "running";
// 1. Ejecutar tareas con runOnInit
for (const task of this.tasks.values()) {
if (task.runOnInit) {
void this.runTask(task);
}
}
// 2. Iniciar ticker para cron (cada segundo)
this.tickInterval = setInterval(() => this.tick(), 1000);
// 3. Iniciar intervalos para tareas de intervalo
for (const task of this.tasks.values()) {
if (task.every !== undefined) {
task.timer = setInterval(() => void this.runTask(task), task.every);
}
}
}Ticker de cron
private tick(): void {
const now = Date.now();
for (const task of this.tasks.values()) {
// Solo tareas cron
if (!task.cronExpr) continue;
if (!task.nextRun) continue;
if (now < task.nextRun) continue;
// Ejecutar tarea
void this.runTask(task);
// Calcular próxima ejecución
task.nextRun = nextMatch(task.cronExpr, new Date()).getTime();
}
}Ejecución de tarea
private async runTask(task: InternalTask): Promise<void> {
task.lastRun = Date.now();
task.runs++;
try {
await task.handler();
} catch (err) {
task.errors++;
if (task.onError) {
task.onError(err);
}
}
}Ejemplos completos
Sistema de backup diario
import { Scheduler } from '@jamx-framework/scheduler';
import { backupDatabase } from './services/backup.js';
import { uploadToS3 } from './services/s3.js';
const scheduler = new Scheduler();
scheduler.add({
name: 'daily-backup',
cron: '0 2 * * *', // 2:00 AM cada día
handler: async () => {
console.log('Starting backup...');
const backup = await backupDatabase();
await uploadToS3(backup, `backup-${Date.now()}.sql`);
console.log('Backup completed');
},
onError: (err) => {
console.error('Backup failed:', err);
// Enviar alerta
sendAlert('Backup failed', String(err));
},
runOnInit: false,
});
scheduler.start();Health check cada minuto
import { Scheduler } from '@jamx-framework/scheduler';
import { checkDatabase, checkRedis, checkExternalAPI } from './health.js';
scheduler.add({
name: 'health-check',
every: 60_000, // cada minuto
handler: async () => {
const checks = await Promise.all([
checkDatabase(),
checkRedis(),
checkExternalAPI(),
]);
const allHealthy = checks.every(c => c.healthy);
if (!allHealthy) {
await sendSlackAlert('Health check failed!');
}
},
onError: (err) => {
console.error('Health check error:', err);
},
runOnInit: true, // ejecutar inmediatamente
});
scheduler.start();Procesador de cola con intervalo
import { Scheduler } from '@jamx-framework/scheduler';
import { queue } from './queue.js';
scheduler.add({
name: 'queue-worker',
every: 5_000, // cada 5 segundos
handler: async () => {
// Procesar hasta 10 jobs a la vez
for (let i = 0; i < 10; i++) {
const job = await queue.dequeue();
if (!job) break;
try {
await processJob(job);
} catch (err) {
await queue.fail(job.id, err);
}
}
},
runOnInit: true,
});
scheduler.start();Reporte semanal
import { Scheduler } from '@jamx-framework/scheduler';
import { generateWeeklyReport } from './reports.js';
import { sendEmail } from './mailer.js';
scheduler.add({
name: 'weekly-report',
cron: '0 8 * * 1', // Lunes 8:00 AM
handler: async () => {
const report = await generateWeeklyReport();
await sendEmail({
to: '[email protected]',
subject: 'Weekly Report',
html: report,
});
},
onError: (err) => {
console.error('Weekly report failed:', err);
},
});
scheduler.start();Limpieza de sesiones expiradas
import { Scheduler } from '@jamx-framework/scheduler';
import { db } from './db.js';
scheduler.add({
name: 'cleanup-sessions',
cron: '0 */6 * * *', // cada 6 horas
handler: async () => {
const deleted = await db.sessions.deleteWhere({
expiresAt: { $lt: Date.now() },
});
console.log(`Deleted ${deleted} expired sessions`);
},
});
scheduler.start();Múltiples tareas con configuración centralizada
import { Scheduler } from '@jamx-framework/scheduler';
const tasks = [
{
name: 'cache-warmup',
cron: '*/10 * * * *', // cada 10 minutos
handler: () => import('./services/cache-warmup.js').then(m => m.warmup()),
},
{
name: 'metrics-aggregation',
cron: '0 * * * *', // cada hora
handler: () => import('./services/metrics.js').then(m => m.aggregate()),
},
{
name: 'log-rotation',
cron: '0 0 * * *', // medianoche
handler: () => import('./services/logs.js').then(m => m.rotate()),
},
];
const scheduler = new Scheduler();
for (const task of tasks) {
scheduler.add({
...task,
onError: (err) => {
console.error(`Task ${task.name} failed:`, err);
},
});
}
scheduler.start();Consideraciones de rendimiento
Ticker de cron
- El ticker corre cada segundo (1000ms)
- Para la mayoría de casos esto es suficiente precisión
- Si necesitas precisión de milisegundos, usa interval tasks
Interval tasks
- Usan
setIntervalnativo de Node.js - No acumulan retraso (Node.js los ejecuta puntualmente)
- Si el handler tarda más que el intervalo, se superponen ejecuciones
Manejo de errores
- Los errores en handlers no detienen el scheduler
- Se capturan y se incrementa el contador
errors onErrorpermite logging o alertas personalizadas
Memoria
- El scheduler mantiene un
Mapde tareas en memoria - No hay persistencia; al reiniciar el proceso se pierden los timers
- Considera usar
runOnInit: truepara tareas críticas
Timezone
- Las expresiones cron usan el timezone del sistema (Node.js)
- Para timezone específico, ajusta la fecha en
nextMatch()o usaprocess.TZ
Testing
Tests unitarios
import { Scheduler, parseCron, matchesCron, nextMatch } from '@jamx-framework/scheduler';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
describe('Scheduler', () => {
let scheduler: Scheduler;
beforeEach(() => {
scheduler = new Scheduler();
});
it('should add and remove tasks', () => {
scheduler.add({
name: 'test-task',
cron: '0 * * * *',
handler: () => {},
});
expect(scheduler.taskStatus('test-task')).not.toBeNull();
scheduler.remove('test-task');
expect(scheduler.taskStatus('test-task')).toBeNull();
});
it('should start and stop', () => {
expect(scheduler.isRunning).toBe(false);
scheduler.start();
expect(scheduler.isRunning).toBe(true);
scheduler.stop();
expect(scheduler.isRunning).toBe(false);
});
it('should track task status', async () => {
let callCount = 0;
scheduler.add({
name: 'counter',
every: 1000,
handler: () => {
callCount++;
},
runOnInit: true,
});
scheduler.start();
await new Promise(r => setTimeout(r, 2500));
scheduler.stop();
expect(callCount).toBeGreaterThanOrEqual(2); // init + al menos 1 intervalo
});
});
describe('Cron Parser', () => {
it('should parse simple cron', () => {
const expr = parseCron('0 0 * * *');
expect(expr.minutes).toEqual([0]);
expect(expr.hours).toEqual([0]);
expect(expr.daysOfMonth).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]);
expect(expr.months).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
expect(expr.daysOfWeek).toEqual([0, 1, 2, 3, 4, 5, 6]);
});
it('should parse ranges', () => {
const expr = parseCron('0 9-17 * * 1-5');
expect(expr.hours).toEqual([9, 10, 11, 12, 13, 14, 15, 16, 17]);
expect(expr.daysOfWeek).toEqual([1, 2, 3, 4, 5]);
});
it('should parse step values', () => {
const expr = parseCron('*/5 * * * *');
expect(expr.minutes).toEqual([0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55]);
});
it('should support aliases', () => {
const expr = parseCron('@daily');
expect(expr.minutes).toEqual([0]);
expect(expr.hours).toEqual([0]);
});
it('should calculate next match', () => {
const expr = parseCron('0 0 * * *'); // medianoche
const from = new Date('2024-01-15T14:30:00');
const next = nextMatch(expr, from);
expect(next.getHours()).toBe(0);
expect(next.getMinutes()).toBe(0);
expect(next.getDate()).toBe(16); // siguiente día
});
});Mock de tiempo en tests
import { vi } from 'vitest';
// Mock de timers
vi.useFakeTimers();
const scheduler = new Scheduler();
scheduler.add({
name: 'fast-task',
every: 1000,
handler: vi.fn(),
});
scheduler.start();
// Avanzar tiempo 3 segundos
vi.advanceTimersByTime(3000);
expect(scheduler.taskStatus('fast-task')?.runs).toBe(3);
scheduler.stop();
vi.useRealTimers();Buenas prácticas
1. Nombres descriptivos
// ❌ Mal
scheduler.add({ name: 'task1', cron: '0 0 * * *', handler: doStuff });
// ✅ Bien
scheduler.add({
name: 'daily-user-cleanup',
cron: '0 3 * * *',
handler: cleanupInactiveUsers,
});2. Manejo de errores
scheduler.add({
name: 'critical-task',
cron: '0 * * * *',
handler: async () => {
try {
await doCriticalWork();
} catch (err) {
// Log y alerta
logger.error('Critical task failed', err);
await sendAlert('Critical task failed', err);
throw err; // para que onError también lo capture
}
},
onError: (err) => {
logger.error('Task error:', err);
},
});3. Tiempos de ejecución
// Asegurar que el handler no se superponga
scheduler.add({
name: 'long-task',
every: 5 * 60 * 1000, // cada 5 minutos
handler: async () => {
// Si la tarea puede tardar más de 5 min, usar lock
const lock = await acquireLock('long-task');
if (!lock) {
console.log('Skipping: previous execution still running');
return;
}
try {
await doLongRunningWork();
} finally {
await lock.release();
}
},
});4. Graceful shutdown
import { Scheduler } from '@jamx-framework/scheduler';
const scheduler = new Scheduler();
// ... añadir tareas
// Manejar señales de terminación
process.on('SIGTERM', async () => {
console.log('Received SIGTERM, stopping scheduler...');
await scheduler.stop();
process.exit(0);
});
process.on('SIGINT', async () => {
console.log('Received SIGINT, stopping scheduler...');
await scheduler.stop();
process.exit(0);
});
scheduler.start();5. Monitoreo
// Exponer métricas
import { Metrics } from '@jamx-framework/metrics';
scheduler.add({
name: 'data-sync',
cron: '0 */6 * * *',
handler: async () => {
const start = Date.now();
try {
await syncData();
Metrics.increment('scheduler.task.success', { task: 'data-sync' });
} catch (err) {
Metrics.increment('scheduler.task.error', { task: 'data-sync' });
throw err;
} finally {
const duration = Date.now() - start;
Metrics.histogram('scheduler.task.duration', duration, { task: 'data-sync' });
}
},
});Limitaciones
Precisión de tiempo
- Cron tasks: precisión de 1 segundo (ticker cada segundo)
- Interval tasks: precisión de ~1ms (depende de event loop de Node.js)
- No garantía de ejecución exacta si el event loop está bloqueado
Timezone
- Usa el timezone del sistema operativo
- No soporta timezone específico por tarea
- Para timezone diferente, ajusta manualmente en el handler
Persistencia
- No persiste estado entre reinicios
- No hay historial de ejecuciones (solo contadores)
- Considera guardar estado en DB si necesitas persistencia
Concurrencia
- Tareas cron e interval se ejecutan concurrentemente
- Si un handler tarda mucho, puede superponerse con la siguiente ejecución
- Usa locks si necesitas exclusión mutua
Número de tareas
- No hay límite teórico, pero miles de tareas pueden afectar rendimiento
- Cada tarea cron revisa condición cada segundo
- Considera agrupar tareas relacionadas
Integración con otros paquetes
Con @jamx-framework/metrics
import { Metrics } from '@jamx-framework/metrics';
scheduler.add({
name: 'report-generation',
cron: '0 0 * * *',
handler: async () => {
const timer = Metrics.startTimer('scheduler.report.duration');
try {
await generateReport();
Metrics.increment('scheduler.report.success');
} catch (err) {
Metrics.increment('scheduler.report.error');
throw err;
} finally {
timer();
}
},
});Con @jamx-framework/queue
import { Queue } from '@jamx-framework/queue';
const queue = new Queue({ driver: 'redis' });
scheduler.add({
name: 'process-queue',
every: 10_000,
handler: async () => {
const job = await queue.dequeue('email');
if (job) {
await sendEmail(job.data);
}
},
});Con @jamx-framework/db
import { Database } from '@jamx-framework/db';
const db = new Database({ driver: 'postgresql', url: process.env.DATABASE_URL! });
scheduler.add({
name: 'vacuum-database',
cron: '0 3 * * 0', // Domingo 3:00 AM
handler: async () => {
await db.raw('VACUUM ANALYZE');
},
});Preguntas frecuentes
¿Cómo ejecutar una tarea solo una vez?
scheduler.add({
name: 'one-time-task',
cron: '0 0 * * *',
runOnInit: true, // se ejecuta al iniciar
handler: () => {
console.log('Running once');
},
});
scheduler.start();
// Después de la primera ejecución, puedes removerla:
// scheduler.remove('one-time-task');¿Cómo programar tareas dinámicamente?
function scheduleUserReminder(userId: string, when: Date) {
const taskName = `reminder-${userId}-${when.getTime()}`;
scheduler.add({
name: taskName,
cron: `${when.getMinutes()} ${when.getHours()} * * *`,
handler: async () => {
await sendReminder(userId);
scheduler.remove(taskName); // eliminar después de ejecutar
},
});
}¿Qué pasa si el servidor se reinicia?
- Todas las tareas se pierden (no hay persistencia)
- Solución: Usar
runOnInit: truepara tareas críticas, o re-registrarlas al startup
¿Puedo pausar una tarea sin eliminarla?
No directamente. Debes:
- Remover la tarea (
remove()) - Guardar su definición en otro lugar
- Re-añadirla después con
add()
¿Cómo manejar dependencias entre tareas?
scheduler.add({
name: 'task-a',
cron: '0 * * * *',
handler: async () => {
await doA();
},
});
scheduler.add({
name: 'task-b',
cron: '5 * * * *', // 5 minutos después de task-a
handler: async () => {
const status = scheduler.taskStatus('task-a');
if (status?.lastRun && status.lastRun > Date.now() - 10 * 60 * 1000) {
await doB();
} else {
console.log('Skipping task-b: task-a did not run recently');
}
},
});¿Cómo limitar la concurrencia?
import { Semaphore } from '@jamx-framework/core';
const semaphore = new Semaphore(2); // máximo 2 concurrentes
scheduler.add({
name: 'concurrent-task',
every: 1000,
handler: async () => {
if (!await semaphore.tryAcquire()) {
console.log('Skipping: too many concurrent executions');
return;
}
try {
await doWork();
} finally {
semaphore.release();
}
},
});Referencia rápida
Crear scheduler
const scheduler = new Scheduler();Añadir tarea cron
scheduler.add({
name: 'task-name',
cron: '0 0 * * *',
handler: async () => { /* ... */ },
});Añadir tarea intervalo
scheduler.add({
name: 'task-name',
every: 60_000, // ms
handler: async () => { /* ... */ },
});Control
scheduler.start();
await scheduler.stop();Estado
scheduler.statusAll(); // todas las tareas
scheduler.taskStatus('name'); // tarea específica
scheduler.isRunning; // booleanEjecución manual
await scheduler.run('task-name');Eliminar
scheduler.remove('task-name');Archivos importantes
src/scheduler.ts- Clase Scheduler principalsrc/cron-parser.ts- Parser de expresiones crontests/unit/cron-parser.test.ts- Tests del parsertests/unit/scheduler.test.ts- Tests del scheduler
Dependencias
@types/node- Tipos de Node.jsvitest- Framework de testingrimraf- Limpieza de directorios
Scripts del paquete
pnpm build- Compila TypeScript a JavaScriptpnpm dev- Compilación en watch modepnpm test- Ejecuta tests unitariospnpm test:watch- Tests en watch modepnpm type-check- Verifica tipos sin compilarpnpm clean- Limpia archivos compilados
