npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@eyjs/event-bus

v1.0.0

Published

Event Bus system for EyJS framework

Downloads

67

Readme

@eyjs/event-bus

npm version License: MIT

Event Bus system for the EyJS framework. Provides domain-driven event handling, decorator-based event listeners, and seamless integration with EyJS applications using hexagonal architecture principles.

Features

  • 🎯 Domain-Driven Events - Clean separation of domain events and handlers
  • 🎨 Decorator-Based Handlers - Simple @EventHandler() decorator for event listeners
  • 🏗️ Hexagonal Architecture - Follows clean architecture principles
  • 🔧 TypeScript Support - Full TypeScript definitions and type safety
  • EyJS Integration - Works seamlessly with EyJS dependency injection
  • 📝 Event Middleware - Flexible middleware system for event processing
  • 🚀 In-Memory Publisher - Built-in in-memory event publisher
  • 🔄 Event Store - Centralized event storage and management

Installation

# Using Bun (recommended)
bun add @eyjs/event-bus

# Using npm
npm install @eyjs/event-bus

# Using yarn
yarn add @eyjs/event-bus

# Using pnpm
pnpm add @eyjs/event-bus

Prerequisites

This package requires:

  • @eyjs/core@^1.0.4 - EyJS core framework

Quick Start

1. Define Domain Events

// domain/events/user-created.event.ts
export class UserCreatedEvent {
  constructor(
    public readonly userId: string,
    public readonly email: string,
    public readonly name: string,
    public readonly timestamp: Date = new Date()
  ) {}
}

// domain/events/user-updated.event.ts
export class UserUpdatedEvent {
  constructor(
    public readonly userId: string,
    public readonly changes: Record<string, any>,
    public readonly timestamp: Date = new Date()
  ) {}
}

2. Create Event Handlers

import { EventHandler } from '@eyjs/event-bus'
import { Injectable } from '@eyjs/core'
import { UserCreatedEvent, UserUpdatedEvent } from '../domain/events'

@Injectable()
export class UserEventHandler {
  @EventHandler(UserCreatedEvent)
  async handleUserCreated(event: UserCreatedEvent) {
    console.log(`User created: ${event.email}`)
    // Send welcome email, create profile, etc.
  }

  @EventHandler(UserUpdatedEvent)
  async handleUserUpdated(event: UserUpdatedEvent) {
    console.log(`User updated: ${event.userId}`)
    // Update search index, notify followers, etc.
  }
}

3. Publish Events

import { EventBus } from '@eyjs/event-bus'
import { Injectable } from '@eyjs/core'
import { UserCreatedEvent } from '../domain/events'

@Injectable()
export class UserService {
  constructor(private readonly eventBus: EventBus) {}

  async createUser(data: { email: string; name: string }) {
    const user = await this.userRepository.create(data)
    
    // Publish domain event
    await this.eventBus.publish(new UserCreatedEvent(
      user.id,
      user.email,
      user.name
    ))
    
    return user
  }
}

API Reference

Core Classes

EventBus

Main event bus for publishing and handling events.

import { EventBus } from '@eyjs/event-bus'

const eventBus = new EventBus()

// Publish an event
await eventBus.publish(new UserCreatedEvent('123', '[email protected]', 'John'))

// Subscribe to events
eventBus.subscribe(UserCreatedEvent, (event) => {
  console.log('User created:', event.email)
})

@EventHandler(eventType)

Decorator for marking methods as event handlers.

@EventHandler(UserCreatedEvent)
async handleUserCreated(event: UserCreatedEvent) {
  // Handle the event
}

Event Publisher

InMemoryEventPublisher

Built-in in-memory event publisher.

import { InMemoryEventPublisher } from '@eyjs/event-bus'

const publisher = new InMemoryEventPublisher()
await publisher.publish(new UserCreatedEvent('123', '[email protected]', 'John'))

Usage Examples

Basic Event Handling

import { EventHandler, EventBus } from '@eyjs/event-bus'
import { Injectable } from '@eyjs/core'

// Domain Events
export class OrderCreatedEvent {
  constructor(
    public readonly orderId: string,
    public readonly customerId: string,
    public readonly total: number,
    public readonly timestamp: Date = new Date()
  ) {}
}

export class OrderCancelledEvent {
  constructor(
    public readonly orderId: string,
    public readonly reason: string,
    public readonly timestamp: Date = new Date()
  ) {}
}

// Event Handlers
@Injectable()
export class OrderEventHandler {
  @EventHandler(OrderCreatedEvent)
  async handleOrderCreated(event: OrderCreatedEvent) {
    console.log(`Order ${event.orderId} created for customer ${event.customerId}`)
    
    // Send confirmation email
    await this.emailService.sendOrderConfirmation(event.customerId, event.orderId)
    
    // Update inventory
    await this.inventoryService.reserveItems(event.orderId)
    
    // Notify warehouse
    await this.warehouseService.prepareOrder(event.orderId)
  }

