refactor: migrate monorepo from pnpm to Bun runtime and workspaces
- root: bun workspaces via package.json, packageManager bun@1.4.0, scripts use bun --filter - backend: Bun.serve, bun test (vitest removed), bun --watch dev script, tsconfig types bun - tests: migrate 6 test files from vitest to bun:test (mock.module) - validate: replace @hono/zod-validator with hono validator + zod safeParse (fixes TS2589) - lockfile: bunfig.toml saveTextLockfile, regenerated bun.lock (text) for turbo - docs: AGENTS.md, README.md, stack.md updated to Bun stack
This commit is contained in:
38
AGENTS.md
38
AGENTS.md
@@ -4,42 +4,42 @@ Monorepo web + API para gestión de cobros grupales. Reglas optimizadas para age
|
||||
|
||||
## Stack & ejecutables de verdad
|
||||
|
||||
- **Package manager: pnpm 9** (`packageManager` en la raíz). Usa `pnpm` para instalar (`pnpm install`), scripts y filtros. **No uses bun/npm/yarn.**
|
||||
- **Monorepo**: pnpm workspaces (`pnpm-workspace.yaml`) orquestado con **Turborepo** (`turbo.json`). Dependencias entre paquetes usan `workspace:*`.
|
||||
- **Runtime: Node** (`"type": "module"`). El backend se ejecuta con **tsx** (`tsx watch src/server.ts`); no es Bun.
|
||||
- Lockfile: `pnpm-lock.yaml` (se versiona). No existe `bun.lock`/`bun.lockb`.
|
||||
- **Package manager: Bun 1.4** (`packageManager` en la raíz). Usa `bun` para instalar (`bun install`), scripts y filtros. **No uses pnpm/npm/yarn.**
|
||||
- **Monorepo**: bun workspaces (`"workspaces"` en el `package.json` raíz) orquestado con **Turborepo** (`turbo.json`). Dependencias entre paquetes usan `workspace:*`.
|
||||
- **Runtime: Bun** (`"type": "module"`). El backend usa `Bun.serve` (`bun --watch src/server.ts`); no es Node/tsx.
|
||||
- Lockfile: `bun.lock` (texto; `bunfig.toml` fija `[install] saveTextLockfile = true` porque el `~/.bunfig.toml` global lo tiene en `false`). Se versiona. No existe `pnpm-lock.yaml`/`bun.lockb`.
|
||||
|
||||
## Comandos
|
||||
|
||||
```bash
|
||||
pnpm install # instala dependencias
|
||||
pnpm dev # levanta API (4000) y web (6173) vía turborepo
|
||||
pnpm typecheck # tsc --noEmit en todos los paquetes. Principal verificación; corre tras tocar código
|
||||
pnpm build # build de backend (prisma generate + tsc) y web (vite build)
|
||||
pnpm lint # biome check en backend y shared
|
||||
pnpm lint:fix # biome check --write
|
||||
pnpm --filter @gruperly/backend typecheck # verificación de un solo paquete
|
||||
pnpm --filter @gruperly/backend test # vitest (backend)
|
||||
pnpm --filter @gruperly/backend db:* # db:generate / db:migrate / db:push / db:studio
|
||||
bun install # instala dependencias
|
||||
bun dev # levanta API (4000) y web (6173) vía turborepo
|
||||
bun run typecheck # tsc --noEmit en todos los paquetes. Principal verificación; corre tras tocar código
|
||||
bun run build # build de backend (prisma generate + tsc) y web (vite build)
|
||||
bun run lint # biome check en backend y shared
|
||||
bun run lint:fix # biome check --write
|
||||
bun --filter @gruperly/backend typecheck # verificación de un solo paquete
|
||||
bun --filter @gruperly/backend test # bun test (backend)
|
||||
bun --filter @gruperly/backend db:* # db:generate / db:migrate / db:push / db:studio
|
||||
```
|
||||
|
||||
## Estructura y fronteras
|
||||
|
||||
- `apps/backend` — Backend Node + Hono. Entry point `src/server.ts` (serve de `@hono/node-server`, puerto **4000** vía `PORT`). `src/app.ts` monta `basePath('/api/v1')` + rutas de módulos + notFound/onError en RFC 7807. Capas:
|
||||
- `src/http/` — infraestructura HTTP: `env.ts`, `problem-details.ts` (+ `problem-builders.ts`, `problem-domain.ts`), `validate.ts` (wrapper de `@hono/zod-validator`), `request-id.ts`, `request-logger.ts`, `security-headers.ts`, `session-auth.ts` (middleware con whitelist pública `/api/v1/health`, `/api/v1/auth`, `/api/auth`).
|
||||
- `apps/backend` — Backend Bun + Hono. Entry point `src/server.ts` (`Bun.serve`, puerto **4000** vía `PORT`). `src/app.ts` monta `basePath('/api/v1')` + rutas de módulos + notFound/onError en RFC 7807. Capas:
|
||||
- `src/http/` — infraestructura HTTP: `env.ts`, `problem-details.ts` (+ `problem-builders.ts`, `problem-domain.ts`), `validate.ts` (wrapper del `validator` de hono —`hono/validator`— + `schema.safeParse`), `request-id.ts`, `request-logger.ts`, `security-headers.ts`, `session-auth.ts` (middleware con whitelist pública `/api/v1/health`, `/api/v1/auth`, `/api/auth`).
|
||||
- `src/modules/<modulo>/` — por módulo: `index.ts`, `routes.ts` y `features/<accion>/{route,use-case}.ts`. **Patrón**: el route valida (`validate.query/json`) y delega en un use-case que devuelve `Result<T, ProblemDetails>` (`ok`/`err` desde `@gruperly/shared`); el route responde con `resultJson` (o `problemJson`).
|
||||
- `src/lib/` — `prisma.ts` (`getPrismaClient`, proxy `default` y clase `UnitOfWork`), `pagination.ts` (offset/metadata), `email.ts`, `error-message.ts`.
|
||||
- `src/logger.ts` — pino + pino-pretty.
|
||||
- Módulos existentes: `health-check`, `auth` (**Better Auth 1.7.2 público**, montado en `/api/v1/auth` vía `basePath` del server; cookie de sesión con `path: "/"`), `groups`, `students`, `payments`, `waitlist` (listados paginados `{ data, pagination }`).
|
||||
- `apps/web` — Frontend React 19 + Vite + Tailwind v4. Entry `src/main.tsx` → `src/router.tsx`. Puerto **6173** (`vite.config.ts`). Auth client con `basePath: '/api/v1/auth'` (`src/lib/auth-client.ts`); el backend llama a `/api/v1/groups/from-organization` (`src/routes/organizations.tsx`).
|
||||
- `packages/shared` — Esquemas Zod (v3.24) + tipos + `Result` + Problem Details. **Se consume como TS fuente directo** (`exports` apunta a `src/index.ts`, sin build previo); se resuelve vía el symlink de pnpm en `node_modules` (`@gruperly/shared` no está en `paths` de los tsconfig). El `paths` de los tsconfig solo mapea `@/*` → `src/*` y `@generated/*` → `generated/*`.
|
||||
- `packages/shared` — Esquemas Zod (v3.24) + tipos + `Result` + Problem Details. **Se consume como TS fuente directo** (`exports` apunta a `src/index.ts`, sin build previo); se resuelve vía el symlink de bun en `node_modules` (`@gruperly/shared` no está en `paths` de los tsconfig). El `paths` de los tsconfig solo mapea `@/*` → `src/*` y `@generated/*` → `generated/*`.
|
||||
- `packages/config` — `tsconfig.base.json`; tsconfigs lo extienden con `"extends": "@gruperly/config/tsconfig.base.json"` (por eso `@gruperly/config` es devDependency de cada paquete).
|
||||
|
||||
## Gotchas operativos
|
||||
|
||||
- **Prisma 7**: requiere `apps/backend/.env` (con `DATABASE_URL`). El CLI lee `apps/backend/prisma.config.ts` (datasource `url` vía `env('DATABASE_URL')`); el schema es **multi-archivo**: `prisma/schema.prisma` (generator con output `../generated/prisma` + datasource simple) que incluye `prisma/models/*.prisma` (`auth.prisma`, `domain.prisma`). El cliente se regenra con `pnpm --filter @gruperly/backend db:generate` y se importa desde `@generated/prisma/client` (alias → `generated/prisma`, gitignoreado); se instancia con `@prisma/adapter-pg`. Sin `.env` el CLI falla; copia desde `apps/backend/.env.example`.
|
||||
- **Tests (vitest)**: viven en `apps/backend/test/`. Mockean el módulo `@/lib/prisma` con `vi.mock` (usando `vi.hoisted` si el mock se comparte como `tx`); los use-cases ponen el db mockable en `constructor(deps)`. Los tests NO pasan por `sessionAuthMiddleware`; si el route lee `c.get('user')`, inyecta un usuario con un middleware en el harness.
|
||||
- **zod en v3.24 (pin exacto)**, no v4: `better-call` (transitiva de better-auth) pide `zod ^4` — ese peer warning es conocido y aceptado.
|
||||
- **Prisma 7**: requiere `apps/backend/.env` (con `DATABASE_URL`). El CLI lee `apps/backend/prisma.config.ts` (datasource `url` vía `env('DATABASE_URL')`); el schema es **multi-archivo**: `prisma/schema.prisma` (generator con output `../generated/prisma` + datasource simple) que incluye `prisma/models/*.prisma` (`auth.prisma`, `domain.prisma`). El cliente se regenra con `bun --filter @gruperly/backend db:generate` y se importa desde `@generated/prisma/client` (alias → `generated/prisma`, gitignoreado); se instancia con `@prisma/adapter-pg`. Sin `.env` el CLI falla; copia desde `apps/backend/.env.example`.
|
||||
- **Tests (bun test)**: viven en `apps/backend/test/`. Mockean el módulo `@/lib/prisma` con `mock.module` (si el mock se comparte como `tx`, declara la const fuera del mock; `vi` de `bun:test` NO tiene `vi.mocked`). Los use-cases ponen el db mockable en `constructor(deps)`. Los tests NO pasan por `sessionAuthMiddleware`; si el route lee `c.get('user')`, inyecta un usuario con un middleware en el harness.
|
||||
- **zod en v3.24 (pin exacto)**, no v4: `better-call` (transitiva de better-auth) pide `zod ^4` — ese peer warning es conocido y aceptado. `validator` de hono (`hono/validator`) sustituyó a `@hono/zod-validator` (sus tipos duales zod v3/v4 causaban TS2589/exhaustion no resolubles con `ZodTypeAny`); las rutas tipan `c.req.valid` vía el `ValidationInput` derivado del schema.
|
||||
- `.env`, `dist/`, `.turbo/`, `apps/backend/generated/`, `node_modules/` están gitignoreados (que no te extrañe su ausencia).
|
||||
|
||||
## Convenciones repo-específicas
|
||||
|
||||
Reference in New Issue
Block a user