arckode-framework 1.3.1 → 1.4.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.
Files changed (65) hide show
  1. package/adapters/jwt.ts +6 -4
  2. package/adapters/mysql.ts +7 -2
  3. package/adapters/postgres.ts +37 -0
  4. package/adapters/sqlite.ts +7 -1
  5. package/adapters/vendor.d.ts +48 -0
  6. package/cli/analyze/checks.ts +333 -0
  7. package/cli/analyze/index.ts +44 -0
  8. package/cli/analyze/report.ts +107 -0
  9. package/cli/analyze/types.ts +46 -0
  10. package/cli/analyze/utils.ts +36 -0
  11. package/cli/analyze.ts +2 -647
  12. package/cli/commands/db-migrate.ts +213 -89
  13. package/cli/commands/db-seed.ts +97 -32
  14. package/cli/commands/db-utils.ts +192 -0
  15. package/cli/commands/new.ts +175 -0
  16. package/cli/commands/routes.ts +94 -0
  17. package/cli/index.ts +57 -404
  18. package/cli/stubs/claude-md-stub.ts +21 -8
  19. package/cli/stubs/module/core.ts +162 -0
  20. package/cli/stubs/module/data.ts +171 -0
  21. package/cli/stubs/module/index.ts +5 -0
  22. package/cli/stubs/module/service.ts +198 -0
  23. package/cli/stubs/module/types.ts +12 -0
  24. package/cli/stubs/module-stub.ts +2 -552
  25. package/kernel/auth.ts +114 -0
  26. package/kernel/cache.ts +37 -0
  27. package/kernel/config.ts +129 -0
  28. package/kernel/container.ts +64 -0
  29. package/kernel/db/orm-migrate.ts +136 -0
  30. package/kernel/db/orm-repository.ts +45 -0
  31. package/kernel/db/orm-utils.ts +93 -0
  32. package/kernel/db/orm.ts +254 -0
  33. package/kernel/db/transactor.ts +17 -0
  34. package/kernel/db/types.ts +72 -0
  35. package/kernel/errors.ts +102 -0
  36. package/kernel/framework.default.ts +41 -0
  37. package/kernel/framework.ts +8 -2144
  38. package/kernel/http/router.ts +131 -0
  39. package/kernel/http/server.ts +303 -0
  40. package/kernel/http/types.ts +56 -0
  41. package/kernel/index.ts +25 -0
  42. package/kernel/logger.ts +50 -0
  43. package/kernel/middlewares.ts +38 -21
  44. package/kernel/modules/create-module.ts +5 -0
  45. package/kernel/modules/system.ts +149 -0
  46. package/kernel/modules/types.ts +46 -0
  47. package/kernel/seeds.ts +48 -0
  48. package/kernel/static.ts +11 -2
  49. package/kernel/testing.ts +8 -3
  50. package/kernel/validator.ts +116 -0
  51. package/modules/events/index.ts +19 -3
  52. package/modules/mail/index.ts +14 -2
  53. package/modules/storage/local-adapter.ts +19 -5
  54. package/modules/ws/index.ts +123 -18
  55. package/package.json +8 -11
  56. package/skills/auth/SKILL.md +36 -220
  57. package/skills/cli/SKILL.md +32 -251
  58. package/skills/config/SKILL.md +30 -239
  59. package/skills/connectors/SKILL.md +32 -295
  60. package/skills/helpers/SKILL.md +26 -195
  61. package/skills/middlewares/SKILL.md +30 -267
  62. package/skills/orm/SKILL.md +42 -349
  63. package/skills/realtime/SKILL.md +22 -297
  64. package/skills/services/SKILL.md +40 -183
  65. package/skills/testing/SKILL.md +34 -266
@@ -1,267 +1,48 @@
1
- # SKILL: Arckode CLI — Generadores y Comandos
1
+ # CLI — Generadores y Comandos
2
2
 
3
- > Activar cuando: crear módulo nuevo, generar código, correr migraciones, analizar arquitectura, listar rutas.
3
+ ## Stack
4
4
 
