JSPM

@jamx-framework/events

1.0.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 6
  • Score
    100M100P100Q65228F
  • License MIT

JAMX Framework — Typed event bus

Package Exports

  • @jamx-framework/events

Readme

@jamx-framework/events

Descripción

Sistema de eventos (Event Bus) para JAMX Framework. Proporciona un mecanismo de comunicación desacoplada entre componentes de la aplicación mediante publicación/suscripción (pub/sub). Permite que diferentes partes de la aplicación se comuniquen sin tener dependencias directas, ideal para arquitecturas modulares y microservicios.

Cómo funciona

El EventBus implementa el patrón pub/sub:

  1. Registro de listeners: Los componentes se suscriben a eventos específicos con un callback.
  2. Emisión de eventos: Cualquier componente puede publicar un evento en el bus.
  3. Distribución: El bus invoca todos los listeners registrados para ese tipo de evento.
  4. Filtrado: Soporta filtros por tipo de evento y datos adicionales.
  5. Async por defecto: Los listeners pueden ser síncronos o asíncronos (retornan Promise).

Componentes principales

  • src/event-bus.ts: Clase EventBus que gestiona suscripciones y emisión
  • src/index.ts: Punto de exportación

Uso básico

import { EventBus } from '@jamx-framework/events';

// Crear instancia global
const bus = new EventBus();

// Suscribirse a un evento
bus.on('user.created', (data) => {
  console.log('Usuario creado:', data.userId);
});

// Suscribirse una sola vez
bus.once('order.completed', (data) => {
  console.log('Orden completada:', data.orderId);
});

// Emitir evento
bus.emit('user.created', { userId: 123, email: 'user@example.com' });

// Emitir evento async
await bus.emitAsync('order.processing', { orderId: 456 });

Ejemplos

Comunicación entre módulos

// En módulo de usuarios
bus.emit('user.registered', { userId: 1, email: 'juan@example.com' });

// En módulo de notificaciones
bus.on('user.registered', async (data) => {
  await mailer.sendWelcomeEmail(data.email);
});

// En módulo de analytics
bus.on('user.registered', (data) => {
  analytics.track('signup', { userId: data.userId });
});

Eventos con datos estructurados

// Definir tipo para evento
interface UserCreatedEvent {
  userId: number;
  email: string;
  timestamp: Date;
}

// Emitir con tipo
bus.emit<UserCreatedEvent>('user.created', {
  userId: 1,
  email: 'juan@example.com',
  timestamp: new Date(),
});

// Escuchar con tipo
bus.on('user.created', (event) => {
  console.log(`Usuario ${event.userId} registrado a las ${event.timestamp}`);
});

Manejo de errores en listeners

bus.on('payment.processed', async (data) => {
  try {
    await processPayment(data);
  } catch (error) {
    console.error('Error procesando pago:', error);
    // Opcional: emitir evento de error
    bus.emit('payment.error', { orderId: data.orderId, error });
  }
});

Remover listeners

// Suscribirse y guardar referencia
const handler = (data) => console.log(data);
bus.on('user.updated', handler);

// Remover listener específico
bus.off('user.updated', handler);

// Remover todos los listeners de un evento
bus.off('user.updated');

// Remover todos los listeners de todos los eventos
bus.removeAllListeners();

Eventos con prioridad

// Añadir listener con prioridad (menor número = mayor prioridad)
bus.on('critical.event', (data) => {
  // Se ejecuta primero
}, { priority: 1 });

bus.on('critical.event', (data) => {
  // Se ejecuta después
}, { priority: 10 });

Eventos con wildcard

// Suscribirse a todos los eventos de usuarios
bus.on('user.*', (eventName, data) => {
  console.log(`Evento de usuario: ${eventName}`, data);
});

// Suscribirse a eventos de una jerarquía
bus.on('order.**', (eventName, data) => {
  // Captura order.created, order.updated, order.cancelled, etc.
});

Flujo interno

  1. Registro: on(event, callback, options?) añade el callback a un array de listeners para ese evento.
  2. Emisión: emit(event, data) recorre todos los listeners del evento y los ejecuta en orden de registro (o prioridad).
  3. Async: Si un listener retorna una Promise, se espera a que se resuelva (en emitAsync).
  4. Filtrado: Antes de ejecutar, se pueden aplicar filtros por tipo de evento o datos.
  5. Error handling: Los errores en listeners no detienen la ejecución de otros listeners (se capturan y se emiten opcionalmente en error event).
  6. Memory management: off() y removeAllListeners() permiten limpiar listeners para evitar memory leaks.

API Reference (Resumen)

EventBus

  • on(event: string, callback: Function, options?: ListenerOptions): () => void
  • once(event: string, callback: Function, options?: ListenerOptions): () => void
  • off(event: string, callback?: Function): void
  • emit(event: string, data?: any): void
  • emitAsync(event: string, data?: any): Promise<void>
  • removeAllListeners(event?: string): void
  • listenerCount(event: string): number
  • eventNames(): string[]

ListenerOptions

  • priority?: number — Orden de ejecución (default: 0)
  • once?: boolean — Se ejecuta solo una vez (alternativa a once())
  • async?: boolean — Si se debe esperar a que termine (default: true)

Performance Considerations

  • Listener lookup: Los listeners se almacenan en un Map por evento, O(1) para búsqueda.
  • Async execution: emitAsync espera todos los listeners; emit no espera.
  • Wildcard matching: Los wildcards (*, **) se evalúan con regex y pueden ser costosos con muchos listeners.
  • Memory leaks: Los listeners deben removerse explícitamente cuando el componente se destruye.

Testing

Tests en packages/events/tests/unit/event-bus.test.ts:

pnpm test

Cubre:

  • Registro y desregistración de listeners
  • Emisión síncrona y asíncrona
  • Eventos con wildcard
  • Prioridades de listeners
  • Manejo de errores
  • Conteo de listeners

Compatibility

  • Compatible con Node.js 18+
  • Funciona en navegadores (si se polyfill)
  • No requiere dependencias externas
  • Type-safe con genéricos

Use Cases

  • Comunicación entre módulos: Desacoplar módulos que necesitan comunicarse
  • Eventos de dominio: UserCreated, OrderCompleted, PaymentProcessed
  • Hooks del sistema: beforeStart, afterInit, onShutdown
  • Logging y auditoría: Capturar eventos para registro
  • Reacción a cambios: Actualizar UI cuando cambian datos

Best Practices

  1. Usar nombres de evento consistentes: module.action (ej: user.created, order.updated)
  2. Estructurar datos de eventos: Objetos con campos claros y tipados
  3. Limpiar listeners: En componentes con ciclo de vida, remover listeners al destruir
  4. No abusar: Para comunicación directa entre clases cercanas, usa inyección de dependencias
  5. Documentar eventos: Mantener un registro de eventos que emite cada módulo

This event bus provides a lightweight, type-safe pub/sub system for JAMX applications, enabling loose coupling and reactive architectures with minimal overhead.