57 lines
4.2 KiB
Markdown
57 lines
4.2 KiB
Markdown
# OpenSpec Change: barrel-explicit-exports
|
|
|
|
- **Change ID**: barrel-explicit-exports
|
|
- **Scope**: cross-cutting — every package under `packages/*/`
|
|
- **Status**: PROPOSED
|
|
- **Date**: 2026-05-03
|
|
- **Reference**: `openspec/AGENTS.md` §6a (Barrel exports — explicit named, not `export *`)
|
|
|
|
## Summary
|
|
|
|
Replace `export * from "./<feature>.js"` with explicit named re-exports in every per-feature `<feature>/index.ts` barrel across the monorepo. The package-root `src/index.ts` files are out of scope for this change — those keep `export *` for now and may be migrated to `export * as <name> from` namespace exports in a future change (also permitted under §6a). Generated selector indexes (`*.select/index.ts`) are also out of scope, since they are the one place §6a explicitly allows transparent forwarding.
|
|
|
|
The motivation is direct from §6a: the public API of a feature module must be the contract the barrel declares, not whatever the source file happens to export today. `export *` silently leaks every new symbol added to the source, which (a) defeats reviewability, (b) makes accidental private-helper exposure trivial, and (c) blurs the documented surface area against the actual one. Tree-shaking is unaffected — `"sideEffects": false` rollup/esbuild treat both forms identically.
|
|
|
|
## Scope
|
|
|
|
**In:** 536 distinct `<feature>/index.ts` files across 70 packages, totalling ~875 `export *` lines (the remaining ~37 of the 912 total live in package roots / generated selectors / impl files).
|
|
|
|
**Out:**
|
|
- 21 package-root `src/index.ts` files (deferred to a follow-up change).
|
|
- 14 `*.select/index.ts` generated selector forwarders (allowed by §6a).
|
|
- 11 non-index impl files using `export *` (e.g. `aes-256-gcm.ts` forwarding to its `.select/` directory) — these are forwarder shims for the selector mechanism, not feature barrels.
|
|
- 1 `.d.ts` third-party type stub.
|
|
|
|
## Why one cross-cutting change instead of one per package
|
|
|
|
This is a single mechanical rule application, not a design decision per package. The rule is already canonicalised in `openspec/AGENTS.md` §6a; there is nothing to negotiate per package. Splitting into 70 micro-proposals would inflate process overhead without changing what gets shipped or how it is reviewed. The per-package verification gate (§13: `lint typecheck test` + 100% coverage, plus dependent sweep on foundation packages) is preserved — the change is implemented one package at a time, gates run per package, and only after all 70 packages are green is the change archived.
|
|
|
|
## Behavior change
|
|
|
|
None. Every symbol that was reachable through `export *` remains reachable, with the same name and same type. Consumers continue to import via `import { X } from "@suaveplan/<pkg>/<feature>/<feature>"` exactly as before. The diff is barrel-internal: the barrel now lists the symbols by name instead of forwarding the whole module.
|
|
|
|
The one observable difference is what does NOT happen any more — adding a new export to `<feature>.ts` will not auto-leak through the barrel. This is the intended invariant.
|
|
|
|
## Verification per package
|
|
|
|
For every package touched:
|
|
|
|
1. `bunx turbo lint typecheck test --filter=@suaveplan/<pkg>` — green.
|
|
2. `bunx turbo test:coverage --filter=@suaveplan/<pkg>` — 100/100/100/100.
|
|
3. Foundation packages (`error`, `events`, `logger`, `validation`, plus `crypto-utils` since several primitive packages depend on it): also `bunx turbo test --filter=...@suaveplan/<pkg>` to catch dependent regressions.
|
|
4. Banned-pattern scan and missing-`.md` scan re-run to confirm no incidental regressions.
|
|
|
|
## Impact
|
|
|
|
### Affected dependents
|
|
|
|
Cross-cutting; every package in `packages/*/` is affected. Foundation packages enumerate their dependent sets via `bunx turbo ls --filter=...@suaveplan/<pkg>` per the §13 step 10 / §14a contract — those sweeps run per foundation package as it ships. For non-foundation packages, the per-package gate in step 1 above is sufficient (no dependents on a leaf package exist by definition).
|
|
|
|
### Migration
|
|
|
|
None for consumers. Internal-only refactor.
|
|
|
|
## Spec delta
|
|
|
|
`openspec/specs/monorepo-structure/spec.md` gains a `Barrel export form` requirement that codifies §6a as an enforceable spec scenario (so future packages cannot regress).
|