Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
<!-- markdownlint-disable MD024 -->
# Capability Spec Delta: crypto-core (crypto-core)
**Package**: `@suaveplan/crypto-core`
**Status**: PROPOSED
> Every requirement below carries a `**Verified by:**` line at authoring time. No exceptions.
## MODIFIED Requirements
### Requirement: ByteSource and Rng are distinct contracts
`@suaveplan/crypto-core` SHALL declare `ByteSource` as the package's callable byte producer (`type ByteSource = (n: number) => Uint8Array`), exported from `src/primitives/random-bytes/random-bytes.ts`. The package SHALL NOT alias `Rng` to `ByteSource` or to any callable shape — the canonical `Rng` (from `@suaveplan/types`) is an object with `nextUint32` / `nextFloat`, fundamentally different from a callable byte producer. The previous `type Rng = ByteSource` alias and the `defaultRng: ByteSource = defaultByteSource` constant SHALL both be removed.
`@suaveplan/crypto-core` MAY re-export the canonical `Rng` from `@suaveplan/types` under the name `Rng` as a back-compat forwarder for one release cycle, with a prose comment naming the canonical import path and the removal change ID (`remove-deprecated-rng-aliases`). The forwarder MUST live at `src/primitives/random-bytes/random-bytes.ts` and be re-surfaced through the three barrels (`src/primitives/random-bytes/index.ts`, `src/primitives/index.ts`, `src/index.ts`). The `defaultRng` re-export SHALL NOT survive in any barrel — consumers use `defaultByteSource` directly.
The `RandomBytesOptions` shape SHALL drop the legacy `rng?: ByteSource` field; only `byteSource?: ByteSource` remains. The internal selector simplifies from `options.byteSource ?? options.rng ?? defaultByteSource` to `options.byteSource ?? defaultByteSource`. Repo-internal grep confirms zero callsites pass `{ rng: ... }` to `randomBytes` today.
`@suaveplan/crypto-core` SHALL preserve every other `ByteSource`-typed surface verbatim — engine constructors (`AeadEngine`, `SignEngine`, `AgreeEngine`, `SealedBoxEngine`, `AuthBoxEngine`), the `byteSource` injection on `generateKeypair` (ed25519, ecdsaP256, x25519), and the `defaultByteSource` constant. None of these reference `Rng`; they reference `ByteSource` by its real name.
**Verified by:** `packages/crypto/crypto-core/src/primitives/random-bytes/random-bytes.ts` (no `type Rng = ByteSource`; no `defaultRng`; presence of `export type { Rng } from "@suaveplan/types"` back-compat re-export) AND `packages/crypto/crypto-core/src/primitives/random-bytes/random-bytes.test.ts::"randomBytes accepts byteSource option"` AND `packages/crypto/crypto-core/src/primitives/random-bytes/random-bytes.test.ts::"randomBytes rejects negative or non-integer n"` AND `bunx turbo typecheck test --filter=@suaveplan/crypto-core` (green with zero assertion edits) AND `bunx turbo test --filter=...@suaveplan/crypto-core` (every consumer green) AND repo-internal grep `grep -rn "defaultRng\b\|\.rng:\|, rng:" packages/crypto/crypto-core/src/ --include="*.ts"` returns zero results outside the documented back-compat re-export comment.
#### Scenario: Rng re-export forwards canonical
- **WHEN** a consumer writes `import type { Rng } from "@suaveplan/crypto-core"`
- **THEN** the imported type is structurally identical to `import type { Rng } from "@suaveplan/types"` (an object, NOT a callable)
#### Scenario: ByteSource remains the callable contract
- **WHEN** a consumer writes `import type { ByteSource } from "@suaveplan/crypto-core"`
- **THEN** the imported type is `(n: number) => Uint8Array`
#### Scenario: defaultRng is removed
- **WHEN** a consumer writes `import { defaultRng } from "@suaveplan/crypto-core"`
- **THEN** `tsc` rejects with "has no exported member `defaultRng`"
#### Scenario: RandomBytesOptions.rng field is removed
- **WHEN** a consumer calls `randomBytes(32, { rng: customSource })`
- **THEN** `tsc` rejects with "Object literal may only specify known properties, and `rng` does not exist in type `RandomBytesOptions`"
#### Scenario: engines preserve ByteSource injection
- **WHEN** a consumer calls `ed25519.generateKeypair({ byteSource: customSource })`
- **THEN** the keypair is generated using `customSource(32)` (behavior unchanged)

