arckode-framework 1.3.2 → 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.
- package/adapters/jwt.ts +6 -4
- package/adapters/mysql.ts +7 -2
- package/adapters/postgres.ts +37 -0
- package/adapters/sqlite.ts +7 -1
- package/adapters/vendor.d.ts +48 -0
- package/cli/analyze/checks.ts +333 -0
- package/cli/analyze/index.ts +44 -0
- package/cli/analyze/report.ts +107 -0
- package/cli/analyze/types.ts +46 -0
- package/cli/analyze/utils.ts +36 -0
- package/cli/analyze.ts +2 -647
- package/cli/commands/db-migrate.ts +213 -89
- package/cli/commands/db-seed.ts +97 -32
- package/cli/commands/db-utils.ts +192 -0
- package/cli/commands/new.ts +175 -0
- package/cli/commands/routes.ts +94 -0
- package/cli/index.ts +57 -404
- package/cli/stubs/module/core.ts +162 -0
- package/cli/stubs/module/data.ts +171 -0
- package/cli/stubs/module/index.ts +5 -0
- package/cli/stubs/module/service.ts +198 -0
- package/cli/stubs/module/types.ts +12 -0
- package/cli/stubs/module-stub.ts +2 -552
- package/kernel/auth.ts +114 -0
- package/kernel/cache.ts +37 -0
- package/kernel/config.ts +129 -0
- package/kernel/container.ts +64 -0
- package/kernel/db/orm-migrate.ts +136 -0
- package/kernel/db/orm-repository.ts +45 -0
- package/kernel/db/orm-utils.ts +93 -0
- package/kernel/db/orm.ts +254 -0
- package/kernel/db/transactor.ts +17 -0
- package/kernel/db/types.ts +72 -0
- package/kernel/errors.ts +102 -0
- package/kernel/framework.default.ts +41 -0
- package/kernel/framework.ts +8 -2144
- package/kernel/http/router.ts +131 -0
- package/kernel/http/server.ts +303 -0
- package/kernel/http/types.ts +56 -0
- package/kernel/index.ts +25 -0
- package/kernel/logger.ts +50 -0
- package/kernel/middlewares.ts +19 -7
- package/kernel/modules/create-module.ts +5 -0
- package/kernel/modules/system.ts +149 -0
- package/kernel/modules/types.ts +46 -0
- package/kernel/seeds.ts +48 -0
- package/kernel/static.ts +11 -2
- package/kernel/testing.ts +8 -3
- package/kernel/validator.ts +116 -0
- package/modules/events/index.ts +19 -3
- package/modules/mail/index.ts +14 -2
- package/modules/storage/local-adapter.ts +19 -5
- package/modules/ws/index.ts +123 -18
- package/package.json +8 -11
- package/skills/auth/SKILL.md +36 -220
- package/skills/cli/SKILL.md +32 -251
- package/skills/config/SKILL.md +30 -239
- package/skills/connectors/SKILL.md +32 -295
- package/skills/helpers/SKILL.md +26 -195
- package/skills/middlewares/SKILL.md +30 -280
- package/skills/orm/SKILL.md +42 -349
- package/skills/realtime/SKILL.md +22 -297
- package/skills/services/SKILL.md +40 -183
- package/skills/testing/SKILL.md +34 -266
package/skills/cli/SKILL.md
CHANGED
|
@@ -1,267 +1,48 @@
|
|
|
1
|
-
#
|
|
1
|
+
# CLI — Generadores y Comandos
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Stack
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Commander.js + custom handlers.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Comandos actuales
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
arckode
|
|
12
|
-
arckode make:
|
|
13
|
-
arckode make:
|
|
14
|
-
arckode make:
|
|
15
|
-
arckode
|
|
16
|
-
arckode
|
|
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
|
-
|
|
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
|
-
|
|
77
|
-
import
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
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
|
-
```
|
|
162
|
-
|
|
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
|
-
|
|
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
|
-
|
|
42
|
+
## Troubleshooting
|
|
253
43
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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 |
|
package/skills/config/SKILL.md
CHANGED
|
@@ -1,253 +1,44 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Config — ENV, Entorno, Jerarquía
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Stack
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
`process.env` + archivo `config.ts` por módulo.
|
|
6
6
|
|
|
7
|
-
##
|
|
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
|
-
|
|
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.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
37
|
+
## Errores silenciosos
|
|
245
38
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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 |
|