Files
2026-05-27 23:29:53 +01:00

70 lines
4.3 KiB
Markdown

# Capability: random-utilities
## ADDED Requirements
### Package identity
- REQ-RAND-001: Package name `@suaveplan/random`, `packages/universal/random`.
- REQ-RAND-002: Runtime deps: `@suaveplan/types`, `@suaveplan/error`, `tslib`, `zod`. Zero crypto-specific deps.
- REQ-RAND-003: `"sideEffects": false`. Subpath exports per feature module.
### CSPRNG source
- REQ-RAND-010: Exports `cryptoRng(): Rng` — factory returning a WebCrypto-backed `Rng` object: `{ nextUint32, nextFloat }`. `crypto.getRandomValues` is the **only** callsite in this package, located exclusively in `src/source/crypto-rng.ts`.
- REQ-RAND-011: Exports `getDefaultRng(): Rng` and `setDefaultRng(rng: Rng): void`. Default is `cryptoRng()` at module init.
- REQ-RAND-012: `setDefaultRng` is documented as a test hook. Tests must restore the previous value in `afterEach`.
### Bytes and numbers
- REQ-RAND-020: Exports `randomBytes(n: number, rng?: Rng): Uint8Array` — fills via `nextUint32()` calls.
- REQ-RAND-021: Exports `randomInt(min: number, max: number, rng?: Rng): number` — uniform integer in `[min, max]` inclusive. Throws if `min > max`.
- REQ-RAND-022: Exports `randomFloat(rng?: Rng): number` — uniform float in `[0, 1)`.
### String generation
- REQ-RAND-030: Exports `randomString(length: number, opts?: { type?: StringType; characters?: string; rng?: Rng }): string`.
- REQ-RAND-031: `StringType = "hex" | "base64" | "base64url" | "url-safe" | "numeric" | "alphanumeric" | "distinguishable" | "ascii-printable"`.
- REQ-RAND-032: `distinguishable` charset: `CDEHKMPRTUWXY012458` (no visually ambiguous characters).
- REQ-RAND-033: `type` and `characters` are mutually exclusive; providing both throws `RandomError("RANDOM.STRING.INVALID_OPTIONS")`.
### ID generation
- REQ-RAND-040: Exports `randomUUID(rng?: Rng): string` — RFC 4122 v4 UUID.
- REQ-RAND-041: Exports `randomUUIDv7(rng?: Rng): string` — time-ordered UUID v7: 48-bit ms timestamp + 74 random bits. Monotonically increasing within the same millisecond via sequence counter.
- REQ-RAND-042: Exports `randomULID(rng?: Rng): string` — Crockford Base32 encoded ULID.
- REQ-RAND-043: Exports `randomNanoid(size?: number, rng?: Rng): string` — URL-safe, default 21 characters.
### Collection operations
- REQ-RAND-050: Exports `shuffle<T>(array: T[], rng?: Rng): T[]` — Fisher-Yates, immutable.
- REQ-RAND-051: Exports `sample<T>(array: T[], n: number, rng?: Rng): T[]` — n distinct elements.
- REQ-RAND-052: Exports `choice<T>(array: T[], rng?: Rng): T` — single random element.
- REQ-RAND-053: Exports `weightedChoice<T>(items: T[], weights: number[], rng?: Rng): T`.
- REQ-RAND-054: Exports `weightedSample<T>(items: T[], weights: number[], n: number, rng?: Rng): T[]` — weighted sampling without replacement.
- REQ-RAND-055: All collection functions throw `RandomError("RANDOM.COLLECTION.EMPTY")` on empty input.
### Object/Record operations
- REQ-RAND-060: Exports `randomKey<T extends object>(obj: T, rng?: Rng): keyof T`.
- REQ-RAND-061: Exports `randomValue<T extends object>(obj: T, rng?: Rng): T[keyof T]`.
- REQ-RAND-062: Exports `randomEntry<T extends object>(obj: T, rng?: Rng): [keyof T, T[keyof T]]`.
- REQ-RAND-063: Exports `randomSubset<T extends object>(obj: T, n: number, rng?: Rng): Partial<T>`.
- REQ-RAND-064: All object functions throw `RandomError("RANDOM.OBJECT.EMPTY")` on empty object.
### Game primitives
- REQ-RAND-070: Exports `coin(p?: number, rng?: Rng): boolean` — Bernoulli trial. Default `p = 0.5`. Throws if `p` outside `[0, 1]`.
- REQ-RAND-071: Exports `dice(sides: number, rng?: Rng): number` — uniform integer in `[1, sides]`.
- REQ-RAND-072: Exports `roll(count: number, sides: number, rng?: Rng): number` — sum of `count` dice.
### Error surface
- REQ-RAND-080: Exports `RandomError extends SuaveplanError<RandomCode>`.
- REQ-RAND-081: Codes: `RANDOM.STRING.INVALID_OPTIONS`, `RANDOM.COLLECTION.EMPTY`, `RANDOM.COLLECTION.INSUFFICIENT`, `RANDOM.OBJECT.EMPTY`, `RANDOM.GAME.INVALID_SIDES`, `RANDOM.GAME.INVALID_PROBABILITY`.
### Quality
- REQ-RAND-090: 100% statement + branch coverage.
- REQ-RAND-091: `tests/no-disallowed.test.ts`: no `Math.random`, `Date.now`, `crypto.getRandomValues` outside `src/source/crypto-rng.ts`.
- REQ-RAND-092: fast-check: all collection operations preserve input length constraints; UUID/ULID format invariants.