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,92 @@
# channel/heartbeat
## Purpose
`HeartbeatRunner` is a tiny lifecycle helper that owns the pair of
`setInterval` timers a `TypedBroadcastChannel` needs to keep peer presence
warm. The heartbeat callback fires once per `heartbeatInterval` and is meant
to post a `heartbeat` frame plus refresh the local tab's `lastSeen`. The
cleanup callback fires once per `heartbeatInterval * 2` and is meant to walk
the known-tabs map and evict any peer whose `lastSeen` is older than the
configured `tabTimeout`.
The helper exists because the upstream library packed both intervals into
`channel.ts` inline; pulling them out keeps the channel core inside the
300-line hard budget mandated by `AGENTS.md` §3 and gives each timer
lifecycle a discrete unit test surface. The runner has no view into the
channel — it only fires the callbacks it was given, so it composes with any
substitute clock or scheduler the consumer wants to thread.
## Features
- Two independent `setInterval`s driven by a single `heartbeatInterval`.
- Idempotent `stop()` — safe to call before `start()` or twice in a row.
- No shared state with the channel; lifecycle ownership is local to the runner.
## Usage: Basic
```ts
import { HeartbeatRunner } from "@suaveplan/broadcast-channel";
const runner = new HeartbeatRunner({
heartbeatInterval: 1_000,
onHeartbeat: () => console.log("ping"),
onCleanup: () => console.log("gc"),
});
runner.start();
// later
runner.stop();
```
## Usage: Advanced
```ts
import { HeartbeatRunner } from "@suaveplan/broadcast-channel";
class Presence {
private readonly runner = new HeartbeatRunner({
heartbeatInterval: 2_000,
onHeartbeat: () => this.broadcastSelf(),
onCleanup: () => this.evictStale(),
});
public open(): void {
this.runner.start();
}
public close(): void {
this.runner.stop();
}
private broadcastSelf(): void {
// post heartbeat frame ...
}
private evictStale(): void {
// remove peers whose lastSeen is past tabTimeout ...
}
}
```
## API Reference
- `class HeartbeatRunner`
- `constructor(opts: HeartbeatRunnerOptions)`
- `start(): void` — schedules both intervals; calling twice replaces the timers.
- `stop(): void` — clears both intervals if running; safe to call repeatedly.
- `interface HeartbeatRunnerOptions`
- `heartbeatInterval: number` — millisecond cadence for `onHeartbeat`.
- `onHeartbeat: () => void`
- `onCleanup: () => void` — fires at `heartbeatInterval * 2`.
## Implementation Notes
The cleanup cadence is intentionally twice the heartbeat cadence: this gives
peers at least one heartbeat opportunity to refresh `lastSeen` before a
cleanup tick can evict them, even under jitter. The runner stores the two
handles as `ReturnType<typeof setInterval>` so the same code typechecks under
both Bun and Node ambient lib variants. Because `start()` re-assigns the
handles without clearing the prior ones first, callers must call `stop()`
before re-starting — `TypedBroadcastChannel` does exactly that during its
`close()`/`open()` cycle. Tests pin the cadence at 20 ms so the suite stays
fast while still exercising at least one of each callback.