mwalajs 1.1.9 → 1.1.11
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/mwala copy 2.mjs +569 -0
- package/bin/mwala.mjs +292 -133
- package/build.txt +20 -0
- package/config/dbUtils.mjs +221 -1
- package/models/h.mjs +1 -0
- package/package.json +2 -2
- package/views/h.ejs +10 -0
- package/views/h.ejs.mjs +10 -0
- package/views/h.mjs +10 -0
- package/users.csv +0 -8
- package/users.json +0 -12
- package/users.sql +0 -19
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
|
7
|
+
import readlineSync from 'readline-sync';
|
|
8
|
+
|
|
9
|
+
//(baada ya imports zingine)
|
|
10
|
+
import pkg from "../package.json" with { type: "json" };
|
|
11
|
+
|
|
12
|
+
// const pkg = JSON.parse(
|
|
13
|
+
// fs.readFileSync(new URL('../package.json', import.meta.url))
|
|
14
|
+
// );
|
|
15
|
+
|
|
16
|
+
// Colors for better UX
|
|
17
|
+
const colors = {
|
|
18
|
+
reset: '\x1b[0m',
|
|
19
|
+
bright: '\x1b[1m',
|
|
20
|
+
dim: '\x1b[2m',
|
|
21
|
+
red: '\x1b[31m',
|
|
22
|
+
green: '\x1b[32m',
|
|
23
|
+
yellow: '\x1b[33m',
|
|
24
|
+
blue: '\x1b[34m',
|
|
25
|
+
magenta: '\x1b[35m',
|
|
26
|
+
cyan: '\x1b[36m',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const error = (msg) => console.error(`${colors.red}❌ ${msg}${colors.reset}`);
|
|
30
|
+
const success = (msg) => console.log(`${colors.green}✅ ${msg}${colors.reset}`);
|
|
31
|
+
const warn = (msg) => console.warn(`${colors.yellow}⚠️ ${msg}${colors.reset}`);
|
|
32
|
+
const info = (msg) => console.log(`${colors.cyan}${msg}${colors.reset}`);
|
|
33
|
+
|
|
34
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
35
|
+
const __dirname = path.dirname(__filename);
|
|
36
|
+
|
|
37
|
+
// ────────────────────────────────────────────────
|
|
38
|
+
// Dynamic imports with error handling
|
|
39
|
+
// ────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
let imports = {};
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const [
|
|
46
|
+
dbCfg,
|
|
47
|
+
migrations,
|
|
48
|
+
setup,
|
|
49
|
+
proj,
|
|
50
|
+
dbUtilsRaw,
|
|
51
|
+
] = await Promise.all([
|
|
52
|
+
import(pathToFileURL(path.join(__dirname, '../config/createdatabase.mjs')).href),
|
|
53
|
+
import(pathToFileURL(path.join(__dirname, '../runMigrations.mjs')).href),
|
|
54
|
+
import(pathToFileURL(path.join(__dirname, '../setupMwalajs.mjs')).href),
|
|
55
|
+
import(pathToFileURL(path.join(__dirname, '../createProject.mjs')).href),
|
|
56
|
+
import(pathToFileURL(path.join(__dirname, '../config/dbUtils.mjs')).href),
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
// 🔥 FIX: handle default export OR named export safely
|
|
60
|
+
const normalize = (mod) => mod?.default ?? mod ?? {};
|
|
61
|
+
|
|
62
|
+
imports = {
|
|
63
|
+
...normalize(dbCfg),
|
|
64
|
+
...normalize(migrations),
|
|
65
|
+
...normalize(setup),
|
|
66
|
+
...normalize(proj),
|
|
67
|
+
...normalize(dbUtilsRaw),
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
} catch (err) {
|
|
71
|
+
error(`Failed to load required modules:\n${err.stack || err.message}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const args = process.argv.slice(2);
|
|
76
|
+
const command = args[0]?.toLowerCase();
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
if (!command || command === 'help' || command === 'h') {
|
|
81
|
+
console.log(`
|
|
82
|
+
${colors.bright}╔════════════════════════════════════════════════════╗${colors.reset}
|
|
83
|
+
${colors.bright}║ MwalaJS CLI v${pkg.version} ║${colors.reset}
|
|
84
|
+
${colors.bright}╚════════════════════════════════════════════════════╝${colors.reset}
|
|
85
|
+
|
|
86
|
+
${colors.cyan}General Commands:${colors.reset}
|
|
87
|
+
mwala -v | --version → Show version
|
|
88
|
+
mwala help | h → Show this help
|
|
89
|
+
|
|
90
|
+
${colors.cyan}Project Management:${colors.reset}
|
|
91
|
+
mwala create-project → Create new project
|
|
92
|
+
mwala init → Initialize MwalaJS in current directory
|
|
93
|
+
|
|
94
|
+
${colors.cyan}Run Application:${colors.reset}
|
|
95
|
+
mwala serve | app.mjs → Start server (runs app.mjs)
|
|
96
|
+
|
|
97
|
+
${colors.cyan}Code Generation:${colors.reset}
|
|
98
|
+
mwala generate model <name>
|
|
99
|
+
mwala generate controller <name>
|
|
100
|
+
mwala generate route <name>
|
|
101
|
+
mwala generate view <name>
|
|
102
|
+
mwala generate midware <name>
|
|
103
|
+
|
|
104
|
+
${colors.cyan}══════════════════════════════════════════════════════${colors.reset}
|
|
105
|
+
${colors.cyan} DATABASE COMMANDS ${colors.reset}
|
|
106
|
+
${colors.cyan}══════════════════════════════════════════════════════${colors.reset}
|
|
107
|
+
|
|
108
|
+
${colors.blue}Setup & Config:${colors.reset}
|
|
109
|
+
mwala create-db → Create / connect database (interactive)
|
|
110
|
+
mwala db:config → Reconfigure database settings
|
|
111
|
+
|
|
112
|
+
${colors.blue}Table Management:${colors.reset}
|
|
113
|
+
mwala db:table list
|
|
114
|
+
mwala db:table create <name>
|
|
115
|
+
mwala db:table drop <name>
|
|
116
|
+
mwala db:table truncate <name>
|
|
117
|
+
mwala db:table rename <old> <new>
|
|
118
|
+
mwala db:table copy <src> <dest>
|
|
119
|
+
mwala db:table exists <name>
|
|
120
|
+
mwala db:table describe <name>
|
|
121
|
+
mwala db:table count <name>
|
|
122
|
+
|
|
123
|
+
${colors.blue}Migrations:${colors.reset}
|
|
124
|
+
mwala migrate all
|
|
125
|
+
mwala rollback last
|
|
126
|
+
mwala rollback all → ⚠️ drops all tables (dangerous)
|
|
127
|
+
|
|
128
|
+
${colors.blue}Data Import / Export:${colors.reset}
|
|
129
|
+
mwala db:import <file.csv|json|sql> <table>
|
|
130
|
+
mwala db:export <table> <file.csv|json|sql>
|
|
131
|
+
|
|
132
|
+
Examples:
|
|
133
|
+
mwala db:import users.csv users
|
|
134
|
+
mwala db:import users.json users
|
|
135
|
+
mwala db:import backup.sql users
|
|
136
|
+
|
|
137
|
+
${colors.blue}Backup & Restore:${colors.reset}
|
|
138
|
+
mwala db:seed <file.js>
|
|
139
|
+
mwala db:backup
|
|
140
|
+
mwala db:restore <file.sql>
|
|
141
|
+
|
|
142
|
+
${colors.blue}Maintenance & Stats:${colors.reset}
|
|
143
|
+
mwala db:size
|
|
144
|
+
mwala db:indexes <table>
|
|
145
|
+
mwala db:analyze <table> → PostgreSQL only
|
|
146
|
+
mwala db:reindex <table>
|
|
147
|
+
mwala db:vacuum
|
|
148
|
+
mwala db:connections
|
|
149
|
+
mwala db:kill-connections → ⚠️ Dangerous – admin only
|
|
150
|
+
mwala db:drop-all-tables → ⚠️ Extremely dangerous
|
|
151
|
+
|
|
152
|
+
${colors.yellow}Tips:${colors.reset}
|
|
153
|
+
- CSV/JSON = insert data only
|
|
154
|
+
- SQL = schema + data control
|
|
155
|
+
- Use --truncate for clean import (if supported)
|
|
156
|
+
|
|
157
|
+
Use: mwala <command> [options]
|
|
158
|
+
`);
|
|
159
|
+
|
|
160
|
+
process.exit(0);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ────────────────────────────────────────────────
|
|
164
|
+
// Helper – run async function with error handling
|
|
165
|
+
// ────────────────────────────────────────────────
|
|
166
|
+
|
|
167
|
+
async function runSafe(fn, successMsg = 'Operation completed', errorPrefix = 'Operation failed') {
|
|
168
|
+
try {
|
|
169
|
+
await fn();
|
|
170
|
+
success(successMsg);
|
|
171
|
+
} catch (err) {
|
|
172
|
+
error(`${errorPrefix}: ${err.message}`);
|
|
173
|
+
if (err.stack) console.error(colors.dim + err.stack + colors.reset);
|
|
174
|
+
process.exitCode = 1;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function runSafeSync(fn, successMsg = 'Operation completed', errorPrefix = 'Operation failed') {
|
|
179
|
+
try {
|
|
180
|
+
fn();
|
|
181
|
+
success(successMsg);
|
|
182
|
+
} catch (err) {
|
|
183
|
+
error(`${errorPrefix}: ${err.message}`);
|
|
184
|
+
if (err.stack) console.error(colors.dim + err.stack + colors.reset);
|
|
185
|
+
process.exitCode = 1;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
// ────────────────────────────────────────────────
|
|
191
|
+
// Command router
|
|
192
|
+
// ────────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
(async () => {
|
|
195
|
+
try {
|
|
196
|
+
switch (command) {
|
|
197
|
+
// case 'version':
|
|
198
|
+
// case '-v':
|
|
199
|
+
// case '--version':
|
|
200
|
+
// console.log('MwalaJS Version: 1.1.0');
|
|
201
|
+
// break;
|
|
202
|
+
|
|
203
|
+
case 'version':
|
|
204
|
+
case '-v':
|
|
205
|
+
case '--version':
|
|
206
|
+
console.log(`MwalaJS Version: ${pkg.version}`);
|
|
207
|
+
break;
|
|
208
|
+
|
|
209
|
+
case 'create-project':
|
|
210
|
+
runSafeSync(createProject, 'Project created successfully');
|
|
211
|
+
break;
|
|
212
|
+
|
|
213
|
+
case 'init':
|
|
214
|
+
runSafe(setupMwalajs, 'MwalaJS initialized successfully');
|
|
215
|
+
break;
|
|
216
|
+
|
|
217
|
+
case 'serve':
|
|
218
|
+
case 'app.mjs': {
|
|
219
|
+
const { spawn } = await import('child_process');
|
|
220
|
+
info('Starting application server...');
|
|
221
|
+
const child = spawn('node', ['app.mjs'], {
|
|
222
|
+
stdio: 'inherit',
|
|
223
|
+
cwd: process.cwd(),
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
process.on('SIGINT', () => child.kill('SIGINT'));
|
|
227
|
+
process.on('SIGTERM', () => child.kill('SIGTERM'));
|
|
228
|
+
|
|
229
|
+
child.on('exit', (code) => {
|
|
230
|
+
console.log(`Server exited with code ${code}`);
|
|
231
|
+
process.exit(code || 0);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
child.on('error', (err) => {
|
|
235
|
+
error(`Failed to start server: ${err.message}`);
|
|
236
|
+
process.exit(1);
|
|
237
|
+
});
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
case 'generate': {
|
|
242
|
+
const subCommand = args[1]?.toLowerCase();
|
|
243
|
+
const name = args[2];
|
|
244
|
+
|
|
245
|
+
if (!subCommand || !name) {
|
|
246
|
+
console.log(' Please specify both subCommand and name.');
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const paths = {
|
|
251
|
+
model: 'models',
|
|
252
|
+
controller: 'controllers',
|
|
253
|
+
route: 'routes',
|
|
254
|
+
view: 'views',
|
|
255
|
+
midware: 'middlewares'
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
if (!paths[subCommand]) {
|
|
259
|
+
console.log(` Invalid subCommand: ${subCommand}. Valid options are: ${Object.keys(paths).join(', ')}`);
|
|
260
|
+
process.exit(1);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// 🔥 dynamic extension
|
|
264
|
+
const ext = subCommand === 'view' ? '.ejs' : '.mjs';
|
|
265
|
+
const filePath = path.join(process.cwd(), paths[subCommand], `${name}${ext}`);
|
|
266
|
+
|
|
267
|
+
if (fs.existsSync(filePath)) {
|
|
268
|
+
console.log(` ${name} ${subCommand} already exists.`);
|
|
269
|
+
process.exit(1);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
let content = '';
|
|
273
|
+
|
|
274
|
+
switch (subCommand) {
|
|
275
|
+
case 'model':
|
|
276
|
+
content = `export const ${name}Model = {};`;
|
|
277
|
+
break;
|
|
278
|
+
|
|
279
|
+
case 'controller':
|
|
280
|
+
content = `export const ${name}Controller = { get${name}Page: (req, res) => { res.render('${name}', { title: '${name} Page' }); } };`;
|
|
281
|
+
break;
|
|
282
|
+
|
|
283
|
+
case 'route':
|
|
284
|
+
content = `import mwalajs from 'mwalajs';
|
|
285
|
+
import { ${name}Controller } from '../controllers/${name}Controller.mjs';
|
|
286
|
+
|
|
287
|
+
const router = mwalajs.Router();
|
|
288
|
+
|
|
289
|
+
router.get('/', ${name}Controller.get${name}Page);
|
|
290
|
+
|
|
291
|
+
export { router as ${name}Route };`;
|
|
292
|
+
break;
|
|
293
|
+
|
|
294
|
+
case 'view':
|
|
295
|
+
content = `<!DOCTYPE html>
|
|
296
|
+
<html lang='en'>
|
|
297
|
+
<head>
|
|
298
|
+
<meta charset='UTF-8'>
|
|
299
|
+
<title>${name} Page</title>
|
|
300
|
+
</head>
|
|
301
|
+
<body>
|
|
302
|
+
<h1>${name} View Page</h1>
|
|
303
|
+
</body>
|
|
304
|
+
</html>`;
|
|
305
|
+
break;
|
|
306
|
+
|
|
307
|
+
case 'midware':
|
|
308
|
+
content = `export const ${name} = (req, res, next) => { next(); };`;
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
313
|
+
fs.writeFileSync(filePath, content);
|
|
314
|
+
|
|
315
|
+
console.log(` ${name} ${subCommand} created successfully in ${paths[subCommand]}/`);
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ── Database Commands ─────────────────────────────────────
|
|
320
|
+
|
|
321
|
+
case 'create-db':
|
|
322
|
+
await runSafe(
|
|
323
|
+
() => getDbConnection().then(() => {}),
|
|
324
|
+
'Database connection established',
|
|
325
|
+
'Database setup failed'
|
|
326
|
+
);
|
|
327
|
+
break;
|
|
328
|
+
|
|
329
|
+
case 'db:config':
|
|
330
|
+
await runSafe(
|
|
331
|
+
() => getDbConnection(),
|
|
332
|
+
'Database configuration updated',
|
|
333
|
+
'Configuration failed'
|
|
334
|
+
);
|
|
335
|
+
break;
|
|
336
|
+
|
|
337
|
+
case 'db:table': {
|
|
338
|
+
const sub = args[1]?.toLowerCase();
|
|
339
|
+
const arg1 = args[2];
|
|
340
|
+
const arg2 = args[3];
|
|
341
|
+
|
|
342
|
+
if (!sub) {
|
|
343
|
+
error('Subcommand required. Use: mwala db:table list | create | drop | ...');
|
|
344
|
+
process.exit(1);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
switch (sub) {
|
|
348
|
+
case 'list':
|
|
349
|
+
await runSafe(listTables, 'Tables listed');
|
|
350
|
+
break;
|
|
351
|
+
|
|
352
|
+
case 'create':
|
|
353
|
+
if (!arg1) return error('Table name required: mwala db:table create <name>');
|
|
354
|
+
await runSafe(() => createTable(arg1), `Table ${arg1} created`);
|
|
355
|
+
break;
|
|
356
|
+
|
|
357
|
+
case 'drop':
|
|
358
|
+
if (!arg1) return error('Table name required');
|
|
359
|
+
await runSafe(() => dropTable(arg1), `Table ${arg1} dropped`);
|
|
360
|
+
break;
|
|
361
|
+
|
|
362
|
+
case 'truncate':
|
|
363
|
+
if (!arg1) return error('Table name required');
|
|
364
|
+
await runSafe(() => truncateTable(arg1), `Table ${arg1} truncated`);
|
|
365
|
+
break;
|
|
366
|
+
|
|
367
|
+
case 'rename':
|
|
368
|
+
if (!arg1 || !arg2) return error('Usage: rename <old> <new>');
|
|
369
|
+
await runSafe(() => renameTable(arg1, arg2), `Table renamed ${arg1} → ${arg2}`);
|
|
370
|
+
break;
|
|
371
|
+
|
|
372
|
+
case 'copy':
|
|
373
|
+
if (!arg1 || !arg2) return error('Usage: copy <source> <destination>');
|
|
374
|
+
await runSafe(() => copyTable(arg1, arg2), `Table copied ${arg1} → ${arg2}`);
|
|
375
|
+
break;
|
|
376
|
+
|
|
377
|
+
case 'exists':
|
|
378
|
+
if (!arg1) return error('Table name required');
|
|
379
|
+
await runSafe(() => checkTableExists(arg1), `Checked existence of ${arg1}`);
|
|
380
|
+
break;
|
|
381
|
+
|
|
382
|
+
case 'describe':
|
|
383
|
+
if (!arg1) return error('Table name required');
|
|
384
|
+
await runSafe(() => describeTable(arg1), `Description for ${arg1}`);
|
|
385
|
+
break;
|
|
386
|
+
|
|
387
|
+
case 'count':
|
|
388
|
+
if (!arg1) return error('Table name required');
|
|
389
|
+
await runSafe(() => countRows(arg1), `Row count for ${arg1}`);
|
|
390
|
+
break;
|
|
391
|
+
|
|
392
|
+
default:
|
|
393
|
+
error(`Unknown subcommand: ${sub}`);
|
|
394
|
+
error('Available: list, create, drop, truncate, rename, copy, exists, describe, count');
|
|
395
|
+
}
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
case 'migrate':
|
|
400
|
+
if (args[1] === 'all') {
|
|
401
|
+
await runSafe(migrateAll, 'All migrations applied');
|
|
402
|
+
} else {
|
|
403
|
+
error('Usage: mwala migrate all');
|
|
404
|
+
}
|
|
405
|
+
break;
|
|
406
|
+
|
|
407
|
+
case 'rollback':
|
|
408
|
+
if (args[1] === 'last') {
|
|
409
|
+
await runSafe(rollbackLastMigration, 'Last migration rolled back');
|
|
410
|
+
} else if (args[1] === 'all') {
|
|
411
|
+
if (readlineSync.keyInYNStrict('⚠️ Really rollback ALL migrations? This is dangerous!')) {
|
|
412
|
+
await runSafe(dropAllTables, 'All tables dropped (full rollback)');
|
|
413
|
+
}
|
|
414
|
+
} else {
|
|
415
|
+
error('Usage: mwala rollback last | all');
|
|
416
|
+
}
|
|
417
|
+
break;
|
|
418
|
+
|
|
419
|
+
case 'db:seed': {
|
|
420
|
+
const file = args[1];
|
|
421
|
+
|
|
422
|
+
if (!file) {
|
|
423
|
+
return error('Seed file required: mwala db:seed <file.js>');
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
await runSafe(
|
|
427
|
+
() => imports.seedDatabase(file),
|
|
428
|
+
`Seed executed: ${file}`
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
case 'db:backup': {
|
|
435
|
+
const name = args[1]; // 👈 HII NDIO IMPORTANT
|
|
436
|
+
|
|
437
|
+
await runSafe(
|
|
438
|
+
() => imports.backupDatabase({ name }),
|
|
439
|
+
'Database backup created'
|
|
440
|
+
);
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
case 'db:restore': {
|
|
445
|
+
const file = args[1];
|
|
446
|
+
|
|
447
|
+
if (!file) {
|
|
448
|
+
return error('Backup file required: mwala db:restore <file.sql>');
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
await runSafe(
|
|
452
|
+
() => imports.restoreDatabase(file),
|
|
453
|
+
`Database restored from ${file}`
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
break;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// case 'db:backup':
|
|
460
|
+
// await runSafe(backupDatabase, 'Database backup created');
|
|
461
|
+
// break;
|
|
462
|
+
|
|
463
|
+
// case 'db:restore':
|
|
464
|
+
// if (!args[1]) return error('Backup file required');
|
|
465
|
+
// await runSafe(() => restoreDatabase(args[1]), 'Database restored');
|
|
466
|
+
// break;
|
|
467
|
+
|
|
468
|
+
case 'db:export': {
|
|
469
|
+
if (!args[1] || !args[2]) {
|
|
470
|
+
return error('Usage: db:export <table> <file.(csv|json|sql)>');
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const table = args[1];
|
|
474
|
+
const file = args[2];
|
|
475
|
+
const ext = path.extname(file).toLowerCase();
|
|
476
|
+
|
|
477
|
+
if (ext === '.csv') {
|
|
478
|
+
await runSafe(() => exportTableToCsv(table, file), 'Table exported to CSV');
|
|
479
|
+
}
|
|
480
|
+
else if (ext === '.json') {
|
|
481
|
+
await runSafe(() => exportTableToJson(table, file), 'Table exported to JSON');
|
|
482
|
+
}
|
|
483
|
+
else if (ext === '.sql') {
|
|
484
|
+
await runSafe(() => exportTableToSql(table, file), 'Table exported to SQL');
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
error('Unsupported file type. Use .csv, .json, or .sql');
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
case 'db:import': {
|
|
495
|
+
if (!args[1] || !args[2]) {
|
|
496
|
+
return error('Usage: db:import <file.(csv|json|sql)> <table>');
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const file = args[1];
|
|
500
|
+
const table = args[2];
|
|
501
|
+
const ext = path.extname(file).toLowerCase();
|
|
502
|
+
|
|
503
|
+
if (ext === '.csv') {
|
|
504
|
+
await runSafe(() => importCsvToTable(file, table), 'CSV imported');
|
|
505
|
+
}
|
|
506
|
+
else if (ext === '.json') {
|
|
507
|
+
await runSafe(() => importJsonToTable(file, table), 'JSON imported');
|
|
508
|
+
}
|
|
509
|
+
else if (ext === '.sql') {
|
|
510
|
+
await runSafe(() => importSqlToTable(file), 'SQL imported');
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
error('Unsupported file type. Use .csv, .json, or .sql');
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
break;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
case 'db:size':
|
|
521
|
+
await runSafe(showDatabaseSize, 'Database size shown');
|
|
522
|
+
break;
|
|
523
|
+
|
|
524
|
+
case 'db:indexes':
|
|
525
|
+
if (!args[1]) return error('Table name required');
|
|
526
|
+
await runSafe(() => listIndexes(args[1]), `Indexes for ${args[1]}`);
|
|
527
|
+
break;
|
|
528
|
+
|
|
529
|
+
case 'db:analyze':
|
|
530
|
+
if (!args[1]) return error('Table name required');
|
|
531
|
+
await runSafe(() => analyzeTable(args[1]), `Table ${args[1]} analyzed`);
|
|
532
|
+
break;
|
|
533
|
+
|
|
534
|
+
case 'db:reindex':
|
|
535
|
+
if (!args[1]) return error('Table name required');
|
|
536
|
+
await runSafe(() => reindexTable(args[1]), `Table ${args[1]} reindexed`);
|
|
537
|
+
break;
|
|
538
|
+
|
|
539
|
+
case 'db:vacuum':
|
|
540
|
+
await runSafe(vacuumDatabase, 'Database vacuumed');
|
|
541
|
+
break;
|
|
542
|
+
|
|
543
|
+
case 'db:connections':
|
|
544
|
+
await runSafe(showConnections, 'Active connections shown');
|
|
545
|
+
break;
|
|
546
|
+
|
|
547
|
+
case 'db:kill-connections':
|
|
548
|
+
if (readlineSync.keyInYNStrict('⚠️ Kill ALL other database connections?')) {
|
|
549
|
+
await runSafe(killConnections, 'Other connections killed');
|
|
550
|
+
}
|
|
551
|
+
break;
|
|
552
|
+
|
|
553
|
+
case 'db:drop-all-tables':
|
|
554
|
+
if (readlineSync.keyInYNStrict('⚠️⚠️ THIS WILL DROP **ALL** TABLES! Continue?')) {
|
|
555
|
+
await runSafe(dropAllTables, 'All tables dropped (irreversible!)');
|
|
556
|
+
}
|
|
557
|
+
break;
|
|
558
|
+
|
|
559
|
+
default:
|
|
560
|
+
error(`Unknown command: ${command}`);
|
|
561
|
+
info('Run "mwala help" to see available commands.');
|
|
562
|
+
process.exit(1);
|
|
563
|
+
}
|
|
564
|
+
} catch (topLevelErr) {
|
|
565
|
+
error(`Unexpected CLI error: ${topLevelErr.message}`);
|
|
566
|
+
console.error(colors.dim + topLevelErr.stack + colors.reset);
|
|
567
|
+
process.exit(1);
|
|
568
|
+
}
|
|
569
|
+
})();
|