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.
- 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/claude-md-stub.ts +21 -8
- 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 +38 -21
- 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 -267
- 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
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { AnalysisResult, Violation } from './types'
|
|
2
|
+
|
|
3
|
+
export function printAnalysis(result: AnalysisResult): void {
|
|
4
|
+
console.log('\n══════════════════════════════════════')
|
|
5
|
+
console.log(' Análisis de Arquitectura')
|
|
6
|
+
console.log('══════════════════════════════════════\n')
|
|
7
|
+
|
|
8
|
+
console.log(`📦 Módulos (${result.modules.length}):`)
|
|
9
|
+
for (const m of result.modules) {
|
|
10
|
+
const icon = m.hasIndex ? '✓' : '✗'
|
|
11
|
+
const parts = [
|
|
12
|
+
m.hasService ? 'service' : null,
|
|
13
|
+
m.hasController ? 'controller' : null,
|
|
14
|
+
m.hasTypes ? 'types' : null,
|
|
15
|
+
m.hasSockets ? 'sockets' : null,
|
|
16
|
+
m.hasValidators ? 'validators' : null,
|
|
17
|
+
m.hasTests ? 'tests' : null,
|
|
18
|
+
].filter(Boolean)
|
|
19
|
+
console.log(` ${icon} ${m.name} [${parts.join(', ')}]`)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
console.log(`\n🔌 Conectores (${result.connectors.length}):`)
|
|
23
|
+
for (const c of result.connectors) console.log(` ${c}`)
|
|
24
|
+
|
|
25
|
+
if (result.violations.length > 0) {
|
|
26
|
+
const byCategory: Record<string, Violation[]> = {
|
|
27
|
+
'Estructura': [], 'Acoplamiento': [], 'Diseño': [],
|
|
28
|
+
'Calidad': [], 'Seguridad': [], 'Performance': [], 'Portabilidad': [],
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
for (const v of result.violations) {
|
|
32
|
+
if (['MISSING_INDEX', 'MISSING_SERVICE', 'MISSING_TYPES', 'MISSING_SOCKETS', 'MISSING_VALIDATORS', 'MISSING_TESTS', 'MISSING_CONNECTORS', 'EMPTY_MODULE_DESCRIPTION', 'MODEL_IN_TYPES_FILE', 'MISSING_MODEL_TS'].includes(v.type)) {
|
|
33
|
+
byCategory['Estructura']!.push(v)
|
|
34
|
+
} else if (['DIRECT_MODULE_IMPORT', 'SERVICE_IMPORTS_OTHER_MODULE', 'CONNECTOR_BUSINESS_LOGIC', 'DUPLICATE_CONNECTOR', 'UNREGISTERED_CONNECTOR'].includes(v.type)) {
|
|
35
|
+
byCategory['Acoplamiento']!.push(v)
|
|
36
|
+
} else if (['BUSINESS_LOGIC_IN_CONTROLLER', 'GOD_SERVICE', 'INDEX_PENDING_CHANGES'].includes(v.type)) {
|
|
37
|
+
byCategory['Diseño']!.push(v)
|
|
38
|
+
} else if (['IDOR_RISK'].includes(v.type)) {
|
|
39
|
+
byCategory['Seguridad']!.push(v)
|
|
40
|
+
} else if (['N_PLUS_ONE_RISK'].includes(v.type)) {
|
|
41
|
+
byCategory['Performance']!.push(v)
|
|
42
|
+
} else if (['SERVICE_DEPENDS_ON_ORM'].includes(v.type)) {
|
|
43
|
+
byCategory['Portabilidad']!.push(v)
|
|
44
|
+
} else {
|
|
45
|
+
byCategory['Calidad']!.push(v)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log(`\n❌ Violaciones (${result.violations.length}):`)
|
|
50
|
+
for (const [cat, vs] of Object.entries(byCategory)) {
|
|
51
|
+
if (vs.length === 0) continue
|
|
52
|
+
console.log(`\n [${cat}]`)
|
|
53
|
+
for (const v of vs) console.log(` • ${v.message}`)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (result.warnings.length > 0) {
|
|
58
|
+
console.log(`\n⚠️ Advertencias (${result.warnings.length}):`)
|
|
59
|
+
for (const w of result.warnings) console.log(` ${w.message}`)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
console.log(`\n📊 Resultado: ${result.valid ? '✅ VÁLIDO' : '❌ VIOLACIONES ENCONTRADAS'}`)
|
|
63
|
+
console.log('══════════════════════════════════════\n')
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function buildManifest(result: AnalysisResult): Record<string, unknown> {
|
|
67
|
+
return {
|
|
68
|
+
_arckode: '1.0.0',
|
|
69
|
+
_generated: new Date().toISOString(),
|
|
70
|
+
_instructions: 'Lee este archivo ANTES de escribir código. Contiene el estado completo del sistema: módulos, conectores, violaciones. Si está desactualizado, corré: bun run analyze --json',
|
|
71
|
+
|
|
72
|
+
system: {
|
|
73
|
+
healthy: result.valid,
|
|
74
|
+
violations: result.violations.length,
|
|
75
|
+
warnings: result.warnings.length,
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
modules: result.modules.map(m => ({
|
|
79
|
+
name: m.name,
|
|
80
|
+
structure: {
|
|
81
|
+
index: m.hasIndex, types: m.hasTypes, sockets: m.hasSockets,
|
|
82
|
+
service: m.hasService, controller: m.hasController,
|
|
83
|
+
validators: m.hasValidators, tests: m.hasTests,
|
|
84
|
+
},
|
|
85
|
+
complete: m.hasIndex && m.hasTypes && m.hasService && m.hasController && m.hasValidators && m.hasTests,
|
|
86
|
+
})),
|
|
87
|
+
|
|
88
|
+
connectors: result.connectors,
|
|
89
|
+
|
|
90
|
+
violations: result.violations.map(v => ({ module: v.module, type: v.type, message: v.message })),
|
|
91
|
+
|
|
92
|
+
warnings: result.warnings.map(w => ({ type: w.type, message: w.message })),
|
|
93
|
+
|
|
94
|
+
rules_summary: [
|
|
95
|
+
'REGLA 1: Módulos NO importan de otros módulos. Usar conectores.',
|
|
96
|
+
'REGLA 2: index.ts es APPEND-ONLY. No eliminar exports.',
|
|
97
|
+
'REGLA 3: Conectores NO tienen lógica de negocio. Solo delegan.',
|
|
98
|
+
'REGLA 4: Cada tabla pertenece a UN módulo.',
|
|
99
|
+
'REGLA 5: Si no está en composition-root.ts, no existe.',
|
|
100
|
+
'REGLA 6: Todo POST/PUT/PATCH requiere validateSchema().',
|
|
101
|
+
'REGLA 7: Controller NO llama al ORM. Llama al service.',
|
|
102
|
+
'REGLA 18: Service recibe RepositoryAdapter<T>, no ORM directamente.',
|
|
103
|
+
'REGLA 22: model.ts separado de types.ts. Schema DB ≠ contrato TS.',
|
|
104
|
+
'REGLA 23: Un DEFAULT por caso. Escalar SOLO cuando la condición lo justifica.',
|
|
105
|
+
],
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface AnalysisResult {
|
|
2
|
+
valid: boolean
|
|
3
|
+
modules: ModuleReport[]
|
|
4
|
+
connectors: string[]
|
|
5
|
+
violations: Violation[]
|
|
6
|
+
warnings: Warning[]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ModuleReport {
|
|
10
|
+
name: string
|
|
11
|
+
hasIndex: boolean
|
|
12
|
+
hasService: boolean
|
|
13
|
+
hasController: boolean
|
|
14
|
+
hasTypes: boolean
|
|
15
|
+
hasSockets: boolean
|
|
16
|
+
hasValidators: boolean
|
|
17
|
+
hasTests: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface Violation {
|
|
21
|
+
type:
|
|
22
|
+
| 'DIRECT_MODULE_IMPORT'
|
|
23
|
+
| 'MISSING_INDEX' | 'MISSING_CONTRACT' | 'MISSING_SERVICE' | 'MISSING_TYPES'
|
|
24
|
+
| 'MISSING_SOCKETS' | 'MISSING_VALIDATORS' | 'MISSING_TESTS'
|
|
25
|
+
| 'MISSING_CONNECTORS' | 'CONNECTOR_BUSINESS_LOGIC' | 'INDEX_PENDING_CHANGES'
|
|
26
|
+
| 'CONTROLLER_MISSING_VALIDATION'
|
|
27
|
+
| 'BUSINESS_LOGIC_IN_CONTROLLER'
|
|
28
|
+
| 'EMPTY_MODULE_DESCRIPTION'
|
|
29
|
+
| 'TESTS_WITHOUT_CASES'
|
|
30
|
+
| 'SERVICE_IMPORTS_OTHER_MODULE'
|
|
31
|
+
| 'GOD_SERVICE'
|
|
32
|
+
| 'N_PLUS_ONE_RISK'
|
|
33
|
+
| 'IDOR_RISK'
|
|
34
|
+
| 'SERVICE_DEPENDS_ON_ORM'
|
|
35
|
+
| 'MODEL_IN_TYPES_FILE'
|
|
36
|
+
| 'MISSING_MODEL_TS'
|
|
37
|
+
| 'DUPLICATE_CONNECTOR'
|
|
38
|
+
| 'UNREGISTERED_CONNECTOR'
|
|
39
|
+
module: string
|
|
40
|
+
message: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface Warning {
|
|
44
|
+
type: 'NO_CONNECTORS' | 'SINGLE_MODULE' | 'NO_COMPOSITION_ROOT' | 'INDEX_PENDING_CHANGES' | 'LEGACY_ACTIONS_FOLDER'
|
|
45
|
+
message: string
|
|
46
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { readdir, access } from 'node:fs/promises'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
|
|
4
|
+
export async function fileExists(path: string): Promise<boolean> {
|
|
5
|
+
try { await access(path); return true } catch { return false }
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function resolvePath(modPath: string, candidates: string[]): Promise<string | null> {
|
|
9
|
+
for (const candidate of candidates) {
|
|
10
|
+
const full = join(modPath, candidate)
|
|
11
|
+
if (await fileExists(full)) return full
|
|
12
|
+
}
|
|
13
|
+
return null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function canonicalizeConnectorName(filename: string): string {
|
|
17
|
+
return filename.replace(/\.ts$/, '').replace(/[-_]/g, '').toLowerCase()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function getAllTsFiles(dir: string): Promise<string[]> {
|
|
21
|
+
const files: string[] = []
|
|
22
|
+
try {
|
|
23
|
+
const entries = await readdir(dir, { withFileTypes: true })
|
|
24
|
+
for (const entry of entries) {
|
|
25
|
+
const full = join(dir, entry.name)
|
|
26
|
+
if (entry.isDirectory()) {
|
|
27
|
+
if (entry.name !== 'node_modules' && entry.name !== 'tests' && entry.name !== '__tests__') {
|
|
28
|
+
files.push(...await getAllTsFiles(full))
|
|
29
|
+
}
|
|
30
|
+
} else if (entry.name.endsWith('.ts')) {
|
|
31
|
+
files.push(full)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} catch { /* ignore */ }
|
|
35
|
+
return files
|
|
36
|
+
}
|