claude-code-arcane 1.0.1 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/agents/engineering/e2e-tester.md +65 -0
- package/agents/engineering/nestjs-engineer.md +74 -0
- package/agents/engineering/nextjs-engineer.md +72 -0
- package/agents/engineering/nextjs-reviewer.md +40 -0
- package/hooks/statusline.sh +57 -31
- package/package.json +1 -1
- package/profiles/backend-nestjs.yaml +58 -0
- package/profiles/backend-nextjs.yaml +56 -0
- package/rules/nestjs-code.md +73 -0
- package/rules/nextjs-code.md +60 -0
- package/rules/react-perf.md +59 -0
- package/skills/ai-sdk-setup/SKILL.md +82 -0
- package/skills/nestjs-best-practices/SKILL.md +70 -0
- package/skills/nestjs-best-practices/references/api-design.md +34 -0
- package/skills/nestjs-best-practices/references/architecture.md +53 -0
- package/skills/nestjs-best-practices/references/database.md +37 -0
- package/skills/nestjs-best-practices/references/dependency-injection.md +45 -0
- package/skills/nestjs-best-practices/references/error-handling.md +43 -0
- package/skills/nestjs-best-practices/references/microservices-devops.md +41 -0
- package/skills/nestjs-best-practices/references/performance.md +32 -0
- package/skills/nestjs-best-practices/references/security.md +50 -0
- package/skills/nestjs-best-practices/references/testing.md +32 -0
- package/skills/nestjs-scaffold/SKILL.md +75 -0
- package/skills/nextjs-best-practices/SKILL.md +64 -0
- package/skills/nextjs-best-practices/references/advanced.md +21 -0
- package/skills/nextjs-best-practices/references/bundle-size.md +32 -0
- package/skills/nextjs-best-practices/references/client-fetching.md +20 -0
- package/skills/nextjs-best-practices/references/javascript.md +47 -0
- package/skills/nextjs-best-practices/references/rendering.md +46 -0
- package/skills/nextjs-best-practices/references/rerender.md +55 -0
- package/skills/nextjs-best-practices/references/server.md +47 -0
- package/skills/nextjs-best-practices/references/waterfalls.md +41 -0
- package/skills/nextjs-scaffold/SKILL.md +76 -0
- package/skills/pgvector-search/SKILL.md +67 -0
- package/skills/seo-nextjs/SKILL.md +81 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nextjs-best-practices
|
|
3
|
+
description: "React + Next.js performance: 64 reglas en 8 categorías (waterfalls, bundle, server, client fetching, re-render, rendering, JS, advanced) priorizadas por impacto, de Vercel Engineering. Usar al escribir/optimizar React o Next.js."
|
|
4
|
+
category: "frontend"
|
|
5
|
+
argument-hint: "[waterfalls|bundle|server|rerender|rendering|js|all]"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Bash, Write, Edit, Task
|
|
8
|
+
---
|
|
9
|
+
# nextjs-best-practices — Performance priorizada por impacto
|
|
10
|
+
|
|
11
|
+
64 reglas de optimización de performance para React y Next.js, de Vercel Engineering, organizadas en 8 categorías priorizadas de **CRITICAL** a **LOW**. Detalle por categoría en `references/`.
|
|
12
|
+
|
|
13
|
+
## MANDATORY WORKFLOW
|
|
14
|
+
|
|
15
|
+
**Diagnóstico dirigido por métricas, no aplicar las 64 reglas a ciegas** (este es el enfoque `vercel-optimize`: medir primero, optimizar solo lo que las métricas señalan).
|
|
16
|
+
|
|
17
|
+
### Step 0: Medir / localizar
|
|
18
|
+
|
|
19
|
+
1. **¿Hay métricas?** Lighthouse, Web Vitals (LCP/INP/CLS), bundle analyzer, traces de Vercel.
|
|
20
|
+
2. **¿Cuál es el síntoma dominante?** TTFB alto → waterfalls/server. Bundle grande / TBT → bundle size. Jank en interacción → re-render. Layout shift → rendering.
|
|
21
|
+
3. Identificar las rutas/componentes concretos que las métricas apuntan. Optimizar **esos**, no todo.
|
|
22
|
+
|
|
23
|
+
### Step 1: Priorizar por impacto
|
|
24
|
+
|
|
25
|
+
| Prioridad | Categoría | Impacto | Reference |
|
|
26
|
+
|-----------|-----------|---------|-----------|
|
|
27
|
+
| 1 | Eliminating Waterfalls | CRITICAL | `references/waterfalls.md` |
|
|
28
|
+
| 2 | Bundle Size Optimization | CRITICAL | `references/bundle-size.md` |
|
|
29
|
+
| 3 | Server-Side Performance | HIGH | `references/server.md` |
|
|
30
|
+
| 4 | Client-Side Data Fetching | MEDIUM-HIGH | `references/client-fetching.md` |
|
|
31
|
+
| 5 | Re-render Optimization | MEDIUM | `references/rerender.md` |
|
|
32
|
+
| 6 | Rendering Performance | MEDIUM | `references/rendering.md` |
|
|
33
|
+
| 7 | JavaScript Performance | LOW-MEDIUM | `references/javascript.md` |
|
|
34
|
+
| 8 | Advanced Patterns | LOW | `references/advanced.md` |
|
|
35
|
+
|
|
36
|
+
### Step 2: Aplicar + medir de nuevo
|
|
37
|
+
|
|
38
|
+
Aplicar la regla, re-medir. Si la métrica no mejoró, revertir y subir de categoría — no acumular micro-optimizaciones sin impacto medible.
|
|
39
|
+
|
|
40
|
+
### Step 3: Checklist de cierre
|
|
41
|
+
|
|
42
|
+
- [ ] Fetches independientes en paralelo (`Promise.all`), no en cascada
|
|
43
|
+
- [ ] Server Components por defecto; `'use client'` empujado abajo en el árbol
|
|
44
|
+
- [ ] Sin barrel imports en el critical path; componentes pesados con `next/dynamic`
|
|
45
|
+
- [ ] Server Actions autenticadas individualmente
|
|
46
|
+
- [ ] Sin componentes definidos dentro de componentes
|
|
47
|
+
- [ ] `next/image` + `generateMetadata` + resource hints donde corresponde
|
|
48
|
+
- [ ] Re-medir Web Vitals post-cambios
|
|
49
|
+
|
|
50
|
+
Si todo el checklist pasa → optimización **COMPLETE**. Antes de escribir cambios significativos, confirmar el approach con el usuario (Question → Decision → Approval).
|
|
51
|
+
|
|
52
|
+
## Tabla síntoma → categoría
|
|
53
|
+
|
|
54
|
+
| Métrica mala | Categoría |
|
|
55
|
+
|--------------|-----------|
|
|
56
|
+
| TTFB / tiempo de respuesta del server | Waterfalls, Server |
|
|
57
|
+
| First Load JS / TBT alto | Bundle Size |
|
|
58
|
+
| INP / jank en interacción | Re-render, JS |
|
|
59
|
+
| LCP | Rendering, Bundle, Server |
|
|
60
|
+
| CLS / flicker en hydration | Rendering |
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
_Adaptado de [vercel-labs/agent-skills](https://github.com/vercel-labs/agent-skills) (react-best-practices, MIT, © Vercel). 64 reglas priorizadas por impacto, reorganizadas al formato skill+references de Arcane._
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# 8. Advanced Patterns (LOW)
|
|
2
|
+
|
|
3
|
+
Patrones de borde con `useEffectEvent` y setup de app. Impacto bajo pero evitan bugs sutiles.
|
|
4
|
+
|
|
5
|
+
## 8.4 useEffectEvent for Stable Callback Refs — LOW
|
|
6
|
+
Referencias de callback estables para componentes memoizados sin re-disparar effects.
|
|
7
|
+
```tsx
|
|
8
|
+
const onTick = useEffectEvent(() => doSomething(latestProp));
|
|
9
|
+
useEffect(() => { const id = setInterval(onTick, 1000); return () => clearInterval(id); }, []);
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## 8.1 Do Not Put Effect Events in Dependency Arrays — LOW
|
|
13
|
+
Los Effect Events no van en el array de dependencias; se llaman desde el effect, no lo re-disparan.
|
|
14
|
+
|
|
15
|
+
## 8.2 Initialize App Once, Not Per Mount — LOW
|
|
16
|
+
Setup de app a nivel module-load, no en effects/mounts de componente.
|
|
17
|
+
|
|
18
|
+
## 8.3 Store Event Handlers in Refs — LOW
|
|
19
|
+
Handlers que cambian seguido en refs → no causan re-render.
|
|
20
|
+
|
|
21
|
+
_Ref: https://react.dev/reference/react/experimental_useEffectEvent_
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# 2. Bundle Size Optimization (CRITICAL)
|
|
2
|
+
|
|
3
|
+
## 2.1 Avoid Barrel File Imports — CRITICAL
|
|
4
|
+
Los barrels (`index.ts` que re-exportan todo) arrastran miles de módulos sin usar.
|
|
5
|
+
```ts
|
|
6
|
+
// ✗ import { debounce } from 'lodash'; (todo lodash)
|
|
7
|
+
// ✓ import debounce from 'lodash/debounce';
|
|
8
|
+
```
|
|
9
|
+
Configurar `optimizePackageImports` en `next.config.js` para libs grandes.
|
|
10
|
+
|
|
11
|
+
## 2.4 Dynamic Imports for Heavy Components — CRITICAL
|
|
12
|
+
```tsx
|
|
13
|
+
const Chart = dynamic(() => import('./Chart'), { ssr: false, loading: () => <Skeleton/> });
|
|
14
|
+
```
|
|
15
|
+
Para charts, editores de texto rico, modales, mapas — todo lo no necesario en el primer render.
|
|
16
|
+
|
|
17
|
+
## 2.5 Prefer Statically Analyzable Paths — HIGH
|
|
18
|
+
Imports con paths literales/mapas explícitos, no construidos en runtime → permite tree-shaking.
|
|
19
|
+
|
|
20
|
+
## 2.2 Conditional Module Loading — HIGH
|
|
21
|
+
Cargar data/módulos grandes solo cuando la feature se activa.
|
|
22
|
+
|
|
23
|
+
## 2.6 Preload Based on User Intent — MEDIUM
|
|
24
|
+
Precargar el bundle pesado en hover/focus, antes de que se necesite.
|
|
25
|
+
```tsx
|
|
26
|
+
<button onMouseEnter={() => import('./HeavyModal')}>Abrir</button>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 2.3 Defer Non-Critical Third-Party Libraries — MEDIUM
|
|
30
|
+
Analytics, logging, error tracking → cargar después de hydration, no bloquear el bundle inicial (`next/script` con `strategy="afterInteractive"` o `lazyOnload`).
|
|
31
|
+
|
|
32
|
+
_Ref: https://nextjs.org/docs/app/api-reference/components/script · https://nextjs.org/docs/app/api-reference/config/next-config-js/optimizePackageImports_
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# 4. Client-Side Data Fetching (MEDIUM-HIGH)
|
|
2
|
+
|
|
3
|
+
## 4.3 Use SWR for Automatic Deduplication — MEDIUM-HIGH
|
|
4
|
+
Dedup de requests, caching y revalidación entre instancias de componente.
|
|
5
|
+
```ts
|
|
6
|
+
const { data } = useSWR(`/api/user/${id}`, fetcher); // mismo key = 1 request
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## 4.2 Use Passive Event Listeners for Scrolling — MEDIUM
|
|
10
|
+
```ts
|
|
11
|
+
el.addEventListener('touchstart', handler, { passive: true }); // scroll inmediato
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 4.4 Version and Minimize localStorage Data — MEDIUM
|
|
15
|
+
Prefijo de versión en las keys (`app:v2:prefs`) y guardar solo campos necesarios → evita conflictos y reduce tamaño.
|
|
16
|
+
|
|
17
|
+
## 4.1 Deduplicate Global Event Listeners — LOW
|
|
18
|
+
Compartir listeners globales entre instancias con un `Map` + `useSWRSubscription()` en vez de uno por componente.
|
|
19
|
+
|
|
20
|
+
_Ref: https://swr.vercel.app_
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# 7. JavaScript Performance (LOW-MEDIUM)
|
|
2
|
+
|
|
3
|
+
Micro-optimizaciones. Aplicar solo en hot paths comprobados por profiling — no preventivamente.
|
|
4
|
+
|
|
5
|
+
## 7.8 Early Length Check for Array Comparisons — MEDIUM-HIGH
|
|
6
|
+
Comparar `length` antes de operaciones caras (sort/deep-compare).
|
|
7
|
+
|
|
8
|
+
## 7.1 Avoid Layout Thrashing — MEDIUM
|
|
9
|
+
Agrupar escrituras de estilo, luego leer layout una vez → evita reflows síncronos forzados.
|
|
10
|
+
|
|
11
|
+
## 7.4 Cache Repeated Function Calls — MEDIUM
|
|
12
|
+
`Map` para memoizar resultados de llamadas repetidas con mismos inputs.
|
|
13
|
+
|
|
14
|
+
## 7.7 Defer Non-Critical Work with requestIdleCallback — MEDIUM
|
|
15
|
+
Analytics, logging, prefetch en periodos idle del browser.
|
|
16
|
+
|
|
17
|
+
## 7.2 Build Index Maps for Repeated Lookups — LOW-MEDIUM
|
|
18
|
+
`Map` para O(1) en vez de múltiples `.find()`.
|
|
19
|
+
|
|
20
|
+
## 7.3 Cache Property Access in Loops — LOW-MEDIUM
|
|
21
|
+
Cachear `obj.prop` antes del loop.
|
|
22
|
+
|
|
23
|
+
## 7.5 Cache Storage API Calls — LOW-MEDIUM
|
|
24
|
+
Cachear lecturas de `localStorage`/`sessionStorage` en memoria.
|
|
25
|
+
|
|
26
|
+
## 7.6 Combine Multiple Array Iterations — LOW-MEDIUM
|
|
27
|
+
Fusionar `.filter()`/`.map()` múltiples en un solo loop.
|
|
28
|
+
|
|
29
|
+
## 7.9 Early Return from Functions — LOW-MEDIUM
|
|
30
|
+
Retornar apenas el resultado está determinado.
|
|
31
|
+
|
|
32
|
+
## 7.10 Hoist RegExp Creation — LOW-MEDIUM
|
|
33
|
+
`RegExp` a nivel módulo, no recrear en cada llamada/render.
|
|
34
|
+
|
|
35
|
+
## 7.11 Use flatMap to Map and Filter in One Pass — LOW-MEDIUM
|
|
36
|
+
`.flatMap(x => cond ? [f(x)] : [])` en vez de `.map().filter(Boolean)`.
|
|
37
|
+
|
|
38
|
+
## 7.13 Use Set/Map for O(1) Lookups — LOW
|
|
39
|
+
`Set.has()` en vez de `.includes()`/`.indexOf()` repetidos.
|
|
40
|
+
|
|
41
|
+
## 7.12 Use Loop for Min/Max Instead of Sort — LOW
|
|
42
|
+
Loop O(n) para min/max en vez de sort O(n log n).
|
|
43
|
+
|
|
44
|
+
## 7.14 Use toSorted() Instead of sort() — LOW
|
|
45
|
+
`.toSorted()` para inmutabilidad (no muta el original).
|
|
46
|
+
|
|
47
|
+
_Ref: https://developer.mozilla.org/docs/Web/API/Window/requestIdleCallback_
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# 6. Rendering Performance (MEDIUM)
|
|
2
|
+
|
|
3
|
+
## 6.2 CSS content-visibility for Long Lists — HIGH
|
|
4
|
+
```css
|
|
5
|
+
.row { content-visibility: auto; contain-intrinsic-size: 0 80px; }
|
|
6
|
+
```
|
|
7
|
+
Difiere render y paint de lo off-screen.
|
|
8
|
+
|
|
9
|
+
## 6.8 Use defer or async on Script Tags — HIGH
|
|
10
|
+
`defer`/`async` para no bloquear el render durante el parsing.
|
|
11
|
+
|
|
12
|
+
## 6.10 Use React DOM Resource Hints — HIGH
|
|
13
|
+
```ts
|
|
14
|
+
import { preload, preconnect } from 'react-dom';
|
|
15
|
+
preconnect('https://cdn.example.com');
|
|
16
|
+
preload('/hero.jpg', { as: 'image' });
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## 6.5 Prevent Hydration Mismatch Without Flickering — MEDIUM
|
|
20
|
+
Script inline síncrono que actualiza el DOM antes de hidratar (ej: theme).
|
|
21
|
+
|
|
22
|
+
## 6.7 Use Activity Component for Show/Hide — MEDIUM
|
|
23
|
+
`<Activity>` preserva estado y DOM para componentes que togglean visibilidad seguido.
|
|
24
|
+
|
|
25
|
+
## 6.6 Suppress Expected Hydration Mismatches — LOW-MEDIUM
|
|
26
|
+
`suppressHydrationWarning` solo para diferencias server/client intencionales (fechas, IDs).
|
|
27
|
+
|
|
28
|
+
## 6.1 Animate SVG Wrapper Instead of SVG Element — LOW
|
|
29
|
+
Envolver el SVG en un div y animar el wrapper → aceleración por hardware.
|
|
30
|
+
|
|
31
|
+
## 6.3 Hoist Static JSX Elements — LOW
|
|
32
|
+
JSX estático fuera del componente → no se recrea en cada render.
|
|
33
|
+
|
|
34
|
+
## 6.4 Optimize SVG Precision — LOW
|
|
35
|
+
Reducir precisión de coordenadas SVG → menor tamaño, sin impacto visual.
|
|
36
|
+
|
|
37
|
+
## 6.9 Use Explicit Conditional Rendering — LOW
|
|
38
|
+
Ternario en vez de `&&` → evita renderizar `0` u otros falsy.
|
|
39
|
+
```tsx
|
|
40
|
+
{count > 0 ? <Badge n={count}/> : null} // no {count && ...}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 6.11 Use useTransition Over Manual Loading States — LOW
|
|
44
|
+
`useTransition` da pending state automático en vez de `useState` manual.
|
|
45
|
+
|
|
46
|
+
_Ref: https://developer.mozilla.org/docs/Web/CSS/content-visibility · https://react.dev/reference/react-dom/preload_
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# 5. Re-render Optimization (MEDIUM)
|
|
2
|
+
|
|
3
|
+
## 5.4 Don't Define Components Inside Components — HIGH
|
|
4
|
+
Definirlos inline los remonta en cada render (pierde estado y DOM). Definir a nivel módulo o extraer.
|
|
5
|
+
```tsx
|
|
6
|
+
// ✗ function Parent(){ function Row(){...}; return <Row/> }
|
|
7
|
+
// ✓ Row a nivel módulo, fuera de Parent
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## 5.1 Calculate Derived State During Rendering — MEDIUM
|
|
11
|
+
Computar de props/state durante el render, no guardarlo en `useState` (se desincroniza).
|
|
12
|
+
```tsx
|
|
13
|
+
const fullName = `${first} ${last}`; // ✓ no useState + useEffect
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## 5.11 Use Functional setState Updates — MEDIUM
|
|
17
|
+
`setCount(c => c + 1)` — opera sobre el último valor, callbacks estables.
|
|
18
|
+
|
|
19
|
+
## 5.12 Use Lazy State Initialization — MEDIUM
|
|
20
|
+
`useState(() => expensiveInit())` — no recomputa en cada render.
|
|
21
|
+
|
|
22
|
+
## 5.13 Use Transitions for Non-Urgent Updates — MEDIUM
|
|
23
|
+
`startTransition(() => setFilter(x))` — mantiene la UI responsiva.
|
|
24
|
+
|
|
25
|
+
## 5.14 useDeferredValue for Expensive Derived Renders — MEDIUM
|
|
26
|
+
Mantiene el input responsivo difiriendo el resultado pesado.
|
|
27
|
+
|
|
28
|
+
## 5.15 Use useRef for Transient Values — MEDIUM
|
|
29
|
+
Valores que cambian seguido y no son UI → `useRef`, no re-render.
|
|
30
|
+
|
|
31
|
+
## 5.2 Defer State Reads to Usage Point — MEDIUM
|
|
32
|
+
Leer state dinámico dentro del callback, no suscribirse en render time.
|
|
33
|
+
|
|
34
|
+
## 5.5 Extract Default Non-primitive Values — MEDIUM
|
|
35
|
+
Defaults objeto/función a constantes a nivel módulo → preserva memoización.
|
|
36
|
+
|
|
37
|
+
## 5.6 Extract to Memoized Components — MEDIUM
|
|
38
|
+
Aislar trabajo caro en componentes memoizados con early returns.
|
|
39
|
+
|
|
40
|
+
## 5.8 Put Interaction Logic in Event Handlers — MEDIUM
|
|
41
|
+
Side effects de interacción van en handlers, no en `useEffect`.
|
|
42
|
+
|
|
43
|
+
## 5.9 Split Combined Hook Computations — MEDIUM
|
|
44
|
+
Separar hooks con dependencias distintas → no recomputar lo independiente.
|
|
45
|
+
|
|
46
|
+
## 5.10 Subscribe to Derived State — MEDIUM
|
|
47
|
+
Suscribirse a un booleano derivado, no al valor continuo → menos renders.
|
|
48
|
+
|
|
49
|
+
## 5.3 Don't Wrap Simple Expressions in useMemo — LOW-MEDIUM
|
|
50
|
+
El overhead del hook supera el cómputo de un primitivo.
|
|
51
|
+
|
|
52
|
+
## 5.7 Narrow Effect Dependencies — LOW
|
|
53
|
+
Dependencias primitivas, no objetos enteros.
|
|
54
|
+
|
|
55
|
+
_Ref: https://react.dev/reference/react/useTransition · https://react.dev/learn/you-might-not-need-an-effect_
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# 3. Server-Side Performance (HIGH)
|
|
2
|
+
|
|
3
|
+
## 3.1 Authenticate Server Actions Like API Routes — CRITICAL
|
|
4
|
+
Cada Server Action verifica auth/authz adentro, no solo confía en middleware.
|
|
5
|
+
```ts
|
|
6
|
+
'use server';
|
|
7
|
+
export async function deletePost(id: string) {
|
|
8
|
+
const session = await auth();
|
|
9
|
+
if (!session?.user) throw new Error('Unauthorized');
|
|
10
|
+
if (!canDelete(session.user, id)) throw new Error('Forbidden');
|
|
11
|
+
// ...
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 3.7 Parallel Data Fetching with Component Composition — CRITICAL
|
|
16
|
+
Componer para que fetches independientes corran a la vez (cada uno en su `<Suspense>`).
|
|
17
|
+
|
|
18
|
+
## 3.8 Parallel Nested Data Fetching — CRITICAL
|
|
19
|
+
Encadenar fetches dependientes dentro de la promesa de cada item → un item lento no bloquea a los demás.
|
|
20
|
+
|
|
21
|
+
## 3.3 Avoid Shared Module State for Request Data — HIGH
|
|
22
|
+
Variables mutables a nivel módulo se comparten entre requests → data leaks. Mantener datos de request locales al árbol de render.
|
|
23
|
+
|
|
24
|
+
## 3.5 Hoist Static I/O to Module Level — HIGH
|
|
25
|
+
Lecturas estáticas (config, archivos) a nivel módulo, no en cada request.
|
|
26
|
+
|
|
27
|
+
## 3.6 Minimize Serialization at RSC Boundaries — HIGH
|
|
28
|
+
Pasar solo los campos que el cliente usa, no el objeto entero.
|
|
29
|
+
|
|
30
|
+
## 3.4 Cross-Request LRU Caching — HIGH
|
|
31
|
+
LRU cache para datos compartidos entre requests dentro de una ventana de tiempo.
|
|
32
|
+
|
|
33
|
+
## 3.9 Per-Request Deduplication with React.cache() — MEDIUM
|
|
34
|
+
```ts
|
|
35
|
+
export const getUser = cache(async (id) => db.user.find(id)); // dedup mismo arg en el request
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 3.10 Use after() for Non-Blocking Operations — MEDIUM
|
|
39
|
+
```ts
|
|
40
|
+
import { after } from 'next/server';
|
|
41
|
+
after(() => logAnalytics(event)); // corre tras enviar la respuesta
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 3.2 Avoid Duplicate Serialization in RSC Props — LOW
|
|
45
|
+
Transformar en el cliente cuando evita duplicar referencias serializadas.
|
|
46
|
+
|
|
47
|
+
_Ref: https://nextjs.org/docs/app/building-your-application/data-fetching · https://react.dev/reference/react/cache_
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# 1. Eliminating Waterfalls (CRITICAL)
|
|
2
|
+
|
|
3
|
+
El factor #1 de TTFB lento. Operaciones que esperan en serie cuando podrían correr en paralelo.
|
|
4
|
+
|
|
5
|
+
## 1.5 Promise.all() for Independent Operations — CRITICAL
|
|
6
|
+
```ts
|
|
7
|
+
// ✗ secuencial: 300ms + 300ms
|
|
8
|
+
const user = await getUser(id);
|
|
9
|
+
const posts = await getPosts(id);
|
|
10
|
+
// ✓ paralelo: 300ms
|
|
11
|
+
const [user, posts] = await Promise.all([getUser(id), getPosts(id)]);
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 1.4 Prevent Waterfall Chains in API Routes — CRITICAL
|
|
15
|
+
Iniciar operaciones independientes inmediatamente, await recién al usarlas.
|
|
16
|
+
```ts
|
|
17
|
+
const userP = getUser(id); // arranca ya
|
|
18
|
+
const settingsP = getSettings(id); // arranca ya
|
|
19
|
+
return { user: await userP, settings: await settingsP };
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 1.3 Dependency-Based Parallelization — CRITICAL
|
|
23
|
+
Cuando hay dependencias parciales, arrancar cada tarea en el momento más temprano posible (libs como `better-all` lo automatizan). No serializar todo por una sola dependencia.
|
|
24
|
+
|
|
25
|
+
## 1.7/3.7 Parallel Data Fetching with Component Composition — CRITICAL
|
|
26
|
+
Reestructurar componentes para que fetches independientes corran simultáneamente en vez de anidados.
|
|
27
|
+
|
|
28
|
+
## 1.6 Strategic Suspense Boundaries — HIGH
|
|
29
|
+
```tsx
|
|
30
|
+
<Suspense fallback={<Skeleton/>}>
|
|
31
|
+
<SlowData /> {/* streamea; el shell se muestra ya */}
|
|
32
|
+
</Suspense>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 1.1 Check Cheap Conditions Before Async Flags — HIGH
|
|
36
|
+
Evaluar guards síncronos antes de operaciones async para saltar trabajo en cold paths.
|
|
37
|
+
|
|
38
|
+
## 1.2 Defer Await Until Needed — HIGH
|
|
39
|
+
Mover el `await` a la rama donde realmente se usa, no bloquear ramas que no lo necesitan.
|
|
40
|
+
|
|
41
|
+
_Ref: https://react.dev/reference/react/Suspense_
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nextjs-scaffold
|
|
3
|
+
description: "Scaffold de app Next.js 15+ App Router con TypeScript estricto, shadcn/ui, Tailwind, RSC y estructura production-ready. Usar para iniciar un proyecto Next nuevo o agregar la base de UI."
|
|
4
|
+
category: "frontend"
|
|
5
|
+
argument-hint: "[project-name]"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Bash, Write, Edit, Task
|
|
8
|
+
---
|
|
9
|
+
# nextjs-scaffold — Next.js + shadcn/ui Scaffolder
|
|
10
|
+
|
|
11
|
+
## MANDATORY WORKFLOW
|
|
12
|
+
|
|
13
|
+
**Antes de generar código, completar en orden.**
|
|
14
|
+
|
|
15
|
+
### Step 0: Gather Requirements
|
|
16
|
+
Clarificar (o inferir del contexto):
|
|
17
|
+
1. **Nombre del proyecto** (kebab-case)
|
|
18
|
+
2. **Router:** App Router (default) / Pages (legacy)
|
|
19
|
+
3. **Styling:** Tailwind + shadcn/ui (default) / CSS Modules
|
|
20
|
+
4. **Data layer:** Server Actions + RSC (default) / API routes / tRPC
|
|
21
|
+
5. **Auth:** Auth.js (NextAuth) / Clerk / ninguna
|
|
22
|
+
6. **DB:** Postgres + Prisma / Drizzle / ninguna
|
|
23
|
+
7. **Deploy:** Vercel (default) / Docker / self-host
|
|
24
|
+
|
|
25
|
+
Si ya está especificado, saltar al Step 1.
|
|
26
|
+
|
|
27
|
+
### Step 1: Crear base
|
|
28
|
+
```bash
|
|
29
|
+
npx create-next-app@latest <name> --ts --app --tailwind --eslint --src-dir --import-alias "@/*"
|
|
30
|
+
cd <name>
|
|
31
|
+
npx shadcn@latest init # style: default, base color: slate
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Step 2: Estructura
|
|
35
|
+
```
|
|
36
|
+
src/
|
|
37
|
+
app/ # App Router: layouts, pages, route handlers
|
|
38
|
+
(marketing)/ # route groups
|
|
39
|
+
api/ # route handlers solo si hace falta
|
|
40
|
+
components/
|
|
41
|
+
ui/ # shadcn primitives
|
|
42
|
+
lib/ # utils, db client, auth
|
|
43
|
+
server/ # server actions, data access (RSC-only)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Step 3: Defaults no negociables
|
|
47
|
+
- `tsconfig`: `"strict": true`, `noUncheckedIndexedAccess`
|
|
48
|
+
- **Server Components por defecto**; `'use client'` solo en hojas interactivas
|
|
49
|
+
- `next/image` + `generateMetadata` en cada page
|
|
50
|
+
- Env vars validadas con Zod (`src/lib/env.ts`)
|
|
51
|
+
- ESLint flat config + `@typescript-eslint/no-floating-promises`
|
|
52
|
+
|
|
53
|
+
### Step 4: Agregar piezas (invocar skills relacionadas)
|
|
54
|
+
- SEO → skill `seo-nextjs`
|
|
55
|
+
- Búsqueda semántica → skill `pgvector-search`
|
|
56
|
+
- Features de IA → skill `ai-sdk-setup`
|
|
57
|
+
- Performance → skill `nextjs-best-practices`
|
|
58
|
+
|
|
59
|
+
### Step 5: Verificar
|
|
60
|
+
`npm run build` limpio + `npx next lint`. Revisar contra las rules `nextjs-code` y `react-perf`. Build limpio + lint sin errores → scaffold **READY**.
|
|
61
|
+
|
|
62
|
+
## Stack Defaults
|
|
63
|
+
|
|
64
|
+
| Componente | Default |
|
|
65
|
+
|------------|---------|
|
|
66
|
+
| Next.js | 15+ App Router |
|
|
67
|
+
| Lenguaje | TypeScript estricto |
|
|
68
|
+
| UI | shadcn/ui + Tailwind |
|
|
69
|
+
| Data | Server Actions + RSC |
|
|
70
|
+
| Validación | Zod |
|
|
71
|
+
| ORM | Prisma |
|
|
72
|
+
| Deploy | Vercel |
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
_Inspirado en `nextjs-shadcn` de [laguagu/claude-code-nextjs-skills](https://github.com/laguagu/claude-code-nextjs-skills). Adaptado al formato Arcane._
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pgvector-search
|
|
3
|
+
description: "Búsqueda semántica con Postgres + pgvector: embeddings, schema con columna vector, índices HNSW/IVFFlat, queries de similitud y RAG. Usar para implementar semantic search o retrieval sobre Postgres."
|
|
4
|
+
category: "backend"
|
|
5
|
+
argument-hint: "[setup|index|query|rag]"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Bash, Write, Edit, Task
|
|
8
|
+
---
|
|
9
|
+
# pgvector-search — Búsqueda semántica con Postgres
|
|
10
|
+
|
|
11
|
+
## MANDATORY WORKFLOW
|
|
12
|
+
|
|
13
|
+
**Antes de generar o modificar código/schema, completar estos pasos en orden.**
|
|
14
|
+
|
|
15
|
+
### Step 0: Gather Requirements
|
|
16
|
+
1. **Modelo de embeddings** y dimensión (ej: OpenAI `text-embedding-3-small` = 1536; Cohere = 1024)
|
|
17
|
+
2. **Volumen** de vectores (define el índice: HNSW para alta recall, IVFFlat para datasets grandes/escritura frecuente)
|
|
18
|
+
3. **Métrica de distancia:** cosine (`<=>`, default para texto) / L2 (`<->`) / inner product (`<#>`)
|
|
19
|
+
4. ¿Es para RAG (retrieval + LLM) o solo ranking de similitud?
|
|
20
|
+
|
|
21
|
+
### Step 1: Habilitar extensión + schema
|
|
22
|
+
```sql
|
|
23
|
+
CREATE EXTENSION IF NOT EXISTS vector;
|
|
24
|
+
CREATE TABLE documents (
|
|
25
|
+
id bigserial PRIMARY KEY,
|
|
26
|
+
content text NOT NULL,
|
|
27
|
+
embedding vector(1536) NOT NULL,
|
|
28
|
+
metadata jsonb
|
|
29
|
+
);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Step 2: Índice
|
|
33
|
+
```sql
|
|
34
|
+
-- HNSW: mejor recall/latencia, recomendado por defecto
|
|
35
|
+
CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);
|
|
36
|
+
-- IVFFlat (alternativa para datasets muy grandes): definir lists ≈ rows/1000
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Step 3: Insertar embeddings
|
|
40
|
+
Generar el embedding del `content` con el modelo y guardarlo. (Si usás Next + AI SDK, ver skill `ai-sdk-setup`.)
|
|
41
|
+
```ts
|
|
42
|
+
const { embedding } = await embed({ model, value: content });
|
|
43
|
+
await sql`INSERT INTO documents (content, embedding) VALUES (${content}, ${JSON.stringify(embedding)})`;
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Step 4: Query de similitud
|
|
47
|
+
```sql
|
|
48
|
+
SELECT content, 1 - (embedding <=> $1) AS similarity
|
|
49
|
+
FROM documents
|
|
50
|
+
ORDER BY embedding <=> $1 -- usa el índice
|
|
51
|
+
LIMIT 5;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Step 5: Buenas prácticas
|
|
55
|
+
- **Chunking** del contenido antes de embeddear (no documentos enteros)
|
|
56
|
+
- Filtrar por `metadata` (jsonb) + similitud para hybrid search
|
|
57
|
+
- `SET hnsw.ef_search` para tunear recall vs latencia
|
|
58
|
+
- Re-embeddear si cambia el modelo (la dimensión debe matchear)
|
|
59
|
+
- No mezclar embeddings de modelos distintos en la misma columna
|
|
60
|
+
|
|
61
|
+
## Cierre
|
|
62
|
+
|
|
63
|
+
Verificar que el índice se usa (`EXPLAIN ANALYZE` debe mostrar Index Scan, no Seq Scan) y medir recall/latencia con queries reales. Schema + índice + query validados → setup **READY**. Confirmar cambios de schema con el usuario antes de aplicarlos. Siguiente paso: skill `ai-sdk-setup` para generar embeddings, o `database-indexing` para tuning.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
_Inspirado en `postgres-semantic-search` de [laguagu/claude-code-nextjs-skills](https://github.com/laguagu/claude-code-nextjs-skills). Adaptado al formato Arcane. Relacionado: skill `database-indexing`._
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: seo-nextjs
|
|
3
|
+
description: "SEO técnico para Next.js App Router: Metadata API, generateMetadata dinámico, sitemap.ts, robots.ts, JSON-LD structured data, Open Graph y canonical. Usar al agregar o auditar SEO en una app Next."
|
|
4
|
+
category: "frontend"
|
|
5
|
+
argument-hint: "[metadata|sitemap|jsonld|audit]"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Bash, Write, Edit, Task
|
|
8
|
+
---
|
|
9
|
+
# seo-nextjs — SEO técnico para Next.js
|
|
10
|
+
|
|
11
|
+
## MANDATORY WORKFLOW
|
|
12
|
+
|
|
13
|
+
**Antes de generar o modificar código, completar estos pasos en orden.**
|
|
14
|
+
|
|
15
|
+
### Step 0: Determinar scope
|
|
16
|
+
1. ¿App nueva o auditoría de SEO existente?
|
|
17
|
+
2. ¿Contenido estático, dinámico (DB/CMS) o mixto?
|
|
18
|
+
3. ¿Necesita structured data (JSON-LD) para rich results? (artículos, productos, FAQ, breadcrumbs)
|
|
19
|
+
|
|
20
|
+
### Step 1: Metadata base (`app/layout.tsx`)
|
|
21
|
+
```ts
|
|
22
|
+
export const metadata: Metadata = {
|
|
23
|
+
metadataBase: new URL('https://example.com'),
|
|
24
|
+
title: { default: 'Site', template: '%s · Site' },
|
|
25
|
+
description: '...',
|
|
26
|
+
openGraph: { type: 'website', siteName: 'Site', images: ['/og.png'] },
|
|
27
|
+
twitter: { card: 'summary_large_image' },
|
|
28
|
+
alternates: { canonical: '/' },
|
|
29
|
+
};
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Step 2: Metadata dinámica (`generateMetadata`)
|
|
33
|
+
```ts
|
|
34
|
+
export async function generateMetadata({ params }): Promise<Metadata> {
|
|
35
|
+
const post = await getPost(params.slug);
|
|
36
|
+
return {
|
|
37
|
+
title: post.title,
|
|
38
|
+
description: post.excerpt,
|
|
39
|
+
alternates: { canonical: `/blog/${post.slug}` },
|
|
40
|
+
openGraph: { images: [post.cover] },
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
Nunca tags `<meta>` manuales en el body — siempre la Metadata API.
|
|
45
|
+
|
|
46
|
+
### Step 3: sitemap.ts y robots.ts
|
|
47
|
+
```ts
|
|
48
|
+
// app/sitemap.ts
|
|
49
|
+
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
|
50
|
+
const posts = await getAllPosts();
|
|
51
|
+
return [{ url: 'https://example.com', changeFrequency: 'daily' },
|
|
52
|
+
...posts.map(p => ({ url: `https://example.com/blog/${p.slug}`, lastModified: p.updatedAt }))];
|
|
53
|
+
}
|
|
54
|
+
// app/robots.ts → reglas + link al sitemap
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Step 4: JSON-LD structured data
|
|
58
|
+
```tsx
|
|
59
|
+
<script type="application/ld+json"
|
|
60
|
+
dangerouslySetInnerHTML={{ __html: JSON.stringify({
|
|
61
|
+
'@context': 'https://schema.org', '@type': 'Article',
|
|
62
|
+
headline: post.title, datePublished: post.date, author: {...} }) }} />
|
|
63
|
+
```
|
|
64
|
+
Validar con Rich Results Test de Google.
|
|
65
|
+
|
|
66
|
+
### Step 5: Checklist
|
|
67
|
+
- [ ] `metadataBase` seteado (evita URLs OG rotas)
|
|
68
|
+
- [ ] Canonical en cada page indexable
|
|
69
|
+
- [ ] OG image 1200×630 por page importante
|
|
70
|
+
- [ ] sitemap.ts + robots.ts presentes
|
|
71
|
+
- [ ] JSON-LD donde aplique rich result
|
|
72
|
+
- [ ] Sin `noindex` accidental en prod
|
|
73
|
+
- [ ] LCP/CLS sanos (ver skill `nextjs-best-practices`)
|
|
74
|
+
|
|
75
|
+
## Cierre
|
|
76
|
+
|
|
77
|
+
Validar el structured data con el Rich Results Test de Google y los meta tags con el inspector del navegador. Si el checklist pasa → SEO **READY**. Confirmar cambios con el usuario antes de escribir. Siguiente paso: skill `nextjs-best-practices` para Web Vitals, o `ai-seo` para estrategia de contenido.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
_Inspirado en las skills de SEO de [laguagu/claude-code-nextjs-skills](https://github.com/laguagu/claude-code-nextjs-skills). Adaptado al formato Arcane. Relacionado: skill `ai-seo`._
|