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

6.5 KiB

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)

# 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):

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:^