@seip/blue-bird 1.1.4 → 1.1.5

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.
@@ -1,342 +1,342 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from "node:fs";
4
- import path from "node:path";
5
- import chalk from "chalk";
6
- import { Database, DB_TYPE } from "../database.js";
7
-
8
- /**
9
- * Generates current timestamp string in YYYYMMDD_HHMMSS format.
10
- * @returns {string}
11
- */
12
- function getTimestamp() {
13
- const now = new Date();
14
- const pad = (n) => String(n).padStart(2, "0");
15
- const year = now.getFullYear();
16
- const month = pad(now.getMonth() + 1);
17
- const day = pad(now.getDate());
18
- const hours = pad(now.getHours());
19
- const mins = pad(now.getMinutes());
20
- const secs = pad(now.getSeconds());
21
- return `${year}${month}${day}_${hours}${mins}${secs}`;
22
- }
23
-
24
- /**
25
- * Creates a new migration file in database/migrations/.
26
- * @param {string} name
27
- */
28
- function makeMigration(name) {
29
- if (!name) {
30
- console.log(chalk.red("[ERROR] Missing migration name."));
31
- console.log("Usage: npx blue-bird make:migration <name>");
32
- process.exit(1);
33
- }
34
-
35
- const cleanName = name.toLowerCase().replace(/[^a-z0-9_]/g, "_");
36
- const filename = `${getTimestamp()}_${cleanName}.sql`;
37
- const migrationsDir = path.resolve(process.cwd(), "database/migrations");
38
-
39
- if (!fs.existsSync(migrationsDir)) {
40
- fs.mkdirSync(migrationsDir, { recursive: true });
41
- }
42
-
43
- const filePath = path.join(migrationsDir, filename);
44
-
45
- const template = `-- =============================================================
46
- -- Migration: ${cleanName}
47
- -- Created At: ${new Date().toISOString()}
48
- -- Driver Compatibility: SQLite / MySQL / PostgreSQL
49
- -- =============================================================
50
-
51
- CREATE TABLE IF NOT EXISTS ${cleanName} (
52
- id INTEGER PRIMARY KEY AUTOINCREMENT,
53
- name VARCHAR(255) NOT NULL,
54
- description TEXT,
55
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
56
- );
57
- `;
58
-
59
- fs.writeFileSync(filePath, template, "utf-8");
60
- console.log(chalk.green(`[OK] Migration created: database/migrations/${filename}`));
61
- }
62
-
63
- /**
64
- * Creates a new seed file in database/seeds/.
65
- * @param {string} name
66
- */
67
- function makeSeed(name) {
68
- if (!name) {
69
- console.log(chalk.red("[ERROR] Missing seed name."));
70
- console.log("Usage: npx blue-bird make:seed <name>");
71
- process.exit(1);
72
- }
73
-
74
- const cleanName = name.toLowerCase().replace(/[^a-z0-9_]/g, "_");
75
- const filename = `${cleanName}.sql`;
76
- const seedsDir = path.resolve(process.cwd(), "database/seeds");
77
-
78
- if (!fs.existsSync(seedsDir)) {
79
- fs.mkdirSync(seedsDir, { recursive: true });
80
- }
81
-
82
- const filePath = path.join(seedsDir, filename);
83
-
84
- const template = `-- =============================================================
85
- -- Seed: ${cleanName}
86
- -- Created At: ${new Date().toISOString()}
87
- -- =============================================================
88
-
89
- -- INSERT INTO table_name (name) VALUES ('Sample Item 1');
90
- `;
91
-
92
- fs.writeFileSync(filePath, template, "utf-8");
93
- console.log(chalk.green(`[OK] Seed file created: database/seeds/${filename}`));
94
- }
95
-
96
- /**
97
- * Ensures migrations tracking table exists.
98
- * @param {Database} db
99
- */
100
- async function ensureMigrationsTable(db) {
101
- let ddl = "";
102
- if (DB_TYPE === "postgres") {
103
- ddl = `CREATE TABLE IF NOT EXISTS _bluebird_migrations (
104
- id SERIAL PRIMARY KEY,
105
- name VARCHAR(255) NOT NULL UNIQUE,
106
- batch INT NOT NULL,
107
- executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
108
- );`;
109
- } else if (DB_TYPE === "mysql") {
110
- ddl = `CREATE TABLE IF NOT EXISTS _bluebird_migrations (
111
- id INT AUTO_INCREMENT PRIMARY KEY,
112
- name VARCHAR(255) NOT NULL UNIQUE,
113
- batch INT NOT NULL,
114
- executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
115
- );`;
116
- } else {
117
- // sqlite default
118
- ddl = `CREATE TABLE IF NOT EXISTS _bluebird_migrations (
119
- id INTEGER PRIMARY KEY AUTOINCREMENT,
120
- name TEXT NOT NULL UNIQUE,
121
- batch INTEGER NOT NULL,
122
- executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
123
- );`;
124
- }
125
-
126
- await db.query(ddl);
127
- }
128
-
129
- /**
130
- * Executes pending database migrations.
131
- */
132
- async function runMigrations() {
133
- const migrationsDir = path.resolve(process.cwd(), "database/migrations");
134
- if (!fs.existsSync(migrationsDir)) {
135
- console.log(chalk.yellow("[INFO] No 'database/migrations' directory found. Nothing to migrate."));
136
- return;
137
- }
138
-
139
- const files = fs
140
- .readdirSync(migrationsDir)
141
- .filter((f) => f.endsWith(".sql") || f.endsWith(".js"))
142
- .sort();
143
-
144
- if (files.length === 0) {
145
- console.log(chalk.yellow("[INFO] No migration files found in 'database/migrations'."));
146
- return;
147
- }
148
-
149
- let db;
150
- try {
151
- db = new Database(5);
152
- await ensureMigrationsTable(db);
153
- } catch (err) {
154
- console.error(chalk.red("[ERROR] Could not connect to database to run migrations:"), err.message);
155
- process.exit(1);
156
- }
157
-
158
- const appliedRows = (await db.query("SELECT name, batch FROM _bluebird_migrations ORDER BY id ASC")) || [];
159
- const appliedSet = new Set(appliedRows.map((r) => r.name));
160
-
161
- const maxBatchRow = await db.query("SELECT MAX(batch) as max_batch FROM _bluebird_migrations", [], "return_row");
162
- const currentBatch = ((maxBatchRow && maxBatchRow.max_batch) || 0) + 1;
163
-
164
- const pending = files.filter((f) => !appliedSet.has(f));
165
-
166
- if (pending.length === 0) {
167
- console.log(chalk.green("[INFO] Database is up to date. No pending migrations."));
168
- process.exit(0);
169
- }
170
-
171
- console.log(chalk.cyan(`[INFO] Running ${pending.length} pending migration(s) (Batch #${currentBatch})...\n`));
172
-
173
- for (const file of pending) {
174
- const filePath = path.join(migrationsDir, file);
175
- try {
176
- if (file.endsWith(".sql")) {
177
- const sql = fs.readFileSync(filePath, "utf-8");
178
- // Split statements by semicolon where appropriate
179
- const statements = sql
180
- .split(/;\s*$/m)
181
- .map((s) => s.trim())
182
- .filter((s) => s.length > 0);
183
-
184
- await db.transaction(async (tx) => {
185
- for (const stmt of statements) {
186
- await tx.query(stmt);
187
- }
188
- await tx.query(
189
- "INSERT INTO _bluebird_migrations (name, batch) VALUES (?, ?)",
190
- [file, currentBatch]
191
- );
192
- });
193
- } else if (file.endsWith(".js")) {
194
- const modulePath = `file://${filePath}`;
195
- const migrationModule = await import(modulePath);
196
- if (typeof migrationModule.up === "function") {
197
- await db.transaction(async (tx) => {
198
- await migrationModule.up(tx);
199
- await tx.query(
200
- "INSERT INTO _bluebird_migrations (name, batch) VALUES (?, ?)",
201
- [file, currentBatch]
202
- );
203
- });
204
- }
205
- }
206
-
207
- console.log(chalk.green(` [MIGRATED] ${file}`));
208
- } catch (err) {
209
- console.error(chalk.red(` [FAILED] ${file}: ${err.message}`));
210
- process.exit(1);
211
- }
212
- }
213
-
214
- console.log(chalk.bold.green("\n[OK] All pending migrations executed successfully."));
215
- process.exit(0);
216
- }
217
-
218
- /**
219
- * Shows migration status list.
220
- */
221
- async function showMigrationStatus() {
222
- const migrationsDir = path.resolve(process.cwd(), "database/migrations");
223
- if (!fs.existsSync(migrationsDir)) {
224
- console.log(chalk.yellow("[INFO] No 'database/migrations' directory found."));
225
- return;
226
- }
227
-
228
- const files = fs
229
- .readdirSync(migrationsDir)
230
- .filter((f) => f.endsWith(".sql") || f.endsWith(".js"))
231
- .sort();
232
-
233
- let db;
234
- try {
235
- db = new Database(5);
236
- await ensureMigrationsTable(db);
237
- } catch (err) {
238
- console.error(chalk.red("[ERROR] Could not connect to database:"), err.message);
239
- process.exit(1);
240
- }
241
-
242
- const appliedRows = (await db.query("SELECT name, batch, executed_at FROM _bluebird_migrations ORDER BY id ASC")) || [];
243
- const appliedMap = new Map(appliedRows.map((r) => [r.name, r]));
244
-
245
- console.log(chalk.bold.cyan("\n============================================================="));
246
- console.log(chalk.bold.cyan(" Database Migrations Status"));
247
- console.log(chalk.bold.cyan("=============================================================\n"));
248
-
249
- for (const file of files) {
250
- if (appliedMap.has(file)) {
251
- const record = appliedMap.get(file);
252
- console.log(` ${chalk.green("[APPLIED]")} ${file.padEnd(40)} (Batch: ${record.batch}, At: ${record.executed_at})`);
253
- } else {
254
- console.log(` ${chalk.yellow("[PENDING]")} ${file}`);
255
- }
256
- }
257
-
258
- console.log("");
259
- process.exit(0);
260
- }
261
-
262
- /**
263
- * Runs seed scripts from database/seeds/.
264
- */
265
- async function runSeeds() {
266
- const seedsDir = path.resolve(process.cwd(), "database/seeds");
267
- if (!fs.existsSync(seedsDir)) {
268
- console.log(chalk.yellow("[INFO] No 'database/seeds' directory found. Nothing to seed."));
269
- return;
270
- }
271
-
272
- const files = fs
273
- .readdirSync(seedsDir)
274
- .filter((f) => f.endsWith(".sql") || f.endsWith(".js"))
275
- .sort();
276
-
277
- if (files.length === 0) {
278
- console.log(chalk.yellow("[INFO] No seed files found in 'database/seeds'."));
279
- return;
280
- }
281
-
282
- let db;
283
- try {
284
- db = new Database(5);
285
- } catch (err) {
286
- console.error(chalk.red("[ERROR] Could not connect to database to run seeds:"), err.message);
287
- process.exit(1);
288
- }
289
-
290
- console.log(chalk.cyan(`[INFO] Running ${files.length} seed file(s)...\n`));
291
-
292
- for (const file of files) {
293
- const filePath = path.join(seedsDir, file);
294
- try {
295
- if (file.endsWith(".sql")) {
296
- const sql = fs.readFileSync(filePath, "utf-8");
297
- const statements = sql
298
- .split(/;\s*$/m)
299
- .map((s) => s.trim())
300
- .filter((s) => s.length > 0);
301
-
302
- await db.transaction(async (tx) => {
303
- for (const stmt of statements) {
304
- await tx.query(stmt);
305
- }
306
- });
307
- } else if (file.endsWith(".js")) {
308
- const modulePath = `file://${filePath}`;
309
- const seedModule = await import(modulePath);
310
- if (typeof seedModule.seed === "function") {
311
- await db.transaction(async (tx) => {
312
- await seedModule.seed(tx);
313
- });
314
- }
315
- }
316
-
317
- console.log(chalk.green(` [SEEDED] ${file}`));
318
- } catch (err) {
319
- console.error(chalk.red(` [FAILED] ${file}: ${err.message}`));
320
- process.exit(1);
321
- }
322
- }
323
-
324
- console.log(chalk.bold.green("\n[OK] Database seeding completed."));
325
- process.exit(0);
326
- }
327
-
328
- // CLI Dispatcher
329
- const rawArgs = process.argv.slice(2);
330
- const cmd = rawArgs[0];
331
-
332
- if (cmd === "make:migration") {
333
- makeMigration(rawArgs[1]);
334
- } else if (cmd === "make:seed") {
335
- makeSeed(rawArgs[1]);
336
- } else if (cmd === "migrate:status") {
337
- showMigrationStatus();
338
- } else if (cmd === "seed") {
339
- runSeeds();
340
- } else {
341
- runMigrations();
342
- }
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import chalk from "chalk";
6
+ import { Database, DB_TYPE } from "../database.js";
7
+
8
+ /**
9
+ * Generates current timestamp string in YYYYMMDD_HHMMSS format.
10
+ * @returns {string}
11
+ */
12
+ function getTimestamp() {
13
+ const now = new Date();
14
+ const pad = (n) => String(n).padStart(2, "0");
15
+ const year = now.getFullYear();
16
+ const month = pad(now.getMonth() + 1);
17
+ const day = pad(now.getDate());
18
+ const hours = pad(now.getHours());
19
+ const mins = pad(now.getMinutes());
20
+ const secs = pad(now.getSeconds());
21
+ return `${year}${month}${day}_${hours}${mins}${secs}`;
22
+ }
23
+
24
+ /**
25
+ * Creates a new migration file in database/migrations/.
26
+ * @param {string} name
27
+ */
28
+ function makeMigration(name) {
29
+ if (!name) {
30
+ console.log(chalk.red("[ERROR] Missing migration name."));
31
+ console.log("Usage: npx blue-bird make:migration <name>");
32
+ process.exit(1);
33
+ }
34
+
35
+ const cleanName = name.toLowerCase().replace(/[^a-z0-9_]/g, "_");
36
+ const filename = `${getTimestamp()}_${cleanName}.sql`;
37
+ const migrationsDir = path.resolve(process.cwd(), "database/migrations");
38
+
39
+ if (!fs.existsSync(migrationsDir)) {
40
+ fs.mkdirSync(migrationsDir, { recursive: true });
41
+ }
42
+
43
+ const filePath = path.join(migrationsDir, filename);
44
+
45
+ const template = `-- =============================================================
46
+ -- Migration: ${cleanName}
47
+ -- Created At: ${new Date().toISOString()}
48
+ -- Driver Compatibility: SQLite / MySQL / PostgreSQL
49
+ -- =============================================================
50
+
51
+ CREATE TABLE IF NOT EXISTS ${cleanName} (
52
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
53
+ name VARCHAR(255) NOT NULL,
54
+ description TEXT,
55
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
56
+ );
57
+ `;
58
+
59
+ fs.writeFileSync(filePath, template, "utf-8");
60
+ console.log(chalk.green(`[OK] Migration created: database/migrations/${filename}`));
61
+ }
62
+
63
+ /**
64
+ * Creates a new seed file in database/seeds/.
65
+ * @param {string} name
66
+ */
67
+ function makeSeed(name) {
68
+ if (!name) {
69
+ console.log(chalk.red("[ERROR] Missing seed name."));
70
+ console.log("Usage: npx blue-bird make:seed <name>");
71
+ process.exit(1);
72
+ }
73
+
74
+ const cleanName = name.toLowerCase().replace(/[^a-z0-9_]/g, "_");
75
+ const filename = `${cleanName}.sql`;
76
+ const seedsDir = path.resolve(process.cwd(), "database/seeds");
77
+
78
+ if (!fs.existsSync(seedsDir)) {
79
+ fs.mkdirSync(seedsDir, { recursive: true });
80
+ }
81
+
82
+ const filePath = path.join(seedsDir, filename);
83
+
84
+ const template = `-- =============================================================
85
+ -- Seed: ${cleanName}
86
+ -- Created At: ${new Date().toISOString()}
87
+ -- =============================================================
88
+
89
+ -- INSERT INTO table_name (name) VALUES ('Sample Item 1');
90
+ `;
91
+
92
+ fs.writeFileSync(filePath, template, "utf-8");
93
+ console.log(chalk.green(`[OK] Seed file created: database/seeds/${filename}`));
94
+ }
95
+
96
+ /**
97
+ * Ensures migrations tracking table exists.
98
+ * @param {Database} db
99
+ */
100
+ async function ensureMigrationsTable(db) {
101
+ let ddl = "";
102
+ if (DB_TYPE === "postgres") {
103
+ ddl = `CREATE TABLE IF NOT EXISTS _bluebird_migrations (
104
+ id SERIAL PRIMARY KEY,
105
+ name VARCHAR(255) NOT NULL UNIQUE,
106
+ batch INT NOT NULL,
107
+ executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
108
+ );`;
109
+ } else if (DB_TYPE === "mysql") {
110
+ ddl = `CREATE TABLE IF NOT EXISTS _bluebird_migrations (
111
+ id INT AUTO_INCREMENT PRIMARY KEY,
112
+ name VARCHAR(255) NOT NULL UNIQUE,
113
+ batch INT NOT NULL,
114
+ executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
115
+ );`;
116
+ } else {
117
+ // sqlite default
118
+ ddl = `CREATE TABLE IF NOT EXISTS _bluebird_migrations (
119
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
120
+ name TEXT NOT NULL UNIQUE,
121
+ batch INTEGER NOT NULL,
122
+ executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
123
+ );`;
124
+ }
125
+
126
+ await db.query(ddl);
127
+ }
128
+
129
+ /**
130
+ * Executes pending database migrations.
131
+ */
132
+ async function runMigrations() {
133
+ const migrationsDir = path.resolve(process.cwd(), "database/migrations");
134
+ if (!fs.existsSync(migrationsDir)) {
135
+ console.log(chalk.yellow("[INFO] No 'database/migrations' directory found. Nothing to migrate."));
136
+ return;
137
+ }
138
+
139
+ const files = fs
140
+ .readdirSync(migrationsDir)
141
+ .filter((f) => f.endsWith(".sql") || f.endsWith(".js"))
142
+ .sort();
143
+
144
+ if (files.length === 0) {
145
+ console.log(chalk.yellow("[INFO] No migration files found in 'database/migrations'."));
146
+ return;
147
+ }
148
+
149
+ let db;
150
+ try {
151
+ db = new Database(5);
152
+ await ensureMigrationsTable(db);
153
+ } catch (err) {
154
+ console.error(chalk.red("[ERROR] Could not connect to database to run migrations:"), err.message);
155
+ process.exit(1);
156
+ }
157
+
158
+ const appliedRows = (await db.query("SELECT name, batch FROM _bluebird_migrations ORDER BY id ASC")) || [];
159
+ const appliedSet = new Set(appliedRows.map((r) => r.name));
160
+
161
+ const maxBatchRow = await db.query("SELECT MAX(batch) as max_batch FROM _bluebird_migrations", [], "return_row");
162
+ const currentBatch = ((maxBatchRow && maxBatchRow.max_batch) || 0) + 1;
163
+
164
+ const pending = files.filter((f) => !appliedSet.has(f));
165
+
166
+ if (pending.length === 0) {
167
+ console.log(chalk.green("[INFO] Database is up to date. No pending migrations."));
168
+ process.exit(0);
169
+ }
170
+
171
+ console.log(chalk.cyan(`[INFO] Running ${pending.length} pending migration(s) (Batch #${currentBatch})...\n`));
172
+
173
+ for (const file of pending) {
174
+ const filePath = path.join(migrationsDir, file);
175
+ try {
176
+ if (file.endsWith(".sql")) {
177
+ const sql = fs.readFileSync(filePath, "utf-8");
178
+ // Split statements by semicolon where appropriate
179
+ const statements = sql
180
+ .split(/;\s*$/m)
181
+ .map((s) => s.trim())
182
+ .filter((s) => s.length > 0);
183
+
184
+ await db.transaction(async (tx) => {
185
+ for (const stmt of statements) {
186
+ await tx.query(stmt);
187
+ }
188
+ await tx.query(
189
+ "INSERT INTO _bluebird_migrations (name, batch) VALUES (?, ?)",
190
+ [file, currentBatch]
191
+ );
192
+ });
193
+ } else if (file.endsWith(".js")) {
194
+ const modulePath = `file://${filePath}`;
195
+ const migrationModule = await import(modulePath);
196
+ if (typeof migrationModule.up === "function") {
197
+ await db.transaction(async (tx) => {
198
+ await migrationModule.up(tx);
199
+ await tx.query(
200
+ "INSERT INTO _bluebird_migrations (name, batch) VALUES (?, ?)",
201
+ [file, currentBatch]
202
+ );
203
+ });
204
+ }
205
+ }
206
+
207
+ console.log(chalk.green(` [MIGRATED] ${file}`));
208
+ } catch (err) {
209
+ console.error(chalk.red(` [FAILED] ${file}: ${err.message}`));
210
+ process.exit(1);
211
+ }
212
+ }
213
+
214
+ console.log(chalk.bold.green("\n[OK] All pending migrations executed successfully."));
215
+ process.exit(0);
216
+ }
217
+
218
+ /**
219
+ * Shows migration status list.
220
+ */
221
+ async function showMigrationStatus() {
222
+ const migrationsDir = path.resolve(process.cwd(), "database/migrations");
223
+ if (!fs.existsSync(migrationsDir)) {
224
+ console.log(chalk.yellow("[INFO] No 'database/migrations' directory found."));
225
+ return;
226
+ }
227
+
228
+ const files = fs
229
+ .readdirSync(migrationsDir)
230
+ .filter((f) => f.endsWith(".sql") || f.endsWith(".js"))
231
+ .sort();
232
+
233
+ let db;
234
+ try {
235
+ db = new Database(5);
236
+ await ensureMigrationsTable(db);
237
+ } catch (err) {
238
+ console.error(chalk.red("[ERROR] Could not connect to database:"), err.message);
239
+ process.exit(1);
240
+ }
241
+
242
+ const appliedRows = (await db.query("SELECT name, batch, executed_at FROM _bluebird_migrations ORDER BY id ASC")) || [];
243
+ const appliedMap = new Map(appliedRows.map((r) => [r.name, r]));
244
+
245
+ console.log(chalk.bold.cyan("\n============================================================="));
246
+ console.log(chalk.bold.cyan(" Database Migrations Status"));
247
+ console.log(chalk.bold.cyan("=============================================================\n"));
248
+
249
+ for (const file of files) {
250
+ if (appliedMap.has(file)) {
251
+ const record = appliedMap.get(file);
252
+ console.log(` ${chalk.green("[APPLIED]")} ${file.padEnd(40)} (Batch: ${record.batch}, At: ${record.executed_at})`);
253
+ } else {
254
+ console.log(` ${chalk.yellow("[PENDING]")} ${file}`);
255
+ }
256
+ }
257
+
258
+ console.log("");
259
+ process.exit(0);
260
+ }
261
+
262
+ /**
263
+ * Runs seed scripts from database/seeds/.
264
+ */
265
+ async function runSeeds() {
266
+ const seedsDir = path.resolve(process.cwd(), "database/seeds");
267
+ if (!fs.existsSync(seedsDir)) {
268
+ console.log(chalk.yellow("[INFO] No 'database/seeds' directory found. Nothing to seed."));
269
+ return;
270
+ }
271
+
272
+ const files = fs
273
+ .readdirSync(seedsDir)
274
+ .filter((f) => f.endsWith(".sql") || f.endsWith(".js"))
275
+ .sort();
276
+
277
+ if (files.length === 0) {
278
+ console.log(chalk.yellow("[INFO] No seed files found in 'database/seeds'."));
279
+ return;
280
+ }
281
+
282
+ let db;
283
+ try {
284
+ db = new Database(5);
285
+ } catch (err) {
286
+ console.error(chalk.red("[ERROR] Could not connect to database to run seeds:"), err.message);
287
+ process.exit(1);
288
+ }
289
+
290
+ console.log(chalk.cyan(`[INFO] Running ${files.length} seed file(s)...\n`));
291
+
292
+ for (const file of files) {
293
+ const filePath = path.join(seedsDir, file);
294
+ try {
295
+ if (file.endsWith(".sql")) {
296
+ const sql = fs.readFileSync(filePath, "utf-8");
297
+ const statements = sql
298
+ .split(/;\s*$/m)
299
+ .map((s) => s.trim())
300
+ .filter((s) => s.length > 0);
301
+
302
+ await db.transaction(async (tx) => {
303
+ for (const stmt of statements) {
304
+ await tx.query(stmt);
305
+ }
306
+ });
307
+ } else if (file.endsWith(".js")) {
308
+ const modulePath = `file://${filePath}`;
309
+ const seedModule = await import(modulePath);
310
+ if (typeof seedModule.seed === "function") {
311
+ await db.transaction(async (tx) => {
312
+ await seedModule.seed(tx);
313
+ });
314
+ }
315
+ }
316
+
317
+ console.log(chalk.green(` [SEEDED] ${file}`));
318
+ } catch (err) {
319
+ console.error(chalk.red(` [FAILED] ${file}: ${err.message}`));
320
+ process.exit(1);
321
+ }
322
+ }
323
+
324
+ console.log(chalk.bold.green("\n[OK] Database seeding completed."));
325
+ process.exit(0);
326
+ }
327
+
328
+ // CLI Dispatcher
329
+ const rawArgs = process.argv.slice(2);
330
+ const cmd = rawArgs[0];
331
+
332
+ if (cmd === "make:migration") {
333
+ makeMigration(rawArgs[1]);
334
+ } else if (cmd === "make:seed") {
335
+ makeSeed(rawArgs[1]);
336
+ } else if (cmd === "migrate:status") {
337
+ showMigrationStatus();
338
+ } else if (cmd === "seed") {
339
+ runSeeds();
340
+ } else {
341
+ runMigrations();
342
+ }