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

@fioc/event-bus

v1.0.4

Published

Type-safe event bus with CQRS support for FIoC. Build event-driven architectures with commands, queries, notifications, and middleware - fully integrated with dependency injection.

Downloads

18

Readme

@fioc/event-bus

FIoC Event Bus provides a comprehensive, type-safe event-driven architecture system built on top of @fioc/core.
It enables CQRS (Command Query Responsibility Segregation), domain events, and event-driven patterns with full dependency injection integration — completely reflection-free and decorator-free.

💡 Built for complex event-driven architectures, microservices, and domain-driven design — without reflection or decorators.


🚀 Quick Start

Install via npm, yarn, or pnpm:

npm install @fioc/event-bus @fioc/core

# or

yarn add @fioc/event-bus @fioc/core

# or

pnpm add @fioc/event-bus @fioc/core

📘 Table of Contents


🧩 Core Concepts

The Event Bus package introduces several key abstractions for building event-driven applications:

| Concept | Description | | ---------------------- | ------------------------------------------------------------------- | | INotification | Domain events that can have multiple handlers | | ICommand | Commands that perform actions (single handler) | | IQuery | Queries that retrieve data (single handler) | | IEventBus | Central service for publishing events and invoking commands/queries | | IHandlerMiddleware | Interceptors for cross-cutting concerns |

Everything is fully typed, DI-integrated, and metadata-aware — no decorators, no reflect-metadata.


🔔 Defining Events & Commands

Notifications (Domain Events)

import { createNotificationDIToken } from "@fioc/event-bus";

// Define notification payload type
export type UserRegisteredPayload = {
  userId: string;
  email: string;
  timestamp: Date;
};

export interface UserRegisteredNotification
  extends INotification<UserRegisteredPayload> {}

// Create notification token
export const UserRegisteredNotificationToken =
  createNotificationDIToken<UserRegisteredNotification>().as("UserRegistered", {
    generics: [
      /* Optional payload token */
    ],
  });

Commands

import { createCommandDIToken } from "@fioc/event-bus";

export type CreateUserCommandPayload = {
  email: string;
  password: string;
  name: string;
};

export interface CreateUserCommand
  extends ICommand<CreateUserCommandPayload, string> {}

export const CreateUserCommandToken =
  createCommandDIToken<CreateUserCommand>().as("CreateUser");

Queries

import { createQueryDIToken } from "@fioc/event-bus";

export type GetUserByIdQueryPayload = {
  userId: string;
};

export interface GetUserByIdQuery
  extends IQuery<GetUserByIdQueryPayload, User> {}

export const GetUserByIdQuery =
  createQueryDIToken<GetUserByIdQuery>().as("GetUserById");

🛠️ Creating Handlers

Notification Handlers (Multiple per Event)

import { createNotificationHandlerDIToken } from "@fioc/event-bus";
import { UserRegisteredNotification } from "./events";

export interface SendWelcomeEmailHandler
  extends INotificationHandler<UserRegisteredNotification> {}

export const SendWelcomeEmailHandlerToken =
  createNotificationHandlerDIToken().as("SendWelcomeEmail", {
    generics: [UserRegisteredNotificationToken], // Needs to specify the notification token
  });

// Implementation
const sendWelcomeEmailHandlerImpl: SendWelcomeEmailHandler = {
  async handle(payload) {
    await emailService.sendWelcomeEmail(event.payload.email);
  },
};

Command Handlers (Single per Command)

import { createCommandHandlerDIToken } from "@fioc/event-bus";
import { CreateUserCommand } from "./commands";

export interface CreateUserHandler extends ICommandHandler<CreateUserCommand> {}

export const CreateUserHandlerToken = createCommandHandlerDIToken().as(
  "CreateUserHandler",
  {
    generics: [CreateUserCommandToken], // Needs to specify the command token
  }
);

// Implementation
const createUserHandlerImpl: CreateUserHandler = {
  async handle(command: { payload: CreateUserCommandPayload }) {
    const user = await userService.createUser(command.payload);
    return user.id; // Must match command result type
  },
};

Query Handlers (Single per Query)

import { createQueryHandlerDIToken } from "@fioc/event-bus";
import { GetUserByIdQuery } from "./queries";

export interface GetUserByIdHandler extends IQueryHandler<GetUserByIdQuery> {}

export const GetUserByIdHandlerToken = createQueryHandlerDIToken().as(
  "GetUserByIdHandler",
  {
    generics: [GetUserByIdQueryToken], // Needs to specify the query token
  }
);

// Implementation
const getUserByIdHandlerImpl: GetUserByIdHandler = {
  async handle(query: { payload: GetUserByIdQueryPayload }) {
    return await userRepository.findById(query.payload.userId);
  },
};

📡 Using the Event Bus

Basic Setup

import { buildDIContainer } from "@fioc/core";
import { EventBusFactory, IEventBusToken } from "@fioc/event-bus";

const container = buildDIContainer()
  .registerFactory(IEventBusToken, EventBusFactory)
  .register(SendWelcomeEmailHandlerToken, sendWelcomeEmailHandler)
  .register(CreateUserHandlerToken, createUserHandler)
  .register(GetUserByIdHandlerToken, getUserByIdHandler)
  .getResult();

const eventBus = container.resolve(IEventBusToken);

