import type { Container } from '../../helpers/inversion/container'; import { BaseHelper } from '@venizia/ignis-helpers/core'; import type { IEventHandlerRetryOptions, TEventHandlerReference } from './common/types'; /** In-process, fire-and-forget domain event bus. `TPayloadMap` keys the event names, so a wrong name * or payload shape is a compile error. Delivery is always async. Retry timing is a property of the * handler's own failure mode, not of the event, so `register` may tune it per registration. But * nobody awaits a fire-and-forget dispatch, so an unbounded window would let one event hold a * handler - and whatever it holds open, e.g. a database connection - for an unbounded time with no * one to report it. What the bus guarantees instead: a bounded MAXIMUM time a single dispatch can * occupy, enforced as a hard ceiling at `register()` - see `EventDispatchRetry`. Retry makes * delivery at-least-once: handlers must be idempotent, see `IEventHandler`. */ export declare class EventBus extends BaseHelper { private readonly container; /** `register` appends, never replaces - two handlers may share an event name, and replacing would * drop the first with no way for the caller to notice. */ private readonly registrationsByName; constructor(opts: { scope?: string; container: Container; }); /** The pre-jitter total of every inter-attempt delay `maxAttempts`/`baseDelayMs` would produce - * jitter (always on, see `dispatch`) only ever shrinks a delay, so this is a true upper bound. */ private static computeTotalWindowMs; /** Loud rejection, never a silent clamp: a caller who believes a number the bus does not honour is * worse off than one who sees the ceiling at boot. */ private static assertValidRetry; /** * Turns `handler` into a uniform invoker plus a description for the exhaustion log line. * `BINDING_KEY` resolves through the container INSIDE the returned closure, so every call re-reads * the current binding; `FUNCTION` calls `fn` directly, so the closure IS the resolution - there is * nothing left to re-read on a later attempt. The tag (`handler.type`) is what this switches on, * not which field happens to be present - see `EventHandlerTypes`' doc. */ private static buildHandlerInvoker; /** * `retry` is optional and per REGISTRATION, not per publish: the right window belongs to the * handler's own failure mode, and two handlers on the same event name may each pick their own. * Omitting it reproduces today's `EventDispatchRetry` defaults exactly. Both `maxAttempts` and the * total backoff window it implies are bounded - see `EventDispatchRetry` - and exceeding either * throws here, at boot where a human sees it, rather than being silently clamped at dispatch time. */ register(opts: { name: K; /** A `BINDING_KEY` reference re-resolves through the container on every retry attempt, so a * rebind reaches a dispatch already in flight. A `FUNCTION` reference is captured as-is: a * rebind has nothing to reach, and every attempt in a long retry window calls the exact `fn` * registered here. */ handler: TEventHandlerReference; retry?: IEventHandlerRetryOptions; }): void; /** * Never blocks and never throws back to the caller: dispatch runs outside this call's flow via * `queueMicrotask`, and a handler failure is caught and logged where it happens (`dispatch`), * never here. */ publish(opts: { name: K; payload: TPayloadMap[K]; traceId?: string; }): void; /** * Attempts/backoff come from the registration (`EventDispatchRetry`'s defaults when omitted), * always with full jitter - `RetryJitterModes.FULL` decorrelates a shared-resource pile-up hardest, * so many handlers retrying the same lock do not collide on the same instant a second and third * time. `invokeHandler` already encodes whether that re-resolves (`BINDING_KEY`) or was captured * once (`FUNCTION`) - see `buildHandlerInvoker` - so this method calls it uniformly. A handler that * throws synchronously is caught exactly like one that returns a rejected promise: `execution` * here is itself `async`, and JS converts a synchronous throw inside an async function body into a * rejection of that function's own promise - `RetryHelper.executeWithRetry`'s attempt loop never * sees the difference. * * `RetryHelper.executeWithRetry` is used without its own `logger` - its internal per-attempt/exhaustion * logging knows nothing about the event name or `traceId`, so it cannot be the trace anyone * relies on. The one line below, logged only on final failure, is. */ private dispatch; } //# sourceMappingURL=event-bus.d.ts.map