millas 0.2.28 → 0.2.30

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.
Files changed (47) hide show
  1. package/bin/millas.js +12 -2
  2. package/package.json +2 -1
  3. package/src/cli.js +117 -20
  4. package/src/commands/call.js +1 -1
  5. package/src/commands/createsuperuser.js +137 -182
  6. package/src/commands/key.js +61 -83
  7. package/src/commands/lang.js +423 -515
  8. package/src/commands/make.js +88 -62
  9. package/src/commands/migrate.js +200 -279
  10. package/src/commands/new.js +55 -50
  11. package/src/commands/route.js +78 -80
  12. package/src/commands/schedule.js +52 -150
  13. package/src/commands/serve.js +158 -191
  14. package/src/console/AppCommand.js +106 -0
  15. package/src/console/BaseCommand.js +726 -0
  16. package/src/console/CommandContext.js +66 -0
  17. package/src/console/CommandRegistry.js +88 -0
  18. package/src/console/Style.js +123 -0
  19. package/src/console/index.js +12 -3
  20. package/src/container/AppInitializer.js +10 -0
  21. package/src/facades/DB.js +195 -0
  22. package/src/index.js +2 -1
  23. package/src/scaffold/maker.js +102 -42
  24. package/src/schematics/Collection.js +28 -0
  25. package/src/schematics/SchematicEngine.js +122 -0
  26. package/src/schematics/Template.js +99 -0
  27. package/src/schematics/index.js +7 -0
  28. package/src/templates/command/default.template.js +14 -0
  29. package/src/templates/command/schema.json +19 -0
  30. package/src/templates/controller/default.template.js +10 -0
  31. package/src/templates/controller/resource.template.js +59 -0
  32. package/src/templates/controller/schema.json +30 -0
  33. package/src/templates/job/default.template.js +11 -0
  34. package/src/templates/job/schema.json +19 -0
  35. package/src/templates/middleware/default.template.js +11 -0
  36. package/src/templates/middleware/schema.json +19 -0
  37. package/src/templates/migration/default.template.js +14 -0
  38. package/src/templates/migration/schema.json +19 -0
  39. package/src/templates/model/default.template.js +14 -0
  40. package/src/templates/model/migration.template.js +17 -0
  41. package/src/templates/model/schema.json +30 -0
  42. package/src/templates/service/default.template.js +12 -0
  43. package/src/templates/service/schema.json +19 -0
  44. package/src/templates/shape/default.template.js +11 -0
  45. package/src/templates/shape/schema.json +19 -0
  46. package/src/validation/BaseValidator.js +3 -0
  47. package/src/validation/types.js +3 -3
@@ -1,273 +1,186 @@
1
1
  'use strict';
2
2
 
3
- const chalk = require('chalk');
4
- const path = require('path');
5
- const fs = require('fs-extra');
3
+ const path = require('path');
4
+ const fs = require('fs-extra');
5
+ const BaseCommand = require('../console/BaseCommand');
6
+ const DB = require('../facades/DB');
6
7
 
