| Phase | Description | Status | Branch/PR |
|---|---|---|---|
| 1 | yarn → pnpm | DONE | migrate-pnpm-shared-tsx |
| 2 | tsx for API dev | DONE | migrate-pnpm-shared-tsx |
| 3 | internal package pattern | DONE | migrate-pnpm-shared-tsx |
- pnpm workspaces + Turbo, 3 workspaces (
api,web,e2e) - API dev uses
tsx watch(hot reload), production stilltscbuild +node dist/... - Web depends on
"api": "workspace:*", resolves toapi/dist/src/lib/index.js pnpm build(turbo) builds api first (tsc), then web (vite) — required because web resolvesfrom "api"to compileddist/- 27 type-only imports + 1 runtime value (
languages, but web defines its own copy) - Effectively zero runtime coupling between api→web
- No shared package: tRPC already provides end-to-end type safety. All web→api imports are type-only. A
@catalogi/sharedpackage would add indirection without real benefit. - Internal package pattern instead: point api's
main/typesto raw.tssource so Vite resolves types directly — notscbuild needed beforeweb:dev.
Swapped package manager. Dockerfiles, CI, hooks, docs all updated.
devscript:dotenv -e ../.env -- tsx watch src/entrypoints/start-api.tsdb:seedscript:dotenv -e ../.env -- tsx scripts/seed.ts- tsx added as devDependency
- Production scripts (
start,job:*) unchanged — stillnode dist/...
Ref: https://turborepo.dev/blog/you-might-not-need-typescript-project-references
Currently web resolves from "api" to api/dist/src/lib/index.js (compiled JS). This means tsc must run before web:dev works. With the internal package pattern, api's main/types point to raw .ts source. Vite natively transpiles .ts imports from node_modules — no build step needed for dev.
Bundle safety: web imports from api are almost entirely import type (erased at transpile time). The only runtime export is languages, which web doesn't import (has its own copy). Vite/Rollup tree-shaking ensures zero api code ends up in the web bundle.
"main": "dist/src/lib/index.js" → "./src/lib/index.ts"
"types": "dist/src/lib/index.d.ts" → "./src/lib/index.ts"
Keep "files", "build" script, and everything else unchanged. tsc build is still needed for:
- Production Docker builds
- CI type checking
- Any future npm publishing (unlikely, but
"files"already declaresdist/)
web:build must still depend on api:build ("dependsOn": ["^build"]) — tRPC type inference needs the compiled router. But web:dev no longer needs a prior api:build.
No turbo.json changes needed (the "dependsOn": ["^build"] on build is correct, dev has no deps).
No changes. All exports are already export type or re-exports. Vite handles .ts resolution natively.
- Production runtime:
tscbuild +node dist/... - Dockerfiles: still
RUN pnpm build, CMDpnpm start - Helm charts, docker-compose: no change
- CI:
pnpm buildstill runs tsc api/tsconfig.json: still emits todist/
# Remove dist to prove web:dev works without it
rm -rf api/dist
# Dev should work (Vite resolves .ts directly)
pnpm dev
# Build should still work (tsc compiles for production)
pnpm build
pnpm typecheck
pnpm test- Editor imports: VSCode may suggest imports from deep api paths (
api/src/core/...) instead offrom "api". Existing behavior, not new. - Accidental runtime imports: if someone adds a non-type import from api that pulls in heavy code (express, kysely, pg...), it would bloat the web bundle. Mitigated by: all current exports are type-only, and code review catches new runtime exports.
tscbuild still required forpnpm build: the internal package pattern only eliminates the build requirement for dev. Production/CI still builds normally.
Phases 3 and 4 from the original plan proposed a @catalogi/shared package for leaf and domain types. This was dropped because:
- tRPC already provides type safety —
TrpcRouterInput/TrpcRouterOutputinfer types end-to-end - All web→api imports are type-only — zero runtime coupling, nothing to "share" at runtime
api/src/lib/already acts as the public API contract — a shared package would just re-export the same types with extra indirection- Single consumer — only web imports from api. Shared packages make sense with multiple consumers.