first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,124 @@
# `createInMemoryEventsBus`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`createInMemoryEventsBus` is the factory entry point of
`@suaveplan/api-events-in-memory`. It returns a fresh
{@link InMemoryEventsBus} whose `publish` calls are recorded in a
per-topic ring of payloads and whose subscriber delivery is delegated
to a private `@suaveplan/events` `EventBus`. Tests can both assert
"this event was published with this payload" (via `published(topic)`)
and assert "this subscriber observed the side-effect" (via the
subscriber callback) without any mocks or test doubles — the real
production bus runs underneath.
## Features
- Recording layer wraps every `publish` / `publishAsync` / `emit` call;
the payload is appended to the topic's history BEFORE dispatch so
history is still complete even if a subscriber throws.
- Strict-throw history-overflow policy via the cap-bounded
{@link History}. `published(topic)`'s "every publish since
`reset()`" guarantee survives long-running test suites.
- Subscriber surface (`on` / `once` / `off`) mirrors
`@suaveplan/events`' `EventBus`; the more powerful
`EventBus.emitAsync` semantics are exposed through `publishAsync`
(sequential await over registered listeners; same delivery order;
same error-capture model).
- `emit(topic, payload)` alias satisfies the structural
`CapabilityEventBusLike` contract from
`@suaveplan/api-types/capabilities` so the bus can be handed to API
plugins as their `events` capability without an additional adapter.
- `dispose()` clears history AND tears down every subscriber so test
isolation is straightforward — call once per test, no leaks.
- `createInMemoryEventBus` (no "s") is a stable alias matching the
prompt's `./adapter` subpath; both spellings will continue to exist.
## Usage: Basic
```ts
import { createInMemoryEventsBus } from "@suaveplan/api-events-in-memory/bus";
const bus = createInMemoryEventsBus();
const seen: unknown[] = [];
bus.on("billing.invoice.created", (payload) => seen.push(payload));
bus.publish("billing.invoice.created", { invoiceId: "inv_1", amount: 100 });
console.log(bus.published("billing.invoice.created"));
// [{ invoiceId: "inv_1", amount: 100 }]
console.log(seen);
// [{ invoiceId: "inv_1", amount: 100 }]
bus.reset();
console.log(bus.published("billing.invoice.created")); // []
```
## Usage: Advanced
Pin the per-topic cap when the test publishes in a tight loop, then
recover by `reset()` between batches:
```ts
import { createInMemoryEventsBus } from "@suaveplan/api-events-in-memory/bus";
import {
EVENTS_IN_MEMORY_CODES,
EventsInMemoryError,
} from "@suaveplan/api-events-in-memory/error-codes";
const bus = createInMemoryEventsBus({ maxHistoryPerTopic: 3 });
async function fanOut(count: number) {
for (let i = 0; i < count; i += 1) {
await bus.publishAsync("metrics.tick", { i });
}
}
try {
await fanOut(5);
} catch (error) {
if (
error instanceof EventsInMemoryError &&
error.code === EVENTS_IN_MEMORY_CODES.HISTORY_OVERFLOW
) {
bus.reset();
await fanOut(2);
} else {
throw error;
}
}
console.log(bus.published("metrics.tick").length); // 2
bus.dispose();
```
## API Reference
- `createInMemoryEventsBus(options?: InMemoryEventsBusOptions): InMemoryEventsBus` — factory.
- `createInMemoryEventBus` — alias, same return value.
- `InMemoryEventsBus.publish(topic, payload): void` — sync publish; appends history first.
- `InMemoryEventsBus.publishAsync(topic, payload): Promise<void>` — async publish.
- `InMemoryEventsBus.emit(topic, payload): Promise<void>` — `CapabilityEventBusLike` alias.
- `InMemoryEventsBus.on(topic, listener): Unsubscribe` — register a subscriber.
- `InMemoryEventsBus.once(topic, listener): Unsubscribe` — one-shot subscriber.
- `InMemoryEventsBus.off(topic, listener): void` — remove a subscriber.
- `InMemoryEventsBus.published(topic): readonly unknown[]` — snapshot of history.
- `InMemoryEventsBus.topics(): readonly string[]` — sorted topic names.
- `InMemoryEventsBus.reset(): void` — clear history; keep subscribers.
- `InMemoryEventsBus.dispose(): void` — clear history AND subscribers.
## Implementation Notes
The underlying `EventBus` is parameterised with the open `EventMap`
shape (`Record<string, unknown>`) so the in-memory adapter can carry
any topic name without forcing the test to declare a typed map up
front. Recording happens BEFORE dispatch because the contract that
matters for tests is "this publish was attempted" — history that
drops on subscriber failure would silently hide test bugs. `emit` and
`publishAsync` share the same body; `emit` exists only so the bus
satisfies the structural `CapabilityEventBusLike` interface from
`@suaveplan/api-types`.