  @EventHandler(OrderCancelledEvent)
  async handleOrderCancelled(event: OrderCancelledEvent) {
    console.log(`Order ${event.orderId} cancelled: ${event.reason}`)
    
    // Release inventory
    await this.inventoryService.releaseItems(event.orderId)
    
    // Process refund
    await this.paymentService.processRefund(event.orderId)
    
    // Notify customer
    await this.emailService.sendCancellationNotice(event.orderId)
  }
}

// Service using events
@Injectable()
export class OrderService {
  constructor(private readonly eventBus: EventBus) {}

  async createOrder(data: { customerId: string; items: any[] }) {
    const order = await this.orderRepository.create(data)
    
    // Publish domain event
    await this.eventBus.publish(new OrderCreatedEvent(
      order.id,
      order.customerId,
      order.total
    ))
    
    return order
  }

  async cancelOrder(orderId: string, reason: string) {
    await this.orderRepository.update(orderId, { status: 'cancelled' })
    
    // Publish domain event
    await this.eventBus.publish(new OrderCancelledEvent(orderId, reason))
  }
}

Multiple Event Handlers

import { EventHandler } from '@eyjs/event-bus'
import { Injectable } from '@eyjs/core'

@Injectable()
export class NotificationEventHandler {
  @EventHandler(UserCreatedEvent)
  async sendWelcomeEmail(event: UserCreatedEvent) {
    await this.emailService.sendWelcomeEmail(event.email)
  }

  @EventHandler(UserCreatedEvent)
  async createUserProfile(event: UserCreatedEvent) {
    await this.profileService.createProfile(event.userId)
  }

  @EventHandler(UserCreatedEvent)
  async addToNewsletter(event: UserCreatedEvent) {
    await this.newsletterService.subscribe(event.email)
  }
}

@Injectable()
export class AnalyticsEventHandler {
  @EventHandler(UserCreatedEvent)
  async trackUserRegistration(event: UserCreatedEvent) {
    await this.analyticsService.track('user_registered', {
      userId: event.userId,
      email: event.email,
      timestamp: event.timestamp
    })
  }
}

Event Middleware

import { EventMiddleware } from '@eyjs/event-bus'

// Logging middleware
export function loggingMiddleware() {
  return async (event: any, next: () => Promise<void>) => {
    console.log(`[${new Date().toISOString()}] Processing event: ${event.constructor.name}`)
    await next()
    console.log(`[${new Date().toISOString()}] Event processed: ${event.constructor.name}`)
  }
}

// Error handling middleware
export function errorHandlingMiddleware() {
  return async (event: any, next: () => Promise<void>) => {
    try {
      await next()
    } catch (error) {
      console.error(`Error processing event ${event.constructor.name}:`, error)
      // Could send to error tracking service
    }
  }
}

// Usage
@Injectable()
export class UserEventHandler {
  @EventHandler(UserCreatedEvent)
  @Use(loggingMiddleware())
  @Use(errorHandlingMiddleware())
  async handleUserCreated(event: UserCreatedEvent) {
    // Event handling logic
  }
}

Custom Event Publisher

import { EventPublisher } from '@eyjs/event-bus'

export class RedisEventPublisher implements EventPublisher {
  constructor(private readonly redis: Redis) {}

  async publish(event: any): Promise<void> {
    const channel = `events:${event.constructor.name}`
    await this.redis.publish(channel, JSON.stringify(event))
  }
}

// Usage
const redisPublisher = new RedisEventPublisher(redis)
const eventBus = new EventBus(redisPublisher)

Event Store Integration

import { EventStore } from '@eyjs/event-bus'

@Injectable()
export class EventStoreService {
  constructor(private readonly eventStore: EventStore) {}

  async saveEvent(event: any): Promise<void> {
    await this.eventStore.save({
      id: generateId(),
      type: event.constructor.name,
      data: event,
      timestamp: new Date(),
      version: 1
    })
  }

  async getEvents(aggregateId: string): Promise<any[]> {
    return await this.eventStore.getEvents(aggregateId)
  }
}

Advanced Usage

Event Sourcing

import { EventHandler, EventStore } from '@eyjs/event-bus'

@Injectable()
export class UserAggregate {
  constructor(private readonly eventStore: EventStore) {}

  async createUser(data: { email: string; name: string }) {
    const event = new UserCreatedEvent(
      generateId(),
      data.email,
      data.name
    )
    
    await this.eventStore.saveEvent(event)
    return event
  }

  async updateUser(userId: string, changes: Record<string, any>) {
    const event = new UserUpdatedEvent(userId, changes)
    await this.eventStore.saveEvent(event)
    return event
  }

  async getUser(userId: string) {
    const events = await this.eventStore.getEvents(userId)
    return this.replayEvents(events)
  }

  private replayEvents(events: any[]): User {
    let user = new User()
    
    for (const event of events) {
      switch (event.type) {
        case 'UserCreatedEvent':
          user = new User(event.data.userId, event.data.email, event.data.name)
          break
        case 'UserUpdatedEvent':
          Object.assign(user, event.data.changes)
          break
      }
    }
    
    return user
  }
}

Event Replay

