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,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Servicio CRUD con Supabase
|
|
3
|
+
tags: [servicio, supabase, crud, api]
|
|
4
|
+
usado_en: []
|
|
5
|
+
fecha_creacion: 2026-05-13
|
|
6
|
+
ultima_modificacion: 2026-05-13
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Cuándo usar
|
|
10
|
+
|
|
11
|
+
Cuando necesites una capa de servicio para operaciones CRUD contra una tabla de Supabase. Separa la lógica de base de datos de los hooks/componentes.
|
|
12
|
+
|
|
13
|
+
## Prompt
|
|
14
|
+
|
|
15
|
+
```markdown
|
|
16
|
+
## Contexto
|
|
17
|
+
Stack: TypeScript + Supabase.
|
|
18
|
+
Convenciones: servicios en src/services/supabase/, un archivo por tabla/entidad, máximo 150 líneas.
|
|
19
|
+
Patrón: servicio con métodos estáticos nombrados como verbo + entidad (createUser, getProductById).
|
|
20
|
+
Referencia: docs/Framework de Desarrollo Asistido por IA.md sección 2 (estructura services/).
|
|
21
|
+
|
|
22
|
+
## Objetivo
|
|
23
|
+
Crear el servicio `{Entity}Service` para operaciones CRUD contra la tabla `{table_name}` de Supabase.
|
|
24
|
+
|
|
25
|
+
## Especificaciones técnicas
|
|
26
|
+
- Archivo: `src/services/supabase/{entity}Service.ts`
|
|
27
|
+
- Cliente Supabase importado desde `src/services/supabase/client.ts`
|
|
28
|
+
- Métodos requeridos:
|
|
29
|
+
- `getAll(filters?)`: listar con filtros opcionales
|
|
30
|
+
- `getById(id: string)`: obtener por ID
|
|
31
|
+
- `create(data: CreateDTO)`: crear registro
|
|
32
|
+
- `update(id: string, data: UpdateDTO)`: actualizar
|
|
33
|
+
- `delete(id: string)`: eliminar (lógico si aplica)
|
|
34
|
+
- Tipos DTO en `src/services/supabase/types.ts` o local en el archivo
|
|
35
|
+
|
|
36
|
+
## Reglas estrictas
|
|
37
|
+
- Cada método debe tener su tipo de entrada y salida definido
|
|
38
|
+
- Manejar errores con try/catch y devolver { data, error } siempre
|
|
39
|
+
- No exponer errores de Supabase al cliente (traducirlos)
|
|
40
|
+
- Si hay políticas RLS, el servicio debe recibir el userId como parámetro
|
|
41
|
+
|
|
42
|
+
## Anti-patrones (prohibido)
|
|
43
|
+
- NO hacer llamadas a Supabase directamente desde componentes
|
|
44
|
+
- NO exponer errores internos de Supabase (code, hint, details)
|
|
45
|
+
- NO mezclar lógica de negocio con lógica de presentación
|
|
46
|
+
|
|
47
|
+
## Ejemplo de estructura
|
|
48
|
+
```typescript
|
|
49
|
+
interface CreateProductDTO {
|
|
50
|
+
name: string
|
|
51
|
+
price: number
|
|
52
|
+
category_id: string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface Product {
|
|
56
|
+
id: string
|
|
57
|
+
name: string
|
|
58
|
+
price: number
|
|
59
|
+
created_at: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const productService = {
|
|
63
|
+
async getAll(): Promise<{ data: Product[]; error: string | null }> {
|
|
64
|
+
// implementación
|
|
65
|
+
},
|
|
66
|
+
async create(dto: CreateProductDTO): Promise<{ data: Product | null; error: string | null }> {
|
|
67
|
+
// implementación
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Formato de salida esperado
|
|
73
|
+
Archivo completo listo para `src/services/supabase/{entity}Service.ts`.
|
|
74
|
+
```
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Formulario con validación (react-hook-form + zod)
|
|
3
|
+
tags: [formulario, validacion, react-hook-form, zod, typescript]
|
|
4
|
+
usado_en: []
|
|
5
|
+
fecha_creacion: 2026-05-13
|
|
6
|
+
ultima_modificacion: 2026-05-13
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Cuándo usar
|
|
10
|
+
|
|
11
|
+
Cuando necesites un formulario con validación en frontend. La combinación react-hook-form + zod es la más usada en proyectos React modernos por su tipado y rendimiento.
|
|
12
|
+
|
|
13
|
+
## Prompt
|
|
14
|
+
|
|
15
|
+
```markdown
|
|
16
|
+
## Contexto
|
|
17
|
+
Stack: React 18 + TypeScript + Tailwind + react-hook-form + zod.
|
|
18
|
+
Convenciones: formularios en src/features/{feature}/components/, schema de validación junto al formulario o en types/.
|
|
19
|
+
Referencia: docs/Framework de Desarrollo Asistido por IA.md sección 7 (patrones de reutilización).
|
|
20
|
+
|
|
21
|
+
## Objetivo
|
|
22
|
+
Crear el formulario `{FormName}` con validación usando react-hook-form + zod.
|
|
23
|
+
|
|
24
|
+
## Especificaciones técnicas
|
|
25
|
+
- Archivo: `src/features/{feature}/components/{FormName}.tsx`
|
|
26
|
+
- Schema zod: definir las validaciones de cada campo
|
|
27
|
+
- hook-form con `zodResolver` para integración
|
|
28
|
+
- Campos del formulario:
|
|
29
|
+
- {campo 1: tipo, validación, placeholder}
|
|
30
|
+
- {campo 2: tipo, validación, placeholder}
|
|
31
|
+
- onSubmit: recibir callback `onSubmit(data: SchemaType) => void`
|
|
32
|
+
- Estados: idle, submitting, error, success
|
|
33
|
+
- Máximo 150 líneas
|
|
34
|
+
|
|
35
|
+
## Reglas estrictas
|
|
36
|
+
- El schema zod debe definir tipos explícitos (z.string(), z.number(), etc.)
|
|
37
|
+
- Los mensajes de error deben estar en español
|
|
38
|
+
- Cada campo debe mostrar su error debajo del input
|
|
39
|
+
- Botón de submit deshabilitado mientras se envía
|
|
40
|
+
- Los inputs deben usar componentes de ui/ (Input, Select, etc.)
|
|
41
|
+
|
|
42
|
+
## Anti-patrones (prohibido)
|
|
43
|
+
- NO usar any
|
|
44
|
+
- NO mezclar lógica de negocio (llamadas API) dentro del formulario
|
|
45
|
+
- NO usar useState para validación manual (para eso está hook-form)
|
|
46
|
+
- NO mostrar errores de API en los mensajes de validación de campo
|
|
47
|
+
|
|
48
|
+
## Ejemplo de schema
|
|
49
|
+
```typescript
|
|
50
|
+
import { z } from "zod"
|
|
51
|
+
|
|
52
|
+
export const {FormName}Schema = z.object({
|
|
53
|
+
email: z.string().email("Email inválido"),
|
|
54
|
+
password: z.string().min(8, "Mínimo 8 caracteres"),
|
|
55
|
+
name: z.string().min(2, "Mínimo 2 caracteres").max(50, "Máximo 50 caracteres"),
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
export type {FormName}SchemaType = z.infer<typeof {FormName}Schema>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Formato de salida esperado
|
|
62
|
+
Archivo completo con schema + componente de formulario + exportaciones.
|
|
63
|
+
```
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Hook para plugin nativo de Capacitor
|
|
3
|
+
tags: [capacitor, hook, nativo, camara, plugin]
|
|
4
|
+
usado_en: []
|
|
5
|
+
fecha_creacion: 2026-05-13
|
|
6
|
+
ultima_modificacion: 2026-05-13
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Cuándo usar
|
|
10
|
+
|
|
11
|
+
Cuando necesites acceder a funcionalidad nativa del dispositivo (cámara, GPS, notificaciones, almacenamiento) a través de plugins de Capacitor. El hook encapsula la lógica del plugin y expone una interfaz limpia para los componentes.
|
|
12
|
+
|
|
13
|
+
## Prompt
|
|
14
|
+
|
|
15
|
+
```markdown
|
|
16
|
+
## Contexto
|
|
17
|
+
Stack: React 18 + TypeScript + Capacitor 6 + Ionic 8.
|
|
18
|
+
Convenciones: hooks de Capacitor en src/hooks/ con prefijo useCapacitor*, un hook por archivo, máximo 80 líneas.
|
|
19
|
+
NO mezclar con lógica de Tauri.
|
|
20
|
+
Referencia: docs/Framework de Desarrollo Asistido por IA.md sección 1 (convenciones Capacitor).
|
|
21
|
+
|
|
22
|
+
## Objetivo
|
|
23
|
+
Crear el hook `useCapacitor{Feature}` que encapsule el plugin `@capacitor/{plugin}`.
|
|
24
|
+
|
|
25
|
+
## Especificaciones técnicas
|
|
26
|
+
- Archivo: `src/hooks/useCapacitor{Feature}.ts`
|
|
27
|
+
- Plugin a usar: `@capacitor/{plugin}`
|
|
28
|
+
- Retornar: `{ result: {tipo} | null; execute: () => Promise<void>; error: string | null; loading: boolean }`
|
|
29
|
+
- Manejar permisos: verificar permiso antes de ejecutar, pedir si no está concedido
|
|
30
|
+
- Manejar error si el permiso es denegado permanentemente
|
|
31
|
+
- Máximo 70 líneas
|
|
32
|
+
|
|
33
|
+
## Reglas estrictas
|
|
34
|
+
- Tipar todas las funciones con TypeScript
|
|
35
|
+
- Verificar permisos antes de ejecutar cualquier acción nativa
|
|
36
|
+
- Si el permiso es denegado, mostrar mensaje claro (no el error interno del plugin)
|
|
37
|
+
- El hook debe funcionar tanto en web (fallback) como en dispositivo real
|
|
38
|
+
|
|
39
|
+
## Anti-patrones (prohibido)
|
|
40
|
+
- NO usar any
|
|
41
|
+
- NO poner lógica de UI en el hook
|
|
42
|
+
- NO mezclar con lógica de Tauri
|
|
43
|
+
- NO asumir que el plugin está disponible sin verificar
|
|
44
|
+
|
|
45
|
+
## Ejemplo correcto
|
|
46
|
+
```typescript
|
|
47
|
+
interface UseCapacitorCameraResult {
|
|
48
|
+
photo: string | null
|
|
49
|
+
takePhoto: () => Promise<void>
|
|
50
|
+
error: string | null
|
|
51
|
+
loading: boolean
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function useCapacitorCamera(): UseCapacitorCameraResult {
|
|
55
|
+
// 1. Verificar permiso con Camera.checkPermissions()
|
|
56
|
+
// 2. Si no concedido, pedir con Camera.requestPermissions()
|
|
57
|
+
// 3. Si denegado, setear error
|
|
58
|
+
// 4. Si concedido, ejecutar Camera.getPhoto({ resultType: CameraResultType.Uri })
|
|
59
|
+
// 5. Retornar { photo, takePhoto, error, loading }
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Formato de salida esperado
|
|
64
|
+
Archivo completo listo para `src/hooks/useCapacitor{Feature}.ts`.
|
|
65
|
+
```
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Refactorizar componente grande en módulos pequeños
|
|
3
|
+
tags: [refactor, division, modularizacion, limites]
|
|
4
|
+
usado_en: []
|
|
5
|
+
fecha_creacion: 2026-05-13
|
|
6
|
+
ultima_modificacion: 2026-05-13
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Cuándo usar
|
|
10
|
+
|
|
11
|
+
Cuando un componente supera los límites definidos en el catálogo (120 líneas UI, 80 hooks, 200 páginas) y necesitas dividirlo siguiendo el protocolo de separación automática.
|
|
12
|
+
|
|
13
|
+
## Prompt
|
|
14
|
+
|
|
15
|
+
```markdown
|
|
16
|
+
## Contexto
|
|
17
|
+
Stack: React 18 + TypeScript + Tailwind.
|
|
18
|
+
Convenciones: docs/Framework de Desarrollo Asistido por IA.md sección 6 (límites por archivo + reglas de separación automática).
|
|
19
|
+
Archivo a refactorizar: `{ruta/al/archivo.tsx}` ({cantidad} líneas actuales).
|
|
20
|
+
|
|
21
|
+
## Objetivo
|
|
22
|
+
Dividir el archivo `{nombre}` en módulos más pequeños según los límites del catálogo.
|
|
23
|
+
|
|
24
|
+
## Análisis del archivo actual
|
|
25
|
+
El archivo contiene:
|
|
26
|
+
1. {sección 1}: {subcomponente o lógica identificable}
|
|
27
|
+
2. {sección 2}: {subcomponente o lógica identificable}
|
|
28
|
+
3. {sección 3}: {estados, lógica de negocio, etc.}
|
|
29
|
+
|
|
30
|
+
## Plan de división propuesto
|
|
31
|
+
Crear los siguientes archivos:
|
|
32
|
+
- `{ruta}/components/{SubComponente1}.tsx` (estimado {N} líneas)
|
|
33
|
+
- `{ruta}/components/{SubComponente2}.tsx` (estimado {N} líneas)
|
|
34
|
+
- `{ruta}/hooks/use{Nombre}Logic.ts` (estimado {N} líneas)
|
|
35
|
+
- El archivo original debe quedar en máximo 120 líneas
|
|
36
|
+
|
|
37
|
+
## Reglas estrictas
|
|
38
|
+
- NO cambiar la funcionalidad existente, solo mover código
|
|
39
|
+
- Mantener los mismos imports y tipos, solo reubicarlos
|
|
40
|
+
- El archivo original debe importar desde los nuevos archivos
|
|
41
|
+
- Actualizar barrel exports (index.ts) si existen
|
|
42
|
+
- No crear archivos de menos de 20 líneas (si es tan pequeño, fusionar)
|
|
43
|
+
|
|
44
|
+
## Anti-patrones (prohibido)
|
|
45
|
+
- NO renombrar funciones o props a menos que sea necesario para claridad
|
|
46
|
+
- NO cambiar la lógica de negocio durante la refactorización
|
|
47
|
+
- NO crear dependencias circulares entre los nuevos archivos
|
|
48
|
+
|
|
49
|
+
## Formato de salida esperado
|
|
50
|
+
Lista de archivos creados/modificados con el contenido de cada uno y los imports actualizados.
|
|
51
|
+
```
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Scaffolding completo de un feature/módulo
|
|
3
|
+
tags: [scaffolding, feature, setup, estructura-inicial]
|
|
4
|
+
usado_en: []
|
|
5
|
+
fecha_creacion: 2026-05-13
|
|
6
|
+
ultima_modificacion: 2026-05-13
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Cuándo usar
|
|
10
|
+
|
|
11
|
+
Al iniciar un nuevo feature o módulo dentro del proyecto. Crea toda la estructura de archivos (esqueletos vacíos con contratos) para que luego solo haya que implementar la lógica.
|
|
12
|
+
|
|
13
|
+
## Prompt
|
|
14
|
+
|
|
15
|
+
```markdown
|
|
16
|
+
## Contexto
|
|
17
|
+
Stack: React 18 + TypeScript + {Tailwind/Supabase/etc}.
|
|
18
|
+
Convenciones: scaffolding con contratos de método, throw new Error("pendiente"), un archivo por responsabilidad.
|
|
19
|
+
Referencia: docs/Framework de Desarrollo Asistido por IA.md sección 2 (estructura features/) y sección 6 (límites).
|
|
20
|
+
|
|
21
|
+
## Objetivo
|
|
22
|
+
Crear el scaffolding completo del feature `{featureName}`.
|
|
23
|
+
|
|
24
|
+
## Estructura a crear
|
|
25
|
+
```
|
|
26
|
+
src/features/{featureName}/
|
|
27
|
+
├── components/
|
|
28
|
+
│ ├── {Component1}.tsx
|
|
29
|
+
│ ├── {Component2}.tsx
|
|
30
|
+
│ └── index.ts
|
|
31
|
+
├── hooks/
|
|
32
|
+
│ ├── use{FeatureName}.ts
|
|
33
|
+
│ └── index.ts
|
|
34
|
+
├── services/
|
|
35
|
+
│ └── {featureName}Service.ts
|
|
36
|
+
├── types/
|
|
37
|
+
│ └── index.ts
|
|
38
|
+
├── {FeatureName}Page.tsx
|
|
39
|
+
└── index.ts
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Especificaciones técnicas
|
|
43
|
+
|
|
44
|
+
### types/index.ts
|
|
45
|
+
- Interfaces de las entidades del feature
|
|
46
|
+
- DTOs de creación y actualización
|
|
47
|
+
|
|
48
|
+
### services/{featureName}Service.ts
|
|
49
|
+
- Métodos CRUD con contrato de método (inputs, outputs, flujo, impacto, error handling)
|
|
50
|
+
- Cuerpo: `throw new Error("Lógica pendiente: {descripción}")`
|
|
51
|
+
|
|
52
|
+
### hooks/use{FeatureName}.ts
|
|
53
|
+
- Hook que conecta el servicio con el estado del componente
|
|
54
|
+
- Estados: data, loading, error
|
|
55
|
+
- Acciones: fetch, create, update, delete
|
|
56
|
+
|
|
57
|
+
### components/{Component1,Component2}.tsx
|
|
58
|
+
- Componentes visuales del feature
|
|
59
|
+
- Props tipadas con interfaces del feature
|
|
60
|
+
- Contrato de componente comentado
|
|
61
|
+
|
|
62
|
+
### {FeatureName}Page.tsx
|
|
63
|
+
- Página principal que compone los componentes
|
|
64
|
+
- Máximo 200 líneas, idealmente < 100
|
|
65
|
+
|
|
66
|
+
## Reglas estrictas
|
|
67
|
+
- Cada archivo debe tener su contrato de método completo como comentario
|
|
68
|
+
- throw new Error("pendiente") en toda función implementable
|
|
69
|
+
- NO implementar lógica de negocio real
|
|
70
|
+
- Los imports deben ser correctos entre los archivos del feature
|
|
71
|
+
|
|
72
|
+
## Anti-patrones (prohibido)
|
|
73
|
+
- NO implementar lógica (solo scaffolding)
|
|
74
|
+
- NO crear archivos de más de 80 líneas en scaffolding
|
|
75
|
+
- NO olvidar los index.ts (barrel exports)
|
|
76
|
+
|
|
77
|
+
## Formato de salida esperado
|
|
78
|
+
Todos los archivos del feature listos para copiar a src/features/{featureName}/.
|
|
79
|
+
```
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Servicio CRUD con Supabase — operaciones batch + RLS + signed URLs
|
|
3
|
+
tags: [service, supabase, crud, rls, typescript]
|
|
4
|
+
usado_en: []
|
|
5
|
+
fecha_creacion: 2026-05-14
|
|
6
|
+
ultima_modificacion: 2026-05-14
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Cuándo usar
|
|
10
|
+
|
|
11
|
+
Cuando necesites un servicio CRUD completo para una tabla de Supabase con operaciones batch, RLS policies, signed URLs para archivos y manejo de errores por código PostgreSQL.
|
|
12
|
+
|
|
13
|
+
## Prompt
|
|
14
|
+
|
|
15
|
+
```markdown
|
|
16
|
+
## Contexto
|
|
17
|
+
Stack: React 18 + TypeScript + Vite + Supabase.
|
|
18
|
+
Convenciones: servicios en src/services/, un archivo por entidad, máximo 150 líneas.
|
|
19
|
+
Referencia DB: docs/CONVENCIONES_DB.md (RLS, batch, signed URLs, error codes).
|
|
20
|
+
Tipos generados desde DB: src/types/supabase.ts (Database).
|
|
21
|
+
|
|
22
|
+
## Objetivo
|
|
23
|
+
Crear el servicio CRUD `productService` para la tabla `products` con operaciones batch, RLS respetado desde el cliente, signed URLs para imágenes, y errores tipados por código PostgreSQL.
|
|
24
|
+
|
|
25
|
+
## Especificaciones técnicas
|
|
26
|
+
- Archivo: `src/services/productService.ts`
|
|
27
|
+
- Cliente Supabase desde `src/services/supabase/client.ts`
|
|
28
|
+
- Tipos desde `src/types/supabase.ts`
|
|
29
|
+
|
|
30
|
+
Operaciones:
|
|
31
|
+
- `getAll(filters?)` — Listar con filtros opcionales, SELECT columnas específicas
|
|
32
|
+
- `getById(id)` — Obtener por ID
|
|
33
|
+
- `create(data)` — Insert con tipado estricto, capturar error 23505 (unique)
|
|
34
|
+
- `update(id, data)` — Update parcial
|
|
35
|
+
- `remove(id)` — Soft delete (columna deleted_at) con transacción
|
|
36
|
+
- `getImageUrl(productId)` — Signed URL expirable para imagen
|
|
37
|
+
- `batchCreate(items)` — Batch insert múltiple
|
|
38
|
+
|
|
39
|
+
Cada función debe:
|
|
40
|
+
- SELECT columnas específicas (nunca `*`)
|
|
41
|
+
- Usar tipos generados desde DB (Database['public']['Tables']['products']['Row'])
|
|
42
|
+
- Capturar errores PostgreSQL por código (23505, 23503, 42501)
|
|
43
|
+
- Respetar RLS (sin Service Role Key en frontend)
|
|
44
|
+
|
|
45
|
+
## Reglas estrictas
|
|
46
|
+
- SELECT con columnas explícitas: `.select('id, name, price, category_id, created_at')`
|
|
47
|
+
- Errores capturados por código: `if (error?.code === '23505')` → mensaje amigable
|
|
48
|
+
- Soft delete con `deleted_at`, nunca hard delete
|
|
49
|
+
- Signed URLs con expiración máxima 1h (3600s)
|
|
50
|
+
- Batch insert con un solo `.insert([...])`, no loop
|
|
51
|
+
- Tipos desde Database generado, nunca interfaces manuales
|
|
52
|
+
|
|
53
|
+
## Anti-patrones (prohibido)
|
|
54
|
+
- NO usar `select('*')`
|
|
55
|
+
- NO exponer Service Role Key en frontend
|
|
56
|
+
- NO hacer inserts en loop
|
|
57
|
+
- NO usar buckets públicos para imágenes
|
|
58
|
+
- NO borrar físicamente (hard delete)
|
|
59
|
+
|
|
60
|
+
## Ejemplo correcto (parcial)
|
|
61
|
+
```typescript
|
|
62
|
+
import { supabase } from './supabase/client'
|
|
63
|
+
import type { Database } from '../types/supabase'
|
|
64
|
+
|
|
65
|
+
type Product = Database['public']['Tables']['products']['Row']
|
|
66
|
+
type ProductInsert = Database['public']['Tables']['products']['Insert']
|
|
67
|
+
|
|
68
|
+
export async function getAll(filters?: { category_id?: string }) {
|
|
69
|
+
let query = supabase
|
|
70
|
+
.from('products')
|
|
71
|
+
.select('id, name, price, category_id, created_at')
|
|
72
|
+
|
|
73
|
+
if (filters?.category_id) {
|
|
74
|
+
query = query.eq('category_id', filters.category_id)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const { data, error } = await query
|
|
78
|
+
if (error) throw new Error(error.message)
|
|
79
|
+
return data as Product[]
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export async function create(data: ProductInsert) {
|
|
83
|
+
const { data: product, error } = await supabase
|
|
84
|
+
.from('products')
|
|
85
|
+
.insert(data)
|
|
86
|
+
.select('id, name, price')
|
|
87
|
+
.single()
|
|
88
|
+
|
|
89
|
+
if (error?.code === '23505') {
|
|
90
|
+
throw new Error('Ya existe un producto con ese identificador')
|
|
91
|
+
}
|
|
92
|
+
if (error) throw new Error(error.message)
|
|
93
|
+
|
|
94
|
+
return product
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Ejemplo incorrecto
|
|
99
|
+
```typescript
|
|
100
|
+
// ❌ SELECT *, sin tipos, sin manejo de errores
|
|
101
|
+
export async function getProducts() {
|
|
102
|
+
const { data } = await supabase.from('products').select('*')
|
|
103
|
+
return data
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function deleteProduct(id: string) {
|
|
107
|
+
await supabase.from('products').delete().eq('id', id)
|
|
108
|
+
// Hard delete irreversible, sin soft delete
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Formato de salida esperado
|
|
113
|
+
Archivo completo `src/services/productService.ts` con todas las funciones CRUD implementadas.
|
|
114
|
+
```
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Hook useSupabaseTable con keyset pagination y filtros
|
|
3
|
+
tags: [hook, supabase, pagination, keyset, typescript]
|
|
4
|
+
usado_en: []
|
|
5
|
+
fecha_creacion: 2026-05-14
|
|
6
|
+
ultima_modificacion: 2026-05-14
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Cuándo usar
|
|
10
|
+
|
|
11
|
+
Cuando necesites un hook que liste datos con paginación keyset (cursor-based), filtros combinados y ordenamiento. Ideal para tablas con muchos datos que deben escalar sin degradación.
|
|
12
|
+
|
|
13
|
+
## Prompt
|
|
14
|
+
|
|
15
|
+
```markdown
|
|
16
|
+
## Contexto
|
|
17
|
+
Stack: React 18 + TypeScript + Vite + Supabase.
|
|
18
|
+
Convenciones: hooks en src/hooks/, un hook por archivo, máximo 80 líneas.
|
|
19
|
+
Referencia DB: docs/CONVENCIONES_DB.md (keyset pagination, partial indexes, columnas específicas).
|
|
20
|
+
|
|
21
|
+
## Objetivo
|
|
22
|
+
Crear el hook `useSupabaseList<T>` que implemente keyset pagination (cursor-based), filtros combinados y ordenamiento. Escala a millones de filas sin degradación.
|
|
23
|
+
|
|
24
|
+
## Especificaciones técnicas
|
|
25
|
+
- Archivo: `src/hooks/useSupabaseList.ts`
|
|
26
|
+
- Retornar: `{ data: T[]; loading: boolean; error: string | null; hasMore: boolean; loadMore: () => void; refetch: () => void }`
|
|
27
|
+
- Parámetros: `UseListOptions<T>`:
|
|
28
|
+
- `table: string` — Nombre de la tabla
|
|
29
|
+
- `select: string` — Columnas separadas por coma (nunca *)
|
|
30
|
+
- `filters?: Record<string, unknown>` — Filtros exactos (columna: valor)
|
|
31
|
+
- `search?: { column: string; value: string }` — Búsqueda textual
|
|
32
|
+
- `orderBy?: { column: keyof T; ascending: boolean }` — Ordenamiento
|
|
33
|
+
- `pageSize?: number` — Items por página (default: 25)
|
|
34
|
+
- `cursorColumn?: string` — Columna del cursor (default: 'id')
|
|
35
|
+
|
|
36
|
+
## Reglas estrictas
|
|
37
|
+
- Paginación keyset: `WHERE cursorColumn > lastValue LIMIT pageSize`
|
|
38
|
+
- SELECT con columnas explícitas en `select`, nunca `*`
|
|
39
|
+
- `loading` debe iniciar en `true` en la primera carga
|
|
40
|
+
- `hasMore` se calcula: si la DB devuelve menos de `pageSize` filas, no hay más
|
|
41
|
+
- `loadMore` append los nuevos datos al array existente (no reemplazar)
|
|
42
|
+
- `refetch` reinicia el estado completo
|
|
43
|
+
- Los filtros deben ser estables (usar `useMemo` para construir la query)
|
|
44
|
+
- El cursor debe incluirse en `select` aunque no se muestre en UI
|
|
45
|
+
|
|
46
|
+
## Anti-patrones (prohibido)
|
|
47
|
+
- NO usar `range()` / offset — usar keyset
|
|
48
|
+
- NO hacer fetch en el render, solo en `useEffect`
|
|
49
|
+
- NO mutar estado directamente (usar spread o inmutable)
|
|
50
|
+
- NO poner lógica de UI en el hook
|
|
51
|
+
|
|
52
|
+
## Ejemplo correcto (parcial)
|
|
53
|
+
```typescript
|
|
54
|
+
interface UseListOptions<T> {
|
|
55
|
+
table: string
|
|
56
|
+
select: string
|
|
57
|
+
filters?: Record<string, unknown>
|
|
58
|
+
orderBy?: { column: keyof T; ascending: boolean }
|
|
59
|
+
pageSize?: number
|
|
60
|
+
cursorColumn?: string
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface UseListResult<T> {
|
|
64
|
+
data: T[]
|
|
65
|
+
loading: boolean
|
|
66
|
+
error: string | null
|
|
67
|
+
hasMore: boolean
|
|
68
|
+
loadMore: () => void
|
|
69
|
+
refetch: () => void
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function useSupabaseList<T extends Record<string, unknown>>({
|
|
73
|
+
table,
|
|
74
|
+
select,
|
|
75
|
+
filters,
|
|
76
|
+
orderBy,
|
|
77
|
+
pageSize = 25,
|
|
78
|
+
cursorColumn = 'id',
|
|
79
|
+
}: UseListOptions<T>): UseListResult<T> {
|
|
80
|
+
const [data, setData] = useState<T[]>([])
|
|
81
|
+
const [loading, setLoading] = useState(true)
|
|
82
|
+
const [error, setError] = useState<string | null>(null)
|
|
83
|
+
const [cursor, setCursor] = useState<string | null>(null)
|
|
84
|
+
const [hasMore, setHasMore] = useState(true)
|
|
85
|
+
|
|
86
|
+
const fetchData = async (isLoadMore = false) => {
|
|
87
|
+
setLoading(true)
|
|
88
|
+
setError(null)
|
|
89
|
+
|
|
90
|
+
let query = supabase
|
|
91
|
+
.from(table)
|
|
92
|
+
.select(select)
|
|
93
|
+
.limit(pageSize)
|
|
94
|
+
.order(orderBy?.column || 'id', { ascending: orderBy?.ascending ?? true })
|
|
95
|
+
|
|
96
|
+
if (filters) {
|
|
97
|
+
Object.entries(filters).forEach(([key, value]) => {
|
|
98
|
+
query = query.eq(key, value)
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (isLoadMore && cursor) {
|
|
103
|
+
query = query.gt(cursorColumn, cursor)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const { data: result, error: err } = await query
|
|
107
|
+
if (err) { setError(err.message); setLoading(false); return }
|
|
108
|
+
|
|
109
|
+
setData(prev => isLoadMore ? [...prev, ...(result as T[])] : (result as T[]))
|
|
110
|
+
setHasMore(result.length === pageSize)
|
|
111
|
+
if (result.length > 0) {
|
|
112
|
+
setCursor(String(result[result.length - 1][cursorColumn]))
|
|
113
|
+
}
|
|
114
|
+
setLoading(false)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
useEffect(() => { fetchData() }, [table, JSON.stringify(filters), orderBy?.column, orderBy?.ascending])
|
|
118
|
+
const loadMore = () => { if (hasMore && !loading) fetchData(true) }
|
|
119
|
+
const refetch = () => { setCursor(null); setHasMore(true); fetchData() }
|
|
120
|
+
|
|
121
|
+
return { data, loading, error, hasMore, loadMore, refetch }
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Ejemplo incorrecto
|
|
126
|
+
```typescript
|
|
127
|
+
// ❌ Offset pagination, select *, sin hasMore
|
|
128
|
+
export function useProducts() {
|
|
129
|
+
const [data, setData] = useState<any[]>([])
|
|
130
|
+
const [page, setPage] = useState(0)
|
|
131
|
+
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
supabase.from('products').select('*').range(page * 25, (page + 1) * 25).then(r => setData(r.data))
|
|
134
|
+
}, [page])
|
|
135
|
+
|
|
136
|
+
return { data, nextPage: () => setPage(p => p + 1) }
|
|
137
|
+
// Se degrada con datos grandes, any, mutación directa
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Formato de salida esperado
|
|
142
|
+
Archivo completo `src/hooks/useSupabaseList.ts` con hook genérico tipado.
|
|
143
|
+
```
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Comando Tauri en Rust con validación de inputs
|
|
3
|
+
tags: [tauri, rust, command, backend]
|
|
4
|
+
usado_en: []
|
|
5
|
+
fecha_creacion: 2026-05-14
|
|
6
|
+
ultima_modificacion: 2026-05-14
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Cuándo usar
|
|
10
|
+
|
|
11
|
+
Cuando necesites crear un comando Tauri v2 en Rust con validación estricta de inputs, manejo de errores tipados, y path sanitization.
|
|
12
|
+
|
|
13
|
+
## Prompt
|
|
14
|
+
|
|
15
|
+
```markdown
|
|
16
|
+
## Contexto
|
|
17
|
+
Stack: Tauri v2 + Rust + React + TypeScript.
|
|
18
|
+
Convenciones: comandos Rust en src-tauri/src/commands/, un archivo por comando.
|
|
19
|
+
Referencia: Tauri v2 IPC — #[tauri::command] con Result<T, String>.
|
|
20
|
+
|
|
21
|
+
## Objetivo
|
|
22
|
+
Crear el comando Tauri `{command_name}` que {descripción del propósito} con validación de inputs, errores tipados y path sanitization.
|
|
23
|
+
|
|
24
|
+
## Especificaciones técnicas
|
|
25
|
+
Archivos a crear:
|
|
26
|
+
- `src-tauri/src/commands/{module}.rs` — Implementación Rust
|
|
27
|
+
- `src-tauri/src/main.rs` — Registrar el módulo con `.invoke_handler()`
|
|
28
|
+
- `src/services/tauri/{module}.ts` — Wrapper TypeScript tipado
|
|
29
|
+
|
|
30
|
+
Comando Rust:
|
|
31
|
+
- `#[tauri::command]`
|
|
32
|
+
- `fn {nombre}({parametros}) -> Result<{output}, String>`
|
|
33
|
+
- Validar todos los inputs antes de ejecutar lógica
|
|
34
|
+
- Sanitizar paths para evitar path traversal
|
|
35
|
+
- Devolver error específico en español/inglés
|
|
36
|
+
|
|
37
|
+
## Reglas estrictas
|
|
38
|
+
- Tipar con tipos concretos (String, no &str; u64, no i32 cuando no negativo)
|
|
39
|
+
- Sanitizar paths: rechazar `..`, `~`, y symlinks a directorios prohibidos
|
|
40
|
+
- No permitir path traversal — restringir a directorio de trabajo
|
|
41
|
+
- Error en String, no en custom enum (Tauri v2 IPC serializa String)
|
|
42
|
+
- Documentar cada error posible con su causa
|
|
43
|
+
|
|
44
|
+
## Anti-patrones
|
|
45
|
+
- NO usar `std::fs::read_to_string` sin validar path
|
|
46
|
+
- NO exponer comandos inseguros sin autenticación
|
|
47
|
+
- NO usar `unwrap()` o `expect()` en producción
|
|
48
|
+
- NO mezclar lógica de negocio con lógica de UI
|
|
49
|
+
|
|
50
|
+
## Ejemplo correcto (parcial)
|
|
51
|
+
```rust
|
|
52
|
+
use std::path::{Path, PathBuf};
|
|
53
|
+
use tauri::Manager;
|
|
54
|
+
|
|
55
|
+
#[tauri::command]
|
|
56
|
+
fn read_file_content(app_handle: tauri::AppHandle, path: String) -> Result<String, String> {
|
|
57
|
+
let base = PathBuf::from(&app_handle.path().resource_dir().map_err(|e| e.to_string())?);
|
|
58
|
+
let requested = base.join(&path);
|
|
59
|
+
|
|
60
|
+
// Sanitize: rechazar path traversal
|
|
61
|
+
if !requested.starts_with(&base) {
|
|
62
|
+
return Err("Acceso denegado: path fuera del directorio permitido".into());
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if !requested.exists() {
|
|
66
|
+
return Err(format!("Archivo no encontrado: {}", path));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
std::fs::read_to_string(&requested).map_err(|e| format!("Error de lectura: {}", e))
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Ejemplo incorrecto
|
|
74
|
+
```rust
|
|
75
|
+
// ❌ Sin validación de path, sin manejo de errores
|
|
76
|
+
#[tauri::command]
|
|
77
|
+
fn read_file(path: String) -> String {
|
|
78
|
+
std::fs::read_to_string(path).unwrap() // Panic en producción!
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Formato de salida esperado
|
|
83
|
+
Código Rust + registro en main.rs + wrapper TypeScript + instrucciones.
|
|
84
|
+
```
|