Referencia: Async — Comandos, Cron y Transacciones
Comandos asíncronos
@command(name)
Decorador de clase. Registra una clase como comando ejecutable. La clase debe extender Storable<D>.
import { command, Storable } from '@wabot-dev/framework'
@command('enviar-email')export class EnviarEmailCommand extends Storable<{ destinatario: string asunto: string cuerpo: string}> {}name es el identificador del comando — debe ser único en la aplicación.
@commandHandler(CommandClass)
Decorador de clase. Registra un handler para procesar un tipo de comando.
import { commandHandler, ICommandHandler, injectable } from '@wabot-dev/framework'
@commandHandler(EnviarEmailCommand)export class EnviarEmailHandler implements ICommandHandler<EnviarEmailCommand> { async handle(cmd: EnviarEmailCommand) { await this.emailService.send({ to: cmd.data.destinatario, subject: cmd.data.asunto, body: cmd.data.cuerpo, }) }}ICommandHandler<C>:
interface ICommandHandler<C> { handle(command: C): void | Promise<void>}Async
Servicio singleton para despachar comandos.
import { Async, injectable } from '@wabot-dev/framework'
@injectable()export class NotificacionService { constructor(private async: Async) {}
async notificar(email: string) { // Ejecutar inmediatamente (en background) await this.async.runCommand(EnviarEmailCommand, { destinatario: email, asunto: 'Bienvenido', cuerpo: 'Gracias por registrarte.', }) }
async notificarLuego(email: string) { // Programar para más tarde await this.async.scheduleCommand( EnviarEmailCommand, { destinatario: email, asunto: 'Recordatorio', cuerpo: '...' }, { hours: 24 }, // en 24 horas ) }}| Método | Descripción |
|---|---|
runCommand(Clase, data) | Crea el job y lo intenta ejecutar de inmediato. |
scheduleCommand(Clase, data, cuando) | Crea el job para ejecutarse en un momento futuro. |
IScheduleAt — opciones de tiempo
type IScheduleAt = | Date // fecha exacta | { seconds: number } | { minutes: number } | { hours: number } | { days: number }runCommandHandlers(handlers, container?)
Inicia el procesador de jobs. Debe llamarse en el entry point de la aplicación.
import { runCommandHandlers } from '@wabot-dev/framework'
await runCommandHandlers([EnviarEmailHandler, ProcesarPagoHandler])Tareas cron
@cronHandler(config) / @cron(config)
Decorador de clase. Registra una tarea que se ejecuta según una expresión cron.
import { cronHandler, ICronHandler } from '@wabot-dev/framework'
@cronHandler({ name: 'limpieza-diaria', cron: '0 2 * * *' })export class LimpiezaDiariaHandler implements ICronHandler { async handle() { await this.servicio.limpiarRegistrosExpirados() }
async handleError(error: any) { // Opcional — manejo de errores del cron }}| Config | Tipo | Descripción |
|---|---|---|
name | string | Identificador único de la tarea. |
cron | string | Expresión cron (5 campos: min hora día mes día-semana). |
disabled | boolean | Si true, la tarea no se ejecuta (default: false). |
ICronHandler:
interface ICronHandler { handle(): void | Promise<void> handleError?(e: any): void | Promise<void>}Referencia rápida de expresiones cron
| Expresión | Cuándo |
|---|---|
* * * * * | Cada minuto |
*/5 * * * * | Cada 5 minutos |
0 * * * * | Cada hora |
0 9 * * * | Todos los días a las 9:00 AM |
0 2 * * * | Todos los días a las 2:00 AM |
0 9 * * 1 | Cada lunes a las 9:00 AM |
0 9 * * 1-5 | Lunes a viernes a las 9:00 AM |
0 0 1 * * | El primer día de cada mes |
runCronHandlers(handlers, container?)
Inicia el scheduler de tareas cron.
import { runCronHandlers } from '@wabot-dev/framework'
await runCronHandlers([LimpiezaDiariaHandler, ReporteSemanalhHandler])@transaction()
Decorador de método. Envuelve el método en una transacción de base de datos. Si el método lanza un error, se hace ROLLBACK de todas las operaciones.
import { transaction, injectable } from '@wabot-dev/framework'
@injectable()export class VentasService { @transaction() async cerrarVenta(prospectId: string, offerId: string) { // Si cualquier operación falla → ROLLBACK automático await this.prospectRepo.marcarGanado(prospectId) await this.offerRepo.marcarAceptada(offerId) await this.facturaRepo.crear({ prospectId, offerId }) }}Transacciones anidadas
Cuando un método @transaction() llama a otro método @transaction(), la interior usa un savepoint PostgreSQL en lugar de una nueva transacción:
@transaction()async operacionExterna() { await this.operacionInterior() // usa SAVEPOINT automáticamente await this.otraOperacion()}
@transaction()async operacionInterior() { }Configuración manual (sin ProjectRunner)
Si no usas el flujo estándar, registra el adaptador manualmente:
import { container, TransactionMetadataStore, PgTransactionAdapter } from '@wabot-dev/framework'import { Pool } from 'pg'
const store = container.resolve(TransactionMetadataStore)store.registerAdapter('default', new PgTransactionAdapter(new Pool({ connectionString: '...' })))Con el flujo estándar (runChatControllers + PostgreSQL configurado), el adaptador se registra automáticamente.