mpx-db 1.0.3 → 1.1.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.
@@ -10,9 +10,17 @@ const MIGRATIONS_DIR = './migrations';
10
10
  /**
11
11
  * Initialize migrations directory
12
12
  */
13
- export async function initMigrations() {
13
+ export async function initMigrations(options = {}) {
14
14
  if (fs.existsSync(MIGRATIONS_DIR)) {
15
- console.log(chalk.yellow('Migrations directory already exists'));
15
+ if (options.json) {
16
+ console.log(JSON.stringify({
17
+ success: false,
18
+ message: 'Migrations directory already exists',
19
+ directory: MIGRATIONS_DIR
20
+ }, null, 2));
21
+ } else {
22
+ console.log(chalk.yellow('Migrations directory already exists'));
23
+ }
16
24
  return;
17
25
  }
18
26
 
@@ -56,16 +64,31 @@ DROP TABLE users;
56
64
 
57
65
  fs.writeFileSync(path.join(MIGRATIONS_DIR, 'README.md'), readme);
58
66
 
59
- console.log(chalk.green('✓ Migrations directory created'));
60
- console.log(chalk.gray(` ${MIGRATIONS_DIR}/`));
67
+ if (options.json) {
68
+ console.log(JSON.stringify({
69
+ success: true,
70
+ directory: MIGRATIONS_DIR,
71
+ message: 'Migrations directory created'
72
+ }, null, 2));
73
+ } else {
74
+ console.log(chalk.green('✓ Migrations directory created'));
75
+ console.log(chalk.gray(` ${MIGRATIONS_DIR}/`));
76
+ }
61
77
  }
62
78
 
63
79
  /**
64
80
  * Create new migration file
65
81
  */
66
- export async function createMigration(description) {
82
+ export async function createMigration(description, options = {}) {
67
83
  if (!fs.existsSync(MIGRATIONS_DIR)) {
68
- console.log(chalk.yellow('Migrations directory not found. Run: mpx-db migrate init'));
84
+ if (options.json) {
85
+ console.log(JSON.stringify({
86
+ success: false,
87
+ error: 'Migrations directory not found'
88
+ }, null, 2));
89
+ } else {
90
+ console.log(chalk.yellow('Migrations directory not found. Run: mpx-db migrate init'));
91
+ }
69
92
  return;
70
93
  }
71
94
 
@@ -98,8 +121,16 @@ export async function createMigration(description) {
98
121
 
99
122
  fs.writeFileSync(filepath, template);
100
123
 
101
- console.log(chalk.green('✓ Created migration'));
102
- console.log(chalk.gray(` ${filepath}`));
124
+ if (options.json) {
125
+ console.log(JSON.stringify({
126
+ success: true,
127
+ filename,
128
+ path: filepath
129
+ }, null, 2));
130
+ } else {
131
+ console.log(chalk.green('✓ Created migration'));
132
+ console.log(chalk.gray(` ${filepath}`));
133
+ }
103
134
  }
104
135
 
105
136
  /**
@@ -131,7 +162,7 @@ function parseMigration(filepath) {
131
162
  /**
132
163
  * Show migration status
133
164
  */
134
- export async function showMigrationStatus(target) {
165
+ export async function showMigrationStatus(target, options = {}) {
135
166
  let db;
136
167
 
137
168
  try {
@@ -149,35 +180,63 @@ export async function showMigrationStatus(target) {
149
180
  const files = getMigrationFiles();
150
181
 
151
182
  if (files.length === 0) {
152
- console.log(chalk.yellow('No migration files found'));
153
- console.log(chalk.gray('Create one with: mpx-db migrate create <description>'));
183
+ if (options.json) {
184
+ console.log(JSON.stringify({ migrations: [] }, null, 2));
185
+ } else {
186
+ console.log(chalk.yellow('No migration files found'));
187
+ if (!options.quiet) {
188
+ console.log(chalk.gray('Create one with: mpx-db migrate create <description>'));
189
+ }
190
+ }
154
191
  return;
155
192
  }
156
193
 
194
+ // Build migration list
195
+ const migrations = files.map(file => {
196
+ const name = file.replace('.sql', '');
197
+ const isApplied = appliedNames.has(name);
198
+ const appliedRecord = applied.find(m => m.name === name);
199
+
200
+ return {
201
+ name,
202
+ status: isApplied ? 'applied' : 'pending',
203
+ appliedAt: appliedRecord ? new Date(appliedRecord.applied_at).toISOString() : null
204
+ };
205
+ });
206
+
207
+ // JSON output
208
+ if (options.json) {
209
+ console.log(JSON.stringify({ migrations }, null, 2));
210
+ return;
211
+ }
212
+
213
+ // Human-readable output
157
214
  const table = new Table({
158
215
  head: ['Migration', 'Status', 'Applied'].map(h => chalk.cyan(h)),
159
216
  style: { head: [], border: ['gray'] }
160
217
  });
161
218
 
162
- for (const file of files) {
163
- const name = file.replace('.sql', '');
164
- const isApplied = appliedNames.has(name);
165
- const appliedRecord = applied.find(m => m.name === name);
166
-
219
+ for (const m of migrations) {
167
220
  table.push([
168
- chalk.white(name),
169
- isApplied ? chalk.green('Applied') : chalk.yellow('Pending'),
170
- appliedRecord ? chalk.gray(new Date(appliedRecord.applied_at).toLocaleString()) : chalk.gray('-')
221
+ chalk.white(m.name),
222
+ m.status === 'applied' ? chalk.green('Applied') : chalk.yellow('Pending'),
223
+ m.appliedAt ? chalk.gray(new Date(m.appliedAt).toLocaleString()) : chalk.gray('-')
171
224
  ]);
172
225
  }
173
226
 
174
227
  console.log(table.toString());
175
228
 
176
- const pending = files.filter(f => !appliedNames.has(f.replace('.sql', '')));
177
- console.log(chalk.gray(`\n${applied.length} applied, ${pending.length} pending`));
229
+ if (!options.quiet) {
230
+ const pending = migrations.filter(m => m.status === 'pending');
231
+ console.log(chalk.gray(`\n${applied.length} applied, ${pending.length} pending`));
232
+ }
178
233
 
179
234
  } catch (err) {
180
- console.error(chalk.red(`✗ ${err.message}`));
235
+ if (options.json) {
236
+ console.log(JSON.stringify({ error: err.message }, null, 2));
237
+ } else {
238
+ console.error(chalk.red(`✗ ${err.message}`));
239
+ }
181
240
  process.exit(1);
182
241
  } finally {
183
242
  if (db) {
@@ -189,7 +248,7 @@ export async function showMigrationStatus(target) {
189
248
  /**
190
249
  * Run pending migrations
191
250
  */
192
- export async function runMigrations(target) {
251
+ export async function runMigrations(target, options = {}) {
193
252
  let db;
194
253
 
195
254
  try {
@@ -205,18 +264,28 @@ export async function runMigrations(target) {
205
264
  const pending = files.filter(f => !appliedNames.has(f.replace('.sql', '')));
206
265
 
207
266
  if (pending.length === 0) {
208
- console.log(chalk.green('✓ All migrations up to date'));
267
+ if (options.json) {
268
+ console.log(JSON.stringify({ success: true, applied: [], count: 0 }, null, 2));
269
+ } else {
270
+ console.log(chalk.green('✓ All migrations up to date'));
271
+ }
209
272
  return;
210
273
  }
211
274
 
212
- console.log(chalk.cyan(`Running ${pending.length} migration(s)...\n`));
275
+ if (!options.quiet && !options.json) {
276
+ console.log(chalk.cyan(`Running ${pending.length} migration(s)...\n`));
277
+ }
278
+
279
+ const appliedList = [];
213
280
 
214
281
  for (const file of pending) {
215
282
  const name = file.replace('.sql', '');
216
283
  const filepath = path.join(MIGRATIONS_DIR, file);
217
284
  const migration = parseMigration(filepath);
218
285
 
219
- console.log(chalk.gray(`→ ${name}`));
286
+ if (!options.quiet && !options.json) {
287
+ console.log(chalk.gray(`→ ${name}`));
288
+ }
220
289
 
221
290
  // Execute migration (split by semicolon for multiple statements)
222
291
  // Remove comment lines first, then split
@@ -235,14 +304,29 @@ export async function runMigrations(target) {
235
304
  }
236
305
 
237
306
  await db.recordMigration(name);
307
+ appliedList.push(name);
238
308
 
239
- console.log(chalk.green(` ✓ Applied`));
309
+ if (!options.quiet && !options.json) {
310
+ console.log(chalk.green(` ✓ Applied`));
311
+ }
240
312
  }
241
313
 
242
- console.log(chalk.green(`\n✓ ${pending.length} migration(s) applied`));
314
+ if (options.json) {
315
+ console.log(JSON.stringify({
316
+ success: true,
317
+ applied: appliedList,
318
+ count: appliedList.length
319
+ }, null, 2));
320
+ } else {
321
+ console.log(chalk.green(`\n✓ ${pending.length} migration(s) applied`));
322
+ }
243
323
 
244
324
  } catch (err) {
245
- console.error(chalk.red(`\n✗ Migration failed: ${err.message}`));
325
+ if (options.json) {
326
+ console.log(JSON.stringify({ success: false, error: err.message }, null, 2));
327
+ } else {
328
+ console.error(chalk.red(`\n✗ Migration failed: ${err.message}`));
329
+ }
246
330
  process.exit(1);
247
331
  } finally {
248
332
  if (db) {
@@ -254,7 +338,7 @@ export async function runMigrations(target) {
254
338
  /**
255
339
  * Rollback last migration
256
340
  */
257
- export async function rollbackMigration(target) {
341
+ export async function rollbackMigration(target, options = {}) {
258
342
  let db;
259
343
 
260
344
  try {
@@ -266,7 +350,11 @@ export async function rollbackMigration(target) {
266
350
  const applied = await db.getAppliedMigrations();
267
351
 
268
352
  if (applied.length === 0) {
269
- console.log(chalk.yellow('No migrations to rollback'));
353
+ if (options.json) {
354
+ console.log(JSON.stringify({ success: false, message: 'No migrations to rollback' }, null, 2));
355
+ } else {
356
+ console.log(chalk.yellow('No migrations to rollback'));
357
+ }
270
358
  return;
271
359
  }
272
360
 
@@ -274,18 +362,28 @@ export async function rollbackMigration(target) {
274
362
  const filepath = path.join(MIGRATIONS_DIR, `${last.name}.sql`);
275
363
 
276
364
  if (!fs.existsSync(filepath)) {
277
- console.error(chalk.red(`✗ Migration file not found: ${filepath}`));
365
+ if (options.json) {
366
+ console.log(JSON.stringify({ success: false, error: 'Migration file not found' }, null, 2));
367
+ } else {
368
+ console.error(chalk.red(`✗ Migration file not found: ${filepath}`));
369
+ }
278
370
  process.exit(1);
279
371
  }
280
372
 
281
373
  const migration = parseMigration(filepath);
282
374
 
283
375
  if (!migration.down) {
284
- console.error(chalk.red(`✗ No down migration found in ${last.name}`));
376
+ if (options.json) {
377
+ console.log(JSON.stringify({ success: false, error: 'No down migration found' }, null, 2));
378
+ } else {
379
+ console.error(chalk.red(`✗ No down migration found in ${last.name}`));
380
+ }
285
381
  process.exit(1);
286
382
  }
287
383
 
288
- console.log(chalk.cyan(`Rolling back: ${last.name}`));
384
+ if (!options.quiet && !options.json) {
385
+ console.log(chalk.cyan(`Rolling back: ${last.name}`));
386
+ }
289
387
 
290
388
  // Execute rollback (split by semicolon for multiple statements)
291
389
  // Remove comment lines first, then split
@@ -305,10 +403,18 @@ export async function rollbackMigration(target) {
305
403
 
306
404
  await db.removeMigration(last.name);
307
405
 
308
- console.log(chalk.green('✓ Rolled back'));
406
+ if (options.json) {
407
+ console.log(JSON.stringify({ success: true, rolledBack: last.name }, null, 2));
408
+ } else {
409
+ console.log(chalk.green('✓ Rolled back'));
410
+ }
309
411
 
310
412
  } catch (err) {
311
- console.error(chalk.red(`✗ Rollback failed: ${err.message}`));
413
+ if (options.json) {
414
+ console.log(JSON.stringify({ success: false, error: err.message }, null, 2));
415
+ } else {
416
+ console.error(chalk.red(`✗ Rollback failed: ${err.message}`));
417
+ }
312
418
  process.exit(1);
313
419
  } finally {
314
420
  if (db) {
@@ -6,7 +6,7 @@ import { createConnection } from '../db/connection.js';
6
6
  /**
7
7
  * Execute a query or statement
8
8
  */
9
- export async function handleQuery(target, sql, options) {
9
+ export async function handleQuery(target, sql, options = {}) {
10
10
  let db;
11
11
 
12
12
  try {
@@ -27,30 +27,60 @@ export async function handleQuery(target, sql, options) {
27
27
  const rows = await db.query(sql);
28
28
  const duration = Date.now() - startTime;
29
29
 
30
- // Display results
31
- if (rows.length === 0) {
32
- console.log(chalk.yellow('No rows returned'));
30
+ // JSON output
31
+ if (options.json) {
32
+ console.log(JSON.stringify({
33
+ success: true,
34
+ type: 'query',
35
+ rows,
36
+ rowCount: rows.length,
37
+ duration
38
+ }, null, 2));
33
39
  } else {
34
- displayTable(rows);
40
+ // Display results
41
+ if (rows.length === 0) {
42
+ console.log(chalk.yellow('No rows returned'));
43
+ } else {
44
+ displayTable(rows);
45
+ }
46
+
47
+ if (!options.quiet) {
48
+ console.log(chalk.gray(`\n${rows.length} row(s) in ${duration}ms`));
49
+ }
35
50
  }
36
-
37
- console.log(chalk.gray(`\n${rows.length} row(s) in ${duration}ms`));
38
51
  } else {
39
52
  // INSERT, UPDATE, DELETE, CREATE, DROP, ALTER - returns affected rows
40
53
  const result = await db.execute(sql);
41
54
  const duration = Date.now() - startTime;
42
55
 
43
- console.log(chalk.green('✓ Statement executed successfully'));
44
- console.log(chalk.gray(` Affected rows: ${result.affectedRows}`));
45
- if (result.insertId) {
46
- console.log(chalk.gray(` Insert ID: ${result.insertId}`));
56
+ // JSON output
57
+ if (options.json) {
58
+ console.log(JSON.stringify({
59
+ success: true,
60
+ type: 'statement',
61
+ affectedRows: result.affectedRows,
62
+ insertId: result.insertId,
63
+ duration
64
+ }, null, 2));
65
+ } else {
66
+ if (!options.quiet) {
67
+ console.log(chalk.green('✓ Statement executed successfully'));
68
+ console.log(chalk.gray(` Affected rows: ${result.affectedRows}`));
69
+ if (result.insertId) {
70
+ console.log(chalk.gray(` Insert ID: ${result.insertId}`));
71
+ }
72
+ console.log(chalk.gray(` Duration: ${duration}ms`));
73
+ }
47
74
  }
48
- console.log(chalk.gray(` Duration: ${duration}ms`));
49
75
  }
50
76
 
51
77
  } catch (err) {
52
- console.error(chalk.red('✗ Query failed'));
53
- console.error(chalk.red(` ${err.message}`));
78
+ if (options.json) {
79
+ console.log(JSON.stringify({ success: false, error: err.message }, null, 2));
80
+ } else {
81
+ console.error(chalk.red('✗ Query failed'));
82
+ console.error(chalk.red(` ${err.message}`));
83
+ }
54
84
  process.exit(1);
55
85
  } finally {
56
86
  if (db) {
@@ -6,7 +6,7 @@ import { resolveConnection } from './query.js';
6
6
  /**
7
7
  * Show database info
8
8
  */
9
- export async function showInfo(target) {
9
+ export async function showInfo(target, options = {}) {
10
10
  let db;
11
11
 
12
12
  try {
@@ -15,22 +15,39 @@ export async function showInfo(target) {
15
15
 
16
16
  const info = await db.getInfo();
17
17
 
18
- console.log(chalk.bold('\nDatabase Information'));
19
- console.log(chalk.gray('─'.repeat(50)));
20
- console.log(`${chalk.cyan('Type:')} ${info.type}`);
21
- if (info.database) {
22
- console.log(`${chalk.cyan('Database:')} ${info.database}`);
18
+ // JSON output
19
+ if (options.json) {
20
+ console.log(JSON.stringify({
21
+ type: info.type,
22
+ database: info.database,
23
+ path: info.path,
24
+ size: info.size,
25
+ sizeFormatted: info.sizeFormatted,
26
+ tables: info.tables,
27
+ totalRows: info.totalRows
28
+ }, null, 2));
29
+ } else {
30
+ console.log(chalk.bold('\nDatabase Information'));
31
+ console.log(chalk.gray('─'.repeat(50)));
32
+ console.log(`${chalk.cyan('Type:')} ${info.type}`);
33
+ if (info.database) {
34
+ console.log(`${chalk.cyan('Database:')} ${info.database}`);
35
+ }
36
+ if (info.path) {
37
+ console.log(`${chalk.cyan('Path:')} ${info.path}`);
38
+ }
39
+ console.log(`${chalk.cyan('Size:')} ${info.sizeFormatted}`);
40
+ console.log(`${chalk.cyan('Tables:')} ${info.tables}`);
41
+ console.log(`${chalk.cyan('Total Rows:')} ${info.totalRows.toLocaleString()}`);
42
+ console.log();
23
43
  }
24
- if (info.path) {
25
- console.log(`${chalk.cyan('Path:')} ${info.path}`);
26
- }
27
- console.log(`${chalk.cyan('Size:')} ${info.sizeFormatted}`);
28
- console.log(`${chalk.cyan('Tables:')} ${info.tables}`);
29
- console.log(`${chalk.cyan('Total Rows:')} ${info.totalRows.toLocaleString()}`);
30
- console.log();
31
44
 
32
45
  } catch (err) {
33
- console.error(chalk.red(`✗ ${err.message}`));
46
+ if (options.json) {
47
+ console.log(JSON.stringify({ error: err.message }, null, 2));
48
+ } else {
49
+ console.error(chalk.red(`✗ ${err.message}`));
50
+ }
34
51
  process.exit(1);
35
52
  } finally {
36
53
  if (db) {
@@ -42,7 +59,7 @@ export async function showInfo(target) {
42
59
  /**
43
60
  * List all tables
44
61
  */
45
- export async function listTables(target) {
62
+ export async function listTables(target, options = {}) {
46
63
  let db;
47
64
 
48
65
  try {
@@ -51,6 +68,19 @@ export async function listTables(target) {
51
68
 
52
69
  const tables = await db.getTables();
53
70
 
71
+ // JSON output
72
+ if (options.json) {
73
+ console.log(JSON.stringify({
74
+ tables: tables.map(t => ({
75
+ name: t.name,
76
+ type: t.type,
77
+ rowCount: t.rows
78
+ }))
79
+ }, null, 2));
80
+ return;
81
+ }
82
+
83
+ // Human-readable output
54
84
  if (tables.length === 0) {
55
85
  console.log(chalk.yellow('No tables found'));
56
86
  return;
@@ -71,10 +101,16 @@ export async function listTables(target) {
71
101
  }
72
102
 
73
103
  console.log(table.toString());
74
- console.log(chalk.gray(`\n${tables.length} table(s)`));
104
+ if (!options.quiet) {
105
+ console.log(chalk.gray(`\n${tables.length} table(s)`));
106
+ }
75
107
 
76
108
  } catch (err) {
77
- console.error(chalk.red(`✗ ${err.message}`));
109
+ if (options.json) {
110
+ console.log(JSON.stringify({ error: err.message }, null, 2));
111
+ } else {
112
+ console.error(chalk.red(`✗ ${err.message}`));
113
+ }
78
114
  process.exit(1);
79
115
  } finally {
80
116
  if (db) {
@@ -86,7 +122,7 @@ export async function listTables(target) {
86
122
  /**
87
123
  * Describe table schema
88
124
  */
89
- export async function describeTable(target, tableName) {
125
+ export async function describeTable(target, tableName, options = {}) {
90
126
  let db;
91
127
 
92
128
  try {
@@ -96,33 +132,55 @@ export async function describeTable(target, tableName) {
96
132
  const schema = await db.getTableSchema(tableName);
97
133
 
98
134
  if (schema.length === 0) {
99
- console.log(chalk.yellow(`Table "${tableName}" not found or has no columns`));
135
+ if (options.json) {
136
+ console.log(JSON.stringify({ error: `Table "${tableName}" not found` }, null, 2));
137
+ } else {
138
+ console.log(chalk.yellow(`Table "${tableName}" not found or has no columns`));
139
+ }
100
140
  return;
101
141
  }
102
142
 
103
- console.log(chalk.bold(`\nTable: ${tableName}`));
104
- console.log(chalk.gray('─'.repeat(80)));
105
-
106
- const table = new Table({
107
- head: ['Column', 'Type', 'Nullable', 'Default', 'Key'].map(h => chalk.cyan(h)),
108
- style: { head: [], border: ['gray'] }
109
- });
110
-
111
- for (const col of schema) {
112
- table.push([
113
- chalk.white(col.name),
114
- chalk.gray(col.type),
115
- col.nullable ? chalk.green('YES') : chalk.red('NO'),
116
- col.default ? chalk.yellow(col.default) : chalk.gray('-'),
117
- col.primaryKey ? chalk.magenta('PRI') : chalk.gray('-')
118
- ]);
143
+ // JSON output
144
+ if (options.json) {
145
+ console.log(JSON.stringify({
146
+ table: tableName,
147
+ columns: schema.map(col => ({
148
+ name: col.name,
149
+ type: col.type,
150
+ nullable: col.nullable,
151
+ defaultValue: col.default,
152
+ primaryKey: col.primaryKey
153
+ }))
154
+ }, null, 2));
155
+ } else {
156
+ console.log(chalk.bold(`\nTable: ${tableName}`));
157
+ console.log(chalk.gray(''.repeat(80)));
158
+
159
+ const table = new Table({
160
+ head: ['Column', 'Type', 'Nullable', 'Default', 'Key'].map(h => chalk.cyan(h)),
161
+ style: { head: [], border: ['gray'] }
162
+ });
163
+
164
+ for (const col of schema) {
165
+ table.push([
166
+ chalk.white(col.name),
167
+ chalk.gray(col.type),
168
+ col.nullable ? chalk.green('YES') : chalk.red('NO'),
169
+ col.default ? chalk.yellow(col.default) : chalk.gray('-'),
170
+ col.primaryKey ? chalk.magenta('PRI') : chalk.gray('-')
171
+ ]);
172
+ }
173
+
174
+ console.log(table.toString());
175
+ console.log();
119
176
  }
120
177
 
121
- console.log(table.toString());
122
- console.log();
123
-
124
178
  } catch (err) {
125
- console.error(chalk.red(`✗ ${err.message}`));
179
+ if (options.json) {
180
+ console.log(JSON.stringify({ error: err.message }, null, 2));
181
+ } else {
182
+ console.error(chalk.red(`✗ ${err.message}`));
183
+ }
126
184
  process.exit(1);
127
185
  } finally {
128
186
  if (db) {
@@ -134,7 +192,7 @@ export async function describeTable(target, tableName) {
134
192
  /**
135
193
  * Dump database schema
136
194
  */
137
- export async function dumpSchema(target) {
195
+ export async function dumpSchema(target, options = {}) {
138
196
  let db;
139
197
 
140
198
  try {
@@ -144,18 +202,19 @@ export async function dumpSchema(target) {
144
202
  const info = await db.getInfo();
145
203
  const tables = await db.getTables();
146
204
 
147
- console.log(chalk.gray(`-- Database: ${info.database || info.path}`));
148
- console.log(chalk.gray(`-- Type: ${info.type}`));
149
- console.log(chalk.gray(`-- Generated: ${new Date().toISOString()}`));
150
- console.log();
205
+ let sqlOutput = '';
206
+
207
+ sqlOutput += `-- Database: ${info.database || info.path}\n`;
208
+ sqlOutput += `-- Type: ${info.type}\n`;
209
+ sqlOutput += `-- Generated: ${new Date().toISOString()}\n\n`;
151
210
 
152
211
  for (const table of tables) {
153
212
  if (table.type !== 'table') continue;
154
213
 
155
214
  const schema = await db.getTableSchema(table.name);
156
215
 
157
- console.log(chalk.cyan(`-- Table: ${table.name}`));
158
- console.log(chalk.gray(`-- Rows: ${table.rows}`));
216
+ sqlOutput += `-- Table: ${table.name}\n`;
217
+ sqlOutput += `-- Rows: ${table.rows}\n`;
159
218
 
160
219
  // This is a simplified dump - real implementations would generate proper DDL
161
220
  const cols = schema.map(c => {
@@ -165,13 +224,24 @@ export async function dumpSchema(target) {
165
224
  return def;
166
225
  });
167
226
 
168
- console.log(`CREATE TABLE ${table.name} (`);
169
- console.log(cols.join(',\n'));
170
- console.log(');\n');
227
+ sqlOutput += `CREATE TABLE ${table.name} (\n`;
228
+ sqlOutput += cols.join(',\n');
229
+ sqlOutput += '\n);\n\n';
230
+ }
231
+
232
+ // JSON output
233
+ if (options.json) {
234
+ console.log(JSON.stringify({ sql: sqlOutput }, null, 2));
235
+ } else {
236
+ console.log(sqlOutput);
171
237
  }
172
238
 
173
239
  } catch (err) {
174
- console.error(chalk.red(`✗ ${err.message}`));
240
+ if (options.json) {
241
+ console.log(JSON.stringify({ error: err.message }, null, 2));
242
+ } else {
243
+ console.error(chalk.red(`✗ ${err.message}`));
244
+ }
175
245
  process.exit(1);
176
246
  } finally {
177
247
  if (db) {