5
- ---
5
+ Commander.js + custom handlers.
6
6
 
7
- ## 1. COMANDOS DISPONIBLES
7
+ ## Comandos actuales
8
8
 
9
- ```bash
10
- arckode new <nombre> # Crear proyecto nuevo
11
- arckode make:module <Nombre> # Generar módulo completo (7 archivos)
12
- arckode make:connector <nombre> <m1> <m2> # Generar conector entre módulos
13
- arckode make:migration <descripcion> # Crear archivo de migración SQL
14
- arckode make:seed <nombre> # Crear archivo de seed
15
- arckode make:auth # Generar módulo de autenticación completo
16
- arckode analyze # Detectar violaciones de arquitectura
17
- arckode analyze --strict # Idem, falla con exit 1 si hay violaciones
18
- arckode routes # Listar todas las rutas del proyecto
19
- arckode db:migrate # Correr migraciones pendientes
20
- arckode db:migrate --rollback # Revertir última migración
21
- ```
22
-
23
- ---
24
-
25
- ## 2. `arckode make:module` — Lo que genera
26
-
27
- ```bash
28
- arckode make:module Producto
29
- ```
30
-
31
- Genera en `src/modules/producto/` (nueva estructura):
32
- ```
33
- index.ts ← ProductoModule() — registra modelos, crea repos, wirea rutas
34
- model.ts ← ProductoModel (ModelDefinition) + registerProductoModels(orm)
35
- types.ts ← ProductoDTO + CreateProductoDTO + UpdateProductoDTO + queries
36
- sockets.ts ← ProductosSockets interface
37
- service.ts ← ProductosService con RepositoryAdapter<ProductoDTO> (al root)
38
- controller.ts ← ProductosController con validateSchema en store/update (al root)
39
- validators/
40
- schema.ts ← crearProductoSchema + actualizarProductoSchema
41
- tests/
42
- service.test.ts ← 2 test cases: listar + crear con error
43
- ```
44
-
45
- **Opcionales (no generados — agregar a mano cuando aplique):**
46
- - `repository.ts` → queries con nombres del dominio (si el service repite filtros)
47
- - `usecases/{caso}.ts` → solo si hay 3+ flujos DIFERENTES (no CRUD)
9
+ | Comando | Descripción |
10
+ |---------|-------------|
11
+ | `arckode new:module <name>` | Scaffold módulo canónico |
12
+ | `arckode make:controller <mod> <name>` | Crea controller |
13
+ | `arckode make:service <mod> <name>` | Crea service |
14
+ | `arckode make:model <mod> <name>` | Crea model + types |
15
+ | `arckode analyze` | Static analysis del módulo actual |
16
+ | `arckode --help` | Todos los comandos |
48
17
 
49
- `service.ts` y `controller.ts` van al ROOT del módulo. `model.ts` está separado de `types.ts` por convención: schema DB vs contrato TypeScript.
18
+ ## Arquitectura de comando
50
19
 
51
- **Importante:** El nombre se usa en PascalCase para clases, camelCase para variables, kebab-case para rutas:
52
- - `arckode make:module LineaPedido` → clase `LineaPedidoService`, tabla `linea_pedidos`, ruta `/linea-pedidos`
53
-
54
- **Después de generar:**
55
- 1. Revisar `types.ts` → ajustar campos al dominio real
56
- 2. Revisar `validators/schema.ts` → ajustar validaciones
57
- 3. Agregar a `composition-root.ts`:
58
- ```ts
59
- import { ProductoModel } from './modules/producto/types'
60
- import { ProductoModule } from './modules/producto'
61
-
62
- orm.define('Producto', ProductoModel)
63
- system.addModule(ProductoModule())
64
- ```
65
-
66
- ---
67
-
68
- ## 3. `arckode make:connector` — Lo que genera
69
-
70
- ```bash
71
- arckode make:connector pedido-inventario pedidos inventario
72
- ```
73
-
74
- Genera `src/connectors/pedido-inventario.ts`:
75
20
  ```ts
76
- import type { ConnectorContext } from 'arckode-framework'
77
- import type { PedidosService } from '../modules/pedidos'
78
- import type { InventarioService } from '../modules/inventario'
79
-
80
- export function conectarPedidoConInventario(ctx: ConnectorContext): void {
81
- const pedidos = ctx.resolveModule<PedidosService>('pedidos')
82
- const inventario = ctx.resolveModule<InventarioService>('inventario')
83
-
84
- // TODO: inyectar sockets
85
- pedidos.setSockets({
86
- // onEventoEjemplo: async (data) => { await inventario.accion(data) }
21
+ // src/cli/commands/new-module.command.ts
22
+ import { Command } from 'commander'
23
+ export const newModuleCommand = new Command('new:module')
24
+ .argument('<name>', 'module name')
25
+ .action(async (name) => {
26
+ await scaffoldModule(name) // → llama al scaffold, no lógica inline
87
27
  })
88
- }
89
28
  ```
