outlet-orm 5.0.0 → 5.5.2

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/bin/migrate.js CHANGED
@@ -1,440 +1,544 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * outlet-migrate CLI
5
- * Migration management tool for outlet-orm
6
- */
7
-
8
- const readline = require('readline');
9
- const fs = require('fs').promises;
10
- const path = require('path');
11
-
12
- const rl = readline.createInterface({
13
- input: process.stdin,
14
- output: process.stdout
15
- });
16
-
17
- function question(query) {
18
- return new Promise(resolve => rl.question(query, resolve));
19
- }
20
-
21
- async function main() {
22
- console.log('\n╔═══════════════════════════════════════╗');
23
- console.log('║ Outlet ORM - Migration Manager ║');
24
- console.log('╚═══════════════════════════════════════╝\n');
25
-
26
- const command = process.argv[2];
27
-
28
- if (command === 'make') {
29
- await makeMigration();
30
- rl.close();
31
- return;
32
- }
33
-
34
- // Support non-interactive commands for automation and CI
35
- const nonInteractive = new Set(['migrate', 'up', 'rollback', 'reset', 'refresh', 'fresh', 'status']);
36
- if (nonInteractive.has(command)) {
37
- const flags = parseFlags(process.argv.slice(3));
38
- await runNonInteractive(command, flags);
39
- rl.close();
40
- return;
41
- }
42
-
43
- // Fallback to interactive menu
44
- await runMigrationCommands();
45
-
46
- rl.close();
47
- }
48
-
49
- /**
50
- * Create a new migration file
51
- */
52
- async function makeMigration() {
53
- const migrationName = process.argv[3];
54
-
55
- if (!migrationName) {
56
- console.error('✗ Error: Migration name is required');
57
- console.log('Usage: outlet-migrate make <migration_name>');
58
- console.log('Example: outlet-migrate make create_users_table');
59
- return;
60
- }
61
-
62
- const migrationsDir = path.join(process.cwd(), 'database', 'migrations');
63
-
64
- // Create migrations directory if it doesn't exist
65
- try {
66
- await fs.mkdir(migrationsDir, { recursive: true });
67
- } catch (error) {
68
- // Directory already exists - ignore error as recursive: true handles this
69
- if (error.code !== 'EEXIST') {
70
- throw error;
71
- }
72
- }
73
-
74
- // Generate timestamp
75
- const timestamp = new Date().toISOString()
76
- .replace(/[-:]/g, '')
77
- .replace(/T/, '_')
78
- .replace(/\..+/, '');
79
-
80
- const fileName = `${timestamp}_${migrationName}.js`;
81
- const filePath = path.join(migrationsDir, fileName);
82
-
83
- // Determine if it's a create or alter migration
84
- const isCreate = migrationName.includes('create_');
85
- const tableName = extractTableName(migrationName);
86
-
87
- const template = isCreate
88
- ? getCreateMigrationTemplate(tableName)
89
- : getAlterMigrationTemplate(tableName);
90
-
91
- await fs.writeFile(filePath, template);
92
-
93
- console.log(`✓ Migration created: ${fileName}`);
94
- console.log(` Location: ${filePath}`);
95
- }
96
-
97
- /**
98
- * Extract table name from migration name
99
- */
100
- function extractTableName(migrationName) {
101
- // Extract table name from patterns like:
102
- // create_users_table -> users
103
- // add_email_to_users_table -> users
104
- // alter_users_table -> users
105
-
106
- const patterns = [
107
- /create_(\w+)_table/,
108
- /to_(\w+)_table/,
109
- /alter_(\w+)_table/,
110
- /(\w+)_table/
111
- ];
112
-
113
- for (const pattern of patterns) {
114
- const match = migrationName.match(pattern);
115
- if (match) {
116
- return match[1];
117
- }
118
- }
119
-
120
- return 'table_name';
121
- }
122
-
123
- /**
124
- * Get migration template for creating a table
125
- */
126
- function getCreateMigrationTemplate(tableName) {
127
- return `/**
128
- * Migration: Create ${tableName} table
129
- */
130
-
131
- const { Migration } = require('outlet-orm');
132
-
133
- class Create${capitalize(tableName)}Table extends Migration {
134
- /**
135
- * Run the migrations
136
- */
137
- async up() {
138
- const schema = this.getSchema();
139
-
140
- await schema.create('${tableName}', (table) => {
141
- table.id();
142
- table.string('name');
143
- table.timestamps();
144
- });
145
- }
146
-
147
- /**
148
- * Reverse the migrations
149
- */
150
- async down() {
151
- const schema = this.getSchema();
152
- await schema.dropIfExists('${tableName}');
153
- }
154
- }
155
-
156
- module.exports = Create${capitalize(tableName)}Table;
157
- `;
158
- }
159
-
160
- /**
161
- * Get migration template for altering a table
162
- */
163
- function getAlterMigrationTemplate(tableName) {
164
- return `/**
165
- * Migration: Alter ${tableName} table
166
- */
167
-
168
- const { Migration } = require('outlet-orm');
169
-
170
- class Alter${capitalize(tableName)}Table extends Migration {
171
- /**
172
- * Run the migrations
173
- */
174
- async up() {
175
- const schema = this.getSchema();
176
-
177
- await schema.table('${tableName}', (table) => {
178
- // Add your column modifications here
179
- // table.string('new_column');
180
- });
181
- }
182
-
183
- /**
184
- * Reverse the migrations
185
- */
186
- async down() {
187
- const schema = this.getSchema();
188
-
189
- await schema.table('${tableName}', (table) => {
190
- // Reverse your column modifications here
191
- // table.dropColumn('new_column');
192
- });
193
- }
194
- }
195
-
196
- module.exports = Alter${capitalize(tableName)}Table;
197
- `;
198
- }
199
-
200
- /**
201
- * Capitalize first letter
202
- */
203
- function capitalize(str) {
204
- return str.charAt(0).toUpperCase() + str.slice(1);
205
- }
206
-
207
- /**
208
- * Simple flag parser for CLI args
209
- * Supports formats:
210
- * --key=value, --key value, -k value, and boolean flags like --yes/-y
211
- */
212
- function parseFlags(argv) {
213
- const text = ` ${argv.join(' ')} `;
214
- const flags = {};
215
- // Booleans
216
- if (/(^|\s)(--yes|-y)(\s|$)/.test(text)) flags.yes = true;
217
- if (/(^|\s)(--force|-f)(\s|$)/.test(text)) flags.force = true;
218
- // Steps with value: supports "--steps N", "--steps=N", "-s N"
219
- const stepsRe = /(?:--steps(?:=|\s+)|-s\s+)(\S+)/;
220
- const stepsMatch = stepsRe.exec(text);
221
- if (stepsMatch) flags.steps = coerce(stepsMatch[1]);
222
- return flags;
223
- }
224
-
225
- function coerce(val) {
226
- if (val === 'true') return true;
227
- if (val === 'false') return false;
228
- const n = Number(val);
229
- return Number.isNaN(n) ? val : n;
230
- }
231
-
232
- /**
233
- * Run migration commands non-interactively
234
- */
235
- async function runNonInteractive(cmd, flags) {
236
- // Load database configuration
237
- const dbConfigPath = path.join(process.cwd(), 'database', 'config.js');
238
-
239
- // Prefer database/config.js; if missing, allow env-based config via .env
240
- let dbConfig;
241
- try {
242
- dbConfig = require(dbConfigPath);
243
- } catch (error) {
244
- // Fallback to env-based configuration
245
- require('dotenv').config();
246
- const env = process.env || {};
247
- dbConfig = {
248
- driver: env.DB_DRIVER || env.DATABASE_DRIVER,
249
- host: env.DB_HOST,
250
- port: env.DB_PORT ? Number(env.DB_PORT) : undefined,
251
- user: env.DB_USER || env.DB_USERNAME,
252
- password: env.DB_PASSWORD,
253
- database: env.DB_DATABASE || env.DB_NAME || env.DB_FILE || env.SQLITE_DB || env.SQLITE_FILENAME
254
- };
255
- if (!dbConfig.driver) {
256
- console.error('\n✗ Error: Could not load database configuration');
257
- console.error(` Make sure ${dbConfigPath} exists OR provide .env variables like DB_DRIVER, DB_HOST, DB_DATABASE`);
258
- console.error(' Run "outlet-init" to create the configuration');
259
- console.error(` Details: ${error.message}`);
260
- return;
261
- }
262
- }
263
-
264
- const { DatabaseConnection, MigrationManager } = require('../src');
265
-
266
- const connection = new DatabaseConnection(dbConfig);
267
- await connection.connect();
268
-
269
- const manager = new MigrationManager(connection);
270
-
271
- try {
272
- switch (cmd) {
273
- case 'migrate':
274
- case 'up':
275
- await manager.run();
276
- break;
277
-
278
- case 'rollback': {
279
- const steps = Number(flags.steps) || 1;
280
- await manager.rollback(steps);
281
- break;
282
- }
283
-
284
- case 'reset': {
285
- if (flags.yes || flags.force) {
286
- await manager.reset();
287
- } else {
288
- console.error('✗ Refused to reset without --yes');
289
- }
290
- break;
291
- }
292
-
293
- case 'refresh': {
294
- if (flags.yes || flags.force) {
295
- await manager.refresh();
296
- } else {
297
- console.error('✗ Refused to refresh without --yes');
298
- }
299
- break;
300
- }
301
-
302
- case 'fresh': {
303
- if (flags.yes || flags.force) {
304
- await manager.fresh();
305
- } else {
306
- console.error('✗ Refused to fresh without --yes');
307
- }
308
- break;
309
- }
310
-
311
- case 'status':
312
- await manager.status();
313
- break;
314
-
315
- default:
316
- console.error(`✗ Unknown command: ${cmd}`);
317
- }
318
- } catch (error) {
319
- console.error('\n✗ Migration error:', error.message);
320
- console.error(error.stack);
321
- }
322
-
323
- await connection.disconnect();
324
- }
325
-
326
- /**
327
- * Run migration commands (migrate, rollback, etc.)
328
- */
329
- async function runMigrationCommands() {
330
- console.log('Select a migration command:\n');
331
- console.log('1. migrate - Run all pending migrations');
332
- console.log('2. rollback - Rollback the last batch of migrations');
333
- console.log('3. reset - Rollback all migrations');
334
- console.log('4. refresh - Reset and re-run all migrations');
335
- console.log('5. fresh - Drop all tables and re-run migrations');
336
- console.log('6. status - Show migration status');
337
- console.log('0. Exit\n');
338
-
339
- const choice = await question('Enter your choice: ');
340
-
341
- if (choice === '0') {
342
- console.log('Goodbye!');
343
- return;
344
- }
345
-
346
- // Load database configuration
347
- const dbConfigPath = path.join(process.cwd(), 'database', 'config.js');
348
-
349
- let dbConfig;
350
- try {
351
- dbConfig = require(dbConfigPath);
352
- } catch (error) {
353
- require('dotenv').config();
354
- const env = process.env || {};
355
- dbConfig = {
356
- driver: env.DB_DRIVER || env.DATABASE_DRIVER,
357
- host: env.DB_HOST,
358
- port: env.DB_PORT ? Number(env.DB_PORT) : undefined,
359
- user: env.DB_USER || env.DB_USERNAME,
360
- password: env.DB_PASSWORD,
361
- database: env.DB_DATABASE || env.DB_NAME || env.DB_FILE || env.SQLITE_DB || env.SQLITE_FILENAME
362
- };
363
- if (!dbConfig.driver) {
364
- console.error('\n✗ Error: Could not load database configuration');
365
- console.error(` Make sure ${dbConfigPath} exists OR provide .env variables like DB_DRIVER, DB_HOST, DB_DATABASE`);
366
- console.error(' Run "outlet-init" to create the configuration');
367
- console.error(` Details: ${error.message}`);
368
- return;
369
- }
370
- }
371
-
372
- const { DatabaseConnection, MigrationManager } = require('../src');
373
-
374
- const connection = new DatabaseConnection(dbConfig);
375
- await connection.connect();
376
-
377
- const manager = new MigrationManager(connection);
378
-
379
- try {
380
- switch (choice) {
381
- case '1':
382
- await manager.run();
383
- break;
384
-
385
- case '2': {
386
- const steps = await question('How many batches to rollback? (default: 1): ');
387
- await manager.rollback(parseInt(steps) || 1);
388
- break;
389
- }
390
-
391
- case '3': {
392
- const confirmReset = await question('Are you sure you want to reset all migrations? (yes/no): ');
393
- if (confirmReset.toLowerCase() === 'yes') {
394
- await manager.reset();
395
- } else {
396
- console.log('Reset cancelled');
397
- }
398
- break;
399
- }
400
-
401
- case '4': {
402
- const confirmRefresh = await question('Are you sure you want to refresh all migrations? (yes/no): ');
403
- if (confirmRefresh.toLowerCase() === 'yes') {
404
- await manager.refresh();
405
- } else {
406
- console.log('Refresh cancelled');
407
- }
408
- break;
409
- }
410
-
411
- case '5': {
412
- const confirmFresh = await question('⚠️ WARNING: This will DROP ALL TABLES! Continue? (yes/no): ');
413
- if (confirmFresh.toLowerCase() === 'yes') {
414
- await manager.fresh();
415
- } else {
416
- console.log('Fresh cancelled');
417
- }
418
- break;
419
- }
420
-
421
- case '6':
422
- await manager.status();
423
- break;
424
-
425
- default:
426
- console.log('Invalid choice');
427
- }
428
- } catch (error) {
429
- console.error('\n✗ Migration error:', error.message);
430
- console.error(error.stack);
431
- }
432
-
433
- await connection.disconnect();
434
- }
435
-
436
- // Run the CLI
437
- main().catch(error => {
438
- console.error('Fatal error:', error);
439
- process.exit(1);
440
- });
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * outlet-migrate CLI
5
+ * Migration management tool for outlet-orm
6
+ */
7
+
8
+ const readline = require('readline');
9
+ const fs = require('fs').promises;
10
+ const path = require('path');
11
+
12
+ const rl = readline.createInterface({
13
+ input: process.stdin,
14
+ output: process.stdout
15
+ });
16
+
17
+ function question(query) {
18
+ return new Promise(resolve => rl.question(query, resolve));
19
+ }
20
+
21
+ async function main() {
22
+ console.log('\n╔═══════════════════════════════════════╗');
23
+ console.log('║ Outlet ORM - Migration Manager ║');
24
+ console.log('╚═══════════════════════════════════════╝\n');
25
+
26
+ const command = process.argv[2];
27
+
28
+ if (command === 'make') {
29
+ await makeMigration();
30
+ rl.close();
31
+ return;
32
+ }
33
+
34
+ if (command === 'make:seed' || command === 'seed:make') {
35
+ await makeSeeder();
36
+ rl.close();
37
+ return;
38
+ }
39
+
40
+ // Support non-interactive commands for automation and CI
41
+ const nonInteractive = new Set(['migrate', 'up', 'rollback', 'reset', 'refresh', 'fresh', 'status', 'seed', 'db:seed']);
42
+ if (nonInteractive.has(command)) {
43
+ const flags = parseFlags(process.argv.slice(3));
44
+ await runNonInteractive(command, flags);
45
+ rl.close();
46
+ return;
47
+ }
48
+
49
+ // Fallback to interactive menu
50
+ await runMigrationCommands();
51
+
52
+ rl.close();
53
+ }
54
+
55
+ /**
56
+ * Create a new migration file
57
+ */
58
+ async function makeMigration() {
59
+ const migrationName = process.argv[3];
60
+
61
+ if (!migrationName) {
62
+ console.error('✗ Error: Migration name is required');
63
+ console.log('Usage: outlet-migrate make <migration_name>');
64
+ console.log('Example: outlet-migrate make create_users_table');
65
+ return;
66
+ }
67
+
68
+ const migrationsDir = path.join(process.cwd(), 'database', 'migrations');
69
+
70
+ // Create migrations directory if it doesn't exist
71
+ try {
72
+ await fs.mkdir(migrationsDir, { recursive: true });
73
+ } catch (error) {
74
+ // Directory already exists - ignore error as recursive: true handles this
75
+ if (error.code !== 'EEXIST') {
76
+ throw error;
77
+ }
78
+ }
79
+
80
+ // Generate timestamp
81
+ const timestamp = new Date().toISOString()
82
+ .replace(/[-:]/g, '')
83
+ .replace(/T/, '_')
84
+ .replace(/\..+/, '');
85
+
86
+ const fileName = `${timestamp}_${migrationName}.js`;
87
+ const filePath = path.join(migrationsDir, fileName);
88
+
89
+ // Determine if it's a create or alter migration
90
+ const isCreate = migrationName.includes('create_');
91
+ const tableName = extractTableName(migrationName);
92
+
93
+ const template = isCreate
94
+ ? getCreateMigrationTemplate(tableName)
95
+ : getAlterMigrationTemplate(tableName);
96
+
97
+ await fs.writeFile(filePath, template);
98
+
99
+ console.log(`✓ Migration created: ${fileName}`);
100
+ console.log(` Location: ${filePath}`);
101
+ }
102
+
103
+ /**
104
+ * Create a new seeder file
105
+ */
106
+ async function makeSeeder() {
107
+ const seederName = process.argv[3];
108
+
109
+ if (!seederName) {
110
+ console.error('✗ Error: Seeder name is required');
111
+ console.log('Usage: outlet-migrate make:seed <seeder_name>');
112
+ console.log('Example: outlet-migrate make:seed UserSeeder');
113
+ return;
114
+ }
115
+
116
+ const seedsDir = path.join(process.cwd(), 'database', 'seeds');
117
+
118
+ try {
119
+ await fs.mkdir(seedsDir, { recursive: true });
120
+ } catch (error) {
121
+ if (error.code !== 'EEXIST') {
122
+ throw error;
123
+ }
124
+ }
125
+
126
+ const className = toSeederClassName(seederName);
127
+ const fileName = `${className}.js`;
128
+ const filePath = path.join(seedsDir, fileName);
129
+
130
+ const template = getSeederTemplate(className);
131
+ await fs.writeFile(filePath, template);
132
+
133
+ console.log(`✓ Seeder created: ${fileName}`);
134
+ console.log(` Location: ${filePath}`);
135
+ }
136
+
137
+ /**
138
+ * Extract table name from migration name
139
+ */
140
+ function extractTableName(migrationName) {
141
+ // Extract table name from patterns like:
142
+ // create_users_table -> users
143
+ // add_email_to_users_table -> users
144
+ // alter_users_table -> users
145
+
146
+ const patterns = [
147
+ /create_(\w+)_table/,
148
+ /to_(\w+)_table/,
149
+ /alter_(\w+)_table/,
150
+ /(\w+)_table/
151
+ ];
152
+
153
+ for (const pattern of patterns) {
154
+ const match = migrationName.match(pattern);
155
+ if (match) {
156
+ return match[1];
157
+ }
158
+ }
159
+
160
+ return 'table_name';
161
+ }
162
+
163
+ /**
164
+ * Get migration template for creating a table
165
+ */
166
+ function getCreateMigrationTemplate(tableName) {
167
+ return `/**
168
+ * Migration: Create ${tableName} table
169
+ */
170
+
171
+ const { Migration } = require('outlet-orm');
172
+
173
+ class Create${capitalize(tableName)}Table extends Migration {
174
+ /**
175
+ * Run the migrations
176
+ */
177
+ async up() {
178
+ const schema = this.getSchema();
179
+
180
+ await schema.create('${tableName}', (table) => {
181
+ table.id();
182
+ table.string('name');
183
+ table.timestamps();
184
+ });
185
+ }
186
+
187
+ /**
188
+ * Reverse the migrations
189
+ */
190
+ async down() {
191
+ const schema = this.getSchema();
192
+ await schema.dropIfExists('${tableName}');
193
+ }
194
+ }
195
+
196
+ module.exports = Create${capitalize(tableName)}Table;
197
+ `;
198
+ }
199
+
200
+ /**
201
+ * Get migration template for altering a table
202
+ */
203
+ function getAlterMigrationTemplate(tableName) {
204
+ return `/**
205
+ * Migration: Alter ${tableName} table
206
+ */
207
+
208
+ const { Migration } = require('outlet-orm');
209
+
210
+ class Alter${capitalize(tableName)}Table extends Migration {
211
+ /**
212
+ * Run the migrations
213
+ */
214
+ async up() {
215
+ const schema = this.getSchema();
216
+
217
+ await schema.table('${tableName}', (table) => {
218
+ // Add your column modifications here
219
+ // table.string('new_column');
220
+ });
221
+ }
222
+
223
+ /**
224
+ * Reverse the migrations
225
+ */
226
+ async down() {
227
+ const schema = this.getSchema();
228
+
229
+ await schema.table('${tableName}', (table) => {
230
+ // Reverse your column modifications here
231
+ // table.dropColumn('new_column');
232
+ });
233
+ }
234
+ }
235
+
236
+ module.exports = Alter${capitalize(tableName)}Table;
237
+ `;
238
+ }
239
+
240
+ /**
241
+ * Capitalize first letter
242
+ */
243
+ function capitalize(str) {
244
+ return str.charAt(0).toUpperCase() + str.slice(1);
245
+ }
246
+
247
+ function toSeederClassName(name) {
248
+ const cleaned = String(name)
249
+ .replace(/\.js$/i, '')
250
+ .replace(/[^a-zA-Z0-9]+/g, ' ')
251
+ .trim();
252
+
253
+ const pascal = cleaned
254
+ .split(/\s+/)
255
+ .filter(Boolean)
256
+ .map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
257
+ .join('');
258
+
259
+ if (!pascal) {
260
+ throw new Error('Invalid seeder name');
261
+ }
262
+
263
+ return pascal.endsWith('Seeder') ? pascal : `${pascal}Seeder`;
264
+ }
265
+
266
+ function getSeederTemplate(className) {
267
+ return `/**
268
+ * Seeder: ${className}
269
+ */
270
+
271
+ const { Seeder } = require('outlet-orm');
272
+
273
+ class ${className} extends Seeder {
274
+ /**
275
+ * Run the seeder
276
+ */
277
+ async run() {
278
+ await this.insert('table_name', [
279
+ // { name: 'Example' }
280
+ ]);
281
+ }
282
+ }
283
+
284
+ module.exports = ${className};
285
+ `;
286
+ }
287
+
288
+ /**
289
+ * Simple flag parser for CLI args
290
+ * Supports formats:
291
+ * --key=value, --key value, -k value, and boolean flags like --yes/-y
292
+ */
293
+ function parseFlags(argv) {
294
+ const text = ` ${argv.join(' ')} `;
295
+ const flags = {};
296
+ // Booleans
297
+ if (/(^|\s)(--yes|-y)(\s|$)/.test(text)) flags.yes = true;
298
+ if (/(^|\s)(--force|-f)(\s|$)/.test(text)) flags.force = true;
299
+ // Steps with value: supports "--steps N", "--steps=N", "-s N"
300
+ const stepsRe = /(?:--steps(?:=|\s+)|-s\s+)(\S+)/;
301
+ const stepsMatch = stepsRe.exec(text);
302
+ if (stepsMatch) flags.steps = coerce(stepsMatch[1]);
303
+ // Seeder target: --class Name, --class=Name, -c Name
304
+ const classRe = /(?:--class(?:=|\s+)|-c\s+)(\S+)/;
305
+ const classMatch = classRe.exec(text);
306
+ if (classMatch) flags.class = classMatch[1];
307
+ return flags;
308
+ }
309
+
310
+ function coerce(val) {
311
+ if (val === 'true') return true;
312
+ if (val === 'false') return false;
313
+ const n = Number(val);
314
+ return Number.isNaN(n) ? val : n;
315
+ }
316
+
317
+ /**
318
+ * Run migration commands non-interactively
319
+ */
320
+ async function runNonInteractive(cmd, flags) {
321
+ // Load database configuration
322
+ const dbConfigPath = path.join(process.cwd(), 'database', 'config.js');
323
+
324
+ // Prefer database/config.js; if missing, allow env-based config via .env
325
+ let dbConfig;
326
+ try {
327
+ dbConfig = require(dbConfigPath);
328
+ } catch (error) {
329
+ // Fallback to env-based configuration
330
+ require('dotenv').config();
331
+ const env = process.env || {};
332
+ dbConfig = {
333
+ driver: env.DB_DRIVER || env.DATABASE_DRIVER,
334
+ host: env.DB_HOST,
335
+ port: env.DB_PORT ? Number(env.DB_PORT) : undefined,
336
+ user: env.DB_USER || env.DB_USERNAME,
337
+ password: env.DB_PASSWORD,
338
+ database: env.DB_DATABASE || env.DB_NAME || env.DB_FILE || env.SQLITE_DB || env.SQLITE_FILENAME
339
+ };
340
+ if (!dbConfig.driver) {
341
+ console.error('\n✗ Error: Could not load database configuration');
342
+ console.error(` Make sure ${dbConfigPath} exists OR provide .env variables like DB_DRIVER, DB_HOST, DB_DATABASE`);
343
+ console.error(' Run "outlet-init" to create the configuration');
344
+ console.error(` Details: ${error.message}`);
345
+ return;
346
+ }
347
+ }
348
+
349
+ const { DatabaseConnection, MigrationManager, SeederManager } = require('../src');
350
+
351
+ const connection = new DatabaseConnection(dbConfig);
352
+ await connection.connect();
353
+
354
+ const manager = new MigrationManager(connection);
355
+
356
+ try {
357
+ switch (cmd) {
358
+ case 'migrate':
359
+ case 'up':
360
+ await manager.run();
361
+ break;
362
+
363
+ case 'rollback': {
364
+ const steps = Number(flags.steps) || 1;
365
+ await manager.rollback(steps);
366
+ break;
367
+ }
368
+
369
+ case 'reset': {
370
+ if (flags.yes || flags.force) {
371
+ await manager.reset();
372
+ } else {
373
+ console.error('✗ Refused to reset without --yes');
374
+ }
375
+ break;
376
+ }
377
+
378
+ case 'refresh': {
379
+ if (flags.yes || flags.force) {
380
+ await manager.refresh();
381
+ } else {
382
+ console.error('✗ Refused to refresh without --yes');
383
+ }
384
+ break;
385
+ }
386
+
387
+ case 'fresh': {
388
+ if (flags.yes || flags.force) {
389
+ await manager.fresh();
390
+ } else {
391
+ console.error('✗ Refused to fresh without --yes');
392
+ }
393
+ break;
394
+ }
395
+
396
+ case 'status':
397
+ await manager.status();
398
+ break;
399
+
400
+ case 'seed':
401
+ case 'db:seed': {
402
+ const seederManager = new SeederManager(connection);
403
+ await seederManager.run(flags.class || null);
404
+ break;
405
+ }
406
+
407
+ default:
408
+ console.error(`✗ Unknown command: ${cmd}`);
409
+ }
410
+ } catch (error) {
411
+ console.error('\n✗ Migration error:', error.message);
412
+ console.error(error.stack);
413
+ }
414
+
415
+ await connection.disconnect();
416
+ }
417
+
418
+ /**
419
+ * Run migration commands (migrate, rollback, etc.)
420
+ */
421
+ async function runMigrationCommands() {
422
+ console.log('Select a migration command:\n');
423
+ console.log('1. migrate - Run all pending migrations');
424
+ console.log('2. rollback - Rollback the last batch of migrations');
425
+ console.log('3. reset - Rollback all migrations');
426
+ console.log('4. refresh - Reset and re-run all migrations');
427
+ console.log('5. fresh - Drop all tables and re-run migrations');
428
+ console.log('6. status - Show migration status');
429
+ console.log('7. seed - Run seeders from database/seeds');
430
+ console.log('8. make:seed - Create a new seeder file');
431
+ console.log('0. Exit\n');
432
+
433
+ const choice = await question('Enter your choice: ');
434
+
435
+ if (choice === '0') {
436
+ console.log('Goodbye!');
437
+ return;
438
+ }
439
+
440
+ // Load database configuration
441
+ const dbConfigPath = path.join(process.cwd(), 'database', 'config.js');
442
+
443
+ let dbConfig;
444
+ try {
445
+ dbConfig = require(dbConfigPath);
446
+ } catch (error) {
447
+ require('dotenv').config();
448
+ const env = process.env || {};
449
+ dbConfig = {
450
+ driver: env.DB_DRIVER || env.DATABASE_DRIVER,
451
+ host: env.DB_HOST,
452
+ port: env.DB_PORT ? Number(env.DB_PORT) : undefined,
453
+ user: env.DB_USER || env.DB_USERNAME,
454
+ password: env.DB_PASSWORD,
455
+ database: env.DB_DATABASE || env.DB_NAME || env.DB_FILE || env.SQLITE_DB || env.SQLITE_FILENAME
456
+ };
457
+ if (!dbConfig.driver) {
458
+ console.error('\n✗ Error: Could not load database configuration');
459
+ console.error(` Make sure ${dbConfigPath} exists OR provide .env variables like DB_DRIVER, DB_HOST, DB_DATABASE`);
460
+ console.error(' Run "outlet-init" to create the configuration');
461
+ console.error(` Details: ${error.message}`);
462
+ return;
463
+ }
464
+ }
465
+
466
+ const { DatabaseConnection, MigrationManager, SeederManager } = require('../src');
467
+
468
+ const connection = new DatabaseConnection(dbConfig);
469
+ await connection.connect();
470
+
471
+ const manager = new MigrationManager(connection);
472
+
473
+ try {
474
+ switch (choice) {
475
+ case '1':
476
+ await manager.run();
477
+ break;
478
+
479
+ case '2': {
480
+ const steps = await question('How many batches to rollback? (default: 1): ');
481
+ await manager.rollback(parseInt(steps) || 1);
482
+ break;
483
+ }
484
+
485
+ case '3': {
486
+ const confirmReset = await question('Are you sure you want to reset all migrations? (yes/no): ');
487
+ if (confirmReset.toLowerCase() === 'yes') {
488
+ await manager.reset();
489
+ } else {
490
+ console.log('Reset cancelled');
491
+ }
492
+ break;
493
+ }
494
+
495
+ case '4': {
496
+ const confirmRefresh = await question('Are you sure you want to refresh all migrations? (yes/no): ');
497
+ if (confirmRefresh.toLowerCase() === 'yes') {
498
+ await manager.refresh();
499
+ } else {
500
+ console.log('Refresh cancelled');
501
+ }
502
+ break;
503
+ }
504
+
505
+ case '5': {
506
+ const confirmFresh = await question('⚠️ WARNING: This will DROP ALL TABLES! Continue? (yes/no): ');
507
+ if (confirmFresh.toLowerCase() === 'yes') {
508
+ await manager.fresh();
509
+ } else {
510
+ console.log('Fresh cancelled');
511
+ }
512
+ break;
513
+ }
514
+
515
+ case '6':
516
+ await manager.status();
517
+ break;
518
+
519
+ case '7': {
520
+ const seederManager = new SeederManager(connection);
521
+ await seederManager.run();
522
+ break;
523
+ }
524
+
525
+ case '8':
526
+ await makeSeeder();
527
+ break;
528
+
529
+ default:
530
+ console.log('Invalid choice');
531
+ }
532
+ } catch (error) {
533
+ console.error('\n✗ Migration error:', error.message);
534
+ console.error(error.stack);
535
+ }
536
+
537
+ await connection.disconnect();
538
+ }
539
+
540
+ // Run the CLI
541
+ main().catch(error => {
542
+ console.error('Fatal error:', error);
543
+ process.exit(1);
544
+ });