outlet-orm 4.2.0 → 5.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/bin/migrate.js CHANGED
@@ -1,442 +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
- // 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('../../lib/Migrations/Migration');
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('../../lib/Migrations/Migration');
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 } = require('../lib/Database/DatabaseConnection');
265
- const MigrationManager = require('../lib/Migrations/MigrationManager');
266
-
267
- const connection = new DatabaseConnection(dbConfig);
268
- await connection.connect();
269
-
270
- const manager = new MigrationManager(connection);
271
-
272
- try {
273
- switch (cmd) {
274
- case 'migrate':
275
- case 'up':
276
- await manager.run();
277
- break;
278
-
279
- case 'rollback': {
280
- const steps = Number(flags.steps) || 1;
281
- await manager.rollback(steps);
282
- break;
283
- }
284
-
285
- case 'reset': {
286
- if (flags.yes || flags.force) {
287
- await manager.reset();
288
- } else {
289
- console.error('✗ Refused to reset without --yes');
290
- }
291
- break;
292
- }
293
-
294
- case 'refresh': {
295
- if (flags.yes || flags.force) {
296
- await manager.refresh();
297
- } else {
298
- console.error('✗ Refused to refresh without --yes');
299
- }
300
- break;
301
- }
302
-
303
- case 'fresh': {
304
- if (flags.yes || flags.force) {
305
- await manager.fresh();
306
- } else {
307
- console.error('✗ Refused to fresh without --yes');
308
- }
309
- break;
310
- }
311
-
312
- case 'status':
313
- await manager.status();
314
- break;
315
-
316
- default:
317
- console.error(`✗ Unknown command: ${cmd}`);
318
- }
319
- } catch (error) {
320
- console.error('\n✗ Migration error:', error.message);
321
- console.error(error.stack);
322
- }
323
-
324
- await connection.disconnect();
325
- }
326
-
327
- /**
328
- * Run migration commands (migrate, rollback, etc.)
329
- */
330
- async function runMigrationCommands() {
331
- console.log('Select a migration command:\n');
332
- console.log('1. migrate - Run all pending migrations');
333
- console.log('2. rollback - Rollback the last batch of migrations');
334
- console.log('3. reset - Rollback all migrations');
335
- console.log('4. refresh - Reset and re-run all migrations');
336
- console.log('5. fresh - Drop all tables and re-run migrations');
337
- console.log('6. status - Show migration status');
338
- console.log('0. Exit\n');
339
-
340
- const choice = await question('Enter your choice: ');
341
-
342
- if (choice === '0') {
343
- console.log('Goodbye!');
344
- return;
345
- }
346
-
347
- // Load database configuration
348
- const dbConfigPath = path.join(process.cwd(), 'database', 'config.js');
349
-
350
- let dbConfig;
351
- try {
352
- dbConfig = require(dbConfigPath);
353
- } catch (error) {
354
- require('dotenv').config();
355
- const env = process.env || {};
356
- dbConfig = {
357
- driver: env.DB_DRIVER || env.DATABASE_DRIVER,
358
- host: env.DB_HOST,
359
- port: env.DB_PORT ? Number(env.DB_PORT) : undefined,
360
- user: env.DB_USER || env.DB_USERNAME,
361
- password: env.DB_PASSWORD,
362
- database: env.DB_DATABASE || env.DB_NAME || env.DB_FILE || env.SQLITE_DB || env.SQLITE_FILENAME
363
- };
364
- if (!dbConfig.driver) {
365
- console.error('\n✗ Error: Could not load database configuration');
366
- console.error(` Make sure ${dbConfigPath} exists OR provide .env variables like DB_DRIVER, DB_HOST, DB_DATABASE`);
367
- console.error(' Run "outlet-init" to create the configuration');
368
- console.error(` Details: ${error.message}`);
369
- return;
370
- }
371
- }
372
-
373
- const { DatabaseConnection } = require('../lib/Database/DatabaseConnection');
374
- const MigrationManager = require('../lib/Migrations/MigrationManager');
375
-
376
- const connection = new DatabaseConnection(dbConfig);
377
- await connection.connect();
378
-
379
- const manager = new MigrationManager(connection);
380
-
381
- try {
382
- switch (choice) {
383
- case '1':
384
- await manager.run();
385
- break;
386
-
387
- case '2': {
388
- const steps = await question('How many batches to rollback? (default: 1): ');
389
- await manager.rollback(parseInt(steps) || 1);
390
- break;
391
- }
392
-
393
- case '3': {
394
- const confirmReset = await question('Are you sure you want to reset all migrations? (yes/no): ');
395
- if (confirmReset.toLowerCase() === 'yes') {
396
- await manager.reset();
397
- } else {
398
- console.log('Reset cancelled');
399
- }
400
- break;
401
- }
402
-
403
- case '4': {
404
- const confirmRefresh = await question('Are you sure you want to refresh all migrations? (yes/no): ');
405
- if (confirmRefresh.toLowerCase() === 'yes') {
406
- await manager.refresh();
407
- } else {
408
- console.log('Refresh cancelled');
409
- }
410
- break;
411
- }
412
-
413
- case '5': {
414
- const confirmFresh = await question('⚠️ WARNING: This will DROP ALL TABLES! Continue? (yes/no): ');
415
- if (confirmFresh.toLowerCase() === 'yes') {
416
- await manager.fresh();
417
- } else {
418
- console.log('Fresh cancelled');
419
- }
420
- break;
421
- }
422
-
423
- case '6':
424
- await manager.status();
425
- break;
426
-
427
- default:
428
- console.log('Invalid choice');
429
- }
430
- } catch (error) {
431
- console.error('\n✗ Migration error:', error.message);
432
- console.error(error.stack);
433
- }
434
-
435
- await connection.disconnect();
436
- }
437
-
438
- // Run the CLI
439
- main().catch(error => {
440
- console.error('Fatal error:', error);
441
- process.exit(1);
442
- });
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
+ });