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,80 @@
# `principal-expiry`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Tracks the underlying token expiry for a frozen principal so the engine can drop the connection at the right moment. The contract requires the `Connection.principal` field to be immutable for the connection's lifetime; this module is the signal the engine uses to flip the connection state to `closing` once the original token would have lapsed.
The watcher is clock-injected so tests can drive expiry deterministically without sleeping. In production the engine polls `check()` from a regular interval (default: aligned to the heartbeat timer).
## Features
- Single-fire — `onExpired` runs at most once per watcher instance.
- Throws `WS_BUN.PRINCIPAL_EXPIRED` after firing so the engine sees the same shaped error as every other WS_BUN failure mode.
- `hasFired` accessor for test assertions and lifecycle bookkeeping.
## Usage: Basic
```ts
import { PrincipalExpiryWatcher } from "@suaveplan/api-ws-bun";
const watcher = new PrincipalExpiryWatcher({
connectionId: "c1",
principalId: "u1",
expiresAt: Date.now() + 60_000,
clock: { now: () => Date.now() },
onExpired: ({ connectionId }) => emitBus(connectionId),
});
setInterval(() => {
try {
watcher.check();
} catch {
// engine closes the connection
}
}, 5_000);
```
## Usage: Advanced
```ts
import { PrincipalExpiryWatcher, WS_BUN_CODES } from "@suaveplan/api-ws-bun";
let busEvent: { connectionId: string; principalId: string } | null = null;
const clock = { value: 0 };
const watcher = new PrincipalExpiryWatcher({
connectionId: "c1",
principalId: "u1",
expiresAt: 100,
clock: { now: () => clock.value },
onExpired: (event) => {
busEvent = event;
},
});
clock.value = 99;
watcher.check(); // no-op
clock.value = 100;
try {
watcher.check();
} catch (err) {
// err.code === WS_BUN_CODES.PRINCIPAL_EXPIRED
}
console.log(busEvent?.principalId); // "u1"
```
## API Reference
- `PrincipalExpiryClock` — `{ now(): number }`.
- `PrincipalExpiryListener` — `(event) => void`.
- `PrincipalExpiryWatcherOptions` — `{ connectionId, principalId, expiresAt, clock, onExpired }`.
- `new PrincipalExpiryWatcher(options)` — constructor.
- `watcher.check(): void` — run an expiry check.
- `watcher.hasFired: boolean` — has the expiry already triggered.
## Implementation Notes
The watcher does NOT subscribe to an event bus on its own — the engine wires the `onExpired` callback into whatever bus the host owns. This keeps `principal-expiry` free of `EventBus` dependencies and lets the test suite assert on `onExpired` directly via a captured closure.
`check()` uses `>=` so the moment `now === expiresAt`, the principal is treated as expired. This matches the convention used elsewhere in the package (e.g. tenant-cap), keeping the "cliff" semantics uniform.