Skip to content
This repository was archived by the owner on Jul 26, 2026. It is now read-only.
This repository was archived by the owner on Jul 26, 2026. It is now read-only.

[Architecture] Replace wildcard source exports with explicit package APIs #44

Description

@mhbdev

Summary

Several workspace packages expose broad wildcard subpaths directly to TypeScript source files:

  • @upstand/usecases exports ./*./src/*.ts.
  • @upstand/infrastructure exports both ./* and ./*/* → source paths.
  • @upstand/domain, @upstand/api, and @upstand/auth also expose wildcard source entrypoints.

The architecture test prevents imports containing /src/, but wildcard exports make most source modules public anyway. Consumers currently import implementation-specific paths such as @upstand/api/routers/index, @upstand/api/ai/upgal, @upstand/usecases/tokens, and individual use-case files.

This weakens package encapsulation: internal file layout effectively becomes the public API, and the compiler consumes package source instead of a curated contract surface.

Reviewed against master at 7f9e26400a474aa5a706d261f24fa2bf3d98ce8c.

Architectural impact

  • Moving or splitting internal files becomes a repository-wide breaking change.
  • Hosts and packages can bypass intended facades and import implementation details.
  • Layer rules operate at package level but cannot distinguish public ports from private adapters/helpers within a package.
  • Accidental side effects or Node-only modules can be imported through a seemingly valid workspace path.
  • Build tools must understand and compile the full source graph of dependencies.
  • Cycles can form through deep internal entrypoints while the top-level package dependency graph remains acyclic.

Proposed direction

  1. Define explicit supported subpath exports for each package, for example:
    • @upstand/usecases/ports;
    • @upstand/usecases/commands/deploy-resource;
    • @upstand/domain/repositories;
    • @upstand/infrastructure/docker;
    • a private composition-only tokens entrypoint if still required.
  2. Remove catch-all ./* and ./*/* exports.
  3. Keep internal modules unexported and import them only relatively inside their package.
  4. Add package-level API barrels organized by stable capability rather than current directory shape.
  5. Consider emitting declaration/runtime artifacts to dist for packages used across hosts, while retaining source maps and workspace development ergonomics.
  6. Add an architecture test that checks every cross-package import against the target package's declared export map.
  7. Add internal module-cycle detection, not only workspace package-cycle detection.
  8. Document semver expectations for each public package entrypoint.

Acceptance criteria

  • Workspace packages no longer expose unrestricted wildcard source paths.
  • Every cross-package import uses an explicitly supported entrypoint.
  • Internal helpers, adapters, DI tokens, and implementation modules cannot be imported by unrelated packages.
  • Architecture tests fail on undeclared/deep internal imports and internal dependency cycles.
  • Public entrypoints are side-effect classified and browser/server compatibility is documented.
  • Refactoring an internal file does not require changes outside the package unless a public contract changes.
  • Package build/type-check workflows continue to work in the monorepo and production images.

Suggested migration order

  1. Inventory current cross-package deep imports.
  2. Introduce explicit aliases without removing wildcards.
  3. Migrate hosts and packages to the explicit aliases.
  4. Enable the export-map architecture check.
  5. Remove wildcard exports package by package.
  6. Evaluate compiled artifacts after API surfaces are stable.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions