Skip to content

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étodoDescripció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
}
}
ConfigTipoDescripción
namestringIdentificador único de la tarea.
cronstringExpresión cron (5 campos: min hora día mes día-semana).
disabledbooleanSi 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ónCuá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 * * 1Cada lunes a las 9:00 AM
0 9 * * 1-5Lunes 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.