90
29
 
91
- **Después de generar:**
92
- 1. Implementar los sockets necesarios
93
- 2. Agregar a `composition-root.ts`:
94
- ```ts
95
- import { conectarPedidoConInventario } from './connectors/pedido-inventario'
96
- system.addConnector('pedido-inventario', conectarPedidoConInventario)
97
- ```
98
-
99
- ---
100
-
101
- ## 4. `arckode analyze` — Violaciones detectadas
102
-
103
- ```bash
104
- arckode analyze
105
- ```
106
-
107
- Output ejemplo:
108
- ```
109
- 🔍 Analizando proyecto...
110
-
111
- ❌ DIRECT_MODULE_IMPORT modules/pedidos/service.ts:5
112
- "import { ProductosService } from '../productos/service'"
113
- → Los módulos no pueden importar de otros módulos directamente.
114
-
115
- ❌ CONTROLLER_MISSING_VALIDATION modules/productos/controller.ts:23
116
- "router.post sin validateSchema()"
117
- → Todo POST debe validar el body antes de llamar al service.
118
-
119
- ⚠️ MISSING_SOCKETS modules/usuarios/
120
- → El módulo no tiene sockets.ts
121
-
122
- ✅ Sin violaciones en: pedidos, inventario, auth
123
-
124
- Resumen: 2 errores, 1 advertencia
125
- ```
126
-
127
- **Violaciones críticas (❌):** Deben corregirse antes del merge.
128
- **Advertencias (⚠️):** Recomendadas pero no bloqueantes.
129
-
130
- ### Tabla completa de checks
131
-
132
- | Código | Severidad | Descripción |
133
- |--------|-----------|-------------|
134
- | `MISSING_INDEX` | ❌ | Módulo sin index.ts |
135
- | `MISSING_SERVICE` | ❌ | Sin service.ts (al root del módulo) |
136
- | `MISSING_CONTROLLER` | ❌ | Sin controller.ts (al root del módulo) |
137
- | `LEGACY_ACTIONS_FOLDER` | ⚠️ | Módulo usa estructura legacy actions/ — mover al root |
138
- | `DUPLICATE_CONNECTOR` | ❌ | Conectores con mismo nombre lógico (kebab + camelCase) |
139
- | `UNREGISTERED_CONNECTOR` | ❌ | Archivo en connectors/ no importado en composition-root |
140
- | `MISSING_TYPES` | ❌ | Sin types.ts |
141
- | `MISSING_VALIDATORS` | ❌ | Sin validators/schema.ts |
142
- | `MISSING_TESTS` | ❌ | Sin directorio tests/ |
143
- | `MISSING_SOCKETS` | ⚠️ | Sin sockets.ts |
144
- | `MISSING_CONNECTORS` | ⚠️ | 2+ módulos sin conectores definidos |
145
- | `DIRECT_MODULE_IMPORT` | ❌ | Un módulo importa de otro |
146
- | `CONNECTOR_BUSINESS_LOGIC` | ❌ | Conector con if/for/lógica |
147
- | `CONTROLLER_MISSING_VALIDATION` | ❌ | POST/PUT/PATCH sin validateSchema |
148
- | `BUSINESS_LOGIC_IN_CONTROLLER` | ❌ | ORM directo en controller |
149
- | `EMPTY_MODULE_DESCRIPTION` | ❌ | description: '' en createModule |
150
- | `TESTS_WITHOUT_CASES` | ❌ | Archivo test sin test() |
151
- | `SERVICE_IMPORTS_OTHER_MODULE` | ❌ | Service importa de otro módulo |
152
- | `GOD_SERVICE` | ⚠️ | Service > 200 líneas |
153
- | `N_PLUS_ONE_RISK` | ⚠️ | ORM dentro de loop |
154
- | `IDOR_RISK` | ❌ | findById sin assertOwnership |
155
- | `SERVICE_DEPENDS_ON_ORM` | ❌ | Service recibe ORM, no RepositoryAdapter |
156
-
157
- ---
158
-
159
- ## 5. `arckode routes` — Ver rutas registradas
30
+ Registrar en `index.ts`:
160
31
 
