70 lines
4.3 KiB
Markdown
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.
|