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

110 lines
5.8 KiB
Markdown

# OpenSpec Change: unify-rng-across-packages
- **Change ID**: unify-rng-across-packages
- **Package**: `@suaveplan/types` (canonical) + downstream consumers
- **Design**: ./design.md
- **Spec delta**: ./specs/types/spec.md
- **Status**: PROPOSED
- **Date**: 2026-05-17
## Why
The 2026-04-27 `types-contract-tightening` change widened the canonical `Rng`
interface in `@suaveplan/types` (adding optional `nextBytes` and
`nextIntInRange` members) and explicitly **deferred** migration of the
packages that were redefining `Rng`-shaped interfaces locally
(`crypto-core`, `cache`, `idempotency`). That deferral has lapsed: every
fresh package author who needs randomness still has to choose between
importing the canonical `Rng` or hand-rolling a local one, and the existing
local redefinitions silently fork the contract.
This change closes the loop. It migrates every genesis package with a local
`Rng` type to consume the canonical `Rng` from `@suaveplan/types`, leaving
deprecated aliases in place for one release cycle so consumers can update at
their own pace.
## What changes
For each affected genesis package, the source files that currently declare a
local `Rng` interface are migrated to:
1. Import the canonical `Rng` from `@suaveplan/types`.
2. Re-export the canonical `Rng` from the original module path under the
original name, with a "Backwards-compatibility alias for `@suaveplan/types`
`Rng`" prose comment (per CLAUDE.md §2 — no `@deprecated` JSDoc).
3. Add `@suaveplan/types` as a `workspace:^` dependency where missing.
4. Re-run `bunx turbo typecheck` and `bunx turbo test` to confirm zero
runtime / behavioural change.
The spec delta is a `## MODIFIED Requirements` block on the existing
`Rng has optional bounded operations` requirement in `openspec/specs/types/`
that adds an explicit single-source-of-truth clause: no other `@suaveplan/*`
package may declare its own `Rng` interface; downstream packages that need a
narrower or wider random-source shape MUST either consume `Rng` directly or
define a sub-interface that `extends` it.
## Impact
### Affected dependents
Run `bunx turbo ls --filter=...@suaveplan/types` to enumerate. Packages that
currently declare a local `Rng` type (surveyed via
`grep -rln "interface Rng\b\|type Rng = " packages/`) are:
- `@suaveplan/crypto-core` — [x] tests run / [x] docs reviewed / [ ] no action needed
(declares `type Rng = ByteSource` as a back-compat alias in
`src/primitives/random-bytes/random-bytes.ts`)
- `@suaveplan/crypto` — [x] tests run / [x] docs reviewed / [ ] no action needed
(re-exports the alias from `crypto-core` via `src/index.ts`)
- `@suaveplan/testing` — [x] tests run / [x] docs reviewed / [ ] no action needed
(declares a full `interface Rng` in `src/context/context.ts`; consumed by
`src/factories/factory/factory.ts`)
For every other dependent of `@suaveplan/types`: [x] no action needed —
they already consume `Rng` from `@suaveplan/types` and are unaffected by
this change.
### Source mapping (local Rng → canonical)
| Package | Local file | Local symbol | Action |
|---|---|---|---|
| `@suaveplan/crypto-core` | `src/primitives/random-bytes/random-bytes.ts` | `type Rng = ByteSource` | Replace with deprecated re-export of canonical `Rng`; keep `ByteSource` (distinct contract). |
| `@suaveplan/crypto` | `src/index.ts` | re-export `Rng` from `crypto-core` | Update re-export to point at canonical alias. |
| `@suaveplan/testing` | `src/context/context.ts` | `interface Rng { nextUint32; nextFloat }` | Import canonical `Rng`; re-export as deprecated alias. Add `@suaveplan/types` workspace dep (rule 8 exception). |
### Verification
- **Verified by:** `bunx turbo typecheck test --filter=@suaveplan/crypto-core`
- **Verified by:** `bunx turbo typecheck test --filter=@suaveplan/crypto`
- **Verified by:** `bunx turbo typecheck test --filter=@suaveplan/testing`
- **Verified by:** `bunx turbo typecheck test --filter=...@suaveplan/testing`
(dependents of `testing` consume `Rng` transitively via `factory.ts`)
- **Verified by:** repo-wide grep `grep -rln "interface Rng\b\|type Rng = " packages/ --include="*.ts" | grep -v "/dist/\|/node_modules/"` returns only the canonical declaration in `packages/core/types/src/rng.ts` plus the deprecated alias forwarders the change introduces.
## Out of scope
- **Widening the canonical `Rng`.** If a consumer needs additional members
(e.g. `nextBigInt`, streaming bytes), open a separate
`types-contract-tightening`-style change against `@suaveplan/types`.
- **Removing the deprecated aliases.** Follow-up change (one release cycle
after this one archives) removes the `Rng` re-exports from `crypto-core`,
`crypto`, and `testing`. Tracked as a TODO in this proposal's design.md.
- **Upstream-only packages (`cache`, `idempotency` upstream RNG redefinitions).**
The 2026-04-27 deferral text named `cache` and `idempotency` as RNG-shape
redefiners. On genesis, `@suaveplan/idempotency` and the three cache
packages (`@suaveplan/cache`, `cache-storage`, `cache-redis`) do **not**
currently reference `Rng` at all (verified by grep). When those packages
port the upstream randomness features, the migration must consume the
canonical `Rng` directly — there is no local type to migrate here yet.
- **Runtime behaviour changes.** This change is type-only; the test suite
must remain green pre- and post-migration with zero assertion edits.
## Migration
- **Old**: consumers importing `Rng` from `@suaveplan/crypto-core`,
`@suaveplan/crypto`, or `@suaveplan/testing`.
- **New**: import `Rng` from `@suaveplan/types`.
- **Search command**: `grep -rln "from ['\"]@suaveplan/\(crypto-core\|crypto\|testing\)['\"]" packages/ | xargs grep -l "Rng\b"`
- **Recommended replacement**: `import type { Rng } from "@suaveplan/types"`.
The deprecated alias re-exports remain functional for one release cycle.