7
- module.exports = function (program) {
8
+ class MigrateCommand extends BaseCommand {
9
+ static description = 'Database migration commands';
10
+ static namespace = 'db';
8
11
 
9
- // ── makemigrations ──────────────────────────────────────────────────────────
10
- program
11
- .command('makemigrations')
12
- .description('Detect model changes and generate migration files (never touches DB)')
13
- .option('--dry-run', 'Show what would be generated without writing files')
14
- .option('--noinput', 'Non-interactive mode fails if dangerous fields need resolution')
15
- .action(async (options) => {
16
- try {
17
- const ctx = getProjectContext();
18
- const Mk = require('../orm/migration/Makemigrations');
19
- const mk = new Mk(ctx.modelsPath, ctx.appMigPath, ctx.systemMigPath, {
20
- nonInteractive: options.noinput || false,
12
+ async onInit(register) {
13
+ // makemigrations
14
+ register
15
+ .command(async (dryRun, noinput) => {
16
+ const ctx = this.getProjectContext();
17
+ const Mk = require('../orm/migration/Makemigrations');
18
+ const mk = new Mk(ctx.modelsPath, ctx.appMigPath, ctx.systemMigPath, {
19
+ nonInteractive: noinput || false,
21
20
  });
22
- const result = await mk.run({ dryRun: options.dryRun });
21
+ const result = await mk.run({ dryRun });
23
22
 
24
23
  if (result.files.length === 0) {
25
- console.log(chalk.yellow(`\n No changes detected.\n`));
24
+ this.warn('No changes detected.');
26
25
  } else {
27
- if (options.dryRun) {
28
- console.log(chalk.cyan('\n Would generate:'));
26
+ if (dryRun) {
27
+ this.info('\nWould generate:');
29
28
  } else {
30
- console.log(chalk.green('\n Migrations generated:'));
29
+ this.success('Migrations generated:');
31
30
  }
32
- result.files.forEach(f => console.log(chalk.cyan(` + ${f}`)));
33
- result.ops.forEach(op => {
34
- // Match Django's +/-/~ prefix style exactly
35
- let prefix, label;
36
- switch (op.type) {
37
- case 'CreateModel': {
38
- const idxLines = [];
39
- for (const idx of (op.indexes || [])) {
40
- const l = idx.name
41
- ? `${idx.name} on ${op.table}`
42
- : `on field(s) ${idx.fields.join(', ')} of model ${op.table}`;
43
- idxLines.push(` ${chalk.green('+')} Create index ${l}`);
44
- }
45
- for (const ut of (op.uniqueTogether || [])) {
46
- idxLines.push(` ${chalk.green('+')} Create constraint on ${op.table} (${ut.join(', ')})`);
47
- }
48
- prefix = chalk.green('+'); label = `Create model ${op.table}`;
49
- console.log(chalk.gray(` ${prefix} ${label}`));
50
- idxLines.forEach(l => console.log(chalk.gray(l)));
51
- return; // return from forEach callback
52
- }
53
- case 'DeleteModel':
54
- prefix = chalk.red('-'); label = `Delete model ${op.table}`; break;
55
- case 'AddField':
56
- prefix = chalk.green('+'); label = `Add field ${op.column} to ${op.table}`; break;
57
- case 'RemoveField':
58
- prefix = chalk.red('-'); label = `Remove field ${op.column} from ${op.table}`; break;
59
- case 'AlterField':
60
- prefix = chalk.yellow('~'); label = `Alter field ${op.column} on ${op.table}`; break;
61
- case 'RenameField':
62
- prefix = chalk.yellow('~'); label = `Rename field ${op.oldColumn} on ${op.table} to ${op.newColumn}`; break;
63
- case 'RenameModel':
64
- prefix = chalk.yellow('~'); label = `Rename model ${op.oldTable} to ${op.newTable}`; break;
65
- case 'AddIndex': {
66
- const idx = op.index;
67
- const idxLabel = idx.name
68
- ? `${idx.name} on ${op.table}`
69
- : `on field(s) ${idx.fields.join(', ')} of model ${op.table}`;
70
- prefix = chalk.green('+'); label = `Create index ${idxLabel}`; break;
71
- }
72
- case 'RemoveIndex': {
73
- const idx = op.index;
74
- const idxLabel = idx.name
75
- ? `${idx.name} from ${op.table}`
76
- : `on field(s) ${idx.fields.join(', ')} of model ${op.table}`;
77
- prefix = chalk.red('-'); label = `Remove index ${idxLabel}`; break;
78
- }
79
- case 'RenameIndex':
80
- prefix = chalk.yellow('~'); label = `Rename index ${op.oldName} on ${op.table} to ${op.newName}`; break;
81
- case 'AlterUniqueTogether':
82
- prefix = chalk.yellow('~'); label = `Alter unique_together on ${op.table}`; break;
83
- default:
84
- prefix = chalk.gray(' '); label = op.type;
85
- }
86
- console.log(chalk.gray(` ${prefix} ${label}`));
87
- });
88
- if (!options.dryRun) {
89
- console.log(chalk.gray('\n Run: millas migrate to apply.\n'));
90
- } else {
91
- console.log();
31
+ result.files.forEach(f => this.info(` + ${f}`));
32
+ result.ops.forEach(op => this.printOperation(op));
33
+ if (!dryRun) {
34
+ this.info('\nRun: millas migrate to apply.\n');
92
35
  }
93
36
  }
94
- } catch (err) {
95
- bail('makemigrations', err);
96
- }
97
- });
37
+ })
38
+ .name('makemigrations')
39
+ .bool('dry-run', 'Show what would be generated without writing files')
40
+ .bool('noinput', 'Non-interactive mode — fails if dangerous fields need resolution')
41
+ .description('Detect model changes and generate migration files (never touches DB)');
98
42
 
99
- // ── migrate ─────────────────────────────────────────────────────────────────
100
- program
101
- .command('migrate')
102
- .description('Apply pending migrations in dependency order (never generates migrations)')
103
- .option('--fake <name>', 'Mark a migration as applied without running it')
104
- .action(async (options) => {
105
- try {
106
- const runner = await getRunner();
43
+ // migrate
44
+ register
45
+ .command(async (fakeAll, fake) => {
46
+ const runner = await this.getRunner();
47
+
48
+ if (fakeAll === true) {
49
+ const pending = await runner.plan();
50
+ if (pending.length === 0) {
51
+ this.warn('No pending migrations to fake.');
52
+ return;
53
+ }
54
+
55
+ for (const mig of pending) {
56
+ await runner.fake(mig.source, mig.name);
57
+ this.success(`Marked "${mig.key}" as applied (fake).`);
58
+ }
59
+ return;
60
+ }
107
61
 
108
- if (options.fake) {
109
- const [source, name] = options.fake.includes(':')
110
- ? options.fake.split(':')
111
- : ['app', options.fake];
62
+ if (fake) {
63
+ const [source, name] = fake.includes(':') ? fake.split(':') : ['app', fake];
112
64
  const result = await runner.fake(source, name);
113
- console.log(chalk.green(`\n Marked "${result.key}" as applied (fake).\n`));
65
+ this.success(`Marked "${result.key}" as applied (fake).`);
114
66
  return;
115
67
  }
116
68
 
117
69
  const result = await runner.migrate();
118
- printResult(result.ran, 'Applying');
119
- } catch (err) {
120
- bail('migrate', err);
121
- } finally {
122
- await closeDb();
123
- }
124
- });
70
+ this.printResult(result.ran, 'Applying');
71
+ })
72
+ .name('migrate')
73
+ .bool('fake-all', 'Mark all pending migrations as applied without running them')
74
+ .str('--fake', v => v.optional(), 'Mark a migration as applied without running it')
75
+ .description('Apply pending migrations in dependency order (never generates migrations)')
76
+ .after(async () => await DB.closeAll());
125
77
 
126
- // ── migrate:plan ─────────────────────────────────────────────────────────────
127
- program
128
- .command('migrate:plan')
129
- .description('Preview which migrations would run without applying them')
130
- .action(async () => {
131
- try {
132
- const runner = await getRunner();
78
+ // migrate:plan
79
+ register
80
+ .command(async () => {
81
+ const runner = await this.getRunner();
133
82
  const pending = await runner.plan();
134
83
 
135
84
  if (pending.length === 0) {
136
- console.log(chalk.yellow('\n Nothing to migrate.\n'));
85
+ this.warn('Nothing to migrate.');
137
86
  return;
138
87
  }
139
88
 
140
- console.log(chalk.cyan('\n Migration plan:\n'));
89
+ this.info('\nMigration plan:\n');
141
90
  pending.forEach(p => {
142
- const color = p.source === 'system' ? chalk.gray : chalk.cyan;
143
- console.log(color(` ${p.key}`));
91
+ const prefix = p.source === 'system' ? ' [system]' : ' ';
92
+ this.info(`${prefix} ${p.key}`);
144
93
  });
145
- console.log();
146
- } catch (err) {
147
- bail('migrate:plan', err);
148
- } finally {
149
- await closeDb();
150
- }
151
- });
94
+ })
95
+ .name('plan')
96
+ .description('Preview which migrations would run without applying them')
97
+ .after(async () => await DB.closeAll());
152
98
 
153
- // ── migrate:status ───────────────────────────────────────────────────────────
154
- program
155
- .command('migrate:status')
156
- .description('Show the status of all migrations')
157
- .action(async () => {
158
- try {
159
- const runner = await getRunner();
160
- const rows = await runner.status();
99
+ // migrate:status
100
+ register
101
+ .command(async () => {
102
+ const runner = await this.getRunner();
103
+ const rows = await runner.status();
161
104
 
162
105
  if (rows.length === 0) {
163
- console.log(chalk.yellow('\n No migrations found.\n'));
106
+ this.warn('No migrations found.');
164
107
  return;
165
108
  }
166
109
 
167
110
  const colW = Math.max(...rows.map(r => r.key.length)) + 2;
168
- console.log(`\n ${'Migration'.padEnd(colW)} ${'Status'.padEnd(10)} Batch`);
169
- console.log(chalk.gray(' ' + '─'.repeat(colW + 20)));
111
+ this.info(`\n${'Migration'.padEnd(colW)} ${'Status'.padEnd(10)} Batch`);
112
+ this.info('─'.repeat(colW + 20));
170
113
 
171
114
  let lastSource = null;
172
115
  for (const row of rows) {
173
116
  if (row.source !== lastSource) {
174
- if (lastSource !== null) console.log();
117
+ if (lastSource !== null) this.info('');
175
118
  lastSource = row.source;
176
119
  }
177
- const status = row.status === 'Applied'
178
- ? chalk.green(row.status.padEnd(10))
179
- : chalk.yellow(row.status.padEnd(10));
180
- const batch = row.batch ? chalk.gray(String(row.batch)) : chalk.gray('—');
181
- const label = row.source === 'system'
182
- ? chalk.gray(row.key.padEnd(colW))
183
- : chalk.cyan(row.key.padEnd(colW));
184
- console.log(` ${label} ${status} ${batch}`);
120
+ const status = row.status.padEnd(10);
121
+ const batch = row.batch ? String(row.batch) : '—';
122
+ const prefix = row.source === 'system' ? '[system]' : '';
123
+ this.info(`${row.key.padEnd(colW)} ${status} ${batch} ${prefix}`);
185
124
  }
186
- console.log();
187
- } catch (err) {
188
- bail('migrate:status', err);
189
- } finally {
190
- await closeDb();
191
- }
192
- });
125
+ })
126
+ .name('status')
127
+ .description('Show the status of all migrations')
128
+ .after(async () => await DB.closeAll());
193
129
 
194
- // ── migrate:rollback ─────────────────────────────────────────────────────────
195
- program
196
- .command('migrate:rollback')
197
- .description('Rollback the last batch of migrations')
198
- .option('--steps <n>', 'Number of batches to rollback', '1')
199
- .action(async (options) => {
200
- try {
201
- const runner = await getRunner();
202
- const result = await runner.rollback(Number(options.steps));
203
- printResult(result.rolledBack, 'Reverting');
204
- } catch (err) {
205
- bail('migrate:rollback', err);
206
- } finally {
207
- await closeDb();
208
- }
209
- });
130
+ // migrate:rollback
131
+ register
132
+ .command(async (steps) => {
133
+ const runner = await this.getRunner();
134
+ const result = await runner.rollback(Number(steps));
135
+ this.printResult(result.rolledBack, 'Reverting');
136
+ })
137
+ .name('rollback')
138
+ .num('--steps', v => v.optional().default(1), 'Number of batches to rollback')
139
+ .description('Rollback the last batch of migrations')
140
+ .after(async () => await DB.closeAll());
210
141
 
211
- // ── migrate:fresh ────────────────────────────────────────────────────────────
212
- program
213
- .command('migrate:fresh')
214
- .description('Drop all tables and re-run every migration from scratch')
215
- .action(async () => {
216
- try {
217
- console.log(chalk.yellow('\n ⚠ Dropping all tables…\n'));
218
- const runner = await getRunner();
142
+ // migrate:fresh
143
+ register
144
+ .command(async () => {
145
+ this.warn('⚠ Dropping all tables');
146
+ const runner = await this.getRunner();
219
147
  const result = await runner.fresh();
220
- printResult(result.ran, 'Applying');
221
- } catch (err) {
222
- bail('migrate:fresh', err);
223
- } finally {
224
- await closeDb();
225
- }
226
- });
148
+ this.printResult(result.ran, 'Applying');
149
+ })
150
+ .name('fresh')
151
+ .description('Drop all tables and re-run every migration from scratch')
152
+ .after(async () => await DB.closeAll());
227
153
 
228
- // ── migrate:reset ────────────────────────────────────────────────────────────
229
- program
230
- .command('migrate:reset')
231
- .description('Rollback ALL migrations')
232
- .action(async () => {
233
- try {
234
- const runner = await getRunner();
154
+ // migrate:reset
155
+ register
156
+ .command(async () => {
157
+ const runner = await this.getRunner();
235
158
  const result = await runner.reset();
236
- printResult(result.rolledBack, 'Reverting');
237
- } catch (err) {
238
- bail('migrate:reset', err);
239
- } finally {
240
- await closeDb();
241
- }
242
- });
159
+ this.printResult(result.rolledBack, 'Reverting');
160
+ })
161
+ .name('reset')
162
+ .description('Rollback ALL migrations')
163
+ .after(async () => await DB.closeAll());
243
164
 
244
- // ── migrate:refresh ──────────────────────────────────────────────────────────
245
- program
246
- .command('migrate:refresh')
247
- .description('Rollback all then re-run all migrations')
248
- .action(async () => {
249
- try {
250
- const runner = await getRunner();
165
+ // migrate:refresh
166
+ register
167
+ .command(async () => {
168
+ const runner = await this.getRunner();
251
169
  const result = await runner.refresh();
252
- printResult(result.ran, 'Applying');
253
- } catch (err) {
254
- bail('migrate:refresh', err);
255
- } finally {
256
- await closeDb();
257
- }
258
- });
170
+ this.printResult(result.ran, 'Applying');
171
+ })
172
+ .name('refresh')
173
+ .description('Rollback all then re-run all migrations')
174
+ .after(async () => await DB.closeAll());
259
175
 
260
- // ── db:seed ──────────────────────────────────────────────────────────────────
261
- program
262
- .command('db:seed')
263
- .description('Run all database seeders')
264
- .action(async () => {
265
- try {
266
- const ctx = getProjectContext();
176
+ // db:seed
177
+ register
178
+ .command(async () => {
179
+ const ctx = this.getProjectContext();
267
180
  const seedersDir = ctx.seedersPath;
268
181
 
269
182
  if (!fs.existsSync(seedersDir)) {
270
- console.log(chalk.yellow('\n No seeders directory found.\n'));
183
+ this.warn('No seeders directory found.');
271
184
  return;
272
185
  }
273
186
 
@@ -276,80 +189,88 @@ module.exports = function (program) {
276
189
  .sort();
277
190
 
278
191
  if (files.length === 0) {
279
- console.log(chalk.yellow('\n No seeder files found.\n'));
192
+ this.warn('No seeder files found.');
280
193
  return;
281
194
  }
282
195
 
283
- const db = await getDbConnection();
284
- console.log();
196
+ const db = DB.connection();
285
197
  for (const file of files) {
286
198
  const seeder = require(path.join(seedersDir, file));
287
199
  await seeder.run(db);
288
- console.log(chalk.green(`Seeded: ${file}`));
200
+ this.success(`Seeded: ${file}`);
289
201
  }
290
- console.log();
291
- } catch (err) {
292
- bail('db:seed', err);
293
- } finally {
294
- await closeDb();
295
- }
296
- });
297
- };
298
-
299
- // ─── Helpers ──────────────────────────────────────────────────────────────────
300
-
301
- function getProjectContext() {
302
- const cwd = process.cwd();
303
- return {
304
- appMigPath: path.join(cwd, 'database/migrations'),
305
- systemMigPath: path.join(__dirname, '../migrations/system'),
306
- seedersPath: path.join(cwd, 'database/seeders'),
307
- modelsPath: path.join(cwd, 'app/models'),
308
- };
309
- }
310
-
311
- async function getDbConnection() {
312
- const configPath = path.join(process.cwd(), 'config/database');
313
- if (!fs.existsSync(configPath + '.js')) {
314
- throw new Error('config/database.js not found. Are you inside a Millas project?');
202
+ })
203
+ .name('seed')
204
+ .description('Run all database seeders')
205
+ .after(async () => await DB.closeAll());
315
206
  }
316
- const config = require(configPath);
317
- const DatabaseManager = require('../orm/drivers/DatabaseManager');
318
- DatabaseManager.configure(config);
319
- return DatabaseManager.connection();
320
- }
321
207
 
322
- async function getRunner() {
323
- const MigrationRunner = require('../orm/migration/MigrationRunner');
324
- const ctx = getProjectContext();
325
- const db = await getDbConnection();
326
- return new MigrationRunner(db, ctx.appMigPath, ctx.systemMigPath);
327
- }
208
+ getProjectContext() {
209
+ return {
210
+ appMigPath: path.join(this.cwd, 'database/migrations'),
211
+ systemMigPath: path.join(__dirname, '../migrations/system'),
212
+ seedersPath: path.join(this.cwd, 'database/seeders'),
213
+ modelsPath: path.join(this.cwd, 'app/models'),
214
+ };
215
+ }
328
216
 
329
- async function closeDb() {
330
- try {
331
- const DatabaseManager = require('../orm/drivers/DatabaseManager');
332
- await DatabaseManager.closeAll();
333
- } catch {}
334
- }
217
+ async getRunner() {
218
+ const MigrationRunner = require('../orm/migration/MigrationRunner');
219
+ const ctx = this.getProjectContext();
220
+ const db = DB.connection();
221
+ return new MigrationRunner(db, ctx.appMigPath, ctx.systemMigPath);
222
+ }
335
223
 
336
- function printResult(list, verb) {
337
- if (!list || list.length === 0) {
338
- console.log(chalk.yellow('\n Nothing to do.\n'));
339
- return;
224
+ printOperation(op) {
225
+ let prefix, label;
226
+ switch (op.type) {
227
+ case 'CreateModel': {
228
+ this.info(` + Create model ${op.table}`);
229
+ for (const idx of (op.indexes || [])) {
230
+ const l = idx.name ? `${idx.name} on ${op.table}` : `on field(s) ${idx.fields.join(', ')} of model ${op.table}`;
231
+ this.info(` + Create index ${l}`);
232
+ }
233
+ for (const ut of (op.uniqueTogether || [])) {
234
+ this.info(` + Create constraint on ${op.table} (${ut.join(', ')})`);
235
+ }
236
+ return;
237
+ }
238
+ case 'DeleteModel': prefix = '-'; label = `Delete model ${op.table}`; break;
239
+ case 'AddField': prefix = '+'; label = `Add field ${op.column} to ${op.table}`; break;
240
+ case 'RemoveField': prefix = '-'; label = `Remove field ${op.column} from ${op.table}`; break;
241
+ case 'AlterField': prefix = '~'; label = `Alter field ${op.column} on ${op.table}`; break;
242
+ case 'RenameField': prefix = '~'; label = `Rename field ${op.oldColumn} on ${op.table} to ${op.newColumn}`; break;
243
+ case 'RenameModel': prefix = '~'; label = `Rename model ${op.oldTable} to ${op.newTable}`; break;
244
+ case 'AddIndex': {
245
+ const idx = op.index;
246
+ const idxLabel = idx.name ? `${idx.name} on ${op.table}` : `on field(s) ${idx.fields.join(', ')} of model ${op.table}`;
247
+ prefix = '+'; label = `Create index ${idxLabel}`; break;
248
+ }
249
+ case 'RemoveIndex': {
250
+ const idx = op.index;
251
+ const idxLabel = idx.name ? `${idx.name} from ${op.table}` : `on field(s) ${idx.fields.join(', ')} of model ${op.table}`;
252
+ prefix = '-'; label = `Remove index ${idxLabel}`; break;
253
+ }
254
+ case 'RenameIndex': prefix = '~'; label = `Rename index ${op.oldName} on ${op.table} to ${op.newName}`; break;
255
+ case 'AlterUniqueTogether': prefix = '~'; label = `Alter unique_together on ${op.table}`; break;
256
+ default: prefix = ' '; label = op.type;
257
+ }
258
+ this.info(` ${prefix} ${label}`);
340
259
  }
341
- console.log(chalk.green(`\n Running migrations:`));
342
- for (const entry of list) {
343
- const label = typeof entry === 'object' ? entry.label || entry.key : entry;
344
- const source = typeof entry === 'object' ? entry.source : null;
345
- const color = source === 'system' ? chalk.gray : chalk.cyan;
346
- console.log(color(` ${verb} ${label}... OK`));
260
+
261
+ printResult(list, verb) {
262
+ if (!list || list.length === 0) {
263
+ this.warn('Nothing to do.');
264
+ return;
265
+ }
266
+ this.success('Running migrations:');
267
+ for (const entry of list) {
268
+ const label = typeof entry === 'object' ? entry.label || entry.key : entry;
269
+ const source = typeof entry === 'object' ? entry.source : null;
270
+ const prefix = source === 'system' ? '[system]' : '';
271
+ this.info(` ${verb} ${label}... OK ${prefix}`);
272
+ }
347
273
  }
348
- console.log();
349
274
  }
350
275
 
351
- function bail(cmd, err) {
352
- console.error(chalk.red(`\n ✖ ${cmd} failed: ${err.message}\n`));
353
- if (process.env.DEBUG) console.error(err.stack);
354
- closeDb().finally(() => process.exit(1));
355
- }
276
+ module.exports = MigrateCommand;