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,77 @@
# `idempotency-cache.ts` — bounded set-based idempotency cache
## Purpose
Provides the default `IdempotencyCache` implementation used by
`eventBusService` to suppress duplicate publishes. The cache satisfies
the `IdempotencyCache` interface from `@suaveplan/events` (`has` /
`add`), so any operator that needs a different storage strategy
(Redis, in-memory LRU with TTL, etc.) drops in their own implementation
without changing the service contract.
The implementation is intentionally minimal: a `Set` plus a parallel
insertion-order array gives O(1) `has`/`add` with bounded memory. The
default capacity (4096 keys) tolerates several minutes of moderate
publish traffic before evicting the oldest keys.
## Features
- Implements `IdempotencyCache` from `@suaveplan/events`.
- O(1) lookup, O(1) amortised insert.
- Hard upper-bound on memory — the oldest key is evicted when the
population exceeds `capacity`.
- Validates `capacity` is a positive integer at construction.
## Usage: Basic
```ts
import { LimitedSetCache } from "@suaveplan/api-plugin-event-bus/services/event-bus-service";
const cache = new LimitedSetCache(1024);
cache.add("evt-1");
cache.has("evt-1"); // true
```
## Usage: Advanced
```ts
import { LimitedSetCache } from "@suaveplan/api-plugin-event-bus/services/event-bus-service";
import { idempotent } from "@suaveplan/events";
const cache = new LimitedSetCache(2);
const handler = idempotent(
(payload) => console.log("once", payload),
{ keyOf: (p) => String((p as { id: string }).id), cache }
);
handler({ id: "a" }); // logs
handler({ id: "a" }); // suppressed
handler({ id: "b" });
handler({ id: "c" }); // adds "c", evicts "a"
handler({ id: "a" }); // logs again — "a" was evicted
```
## API Reference
### `class LimitedSetCache implements IdempotencyCache`
- `constructor(capacity: number)` — throws `RangeError` if `capacity`
is not a positive integer.
- `has(key: string): boolean`.
- `add(key: string): void` — no-op when the key is already present;
evicts oldest when the population exceeds `capacity`.
- `size: number` — current key count.
## Implementation Notes
- Eviction is FIFO, not strictly LRU — re-`add`ing an existing key
does not promote it. This matches the existing
`@suaveplan/events.idempotent` cache contract (`has` is `O(1)`,
`add` is `O(1)`) and avoids the doubly-linked-list overhead an LRU
would incur for the same upper-memory guarantee.
- The dead-store guard (`if (oldest !== undefined)`) is required
because `Array.prototype.shift` types as `T | undefined` even when
the call site knows the array is non-empty.
- The cache lives in-process — restarting the API server clears it.
Operators needing durable dedup wire a Redis-backed
`IdempotencyCache` via a future plugin option (out of scope for
this change).