Publishing Notifications

// Publish a domain event
await eventBus.publish({
  token: UserRegisteredNotification,
  payload: {
    userId: "123",
    email: "[email protected]",
    timestamp: new Date(),
  },
  createdAt: new Date(),
});

// With different execution strategies
await eventBus.publish(notification, "parallel"); // All handlers run concurrently
await eventBus.publish(notification, "sequential"); // Handlers run one after another
await eventBus.publish(notification, "besteffort"); // Parallel but continue on handler errors

Invoking Commands & Queries

// Invoke a command
const userId = await eventBus.invoke({
  token: CreateUserCommand,
  payload: {
    email: "[email protected]",
    password: "securepassword",
    name: "John Doe",
  },
  createdAt: new Date(),
});

// Invoke a query
const user = await eventBus.invoke({
  token: GetUserByIdQuery,
  payload: { userId: "123" },
  createdAt: new Date(),
});

🔌 Middleware & Interceptors

Middleware allows you to implement cross-cutting concerns like logging, validation, and error handling.

Creating Middleware

import { createMiddlewareDIToken, IQueryToken, ICommandToken } from "@fioc/event-bus";

export interface LoggingMiddleware extends IHandlerMiddleware<IQuery<any> | ICommand<any> ,unknown>

export const LoggingMiddleware = createMiddlewareDIToken().as(
  "LoggingMiddleware",
  {
    generics: [IQueryToken, ICommandToken], // Apply to all commands and queries
  }
);

const loggingMiddleware = {
  async handle(request, next: (request: any) => Promise<any>) {
    console.log("Processing request:", request.token.key);
    const startTime = Date.now();

    try {
      const result = await next(request);
      console.log("Request completed in", Date.now() - startTime, "ms");
      return result;
    } catch (error) {
      console.error("Request failed:", error);
      throw error;
    }
  },
};

Middleware Registration & Ordering

import { MiddleWareOrderToken } from "@fioc/event-bus";

const container = buildDIContainer()
  .register(MiddleWareOrderToken, [LoggingMiddleware, ValidationMiddleware]) // Middleware Order declaration
  .registerFactory(IEventBusToken, EventBusFactory)
  .register(LoggingMiddleware, loggingMiddleware)
  .register(ValidationMiddleware, validationMiddleware)
  .getResult();

Applying Middleware to Specific Event Types

// Apply to all notifications
const NotificationLoggingMiddleware = createMiddlewareDIToken().as(
  "NotificationLogging",
  {
    generics: [INotificationToken], // Apply to all notifications
  }
);

// Apply to specific notification
const UserEventMiddleware = createMiddlewareDIToken().as(
  "UserEventMiddleware",
  {
    generics: [UserRegisteredNotification], // Apply only to user registration
  }
);

// Apply to multiple types
const CommandQueryMiddleware = createMiddlewareDIToken().as(
  "CommandQueryMiddleware",
  {
    generics: [ICommandToken, IQueryToken], // Apply to all commands and queries
  }
);

⚡ Execution Strategies

The event bus supports different execution strategies for notification handlers:

Parallel (Default)

await eventBus.publish(notification, "parallel");
  • All handlers execute concurrently
  • Fastest execution time
  • Use when handlers are independent

Sequential

await eventBus.publish(notification, "sequential");
  • Handlers execute one after another
  • Guaranteed order of execution
  • Use when handlers have dependencies

Best Effort (Default)

const errors = await eventBus.publish(notification, "besteffort");
  • All handlers execute concurrently
  • Continues execution even if some handlers fail
  • Returns array of errors from failed handlers
  • Use for non-critical background tasks


📚 API Reference

Core Tokens

| Token | Description | | --------------------------- | ----------------------------------------- | | IEventBusToken | The main event bus service | | MiddleWareOrderToken | Array defining middleware execution order | | INotificationToken | Base token for all notifications | | ICommandToken | Base token for all commands | | IQueryToken | Base token for all queries | | INotificationHandlerToken | Base token for notification handlers | | ICommandHandlerToken | Base token for command handlers | | IQueryHandlerToken | Base token for query handlers | | IHandlerMiddlewareToken | Base token for middleware |

Factory Functions

| Function | Description | | ------------------------------------ | ----------------------------------- | | createNotificationDIToken() | Creates notification tokens | | createCommandDIToken() | Creates command tokens | | createQueryDIToken() | Creates query tokens | | createNotificationHandlerDIToken() | Creates notification handler tokens | | createCommandHandlerDIToken() | Creates command handler tokens | | createQueryHandlerDIToken() | Creates query handler tokens | | createMiddlewareDIToken() | Creates middleware tokens |

Interfaces

| Interface | Description | | -------------------------- | ------------------------------ | | IEventBus | Main event bus interface | | INotification<T> | Domain event interface | | ICommand<T, R> | Command interface | | IQuery<T, R> | Query interface | | INotificationHandler<T> | Notification handler interface | | ICommandHandler<T> | Command handler interface | | IQueryHandler<T> | Query handler interface | | IHandlerMiddleware<T, R> | Middleware interface |


🤝 Contributing

Contributions are welcome!
Open an issue or submit a PR on GitHub.

Please include tests for new features or fixes and keep commits focused.


📜 License

Licensed under the MIT License.