first commit
This commit is contained in:
81
docs/foundation/bitpacker/bitpacker-network/README.md
Normal file
81
docs/foundation/bitpacker/bitpacker-network/README.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# @suaveplan/bitpacker-network - Bitpacker network plugin
|
||||
|
||||
## Summary
|
||||
|
||||
Network simulation utilities for testing bitpacker under realistic network conditions.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [defaults](./src/defaults/defaults.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [default-clock](./src/defaults/default-clock.md) — `defaultClock` is the **single allow-listed wall-clock source** in `@suaveplan/bitpacker-network`. The determin…
|
||||
- [default-rng](./src/defaults/default-rng.md) — `defaultRng` is the canonical CSPRNG-backed `Rng` factory used by `NetworkSimulator` and any other deterministic-when-a…
|
||||
- [defaults](./src/defaults/defaults.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [otel](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [instruments](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [register-schemas](./src/register-schemas/register-schemas.md) — Idempotent integration with `@suaveplan/schema-registry` for the `@suaveplan/bitpacker-network` package. Exposes `regis…
|
||||
- [register-schemas](./src/register-schemas/register-schemas.md) — Idempotent integration with `@suaveplan/schema-registry` for the `@suaveplan/bitpacker-network` package. Exposes `regis…
|
||||
- [simulator](./src/simulator/simulator.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - simulator.schema — Zod schema for `NetworkSimulator…
|
||||
- [simulator](./src/simulator/simulator.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - simulator.schema — Zod schema for `NetworkSimulator…
|
||||
- [simulator.schema](./src/simulator/simulator.schema.md) — Zod schema for `NetworkSimulatorConstructorOptions`, the constructor bundle for `NetworkSimulator` and `createNetworkSi…
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - types.schema — Zod schema for `NetworkSimulationOpt…
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - types.schema — Zod schema for `NetworkSimulationOpt…
|
||||
- [types.schema](./src/types/types.schema.md) — Zod schema for `NetworkSimulationOptions`, the configuration accepted by `NetworkSimulator`'s constructor and `setOptio…
|
||||
- [index](./src/index.md) — **Path:** `bitpacker-network/src/index.ts` **Export:** See source for details.
|
||||
<!-- end:auto-docs --> - [default-clock](./src/defaults/default-clock.md) — `defaultClock` is the **single allow-listed wall-clock source** in `@suaveplan/bitpacker-network`. The determin…
|
||||
- [default-rng](./src/defaults/default-rng.md) — `defaultRng` is the canonical CSPRNG-backed `Rng` factory used by `NetworkSimulator` and any other deterministic-when-a…
|
||||
- [defaults](./src/defaults/defaults.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [instruments](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [simulator](./src/simulator/simulator.md) — <!-- begin:auto-docs --> _(no submodules)_- simulator.schema — Zod schema for `NetworkSimulator…
|
||||
- [simulator](./src/simulator/simulator.md) — <!-- begin:auto-docs --> _(no submodules)_- simulator.schema — Zod schema for `NetworkSimulator…
|
||||
- [simulator.schema](./src/simulator/simulator.schema.md) — Zod schema for `NetworkSimulatorConstructorOptions`, the constructor bundle for `NetworkSimulator` and `createNetworkSi…
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- types.schema — Zod schema for `NetworkSimulationOpt…
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- types.schema — Zod schema for `NetworkSimulationOpt…
|
||||
- [types.schema](./src/types/types.schema.md) — Zod schema for `NetworkSimulationOptions`, the configuration accepted by `NetworkSimulator`'s constructor and `setOptio…
|
||||
- [index](./src/index.md) — **Path:** `bitpacker-network/src/index.ts` **Export:** See source for details.
|
||||
- [register-schemas](./src/register-schemas.md) — Idempotent integration with `@suaveplan/schema-registry` for the `@suaveplan/bitpacker-network` package. Exposes `regis…
|
||||
- [default-clock](./src/defaults/default-clock.md) — `defaultClock` is the **single allow-listed wall-clock source** in `@suaveplan/bitpacker-network`. The determin…
|
||||
- [default-rng](./src/defaults/default-rng.md) — `defaultRng` is the canonical CSPRNG-backed `Rng` factory used by `NetworkSimulator` and any other deterministic-when-a…
|
||||
- [defaults](./src/defaults/defaults.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [instruments](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [simulator](./src/simulator/simulator.md) — <!-- begin:auto-docs --> _(no submodules)_- simulator.schema — Zod schema for `NetworkSimulator…
|
||||
- [simulator](./src/simulator/simulator.md) — <!-- begin:auto-docs --> _(no submodules)_- simulator.schema — Zod schema for `NetworkSimulator…
|
||||
- [simulator.schema](./src/simulator/simulator.schema.md) — Zod schema for `NetworkSimulatorConstructorOptions`, the constructor bundle for `NetworkSimulator` and `createNetworkSi…
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- types.schema — Zod schema for `NetworkSimulationOpt…
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- types.schema — Zod schema for `NetworkSimulationOpt…
|
||||
- [types.schema](./src/types/types.schema.md) — Zod schema for `NetworkSimulationOptions`, the configuration accepted by `NetworkSimulator`'s constructor and `setOptio…
|
||||
- [index](./src/index.md) — **Path:** `bitpacker-network/src/index.ts` **Export:** See source for details.
|
||||
- [register-schemas](./src/register-schemas.md) — Idempotent integration with `@suaveplan/schema-registry` for the `@suaveplan/bitpacker-network` package. Exposes `regis…
|
||||
- [default-clock](./src/defaults/default-clock.md) — `defaultClock` is the **single allow-listed wall-clock source** in `@suaveplan/bitpacker-network`. The determin…
|
||||
- [default-rng](./src/defaults/default-rng.md) — `defaultRng` is the canonical CSPRNG-backed `Rng` factory used by `NetworkSimulator` and any other deterministic-when-a…
|
||||
- [defaults](./src/defaults/defaults.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [instruments](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [simulator](./src/simulator/simulator.md) — <!-- begin:auto-docs --> _(no submodules)_- simulator.schema — Zod schema for `NetworkSimulator…
|
||||
- [simulator](./src/simulator/simulator.md) — <!-- begin:auto-docs --> _(no submodules)_- simulator.schema — Zod schema for `NetworkSimulator…
|
||||
- [simulator.schema](./src/simulator/simulator.schema.md) — Zod schema for `NetworkSimulatorConstructorOptions`, the constructor bundle for `NetworkSimulator` and `createNetworkSi…
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- types.schema — Zod schema for `NetworkSimulationOpt…
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- types.schema — Zod schema for `NetworkSimulationOpt…
|
||||
- [types.schema](./src/types/types.schema.md) — Zod schema for `NetworkSimulationOptions`, the configuration accepted by `NetworkSimulator`'s constructor and `setOptio…
|
||||
- [index](./src/index.md) — **Path:** `bitpacker-network/src/index.ts` **Export:** See source for details.
|
||||
- [register-schemas](./src/register-schemas.md) — Idempotent integration with `@suaveplan/schema-registry` for the `@suaveplan/bitpacker-network` package. Exposes `regis…
|
||||
- [default-clock](./src/defaults/default-clock.md) — `defaultClock` is the **single allow-listed wall-clock source** in `@suaveplan/bitpacker-network`. The determin…
|
||||
- [default-rng](./src/defaults/default-rng.md) — `defaultRng` is the canonical CSPRNG-backed `Rng` factory used by `NetworkSimulator` and any other deterministic-when-a…
|
||||
- [defaults](./src/defaults/defaults.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_ - [instruments](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [simulator](./src/simulator/simulator.md) — <!-- begin:auto-docs --> _(no submodules)_ - [simulator](./src/simulator/simulator.md) — <!-- begin:auto-docs --> _(no submodules)_ - [simulator.schema](./src/simulator/simulator.schema.md) — Zod schema for `NetworkSimulatorConstructorOptions`, the constructor bundle for `NetworkSimulator` and `createNetworkSi…
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ - [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ - [types.schema](./src/types/types.schema.md) — Zod schema for `NetworkSimulationOptions`, the configuration accepted by `NetworkSimulator`'s constructor and `setOptio…
|
||||
- [index](./src/index.md) — **Path:** `bitpacker-network/src/index.ts` **Export:** See source for details.
|
||||
- [register-schemas](./src/register-schemas.md) — Idempotent integration with `@suaveplan/schema-registry` for the `@suaveplan/bitpacker-network` package. Exposes `regis…
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @suaveplan/bitpacker-network
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
import { NetworkSimulator } from "@suaveplan/bitpacker-network";
|
||||
|
||||
const sim = new NetworkSimulator({ latency: 50, jitter: 10, packetLoss: 0.02 });
|
||||
|
||||
sim.send(new Uint8Array([1, 2, 3]));
|
||||
const received = sim.receive(); // may drop packet based on loss rate
|
||||
```
|
||||
|
||||
## Modules
|
||||
|
||||
- `src/index.ts` — `NetworkSimulator`: configurable latency, jitter, and packet loss simulation
|
||||
@@ -0,0 +1,45 @@
|
||||
# default-clock
|
||||
|
||||
## Purpose
|
||||
|
||||
`defaultClock` is the **single allow-listed wall-clock source** in `@suaveplan/bitpacker-network`. The determinism rule is that `Date.now()` may not appear at module scope in implementation files — every primitive that needs to read time must accept an injected `Clock` from `@suaveplan/types`. This file is the one exception: it provides the production-default `Clock` that callers can use when they don't need deterministic-replay behaviour, while keeping a clean grep boundary for the spec-conformance test that scans the package.
|
||||
|
||||
## Features
|
||||
|
||||
- Production-grade real-time clock backed by `Date.now()`.
|
||||
- Implements the canonical `Clock` interface from `@suaveplan/types`.
|
||||
- Single allow-listed `Date.now()` site in the package — every other module reads time via injection only.
|
||||
- Trivially overridable in tests by injecting a `FakeClock` from `@suaveplan/testing`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { defaultClock } from "@suaveplan/bitpacker-network";
|
||||
const now = defaultClock(); // milliseconds since UNIX epoch
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { FakeClock } from "@suaveplan/testing";
|
||||
import { NetworkSimulator } from "@suaveplan/bitpacker-network";
|
||||
|
||||
// Deterministic timing in tests: drive the simulator with a FakeClock
|
||||
// instead of the wall clock, then advance it explicitly.
|
||||
const fakeClock = new FakeClock(0);
|
||||
const sim = new NetworkSimulator({ clock: fakeClock.now });
|
||||
fakeClock.advance(1000);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `defaultClock: Clock`
|
||||
|
||||
- **Type:** `Clock` (from `@suaveplan/types`) — i.e. `() => number`.
|
||||
- **Returns:** Wall-clock milliseconds since the UNIX epoch via `Date.now()`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The `Clock` interface in `@suaveplan/types` is `() => number` — no instance state, just a function call.
|
||||
- This is intentionally a `const` exported value (not a factory). All callers share the same function reference; testing replaces it by passing a different `Clock` into the consumer's constructor, never by mutating this export.
|
||||
- The spec-conformance test `simulator.spec-conformance.test.ts` scans for `Date.now()` outside this file and fails the build if any other implementation file calls it. That's the contract this module exists to satisfy.
|
||||
@@ -0,0 +1,45 @@
|
||||
# default-rng
|
||||
|
||||
## Purpose
|
||||
|
||||
`defaultRng` is the canonical CSPRNG-backed `Rng` factory used by `NetworkSimulator` and any other deterministic-when-asked, random-by-default primitive in `@suaveplan/bitpacker-network`. It exists so callers don't have to reach into `@suaveplan/random` directly to obtain the standard random source — and so that swapping the simulator to a seeded RNG in tests is a single keyword argument away.
|
||||
|
||||
The factory delegates to `cryptoRng()` from `@suaveplan/random`, which uses `globalThis.crypto.getRandomValues` underneath. In environments without a CSPRNG (some embedded runtimes) the underlying call throws a `RANDOM.SOURCE.UNAVAILABLE` error. We surface that directly: the policy is "if you don't have a CSPRNG, you must inject a seeded `Rng` yourself" — never silently downgrade to `Math.random()`.
|
||||
|
||||
## Features
|
||||
|
||||
- Zero-config default for production — call `defaultRng()` and you get a `Rng` instance backed by Web Crypto.
|
||||
- Errors surface immediately when the host lacks a CSPRNG.
|
||||
- Trivially overridable in tests by passing a `Mulberry32Rng` (or any `Rng`) into the simulator constructor.
|
||||
- No module-scope state; each call returns a fresh `Rng` instance.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { defaultRng } from "@suaveplan/bitpacker-network";
|
||||
const rng = defaultRng();
|
||||
const value = rng.nextFloat(); // [0, 1)
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { Mulberry32Rng } from "@suaveplan/pseudo-random";
|
||||
import { NetworkSimulator } from "@suaveplan/bitpacker-network";
|
||||
|
||||
// Deterministic test: seed a Mulberry32 PRNG instead of using defaultRng().
|
||||
const sim = new NetworkSimulator({ rng: new Mulberry32Rng(42), clock: testClock });
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `defaultRng(): Rng`
|
||||
|
||||
- **Returns:** A new `Rng` instance backed by `cryptoRng()` from `@suaveplan/random`.
|
||||
- **Throws:** Re-throws `RANDOM.SOURCE.UNAVAILABLE` from `cryptoRng()` if `globalThis.crypto.getRandomValues` is unavailable.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `Rng` interface from `@suaveplan/types` is the canonical contract; `cryptoRng()` and `Mulberry32Rng` both implement it.
|
||||
- We surface `cryptoRng()` directly rather than wrapping in a try/catch because callers should know if their environment lacks a CSPRNG — silent fallback would defeat determinism guarantees elsewhere in the simulator.
|
||||
- This factory is used as the default for `NetworkSimulator`'s `rng` constructor option.
|
||||
@@ -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.
|
||||
8
docs/foundation/bitpacker/bitpacker-network/src/index.md
Normal file
8
docs/foundation/bitpacker/bitpacker-network/src/index.md
Normal file
@@ -0,0 +1,8 @@
|
||||
## index
|
||||
|
||||
**Path:** `bitpacker-network/src/index.ts`
|
||||
**Export:** See source for details.
|
||||
|
||||
### What it does
|
||||
|
||||
Part of the @suaveplan/ package.
|
||||
@@ -0,0 +1,83 @@
|
||||
# Bitpacker Network OTel Instruments
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
OTel meter, tracer, counter, and histogram declarations for `@suaveplan/bitpacker-network`. Ships canonical instrumentation primitives for the `NetworkSimulator`'s send/receive boundaries plus the burst-loss event. Centralizing the metric/span vocabulary here lets a downstream consumer attach observability to the simulator without re-deriving names.
|
||||
|
||||
The metric and span names follow the suaveplan convention with snake-case for the multi-word package: tracer/meter named `@suaveplan/bitpacker-network`; spans named `bitpacker_network.<operation>` (`send`, `receive`, `burst`); counters/histograms named `bitpacker_network.<scope>.<metric>`.
|
||||
|
||||
## Features
|
||||
|
||||
- `getBitPackerNetworkTracer()` — returns the package's tracer
|
||||
- `networkPacketsTotal` — counter labelled by `status` (one of the five `PacketStatus` values)
|
||||
- `networkLatencyMs` — histogram of simulated latency for delivered packets
|
||||
- `networkPacketBytes` — histogram of per-packet byte size, labelled by `status`
|
||||
- `BITPACKER_NETWORK_SPAN_NAMES` — three canonical span names
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
BITPACKER_NETWORK_SPAN_NAMES,
|
||||
networkPacketsTotal,
|
||||
getBitPackerNetworkTracer,
|
||||
} from "@suaveplan/bitpacker-network";
|
||||
|
||||
const tracer = getBitPackerNetworkTracer();
|
||||
tracer.startActiveSpan(BITPACKER_NETWORK_SPAN_NAMES.send, (span) => {
|
||||
try {
|
||||
// send work
|
||||
networkPacketsTotal.add(1, { status: "delivered" });
|
||||
} finally {
|
||||
span.end();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
networkLatencyMs,
|
||||
networkPacketsTotal,
|
||||
PacketStatus,
|
||||
} from "@suaveplan/bitpacker-network";
|
||||
|
||||
function recordOutcome(status: PacketStatus, latencyMs: number, bytes: number) {
|
||||
networkPacketsTotal.add(1, { status });
|
||||
if (status === PacketStatus.DELIVERED) {
|
||||
networkLatencyMs.record(latencyMs);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `getBitPackerNetworkTracer(): Tracer`
|
||||
|
||||
Returns the OTel `Tracer` instance for the package.
|
||||
|
||||
### `networkPacketsTotal: Counter`
|
||||
|
||||
Counter labelled by `status: "delivered" | "lost" | "duplicated" | "corrupted" | "reordered"`.
|
||||
|
||||
### `networkLatencyMs: Histogram`
|
||||
|
||||
Histogram of simulated per-packet latency in milliseconds, recorded only on `delivered` packets.
|
||||
|
||||
### `networkPacketBytes: Histogram`
|
||||
|
||||
Histogram of per-packet byte size, labelled by `status`.
|
||||
|
||||
### `BITPACKER_NETWORK_SPAN_NAMES`
|
||||
|
||||
Const map: `{ send, receive, burst }` → canonical span names.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- **Status is a label, not separate counters.** `networkPacketsTotal{status="lost"}` is more flexible than separate `dropped_total`/`reordered_total`/etc; new packet states fold in without schema churn.
|
||||
- **Latency only on delivered.** Lost/dropped packets have no meaningful latency. Recording would distort the latency histogram with zeros.
|
||||
- **No per-packet spans.** A simulator running 10k packets/s would produce 10k spans/s if each were instrumented. The counter-and-histogram pattern is sufficient at this rate.
|
||||
@@ -0,0 +1,61 @@
|
||||
# Register Schemas — `@suaveplan/bitpacker-network`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Idempotent integration with `@suaveplan/schema-registry` for the `@suaveplan/bitpacker-network` package. Exposes `registerBitPackerNetworkSchemas(registry)` which converts the package's two public boundary schemas into JSON Schema form and registers them under `bitpacker:@suaveplan/bitpacker-network` at version 1. Re-registration on the same registry is a no-op.
|
||||
|
||||
The two schemas covered are: `networkSimulationOptions` (the user-facing simulation profile) and `networkSimulatorConstructorOptions` (the same options plus the `rng`/`clock` determinism injection points).
|
||||
|
||||
## Features
|
||||
|
||||
- `BITPACKER_NETWORK_SUBJECT` constant — `"bitpacker:@suaveplan/bitpacker-network"`
|
||||
- `BITPACKER_NETWORK_SCHEMA_VERSION` constant — `1`
|
||||
- `registerBitPackerNetworkSchemas(registry)` — idempotent registration
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { createRegistry } from "@suaveplan/schema-registry";
|
||||
import {
|
||||
BITPACKER_NETWORK_SUBJECT,
|
||||
registerBitPackerNetworkSchemas,
|
||||
} from "@suaveplan/bitpacker-network/register-schemas";
|
||||
|
||||
const registry = createRegistry();
|
||||
registerBitPackerNetworkSchemas(registry);
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { createRegistry, SchemaRegistry } from "@suaveplan/schema-registry";
|
||||
import { registerBitPackerSchemas } from "@suaveplan/bitpacker/register-schemas";
|
||||
import { registerBitPackerNetworkSchemas } from "@suaveplan/bitpacker-network/register-schemas";
|
||||
|
||||
const registry = createRegistry();
|
||||
registerBitPackerSchemas(registry);
|
||||
registerBitPackerNetworkSchemas(registry);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `BITPACKER_NETWORK_SUBJECT: "bitpacker:@suaveplan/bitpacker-network"`
|
||||
|
||||
Const string.
|
||||
|
||||
### `BITPACKER_NETWORK_SCHEMA_VERSION: 1`
|
||||
|
||||
Const number.
|
||||
|
||||
### `registerBitPackerNetworkSchemas(registry: SchemaRegistry): void`
|
||||
|
||||
Idempotent registration. Returns without writing if the subject is already present.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- **Two-key payload.** `networkSimulationOptions` for the user-facing config; `networkSimulatorConstructorOptions` for the constructor bundle that adds `rng`/`clock`.
|
||||
- **`unrepresentable: "any"`.** Required because the constructor schema uses `z.custom` for the `Rng` and `Clock` injection points.
|
||||
@@ -0,0 +1,156 @@
|
||||
# NetworkSimulator
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - simulator.schema — Zod schema for `NetworkSimulator…
|
||||
- [simulator.schema](./simulator.schema.md) — Zod schema for `NetworkSimulatorConstructorOptions`, the constructor bundle for `NetworkSimulator` and `createNetworkSi…
|
||||
- simulator.schema — Zod schema for `NetworkSimulator…
|
||||
|
||||
- [simulator.schema](./simulator.schema.md) — Zod schema for `NetworkSimulatorConstructorOptions`, the constructor bundle for `NetworkSimulator` and `createNetworkSi…
|
||||
|
||||
- simulator.schema — Zod schema for `NetworkSimulator…
|
||||
|
||||
- [simulator.schema](./simulator.schema.md) — Zod schema for `NetworkSimulatorConstructorOptions`, the constructor bundle for `NetworkSimulator` and `createNetworkSi…
|
||||
|
||||
- simulator.schema — Zod schema for `NetworkSimulator…
|
||||
|
||||
- [simulator.schema](./simulator.schema.md) — Zod schema for `NetworkSimulatorConstructorOptions`, the constructor bundle for `NetworkSimulator` and `createNetworkSi…
|
||||
- [simulator.schema](./simulator.schema.md) — Zod schema for `NetworkSimulatorConstructorOptions`, the constructor bundle for `NetworkSimulator` and `createNetworkSi…
|
||||
|
||||
## Purpose
|
||||
|
||||
`NetworkSimulator` simulates a network channel for testing real-time and multiplayer code paths without involving actual transport. It models latency, jitter, packet loss, duplication, corruption, reordering, bandwidth caps, burstiness, and disconnects, with deterministic, replayable outputs given a seeded `Rng`. Tests inject the seed; production code uses the package default (`cryptoRng()` from `@suaveplan/random`).
|
||||
|
||||
This is the port of upstream `bitpacker/src/network/network-simulation.ts`. The audit-driven hardening replaces every upstream `Math.random()` and `Date.now()` call with injected `Rng` and `Clock` so simulator behaviour is reproducible across runs given identical inputs. The class composes a minimal typed listener table internally rather than extending an `EventEmitter`, keeping the public surface minimal.
|
||||
|
||||
## Features
|
||||
|
||||
- Eight typed events: `packet:sent`, `packet:received`, `packet:lost`, `packet:duplicated`, `packet:corrupted`, `packet:reordered`, `bandwidth:throttled`, `stats:updated`, plus `connection:disconnected` / `connection:reconnected`.
|
||||
- Seven `NetworkCondition` presets (`PERFECT`, `GOOD`, `AVERAGE`, `POOR`, `TERRIBLE`, `MOBILE`, `SATELLITE`) plus `CUSTOM` for caller-supplied options.
|
||||
- Bandwidth throttling enforced over a rolling 1-second window.
|
||||
- Burstiness scheduler simulates periodic high-loss windows.
|
||||
- Random-disconnect scheduler samples exponentially-distributed time-to-event.
|
||||
- Stat counters with overflow guard — `packetsSent` near `MAX_SAFE_INTEGER` throws `BITPACKER.NETWORK_STAT_OVERFLOW`.
|
||||
- `Rng` and `Clock` injection for deterministic replay; defaults are `cryptoRng()` and `defaultClock`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```typescript
|
||||
import {
|
||||
NetworkCondition,
|
||||
NetworkSimulator,
|
||||
} from "@suaveplan/bitpacker-network";
|
||||
|
||||
const sim = new NetworkSimulator({ condition: NetworkCondition.MOBILE });
|
||||
|
||||
sim.on("packet:received", (packet) => {
|
||||
console.log(`got ${packet.size} bytes after ${packet.timestamp}ms`);
|
||||
});
|
||||
|
||||
sim.on("packet:lost", () => {
|
||||
console.log("packet lost");
|
||||
});
|
||||
|
||||
const id = sim.sendPacket(new Uint8Array([1, 2, 3, 4]));
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```typescript
|
||||
import { Mulberry32Rng } from "@suaveplan/pseudo-random";
|
||||
import { NetworkSimulator } from "@suaveplan/bitpacker-network";
|
||||
|
||||
// Deterministic replay: the same seed produces the same loss / duplication /
|
||||
// corruption / reordering pattern on every run, so a regression test can
|
||||
// pin a specific simulator outcome.
|
||||
const seededRng = new Mulberry32Rng(0xCAFE_F00D);
|
||||
let now = 0;
|
||||
const fakeClock = (): number => now;
|
||||
|
||||
const sim = new NetworkSimulator({
|
||||
rng: seededRng,
|
||||
clock: fakeClock,
|
||||
latencyMin: 50,
|
||||
latencyMax: 100,
|
||||
jitter: 10,
|
||||
packetLoss: 5,
|
||||
packetDuplication: 1,
|
||||
enableThrottling: true,
|
||||
bandwidthKbps: 1000,
|
||||
});
|
||||
|
||||
let receivedCount = 0;
|
||||
sim.on("packet:received", () => receivedCount++);
|
||||
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
sim.sendPacket(new Uint8Array([i & 0xff]));
|
||||
now += 10;
|
||||
}
|
||||
|
||||
const stats = sim.getStats();
|
||||
// stats are reproducible across runs given the same seed + inputs
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `new NetworkSimulator(options?)`
|
||||
|
||||
`options` extends `NetworkSimulationOptions` with optional `rng?: Rng` and `clock?: Clock`. Defaults: `cryptoRng()` and `defaultClock`.
|
||||
|
||||
### `sendPacket(data, callback?) → number`
|
||||
|
||||
Submit a packet. Returns the assigned id, or `-1` if disconnected. The simulator decides the packet's fate (delivered / lost / duplicated / corrupted / reordered) using the configured probabilities and the injected `Rng`. Callback fires once on the packet's terminal status.
|
||||
|
||||
### `on(event, listener) → () => void`
|
||||
|
||||
### `off(event, listener) → void`
|
||||
|
||||
### `removeAllListeners() → void`
|
||||
|
||||
Typed listener registry. `on` returns an unsubscribe function.
|
||||
|
||||
### `getStats() → NetworkSimulationStats`
|
||||
|
||||
Snapshot of aggregate counters and rolling averages. Refreshes derived fields (`uptime`, `packetLossRate`, `throughputKbps`) before returning.
|
||||
|
||||
### `resetStats() → void`
|
||||
|
||||
Zero all counters and reset `startTime` (useful between independent test runs of one simulator).
|
||||
|
||||
### `setOptions(options) → void`
|
||||
|
||||
Reconfigure a running simulator. Re-arms or cancels burstiness / random-disconnect timers based on the new options.
|
||||
|
||||
### `getOptions() → NetworkSimulationOptions`
|
||||
|
||||
Snapshot of the current options.
|
||||
|
||||
### `disconnect() → void` / `reconnect() → void` / `isConnected() → boolean`
|
||||
|
||||
Manual connection control (bypasses random-disconnect scheduler).
|
||||
|
||||
### `getQueueSize() → number` / `clearQueues() → void`
|
||||
|
||||
Inspection / reset of in-flight packet and reorder queues.
|
||||
|
||||
### `destroy() → void`
|
||||
|
||||
Stops every timer, clears queues, and removes every listener. Idempotent.
|
||||
|
||||
### `createNetworkSimulator(condition, options?) → NetworkSimulator`
|
||||
|
||||
Factory shortcut for `new NetworkSimulator({ condition, ...options })`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The simulator deliberately does not extend `@suaveplan/events::EventBus`. The bus is overkill for the eight-event surface here (we don't need interceptors, FIFO ordering across cross-plugin topics, or backpressure), and inheriting from a typed bus would leak its full surface into `NetworkSimulator`'s public API. The minimal internal listener table covers the simulator's observability needs without that overhead.
|
||||
|
||||
The `STAT_OVERFLOW_GUARD = MAX_SAFE_INTEGER - 1024` margin reserves enough room for one packet to land cleanly even at the boundary; calling `sendPacket` while `packetsSent` is at or above this guard throws `BITPACKER.NETWORK_STAT_OVERFLOW`. Consumers running multi-day soak tests should `resetStats()` periodically.
|
||||
|
||||
Bandwidth throttling uses a 1-second rolling window: bytes sent within a window count toward the cap; once the cap is reached, further packets in that window emit `bandwidth:throttled` and the per-send callback fires with `PacketStatus.LOST`. The window resets when `now - lastBandwidthCheck > 1000` ms.
|
||||
|
||||
Burstiness schedules a 1-second-long burst window every `burstInterval` ms (default 5000). During a burst, `burstPacketLoss` (default 25%) replaces the normal `packetLoss` for every packet sent. The scheduler self-rearms on every burst end.
|
||||
|
||||
Random disconnects use an exponential-distribution sample for time-to-event: `t = -log(U) / λ` where `U ~ Uniform(0,1)`. The simulator guards against `U = 0` (which would produce `Infinity`) by clamping to `Number.EPSILON`. After disconnect, reconnection happens after a uniformly random 5–15 s delay, then the scheduler rearms.
|
||||
|
||||
The `rng.nextFloat()` calls for loss / duplication / corruption / reordering decisions all happen sequentially in `sendPacket` — a seeded `Rng` produces a fixed sequence of decisions across runs, which is what makes test-time replay possible.
|
||||
@@ -0,0 +1,70 @@
|
||||
# Network Simulator Constructor Options Schema
|
||||
|
||||
## Purpose
|
||||
|
||||
Zod schema for `NetworkSimulatorConstructorOptions`, the constructor bundle for `NetworkSimulator` and `createNetworkSimulator`. Extends the `NetworkSimulationOptions` validation with two determinism injection points — `rng` (a structural `Rng` from `@suaveplan/types`) and `clock` (a structural `Clock`) — so test fakes flow through with referential identity preserved.
|
||||
|
||||
The schema validates the structural duck-types of `Rng` and `Clock` rather than requiring concrete classes, because tests inject `fakeRng()` and `fakeClock()` from `@suaveplan/testing` that satisfy the shape without inheriting from a base class. Reflecting that flexibility in the schema keeps the test surface clean.
|
||||
|
||||
## Features
|
||||
|
||||
- `networkSimulatorConstructorOptionsSchema` — strict, extends the simulation options bundle
|
||||
- Structural `Rng` validation — must expose `nextFloat(): number`
|
||||
- Structural `Clock` validation — must expose `now(): number`
|
||||
- Identity preservation for `rng` and `clock` via `z.custom`
|
||||
- `as unknown as z.ZodType<T>` cast for the EOPT mismatch
|
||||
- `networkSimulationOptionsSchema` is re-exported for convenience
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
NetworkSimulator,
|
||||
networkSimulatorConstructorOptionsSchema,
|
||||
defaultRng,
|
||||
defaultClock,
|
||||
} from "@suaveplan/bitpacker-network";
|
||||
|
||||
const opts = networkSimulatorConstructorOptionsSchema.parse({
|
||||
packetLoss: 1,
|
||||
rng: defaultRng(),
|
||||
clock: defaultClock(),
|
||||
});
|
||||
const sim = new NetworkSimulator(opts);
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
createNetworkSimulator,
|
||||
networkSimulatorConstructorOptionsSchema,
|
||||
} from "@suaveplan/bitpacker-network";
|
||||
import { fakeClock, fakeRng } from "@suaveplan/testing/runner";
|
||||
|
||||
function deterministicSim(seed: number) {
|
||||
return createNetworkSimulator(
|
||||
networkSimulatorConstructorOptionsSchema.parse({
|
||||
packetLoss: 5,
|
||||
rng: fakeRng(seed),
|
||||
clock: fakeClock(),
|
||||
})
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `networkSimulatorConstructorOptionsSchema: z.ZodType<NetworkSimulatorConstructorOptions>`
|
||||
|
||||
Strict schema extending the simulation options shape with `rng` and `clock`. Both injection points are validated structurally and preserved by identity. Unknown keys throw.
|
||||
|
||||
### `networkSimulationOptionsSchema` (re-exported)
|
||||
|
||||
The base options schema; see `../types/types.schema.md` for full details.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- **Structural `Rng` / `Clock`.** Test fakes from `@suaveplan/testing` are duck-typed objects, not class instances. Validating via `z.custom<T>((v): v is T => …)` accepts both the class form and the duck-type, and preserves identity for both.
|
||||
- **Inline option fields, not extension.** Zod doesn't have a clean `extend` story for `z.ZodType`-typed schemas, so the constructor schema re-states the option fields rather than composing the simulation options schema. The runtime behaviour is identical; the duplication is acceptable for one schema.
|
||||
- **Identity matters for tests.** A test that injects `fakeRng()` then asserts on its emitted call sequence requires `===` equality between the injected and consumed instance; `z.function()`-style wrapping would break this.
|
||||
@@ -0,0 +1,97 @@
|
||||
# Network simulation types
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - types.schema — Zod schema for `NetworkSimulationOpt…
|
||||
- [types.schema](./types.schema.md) — Zod schema for `NetworkSimulationOptions`, the configuration accepted by `NetworkSimulator`'s constructor and `setOptio…
|
||||
- types.schema — Zod schema for `NetworkSimulationOpt…
|
||||
|
||||
- [types.schema](./types.schema.md) — Zod schema for `NetworkSimulationOptions`, the configuration accepted by `NetworkSimulator`'s constructor and `setOptio…
|
||||
|
||||
- types.schema — Zod schema for `NetworkSimulationOpt…
|
||||
|
||||
- [types.schema](./types.schema.md) — Zod schema for `NetworkSimulationOptions`, the configuration accepted by `NetworkSimulator`'s constructor and `setOptio…
|
||||
|
||||
- types.schema — Zod schema for `NetworkSimulationOpt…
|
||||
|
||||
- [types.schema](./types.schema.md) — Zod schema for `NetworkSimulationOptions`, the configuration accepted by `NetworkSimulator`'s constructor and `setOptio…
|
||||
- [types.schema](./types.schema.md) — Zod schema for `NetworkSimulationOptions`, the configuration accepted by `NetworkSimulator`'s constructor and `setOptio…
|
||||
|
||||
## Purpose
|
||||
|
||||
Defines the type companions and constant tables used by `NetworkSimulator`. These types were previously colocated with the misplaced simulator implementation in `@suaveplan/bitpacker-game/src/types.ts`; the 2026-05-02 audit moved them here so the network-simulator concern lives in one package. The shapes and default values match upstream's `bitpacker/src/types.ts` byte-for-byte to preserve wire-compat with any code that branches on these enums.
|
||||
|
||||
## Features
|
||||
|
||||
- `NetworkCondition` enum with seven presets (`PERFECT`, `GOOD`, `AVERAGE`, `POOR`, `TERRIBLE`, `MOBILE`, `SATELLITE`) plus `CUSTOM` for caller-supplied configurations.
|
||||
- `PacketStatus` enum reflecting the simulator's per-packet outcomes (`DELIVERED`, `LOST`, `DUPLICATED`, `CORRUPTED`, `REORDERED`).
|
||||
- `NetworkSimulationOptions` covering latency / jitter / loss / duplication / corruption / reordering / bandwidth / disconnect / MTU / burstiness.
|
||||
- `NETWORK_CONDITIONS` map binding each condition to a baseline options object — picking `condition: NetworkCondition.MOBILE` populates the seven mobile-network defaults.
|
||||
- `DEFAULT_NETWORK_SIMULATION_OPTIONS` for `CUSTOM` and absent-condition cases.
|
||||
- `PacketInfo` — packet metadata emitted via `packet:*` events.
|
||||
- `NetworkSimulationStats` — aggregate counters reported by `getStats()`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```typescript
|
||||
import {
|
||||
NetworkCondition,
|
||||
type NetworkSimulationOptions,
|
||||
} from "@suaveplan/bitpacker-network";
|
||||
|
||||
const opts: NetworkSimulationOptions = {
|
||||
condition: NetworkCondition.MOBILE,
|
||||
};
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```typescript
|
||||
import {
|
||||
NETWORK_CONDITIONS,
|
||||
NetworkCondition,
|
||||
type NetworkSimulationOptions,
|
||||
} from "@suaveplan/bitpacker-network";
|
||||
|
||||
// Override one field of a preset:
|
||||
const opts: NetworkSimulationOptions = {
|
||||
...NETWORK_CONDITIONS[NetworkCondition.SATELLITE],
|
||||
packetLoss: 0, // perfect satellite — useful for diff studies
|
||||
};
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `enum NetworkCondition`
|
||||
|
||||
Closed list of named profiles. Adding a value here is a wire-format extension; renaming or removing one is a wire-format break.
|
||||
|
||||
### `enum PacketStatus`
|
||||
|
||||
Closed list of per-packet outcomes the simulator reports.
|
||||
|
||||
### `interface NetworkSimulationOptions`
|
||||
|
||||
Caller-side configuration. All fields optional. Numeric percentages are 0–100 (not 0–1) — match upstream convention.
|
||||
|
||||
### `const DEFAULT_NETWORK_SIMULATION_OPTIONS`
|
||||
|
||||
Baseline used when no `condition` is supplied or when `condition` is `CUSTOM`.
|
||||
|
||||
### `const NETWORK_CONDITIONS`
|
||||
|
||||
Lookup table mapping every `NetworkCondition` to the base options for that preset.
|
||||
|
||||
### `interface PacketInfo`
|
||||
|
||||
Emitted alongside `packet:*` events. `data` is a fresh `Uint8Array` copy (the simulator deep-copies on send so the caller's buffer isn't shared with the simulated network).
|
||||
|
||||
### `interface NetworkSimulationStats`
|
||||
|
||||
Counters and rolling averages. Read via `simulator.getStats()` or via the `stats:updated` event.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`NETWORK_CONDITIONS[NetworkCondition.CUSTOM]` is `{...DEFAULT_NETWORK_SIMULATION_OPTIONS}` — choosing `CUSTOM` is equivalent to omitting the condition entirely. The simulator constructor honours either spelling.
|
||||
|
||||
The percentage convention (0–100, not 0–1) is the upstream convention preserved verbatim. Switching to 0–1 would be a breaking config change. If a future redesign normalises this, document it as a wire-format extension.
|
||||
@@ -0,0 +1,72 @@
|
||||
# Network Simulation Options Schema
|
||||
|
||||
## Purpose
|
||||
|
||||
Zod schema for `NetworkSimulationOptions`, the configuration accepted by `NetworkSimulator`'s constructor and `setOptions`. Validates the configuration at the package boundary so an out-of-range percentage (`packetLoss: 150`), a negative latency, or an unknown key all fail at parse time rather than producing a simulator that silently misbehaves under load.
|
||||
|
||||
The schema treats the `*Loss`, `*Duplication`, `corruption`, `*Reordering`, `disconnectChance`, and `burstPacketLoss` fields as percentages bounded to `[0, 100]` because that's the simulator's documented contract. Latency and bandwidth fields are non-negative; the boolean toggles are unbounded.
|
||||
|
||||
## Features
|
||||
|
||||
- `networkSimulationOptionsSchema` — strict, every field optional with documented bounds
|
||||
- Percentages enforced to `[0, 100]` (not `[0, 1]`)
|
||||
- `condition` token must be a `NetworkCondition` enum value
|
||||
- `as unknown as z.ZodType<T>` cast for the EOPT mismatch
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
NetworkSimulator,
|
||||
networkSimulationOptionsSchema,
|
||||
NetworkCondition,
|
||||
} from "@suaveplan/bitpacker-network";
|
||||
|
||||
const opts = networkSimulationOptionsSchema.parse({
|
||||
condition: NetworkCondition.AVERAGE,
|
||||
packetLoss: 1.5,
|
||||
});
|
||||
const sim = new NetworkSimulator(opts);
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
createNetworkSimulator,
|
||||
networkSimulationOptionsSchema,
|
||||
NetworkCondition,
|
||||
} from "@suaveplan/bitpacker-network";
|
||||
|
||||
function safeSimulator(rawOpts: unknown) {
|
||||
const result = networkSimulationOptionsSchema.safeParse(rawOpts);
|
||||
if (!result.success) {
|
||||
throw new Error(
|
||||
`Network simulation options rejected: ${result.error.message}`
|
||||
);
|
||||
}
|
||||
return createNetworkSimulator(result.data);
|
||||
}
|
||||
|
||||
const sim = safeSimulator({
|
||||
latencyMin: 50,
|
||||
latencyMax: 200,
|
||||
jitter: 25,
|
||||
packetLoss: 5,
|
||||
enableBurstiness: true,
|
||||
condition: NetworkCondition.MOBILE,
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `networkSimulationOptionsSchema: z.ZodType<NetworkSimulationOptions>`
|
||||
|
||||
Strict schema. Every field is optional. Numeric percentages (`packetLoss`, `packetDuplication`, `corruption`, `packetReordering`, `disconnectChance`, `burstPacketLoss`) are bounded to `[0, 100]`. Latency and timing fields are non-negative finite numbers. `mtu` and `packetBatchSize` are non-negative integers (mtu strictly positive). `condition` is a `NetworkCondition` enum value.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- **Percentages are `[0, 100]`, not `[0, 1]`.** The simulator interprets `packetLoss: 5` as 5% loss. Validating `[0, 1]` would silently allow `5` which the simulator would then floor or treat as 100%, masking the bug. Refusing it at parse time keeps the contract honest.
|
||||
- **`mtu` is positive.** A zero or negative MTU produces an undefined transmission state; refusing it forces the caller to either omit the field (defaults to 1500) or supply a meaningful value.
|
||||
- **`condition: CUSTOM` honours every other field.** The schema does NOT enforce mutual exclusion between `condition` and the per-field overrides, because the simulator deliberately allows callers to start from a preset and override individual fields.
|
||||
- **Strict mode.** Unknown keys are rejected; misspelled flags would otherwise silently default and hide configuration bugs.
|
||||
Reference in New Issue
Block a user