import type { z } from "zod"; import type { EventDescriptor } from "./event-descriptor"; import type { EventHandler, IEventBus } from "./event-bus.interface"; export type InMemoryEventBusOptions = { /** When true, rethrow the first handler error (default: false — errors swallowed). */ failFast?: boolean; }; export class InMemoryEventBus implements IEventBus { private readonly handlers = new Map[]>(); constructor(private readonly options: InMemoryEventBusOptions = {}) {} async publish( descriptor: EventDescriptor>, payload: T, ): Promise { descriptor.schema.parse(payload); const subscribers = this.handlers.get(descriptor.name) ?? []; if (subscribers.length === 0) return; const settled = await Promise.allSettled( subscribers.map((h) => h(payload)), ); if (this.options.failFast) { const failure = settled.find((s) => s.status === "rejected"); // Only the first rejection is rethrown. Other failures are intentionally // dropped — `failFast` is a test-affordance, not a fault-tolerance design. if (failure && failure.status === "rejected") throw failure.reason; } } subscribe( descriptor: EventDescriptor>, _consumerFeature: string, handler: EventHandler, ): void { const arr = this.handlers.get(descriptor.name) ?? []; arr.push(handler as EventHandler); this.handlers.set(descriptor.name, arr); } }