3.3 KiB
Defaults — Clock and RNG factories
(no submodules)
Purpose
@suaveplan/bitpacker-network enforces the determinism rule (no module-scope Date.now() or Math.random() calls in implementation files). To do that without infecting every consumer with mandatory clock / rng parameters, this package allow-lists ONE factory per source: defaultClock and defaultRng(). Every other module reads time and randomness only through the injected primitives passed at construction time, defaulting to these factories.
This is the same pattern @suaveplan/events uses (events/clock/default-clock.ts) and is the single explicit boundary between deterministic, replayable code paths and the host environment's wall clock / CSPRNG.
Features
defaultClock—Clock = () => Date.now(). Produces ms-since-epoch reads from the host wall clock.defaultRng()— factory that wraps@suaveplan/random::cryptoRng(). Returns a fresh CSPRNG-backedRngper call. Throws if the host has nocrypto.getRandomValues.
Usage: Basic Example
import { NetworkSimulator } from "@suaveplan/bitpacker-network";
// Without overrides: NetworkSimulator wires defaultClock and defaultRng() internally.
const sim = new NetworkSimulator();
Usage: Advanced Example
import { NetworkSimulator } from "@suaveplan/bitpacker-network";
import { Mulberry32Rng } from "@suaveplan/pseudo-random";
// Tests: inject a seeded RNG for reproducible packet-loss patterns.
const seededRng = new Mulberry32Rng(0xDEADBEEF);
const fakeClock = (): number => 0; // every call returns t=0
const sim = new NetworkSimulator({
rng: seededRng,
clock: fakeClock,
});
// Two simulators with the same seed produce byte-identical loss sequences.
API Reference
defaultClock: Clock
Constant. () => number returning the current millisecond timestamp via Date.now(). Suitable for the simulator's startTime, lastBandwidthCheck, packet timestamp, and uptime calculations. NOT monotonic — consumers needing strict monotonicity should inject a different Clock.
defaultRng(): Rng
Factory function. Returns a fresh Rng backed by globalThis.crypto.getRandomValues. Throws RandomError (code RANDOM.SOURCE.UNAVAILABLE) if the host environment lacks the API. The simulator's constructor calls this lazily — if the consumer always provides their own rng option, the factory is never invoked, so older / non-browser hosts that lack crypto.getRandomValues can still use the simulator with a seeded Rng.
Implementation Notes
The package's src/simulator/simulator.spec-conformance.test.ts includes a grep guard that scans implementation files for Date.now() / Math.random() and fails the build if either appears outside defaults/default-clock.ts and defaults/default-rng.ts. Those two files are explicitly allow-listed in the grep — that's the integrity boundary that keeps the rest of the package deterministic given identical injected primitives.
If a future addition needs another determinism-violating API (e.g. performance.now(), process.hrtime()), the convention is: add a new file under defaults/ and extend the spec-conformance allow-list to include it. Spreading the violations across implementation files is what the rule prevents.