161
- ```bash
162
- arckode routes
163
- ```
164
-
165
- Output:
166
- ```
167
- GET /productos
168
- POST /productos [validate: crearProductoSchema]
169
- GET /productos/:id
170
- PUT /productos/:id [auth: admin] [validate: actualizarProductoSchema]
171
- DELETE /productos/:id [auth: admin]
172
- POST /auth/login
173
- POST /auth/registro
174
- POST /auth/refresh
175
- ```
176
-
177
- ---
178
-
179
- ## 6. `arckode db:migrate` — Migraciones SQL
180
-
181
- ```bash
182
- # Ver pendientes y correrlos
183
- arckode db:migrate
184
-
185
- # Revertir último
186
- arckode db:migrate --rollback
187
- ```
188
-
189
- **Tabla de control:** `_arckode_migrations` (columnas: name, runAt, direction)
190
-
191
- **Los archivos de migración van en `src/migrations/`:**
192
- ```
193
- src/migrations/
194
- 1716000000_create_productos.ts
195
- 1716100000_add_stock_to_productos.ts
196
- 1716200000_add_index_pedidos_usuario.ts
197
- ```
198
-
199
- El timestamp en el nombre determina el orden de ejecución.
200
-
201
- ---
202
-
203
- ## 7. `arckode new` — Proyecto desde cero
204
-
205
- ```bash
206
- arckode new mi-api
207
- cd mi-api
208
- ```
209
-
210
- Genera:
211
- ```
212
- mi-api/
213
- ├── src/
214
- │ ├── composition-root.ts ← con loadEnv(), ORM, Router, System
215
- │ └── modules/
216
- │ └── .gitkeep
217
- ├── .env.example
218
- ├── .gitignore
219
- ├── CLAUDE.md ← apunta a este skill
220
- ├── package.json ← arckode-framework como dependencia
221
- └── tsconfig.json ← strict mode
222
- ```
223
-
224
- ---
225
-
226
- ## 8. INTEGRAR CON CI/CD
227
-
228
- ```yaml
229
- # .github/workflows/ci.yml
230
- - name: Analyze architecture
231
- run: arckode analyze --strict # falla el CI si hay violaciones
232
-
233
- - name: Run tests
234
- run: bun test
235
-
236
- - name: Type check
237
- run: bun run --bun tsc --noEmit
32
+ ```ts
33
+ program.addCommand(newModuleCommand)
238
34
  ```
239
35
 
240
- ---
241
-
242
- ## 9. WORKFLOW COMPLETO — Agregar feature
243
-
244
- ```bash
245
- # 1. Generar el módulo
246
- arckode make:module Pago
247
-
248
- # 2. Ajustar types.ts con los campos reales
36
+ ## Cómo extender
249
37
 
250
- # 3. Ajustar validators/schema.ts
38
+ 1. Crear archivo en `src/cli/commands/`
39
+ 2. Función handler separada en `src/cli/handlers/`
40
+ 3. Exportar e importar en `cli/index.ts`
251
41
 
252
- # 4. Implementar service.ts (lógica de negocio)
42
+ ## Troubleshooting
253
43
 