View File

@@ -0,0 +1,31 @@
<!-- markdownlint-disable MD024 -->
# Capability Spec Delta: crypto (crypto)
**Package**: `@suaveplan/crypto`
**Status**: PROPOSED
> Every requirement below carries a `**Verified by:**` line at authoring time. No exceptions.
## MODIFIED Requirements
### Requirement: Top-level barrel forwards canonical Rng
`@suaveplan/crypto/src/index.ts` SHALL re-export `Rng` from `@suaveplan/crypto-core` — which in turn forwards the canonical `Rng` from `@suaveplan/types`. The legacy `defaultRng` re-export SHALL be dropped; consumers use `defaultByteSource` (preserved under its real name) when they need the default callable byte producer. The `ByteSource` re-export SHALL remain unchanged — every existing consumer of `import { ByteSource } from "@suaveplan/crypto"` continues to work.
**Verified by:** `packages/crypto/crypto/src/index.ts` (presence of `Rng` re-export; absence of `defaultRng` re-export; presence of `ByteSource`, `defaultByteSource`, `randomBytes`, `RandomBytesOptions` re-exports) AND `bunx turbo typecheck test --filter=@suaveplan/crypto` (green with zero assertion edits) AND repo-internal grep `grep -rn "defaultRng\b" packages/crypto/crypto/src/ --include="*.ts"` returns zero results.
#### Scenario: Rng re-export forwards canonical
- **WHEN** a consumer writes `import type { Rng } from "@suaveplan/crypto"`
- **THEN** the imported type is structurally identical to `import type { Rng } from "@suaveplan/types"`
#### Scenario: defaultRng re-export is removed
- **WHEN** a consumer writes `import { defaultRng } from "@suaveplan/crypto"`
- **THEN** `tsc` rejects with "has no exported member `defaultRng`"
#### Scenario: defaultByteSource remains available
- **WHEN** a consumer writes `import { defaultByteSource } from "@suaveplan/crypto"`
- **THEN** `tsc` accepts AND `defaultByteSource(32).length === 32`

View File

@@ -0,0 +1,28 @@
<!-- markdownlint-disable MD024 -->
# Capability Spec Delta: mime (mime)
**Package**: `@suaveplan/mime`
**Status**: PROPOSED
> Every requirement below carries a `**Verified by:**` line at authoring time. No exceptions.
## MODIFIED Requirements
### Requirement: Stream byte source uses a distinct name
The file-scope helper type at `src/detection/from-stream.ts` (currently `type ByteSource = ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>`) SHALL be renamed to `StreamByteSource`. The rename removes a cross-package name collision with `@suaveplan/crypto-core`'s and `@suaveplan/crypto-utils`'s `ByteSource` callable type, which is a distinct concept (`(n: number) => Uint8Array`). The local `StreamByteSource` remains file-scope only (not exported from `src/detection/from-stream.ts`); this change has zero external-consumer impact.
`@suaveplan/mime` SHALL NOT declare a top-level `Rng`-shaped type — it has no need for a random-number source and does not appear in the unification audit's "structurally compatible / incompatible" categorisation.
**Verified by:** `packages/data/mime/src/detection/from-stream.ts` (declaration `type StreamByteSource = ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>` and parameter `source: StreamByteSource`) AND `packages/data/mime/src/detection/from-stream.test.ts` (test names unchanged; runtime unaffected) AND `bunx turbo typecheck test --filter=@suaveplan/mime` (green with zero assertion edits) AND repo-internal grep `grep -rn "ByteSource" packages/data/mime/src/ --include="*.ts"` returns only `StreamByteSource` matches.
#### Scenario: rename is internal-only
- **WHEN** a consumer imports anything from `@suaveplan/mime`
- **THEN** no breaking change is observable — `StreamByteSource` was never exported
#### Scenario: detectFromStream signature unchanged at the public boundary
- **WHEN** a consumer calls `detectFromStream(readableStream)`
- **THEN** the call typechecks and behaviour is unchanged (the parameter is structurally identical to before — only the local alias name changed)

