first commit
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
# Defaults — Clock and RNG factories
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## 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
|
||||
|
||||
```typescript
|
||||
import { NetworkSimulator } from "@suaveplan/bitpacker-network";
|
||||
|
||||
// Without overrides: NetworkSimulator wires defaultClock and defaultRng() internally.
|
||||
const sim = new NetworkSimulator();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```typescript
|
||||
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.
|
||||
Reference in New Issue
Block a user