arckode-framework 1.0.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/README.md +546 -0
- package/adapters/__tests__/mysql.test.ts +283 -0
- package/adapters/jwt.ts +18 -0
- package/adapters/mysql.ts +98 -0
- package/adapters/postgres.ts +52 -0
- package/adapters/redis-cache.ts +64 -0
- package/adapters/sqlite.ts +73 -0
- package/adapters/vendor.d.ts +48 -0
- package/bin/arckode.js +7 -0
- package/cli/analyze.ts +506 -0
- package/cli/commands/db-migrate.ts +121 -0
- package/cli/commands/db-seed.ts +54 -0
- package/cli/commands/generate-api-client.ts +106 -0
- package/cli/commands/make-adapter.ts +132 -0
- package/cli/commands/make-auth.ts +297 -0
- package/cli/commands/make-frontend-module.ts +271 -0
- package/cli/commands/make-helper.ts +65 -0
- package/cli/commands/make-migration.ts +30 -0
- package/cli/commands/make-seed.ts +29 -0
- package/cli/generate.ts +132 -0
- package/cli/index.ts +604 -0
- package/cli/stubs/frontend-stub.ts +294 -0
- package/cli/stubs/fullstack-stub.ts +46 -0
- package/cli/stubs/module-stub.ts +469 -0
- package/kernel/__tests__/adapters.test.ts +101 -0
- package/kernel/__tests__/analyzer.test.ts +282 -0
- package/kernel/__tests__/framework.test.ts +617 -0
- package/kernel/__tests__/middlewares.test.ts +174 -0
- package/kernel/__tests__/static.test.ts +94 -0
- package/kernel/framework.ts +1851 -0
- package/kernel/middlewares.ts +179 -0
- package/kernel/static.ts +76 -0
- package/kernel/testing.ts +237 -0
- package/modules/events/index.ts +99 -0
- package/modules/mail/index.ts +51 -0
- package/modules/mail/smtp-adapter.ts +42 -0
- package/modules/queue/index.ts +78 -0
- package/modules/storage/index.ts +40 -0
- package/modules/storage/local-adapter.ts +41 -0
- package/modules/ws/__tests__/ws.test.ts +114 -0
- package/modules/ws/index.ts +136 -0
- package/package.json +99 -0
- package/skills/auth/SKILL.md +243 -0
- package/skills/cli/SKILL.md +258 -0
- package/skills/config/SKILL.md +253 -0
- package/skills/connectors/SKILL.md +259 -0
- package/skills/helpers/SKILL.md +206 -0
- package/skills/middlewares/SKILL.md +282 -0
- package/skills/orm/SKILL.md +260 -0
- package/skills/realtime/SKILL.md +307 -0
- package/skills/services/SKILL.md +206 -0
- package/skills/testing/SKILL.md +257 -0
package/README.md
ADDED
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
# Arckode Framework
|
|
2
|
+
|
|
3
|
+
Framework TypeScript/Bun para construir APIs modulares. Diseñado desde el principio para trabajar con IA: arquitectura predecible, límites claros, cero magia.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
arckode new mi-api
|
|
7
|
+
cd mi-api && bun install
|
|
8
|
+
arckode make:auth
|
|
9
|
+
arckode make:module Productos
|
|
10
|
+
bun run dev
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Por qué Arckode
|
|
16
|
+
|
|
17
|
+
La mayoría de los frameworks están optimizados para que los humanos escriban código. Arckode está optimizado para que **la IA genere código correcto sin supervisión constante**.
|
|
18
|
+
|
|
19
|
+
La IA lee el `composition-root.ts` y sabe todo lo que necesita:
|
|
20
|
+
- Qué módulos existen y qué hacen
|
|
21
|
+
- Cómo se conectan entre sí
|
|
22
|
+
- Qué datos maneja cada uno
|
|
23
|
+
- Qué reglas tienen que cumplir
|
|
24
|
+
|
|
25
|
+
No hay decoradores, no hay magia de inyección, no hay convenciones implícitas. Todo está explícito en un solo lugar.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Instalación
|
|
30
|
+
|
|
31
|
+
**Requisito: [Bun](https://bun.sh) >= 1.0**
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# 1. Instalar el CLI globalmente — una sola vez por máquina
|
|
35
|
+
bun install -g arckode-framework
|
|
36
|
+
|
|
37
|
+
# 2. Crear tu proyecto
|
|
38
|
+
arckode new mi-api
|
|
39
|
+
|
|
40
|
+
# 3. Entrar al proyecto e instalar dependencias
|
|
41
|
+
cd mi-api && bun install
|
|
42
|
+
|
|
43
|
+
# 4. Configurar entorno
|
|
44
|
+
echo 'JWT_SECRET=cambia-esto-en-produccion' > .env
|
|
45
|
+
|
|
46
|
+
# 5. Iniciar
|
|
47
|
+
bun run src/composition-root.ts
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
> El CLI instala `arckode-framework` como dependencia de tu proyecto automáticamente. No necesitás referenciar rutas del framework — todo se importa como `from 'arckode-framework'`.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Inicio rápido
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
arckode new mi-api
|
|
58
|
+
cd mi-api && bun install
|
|
59
|
+
arckode make:auth # módulo completo de auth con JWT
|
|
60
|
+
arckode make:module Clientes
|
|
61
|
+
arckode analyze # 0 violaciones garantizado
|
|
62
|
+
bun run dev # hot reload
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Conceptos core
|
|
68
|
+
|
|
69
|
+
### Módulo
|
|
70
|
+
|
|
71
|
+
La unidad básica del sistema. Cada módulo es dueño de sus datos y su lógica. **Nunca importa de otro módulo directamente.**
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
modules/productos/
|
|
75
|
+
index.ts ← puerta pública (solo exports)
|
|
76
|
+
types.ts ← DTOs y ModelDefinition
|
|
77
|
+
sockets.ts ← hooks para conectores (opcional)
|
|
78
|
+
actions/
|
|
79
|
+
service.ts ← lógica de negocio
|
|
80
|
+
controller.ts ← capa HTTP (sin lógica)
|
|
81
|
+
validators/
|
|
82
|
+
schema.ts ← validación de entrada
|
|
83
|
+
tests/
|
|
84
|
+
service.test.ts
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
// modules/productos/index.ts
|
|
89
|
+
export function ProductosModule() {
|
|
90
|
+
return createModule({
|
|
91
|
+
name: 'productos',
|
|
92
|
+
version: '1.0.0',
|
|
93
|
+
description: 'Gestión del catálogo de productos',
|
|
94
|
+
contract: {
|
|
95
|
+
actions: ['listar', 'crear', 'actualizar', 'eliminar'],
|
|
96
|
+
events: ['onProductoCreado'],
|
|
97
|
+
tables: ['productos'],
|
|
98
|
+
},
|
|
99
|
+
create({ logger, orm, cache, router }) {
|
|
100
|
+
const repo = new OrmRepository<ProductoDTO>(orm, 'Producto')
|
|
101
|
+
const service = new ProductosService(repo, logger.child('productos'), cache)
|
|
102
|
+
const controller = new ProductosController(service, logger.child('productos'))
|
|
103
|
+
|
|
104
|
+
router.get('/productos', (req) => controller.index(req))
|
|
105
|
+
router.post('/productos', (req) => controller.store(req))
|
|
106
|
+
router.put('/productos/:id', (req) => controller.update(req))
|
|
107
|
+
router.delete('/productos/:id', (req) => controller.destroy(req))
|
|
108
|
+
|
|
109
|
+
return service // resolveModule('productos') devuelve esto
|
|
110
|
+
},
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### RepositoryAdapter\<T\>
|
|
116
|
+
|
|
117
|
+
Los servicios nunca dependen del ORM directamente. Dependen de la interfaz genérica — el ORM concreto se elige en `composition-root.ts`.
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
// ❌ PROHIBIDO — acoplado al ORM, imposible cambiar a MongoDB/Prisma
|
|
121
|
+
class ProductosService {
|
|
122
|
+
constructor(private orm: ORM) {}
|
|
123
|
+
listar() { return this.orm.findMany('Producto') }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ✅ CORRECTO — desacoplado, testeable, intercambiable
|
|
127
|
+
class ProductosService {
|
|
128
|
+
constructor(private repo: RepositoryAdapter<ProductoDTO>) {}
|
|
129
|
+
listar() { return this.repo.findMany() }
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
// composition-root.ts — se elige la implementación una sola vez
|
|
135
|
+
const repo = new OrmRepository<ProductoDTO>(orm, 'Producto') // SQLite/Postgres
|
|
136
|
+
// O mañana:
|
|
137
|
+
const repo = new MongoProductoRepo(collection) // MongoDB
|
|
138
|
+
// El service nunca cambia.
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Connector
|
|
142
|
+
|
|
143
|
+
El único puente entre módulos. Solo delegación — nunca lógica de negocio.
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
// connectors/pedido-stock.ts
|
|
147
|
+
export function conectarPedidoConStock(ctx: ConnectorContext): void {
|
|
148
|
+
const productos = ctx.resolveModule<ProductosService>('productos')
|
|
149
|
+
|
|
150
|
+
ctx.resolveModule('pedidos', {
|
|
151
|
+
onPedidoCreado: async (pedido) => {
|
|
152
|
+
await productos.descontarStock(pedido.productoId, pedido.cantidad)
|
|
153
|
+
},
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Composition Root
|
|
159
|
+
|
|
160
|
+
El único archivo que conoce todo el sistema. La IA lo lee y entiende la arquitectura completa.
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
// src/composition-root.ts
|
|
164
|
+
import { ConfigStore, ORM, Router, NodeServer, MemoryCache, System, Auth, loadEnv } from 'arckode-framework'
|
|
165
|
+
import { SqliteAdapter } from 'arckode-framework/adapters/sqlite'
|
|
166
|
+
import { jwtTokenAdapter } from 'arckode-framework/adapters/jwt'
|
|
167
|
+
import { ProductoModel } from './modules/productos/types'
|
|
168
|
+
import { ProductosModule } from './modules/productos'
|
|
169
|
+
import { PedidosModule } from './modules/pedidos'
|
|
170
|
+
import { conectarPedidoConStock } from './connectors/pedido-stock'
|
|
171
|
+
|
|
172
|
+
const env = await loadEnv() // carga .env + .env.{NODE_ENV}
|
|
173
|
+
|
|
174
|
+
const config = new ConfigStore()
|
|
175
|
+
config.define({
|
|
176
|
+
PORT: { type: 'number', default: 3000 },
|
|
177
|
+
DB_PATH: { type: 'string', default: './data/db.sqlite' },
|
|
178
|
+
JWT_SECRET: { type: 'string', required: true },
|
|
179
|
+
}).load(env)
|
|
180
|
+
|
|
181
|
+
const db = new SqliteAdapter({ path: config.get('DB_PATH') })
|
|
182
|
+
await db.connect()
|
|
183
|
+
const orm = new ORM(db)
|
|
184
|
+
|
|
185
|
+
orm.define('Producto', ProductoModel) // cada módulo es dueño de su ModelDefinition
|
|
186
|
+
await orm.migrate()
|
|
187
|
+
|
|
188
|
+
const system = new System({ config, orm, router, http, cache, auth, ... })
|
|
189
|
+
system.addModule(ProductosModule())
|
|
190
|
+
system.addModule(PedidosModule())
|
|
191
|
+
system.addConnector('pedido-stock', conectarPedidoConStock)
|
|
192
|
+
await system.start()
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## CLI — Referencia completa
|
|
198
|
+
|
|
199
|
+
### Proyectos
|
|
200
|
+
|
|
201
|
+
| Comando | Descripción |
|
|
202
|
+
|---|---|
|
|
203
|
+
| `arckode new <nombre>` | Crear proyecto backend completo |
|
|
204
|
+
| `arckode new:frontend [nombre]` | Crear frontend Vue 3 + Vite + TypeScript |
|
|
205
|
+
|
|
206
|
+
### Generadores de backend
|
|
207
|
+
|
|
208
|
+
| Comando | Descripción |
|
|
209
|
+
|---|---|
|
|
210
|
+
| `arckode make:auth` | Módulo de autenticación completo (login, register, JWT, perfil) |
|
|
211
|
+
| `arckode make:module <Nombre>` | Módulo con service, controller, types, validators, tests |
|
|
212
|
+
| `arckode make:connector <nombre> <mod1> <mod2>` | Conector entre módulos |
|
|
213
|
+
| `arckode make:seed <Nombre>` | Seed de datos |
|
|
214
|
+
| `arckode make:migration <nombre>` | Migración SQL con `up()` y `down()` |
|
|
215
|
+
| `arckode make:helper <nombre>` | Helper puro (sin efectos secundarios) |
|
|
216
|
+
| `arckode make:adapter <Adapter> <Interfaz>` | Adapter de librería externa |
|
|
217
|
+
|
|
218
|
+
### Generadores de frontend
|
|
219
|
+
|
|
220
|
+
| Comando | Descripción |
|
|
221
|
+
|---|---|
|
|
222
|
+
| `arckode make:page <Nombre>` | Módulo frontend (API client + composable + página + router) |
|
|
223
|
+
| `arckode generate:api [frontend-path]` | Genera API clients desde módulos del backend |
|
|
224
|
+
|
|
225
|
+
### Base de datos
|
|
226
|
+
|
|
227
|
+
| Comando | Descripción |
|
|
228
|
+
|---|---|
|
|
229
|
+
| `arckode db:migrate` | Ejecutar migraciones pendientes (`src/migrations/`) |
|
|
230
|
+
| `arckode db:migrate down` | Revertir la última migración |
|
|
231
|
+
| `arckode db:seed` | Listar seeds disponibles |
|
|
232
|
+
|
|
233
|
+
### Análisis y diagnóstico
|
|
234
|
+
|
|
235
|
+
| Comando | Descripción |
|
|
236
|
+
|---|---|
|
|
237
|
+
| `arckode analyze` | Detecta 15+ tipos de violaciones de arquitectura |
|
|
238
|
+
| `arckode routes` | Lista todas las rutas registradas (análisis estático) |
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Adapters
|
|
243
|
+
|
|
244
|
+
### Base de datos
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
// SQLite — desarrollo y apps de baja escala
|
|
248
|
+
import { SqliteAdapter } from 'arckode-framework/adapters/sqlite'
|
|
249
|
+
const db = new SqliteAdapter({ path: './data/app.sqlite' })
|
|
250
|
+
|
|
251
|
+
// PostgreSQL — producción
|
|
252
|
+
import { PostgresAdapter } from 'arckode-framework/adapters/postgres'
|
|
253
|
+
const db = new PostgresAdapter({ connectionString: process.env.DATABASE_URL, poolMax: 10 })
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Cache
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
// En memoria — desarrollo (se pierde al reiniciar)
|
|
260
|
+
const cache = new MemoryCache()
|
|
261
|
+
|
|
262
|
+
// Redis — producción
|
|
263
|
+
import { RedisCacheAdapter } from 'arckode-framework/adapters/redis-cache'
|
|
264
|
+
const cache = new RedisCacheAdapter({ url: process.env.REDIS_URL })
|
|
265
|
+
await cache.connect()
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Módulos opcionales
|
|
271
|
+
|
|
272
|
+
### Queue
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
import { QueueService, MemoryQueueAdapter } from 'arckode-framework/queue'
|
|
276
|
+
|
|
277
|
+
const queue = new QueueService(new MemoryQueueAdapter())
|
|
278
|
+
|
|
279
|
+
queue.register('enviar-email', async (job) => {
|
|
280
|
+
await mail.send(job.data as EmailData)
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
await queue.dispatch('enviar-email', { to: 'user@example.com', subject: 'Bienvenido' })
|
|
284
|
+
await queue.dispatch('enviar-email', { ... }, { delay: 5000, maxAttempts: 3 })
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Events (pub-sub)
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
import { EventBus } from 'arckode-framework/events'
|
|
291
|
+
|
|
292
|
+
const events = new EventBus()
|
|
293
|
+
events.on('pedido.creado', async (pedido) => { ... })
|
|
294
|
+
events.emit('pedido.creado', pedido)
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### WebSockets
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
import { WsServer } from 'arckode-framework/ws'
|
|
301
|
+
|
|
302
|
+
const ws = new WsServer()
|
|
303
|
+
ws.on('connection', (client) => { client.send({ type: 'welcome' }) })
|
|
304
|
+
ws.broadcast({ type: 'stock-bajo', productoId: id })
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Mail
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
import { MailService } from 'arckode-framework/mail'
|
|
311
|
+
import { SmtpAdapter } from 'arckode-framework/mail/smtp'
|
|
312
|
+
|
|
313
|
+
const mail = new MailService(new SmtpAdapter({ host: 'smtp.gmail.com', port: 587, ... }))
|
|
314
|
+
await mail.send({ to: 'user@example.com', subject: 'Bienvenido', html: '<p>Hola</p>' })
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Storage
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
import { StorageService } from 'arckode-framework/storage'
|
|
321
|
+
import { LocalAdapter } from 'arckode-framework/storage/local'
|
|
322
|
+
|
|
323
|
+
const storage = new StorageService(new LocalAdapter({ path: './uploads' }))
|
|
324
|
+
const url = await storage.save('avatars/user.png', fileBuffer)
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## Middlewares
|
|
330
|
+
|
|
331
|
+
```ts
|
|
332
|
+
import { cors, rateLimit, requestLogger, timeout, compression, bodyLimit } from 'arckode-framework/middlewares'
|
|
333
|
+
|
|
334
|
+
// Globales
|
|
335
|
+
router.use(cors({ origins: ['https://miapp.com'] }))
|
|
336
|
+
router.use(rateLimit({ windowMs: 60_000, max: 100 }))
|
|
337
|
+
router.use(requestLogger(logger))
|
|
338
|
+
router.use(compression())
|
|
339
|
+
|
|
340
|
+
// Por ruta
|
|
341
|
+
router.get('/admin', handler, [auth.authenticate('admin'), timeout(3000)])
|
|
342
|
+
router.post('/upload', handler, [bodyLimit(10 * 1024 * 1024)]) // 10MB
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Auth
|
|
348
|
+
|
|
349
|
+
```ts
|
|
350
|
+
const auth = new Auth(jwtTokenAdapter, process.env.JWT_SECRET, logger)
|
|
351
|
+
|
|
352
|
+
// Crear token
|
|
353
|
+
const token = await auth.createToken({ id: user.id, role: 'admin' })
|
|
354
|
+
|
|
355
|
+
// Hashear password (scrypt — sin dependencias externas)
|
|
356
|
+
const hash = await auth.hashPassword(password)
|
|
357
|
+
const ok = await auth.comparePassword(password, hash)
|
|
358
|
+
|
|
359
|
+
// Proteger rutas
|
|
360
|
+
router.get('/perfil', handler, [auth.authenticate()]) // cualquier usuario autenticado
|
|
361
|
+
router.get('/admin', handler, [auth.authenticate('admin')]) // solo admins
|
|
362
|
+
router.delete('/users/:id', handler, [auth.authenticate('admin', 'superadmin')])
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## Variables de entorno
|
|
368
|
+
|
|
369
|
+
`loadEnv()` carga `.env` base y `.env.{NODE_ENV}` con override por stage. `process.env` siempre tiene prioridad máxima.
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
# .env
|
|
373
|
+
PORT=3000
|
|
374
|
+
DB_PATH=./data/dev.sqlite
|
|
375
|
+
LOG_LEVEL=debug
|
|
376
|
+
|
|
377
|
+
# .env.production
|
|
378
|
+
DB_PATH=./data/prod.sqlite
|
|
379
|
+
LOG_LEVEL=warn
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
NODE_ENV=production bun run src/composition-root.ts
|
|
384
|
+
# Lee .env → sobrescribe con .env.production → process.env tiene prioridad
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
## Testing
|
|
390
|
+
|
|
391
|
+
```ts
|
|
392
|
+
import { createTestClient, createRecordingOrm } from 'arckode-framework/testing'
|
|
393
|
+
import { OrmRepository } from 'arckode-framework'
|
|
394
|
+
|
|
395
|
+
// ORM que registra llamadas en memoria — sin base de datos real
|
|
396
|
+
const orm = createRecordingOrm()
|
|
397
|
+
const repo = new OrmRepository<ProductoDTO>(orm, 'Producto')
|
|
398
|
+
const service = new ProductosService(repo, logger, cache)
|
|
399
|
+
|
|
400
|
+
// Cliente HTTP que hace requests al Router sin levantar un server real
|
|
401
|
+
const client = createTestClient(router)
|
|
402
|
+
|
|
403
|
+
test('listar productos vacío', async () => {
|
|
404
|
+
const res = await client.get('/productos')
|
|
405
|
+
expect(res.status).toBe(200)
|
|
406
|
+
expect(res.body).toEqual([])
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
test('crear producto', async () => {
|
|
410
|
+
const res = await client.post('/productos', {
|
|
411
|
+
body: { nombre: 'Laptop', precio: 1500, stock: 10 },
|
|
412
|
+
})
|
|
413
|
+
expect(res.status).toBe(201)
|
|
414
|
+
expect(res.body.nombre).toBe('Laptop')
|
|
415
|
+
})
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
---
|
|
419
|
+
|
|
420
|
+
## Análisis de arquitectura
|
|
421
|
+
|
|
422
|
+
```bash
|
|
423
|
+
arckode analyze
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
```
|
|
427
|
+
══════════════════════════════════════════════
|
|
428
|
+
Arckode — Análisis de Arquitectura
|
|
429
|
+
══════════════════════════════════════════════
|
|
430
|
+
|
|
431
|
+
VIOLACIONES ENCONTRADAS: 2
|
|
432
|
+
|
|
433
|
+
[Acoplamiento]
|
|
434
|
+
❌ modules/pedidos/actions/service.ts:12
|
|
435
|
+
Importa directamente de otro módulo (CLAUDE #1)
|
|
436
|
+
→ Usar un conector en /connectors/
|
|
437
|
+
|
|
438
|
+
[Portabilidad]
|
|
439
|
+
❌ modules/clientes/actions/service.ts:8
|
|
440
|
+
El service inyecta ORM directamente (CLAUDE #18)
|
|
441
|
+
→ Usar RepositoryAdapter<ClienteDTO>
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
| Categoría | Violations detectadas |
|
|
445
|
+
|---|---|
|
|
446
|
+
| Estructura | `MISSING_INDEX`, `MISSING_TYPES`, `MISSING_SERVICE`, `MISSING_CONTROLLER`, `MISSING_TESTS` |
|
|
447
|
+
| Acoplamiento | `DIRECT_MODULE_IMPORT` |
|
|
448
|
+
| Diseño | `BUSINESS_LOGIC_IN_CONTROLLER`, `CONTROLLER_MISSING_VALIDATION`, `EMPTY_MODULE_DESCRIPTION` |
|
|
449
|
+
| Calidad | `TESTS_WITHOUT_CASES`, `GOD_SERVICE` |
|
|
450
|
+
| Seguridad | `IDOR_RISK`, `HARDCODED_SECRET`, `INSECURE_PASSWORD` |
|
|
451
|
+
| Performance | `N_PLUS_ONE_RISK` |
|
|
452
|
+
| Portabilidad | `SERVICE_DEPENDS_ON_ORM` |
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
456
|
+
## Ejemplos incluidos
|
|
457
|
+
|
|
458
|
+
| Ejemplo | Descripción |
|
|
459
|
+
|---|---|
|
|
460
|
+
| `examples/ecommerce/` | Productos, pedidos, conector de stock, auth |
|
|
461
|
+
| `examples/completo/` | Auth, productos, pedidos, mail, storage, queue, WebSockets |
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## Documentación adicional
|
|
466
|
+
|
|
467
|
+
| Archivo | Contenido |
|
|
468
|
+
|---|---|
|
|
469
|
+
| `CLAUDE.md` | 18 reglas inmutables — el contrato que la IA sigue |
|
|
470
|
+
| `kernel/framework.ts` | Fuente completa del kernel (la IA lo lee entero) |
|
|
471
|
+
| `kernel/testing.ts` | Utilidades de testing |
|
|
472
|
+
| `kernel/middlewares.ts` | Middlewares disponibles |
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## Cómo funciona la distribución
|
|
477
|
+
|
|
478
|
+
### Para usuarios del framework
|
|
479
|
+
|
|
480
|
+
```bash
|
|
481
|
+
# Instalar CLI una vez
|
|
482
|
+
bun install -g arckode-framework
|
|
483
|
+
|
|
484
|
+
# Crear proyecto — el CLI genera todo: composition-root.ts, CLAUDE.md, .env, package.json
|
|
485
|
+
arckode new mi-tienda --db=postgres
|
|
486
|
+
|
|
487
|
+
# CLAUDE.md se genera automáticamente en tu proyecto con las 18 reglas del framework.
|
|
488
|
+
# La IA lo lee primero antes de escribir cualquier línea de código.
|
|
489
|
+
|
|
490
|
+
# Actualizar el framework en tu proyecto
|
|
491
|
+
bun update arckode-framework
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### Para la IA (flujo de trabajo)
|
|
495
|
+
|
|
496
|
+
Cuando la IA trabaja en un proyecto con Arckode, hace esto en orden:
|
|
497
|
+
|
|
498
|
+
```
|
|
499
|
+
1. Lee CLAUDE.md → entiende las 18 reglas que NO puede violar
|
|
500
|
+
2. Lee composition-root.ts → entiende todos los módulos, conectores y dependencias
|
|
501
|
+
3. Corre arckode analyze → verifica que el estado actual tiene 0 violaciones
|
|
502
|
+
4. Genera código → siguiendo la estructura exacta del framework
|
|
503
|
+
5. Corre arckode analyze → verifica que sus cambios no introdujeron violaciones
|
|
504
|
+
6. Corre bun test → verifica que nada se rompió
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### Para el autor del framework (actualizaciones)
|
|
508
|
+
|
|
509
|
+
```bash
|
|
510
|
+
# 1. Hacer los cambios
|
|
511
|
+
# 2. Correr tests y typecheck
|
|
512
|
+
bun test && bun run typecheck
|
|
513
|
+
|
|
514
|
+
# 3. Subir la versión en package.json (semver)
|
|
515
|
+
# 1.0.0 → 1.0.1 patch: bugfix
|
|
516
|
+
# 1.0.0 → 1.1.0 minor: feature nueva, retrocompatible
|
|
517
|
+
# 1.0.0 → 2.0.0 major: breaking change
|
|
518
|
+
|
|
519
|
+
# 4. Publicar
|
|
520
|
+
npm publish
|
|
521
|
+
|
|
522
|
+
# Los usuarios actualizan corriendo en su proyecto:
|
|
523
|
+
bun update arckode-framework
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
---
|
|
527
|
+
|
|
528
|
+
## Adapters opcionales
|
|
529
|
+
|
|
530
|
+
Arckode solo instala lo que usás. Cada adapter tiene su dependencia peer:
|
|
531
|
+
|
|
532
|
+
| Adapter | Instalar |
|
|
533
|
+
|---|---|
|
|
534
|
+
| SQLite | `bun add better-sqlite3` |
|
|
535
|
+
| PostgreSQL | `bun add pg` |
|
|
536
|
+
| MySQL | `bun add mysql2` |
|
|
537
|
+
| Redis (cache) | `bun add redis` |
|
|
538
|
+
| Mail (SMTP) | `bun add nodemailer` |
|
|
539
|
+
|
|
540
|
+
El framework core (`kernel/framework.ts`) **no depende de ninguna librería externa** — solo de Node.js/Bun nativo.
|
|
541
|
+
|
|
542
|
+
---
|
|
543
|
+
|
|
544
|
+
## Licencia
|
|
545
|
+
|
|
546
|
+
MIT
|