View File

@@ -0,0 +1,36 @@
<!-- markdownlint-disable MD024 -->
# Capability Spec Delta: testing (testing)
**Package**: `@suaveplan/testing`
**Status**: PROPOSED
> Every requirement below carries a `**Verified by:**` line at authoring time. No exceptions.
## MODIFIED Requirements
### Requirement: Rng surface re-exports the canonical type
`@suaveplan/testing` SHALL re-export the canonical `Rng` interface from `@suaveplan/types` rather than declaring a local `interface Rng`. The re-export lives at `src/context/context.ts` and is named `Rng`, preserving the existing consumer import path (`import type { Rng } from "@suaveplan/testing"`) for one release cycle. The local `cryptoRng` and `seededRng(seed)` factories SHALL continue to satisfy the canonical type — they provide the required `nextUint32` / `nextFloat` members and omit the optional `nextBytes` / `nextIntInRange` members. The package SHALL add `@suaveplan/types` to `dependencies` as `workspace:^`; this is the sole `@suaveplan/*` runtime dependency permitted for testing-infrastructure packages under CLAUDE.md rule 8, and is allowed because `@suaveplan/types` is types-only and emits zero runtime (verified by the byte-size guard).
**Verified by:** `packages/testing/testing/src/context/context.ts` (re-export line `export type { Rng } from "@suaveplan/types"`) AND `packages/testing/testing/src/context/context.test.ts::"cryptoRng satisfies canonical Rng"` AND `packages/testing/testing/src/context/context.test.ts::"seededRng satisfies canonical Rng"` AND `packages/testing/testing/package.json` `dependencies` contains `"@suaveplan/types": "workspace:^"` AND `bunx turbo test --filter=...@suaveplan/testing` (foundation dependent sweep).
#### Scenario: local Rng re-export forwards canonical
- **WHEN** a consumer writes `import type { Rng } from "@suaveplan/testing"`
- **THEN** the imported type is structurally identical to `import type { Rng } from "@suaveplan/types"`
#### Scenario: cryptoRng satisfies canonical
- **WHEN** `const r: import("@suaveplan/types").Rng = cryptoRng`
- **THEN** `tsc` accepts (cryptoRng provides nextUint32 + nextFloat; optional members omitted)
#### Scenario: seededRng satisfies canonical
- **WHEN** `const r: import("@suaveplan/types").Rng = seededRng(42)`
- **THEN** `tsc` accepts AND `r.nextUint32()` is deterministic given seed 42
#### Scenario: byte-size guard remains green
- **WHEN** the testing-infra byte-size guard runs after `@suaveplan/types` is added as a dependency
- **THEN** the guard exits zero (because `@suaveplan/types` emits zero runtime bytes — it is a types-only package)

View File

