112 lines
6.5 KiB
Markdown
112 lines
6.5 KiB
Markdown
# Design: unify-rng-across-packages
|
|
|
|
## Goal
|
|
|
|
Single canonical `Rng` interface across every `@suaveplan/*` package, with
|
|
zero runtime behaviour change and a one-release-cycle deprecation window
|
|
for the local aliases that are being phased out.
|
|
|
|
## Strategy
|
|
|
|
1. **Structural conformance over re-declaration.** The canonical `Rng` in
|
|
`@suaveplan/types` (`src/rng.ts`) is the contract. Local types that
|
|
exactly match this shape become deprecated re-exports of the canonical.
|
|
Local types that diverge are evaluated case-by-case (see compatibility
|
|
table below).
|
|
2. **Deprecated forwarders, not deletions.** Per CLAUDE.md §2, JSDoc
|
|
`@deprecated` tags are banned. The aliases are kept as named re-exports
|
|
with prose comments ("Backwards-compatibility alias for `@suaveplan/types`
|
|
`Rng` — prefer the canonical import; this re-export will be removed in a
|
|
follow-up change.") so existing consumers continue to compile.
|
|
3. **Type-only migration.** No runtime behaviour changes. Every package's
|
|
full test suite must remain green with zero assertion edits before and
|
|
after the swap. Verified with
|
|
`bunx turbo typecheck test --filter=@suaveplan/<pkg>` (rule 1: never
|
|
`tsc` directly).
|
|
|
|
## Compatibility table
|
|
|
|
| Package | Local symbol | Local surface | Canonical surface | Compatible? | Action |
|
|
|---|---|---|---|---|---|
|
|
| `@suaveplan/types` | `Rng` | `{ nextUint32(): number; nextFloat(): number; nextBytes?(n): Uint8Array; nextIntInRange?(min, maxExclusive): number }` | — (canonical) | — | None — source of truth. |
|
|
| `@suaveplan/crypto-core` | `type Rng = ByteSource` where `ByteSource = (n: number) => Uint8Array` | `(n) => Uint8Array` (a **function**, not an object with methods) | object with `nextUint32`/`nextFloat` | **No** — structurally incompatible | Drop the `Rng` alias entirely; keep `ByteSource` as the package's own concept (distinct contract: a byte-source callable, not a uniform-distribution number source). Re-export the canonical `Rng` as a deprecated forwarder so any external code that imported `crypto-core`'s `Rng` keeps compiling but now sees the canonical shape. **Note**: any consumer that was actually using `crypto-core`'s `Rng` as a callable will fail typecheck — this is intentional and the migration note in proposal.md flags it. |
|
|
| `@suaveplan/crypto` | re-exports `Rng` from `crypto-core` | same as crypto-core | object | inherits from crypto-core | Update `src/index.ts` to forward the canonical `Rng` (via crypto-core's new deprecated re-export). |
|
|
| `@suaveplan/testing` | `interface Rng { nextUint32(): number; nextFloat(): number }` | exact subset of canonical | object | **Yes** — structural match (canonical adds two OPTIONAL members; required members are identical) | Import canonical `Rng`; re-export as deprecated forwarder. `cryptoRng`/`seededRng` implementations satisfy the canonical type without code changes (they implement the required members and omit the optional ones). Add `@suaveplan/types` as a `workspace:^` dependency (rule 8 exception: testing-infrastructure may depend on `@suaveplan/types` for types-only, byte-size-test guarded). |
|
|
|
|
## Why `crypto-core`'s `ByteSource` is not the canonical `Rng`
|
|
|
|
The canonical `Rng` is an **object** with `nextUint32` / `nextFloat` (uniform
|
|
number sources). `crypto-core`'s `ByteSource` is a **function** that returns
|
|
random bytes — a fundamentally different shape and use case (key material,
|
|
nonces, AEAD entropy). These should never have shared the `Rng` name. The
|
|
migration cleans up the naming collision: `ByteSource` stays under its real
|
|
name, and `Rng` becomes the canonical numeric RNG everywhere.
|
|
|
|
## Back-compat strategy
|
|
|
|
For one release cycle (until the follow-up removal change archives):
|
|
|
|
- `@suaveplan/crypto-core/random-bytes` exports a deprecated `Rng` re-export
|
|
pointing at `@suaveplan/types`' `Rng`. The original `type Rng = ByteSource`
|
|
alias is **removed** because keeping both shapes under the same name would
|
|
break the typecheck.
|
|
- `@suaveplan/crypto/index` forwards the same deprecated alias.
|
|
- `@suaveplan/testing/context` exports a deprecated `Rng` re-export of the
|
|
canonical type. The local `interface Rng` declaration is removed.
|
|
|
|
The deprecation prose comment names the canonical import path and the
|
|
removal change ID (TBD when the follow-up is opened).
|
|
|
|
## Removal follow-up (not in scope here)
|
|
|
|
A separate OpenSpec change titled
|
|
`remove-deprecated-rng-aliases` (opened **at least one release cycle after
|
|
this change archives**) will:
|
|
|
|
1. Delete the `Rng` re-export from `@suaveplan/crypto-core/primitives/random-bytes/random-bytes.ts`.
|
|
2. Delete the `Rng` re-export from `@suaveplan/crypto/src/index.ts`.
|
|
3. Delete the `Rng` re-export from `@suaveplan/testing/src/context/context.ts`.
|
|
4. Run dependent sweep (`bunx turbo test --filter=...@suaveplan/<pkg>`) for
|
|
each of the three packages.
|
|
5. Update consumers caught by the sweep with the canonical import.
|
|
|
|
This is tracked as a follow-up item — not a phase-2 split of this change
|
|
(per CLAUDE.md §2b, this change is complete on its own surface).
|
|
|
|
## Error codes
|
|
|
|
No new error codes are introduced. The migration is structural-types-only;
|
|
no new throw sites, no new error paths.
|
|
|
|
## Verification commands (per CLAUDE.md rule 1)
|
|
|
|
```bash
|
|
# Per affected package — full pre-publish gate
|
|
bunx turbo lint typecheck test --filter=@suaveplan/crypto-core
|
|
bunx turbo lint typecheck test --filter=@suaveplan/crypto
|
|
bunx turbo lint typecheck test --filter=@suaveplan/testing
|
|
|
|
# Dependent sweep for testing (factory consumers)
|
|
bunx turbo test --filter=...@suaveplan/testing
|
|
|
|
# Repo-wide grep — only the canonical declaration plus the three deprecated
|
|
# re-exports should remain
|
|
grep -rln "interface Rng\b\|type Rng = " packages/ --include="*.ts" \
|
|
| grep -v "/dist/\|/node_modules/"
|
|
```
|
|
|
|
## File layout (per affected package)
|
|
|
|
No new files. The migration edits exactly three implementation files and
|
|
their three sibling `.md` files (doc-drift gate, CLAUDE.md §4a):
|
|
|
|
```text
|
|
packages/crypto/crypto-core/src/primitives/random-bytes/random-bytes.ts # swap alias
|
|
packages/crypto/crypto-core/src/primitives/random-bytes/random-bytes.md # update API reference
|
|
packages/crypto/crypto/src/index.ts # update forwarder
|
|
packages/crypto/crypto/src/index.md # update API reference (if mentions Rng)
|
|
packages/testing/testing/src/context/context.ts # swap interface for re-export
|
|
packages/testing/testing/src/context/context.md # update API reference
|
|
packages/testing/testing/package.json # add @suaveplan/types: workspace:^
|
|
```
|