7.3 KiB
7.3 KiB
AGENTS.md — Gruperly
Monorepo web + API para gestión de cobros grupales. Reglas optimizadas para agentes.
Stack & ejecutables de verdad
- Package manager: Bun 1.4 (
packageManageren la raíz). Usabunpara instalar (bun install), scripts y filtros. No uses pnpm/npm/yarn. - Monorepo: bun workspaces (
"workspaces"en elpackage.jsonraíz) orquestado con Turborepo (turbo.json). Dependencias entre paquetes usanworkspace:*. - Runtime: Bun (
"type": "module"). El backend usaBun.serve(bun --watch src/server.ts); no es Node/tsx. - Lockfile:
bun.lock(texto;bunfig.tomlfija[install] saveTextLockfile = trueporque el~/.bunfig.tomlglobal lo tiene enfalse). Se versiona. No existepnpm-lock.yaml/bun.lockb.
Comandos
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 Bun + Hono. Entry pointsrc/server.ts(Bun.serve, puerto 4000 víaPORT).src/app.tsmontabasePath('/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 delvalidatorde 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.tsyfeatures/<accion>/{route,use-case}.ts. Patrón: el route valida (validate.query/json) y delega en un use-case que devuelveResult<T, ProblemDetails>(ok/errdesde@gruperly/shared); el route responde conresultJson(oproblemJson).src/lib/—prisma.ts(getPrismaClient, proxydefaulty claseUnitOfWork),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/authvíabasePathdel server; cookie de sesión conpath: "/"),groups,attendees,payments,waitlist(listados paginados{ data, pagination }).
apps/web— Frontend React 19 + Vite + Tailwind v4. Entrysrc/main.tsx→src/router.tsx. Puerto 6173 (vite.config.ts). Auth client conbasePath: '/api/v1/auth'(src/lib/auth-client.ts).packages/shared— Esquemas Zod (v3.24) + tipos +Result+ Problem Details. Se consume como TS fuente directo (exportsapunta asrc/index.ts, sin build previo); se resuelve vía el symlink de bun ennode_modules(@gruperly/sharedno está enpathsde los tsconfig). Elpathsde 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/configes devDependency de cada paquete).
Gotchas operativos
- Prisma 7: requiere
apps/backend/.env(conDATABASE_URL). El CLI leeapps/backend/prisma.config.ts(datasourceurlvíaenv('DATABASE_URL')); el schema es multi-archivo:prisma/schema.prisma(generator con output../generated/prisma+ datasource simple) que incluyeprisma/models/*.prisma(auth.prisma,domain.prisma). El cliente se regenra conbun --filter @gruperly/backend db:generatey se importa desde@generated/prisma/client(alias →generated/prisma, gitignoreado); se instancia con@prisma/adapter-pg. Sin.envel CLI falla; copia desdeapps/backend/.env.example. - Tests (bun test): viven en
apps/backend/test/. Mockean el módulo@/lib/prismaconmock.module(si el mock se comparte comotx, declara la const fuera del mock;videbun:testNO tienevi.mocked). Los use-cases ponen el db mockable enconstructor(deps). Los tests NO pasan porsessionAuthMiddleware; si el route leec.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) pidezod ^4— ese peer warning es conocido y aceptado.validatorde hono (hono/validator) sustituyó a@hono/zod-validator(sus tipos duales zod v3/v4 causaban TS2589/exhaustion no resolubles conZodTypeAny); las rutas tipanc.req.validvía elValidationInputderivado del schema. .env,dist/,.turbo/,apps/backend/generated/,node_modules/están gitignoreados (que no te extrañe su ausencia).
Convenciones repo-específicas
- Frontend en español: copy de UI, comentarios y textos en español.
- Base de datos: todas las tablas usan snake_case vía
@@mapenprisma/models/*.prisma(p. ej.User→users,WaitlistEntry→waitlist_entries). Al agregar un modelo nuevo, incluir siempre@@map("nombre_tabla"). - API responde errores en Problem Details RFC 7807 (
application/problem+json) y resultados como{ data, pagination }; los "use cases" devuelvenResulty nunca lanzan excepciones de dominio. - Tailwind v4 con tokens en
@themedentro deapps/web/src/index.css(p. ej.--color-accent: #1e90ff). Se usan como clases auto-generadas:text-accent,bg-success-soft, etc. Radius por defecto0.75rem(rounded-xl). - Router NO es file-based (aunque
stack.mdlo diga): las rutas se declaran manualmente enapps/web/src/router.tsxconcreateRoute+addChildreny se registran vía module augmentation. Cada vista nueva debe añadirse ahí. - Nav (Inicio/Grupos/Cobros/Ajustes) vive en
apps/web/src/components/layout/nav-items.ts; es la fuente única paraBottomNav(móvil) ySidebar(desktop) — no dupliques la lista. - UI: primitivos propios en
apps/web/src/components/ui/(avatar, button, badge) más helpercn()ensrc/lib/utils.ts(clsx + tailwind-merge). Aunquestack.mdmencione Shadcn, todavía no está instalado (sin Radix); úsalos directos. - Layout mobile-first:
RootLayoutusa columnamax-w-mden móvil y dos columnas (Sidebar + contenidomax-w-6xl) enlg+. - Sin "volver atrás" en mobile: en ninguna vista se muestra un link/botón de navegación a la página anterior cuando se ve en mobile (se apoya en el gesto de back del dispositivo). Los links de "volver" solo en desktop: usar
hidden ... lg:inline-flex(p. ej.create-group,group-detail) ohidden lg:flex(Breadcrumb). No confundir con botones "Volver" de pasos dentro de un wizard/formulario: esos sí se mantienen.
Fuentes de contexto
stack.md— arquitectura y spec técnica de referencia (UI tokens exactos, stack por app).README.md— setup de la base de datos y comandos generales.