feat(F-141): completed feature

This commit is contained in:
chattie
2026-08-21 22:02:01 +02:00
parent 0a1d32ecc3
commit 022347349d
17 changed files with 253 additions and 31 deletions

View File

@@ -0,0 +1,42 @@
# F-141 — Arquitectura del fix de build Next.js
## Diagnóstico
Hay tres aplicaciones Next independientes en el monorepo:
- `project/apps/admin` — panel admin.
- `project/frontend` — customer frontend, con `/categories` como Server Component async.
- `project/storefront` — storefront SEO.
`project/frontend` no tenía `turbopack.root` ni `outputFileTracingRoot`. Además, `/categories` declara ISR (`revalidate = 3600`) y ejecuta `fetchCategories()` durante el prerender. Cuando `NEXT_PUBLIC_API_URL` apunta a `192.168.18.93:3000` y el backend no está levantado, el build falla con `ECONNREFUSED`.
El error posterior que intenta leer:
```text
project/frontend/.next/prerender-manifest.json
```
es una consecuencia de que el build del frontend aborta antes de generar su manifest, no un archivo que deba copiarse desde otra aplicación.
## Diseño
1. Fijar la raíz de Turbopack a cada aplicación:
- `project/frontend/next.config.ts`: `turbopack.root = frontendRoot`.
- Mantener la configuración equivalente ya aplicada en admin y storefront.
2. Fijar `outputFileTracingRoot` a la raíz de cada app para que Next no recorra el workspace ni confunda `.next` de otra aplicación.
3. Convertir `/categories` a renderizado dinámico (`dynamic = 'force-dynamic'`) porque sus datos dependen del API vivo. Así el build no hace llamadas de negocio al backend; el fetch sucede cuando se atiende la petición.
4. Mantener la fuente de verdad y el contrato API intactos. No introducir mocks, datos paralelos ni fallback de categorías vacío.
## No incluido
- No cambiar las URLs del API.
- No ocultar errores de runtime.
- No modificar `storefront` más allá de la configuración necesaria.
- No transformar todos los reportes o dashboards; eso pertenece a F-142.
## Verificación
- `cd project/frontend && rm -rf .next && NEXT_PUBLIC_API_URL=http://192.168.18.93:3000 npm run build` debe terminar aunque el backend no esté disponible.
- La salida no debe seleccionar `project/package-lock.json` ni mostrar el warning de lockfiles.
- `project/frontend/.next/prerender-manifest.json` debe existir después del build.
- Admin, backend y storefront no deben perder su build.

View File

@@ -0,0 +1,52 @@
# F-141 — Implementer evidence
## Cambios realizados
- `project/frontend/next.config.ts`
- Añadidos `turbopack.root` y `outputFileTracingRoot`, ambos fijados a `frontendRoot`.
- El frontend ya no hereda la raíz de workspace de `project/package-lock.json` ni intenta mezclar el `.next` de `frontend` con otra aplicación.
- Rutas del customer frontend que consultan catálogo/CMS en servidor:
- `src/app/page.tsx`
- `src/app/categories/page.tsx`
- `src/app/categories/[slug]/page.tsx`
- `src/app/brands/page.tsx`
- `src/app/brands/[slug]/page.tsx`
- `src/app/products/page.tsx`
- `src/app/products/[slug]/page.tsx`
- `src/app/admin/products/page.tsx`
- Se cambió ISR estático (`revalidate = 3600`) por `dynamic = 'force-dynamic'` donde los datos son obligatorios.
- `/search` ya era dinámico y no requirió cambios.
## Motivo técnico
`/categories` ejecutaba `fetchCategories()` durante el prerender estático. Con `NEXT_PUBLIC_API_URL=http://192.168.18.93:3000` y backend apagado, Next abortaba el build con `ECONNREFUSED`. Convertir las páginas dependientes de API en dinámicas mantiene la fuente de verdad en el backend y traslada el fetch al request runtime; no añade mocks ni datos paralelos.
El error posterior de `frontend/.next/prerender-manifest.json` era consecuencia del build abortado antes de escribir el manifest. Tras el build correcto el archivo vuelve a existir.
## Verificación
```text
cd project/frontend
rm -rf .next
NEXT_PUBLIC_API_URL=http://192.168.18.93:3000 npm run build ✅
```
Resultado frontend:
- sin warning de múltiples lockfiles;
- sin `ECONNREFUSED`;
- sin error de prerender `/categories` ni `/brands`;
- TypeScript correcto;
- `project/frontend/.next/prerender-manifest.json` presente;
- rutas de catálogo aparecen como `ƒ` (server-rendered on demand).
También verificado:
```text
project/apps/admin: npm run build ✅
project/storefront: npm run build ✅
project/frontend: npx tsc --noEmit ✅
./scripts/verify.sh ✅
```
No se modificaron contratos API ni el backend. La aplicación sigue obteniendo ventas/catálogo del sistema real en runtime.

View File