254
- # 5. Si se conecta con otro módulo
255
- arckode make:connector pago-pedido pagos pedidos
256
-
257
- # 6. Agregar a composition-root.ts
258
-
259
- # 7. Si necesita migración SQL
260
- arckode make:migration add_pagos_table
261
-
262
- # 8. Verificar todo
263
- bun run src/composition-root.ts # sin errores TypeScript
264
- arckode analyze # 0 violaciones críticas
265
- bun test modules/pago # tests pasan
266
- arckode routes # rutas visibles
267
- ```
44
+ | Problema | Causa | Fix |
45
+ |----------|-------|-----|
46
+ | Comando no registrado | `index.ts` sin `addCommand` | Import + add |
47
+ | Handler pesado | Lógica inline en action | Separar en handler |
48
+ | Sin validación | `argument('<name>')` sin name | Input normalizado en handler |
@@ -1,253 +1,44 @@
1
- # SKILL: Arckode Config — ConfigStore, Logger y Cache
1
+ # Config — ENV, Entorno, Jerarquía
2
2
 
3
- > Activar cuando: configurar variables de entorno, agregar config nueva, usar el logger, trabajar con cache.
3
+ ## Stack
4
4
 
5
- ---
5
+ `process.env` + archivo `config.ts` por módulo.
6
6
 
7
- ## 1. CONFIGSTORE + loadEnv
7
+ ## Jerarquía
8
8
 
9
- ### Setup completo
10
-
11
- ```ts
12
- import { ConfigStore, loadEnv } from 'arckode-framework'
13
-
14
- // 1. Cargar archivos .env (SIEMPRE await)
15
- const env = await loadEnv()
16
-
17
- // 2. Definir schema y cargar valores
18
- const config = new ConfigStore()
19
- config
20
- .define({
21
- // Servidor
22
- PORT: { type: 'number', default: 3000 },
23
- HOST: { type: 'string', default: '0.0.0.0' },
24
- NODE_ENV: { type: 'string', default: 'development' },
25
-
26
- // Base de datos
27
- DB_PATH: { type: 'string', default: './data/db.sqlite' },
28
- DB_HOST: { type: 'string', required: false },
29
- DB_PORT: { type: 'number', default: 5432 },
30
- DB_NAME: { type: 'string', required: false },
31
- DB_USER: { type: 'string', required: false },
32
- DB_PASSWORD: { type: 'string', required: false },
33
-
34
- // Auth
35
- JWT_SECRET: { type: 'string', required: true }, // fail-fast si falta
36
-
37
- // Opcionales con defaults
38
- LOG_LEVEL: { type: 'string', default: 'info' },
39
- SMTP_HOST: { type: 'string', required: false },
40
- SMTP_PORT: { type: 'number', default: 587 },
41
- })
42
- .load(env)
43
9
  ```
44
-
45
- ### Tipos de config disponibles
46
-
47
- | Tipo | Validación | Ejemplo |
48
- |------|-----------|---------|
49
- | `string` | Cualquier texto | `'localhost'` |
50
- | `number` | Parseado de string | `'3000'` → `3000` |
51
- | `boolean` | `'true'/'false'/'1'/'0'` | `'true'` → `true` |
52
- | `url` | Válida como URL | `'https://api.com'` |
53
- | `email` | Válido como email | `'a@b.com'` |
54
-
55
- ### Opciones por campo
56
-
57
- ```ts
58
- config.define({
59
- CAMPO: {
60
- type: 'string', // tipo (requerido)
61
- required: true, // fail-fast si no está en .env — default: false
62
- default: 'valor', // valor si no está en .env
63
- // required + default no tienen sentido juntos — elegir uno
64
- }
65
- })
10
+ process.env.* ← values directos
11
+ src/shared/config.ts ← defaults + validación
12
+ modules/{mod}/config.ts ← overrides del módulo
66
13
  ```
67
14
 
68
- ### Leer valores
69
-
70
15
  ```ts