import { EventBus, EventStore } from '@eyjs/event-bus'

@Injectable()
export class EventReplayService {
  constructor(
    private readonly eventBus: EventBus,
    private readonly eventStore: EventStore
  ) {}

  async replayEvents(fromDate: Date, toDate: Date) {
    const events = await this.eventStore.getEventsByDateRange(fromDate, toDate)
    
    for (const event of events) {
      await this.eventBus.publish(event.data)
    }
  }

  async replayEventsForAggregate(aggregateId: string) {
    const events = await this.eventStore.getEvents(aggregateId)
    
    for (const event of events) {
      await this.eventBus.publish(event.data)
    }
  }
}

Event Versioning

export class UserCreatedEventV1 {
  constructor(
    public readonly userId: string,
    public readonly email: string,
    public readonly name: string
  ) {}
}

export class UserCreatedEventV2 {
  constructor(
    public readonly userId: string,
    public readonly email: string,
    public readonly name: string,
    public readonly phoneNumber?: string,
    public readonly preferences?: Record<string, any>
  ) {}
}

@Injectable()
export class UserEventHandler {
  @EventHandler(UserCreatedEventV1)
  async handleUserCreatedV1(event: UserCreatedEventV1) {
    // Handle V1 events
  }

  @EventHandler(UserCreatedEventV2)
  async handleUserCreatedV2(event: UserCreatedEventV2) {
    // Handle V2 events with additional fields
  }
}

Configuration

Event Bus Configuration

import { EventBus, InMemoryEventPublisher } from '@eyjs/event-bus'

const eventBus = new EventBus({
  publisher: new InMemoryEventPublisher(),
  middleware: [
    loggingMiddleware(),
    errorHandlingMiddleware()
  ],
  retryPolicy: {
    maxRetries: 3,
    retryDelay: 1000
  }
})

Environment Configuration

# Event Bus Configuration
EVENT_BUS_TYPE=in-memory
EVENT_BUS_RETRY_ATTEMPTS=3
EVENT_BUS_RETRY_DELAY=1000

# Event Store Configuration
EVENT_STORE_TYPE=memory
EVENT_STORE_CONNECTION_STRING=redis://localhost:6379

Testing

Unit Tests

import { describe, it, expect, beforeEach } from 'bun:test'
import { EventBus, InMemoryEventPublisher } from '@eyjs/event-bus'
import { UserCreatedEvent } from '../domain/events'

describe('EventBus', () => {
  let eventBus: EventBus
  let publisher: InMemoryEventPublisher

  beforeEach(() => {
    publisher = new InMemoryEventPublisher()
    eventBus = new EventBus({ publisher })
  })

  it('should publish and handle events', async () => {
    let handledEvent: UserCreatedEvent | null = null

    eventBus.subscribe(UserCreatedEvent, (event) => {
      handledEvent = event
    })

    const event = new UserCreatedEvent('123', '[email protected]', 'John')
    await eventBus.publish(event)

    expect(handledEvent).not.toBeNull()
    expect(handledEvent?.userId).toBe('123')
    expect(handledEvent?.email).toBe('[email protected]')
  })
})

Integration Tests

import { describe, it, expect } from 'bun:test'

describe('Event Integration', () => {
  it('should handle user creation flow', async () => {
    const userService = container.get(UserService)
    const emailService = container.get(EmailService)

    // Mock email service
    const sendEmailSpy = spyOn(emailService, 'sendWelcomeEmail')

    // Create user (should trigger event)
    await userService.createUser({
      email: '[email protected]',
      name: 'Test User'
    })

    // Verify event was handled
    expect(sendEmailSpy).toHaveBeenCalledWith('[email protected]')
  })
})

Performance

Optimization Tips

  1. Use Async Event Handlers
@EventHandler(UserCreatedEvent)
async handleUserCreated(event: UserCreatedEvent) {
  // Async operations don't block other handlers
  await this.emailService.sendEmail(event.email)
}
  1. Batch Event Processing
@EventHandler(UserCreatedEvent)
async handleUserCreated(event: UserCreatedEvent) {
  // Add to batch instead of processing immediately
  this.batchProcessor.add(event)
}
  1. Event Filtering
@EventHandler(UserCreatedEvent)
async handleUserCreated(event: UserCreatedEvent) {
  // Only process events for specific conditions
  if (event.email.endsWith('@company.com')) {
    await this.processCompanyUser(event)
  }
}

Troubleshooting

Common Issues

1. Events not being handled

  • Ensure event handlers are registered with EyJS container
  • Check that @EventHandler() decorator is applied correctly
  • Verify event types match exactly

2. Circular dependencies

  • Use dependency injection properly
  • Avoid direct imports between event handlers
  • Consider using event middleware for cross-cutting concerns

3. Event ordering issues

  • Events are processed asynchronously by default
  • Use event middleware for ordering if needed
  • Consider using event sourcing for strict ordering

Debug Mode

Enable debug logging:

DEBUG=eyjs:event-bus

Contributing

Contributions are welcome! Please read our Contributing Guide for details.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support