tests — events

Module: tests-events Cohesion: 0.80 Members: 0

tests — events

This document describes the core components of the src/events module, as revealed and validated by the tests/events/event-bus.test.ts test suite. This module provides a robust, type-safe eventing system designed for internal application communication, offering features like event history, statistics, listener prioritization, and filtering.

Overview

The src/events module provides a flexible and type-safe way to manage events within the application. It centers around the TypedEventEmitter class, which offers enhanced capabilities over standard Node.js EventEmitters, including strong typing for event names and payloads, event history, and performance statistics. A global EventBus is provided for application-wide communication, and utility type guards help with runtime event type narrowing.

Core Concepts

Key Components

1. TypedEventEmitter

The TypedEventEmitter is the foundational class for event management. It's a generic class where TEventMap is an interface mapping event names (strings) to their corresponding event data types.

Features:

    type ToolEvents = {
      &#39;tool:started&#39;: { toolName: string; args: Record<string, any> };
      &#39;tool:completed&#39;: { toolName: string; duration: number };
    };
    const emitter = new TypedEventEmitter<ToolEvents>();

    emitter.on(&#39;tool:started&#39;, (event) => {
      class="hl-cmt">// event is correctly typed as { toolName: string; args: Record<string, any> }
      console.log(`Tool ${event.toolName} started.`);
    });
    emitter.emit(&#39;tool:completed&#39;, { toolName: &#39;search&#39;, duration: 100 });

2. FilteredEventEmitter

The FilteredEventEmitter provides a mechanism to create a specialized emitter that only processes events from a source TypedEventEmitter that match a specific type and an additional filter function.

const emitter = new TypedEventEmitter<ToolEvents>();
const bashToolEvents = emitter.filter(&#39;tool:started&#39;, (event) => event.toolName === &#39;bash&#39;);

bashToolEvents.on((event) => {
  class="hl-cmt">// This listener only receives &#39;tool:started&#39; events where toolName is &#39;bash&#39;
  console.log(`Bash tool started: ${event.args.command}`);
});

emitter.emit(&#39;tool:started&#39;, { toolName: &#39;bash&#39;, args: { command: &#39;ls&#39; } }); class="hl-cmt">// Received by bashToolEvents listener
emitter.emit(&#39;tool:started&#39;, { toolName: &#39;search&#39;, args: { query: &#39;foo&#39; } }); class="hl-cmt">// Not received

3. EventBus

The EventBus is a singleton instance of TypedEventEmitter, serving as the central hub for application-wide events.

4. TypedEventEmitterAdapter

This class acts as an adapter, wrapping a TypedEventEmitter instance and exposing both its type-safe API (onTyped, emitTyped, etc.) and the standard Node.js EventEmitter API (on, emit). This allows for interoperability with code that expects a native EventEmitter while still leveraging the type safety and advanced features of TypedEventEmitter internally.

5. Type Guards

The module provides several type guard functions to safely narrow down the type of a BaseEvent at runtime. These are crucial when working with AllEvents or BaseEvent unions, allowing you to access specific properties of an event without type assertions.

import { getGlobalEventBus, isToolEvent, AllEvents } from &#39;./events&#39;;

const bus = getGlobalEventBus();

bus.onAny((event: AllEvents) => {
  if (isToolEvent(event)) {
    class="hl-cmt">// event is now safely typed as ToolEvent
    console.log(`Tool event: ${event.toolName} - Type: ${event.type}`);
  } else {
    console.log(`Other event: ${event.type}`);
  }
});

Component Relationships

The following diagram illustrates the relationships between the main eventing components:

classDiagram
    class EventEmitter {
        +on(event, listener)
        +emit(event, ...args)
    }
    class TypedEventEmitter<T> {
        +on(type, listener, options)
        +emit(type, event)
        +getHistory()
        +getStats()
        +filter(type, filterFn) FilteredEventEmitter
        +waitFor(type, options) Promise<Event>
        +pipe(type, target)
        +dispose()
    }
    class FilteredEventEmitter<TEvent> {
        +on(listener)
        +once(listener)
        +off(id)
        +offAll()
    }
    class EventBus {
        +getInstance() EventBus
        +resetInstance()
    }
    class TypedEventEmitterAdapter<T> {
        +onTyped(type, listener)
        +emitTyped(type, event)
        +getTypedEmitter() TypedEventEmitter<T>
    }

    TypedEventEmitter <|-- EventBus : extends
    TypedEventEmitterAdapter o-- TypedEventEmitter : aggregates
    EventEmitter <|-- TypedEventEmitterAdapter : extends (conceptual)
    TypedEventEmitter ..> FilteredEventEmitter : creates

Usage Patterns and Best Practices