71
- config.get('PORT') // → string (tipo no inferido)
72
- config.get<number>('PORT') // number (con genérico)
73
- config.get<boolean>('DEBUG') // → boolean
74
- config.get('JWT_SECRET') // → string
75
-
76
- // Fail-fast en get(): si la key no fue definida → error en startup
77
- ```
78
-
79
- ### Archivos .env cargados (en orden de precedencia)
80
-
81
- ```
82
- process.env ← máxima prioridad (CI/CD, Docker)
83
- .env.{NODE_ENV}.local ← ej: .env.development.local
84
- .env.{NODE_ENV} ← ej: .env.production
85
- .env.local ← local, no commitear
86
- .env ← base del proyecto
87
- ```
88
-
89
- **.gitignore obligatorio:**
90
- ```gitignore
91
- .env.local
92
- .env.*.local
93
- .env.production
94
- ```
95
-
96
- ---
97
-
98
- ## 2. LOGGER
99
-
100
- ### Crear logger
101
-
102
- ```ts
103
- import { Logger } from 'arckode-framework'
104
-
105
- // En composition-root
106
- const logger = new Logger('app', 'info') // nombre, log level
107
-
108
- // Logger hijo por módulo (recomendado)
109
- const log = logger.child('productos') // → [app.productos]
110
- const log = logger.child('productos.stock') // → [app.productos.stock]
111
- ```
112
-
113
- ### Niveles disponibles: `debug` | `info` | `warn` | `error`
114
-
115
- ```ts
116
- log.debug('Mensaje de debug', { userId, queryTime }) // solo si LOG_LEVEL=debug
117
- log.info('Operación completada', { id: item.id })
118
- log.warn('Comportamiento inesperado', { field, value })
119
- log.error('Error crítico', { error: err.message, stack: err.stack })
120
- ```
121
-
122
- ### Qué loguear y cómo
123
-
124
- ```ts
125
- // ✅ Info: eventos de negocio con contexto suficiente para debugging
126
- log.info('Pedido confirmado', { pedidoId, usuarioId, total })
127
- log.info('Usuario registrado', { userId, email })
128
-
129
- // ✅ Warn: comportamiento inusual que no es error
130
- log.warn('Stock bajo', { productoId, stockActual: 3, umbral: 10 })
131
- log.warn('Token próximo a expirar', { userId, expiresIn: '5min' })
132
-
133
- // ✅ Error: errores reales con contexto completo
134
- log.error('Fallo al enviar email', { to, error: err.message })
135
-
136
- // ❌ Debug en producción — usar solo en desarrollo
137
- log.debug('Query ejecutada', { sql, params, duration })
138
-
139
- // ❌ Nunca loguear datos sensibles
140
- log.info('Login', { email, password: dto.password }) // NO
141
- log.info('Login', { email }) // ✅
142
- ```
143
-
144
- ### Log level por entorno
145
-
146
- ```env
147
- # .env
148
- LOG_LEVEL=info
149
-
150
- # .env.development
151
- LOG_LEVEL=debug
152
- ```
153
-
154
- ---
155
-
156
- ## 3. CACHE
157
-
158
- ### Setup
159
-
160
- ```ts
161
- import { MemoryCache } from 'arckode-framework'
162
-
163
- const cache = new MemoryCache()
164
-
165
- // Para producción distribuida → Redis
166
- import { RedisCache } from 'arckode-framework/adapters/redis-cache'
167
- const cache = new RedisCache({ host: config.get('REDIS_HOST'), port: 6379 })
168
- ```
169
-
170
- ### API del Cache
171
-
172
- ```ts
173
- // Leer
174
- const value = await cache.get<ProductoDTO[]>('productos:list') // null si no existe o expiró
175
-
176
- // Guardar con TTL (en segundos)
177
- await cache.set('productos:list', items, 60) // expira en 60s
178
- await cache.set('usuario:123', user, 300) // expira en 5 min
179
- await cache.set('config:global', data) // sin TTL — persiste hasta flush()
180
-
181
- // Invalidar
182
- await cache.delete('productos:list') // borrar una key
183
- await cache.flush() // borrar todo
184
-
185
- // Estadísticas
186
- console.log(cache.stats)
187
- // { size: 42, hits: 1203, misses: 87, hitRate: '93.27%' }
188
- ```
189
-
190
- ### Patrón cache-aside (el más común)
191
-
192
- ```ts
193
- async listar(): Promise<ProductoDTO[]> {
194
- const KEY = 'productos:list'
195
-
196
- const cached = await this.cache.get<ProductoDTO[]>(KEY)
197
- if (cached) return cached // cache HIT
198
-
199
- const items = await this.repo.findMany({ activo: true })
200
- await this.cache.set(KEY, items, 60) // guardar 60s
201
- return items
16
+ // src/shared/config.ts
17
+ export const config = {
18
+ port: parseInt(process.env.PORT || '3000'),
19
+ db: {
20
+ host: process.env.DB_HOST || 'localhost',
21
+ port: parseInt(process.env.DB_PORT || '3306'),
22
+ },
23
+ jwt: {
24
+ secret: process.env.JWT_SECRET!,
25
+ },
202
26
  }
203
27
  ```
204
28
 
205
- ### Invalidar al mutar
206
-
207
- ```ts
208
- async crear(dto: CreateProductoDTO): Promise<ProductoDTO> {
209
- const item = await this.repo.create(...)
210
- await this.cache.delete('productos:list') // invalidar lista
211
- return item
212
- }
213
-
214
- async actualizar(id: string, dto: UpdateProductoDTO): Promise<ProductoDTO> {
215
- const item = await this.repo.update(id, dto)
216
- await this.cache.delete('productos:list') // invalidar lista
217
- await this.cache.delete(`producto:${id}`) // invalidar el item específico
218
- return item!
219
- }
220
- ```
221
-
222
- ### Naming convention de keys
223
-
224
- ```
225
- {entidad}:list → productos:list
226
- {entidad}:{id} → producto:abc-123
227
- {entidad}:{id}:{sub-recurso} → usuario:abc-123:pedidos
228
- {scope}:{entidad}:{filtro} → admin:reportes:2026-05
229
- ```
230
-
231
- ### TTL recomendados
232
-
233
- | Tipo de dato | TTL |
234
- |-------------|-----|
235
- | Listas con paginación | 30-60s |
236
- | Item individual | 2-5 min |
237
- | Datos de config | 10-30 min |
238
- | Contadores/stats | 5 min |
239
- | Tokens/sessions | igual que el token |
240
- | Datos de referencia (países, categorías) | 1h+ |
29
+ ## .env requerido (sin default)
241
30
 
242
- ---
31
+ | Var | Por qué |
32
+ |-----|---------|
33
+ | `JWT_SECRET` | Firma de tokens |
34
+ | `DB_PASSWORD` | Acceso a DB |
35
+ | `REDIS_URL` | Cache + colas |
243
36
 
244
- ## 4. CHECKLIST CONFIG
37
+ ## Errores silenciosos
245
38
 
246
- - [ ] `loadEnv()` con `await` — nunca sincrónico
247
- - [ ] Keys `required: true` para todo lo que cause crash si falta
248
- - [ ] Nunca `config.get()` sin haber llamado `config.define()` antes
249
- - [ ] Logger hijo por módulo: `logger.child('nombre-modulo')`
250
- - [ ] Nunca loguear passwords, tokens ni datos sensibles
251
- - [ ] Cache invalidado en toda operación de escritura que afecte la key
252
- - [ ] TTL explícito en `cache.set()` para datos que cambian frecuentemente
253
- - [ ] `.env.production` en `.gitignore` — nunca commitear secrets
39
+ | Error | Señal | Fix |
40
+ |-------|-------|-----|
41
+ | Config en runtime | `process.env.X` en controllers | Leer en `config.ts` startup |
42
+ | Sin validación | `PORT` string vs number | `parseInt` + fallback |
43
+ | `.env` en producción | Variables ausentes | Chequear en bootstrap |
44
+ | Módulo override muta shared | `config.port = 3001` en módulo | Clonar o no mutar |