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,86 @@
# `event-buffer`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The `event-buffer` module owns the per-`(tenant, topic)` replay storage
the engine consults on `Last-Event-Id` reconnects. Two implementations
are exported: an in-memory ring buffer for single-host deployments and a
broker-backed adapter for clusters that share replay state with the WS
engine. Both expose the same `EventBuffer` surface so the engine code
path is identical regardless of deployment topology.
## Features
- Multi-key buffer (`tenantId`, `topic`) on top of the abstract
`ReplayBuffer` exported by `@suaveplan/api-sse`.
- In-memory mode: lazy slot allocation, size + ttl bounded ring buffer,
one `ReplayBuffer` per `(tenantId, topic)`.
- Broker mode: delegates `publish` to the wired `SseBroker`, keeps a
shadow in-memory mirror so reads stay synchronous.
- `evictExpired(now)` sweeps every per-key buffer in one call.
- Synchronous `size(key)` probe used by the `BUFFER_MISS` decision.
## Usage: Basic
```ts
import { createInMemoryEventBuffer } from "@suaveplan/api-sse-bun";
const buffer = createInMemoryEventBuffer({
eventBufferSize: 256,
eventBufferMs: 300_000,
});
const key = { tenantId: "acme" as never, topic: "audit" };
buffer.append(key, { id: "1", data: "first" });
const replay = await buffer.since(key, "non-existent");
console.log(replay); // []
```
## Usage: Advanced
```ts
import {
createBrokerBackedEventBuffer,
type EventBuffer,
} from "@suaveplan/api-sse-bun";
import type { SseBroker } from "@suaveplan/api-sse";
declare const broker: SseBroker;
const buffer: EventBuffer = createBrokerBackedEventBuffer({
eventBufferSize: 1024,
eventBufferMs: 600_000,
broker,
});
await buffer.append(
{ tenantId: "acme" as never, topic: "audit" },
{ id: "1", data: "first" }
);
```
## API Reference
- `interface EventBuffer` — `append(key, event)`, `since(key, lastId)`,
`size(key)`, `evictExpired(now)`, `mode: "memory" | "broker"`.
- `interface InMemoryEventBufferOptions` — `eventBufferSize`,
`eventBufferMs`, optional `clock`.
- `function createInMemoryEventBuffer(options): EventBuffer`.
- `interface BrokerBackedEventBufferOptions extends InMemoryEventBufferOptions`
— adds required `broker: SseBroker`.
- `function createBrokerBackedEventBuffer(options): EventBuffer`.
## Implementation Notes
The broker-backed adapter keeps a shadow in-memory mirror because the
`SseBroker` contract does not yet expose a synchronous read path —
real implementations (Redis sorted-set) require an async query and the
engine resolves `since` synchronously during the handshake. The shadow
mirror is consistent on the local host; cross-host replay (a client
reconnects to a different host than the one that emitted the event)
delegates to the broker's `since` surface, which lives in the broker
package, not here. Eviction in broker mode operates only on the local
shadow — the broker manages its own TTL via Redis' native expiration.