Files
skills/docs/foundation/bitpacker/bitpacker-network/src/defaults/defaults.md
2026-05-29 08:20:56 +01:00

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-backed Rng per call. Throws if the host has no crypto.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.