@@ -0,0 +1,49 @@
<!-- markdownlint-disable MD024 -->
# Capability Spec Delta: types (types)
**Package**: `@suaveplan/types`
**Status**: PROPOSED
> Every requirement below carries a `**Verified by:**` line at authoring time. No exceptions.
## MODIFIED Requirements
### Requirement: Rng is the canonical random-source contract
The `Rng` interface SHALL declare `nextUint32(): number` and `nextFloat(): number` as REQUIRED members, and `nextBytes(n: number): Uint8Array` and `nextIntInRange(min: number, maxExclusive: number): number` as OPTIONAL members. Implementations SHOULD provide the optional members when efficient (CSPRNGs typically expose `getRandomValues` directly; deterministic seeded RNGs can derive both from `nextUint32`). Consumers needing them MUST narrow on presence (`if (rng.nextBytes)`). `nextIntInRange` is contractually expected to use unbiased rejection sampling, not the `% range` shortcut.
`@suaveplan/types` SHALL be the **single canonical home** for the `Rng` interface across the genesis monorepo. No other `@suaveplan/*` package MAY declare its own top-level `Rng` interface or `type Rng = ...` alias that differs structurally from the canonical export. Packages that need a narrower or wider random-source shape MUST either:
1. consume the canonical `Rng` directly via `import type { Rng } from "@suaveplan/types"`, OR
2. define a sub-interface that `extends Rng` (e.g. `@suaveplan/pseudo-random`'s `SeededRng extends Rng`), OR
3. define a distinct concept under a non-`Rng` name (e.g. `ByteSource` for a callable byte producer `(n: number) => Uint8Array`, since that is a different contract from a uniform-numeric RNG).
Packages MAY re-export the canonical `Rng` under the name `Rng` as a backwards-compatibility forwarder during a documented deprecation window; such a re-export MUST carry a prose comment naming the canonical import path and the removal change ID. The JSDoc `@deprecated` tag is banned per CLAUDE.md §2.
**Verified by:** `packages/core/types/src/rng.ts` (canonical interface declaration) AND `packages/core/types/src/rng.md` (single-source-of-truth narrative) AND repo-wide `grep -rln "interface Rng\b\|type Rng\s*=\|class Rng\b\|RngLike\b\|RandomSource\b" packages/ --include="*.ts" --exclude-dir=dist --exclude-dir=node_modules` returns exactly `packages/core/types/src/rng.ts`, `packages/random/pseudo-random/src/types/seeded-rng.types.ts` (legitimate `SeededRng extends Rng`), and the documented back-compat re-export forwarder lines in `crypto-core/random-bytes`, `crypto-core/index`, `crypto-core/primitives/index`, `crypto-core/primitives/random-bytes/index`, `crypto/index`, and `testing/context`.
#### Scenario: nextBytes is optional
- **WHEN** a consumer declares `const r: Rng = { nextUint32: () => 0, nextFloat: () => 0 }`
- **THEN** `tsc` accepts (no `nextBytes` provided)
#### Scenario: nextIntInRange signature
- **WHEN** an implementation provides `nextIntInRange(min, maxExclusive): number`
- **THEN** `tsc` accepts AND `nextIntInRange(0, 10)` returns `number`
#### Scenario: canonical Rng is the only authoritative declaration
- **WHEN** a developer runs `grep -rln "interface Rng\b\|type Rng\s*=\|class Rng\b\|RngLike\b\|RandomSource\b" packages/ --include="*.ts" --exclude-dir=dist --exclude-dir=node_modules` across the genesis monorepo
- **THEN** the only authoritative `Rng` declaration is `packages/core/types/src/rng.ts`, the only legitimate extension is `packages/random/pseudo-random/src/types/seeded-rng.types.ts`'s `SeededRng extends Rng`, and any additional matches are documented back-compat re-export forwarder lines (`export type { Rng } from "@suaveplan/types";`) — never an independent interface body or a structurally-incompatible `type Rng = ...` alias
#### Scenario: sub-interface extension is permitted
- **WHEN** a downstream package defines `interface BoundedRng extends Rng { nextIntInRange(min: number, maxExclusive: number): number }` to require the optional member
- **THEN** `tsc` accepts AND any value of type `BoundedRng` is assignable to `Rng`
#### Scenario: distinct-concept names are preserved
- **WHEN** a package needs a callable byte producer `(n: number) => Uint8Array`
- **THEN** it MUST declare that type under a distinct name (e.g. `ByteSource`) and SHALL NOT alias it as `Rng`, because the canonical `Rng` is an object with `nextUint32`/`nextFloat`, not a callable