openprompt-lang 0.3.0
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/LICENSE +21 -0
- package/README.md +663 -0
- package/bin/cli.js +110 -0
- package/bin/lint.js +50 -0
- package/docs/COMMANDS.md +229 -0
- package/docs/COMMITS/INDEX.md +11 -0
- package/docs/COMMITS/v0.1.0-existing.md +31 -0
- package/docs/COMMITS/v0.1.0-inicial.md +50 -0
- package/docs/COMMITS/v0.1.0-readme.md +24 -0
- package/docs/COMMITS/v0.2.0-strict-db-templates.md +50 -0
- package/docs/COMMITS/v0.3.0-parser-fixes-vscode.md +67 -0
- package/docs/COMMITS/v0.3.0-versioning-component.md +44 -0
- package/docs/DEPENDENCIES.md +45 -0
- package/docs/FRAMEWORK.md +1741 -0
- package/docs/SYNTAX.md +359 -0
- package/docs/VERSIONING.md +150 -0
- package/docs/referencia-metodologia/Anexos Finales Documentos de Respaldo y Estandarizaci/303/263n.md" +90 -0
- package/docs/referencia-metodologia/Cotizaciones.md +84 -0
- package/docs/referencia-metodologia/Example.md +1 -0
- package/docs/referencia-metodologia/ExtractorInformacion.py +78 -0
- package/docs/referencia-metodologia/Fase - 1 .- Desarrollo de la Metodolog/303/255a.md" +67 -0
- package/docs/referencia-metodologia/Fase - 2 .- Levantamiento de requisitos generales y traduccion a la IA.md +64 -0
- package/docs/referencia-metodologia/Fase - 3 .- Prototipado visual con IA (Figma Maker o equivalentes).md +64 -0
- package/docs/referencia-metodologia/Fase - 4 .- Especificacion de requisitos e iteracion con el cliente.md +58 -0
- package/docs/referencia-metodologia/Fase - 5 .- Estructuracion y maquetado de funciones (Scaffolding).md +118 -0
- package/docs/referencia-metodologia/Fase - 6 .- Estructuracion del backlog y division de tareas.md +48 -0
- package/docs/referencia-metodologia/Fase - 7 .- Desarrollo activo, pruebas y control de versiones.md +98 -0
- package/docs/referencia-metodologia/Fase - 8 .- Entrega, capacitaci/303/263n y mantenimiento.md" +55 -0
- package/docs/referencia-metodologia/Figma prompt template.md +130 -0
- package/docs/referencia-metodologia/Framework de Desarrollo Asistido por IA.md +1741 -0
- package/docs/referencia-metodologia/Indice General.md +83 -0
- package/docs/referencia-metodologia/Prompt refactorizar o creacion desde cero.md +50 -0
- package/docs/referencia-metodologia/docs/CONVENCIONES_DB.md +410 -0
- package/docs/referencia-metodologia/docs/CONVENCIONES_DOCUMENTACION.md +209 -0
- package/docs/referencia-metodologia/docs/PROMPTS/INDEX.md +73 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/01-hook-supabase.md +79 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/02-componente-ui.md +82 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/03-pagina-feature.md +70 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/04-comando-tauri.md +56 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/05-store-zustand.md +74 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/06-servicio-supabase.md +74 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/07-formulario-validacion.md +63 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/08-hook-capacitor.md +65 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/09-refactor-division.md +51 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/10-scaffolding-inicial.md +79 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/11-supabase-crud-service.md +114 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/12-supabase-hook-usetable.md +143 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/13-tauri-command-rust.md +84 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/14-tauri-wrapper-typescript.md +92 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/15-documentar-tabla-db.md +50 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/16-diagrama-arquitectura.md +60 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/17-documentar-api-rpc.md +56 -0
- package/docs/referencia-metodologia/docs/PROMPTS/STACK/ionic-capacitor.md +52 -0
- package/docs/referencia-metodologia/docs/PROMPTS/STACK/react-web-puro.md +46 -0
- package/docs/referencia-metodologia/docs/PROMPTS/STACK/tauri-desktop.md +53 -0
- package/package.json +56 -0
- package/schemas/prompt-lang.json +98 -0
- package/src/commands/component.js +326 -0
- package/src/commands/context.js +206 -0
- package/src/commands/figma.js +63 -0
- package/src/commands/init.js +373 -0
- package/src/commands/suggest.js +31 -0
- package/src/commands/validate.js +183 -0
- package/src/generators/figma-prompt.js +56 -0
- package/src/utils/ai.js +143 -0
- package/src/utils/annotations.js +510 -0
- package/src/utils/config.js +60 -0
- package/vscode-extension/README.md +31 -0
- package/vscode-extension/language-configuration.json +7 -0
- package/vscode-extension/package.json +62 -0
- package/vscode-extension/snippets/promptlang.json +105 -0
- package/vscode-extension/syntaxes/annotations.tmGrammar.json +39 -0
- package/vscode-extension/syntaxes/promptlang.tmGrammar.json +14 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Metodología de Desarrollo Asistido por IA
|
|
3
|
+
author: Matías Retamal
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
tags:
|
|
6
|
+
- metodologia
|
|
7
|
+
- saas
|
|
8
|
+
- ia
|
|
9
|
+
- arquitectura
|
|
10
|
+
date: 2026-04-25
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# 🧠 Metodología de Desarrollo Asistido por IA
|
|
14
|
+
|
|
15
|
+
> [!info] Sobre este documento
|
|
16
|
+
> Este índice interactivo contiene la estructura definitiva del ciclo de vida de desarrollo de software para proyectos SaaS y Pymes, optimizado para el trabajo Full-Stack y la delegación algorítmica de código mediante Inteligencia Artificial.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 🗂️ Índice General
|
|
21
|
+
|
|
22
|
+
> [!abstract] ETAPA I: Descubrimiento y Planificación Estratégica
|
|
23
|
+
> Comprende desde la primera reunión hasta la congelación del presupuesto y el alcance del MVP.
|
|
24
|
+
>
|
|
25
|
+
> **[[#1. Fase 1: Levantamiento de requisitos generales (Discovery)|1. Fase 1: Levantamiento de requisitos generales (Discovery)]]**
|
|
26
|
+
> - a. Detallado del funcionamiento del negocio y su dolor principal
|
|
27
|
+
> - b. Definición de límites comerciales (Alcance inicial)
|
|
28
|
+
>
|
|
29
|
+
> **[[#2. Fase 2: Traducción técnica y contratos (Historias de usuario)|2. Fase 2: Traducción técnica y contratos (Historias de usuario)]]**
|
|
30
|
+
> - a. Creación de Historias de Usuario
|
|
31
|
+
> - b. Traducción a definiciones de métodos (El Contrato Estructural)
|
|
32
|
+
>
|
|
33
|
+
> **[[#3. Fase 3: Diseño UI/UX y estructura visual (Wireframing)|3. Fase 3: Diseño UI/UX y estructura visual (Wireframing)]]**
|
|
34
|
+
> - a. Diseño de Prototipos interactivos
|
|
35
|
+
> - b. Estructura de vistas y componentes (Preparación para React)
|
|
36
|
+
>
|
|
37
|
+
> **[[#4. Fase 4: Especificación de requisitos e iteración con el cliente|4. Fase 4: Especificación de requisitos e iteración con el cliente]]**
|
|
38
|
+
> - a. Integración de correcciones del cliente (Feedback Loop)
|
|
39
|
+
> - b. Ciclo de preguntas, control de alcance y presupuesto final
|
|
40
|
+
> - c. Especificación de nuevos elementos funcionales
|
|
41
|
+
> - d. Bibliotecas necesarias y factibilidad técnica
|
|
42
|
+
> - e. Checklist de Aprobación: Inicio Oficial de Desarrollo (Go / No-Go)
|
|
43
|
+
|
|
44
|
+
> [!example] ETAPA II: Arquitectura y Preparación para IA
|
|
45
|
+
> Diseño de la arquitectura de carpetas, esqueletos de código (Scaffolding) y división estricta de tareas.
|
|
46
|
+
>
|
|
47
|
+
> **[[#5. Fase 5: Estructuración y maquetado de funciones (Scaffolding)|5. Fase 5: Estructuración y maquetado de funciones (Scaffolding)]]**
|
|
48
|
+
> - a. Creación de estructura básica de archivos de documentación
|
|
49
|
+
> - b. Definición de estructura de carpetas definitiva
|
|
50
|
+
> - c. Creación de archivos y esqueletos base (Scaffolding)
|
|
51
|
+
> - d. Ejemplo práctico de definición técnica (Contrato en Código)
|
|
52
|
+
> - e. Diagramas de apoyo técnico
|
|
53
|
+
> - f. Prompt Maestro para Generación de Scaffolding con IA
|
|
54
|
+
>
|
|
55
|
+
> **[[#6. Fase 6: Estructuración del backlog y división de tareas|6. Fase 6: Estructuración del backlog y división de tareas]]**
|
|
56
|
+
> - a. Sistema de Gestión Centralizado (Enfoque JSON-Driven)
|
|
57
|
+
> - b. Estrategia de Desarrollo por Capas (Layered Development)
|
|
58
|
+
> - c. Granularidad Extrema para la IA (Micro-Tasking)
|
|
59
|
+
> - d. Refinamiento Continuo (Refactoring Temprano)
|
|
60
|
+
|
|
61
|
+
> [!tip] ETAPA III: Desarrollo, Testing y Puesta en Producción
|
|
62
|
+
> Programación activa, control de versiones semántico y transferencia del software al cliente.
|
|
63
|
+
>
|
|
64
|
+
> **[[#7. Fase 7: Desarrollo activo, pruebas y control de versiones|7. Fase 7: Desarrollo activo, pruebas y control de versiones]]**
|
|
65
|
+
> - a. Metodología de Pruebas (Testing y Criterios de Aceptación)
|
|
66
|
+
> - b. Control de Versiones y Trazabilidad (Micro-Commits)
|
|
67
|
+
> - c. Ejemplo Práctico de un Commit Estandarizado
|
|
68
|
+
> - d. Gestión de Despliegues y Versionado Semántico
|
|
69
|
+
> - e. Sistema de Criterios para Cambio de Versión (SemVer Checklist)
|
|
70
|
+
>
|
|
71
|
+
> **[[#8. Fase 8: Entrega, capacitación y mantenimiento|8. Fase 8: Entrega, capacitación y mantenimiento]]**
|
|
72
|
+
> - a. Protocolo de Entrega y Cierre Administrativo
|
|
73
|
+
> - b. Capacitación y Onboarding del Cliente
|
|
74
|
+
> - c. Políticas de Soporte y Canales de Comunicación
|
|
75
|
+
> - d. Mantenimiento, Monitoreo y Escalabilidad
|
|
76
|
+
|
|
77
|
+
> [!quote] 📎 ETAPA IV: Anexos Comerciales y Legales
|
|
78
|
+
> **[[#Anexos Finales|Documentos de Respaldo y Estandarización]]**
|
|
79
|
+
> - 💰 Anexo A: Tabla de Precios de Referencia (UF / CLP)
|
|
80
|
+
> - 📄 Anexo B: Plantilla - Propuesta Inicial de Desarrollo
|
|
81
|
+
> - 🤝 Anexo C: Plantilla - Acta de Entrega Final y Descargos de Responsabilidad
|
|
82
|
+
|
|
83
|
+
---
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
`**SYSTEM/CONTEXT:**`
|
|
2
|
+
`Eres un Arquitecto de Software Senior especializado en React 18+, TypeScript, Tailwind CSS y proyectos SaaS escalables. **OBLIGATORIO: Respeta y maximiza convenciones React para reusabilidad** (composition > inheritance, custom hooks para lógica, compound components, single responsibility, generics TS, shadcn/ui patterns con Tailwind utility-first, memo/useCallback, evita HOCs si hooks bastan). Analiza el proyecto, crea /docs/, audita priorizando reusabilidad perdida/ganada.`
|
|
3
|
+
|
|
4
|
+
`**OBJETIVO PRINCIPAL:**`
|
|
5
|
+
`Mismo que antes, pero **en TODAS secciones integra reusabilidad React**: scaffolding con componentes reutilizables (ej: ui/Button.tsx genérico), ejemplos muestran composition, auditoría mide % de código reusable.`
|
|
6
|
+
|
|
7
|
+
`**ESTRUCTURA /docs/:** Igual, + nueva sección **07-REUSABILIDAD-REACT.md** (convenciones aplicadas, métricas reusabilidad).`
|
|
8
|
+
|
|
9
|
+
`**INSTRUCCIONES DETALLADAS (actualizadas):**`
|
|
10
|
+
|
|
11
|
+
1. `**Auditoría (AUDITORIA.md):**`
|
|
12
|
+
`Agrega columna | % Reusabilidad | (ej: "Components duplicados: 40% → refactor a hooks genéricos"). Plan acción: "Prioriza hooks como useFormInput reusable en orders/properties" .`
|
|
13
|
+
|
|
14
|
+
2. `**Scaffolding (tree-scaffolding.txt):**`
|
|
15
|
+
`Enfatiza reusabilidad:`
|
|
16
|
+
|
|
17
|
+
`text`
|
|
18
|
+
|
|
19
|
+
`src/ ├── components/ │ └── ui/ (shadcn-style reutilizables: Button.tsx, Input.tsx, Card.tsx, index.ts exports) ├── hooks/ (useQueryProps.ts genérico, useAuth.ts, useFormReusable.ts) ├── features/properties/ (compone ui/Button + hooks)`
|
|
20
|
+
|
|
21
|
+
`Nombres: ui/DataTable.tsx (genérico con <T>), `hooks/useCRUD.ts<T>`.`
|
|
22
|
+
|
|
23
|
+
`3. **Ejemplos (correctos/incorrectos.md):**`
|
|
24
|
+
|
|
25
|
+
`- **Correcto (Reusabilidad):**`
|
|
26
|
+
|
|
27
|
+
`tsx`
|
|
28
|
+
|
|
29
|
+
``// ui/Button.tsx (shadcn pattern, variants Tailwind) import { cva, type VariantProps } from 'class-variance-authority'; import { memo } from 'react'; const buttonVariants = cva("..."); // variants: primary, destructive interface ButtonProps extends VariantProps<typeof buttonVariants> { children: React.ReactNode; onClick?: () => void; } export const Button = memo<ButtonProps>(({ variant, ...props }) => <button className={buttonVariants({ variant })} {...props} />); // Uso reusable: <Button variant="primary" onClick={handleSave}>Guardar</Button> en forms/CRUD``
|
|
30
|
+
|
|
31
|
+
`Impacto: Reusable en 10+ lugares, composition con Card/Form.`
|
|
32
|
+
|
|
33
|
+
`- **Incorrecto:** God-component con 20 props/if-else. Fix: Split en hooks + ui primitives.`
|
|
34
|
+
|
|
35
|
+
`4. **Diccionario Variables:** Incluye | Reutilizable en | (ej: `buttonVariants` | ui/Button, Card | Afecta todos CTAs).`
|
|
36
|
+
|
|
37
|
+
`5. **Flujos:** Diagramas muestran composition: Page → Feature → ui/Hooks.`
|
|
38
|
+
|
|
39
|
+
`6. **Nueva 07-REUSABILIDAD-REACT.md:**`
|
|
40
|
+
|
|
41
|
+
`- Lista convenciones: Custom hooks genéricos, Compound (Select + Options), Memo para perf.[](https://dev.to/hasancse/best-practices-for-creating-reusable-custom-hooks-in-react-37nj)`
|
|
42
|
+
|
|
43
|
+
`- Métricas: Tabla | Componente | Lugares Reusado | Mejora Sugerida |.`
|
|
44
|
+
|
|
45
|
+
`- Ej: "useQuery → genérico<T> para Supabase en properties/orders".`
|
|
46
|
+
|
|
47
|
+
`7. **INDEX-MAESTRO.md:** Sección ## Reusabilidad [link a 07].`
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
`**VALIDACIÓN:** Confirma >70% componentes reutilizables post-refactor. Genera todo listo para copiar.`
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
# Convenciones de Base de Datos — Supabase / PostgreSQL
|
|
2
|
+
|
|
3
|
+
> Prácticas priorizadas por impacto en rendimiento, seguridad y escalabilidad para proyectos React + Supabase. Cada práctica incluye ejemplo correcto ✅ e incorrecto ❌.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. SELECT con columnas específicas
|
|
8
|
+
|
|
9
|
+
Nunca uses `select(*)`. Reduce bandwidth 70-90%.
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
// ✅ Correcto
|
|
13
|
+
supabase.from('products').select('id, name, price, category_id')
|
|
14
|
+
|
|
15
|
+
// ❌ Incorrecto
|
|
16
|
+
supabase.from('products').select('*')
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Por qué:** `*` trae columnas que no necesitas (JSONB grandes, texto largo), satura la red y ralentiza el render.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 2. Keyset pagination (cursor-based)
|
|
24
|
+
|
|
25
|
+
Usa `WHERE id > last_id LIMIT n` en lugar de `offset`. Escala infinito sin degradación.
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// ✅ Correcto — keyset pagination
|
|
29
|
+
async function getUsers(pageSize = 25, cursor?: string) {
|
|
30
|
+
let query = supabase.from('users').select('id, name, email').limit(pageSize).order('id', { ascending: true })
|
|
31
|
+
if (cursor) query = query.gt('id', cursor)
|
|
32
|
+
return query
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ❌ Incorrecto — offset based (se degrada con datos grandes)
|
|
36
|
+
supabase.from('users').select('id, name').range(offset, offset + 25)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Por qué:** `OFFSET` escanea filas descartadas en cada página. Keyset usa índices directamente.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 3. RLS políticas granulares
|
|
44
|
+
|
|
45
|
+
Cada tabla debe tener Row Level Security activado con políticas `USING (auth.uid() = user_id)`.
|
|
46
|
+
|
|
47
|
+
```sql
|
|
48
|
+
-- ✅ Correcto — RLS granular
|
|
49
|
+
CREATE POLICY "users_own_profile" ON profiles
|
|
50
|
+
FOR ALL USING (auth.uid() = id);
|
|
51
|
+
|
|
52
|
+
CREATE POLICY "select_own_orders" ON orders
|
|
53
|
+
FOR SELECT USING (auth.uid() = user_id);
|
|
54
|
+
|
|
55
|
+
-- ❌ Incorrecto — bucket público o sin RLS
|
|
56
|
+
-- (no deshabilitar RLS en tablas con datos de usuario)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**En el código:**
|
|
60
|
+
```typescript
|
|
61
|
+
// ✅ El frontend solo consulta, RLS filtra automáticamente
|
|
62
|
+
const { data } = await supabase.from('orders').select('id, total')
|
|
63
|
+
|
|
64
|
+
// ❌ Service Role Key en el cliente (NUNCA)
|
|
65
|
+
const serviceClient = createClient(url, process.env.SERVICE_ROLE_KEY) // PELIGRO
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Por qué:** RLS es zero-trust: aunque el token sea válido, el usuario solo ve sus datos. No necesitas middleware de autorización.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 4. RPC para lógica compleja (evitar N+1)
|
|
73
|
+
|
|
74
|
+
Operaciones multi-paso o joins pesados van en funciones Postgres, no en el cliente.
|
|
75
|
+
|
|
76
|
+
```sql
|
|
77
|
+
-- ✅ Correcto — RPC en base de datos
|
|
78
|
+
CREATE OR REPLACE FUNCTION get_dashboard_stats(user_id UUID)
|
|
79
|
+
RETURNS JSONB LANGUAGE plpgsql SECURITY DEFINER AS $$
|
|
80
|
+
DECLARE
|
|
81
|
+
result JSONB;
|
|
82
|
+
BEGIN
|
|
83
|
+
SELECT jsonb_build_object(
|
|
84
|
+
'total_orders', (SELECT count(*) FROM orders WHERE orders.user_id = get_dashboard_stats.user_id),
|
|
85
|
+
'total_spent', (SELECT COALESCE(sum(total), 0) FROM orders WHERE orders.user_id = get_dashboard_stats.user_id)
|
|
86
|
+
) INTO result;
|
|
87
|
+
RETURN result;
|
|
88
|
+
END;
|
|
89
|
+
$$;
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// ✅ Llamada desde el frontend
|
|
94
|
+
const { data } = await supabase.rpc('get_dashboard_stats', { user_id: userId })
|
|
95
|
+
|
|
96
|
+
// ❌ Múltiples queries desde el cliente (N+1)
|
|
97
|
+
const orders = await supabase.from('orders').select('id')
|
|
98
|
+
for (const order of orders.data) {
|
|
99
|
+
await supabase.from('order_items').select('*').eq('order_id', order.id)
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Por qué:** RPC ejecuta lógica del lado de la DB, sin round-trips múltiples, con acceso directo a memoria.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 5. Índices en columnas WHERE / JOIN / ORDER BY
|
|
108
|
+
|
|
109
|
+
Crea índices compuestos para los filtros más comunes de cada tabla.
|
|
110
|
+
|
|
111
|
+
```sql
|
|
112
|
+
-- ✅ Correcto — índice compuesto para filtros frecuentes
|
|
113
|
+
CREATE INDEX idx_orders_user_status ON orders(user_id, status) WHERE status != 'cancelled';
|
|
114
|
+
|
|
115
|
+
-- ✅ Cubrir SELECT con INCLUDE
|
|
116
|
+
CREATE INDEX idx_orders_user_id ON orders(user_id) INCLUDE (total, created_at);
|
|
117
|
+
|
|
118
|
+
-- ❌ Sin índice en columna de filtro frecuente
|
|
119
|
+
-- SELECT * FROM orders WHERE user_id = '...' AND status = 'active' ← full scan
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**En migración:**
|
|
123
|
+
```bash
|
|
124
|
+
supabase migration new add_orders_indexes
|
|
125
|
+
# Editar el archivo SQL generado
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Por qué:** Los índices convierten scans secuenciales en búsquedas O(log n). Los partial indexes son 90% más pequeños.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 6. Signed URLs para archivos (no buckets públicos)
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
// ✅ Correcto — Signed URL con expiración
|
|
136
|
+
const { data } = await supabase.storage
|
|
137
|
+
.from('avatars')
|
|
138
|
+
.createSignedUrl(`users/${userId}.jpg`, 3600) // expira en 1h
|
|
139
|
+
|
|
140
|
+
// ❌ Incorrecto — bucket público
|
|
141
|
+
// Bucket config: public = true ← cualquiera con la URL accede
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Por qué:** Signed URLs limitan acceso temporal. Incluso con RLS en tablas, los archivos en buckets públicos son accesibles por cualquiera que tenga la URL.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 7. Service Role Key solo en backend
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// ✅ Correcto — Service Key solo en Edge Functions o backend
|
|
152
|
+
// (Nunca en código frontend)
|
|
153
|
+
const serviceClient = createClient(url, process.env.SERVICE_ROLE_KEY)
|
|
154
|
+
|
|
155
|
+
// ❌ Incorrecto — Service Key en frontend
|
|
156
|
+
// Si alguien extrae la key, tiene acceso total a tu BD
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Regla:** En el frontend usa solo la `anon key` con RLS. Para operaciones admin usa RPC con `SECURITY DEFINER` o Edge Functions.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 8. Batch operations
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
// ✅ Correcto — batch insert
|
|
167
|
+
const { error } = await supabase.from('products').insert([
|
|
168
|
+
{ name: 'Pro A', price: 100 },
|
|
169
|
+
{ name: 'Pro B', price: 200 },
|
|
170
|
+
{ name: 'Pro C', price: 300 },
|
|
171
|
+
])
|
|
172
|
+
|
|
173
|
+
// ❌ Incorrecto — inserts en loop
|
|
174
|
+
for (const product of products) {
|
|
175
|
+
await supabase.from('products').insert(product)
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Por qué:** Un batch insert es ~10x más rápido que inserts individuales por el overhead de cada query.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 9. Tipos precisos (UUID, TIMESTAMPTZ, JSONB)
|
|
184
|
+
|
|
185
|
+
```sql
|
|
186
|
+
-- ✅ Correcto — tipos específicos
|
|
187
|
+
CREATE TABLE products (
|
|
188
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
189
|
+
name TEXT NOT NULL,
|
|
190
|
+
price NUMERIC(10,2) NOT NULL DEFAULT 0,
|
|
191
|
+
metadata JSONB DEFAULT '{}',
|
|
192
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
-- ❌ Incorrecto — tipos genéricos
|
|
196
|
+
CREATE TABLE products (
|
|
197
|
+
id TEXT PRIMARY KEY, -- UUID como TEXT desperdicia espacio e índices
|
|
198
|
+
price FLOAT, -- FLOAT tiene errores de redondeo
|
|
199
|
+
created_at TIMESTAMP -- Sin timezone = ambigüedad
|
|
200
|
+
);
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Por qué:** `UUID` vs `TEXT`: 16 bytes vs variable. `TIMESTAMPTZ` evita ambigüedad de zona horaria. `NUMERIC` es exacto para dinero.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## 10. Types generados desde la DB
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
# ✅ Correcto — tipos sincronizados automáticamente
|
|
211
|
+
supabase gen types typescript --local > src/types/supabase.ts
|
|
212
|
+
|
|
213
|
+
# ❌ Incorrecto — tipos escritos a mano (se desincronizan)
|
|
214
|
+
interface Product {
|
|
215
|
+
id: string
|
|
216
|
+
name: string
|
|
217
|
+
// ... se rompe cuando agregas columnas
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Luego úsalos:
|
|
222
|
+
```typescript
|
|
223
|
+
import { Database } from '../types/supabase'
|
|
224
|
+
type Product = Database['public']['Tables']['products']['Row']
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## 11. Migraciones versionadas
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
# ✅ Correcto — cada cambio de schema es una migración
|
|
233
|
+
supabase migration new add_product_categories
|
|
234
|
+
|
|
235
|
+
# Editar el SQL, luego:
|
|
236
|
+
supabase db push
|
|
237
|
+
|
|
238
|
+
# ❌ Incorrecto — cambios directos en tabla
|
|
239
|
+
ALTER TABLE products ADD COLUMN category_id UUID; -- sin migración
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**Por qué:** Las migraciones dan historial, rollback, y reproducción exacta en producción.
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## 12. Error handling por código PostgreSQL
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
// ✅ Correcto — capturar errores por código
|
|
250
|
+
try {
|
|
251
|
+
const { error } = await supabase.from('users').insert({ email })
|
|
252
|
+
if (error?.code === '23505') {
|
|
253
|
+
// Unique violation — email ya existe
|
|
254
|
+
return { error: 'Este email ya está registrado' }
|
|
255
|
+
}
|
|
256
|
+
if (error?.code === '23503') {
|
|
257
|
+
// Foreign key violation
|
|
258
|
+
return { error: 'Referencia inválida' }
|
|
259
|
+
}
|
|
260
|
+
} catch (err) {
|
|
261
|
+
console.error('Error inesperado:', err)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ❌ Incorrecto — error genérico
|
|
265
|
+
catch (err) {
|
|
266
|
+
showToast('Error de base de datos') // sin contexto para el usuario
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
**Códigos comunes:** `23505` (unique), `23503` (foreign key), `42P01` (tabla no existe), `42501` (RLS denegado).
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## 13. Soft deletes
|
|
275
|
+
|
|
276
|
+
```sql
|
|
277
|
+
-- ✅ Correcto — borrado lógico con columna deleted_at
|
|
278
|
+
ALTER TABLE products ADD COLUMN deleted_at TIMESTAMPTZ;
|
|
279
|
+
|
|
280
|
+
-- RLS que excluye borrados
|
|
281
|
+
CREATE POLICY "select_active_products" ON products
|
|
282
|
+
FOR SELECT USING (deleted_at IS NULL);
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
// ✅ Soft delete
|
|
287
|
+
await supabase.from('products').update({ deleted_at: new Date().toISOString() }).eq('id', id)
|
|
288
|
+
|
|
289
|
+
// ❌ Hard delete (irreversible)
|
|
290
|
+
await supabase.from('products').delete().eq('id', id)
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**Por qué:** Soft delete permite recuperación, auditoría, y evita "cascades" de FK que rompen datos relacionados.
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## 14. Transacciones multi-paso vía RPC
|
|
298
|
+
|
|
299
|
+
Para operaciones que involucran múltiples tablas, usa una función RPC con transacción:
|
|
300
|
+
|
|
301
|
+
```sql
|
|
302
|
+
-- ✅ Correcto — todo en una transacción
|
|
303
|
+
CREATE OR REPLACE FUNCTION create_order(p_user_id UUID, p_items JSONB)
|
|
304
|
+
RETURNS JSONB LANGUAGE plpgsql SECURITY DEFINER AS $$
|
|
305
|
+
DECLARE
|
|
306
|
+
v_order_id UUID;
|
|
307
|
+
v_total NUMERIC;
|
|
308
|
+
BEGIN
|
|
309
|
+
INSERT INTO orders (user_id, status) VALUES (p_user_id, 'pending') RETURNING id INTO v_order_id;
|
|
310
|
+
INSERT INTO order_items (order_id, product_id, quantity, price)
|
|
311
|
+
SELECT v_order_id, item->>'product_id', (item->>'quantity')::INT, (item->>'price')::NUMERIC
|
|
312
|
+
FROM jsonb_array_elements(p_items) AS item;
|
|
313
|
+
SELECT SUM(quantity * price) INTO v_total FROM order_items WHERE order_id = v_order_id;
|
|
314
|
+
UPDATE orders SET total = v_total WHERE id = v_order_id;
|
|
315
|
+
RETURN jsonb_build_object('order_id', v_order_id, 'total', v_total);
|
|
316
|
+
END;
|
|
317
|
+
$$;
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
// ✅ Llamada única
|
|
322
|
+
const { data } = await supabase.rpc('create_order', { p_user_id: userId, p_items: cart })
|
|
323
|
+
|
|
324
|
+
// ❌ Múltiples queries sin transacción
|
|
325
|
+
const { data: order } = await supabase.from('orders').insert({ user_id: userId }).select()
|
|
326
|
+
// Si esto falla después, tienes datos huérfanos
|
|
327
|
+
await supabase.from('order_items').insert(items.map(i => ({ order_id: order.id, ...i })))
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## 15. Realtime channels con cleanup
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
// ✅ Correcto — unsubscribe en cleanup
|
|
336
|
+
useEffect(() => {
|
|
337
|
+
const channel = supabase
|
|
338
|
+
.channel('orders-feed')
|
|
339
|
+
.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'orders' }, payload => {
|
|
340
|
+
setOrders(prev => [...prev, payload.new])
|
|
341
|
+
})
|
|
342
|
+
.subscribe()
|
|
343
|
+
|
|
344
|
+
return () => { supabase.removeChannel(channel) } // cleanup!
|
|
345
|
+
}, [])
|
|
346
|
+
|
|
347
|
+
// ❌ Incorrecto — sin cleanup (fuga de conexiones)
|
|
348
|
+
useEffect(() => {
|
|
349
|
+
supabase.channel('orders-feed').on('postgres_changes', ...).subscribe()
|
|
350
|
+
// Sin return → se acumulan canales al remontar el componente
|
|
351
|
+
}, [])
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## 16. Unique constraints y Foreign Keys
|
|
357
|
+
|
|
358
|
+
```sql
|
|
359
|
+
-- ✅ Correcto — constraints explícitas
|
|
360
|
+
CREATE TABLE products (
|
|
361
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
362
|
+
sku TEXT UNIQUE NOT NULL,
|
|
363
|
+
category_id UUID NOT NULL REFERENCES categories(id) ON DELETE RESTRICT
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
-- ❌ Incorrecto — validación solo en código
|
|
367
|
+
-- (dos usuarios pueden insertar el mismo SKU si la request llega al mismo tiempo)
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**Por qué:** Las constraints de base de datos son el último filtro de integridad. El código puede tener bugs, la base de datos no.
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## 17. Partial indexes para filtros comunes
|
|
375
|
+
|
|
376
|
+
```sql
|
|
377
|
+
-- ✅ Correcto — índice solo para filas activas (más pequeño, más rápido)
|
|
378
|
+
CREATE INDEX idx_products_active ON products(created_at) WHERE active = true;
|
|
379
|
+
|
|
380
|
+
-- ❌ Incorrecto — índice completo en tabla con 80% inactivos
|
|
381
|
+
CREATE INDEX idx_products_created ON products(created_at);
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
**Por qué:** Un partial index es ~90% más pequeño que uno completo cuando filtras por un subconjunto.
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## 18. Covering indexes (INCLUDE)
|
|
389
|
+
|
|
390
|
+
```sql
|
|
391
|
+
-- ✅ Correcto — index-only scan
|
|
392
|
+
CREATE INDEX idx_orders_user ON orders(user_id) INCLUDE (total, status, created_at);
|
|
393
|
+
|
|
394
|
+
-- ❌ Incorrecto — necesita ir a la tabla para obtener columnas extra
|
|
395
|
+
CREATE INDEX idx_orders_user ON orders(user_id);
|
|
396
|
+
-- SELECT user_id, total, status FROM orders WHERE user_id = 'x' → necesita heap lookup
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
**Por qué:** Con `INCLUDE`, la query se resuelve solo con el índice, sin tocar la tabla principal.
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## Resumen: Prioridad por fase del proyecto
|
|
404
|
+
|
|
405
|
+
| Fase | Prácticas esenciales |
|
|
406
|
+
|---|---|
|
|
407
|
+
| MVP / Prototipo | 1 (SELECT), 3 (RLS), 9 (tipos), 10 (types gen), 16 (constraints) |
|
|
408
|
+
| Producción | + 4 (RPC), 5 (índices), 6 (signed URLs), 12 (error codes), 15 (realtime) |
|
|
409
|
+
| Escalando | + 2 (keyset), 8 (batch), 11 (migrations), 13 (soft delete), 14 (transacciones), 17 (partial), 18 (covering) |
|
|
410
|
+
| Auditoría/Seguridad | + 7 (service key), todo RLS revisado |
|