@@ -0,0 +1,20 @@
{
"feature_id": "F-141",
"agent": "leader",
"verdict": "APPROVED",
"summary": "F-141 cerrado: se eliminaron los warnings de raíz de workspace del customer frontend y el build ya no depende de que el backend esté disponible durante el prerender de catálogo.",
"checks": [
"reviewer.json APPROVED",
"security.json APPROVED",
"qa.json APPROVED",
"frontend clean build with API unavailable passed",
"frontend prerender-manifest.json present",
"admin build passed",
"storefront build passed",
"backend build/tests passed",
"verify.sh passed"
],
"commit_message": "fix(F-141): isolate frontend Next build from API prerender",
"next_step": "Start F-142: Reporting architecture and implementation task plan",
"closed_at": "2026-08-21T20:04:00Z"
}

View File

@@ -0,0 +1,21 @@
{
"feature_id": "F-141",
"agent": "qa",
"stage": "qa_gate",
"verdict": "APPROVED",
"reviewed_at": "2026-08-21T20:03:00Z",
"summary": "La regresión está cubierta: el customer frontend compila con la URL del backend inaccesible, genera el manifest y clasifica las páginas dependientes del API como dinámicas. Las otras aplicaciones y backend siguen verdes.",
"acceptance_traceability": [
{"criterion":"No workspace-root warning in frontend build","ok":true,"evidence":"NEXT_PUBLIC_API_URL=http://192.168.18.93:3000 npm run build: no multiple-lockfile warning"},
{"criterion":"/categories build does not fail on ECONNREFUSED","ok":true,"evidence":"Clean frontend build completed with unreachable 192.168.18.93:3000; /categories listed as ƒ"},
{"criterion":"No missing prerender manifest after successful build","ok":true,"evidence":"test -f project/frontend/.next/prerender-manifest.json → manifest-present"},
{"criterion":"Admin regression check","ok":true,"evidence":"rm -rf apps/admin/.next && npm run build → exit 0"},
{"criterion":"Storefront regression check","ok":true,"evidence":"rm -rf storefront/.next && npm run build → exit 0"},
{"criterion":"Backend regression check","ok":true,"evidence":"npm run build → exit 0; npm test → 191 passed, 56 skipped"},
{"criterion":"TypeScript checks","ok":true,"evidence":"frontend npx tsc --noEmit → exit 0; backend build typecheck → exit 0"},
{"criterion":"Verify harness","ok":true,"evidence":"./scripts/verify.sh → exit 0"}
],
"checks": [],
"issues": [],
"notes":"The second prerender-manifest ENOENT was validated as a cascade from the aborted frontend build; a successful clean build recreates the manifest in project/frontend/.next."
}

View File

@@ -0,0 +1,19 @@
{
"feature_id": "F-141",
"agent": "reviewer",
"stage": "review_gate",
"verdict": "APPROVED",
"reviewed_at": "2026-08-21T20:02:00Z",
"summary": "El build del customer frontend queda aislado de las demás aplicaciones Next y deja de depender de que el backend esté accesible durante el prerender. La solución preserva el API real en runtime y no introduce datos simulados.",
"checks": [
{"item":"Turbopack root is explicit","ok":true,"evidence":"frontend/next.config.ts sets turbopack.root to frontendRoot"},
{"item":"Output tracing root is explicit","ok":true,"evidence":"frontend/next.config.ts sets outputFileTracingRoot to frontendRoot"},
{"item":"Categories no longer calls API during build","ok":true,"evidence":"categories/page.tsx exports dynamic='force-dynamic'; clean build passes with API URL 192.168.18.93:3000 unavailable"},
{"item":"Other uncached catalog routes covered","ok":true,"evidence":"home, brands, products, detail routes and legacy admin products route use runtime rendering"},
{"item":"No fallback/mock source introduced","ok":true,"evidence":"Existing fetch functions and API contracts remain unchanged"},
{"item":"Existing app builds preserved","ok":true,"evidence":"admin and storefront production builds exit 0; backend build exit 0"},
{"item":"Manifest failure explained and resolved","ok":true,"evidence":"frontend/.next/prerender-manifest.json exists after clean build"}
],
"issues": [],
"notes":"Dynamic rendering is intentional for API-dependent catalog pages. Static informational pages remain static where their existing CMS fetch is already guarded with catch()."
}

View File

@@ -0,0 +1,17 @@
{
"feature_id": "F-141",
"agent": "security",
"stage": "security_gate",
"verdict": "APPROVED",
"reviewed_at": "2026-08-21T20:02:30Z",
"summary": "El cambio solo afecta a la configuración de build y al momento de ejecución de lecturas públicas del catálogo. No amplía permisos, no expone secretos y no modifica endpoints.",
"checks": [
{"item":"No secrets committed","ok":true,"evidence":"No .env files or credentials modified; NEXT_PUBLIC_API_URL remains environment-provided"},
{"item":"No API security bypass","ok":true,"evidence":"Runtime requests continue through existing fetch functions and backend routes"},
{"item":"No sensitive data moved to build output","ok":true,"evidence":"Catalog pages are server-rendered on demand instead of serializing failed/build-time API data"},
{"item":"Workspace isolation safe","ok":true,"evidence":"Both Turbopack and output tracing roots are constrained to project/frontend"},
{"item":"Error behavior remains explicit","ok":true,"evidence":"Required catalog fetches still throw on HTTP/network failures at runtime; no silent empty mock is returned"}
],
"issues": [],
"notes":"Dynamic server rendering may expose runtime availability errors to the normal Next error boundary if the backend is down. This is preferable to silently shipping an empty catalog and is unchanged from the existing runtime semantics."
}