arckode-framework 1.3.2 → 1.4.1
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
|
@@ -1,121 +1,245 @@
|
|
|
1
|
-
// cli/commands/db-migrate.ts —
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
// cli/commands/db-migrate.ts — Migraciones SQL versionadas (multi-DB)
|
|
2
|
+
// Soporta SQLite (bun:sqlite nativo), MySQL y PostgreSQL
|
|
3
|
+
// Detecta BD desde .env: DATABASE_URL (postgres), DB_HOST (mysql), DB_PATH (sqlite)
|
|
4
|
+
//
|
|
5
|
+
// Acciones disponibles:
|
|
6
|
+
// up — aplica migraciones pendientes (default)
|
|
7
|
+
// down — revierte la última migración aplicada
|
|
8
|
+
// fresh — dropea TODAS las tablas y re-corre todo desde cero
|
|
9
|
+
// reset — revierte TODAS las migraciones (newest first)
|
|
10
|
+
// refresh — reset + up (equivalente a migrate:refresh de Laravel)
|
|
11
|
+
// status — muestra el estado de cada migración
|
|
12
|
+
|
|
13
|
+
import { readdir } from 'node:fs/promises'
|
|
14
|
+
import { join } from 'node:path'
|
|
8
15
|
import { existsSync } from 'node:fs'
|
|
16
|
+
import { parseEnv, createCliDbRunner, type CliDbRunner } from './db-utils'
|
|
17
|
+
|
|
18
|
+
// Interfaz que reciben up() y down() en cada archivo de migración.
|
|
19
|
+
// Importá este tipo en tus migraciones para evitar usar `any`:
|
|
20
|
+
// import type { MigrationRunner } from 'arckode-framework/cli/commands/db-migrate'
|
|
21
|
+
export interface MigrationRunner {
|
|
22
|
+
run(sql: string, params?: unknown[]): Promise<void>
|
|
23
|
+
exec(sql: string): Promise<void>
|
|
24
|
+
}
|
|
9
25
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
26
|
+
export type MigrateAction = 'up' | 'down' | 'fresh' | 'reset' | 'refresh' | 'status'
|
|
27
|
+
|
|
28
|
+
// ─── Tracking table (_arckode_migrations) ────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
async function ensureTable(db: CliDbRunner): Promise<void> {
|
|
31
|
+
await db.exec(`
|
|
32
|
+
CREATE TABLE IF NOT EXISTS _arckode_migrations (
|
|
33
|
+
name VARCHAR(255) PRIMARY KEY,
|
|
34
|
+
direction VARCHAR(10) NOT NULL DEFAULT 'up',
|
|
35
|
+
runAt VARCHAR(50) NOT NULL
|
|
36
|
+
)
|
|
37
|
+
`)
|
|
18
38
|
}
|
|
19
39
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
40
|
+
async function getApplied(db: CliDbRunner): Promise<Set<string>> {
|
|
41
|
+
const rows = await db.query<{ name: string }>(
|
|
42
|
+
'SELECT name FROM _arckode_migrations WHERE direction = ?', ['up'],
|
|
43
|
+
)
|
|
44
|
+
return new Set(rows.map(r => r.name))
|
|
23
45
|
}
|
|
24
46
|
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
47
|
+
async function getAllAppliedOrdered(db: CliDbRunner): Promise<string[]> {
|
|
48
|
+
const rows = await db.query<{ name: string }>(
|
|
49
|
+
'SELECT name FROM _arckode_migrations WHERE direction = ? ORDER BY runAt ASC', ['up'],
|
|
50
|
+
)
|
|
51
|
+
return rows.map(r => r.name)
|
|
52
|
+
}
|
|
28
53
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
54
|
+
async function getAppliedAt(db: CliDbRunner, name: string): Promise<string> {
|
|
55
|
+
const rows = await db.query<{ runAt: string }>(
|
|
56
|
+
'SELECT runAt FROM _arckode_migrations WHERE name = ?', [name],
|
|
57
|
+
)
|
|
58
|
+
return rows[0]?.runAt ?? '?'
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function markApplied(db: CliDbRunner, name: string): Promise<void> {
|
|
62
|
+
await db.run(
|
|
63
|
+
'INSERT INTO _arckode_migrations (name, direction, runAt) VALUES (?, ?, ?)',
|
|
64
|
+
[name, 'up', new Date().toISOString()],
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function markReverted(db: CliDbRunner, name: string): Promise<void> {
|
|
69
|
+
await db.run('DELETE FROM _arckode_migrations WHERE name = ?', [name])
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function getLastApplied(db: CliDbRunner): Promise<string | undefined> {
|
|
73
|
+
const rows = await db.query<{ name: string }>(
|
|
74
|
+
'SELECT name FROM _arckode_migrations WHERE direction = ? ORDER BY runAt DESC LIMIT 1', ['up'],
|
|
75
|
+
)
|
|
76
|
+
return rows[0]?.name
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ─── Helpers de ejecución ────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
async function applyOne(db: CliDbRunner, file: string, filePath: string): Promise<void> {
|
|
82
|
+
const migration = await import(filePath)
|
|
83
|
+
if (typeof migration.up !== 'function') {
|
|
84
|
+
console.warn(`⚠️ ${file} no exporta up() — saltando`)
|
|
32
85
|
return
|
|
33
86
|
}
|
|
87
|
+
console.log(` ▶ Aplicando: ${file}`)
|
|
88
|
+
await db.inTransaction(async () => {
|
|
89
|
+
await migration.up(db as MigrationRunner)
|
|
90
|
+
await markApplied(db, file)
|
|
91
|
+
})
|
|
92
|
+
console.log(` ✅ ${file}`)
|
|
93
|
+
}
|
|
34
94
|
|
|
35
|
-
|
|
36
|
-
const
|
|
95
|
+
async function revertOne(db: CliDbRunner, name: string, migrationsDir: string): Promise<void> {
|
|
96
|
+
const filePath = join(migrationsDir, name)
|
|
97
|
+
if (!existsSync(filePath)) {
|
|
98
|
+
console.warn(` ⚠️ Archivo ${name} no existe — saltando revert`)
|
|
99
|
+
await markReverted(db, name)
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
const migration = await import(filePath)
|
|
103
|
+
if (typeof migration.down !== 'function') {
|
|
104
|
+
console.warn(` ⚠️ ${name} no exporta down() — saltando`)
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
console.log(` ▶ Revirtiendo: ${name}`)
|
|
108
|
+
await db.inTransaction(async () => {
|
|
109
|
+
await migration.down(db as MigrationRunner)
|
|
110
|
+
await markReverted(db, name)
|
|
111
|
+
})
|
|
112
|
+
console.log(` ✅ Revertida: ${name}`)
|
|
113
|
+
}
|
|
37
114
|
|
|
38
|
-
|
|
115
|
+
// ─── Acciones ────────────────────────────────────────────────────────────────
|
|
39
116
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
direction TEXT NOT NULL,
|
|
44
|
-
runAt TEXT NOT NULL
|
|
45
|
-
)
|
|
46
|
-
`)
|
|
117
|
+
async function runUp(db: CliDbRunner, files: string[], migrationsDir: string): Promise<void> {
|
|
118
|
+
const applied = await getApplied(db)
|
|
119
|
+
const pending = files.filter(f => !applied.has(f))
|
|
47
120
|
|
|
48
|
-
|
|
49
|
-
.
|
|
50
|
-
.sort()
|
|
51
|
-
|
|
52
|
-
if (files.length === 0) {
|
|
53
|
-
console.log('📭 No hay archivos en src/migrations/')
|
|
54
|
-
db.close()
|
|
121
|
+
if (pending.length === 0) {
|
|
122
|
+
console.log('✅ No hay migraciones pendientes')
|
|
55
123
|
return
|
|
56
124
|
}
|
|
57
125
|
|
|
58
|
-
const
|
|
59
|
-
(db
|
|
60
|
-
|
|
61
|
-
)
|
|
126
|
+
for (const file of pending) {
|
|
127
|
+
await applyOne(db, file, join(migrationsDir, file))
|
|
128
|
+
}
|
|
129
|
+
console.log(`\n${pending.length} migración(es) aplicada(s)`)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function runDown(db: CliDbRunner, migrationsDir: string): Promise<void> {
|
|
133
|
+
const last = await getLastApplied(db)
|
|
134
|
+
if (!last) { console.log('📭 No hay migraciones para revertir'); return }
|
|
135
|
+
await revertOne(db, last, migrationsDir)
|
|
136
|
+
}
|
|
62
137
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
138
|
+
async function runReset(db: CliDbRunner, migrationsDir: string): Promise<void> {
|
|
139
|
+
const applied = await getAllAppliedOrdered(db)
|
|
140
|
+
if (applied.length === 0) { console.log('📭 No hay migraciones para revertir'); return }
|
|
141
|
+
|
|
142
|
+
console.log(`🔄 Revirtiendo ${applied.length} migración(es)...\n`)
|
|
143
|
+
for (const name of [...applied].reverse()) {
|
|
144
|
+
await revertOne(db, name, migrationsDir)
|
|
66
145
|
}
|
|
146
|
+
console.log(`\n${applied.length} migración(es) revertida(s)`)
|
|
147
|
+
}
|
|
67
148
|
|
|
68
|
-
|
|
69
|
-
|
|
149
|
+
async function runFresh(db: CliDbRunner, files: string[], migrationsDir: string): Promise<void> {
|
|
150
|
+
console.log('🗑 Dropeando todas las tablas...')
|
|
151
|
+
await db.dropAllTables()
|
|
152
|
+
console.log('✅ Tablas dropeadas\n')
|
|
70
153
|
|
|
71
|
-
|
|
72
|
-
console.log('✅ No hay migraciones pendientes')
|
|
73
|
-
db.close()
|
|
74
|
-
return
|
|
75
|
-
}
|
|
154
|
+
await ensureTable(db)
|
|
76
155
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
156
|
+
console.log(`▶ Aplicando ${files.length} migración(es)...\n`)
|
|
157
|
+
for (const file of files) {
|
|
158
|
+
await applyOne(db, file, join(migrationsDir, file))
|
|
159
|
+
}
|
|
160
|
+
console.log(`\n${files.length} migración(es) aplicada(s)`)
|
|
161
|
+
}
|
|
80
162
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
163
|
+
async function runRefresh(db: CliDbRunner, files: string[], migrationsDir: string): Promise<void> {
|
|
164
|
+
console.log('🔄 Refresh: reset + migrate\n')
|
|
165
|
+
await runReset(db, migrationsDir)
|
|
166
|
+
console.log()
|
|
167
|
+
await ensureTable(db)
|
|
168
|
+
await runUp(db, files, migrationsDir)
|
|
169
|
+
}
|
|
85
170
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
db.prepare('INSERT INTO _arckode_migrations (name, direction, runAt) VALUES (?, ?, ?)')
|
|
89
|
-
.run(file, 'up', new Date().toISOString())
|
|
90
|
-
console.log(`✅ ${file}`)
|
|
91
|
-
}
|
|
171
|
+
async function runStatus(db: CliDbRunner, files: string[]): Promise<void> {
|
|
172
|
+
const applied = await getApplied(db)
|
|
92
173
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const last = db.prepare('SELECT name FROM _arckode_migrations WHERE direction = ? ORDER BY runAt DESC LIMIT 1')
|
|
97
|
-
.get('up') as { name: string } | undefined
|
|
174
|
+
const COL_NAME = Math.max(40, ...files.map(f => f.length)) + 2
|
|
175
|
+
const header = ` ${'Migración'.padEnd(COL_NAME)} ${'Estado'.padEnd(10)} Aplicada en`
|
|
176
|
+
const sep = ` ${'-'.repeat(COL_NAME)} ${'-'.repeat(10)} ${'-'.repeat(26)}`
|
|
98
177
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
return
|
|
103
|
-
}
|
|
178
|
+
console.log('\n📋 Estado de migraciones\n')
|
|
179
|
+
console.log(header)
|
|
180
|
+
console.log(sep)
|
|
104
181
|
|
|
105
|
-
|
|
106
|
-
|
|
182
|
+
let appliedCount = 0
|
|
183
|
+
let pendingCount = 0
|
|
184
|
+
|
|
185
|
+
for (const file of files) {
|
|
186
|
+
const isApplied = applied.has(file)
|
|
187
|
+
const status = isApplied ? '✅ aplicada' : '⏳ pendiente'
|
|
188
|
+
const when = isApplied ? await getAppliedAt(db, file) : '—'
|
|
189
|
+
console.log(` ${file.padEnd(COL_NAME)} ${status.padEnd(10)} ${when}`)
|
|
190
|
+
if (isApplied) appliedCount++; else pendingCount++
|
|
191
|
+
}
|
|
107
192
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
193
|
+
// Migraciones aplicadas cuyo archivo ya no existe (huérfanas)
|
|
194
|
+
for (const name of applied) {
|
|
195
|
+
if (!files.includes(name)) {
|
|
196
|
+
console.log(` ${name.padEnd(COL_NAME)} ⚠️ sin archivo (archivo eliminado del repo)`)
|
|
112
197
|
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
console.log(sep)
|
|
201
|
+
console.log(`\n ${appliedCount} aplicada(s) · ${pendingCount} pendiente(s)\n`)
|
|
202
|
+
}
|
|
113
203
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
204
|
+
// ─── Comando principal ────────────────────────────────────────────────────────
|
|
205
|
+
|
|
206
|
+
export async function dbMigrate(action: MigrateAction = 'up'): Promise<void> {
|
|
207
|
+
const basePath = process.cwd()
|
|
208
|
+
const migrationsDir = join(basePath, 'src', 'migrations')
|
|
209
|
+
|
|
210
|
+
if (!existsSync(migrationsDir)) {
|
|
211
|
+
console.error('❌ No se encuentra src/migrations/')
|
|
212
|
+
console.error(' Creá una con: arckode make:migration <nombre>')
|
|
213
|
+
return
|
|
118
214
|
}
|
|
119
215
|
|
|
120
|
-
|
|
216
|
+
const files = (await readdir(migrationsDir))
|
|
217
|
+
.filter(f => f.endsWith('.ts') || f.endsWith('.js'))
|
|
218
|
+
.sort()
|
|
219
|
+
|
|
220
|
+
const env = parseEnv(basePath)
|
|
221
|
+
const db = await createCliDbRunner(env, basePath)
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
// fresh no llama ensureTable antes de dropear (dropeará todo incluyendo la tracking table)
|
|
225
|
+
if (action !== 'fresh') await ensureTable(db)
|
|
226
|
+
|
|
227
|
+
switch (action) {
|
|
228
|
+
case 'up': await runUp(db, files, migrationsDir); break
|
|
229
|
+
case 'down': await runDown(db, migrationsDir); break
|
|
230
|
+
case 'reset': await runReset(db, migrationsDir); break
|
|
231
|
+
case 'fresh': await runFresh(db, files, migrationsDir); break
|
|
232
|
+
case 'refresh': await runRefresh(db, files, migrationsDir); break
|
|
233
|
+
case 'status': {
|
|
234
|
+
if (files.length === 0) { console.log('📭 No hay migraciones en src/migrations/'); break }
|
|
235
|
+
await runStatus(db, files)
|
|
236
|
+
break
|
|
237
|
+
}
|
|
238
|
+
default:
|
|
239
|
+
console.error(`❌ Acción desconocida: "${action}"`)
|
|
240
|
+
console.error(' Opciones: up | down | fresh | reset | refresh | status')
|
|
241
|
+
}
|
|
242
|
+
} finally {
|
|
243
|
+
await db.close()
|
|
244
|
+
}
|
|
121
245
|
}
|
package/cli/commands/db-seed.ts
CHANGED
|
@@ -1,54 +1,119 @@
|
|
|
1
|
-
// cli/commands/db-seed.ts —
|
|
2
|
-
//
|
|
1
|
+
// cli/commands/db-seed.ts — Ejecuta seeds en src/seeds/
|
|
2
|
+
// Conecta a la BD (misma detección que db:migrate) y corre cada seed file.
|
|
3
|
+
// Cada archivo exporta funciones que reciben un proxy del ORM con create().
|
|
4
|
+
// Úsalo para cargar datos de prueba o datos iniciales de producción.
|
|
3
5
|
|
|
4
|
-
import {
|
|
6
|
+
import { readdir } from 'node:fs/promises'
|
|
5
7
|
import { join } from 'node:path'
|
|
6
8
|
import { existsSync } from 'node:fs'
|
|
9
|
+
import { parseEnv, createCliDbRunner, type CliDbRunner } from './db-utils'
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
// Interfaz pública para los archivos de seed auto-generados.
|
|
12
|
+
// Importá este tipo en tus seeds para evitar usar `any`:
|
|
13
|
+
// import type { SeedOrm } from 'arckode-framework/cli/commands/db-seed'
|
|
14
|
+
export interface SeedOrm {
|
|
15
|
+
create(modelName: string, data: Record<string, unknown>): Promise<Record<string, unknown>>
|
|
16
|
+
}
|
|
11
17
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
// Proxy mínimo que imita orm.create() para seeds auto-generados.
|
|
19
|
+
// Infiere el nombre de tabla convirtiendo el model name a lowercase.
|
|
20
|
+
// Para seeds que necesiten JOINs o lógica compleja, importá el composition root.
|
|
21
|
+
function makeSeedProxy(db: CliDbRunner): SeedOrm {
|
|
22
|
+
return {
|
|
23
|
+
async create(modelName: string, data: Record<string, unknown>): Promise<Record<string, unknown>> {
|
|
24
|
+
const table = modelName.toLowerCase()
|
|
25
|
+
const record: Record<string, unknown> = {
|
|
26
|
+
id: (data.id as string | undefined) ?? crypto.randomUUID(),
|
|
27
|
+
...data,
|
|
28
|
+
createdAt: (data.createdAt as string | undefined) ?? new Date().toISOString(),
|
|
29
|
+
updatedAt: new Date().toISOString(),
|
|
30
|
+
}
|
|
31
|
+
const cols = Object.keys(record)
|
|
32
|
+
const vals = Object.values(record)
|
|
33
|
+
const placeholders = cols.map(() => '?').join(', ')
|
|
34
|
+
await db.run(
|
|
35
|
+
`INSERT INTO ${table} (${cols.join(', ')}) VALUES (${placeholders})`,
|
|
36
|
+
vals,
|
|
37
|
+
)
|
|
38
|
+
return record
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function runSeedFile(filePath: string, db: CliDbRunner): Promise<void> {
|
|
44
|
+
const mod = await import(filePath)
|
|
45
|
+
const proxy = makeSeedProxy(db)
|
|
46
|
+
let ran = 0
|
|
47
|
+
|
|
48
|
+
for (const [key, value] of Object.entries(mod)) {
|
|
49
|
+
if (typeof value !== 'function') continue
|
|
50
|
+
// Convención: funciones seed* o run o default
|
|
51
|
+
if (!key.toLowerCase().startsWith('seed') && key !== 'run' && key !== 'default') continue
|
|
52
|
+
|
|
53
|
+
await (value as (orm: typeof proxy) => Promise<void>)(proxy)
|
|
54
|
+
ran++
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (ran === 0) {
|
|
58
|
+
console.warn(` ⚠️ No se encontró ninguna función seed en ${filePath}`)
|
|
59
|
+
console.warn(` Asegurate de exportar una función que empiece con "seed".`)
|
|
16
60
|
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function dbSeed(seedName?: string): Promise<void> {
|
|
64
|
+
const basePath = process.cwd()
|
|
65
|
+
const seedsDir = join(basePath, 'src', 'seeds')
|
|
17
66
|
|
|
18
|
-
|
|
19
|
-
const seedsPath = join(basePath, 'src', 'seeds')
|
|
20
|
-
if (!existsSync(seedsPath)) {
|
|
67
|
+
if (!existsSync(seedsDir)) {
|
|
21
68
|
console.error('❌ No hay directorio src/seeds/')
|
|
22
69
|
console.error(' Creá seeds con: arckode make:seed <Nombre>')
|
|
23
70
|
return
|
|
24
71
|
}
|
|
25
72
|
|
|
26
|
-
const
|
|
27
|
-
|
|
73
|
+
const allFiles = (await readdir(seedsDir))
|
|
74
|
+
.filter(f => f.endsWith('.ts') || f.endsWith('.js'))
|
|
75
|
+
.sort()
|
|
28
76
|
|
|
29
|
-
if (
|
|
77
|
+
if (allFiles.length === 0) {
|
|
30
78
|
console.log('📭 No hay seeds para ejecutar.')
|
|
31
79
|
return
|
|
32
80
|
}
|
|
33
81
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
82
|
+
// Sin argumento: listar + pedir confirmación o ejecutar todos
|
|
83
|
+
if (!seedName) {
|
|
84
|
+
console.log('🌱 Seeds disponibles:')
|
|
85
|
+
for (const f of allFiles) console.log(` - ${f.replace(/\.(ts|js)$/, '')}`)
|
|
86
|
+
console.log('\n Para ejecutar todos: arckode db:seed --all')
|
|
87
|
+
console.log(' Para ejecutar uno: arckode db:seed <nombre>')
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const runAll = seedName === '--all'
|
|
92
|
+
const targets = runAll
|
|
93
|
+
? allFiles
|
|
94
|
+
: allFiles.filter(f => f.startsWith(seedName))
|
|
95
|
+
|
|
96
|
+
if (targets.length === 0) {
|
|
97
|
+
console.error(`❌ Seed "${seedName}" no encontrado.`)
|
|
98
|
+
console.log(` Seeds disponibles: ${allFiles.map(f => f.replace(/\.(ts|js)$/, '')).join(', ')}`)
|
|
44
99
|
return
|
|
45
100
|
}
|
|
46
101
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
102
|
+
const env = parseEnv(basePath)
|
|
103
|
+
const db = await createCliDbRunner(env, basePath)
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
for (const file of targets) {
|
|
107
|
+
const filePath = join(seedsDir, file)
|
|
108
|
+
console.log(`🌱 Ejecutando: ${file}`)
|
|
109
|
+
await runSeedFile(filePath, db)
|
|
110
|
+
console.log(`✅ ${file}`)
|
|
111
|
+
}
|
|
112
|
+
console.log(`\n${targets.length} seed(s) ejecutado(s)`)
|
|
113
|
+
} catch (err) {
|
|
114
|
+
console.error('❌ Error ejecutando seed:', err instanceof Error ? err.message : err)
|
|
115
|
+
throw err
|
|
116
|
+
} finally {
|
|
117
|
+
await db.close()
|
|
50
118
|
}
|
|
51
|
-
console.log('')
|
|
52
|
-
console.log(' Para ejecutar todos: RUN_SEEDS=true bun run src/composition-root.ts')
|
|
53
|
-
console.log(' Para ejecutar uno: bun arckode db:seed <nombre>')
|
|
54
119
|
}
|