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.
- package/README.md +135 -2
- package/package.json +8 -2
- package/src/cli.js +125 -15
- package/src/commands/connections.js +63 -10
- package/src/commands/data.js +20 -7
- package/src/commands/migrate.js +142 -36
- package/src/commands/query.js +44 -14
- package/src/commands/schema.js +120 -50
- package/src/mcp.js +307 -0
- package/src/schema.js +594 -0
- package/src/update.js +72 -0
package/src/commands/migrate.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
60
|
-
|
|
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
|
-
|
|
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
|
-
|
|
102
|
-
|
|
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
|
-
|
|
153
|
-
|
|
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
|
|
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
|
-
|
|
170
|
-
|
|
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
|
-
|
|
177
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
309
|
+
if (!options.quiet && !options.json) {
|
|
310
|
+
console.log(chalk.green(` ✓ Applied`));
|
|
311
|
+
}
|
|
240
312
|
}
|
|
241
313
|
|
|
242
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) {
|
package/src/commands/query.js
CHANGED
|
@@ -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
|
-
//
|
|
31
|
-
if (
|
|
32
|
-
console.log(
|
|
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
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
53
|
-
|
|
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) {
|
package/src/commands/schema.js
CHANGED
|
@@ -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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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
|
-
|
|
104
|
+
if (!options.quiet) {
|
|
105
|
+
console.log(chalk.gray(`\n${tables.length} table(s)`));
|
|
106
|
+
}
|
|
75
107
|
|
|
76
108
|
} catch (err) {
|
|
77
|
-
|
|
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
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
158
|
-
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
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) {
|