drixio 1.1.5 → 1.1.7

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/dist/cli.js CHANGED
@@ -1,20 +1,332 @@
1
1
  #!/usr/bin/env node
2
- var Jt=Object.defineProperty;var E=(s,a)=>()=>(s&&(a=s(s=0)),a);var L=(s,a)=>{for(var t in a)Jt(s,t,{get:a[t],enumerable:!0})};import N from"picocolors";function Oe(){console.log(`
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
3
11
 
12
+ // src/cli/ui/logo.ts
13
+ import pc from "picocolors";
14
+ function printLogo() {
15
+ console.log(
16
+ "\n\n\n" + pc.bold(
17
+ l1(" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \n") + l2(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u2588\u2588\u2551 \u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\n") + l3(" \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\n") + l4(" \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\n") + l5(" \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u255D \u2588\u2588\u2557 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\n") + l6(" \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D")
18
+ )
19
+ );
20
+ }
21
+ function printCustomDashboard(title, rows) {
22
+ const terminalWidth = 74;
23
+ const dashes = "\u2550".repeat(terminalWidth - 2);
24
+ console.log(pc.cyan(`
25
+ \u2554${dashes}\u2557`));
26
+ const spacesNeeded = terminalWidth - 2 - title.length;
27
+ const leftSpace = Math.max(0, Math.floor(spacesNeeded / 2));
28
+ const rightSpace = Math.max(0, spacesNeeded - leftSpace);
29
+ const titleContent = " ".repeat(leftSpace) + pc.bold(pc.white(title)) + " ".repeat(rightSpace);
30
+ console.log(pc.cyan("\u2551") + titleContent + pc.cyan("\u2551"));
31
+ console.log(pc.cyan(`\u2560${"\u2550".repeat(terminalWidth - 2)}\u2563`));
32
+ const printLine = (label, value) => {
33
+ const paddedLabel = label.padEnd(12);
34
+ const lineContent = ` ${pc.bold(paddedLabel)}: ${value}`;
35
+ const rawText = ` ${paddedLabel}: ` + value.replace(/\x1b\[[0-9;]*m/g, "");
36
+ const padding = " ".repeat(Math.max(0, terminalWidth - 2 - rawText.length));
37
+ console.log(pc.cyan("\u2551") + lineContent + padding + pc.cyan("\u2551"));
38
+ };
39
+ for (const row of rows) {
40
+ printLine(row.label, row.value);
41
+ }
42
+ console.log(pc.cyan(`\u255A${"\u2550".repeat(terminalWidth - 2)}\u255D`));
43
+ console.log(
44
+ pc.dim(
45
+ " Use arrow keys to navigate \u2022 Enter to select \u2022 Ctrl+C to exit\n"
46
+ )
47
+ );
48
+ }
49
+ function printDashboard(dbConfig) {
50
+ const headerTitle = ` Lightweight Interactive TUI Database Client \u2022 v${process.env.npm_package_version} `;
51
+ let dbTypeVal = "None";
52
+ let targetVal = "-";
53
+ let sourceVal = "None";
54
+ if (dbConfig.type === "unknown") {
55
+ sourceVal = pc.dim(
56
+ "No configuration found. Please run check to configure."
57
+ );
58
+ } else {
59
+ dbTypeVal = dbConfig.type.toUpperCase();
60
+ targetVal = dbConfig.targetUrl;
61
+ sourceVal = dbConfig.source === ".env" ? "Loaded from project .env file" : dbConfig.source === "auto-detected" ? "Auto-detected local SQLite file" : "Manual connection config";
62
+ }
63
+ printCustomDashboard(headerTitle, [
64
+ { label: "Database", value: dbTypeVal },
65
+ {
66
+ label: "Target",
67
+ value: targetVal.length > 45 ? "..." + targetVal.slice(-42) : targetVal
68
+ },
69
+ { label: "Source", value: sourceVal },
70
+ {
71
+ label: "Working Dir",
72
+ value: process.cwd().length > 45 ? "..." + process.cwd().slice(-42) : process.cwd()
73
+ }
74
+ ]);
75
+ }
76
+ var rgb, l1, l2, l3, l4, l5, l6;
77
+ var init_logo = __esm({
78
+ "src/cli/ui/logo.ts"() {
79
+ "use strict";
80
+ rgb = (r, g, b) => (text) => `\x1B[38;2;${r};${g};${b}m${text}\x1B[39m`;
81
+ l1 = rgb(0, 255, 255);
82
+ l2 = rgb(0, 230, 245);
83
+ l3 = rgb(0, 210, 235);
84
+ l4 = rgb(0, 188, 220);
85
+ l5 = rgb(0, 168, 205);
86
+ l6 = rgb(0, 148, 188);
87
+ }
88
+ });
4
89
 
5
- `+N.bold(Xt(` \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557
6
- `)+Zt(` \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u2588\u2588\u2551 \u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557
7
- `)+ea(` \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
8
- `)+ta(` \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
9
- `)+aa(` \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u255D \u2588\u2588\u2557 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
10
- `)+oa(" \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D")))}function se(s,a){let r="\u2550".repeat(72);console.log(N.cyan(`
11
- \u2554${r}\u2557`));let o=72-s.length,e=Math.max(0,Math.floor(o/2)),n=Math.max(0,o-e),c=" ".repeat(e)+N.bold(N.white(s))+" ".repeat(n);console.log(N.cyan("\u2551")+c+N.cyan("\u2551")),console.log(N.cyan(`\u2560${"\u2550".repeat(72)}\u2563`));let i=(l,m)=>{let p=l.padEnd(12),u=` ${N.bold(p)}: ${m}`,d=` ${p}: `+m.replace(/\x1b\[[0-9;]*m/g,""),f=" ".repeat(Math.max(0,72-d.length));console.log(N.cyan("\u2551")+u+f+N.cyan("\u2551"))};for(let l of a)i(l.label,l.value);console.log(N.cyan(`\u255A${"\u2550".repeat(72)}\u255D`)),console.log(N.dim(` Use arrow keys to navigate \u2022 Enter to select \u2022 Ctrl+C to exit
12
- `))}function Ue(s){let a=" Lightweight Interactive TUI Database Client \u2022 v1.1.5 ",t="None",r="-",o="None";s.type==="unknown"?o=N.dim("No configuration found. Please run check to configure."):(t=s.type.toUpperCase(),r=s.targetUrl,o=s.source===".env"?"Loaded from project .env file":s.source==="auto-detected"?"Auto-detected local SQLite file":"Manual connection config"),se(a,[{label:"Database",value:t},{label:"Target",value:r.length>45?"..."+r.slice(-42):r},{label:"Source",value:o},{label:"Working Dir",value:process.cwd().length>45?"..."+process.cwd().slice(-42):process.cwd()}])}var J,Xt,Zt,ea,ta,aa,oa,Se=E(()=>{"use strict";J=(s,a,t)=>r=>`\x1B[38;2;${s};${a};${t}m${r}\x1B[39m`,Xt=J(0,255,255),Zt=J(0,230,245),ea=J(0,210,235),ta=J(0,188,220),aa=J(0,168,205),oa=J(0,148,188)});var $e,We=E(()=>{"use strict";$e=class{dbPath;db=null;constructor(a){this.dbPath=a}async getDb(){if(!this.db){if(!(await import("fs")).existsSync(this.dbPath))throw new Error(`Failed to found database file at: ${this.dbPath}`);let t=await import("sqlite");this.db=new t.DatabaseSync(this.dbPath)}return this.db}quoteIdentifier(a){return`"${a.replace(/"/g,'""')}"`}async getTables(){return(await this.getDb()).prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name;").all().map(r=>r.name)}async getSchema(a){let t=await this.getDb(),r=this.quoteIdentifier(a),e=t.prepare(`PRAGMA table_info(${r})`).all(),c=t.prepare(`PRAGMA foreign_key_list(${r})`).all();return e.map(i=>{let l=c.find(m=>m.from===i.name);return{name:i.name,type:i.type,isPk:i.pk>0,nullable:i.notnull===0,defaultValue:i.dflt_value!=null?String(i.dflt_value):void 0,fkTarget:l?{table:l.table,column:l.to}:void 0}})}async getIndexes(a){let t=await this.getDb(),r=this.quoteIdentifier(a),e=t.prepare(`PRAGMA index_list(${r})`).all(),n=[];for(let c of e){if(c.origin==="pk")continue;let l=t.prepare(`PRAGMA index_info(${this.quoteIdentifier(c.name)})`).all();n.push({name:c.name,columns:l.map(m=>m.name),isUnique:c.unique>0})}return n}async getData(a,t=50,r=0,o,e){let n=await this.getDb(),i=(await this.getSchema(a)).map(u=>u.name),l=`SELECT * FROM ${this.quoteIdentifier(a)}`;o&&(l+=` WHERE ${o}`),e&&(l+=` ORDER BY ${this.quoteIdentifier(e.col)} ${e.asc?"ASC":"DESC"}`),l+=` LIMIT ${t} OFFSET ${r}`;let p=n.prepare(l).all();return{columns:i,rows:p}}async query(a){let t=await this.getDb(),r=a.trim().toUpperCase();if(r.startsWith("SELECT")||r.startsWith("PRAGMA")||r.startsWith("EXPLAIN")||r.startsWith("WITH")){let n=t.prepare(a).all(),c=[];return n.length>0&&(c=Object.keys(n[0])),{columns:c,rows:n}}else return t.prepare(a).run(),{columns:["Result"],rows:[{Result:"Success"}]}}async executeSql(a){(await this.getDb()).exec(a)}async close(){this.db&&(this.db.close(),this.db=null)}async insert(a,t){if(t.length===0)return;let r=await this.getDb(),o=Object.keys(t[0]),e=o.map(l=>this.quoteIdentifier(l)).join(", "),n=o.map(()=>"?").join(", "),c=`INSERT INTO ${this.quoteIdentifier(a)} (${e}) VALUES (${n})`,i=r.prepare(c);for(let l of t){let m=o.map(p=>l[p]);i.run(...m)}}}});import na from"pg";var xe,je=E(()=>{"use strict";xe=class{client;connected=!1;constructor(a){this.client=new na.Client({connectionString:a})}async connectIfNecessary(){this.connected||(await this.client.connect(),this.connected=!0)}quoteIdentifier(a){return`"${a.replace(/"/g,'""')}"`}async getTables(){return await this.connectIfNecessary(),(await this.client.query(`
90
+ // src/adapters/sqlite.ts
91
+ var SqliteAdapter;
92
+ var init_sqlite = __esm({
93
+ "src/adapters/sqlite.ts"() {
94
+ "use strict";
95
+ SqliteAdapter = class {
96
+ dbPath;
97
+ db = null;
98
+ constructor(connection) {
99
+ this.dbPath = connection;
100
+ }
101
+ async getDb() {
102
+ if (!this.db) {
103
+ let modFs = "node:fs";
104
+ const fs12 = await import(modFs);
105
+ if (!fs12.existsSync(this.dbPath)) {
106
+ throw new Error(`Failed to found database file at: ${this.dbPath}`);
107
+ }
108
+ let mod = "node:sqlite";
109
+ const sqlite = await import(mod);
110
+ this.db = new sqlite.DatabaseSync(this.dbPath);
111
+ }
112
+ return this.db;
113
+ }
114
+ quoteIdentifier(name) {
115
+ return `"${name.replace(/"/g, '""')}"`;
116
+ }
117
+ async getStatus() {
118
+ try {
119
+ const db = await this.getDb();
120
+ const vQuery = db.prepare("SELECT sqlite_version() as v");
121
+ const vRow = vQuery.get();
122
+ let modFs = "node:fs";
123
+ const fs12 = await import(modFs);
124
+ const stats = fs12.statSync(this.dbPath);
125
+ let modPath = "node:path";
126
+ const path13 = await import(modPath);
127
+ const dbName = path13.basename(this.dbPath);
128
+ return {
129
+ status: "connected",
130
+ dbType: "sqlite",
131
+ dbName,
132
+ version: vRow?.v,
133
+ activeConnections: 1,
134
+ // SQLite is single file, essentially 1 active connection for the app
135
+ sizeBytes: stats.size,
136
+ uptime: process.uptime()
137
+ };
138
+ } catch (e) {
139
+ return {
140
+ status: "error",
141
+ dbType: "sqlite"
142
+ };
143
+ }
144
+ }
145
+ async getTables() {
146
+ const db = await this.getDb();
147
+ const results = db.prepare(
148
+ "SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name;"
149
+ ).all();
150
+ return results.map((row) => row.name);
151
+ }
152
+ async getSchema(tableName) {
153
+ const db = await this.getDb();
154
+ const quoted = this.quoteIdentifier(tableName);
155
+ const pragmaQuery = db.prepare(`PRAGMA table_info(${quoted})`);
156
+ const info = pragmaQuery.all();
157
+ const fkQuery = db.prepare(`PRAGMA foreign_key_list(${quoted})`);
158
+ const fks = fkQuery.all();
159
+ return info.map((col) => {
160
+ const fk = fks.find((f) => f.from === col.name);
161
+ return {
162
+ name: col.name,
163
+ type: col.type,
164
+ isPk: col.pk > 0,
165
+ nullable: col.notnull === 0,
166
+ defaultValue: col.dflt_value != null ? String(col.dflt_value) : void 0,
167
+ fkTarget: fk ? { table: fk.table, column: fk.to } : void 0
168
+ };
169
+ });
170
+ }
171
+ async getIndexes(tableName) {
172
+ const db = await this.getDb();
173
+ const quoted = this.quoteIdentifier(tableName);
174
+ const indexListQuery = db.prepare(`PRAGMA index_list(${quoted})`);
175
+ const indexList = indexListQuery.all();
176
+ const indexes = [];
177
+ for (const idx of indexList) {
178
+ if (idx.origin === "pk") continue;
179
+ const indexInfoQuery = db.prepare(`PRAGMA index_info(${this.quoteIdentifier(idx.name)})`);
180
+ const columns = indexInfoQuery.all();
181
+ indexes.push({
182
+ name: idx.name,
183
+ columns: columns.map((c) => c.name),
184
+ isUnique: idx.unique > 0
185
+ });
186
+ }
187
+ return indexes;
188
+ }
189
+ async getData(tableName, limit = 50, offset = 0, whereClause, orderBy) {
190
+ const db = await this.getDb();
191
+ const schema = await this.getSchema(tableName);
192
+ const columns = schema.map((col) => col.name);
193
+ let sql = `SELECT * FROM ${this.quoteIdentifier(tableName)}`;
194
+ if (whereClause) {
195
+ sql += ` WHERE ${whereClause}`;
196
+ }
197
+ if (orderBy) {
198
+ sql += ` ORDER BY ${this.quoteIdentifier(orderBy.col)} ${orderBy.asc ? "ASC" : "DESC"}`;
199
+ }
200
+ sql += ` LIMIT ${limit} OFFSET ${offset}`;
201
+ const dataQuery = db.prepare(sql);
202
+ const rows = dataQuery.all();
203
+ return { columns, rows };
204
+ }
205
+ async query(sql) {
206
+ const db = await this.getDb();
207
+ const trimmed = sql.trim().toUpperCase();
208
+ const isRead = trimmed.startsWith("SELECT") || trimmed.startsWith("PRAGMA") || trimmed.startsWith("EXPLAIN") || trimmed.startsWith("WITH");
209
+ if (isRead) {
210
+ const query = db.prepare(sql);
211
+ const rows = query.all();
212
+ let columns = [];
213
+ if (rows.length > 0) {
214
+ columns = Object.keys(rows[0]);
215
+ }
216
+ return { columns, rows };
217
+ } else {
218
+ const query = db.prepare(sql);
219
+ query.run();
220
+ return { columns: ["Result"], rows: [{ Result: "Success" }] };
221
+ }
222
+ }
223
+ async executeSql(sql) {
224
+ const db = await this.getDb();
225
+ db.exec(sql);
226
+ }
227
+ async close() {
228
+ if (this.db) {
229
+ this.db.close();
230
+ this.db = null;
231
+ }
232
+ }
233
+ async insert(tableName, rows) {
234
+ if (rows.length === 0) return;
235
+ const db = await this.getDb();
236
+ const cols = Object.keys(rows[0]);
237
+ const colsQuoted = cols.map((c) => this.quoteIdentifier(c)).join(", ");
238
+ const placeholders = cols.map(() => "?").join(", ");
239
+ const sql = `INSERT INTO ${this.quoteIdentifier(tableName)} (${colsQuoted}) VALUES (${placeholders})`;
240
+ const stmt = db.prepare(sql);
241
+ for (const row of rows) {
242
+ const values = cols.map((c) => row[c]);
243
+ stmt.run(...values);
244
+ }
245
+ }
246
+ };
247
+ }
248
+ });
249
+
250
+ // src/adapters/postgres.ts
251
+ import pg from "pg";
252
+ var PostgresAdapter;
253
+ var init_postgres = __esm({
254
+ "src/adapters/postgres.ts"() {
255
+ "use strict";
256
+ PostgresAdapter = class {
257
+ client;
258
+ connected = false;
259
+ constructor(connection) {
260
+ this.client = new pg.Client({
261
+ connectionString: connection
262
+ });
263
+ }
264
+ async connectIfNecessary() {
265
+ if (!this.connected) {
266
+ await this.client.connect();
267
+ this.connected = true;
268
+ }
269
+ }
270
+ quoteIdentifier(name) {
271
+ return `"${name.replace(/"/g, '""')}"`;
272
+ }
273
+ async getStatus() {
274
+ try {
275
+ await this.connectIfNecessary();
276
+ const dbRes = await this.client.query("SELECT current_database() as db, version() as version");
277
+ const dbName = dbRes.rows[0]?.db;
278
+ const version = dbRes.rows[0]?.version?.split(" ")[1] || dbRes.rows[0]?.version;
279
+ let activeConnections = 0;
280
+ let transactions = 0;
281
+ let uptime = 0;
282
+ try {
283
+ const uptimeRes = await this.client.query("SELECT EXTRACT(EPOCH FROM (now() - pg_postmaster_start_time())) as uptime");
284
+ uptime = parseInt(uptimeRes.rows[0]?.uptime || "0", 10);
285
+ } catch (e) {
286
+ }
287
+ try {
288
+ const statRes = await this.client.query("SELECT sum(numbackends) as conns, sum(xact_commit + xact_rollback) as txs FROM pg_stat_database");
289
+ activeConnections = parseInt(statRes.rows[0]?.conns || "0", 10);
290
+ transactions = parseInt(statRes.rows[0]?.txs || "0", 10);
291
+ } catch (e) {
292
+ }
293
+ let sizeBytes = 0;
294
+ try {
295
+ const sizeRes = await this.client.query("SELECT pg_database_size(current_database()) as size");
296
+ sizeBytes = parseInt(sizeRes.rows[0]?.size || "0", 10);
297
+ } catch (e) {
298
+ }
299
+ return {
300
+ status: "connected",
301
+ dbType: "postgres",
302
+ dbName,
303
+ version,
304
+ activeConnections,
305
+ sizeBytes,
306
+ transactions,
307
+ uptime
308
+ };
309
+ } catch (e) {
310
+ return {
311
+ status: "error",
312
+ dbType: "postgres"
313
+ };
314
+ }
315
+ }
316
+ async getTables() {
317
+ await this.connectIfNecessary();
318
+ const query = `
13
319
  SELECT tablename
14
320
  FROM pg_catalog.pg_tables
15
321
  WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'
16
322
  ORDER BY tablename;
17
- `)).rows.map(r=>r.tablename)}async getSchema(a){return await this.connectIfNecessary(),(await this.client.query(`
323
+ `;
324
+ const res = await this.client.query(query);
325
+ return res.rows.map((row) => row.tablename);
326
+ }
327
+ async getSchema(tableName) {
328
+ await this.connectIfNecessary();
329
+ const query = `
18
330
  SELECT c.column_name, c.data_type, c.is_nullable, c.column_default,
19
331
  (SELECT count(*)
20
332
  FROM information_schema.key_column_usage kcu
@@ -22,7 +334,8 @@ var Jt=Object.defineProperty;var E=(s,a)=>()=>(s&&(a=s(s=0)),a);var L=(s,a)=>{fo
22
334
  ON kcu.constraint_name = tc.constraint_name
23
335
  WHERE tc.constraint_type = 'PRIMARY KEY'
24
336
  AND kcu.table_name = c.table_name
25
- AND kcu.column_name = c.column_name) as is_pk,
337
+ AND kcu.column_name = c.column_name
338
+ AND kcu.table_schema = c.table_schema) as is_pk,
26
339
  (SELECT ccu.table_name || '.' || ccu.column_name
27
340
  FROM information_schema.table_constraints tc
28
341
  JOIN information_schema.key_column_usage kcu
@@ -31,11 +344,33 @@ var Jt=Object.defineProperty;var E=(s,a)=>()=>(s&&(a=s(s=0)),a);var L=(s,a)=>{fo
31
344
  ON ccu.constraint_name = tc.constraint_name
32
345
  WHERE tc.constraint_type = 'FOREIGN KEY'
33
346
  AND tc.table_name = c.table_name
347
+ AND tc.table_schema = c.table_schema
34
348
  AND kcu.column_name = c.column_name
35
349
  LIMIT 1) as fk_target
36
350
  FROM information_schema.columns c
37
- WHERE c.table_name = $1;
38
- `,[a])).rows.map(o=>{let e;if(o.fk_target){let n=o.fk_target.split(".");e={table:n[0],column:n[1]}}return{name:o.column_name,type:o.data_type,isPk:parseInt(o.is_pk)>0,nullable:o.is_nullable==="YES",defaultValue:o.column_default!=null?String(o.column_default):void 0,fkTarget:e}})}async getIndexes(a){await this.connectIfNecessary();let o=(await this.client.query(`
351
+ WHERE c.table_name = $1 AND c.table_schema = 'public'
352
+ ORDER BY c.ordinal_position;
353
+ `;
354
+ const res = await this.client.query(query, [tableName]);
355
+ return res.rows.map((col) => {
356
+ let fkTarget;
357
+ if (col.fk_target) {
358
+ const parts = col.fk_target.split(".");
359
+ fkTarget = { table: parts[0], column: parts[1] };
360
+ }
361
+ return {
362
+ name: col.column_name,
363
+ type: col.data_type,
364
+ isPk: parseInt(col.is_pk) > 0,
365
+ nullable: col.is_nullable === "YES",
366
+ defaultValue: col.column_default != null ? String(col.column_default) : void 0,
367
+ fkTarget
368
+ };
369
+ });
370
+ }
371
+ async getIndexes(tableName) {
372
+ await this.connectIfNecessary();
373
+ const query = `
39
374
  SELECT
40
375
  i.relname as index_name,
41
376
  a.attname as column_name,
@@ -55,144 +390,3627 @@ var Jt=Object.defineProperty;var E=(s,a)=>()=>(s&&(a=s(s=0)),a);var L=(s,a)=>{fo
55
390
  AND t.relname = $1
56
391
  ORDER BY
57
392
  i.relname, a.attnum;
58
- `,[a])).rows,e=new Map;for(let n of o){if(n.is_primary)continue;let c=n.index_name;e.has(c)||e.set(c,{name:c,columns:[],isUnique:n.is_unique}),e.get(c).columns.push(n.column_name)}return Array.from(e.values())}async getData(a,t=50,r=0,o,e){await this.connectIfNecessary();let c=(await this.getSchema(a)).map(m=>m.name),i=`SELECT * FROM ${this.quoteIdentifier(a)}`;o&&(i+=` WHERE ${o}`),e&&(i+=` ORDER BY ${this.quoteIdentifier(e.col)} ${e.asc?"ASC":"DESC"}`),i+=" LIMIT $1 OFFSET $2";let l=await this.client.query(i,[t,r]);return{columns:c,rows:l.rows}}async query(a){await this.connectIfNecessary();let t=await this.client.query(a),r=[];return t.fields&&(r=t.fields.map(o=>o.name)),{columns:r,rows:t.rows||[]}}async executeSql(a){await this.connectIfNecessary(),await this.client.query(a)}async close(){this.connected&&(await this.client.end(),this.connected=!1)}async insert(a,t){if(t.length===0)return;await this.connectIfNecessary();let r=Object.keys(t[0]),o=r.map(e=>this.quoteIdentifier(e)).join(", ");for(let e of t){let n=r.map((l,m)=>`$${m+1}`).join(", "),c=`INSERT INTO ${this.quoteIdentifier(a)} (${o}) VALUES (${n})`,i=r.map(l=>e[l]);await this.client.query(c,i)}}}});import sa from"mysql2/promise";var Te,Qe=E(()=>{"use strict";Te=class{connection;pool=null;constructor(a){this.connection=a}async getPool(){return this.pool||(this.pool=sa.createPool(this.connection),this.pool.on("connection",a=>{a.query("SET SESSION sql_mode = 'ANSI_QUOTES'")})),this.pool}quoteIdentifier(a){return`\`${a.replace(/`/g,"``")}\``}async getTables(){let a=await this.getPool(),[t]=await a.query("SHOW TABLES;");return t.map(r=>Object.values(r)[0])}async getSchema(a){let t=await this.getPool(),[r]=await t.query(`SHOW COLUMNS FROM ${this.quoteIdentifier(a)}`),o=r,[e]=await t.query(`
393
+ `;
394
+ const res = await this.client.query(query, [tableName]);
395
+ const rows = res.rows;
396
+ const indexMap = /* @__PURE__ */ new Map();
397
+ for (const row of rows) {
398
+ if (row.is_primary) continue;
399
+ const idxName = row.index_name;
400
+ if (!indexMap.has(idxName)) {
401
+ indexMap.set(idxName, {
402
+ name: idxName,
403
+ columns: [],
404
+ isUnique: row.is_unique
405
+ });
406
+ }
407
+ indexMap.get(idxName).columns.push(row.column_name);
408
+ }
409
+ return Array.from(indexMap.values());
410
+ }
411
+ async getData(tableName, limit = 50, offset = 0, whereClause, orderBy) {
412
+ await this.connectIfNecessary();
413
+ const schema = await this.getSchema(tableName);
414
+ const columns = schema.map((col) => col.name);
415
+ let sql = `SELECT * FROM ${this.quoteIdentifier(tableName)}`;
416
+ if (whereClause) {
417
+ sql += ` WHERE ${whereClause}`;
418
+ }
419
+ if (orderBy) {
420
+ sql += ` ORDER BY ${this.quoteIdentifier(orderBy.col)} ${orderBy.asc ? "ASC" : "DESC"}`;
421
+ }
422
+ sql += ` LIMIT $1 OFFSET $2`;
423
+ const res = await this.client.query(sql, [limit, offset]);
424
+ return { columns, rows: res.rows };
425
+ }
426
+ async query(sql) {
427
+ await this.connectIfNecessary();
428
+ const res = await this.client.query(sql);
429
+ let columns = [];
430
+ if (res.fields) {
431
+ columns = res.fields.map((f) => f.name);
432
+ }
433
+ return { columns, rows: res.rows || [] };
434
+ }
435
+ async executeSql(sql) {
436
+ await this.connectIfNecessary();
437
+ await this.client.query(sql);
438
+ }
439
+ async close() {
440
+ if (this.connected) {
441
+ await this.client.end();
442
+ this.connected = false;
443
+ }
444
+ }
445
+ async insert(tableName, rows) {
446
+ if (rows.length === 0) return;
447
+ await this.connectIfNecessary();
448
+ const cols = Object.keys(rows[0]);
449
+ const colsQuoted = cols.map((c) => this.quoteIdentifier(c)).join(", ");
450
+ for (const row of rows) {
451
+ const placeholders = cols.map((_, i) => `$${i + 1}`).join(", ");
452
+ const sql = `INSERT INTO ${this.quoteIdentifier(tableName)} (${colsQuoted}) VALUES (${placeholders})`;
453
+ const values = cols.map((c) => row[c]);
454
+ await this.client.query(sql, values);
455
+ }
456
+ }
457
+ };
458
+ }
459
+ });
460
+
461
+ // src/adapters/mysql.ts
462
+ import mysql from "mysql2/promise";
463
+ var MysqlAdapter;
464
+ var init_mysql = __esm({
465
+ "src/adapters/mysql.ts"() {
466
+ "use strict";
467
+ MysqlAdapter = class {
468
+ connection;
469
+ pool = null;
470
+ constructor(connection) {
471
+ this.connection = connection;
472
+ }
473
+ async getPool() {
474
+ if (!this.pool) {
475
+ this.pool = mysql.createPool(this.connection);
476
+ this.pool.on("connection", (connection) => {
477
+ connection.query("SET SESSION sql_mode = 'ANSI_QUOTES'");
478
+ });
479
+ }
480
+ return this.pool;
481
+ }
482
+ quoteIdentifier(name) {
483
+ return `\`${name.replace(/`/g, "``")}\``;
484
+ }
485
+ async getStatus() {
486
+ try {
487
+ const pool = await this.getPool();
488
+ const [dbRow] = await pool.query("SELECT DATABASE() as db, VERSION() as version");
489
+ const dbName = dbRow[0]?.db;
490
+ const version = dbRow[0]?.version;
491
+ const [statusRows] = await pool.query("SHOW GLOBAL STATUS WHERE Variable_name IN ('Threads_connected', 'Queries', 'Uptime')");
492
+ let activeConnections = 0;
493
+ let queries = 0;
494
+ let uptime = 0;
495
+ for (const row of statusRows) {
496
+ if (row.Variable_name === "Threads_connected") activeConnections = parseInt(row.Value, 10);
497
+ if (row.Variable_name === "Queries") queries = parseInt(row.Value, 10);
498
+ if (row.Variable_name === "Uptime") uptime = parseInt(row.Value, 10);
499
+ }
500
+ let sizeBytes = 0;
501
+ if (dbName) {
502
+ const [sizeRow] = await pool.query("SELECT SUM(data_length + index_length) as size FROM information_schema.TABLES WHERE table_schema = ?", [dbName]);
503
+ sizeBytes = parseInt(sizeRow[0]?.size || "0", 10);
504
+ }
505
+ return {
506
+ status: "connected",
507
+ dbType: "mysql",
508
+ dbName,
509
+ version,
510
+ activeConnections,
511
+ sizeBytes,
512
+ queries,
513
+ uptime
514
+ };
515
+ } catch (e) {
516
+ return {
517
+ status: "error",
518
+ dbType: "mysql"
519
+ };
520
+ }
521
+ }
522
+ async getTables() {
523
+ const pool = await this.getPool();
524
+ const [rows] = await pool.query("SHOW TABLES;");
525
+ return rows.map((row) => Object.values(row)[0]);
526
+ }
527
+ async getSchema(tableName) {
528
+ const pool = await this.getPool();
529
+ const [rows] = await pool.query(
530
+ `SHOW COLUMNS FROM ${this.quoteIdentifier(tableName)}`
531
+ );
532
+ const columns = rows;
533
+ const [fkRows] = await pool.query(
534
+ `
59
535
  SELECT COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME
60
536
  FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
61
537
  WHERE TABLE_SCHEMA = DATABASE()
62
538
  AND TABLE_NAME = ?
63
539
  AND REFERENCED_TABLE_NAME IS NOT NULL
64
- `,[a]),n=e;return o.map(c=>{let i=n.find(p=>p.COLUMN_NAME===c.Field),l;if(c.Type.toUpperCase().startsWith("ENUM")){let p=c.Type.match(/enum\((.*?)\)/i);p&&(l=p[1].split(",").map(u=>u.trim().replace(/^'|'$/g,"")))}return{name:c.Field,type:c.Type,isPk:c.Key==="PRI",nullable:c.Null==="YES",defaultValue:c.Default!=null?String(c.Default):void 0,enumValues:l,fkTarget:i?{table:i.REFERENCED_TABLE_NAME,column:i.REFERENCED_COLUMN_NAME}:void 0}})}async getIndexes(a){let t=await this.getPool(),[r]=await t.query(`SHOW INDEX FROM ${this.quoteIdentifier(a)}`),o=new Map;for(let e of r){if(e.Key_name==="PRIMARY")continue;let n=e.Key_name;o.has(n)||o.set(n,{name:n,columns:[],isUnique:e.Non_unique===0}),o.get(n).columns.push(e.Column_name)}return Array.from(o.values())}async getData(a,t=50,r=0,o,e){let n=await this.getPool(),i=(await this.getSchema(a)).map(p=>p.name),l=`SELECT * FROM ${this.quoteIdentifier(a)}`;o&&(l+=` WHERE ${o}`),e&&(l+=` ORDER BY ${this.quoteIdentifier(e.col)} ${e.asc?"ASC":"DESC"}`),l+=` LIMIT ${t} OFFSET ${r}`;let[m]=await n.query(l);return{columns:i,rows:m}}async query(a){let t=await this.getPool(),[r,o]=await t.query(a),e=[],n=[];return o&&Array.isArray(o)?(e=o.map(c=>c.name),n=r):(e=["Result"],n=[{Result:"Success",AffectedRows:r.affectedRows}]),{columns:e,rows:n}}async executeSql(a){await(await this.getPool()).query(a)}async close(){this.pool&&(await this.pool.end(),this.pool=null)}async insert(a,t){if(t.length===0)return;let r=await this.getPool(),o=Object.keys(t[0]),e=o.map(i=>this.quoteIdentifier(i)).join(", "),n=o.map(()=>"?").join(", "),c=`INSERT INTO ${this.quoteIdentifier(a)} (${e}) VALUES (${n})`;for(let i of t){let l=o.map(m=>i[m]);await r.query(c,l)}}}});var Ve={};L(Ve,{createDBAdapter:()=>h});function h(s){switch(s.type){case"sqlite":return new $e(s.targetUrl);case"postgres":return new xe(s.targetUrl);case"mysql":return new Te(s.targetUrl);default:throw new Error(`Unsupported database type: ${s.type}`)}}var A=E(()=>{"use strict";We();je();Qe()});function j(s){switch(s){case"sqlite":return new De;case"postgres":return new Le;case"mysql":return new Ne;default:return new De}}var De,Le,Ne,Ae=E(()=>{"use strict";De=class{quoteIdentifier(a){return`"${a}"`}escapeString(a){return a.replace(/'/g,"''")}buildCreateTable(a,t){let r=t.map(e=>{let n="";return e.type==="Integer"&&(n="INTEGER"),e.type==="Text"&&(n="TEXT"),e.type==="Boolean"&&(n="BOOLEAN"),e.type==="Decimal"&&(n="REAL"),e.type==="DateTime"&&(n="DATETIME"),e.type==="Enum"&&e.enumValues&&(n=`TEXT CHECK(${this.quoteIdentifier(e.name)} IN (${e.enumValues.map(c=>`'${this.escapeString(c)}'`).join(", ")}))`),e.isPk&&e.type==="Integer"?n="INTEGER PRIMARY KEY AUTOINCREMENT":e.isPk?n+=" PRIMARY KEY":e.nullable||(n+=" NOT NULL"),e.defaultValue&&e.defaultValue!=="AutoInc"&&!e.defaultValue.startsWith("FK ->")&&(e.defaultValue==="Timestamp"?n+=" DEFAULT CURRENT_TIMESTAMP":n+=` DEFAULT ${e.defaultValue}`),` ${this.quoteIdentifier(e.name)} ${n}`}),o=t.filter(e=>e.fkTarget).map(e=>` FOREIGN KEY (${this.quoteIdentifier(e.name)}) REFERENCES ${this.quoteIdentifier(e.fkTarget.table)}(${this.quoteIdentifier(e.fkTarget.column)})`);return o.length>0&&r.push(...o),`CREATE TABLE ${this.quoteIdentifier(a)} (
65
- ${r.join(`,
66
- `)}
67
- );`}},Le=class{quoteIdentifier(a){return`"${a}"`}escapeString(a){return a.replace(/'/g,"''")}buildCreateTable(a,t){let r=t.map(e=>{let n="";return e.isPk&&e.type==="Integer"?n="SERIAL PRIMARY KEY":(e.type==="Integer"&&(n="INTEGER"),e.type==="Text"&&(n="TEXT"),e.type==="Boolean"&&(n="BOOLEAN"),e.type==="Decimal"&&(n="NUMERIC"),e.type==="DateTime"&&(n="TIMESTAMP"),e.type==="Enum"&&e.enumValues&&(n=`VARCHAR(255) CHECK(${this.quoteIdentifier(e.name)} IN (${e.enumValues.map(c=>`'${this.escapeString(c)}'`).join(", ")}))`),e.isPk&&(n+=" PRIMARY KEY"),!e.nullable&&!e.isPk&&(n+=" NOT NULL")),e.defaultValue&&e.defaultValue!=="AutoInc"&&!e.defaultValue.startsWith("FK ->")&&(e.defaultValue==="Timestamp"?n+=" DEFAULT CURRENT_TIMESTAMP":n+=` DEFAULT ${e.defaultValue}`),` ${this.quoteIdentifier(e.name)} ${n}`}),o=t.filter(e=>e.fkTarget).map(e=>` FOREIGN KEY (${this.quoteIdentifier(e.name)}) REFERENCES ${this.quoteIdentifier(e.fkTarget.table)}(${this.quoteIdentifier(e.fkTarget.column)})`);return o.length>0&&r.push(...o),`CREATE TABLE ${this.quoteIdentifier(a)} (
68
- ${r.join(`,
69
- `)}
70
- );`}},Ne=class{quoteIdentifier(a){return`\`${a}\``}escapeString(a){return a.replace(/'/g,"''")}buildCreateTable(a,t){let r=t.map(e=>{let n="";return e.type==="Integer"&&(n="INT"),e.type==="Text"&&(n="VARCHAR(255)"),e.type==="Boolean"&&(n="BOOLEAN"),e.type==="Decimal"&&(n="DOUBLE"),e.type==="DateTime"&&(n="DATETIME"),e.type==="Enum"&&e.enumValues&&(n=`VARCHAR(255) CHECK(${this.quoteIdentifier(e.name)} IN (${e.enumValues.map(c=>`'${this.escapeString(c)}'`).join(", ")}))`),e.isPk&&e.type==="Integer"?n+=" AUTO_INCREMENT PRIMARY KEY":e.isPk&&(n+=" PRIMARY KEY"),!e.nullable&&!e.isPk&&(n+=" NOT NULL"),e.defaultValue&&e.defaultValue!=="AutoInc"&&!e.defaultValue.startsWith("FK ->")&&(e.defaultValue==="Timestamp"?n+=" DEFAULT CURRENT_TIMESTAMP":n+=` DEFAULT ${e.defaultValue}`),` ${this.quoteIdentifier(e.name)} ${n}`}),o=t.filter(e=>e.fkTarget).map(e=>` FOREIGN KEY (${this.quoteIdentifier(e.name)}) REFERENCES ${this.quoteIdentifier(e.fkTarget.table)}(${this.quoteIdentifier(e.fkTarget.column)})`);return o.length>0&&r.push(...o),`CREATE TABLE ${this.quoteIdentifier(a)} (
71
- ${r.join(`,
72
- `)}
73
- );`}}});import $ from"picocolors";function ee(s,a,t={}){let{title:r,emptyMessage:o="No records found",maxColWidth:e=30}=t,n={},c=0;for(let f of s){let y=f.length;for(let x of a){let k=String(x[f]??"");k.length>y&&(y=k.length)}let b=Math.max(f.length,Math.min(e,y));n[f]=b,c+=b}let i=3*(s.length-1),l=c+i+4,m=Math.max(74,l),p=m-4-i;if(c<p&&s.length>0){let f=s[s.length-1];n[f]+=p-c}let u=(f,y)=>f.length>y?f.slice(0,y-3)+"...":f,d="\u2550".repeat(m-2);if(console.log($.cyan(`
74
- \u2554${d}\u2557`)),r){let f=m-2-r.length,y=Math.max(0,Math.floor(f/2)),b=Math.max(0,f-y),x=" ".repeat(y)+$.bold($.white(r))+" ".repeat(b);console.log($.cyan("\u2551")+x+$.cyan("\u2551")),console.log($.cyan(`\u2560${d}\u2563`))}if(s.length>0){let f=s.map(y=>$.bold($.white(u(y,n[y]).padEnd(n[y])))).join($.cyan(" \u2551 "));console.log($.cyan("\u2551 ")+f+$.cyan(" \u2551")),console.log($.cyan(`\u2560${d}\u2563`))}if(a.length===0){let f=m-4-o.length,y=Math.max(0,Math.floor(f/2)),b=Math.max(0,f-y);console.log($.cyan("\u2551 ")+" ".repeat(y)+$.yellow(o)+" ".repeat(b)+$.cyan(" \u2551"))}else for(let f of a){let y=s.map(b=>$.white(u(String(f[b]??""),n[b]).padEnd(n[b]))).join($.cyan(" \u2551 "));console.log($.cyan("\u2551 ")+y+$.cyan(" \u2551"))}console.log($.cyan(`\u255A${d}\u255D`))}var Ce=E(()=>{"use strict"});var at={};L(at,{runQueryCommand:()=>ga});import ce from"picocolors";async function ga(s,a){s.type==="unknown"&&(console.log(ce.red("Error: No database connection found. Cannot run query.")),process.exit(1));let t=h(s),r=a[0];if(!r){let{input:o}=await import("@inquirer/prompts");r=await o({message:"Enter your SQL query:"})}(!r||r.trim()==="")&&(console.log(ce.yellow("No query provided. Exiting.")),process.exit(0));try{let o=await t.query(r);o.columns.length===0&&(console.log(ce.yellow("Query executed successfully. (No output)")),process.exit(0)),ee(o.columns,o.rows,{title:"Query Result",maxColWidth:50}),console.log(ce.dim(`
75
- (${o.rows.length} rows)`)),await t.close(),process.exit(0)}catch(o){console.log(ce.red(`
76
- Query Error: ${o.message}
77
- `)),process.exit(1)}}var ot=E(()=>{"use strict";A();Ce()});var nt={};L(nt,{runExportCommand:()=>fa});import F from"picocolors";import me from"fs/promises";import ue from"path";async function fa(s,a,t){s.type==="unknown"&&(console.log(F.red("Error: No database connection found. Cannot run export.")),process.exit(1));let r=h(s),o=a[0],e=t.format,n=t["schema-only"],{select:c}=await import("@inquirer/prompts");if(!o){let m=await r.getTables();m.length===0&&(console.log(F.yellow("No tables found in the database.")),process.exit(0));let p=[{name:"All Tables (*)",value:"*"},...m.map(u=>({name:u,value:u}))];o=await c({message:"Which table do you want to export?",choices:p})}e||(e=await c({message:"Which format do you want to export?",choices:[{name:"CSV",value:"csv"},{name:"JSON",value:"json"}]}));let i=ue.join(process.cwd(),"drixio_exports");await me.mkdir(i,{recursive:!0});let l=o==="*"?await r.getTables():[o];console.log(F.cyan(`
78
- Starting export to ${i}...`));for(let m of l)try{if(n){let p=await r.getSchema(m);if(e==="json"){let u=ue.join(i,`${m}_schema.json`);await me.writeFile(u,JSON.stringify(p,null,2),"utf-8"),console.log(F.green(`\u2714 Exported Schema (JSON): ${m}`))}else{let u=ue.join(i,`${m}_schema.csv`),d=`Name,Type,IsPrimaryKey,Nullable
79
- `,f=p.map(y=>`"${y.name}","${y.type}","${y.isPk}","${y.nullable}"`).join(`
80
- `);await me.writeFile(u,d+f,"utf-8"),console.log(F.green(`\u2714 Exported Schema (CSV): ${m}`))}}else{let p=await r.getData(m,9999999,0);if(e==="json"){let u=ue.join(i,`${m}_data.json`);await me.writeFile(u,JSON.stringify(p.rows,null,2),"utf-8"),console.log(F.green(`\u2714 Exported Data (JSON): ${m} (${p.rows.length} rows)`))}else{let u=ue.join(i,`${m}_data.csv`),d="";if(p.columns.length>0){let f=p.columns.join(",")+`
81
- `,y=p.rows.map(b=>p.columns.map(x=>`"${String(b[x]??"").replace(/"/g,'""')}"`).join(",")).join(`
82
- `);d=f+y}await me.writeFile(u,d,"utf-8"),console.log(F.green(`\u2714 Exported Data (CSV): ${m} (${p.rows.length} rows)`))}}}catch(p){console.log(F.red(`\u2718 Failed to export table ${m}: ${p.message}`))}await r.close(),console.log(F.cyan("Export complete.")),process.exit(0)}var st=E(()=>{"use strict";A()});import{Hono as ya}from"hono";function rt(s,a){let t=new ya,r=h(a);t.get("/tables",async o=>{try{let e=await r.getTables();return o.json({success:!0,data:e})}catch(e){return o.json({success:!1,error:e.message},500)}}),t.get("/config",o=>o.json({success:!0,data:{dbType:a.type}})),t.get("/tables/stats",async o=>{try{let e=await r.getTables(),n={};for(let c of e)try{let i=await r.query(`SELECT COUNT(*) as c FROM ${r.quoteIdentifier(c)}`);if(i&&i.rows&&i.rows.length>0){let l=i.rows[0],m=Object.values(l)[0];n[c]=parseInt(String(m),10)||0}else n[c]=0}catch{n[c]=0}return o.json({success:!0,data:n})}catch(e){return o.json({success:!1,error:e.message},500)}}),t.get("/tables/:name/schema",async o=>{let e=o.req.param("name");try{let n=await r.getSchema(e);return o.json({success:!0,data:n})}catch(n){return o.json({success:!1,error:n.message},500)}}),t.get("/tables/:name/indexes",async o=>{let e=o.req.param("name");try{let n=await r.getIndexes(e);return o.json({success:!0,data:n})}catch(n){return o.json({success:!1,error:n.message},500)}}),t.get("/tables/:name/data",async o=>{let e=o.req.param("name"),n=parseInt(o.req.query("limit")||"50",10),c=parseInt(o.req.query("offset")||"0",10),i=o.req.query("where")||"",l=o.req.query("orderCol"),m=o.req.query("orderAsc"),p;l&&(p={col:l,asc:m!=="false"});try{let u=await r.getData(e,n,c,i,p);return o.json({success:!0,data:u})}catch(u){return o.json({success:!1,error:u.message},500)}}),t.post("/query",async o=>{try{let{sql:e}=await o.req.json(),n=await r.query(e);return o.json({success:!0,data:n})}catch(e){return o.json({success:!1,error:e.message},500)}}),s.route("/api",t)}var it=E(()=>{"use strict";A()});var ct={};L(ct,{runStudio:()=>xa});import{serve as ha}from"@hono/node-server";import{Hono as wa}from"hono";import{serveStatic as ba}from"@hono/node-server/serve-static";import Ea from"open";import pe from"path";import lt from"picocolors";import{fileURLToPath as Sa}from"url";async function xa(s){let a=new wa;rt(a,s);let r=qe.includes("src")||qe.includes("server")?pe.resolve(qe,"../../dist/studio"):pe.resolve(qe,"./studio"),o=await import("fs/promises");a.use("/*",ba({root:pe.relative(process.cwd(),r)})),a.get("*",async c=>{let i=pe.join(r,"index.html");try{let l=await o.readFile(i,"utf-8");return c.html(l)}catch{return c.text("Drixio Studio static files not found. Did you run build?",404)}});let e=process.env.PORT?parseInt(process.env.PORT,10):51213,n=await Ta(e);console.log(lt.cyan(`
83
- Starting Drixio Studio on http://localhost:${n}...`)),console.log(lt.dim(`Press Ctrl+C to stop the server.
84
- `)),ha({fetch:a.fetch,port:n}),await Ea(`http://localhost:${n}`)}async function Ta(s){let a=await import("net"),t=s;for(;;){if(await new Promise(o=>{let e=a.createServer();e.unref(),e.on("error",()=>o(!1)),e.listen(t,()=>{e.close(()=>o(!0))})}))return t;t++}}var $a,qe,mt=E(()=>{"use strict";it();$a=Sa(import.meta.url),qe=pe.dirname($a)});var ut={};L(ut,{runBackupCommand:()=>Da});import I from"picocolors";import Ie from"fs/promises";import ve from"path";async function Da(s){s.type==="unknown"&&(console.log(I.red("Error: No database connection found. Cannot run backup.")),process.exit(1));let a=new Date().toISOString().replace(/[:.]/g,"-"),t=ve.join(process.cwd(),`drixio_backup_${a}`);if(console.log(I.cyan(`
85
- Starting database backup...`)),console.log(I.dim(`Database: ${s.type}`)),console.log(I.dim(`Target: ${s.targetUrl}`)),console.log(I.dim(`Backup Directory: ${t}
86
- `)),await Ie.mkdir(t,{recursive:!0}),s.type==="sqlite")try{let o=ve.basename(s.targetUrl)||"database.sqlite",e=ve.join(t,o);await Ie.copyFile(s.targetUrl,e),console.log(I.green(`\u2714 Copied raw SQLite file to ${e}`))}catch(o){console.log(I.red(`\u2718 Failed to copy SQLite file: ${o.message}`))}let r=h(s);try{let o=await r.getTables();o.length===0&&console.log(I.yellow("No tables found to backup."));for(let e of o)try{let n=await r.getSchema(e),c=await r.getData(e,9999999,0),i={table:e,schema:n,totalRows:c.rows.length,data:c.rows},l=ve.join(t,`${e}.json`);await Ie.writeFile(l,JSON.stringify(i,null,2),"utf-8"),console.log(I.green(`\u2714 Dumped table: ${e} (${c.rows.length} rows)`))}catch(n){console.log(I.red(`\u2718 Failed to dump table ${e}: ${n.message}`))}}catch(o){console.log(I.red(`
87
- Backup Error: ${o.message}`))}finally{await r.close()}console.log(I.cyan(`
88
- Backup successfully completed at ${t}`)),process.exit(0)}var pt=E(()=>{"use strict";A()});var gt={};L(gt,{runImportCommand:()=>qa});import M from"picocolors";import dt from"fs/promises";import Aa from"path";function Ca(s){let a=s.split(/\r?\n/).filter(e=>e.trim()!=="");if(a.length===0)return[];let t=e=>{let n=[],c="",i=!1;for(let l=0;l<e.length;l++){let m=e[l];m==='"'&&e[l+1]==='"'?(c+='"',l++):m==='"'?i=!i:m===","&&!i?(n.push(c),c=""):c+=m}return n.push(c),n},r=t(a[0]),o=[];for(let e=1;e<a.length;e++){let n=t(a[e]),c={};r.forEach((i,l)=>{c[i]=n[l]??""}),o.push(c)}return o}async function qa(s,a,t){s.type==="unknown"&&(console.log(M.red("Error: No database connection found. Cannot run import.")),process.exit(1));let r=h(s),o=a[0],e=t.table,{input:n,select:c}=await import("@inquirer/prompts");o||(o=await n({message:"Enter the path to your CSV or JSON file:"}));let i=Aa.resolve(process.cwd(),o);try{await dt.access(i)}catch{console.log(M.red(`Error: File not found at ${i}`)),process.exit(1)}if(!e){let d=await r.getTables();d.length===0&&(console.log(M.yellow("No tables found. Please create a table first.")),process.exit(1)),e=await c({message:"Which table do you want to import data into?",choices:d.map(f=>({name:f,value:f}))})}console.log(M.cyan(`
89
- Reading file...`));let l=await dt.readFile(i,"utf-8"),m=[];if(o.toLowerCase().endsWith(".json"))try{if(m=JSON.parse(l),!Array.isArray(m))throw new Error("JSON root must be an array of objects.")}catch(d){console.log(M.red(`Invalid JSON format: ${d.message}`)),process.exit(1)}else o.toLowerCase().endsWith(".csv")?m=Ca(l):(console.log(M.red("Error: Unsupported file extension. Please provide a .csv or .json file.")),process.exit(1));m.length===0&&(console.log(M.yellow("No data found to import.")),process.exit(0)),console.log(M.cyan(`Importing ${m.length} rows into '${e}'...`));let p=500,u=0;try{for(let d=0;d<m.length;d+=p){let f=m.slice(d,d+p);await r.insert(e,f),u+=f.length,process.stdout.write(`\r${M.dim(`Progress: ${u} / ${m.length}`)}`)}console.log(M.green(`
90
-
91
- \u2714 Successfully imported ${u} rows into ${e}!`))}catch(d){console.log(M.red(`
92
- \u2718 Import failed at row ${u}: ${d.message}`))}finally{await r.close()}process.exit(0)}var ft=E(()=>{"use strict";A()});var yt={};L(yt,{runSeedCommand:()=>La});import _ from"picocolors";function de(s){let a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",t="";for(let r=0;r<s;r++)t+=a.charAt(Math.floor(Math.random()*a.length));return t}function va(){let s=["gmail.com","yahoo.com","outlook.com","example.com"],a=de(8).toLowerCase(),t=s[Math.floor(Math.random()*s.length)];return`${a}@${t}`}function Ra(s){let a=s.name.toLowerCase();if(a.includes("email"))return va();if(a.includes("phone"))return`+1${Math.floor(Math.random()*9e9+1e9)}`;if(a.includes("name"))return`User_${de(5)}`;if(a.includes("url")||a.includes("link"))return`https://example.com/${de(6)}`;if(a.includes("date")||a.includes("time")||a.includes("created")||a.includes("updated"))return new Date(Date.now()-Math.floor(Math.random()*1e10)).toISOString().replace("T"," ").slice(0,19);let t=s.type.toLowerCase();return t.includes("int")||t.includes("num")||t.includes("float")||t.includes("double")?Math.floor(Math.random()*1e3):t.includes("bool")||t==="tinyint(1)"?Math.random()>.5:t.includes("char")||t.includes("text")||t.includes("string")?de(10):de(5)}async function La(s,a){s.type==="unknown"&&(console.log(_.red("Error: No database connection found. Cannot run seed.")),process.exit(1));let t=h(s),r=a[0],o=a[1],{input:e,select:n}=await import("@inquirer/prompts");if(!r){let d=await t.getTables();d.length===0&&(console.log(_.yellow("No tables found. Please create a table first.")),process.exit(1)),r=await n({message:"Which table do you want to seed with fake data?",choices:d.map(f=>({name:f,value:f}))})}let c=parseInt(o);if(isNaN(c)||c<=0){let d=await e({message:"How many rows to generate?",default:"50"});c=parseInt(d),(isNaN(c)||c<=0)&&(console.log(_.red("Error: Invalid count number.")),process.exit(1))}console.log(_.cyan(`
93
- Analyzing schema for table '${r}'...`));let i;try{i=await t.getSchema(r)}catch(d){console.log(_.red(`Error: ${d.message}`)),process.exit(1)}let l=i.filter(d=>!d.isPk);l.length===0&&console.log(_.yellow("Table only has Primary Key columns. Seeding might fail if they don't auto-increment.")),console.log(_.cyan(`Generating ${c} records...`));let m=[];for(let d=0;d<c;d++){let f={};for(let y of l.length>0?l:i)f[y.name]=Ra(y);m.push(f)}let p=500,u=0;try{s.type==="sqlite"?await t.executeSql("PRAGMA foreign_keys = OFF;"):s.type==="mysql"?await t.executeSql("SET FOREIGN_KEY_CHECKS = 0;"):s.type==="postgres"&&await t.executeSql("SET session_replication_role = replica;");for(let d=0;d<m.length;d+=p){let f=m.slice(d,d+p);await t.insert(r,f),u+=f.length,process.stdout.write(`\r${_.dim(`Progress: ${u} / ${c}`)}`)}console.log(_.green(`
94
-
95
- \u2714 Successfully seeded ${u} fake records into ${r}!`))}catch(d){console.log(_.red(`
96
- \u2718 Seed failed: ${d.message}`))}finally{try{s.type==="sqlite"?await t.executeSql("PRAGMA foreign_keys = ON;"):s.type==="mysql"?await t.executeSql("SET FOREIGN_KEY_CHECKS = 1;"):s.type==="postgres"&&await t.executeSql("SET session_replication_role = DEFAULT;")}catch{}await t.close()}process.exit(0)}var ht=E(()=>{"use strict";A()});var wt={};L(wt,{runDiagramCommand:()=>ka});import Q from"picocolors";import Na from"fs/promises";import Ia from"path";async function ka(s){s.type==="unknown"&&(console.log(Q.red("Error: No database connection found. Cannot generate diagram.")),process.exit(1));let a=h(s);console.log(Q.cyan(`
97
- Scanning database to generate ER diagram...`));try{let t=await a.getTables();t.length===0&&(console.log(Q.yellow("No tables found in the database.")),process.exit(0));let r=`erDiagram
98
- `;for(let n of t){r+=` ${n} {
99
- `;let c=await a.getSchema(n);for(let i of c){let l=i.type.replace(/\s+/g,"_").replace(/[^a-zA-Z0-9_]/g,""),m=i.isPk?" PK":"";r+=` ${l} ${i.name}${m}
100
- `}r+=` }
101
- `}let o=`
540
+ `,
541
+ [tableName]
542
+ );
543
+ const fks = fkRows;
544
+ return columns.map((col) => {
545
+ const fk = fks.find((f) => f.COLUMN_NAME === col.Field);
546
+ let enumValues;
547
+ const typeUpper = col.Type.toUpperCase();
548
+ if (typeUpper.startsWith("ENUM")) {
549
+ const enumMatch = col.Type.match(/enum\((.*?)\)/i);
550
+ if (enumMatch) {
551
+ enumValues = enumMatch[1].split(",").map((s) => s.trim().replace(/^'|'$/g, ""));
552
+ }
553
+ }
554
+ return {
555
+ name: col.Field,
556
+ type: col.Type,
557
+ isPk: col.Key === "PRI",
558
+ nullable: col.Null === "YES",
559
+ defaultValue: col.Default != null ? String(col.Default) : void 0,
560
+ enumValues,
561
+ fkTarget: fk ? {
562
+ table: fk.REFERENCED_TABLE_NAME,
563
+ column: fk.REFERENCED_COLUMN_NAME
564
+ } : void 0
565
+ };
566
+ });
567
+ }
568
+ async getIndexes(tableName) {
569
+ const pool = await this.getPool();
570
+ const [rows] = await pool.query(`SHOW INDEX FROM ${this.quoteIdentifier(tableName)}`);
571
+ const indexMap = /* @__PURE__ */ new Map();
572
+ for (const row of rows) {
573
+ if (row.Key_name === "PRIMARY") continue;
574
+ const idxName = row.Key_name;
575
+ if (!indexMap.has(idxName)) {
576
+ indexMap.set(idxName, {
577
+ name: idxName,
578
+ columns: [],
579
+ isUnique: row.Non_unique === 0
580
+ });
581
+ }
582
+ indexMap.get(idxName).columns.push(row.Column_name);
583
+ }
584
+ return Array.from(indexMap.values());
585
+ }
586
+ async getData(tableName, limit = 50, offset = 0, whereClause, orderBy) {
587
+ const pool = await this.getPool();
588
+ const schema = await this.getSchema(tableName);
589
+ const columns = schema.map((col) => col.name);
590
+ let sql = `SELECT * FROM ${this.quoteIdentifier(tableName)}`;
591
+ if (whereClause) {
592
+ sql += ` WHERE ${whereClause}`;
593
+ }
594
+ if (orderBy) {
595
+ sql += ` ORDER BY ${this.quoteIdentifier(orderBy.col)} ${orderBy.asc ? "ASC" : "DESC"}`;
596
+ }
597
+ const [rows] = await pool.query(sql + " LIMIT ? OFFSET ?", [limit, offset]);
598
+ return { columns, rows };
599
+ }
600
+ async query(sql) {
601
+ const pool = await this.getPool();
602
+ const [rows, fields] = await pool.query(sql);
603
+ let columns = [];
604
+ let data = [];
605
+ if (fields && Array.isArray(fields)) {
606
+ columns = fields.map((f) => f.name);
607
+ data = rows;
608
+ } else {
609
+ columns = ["Result"];
610
+ data = [
611
+ { Result: "Success", AffectedRows: rows.affectedRows }
612
+ ];
613
+ }
614
+ return { columns, rows: data };
615
+ }
616
+ async executeSql(sql) {
617
+ const pool = await this.getPool();
618
+ await pool.query(sql);
619
+ }
620
+ async close() {
621
+ if (this.pool) {
622
+ await this.pool.end();
623
+ this.pool = null;
624
+ }
625
+ }
626
+ async insert(tableName, rows) {
627
+ if (rows.length === 0) return;
628
+ const pool = await this.getPool();
629
+ const cols = Object.keys(rows[0]);
630
+ const colsQuoted = cols.map((c) => this.quoteIdentifier(c)).join(", ");
631
+ const placeholders = cols.map(() => "?").join(", ");
632
+ const sql = `INSERT INTO ${this.quoteIdentifier(tableName)} (${colsQuoted}) VALUES (${placeholders})`;
633
+ for (const row of rows) {
634
+ const values = cols.map((c) => row[c]);
635
+ await pool.query(sql, values);
636
+ }
637
+ }
638
+ };
639
+ }
640
+ });
641
+
642
+ // src/core/factory.ts
643
+ var factory_exports = {};
644
+ __export(factory_exports, {
645
+ createDBAdapter: () => createDBAdapter
646
+ });
647
+ function createDBAdapter(config) {
648
+ switch (config.type) {
649
+ case "sqlite":
650
+ return new SqliteAdapter(config.targetUrl);
651
+ case "postgres":
652
+ return new PostgresAdapter(config.targetUrl);
653
+ case "mysql":
654
+ return new MysqlAdapter(config.targetUrl);
655
+ default:
656
+ throw new Error(`Unsupported database type: ${config.type}`);
657
+ }
658
+ }
659
+ var init_factory = __esm({
660
+ "src/core/factory.ts"() {
661
+ "use strict";
662
+ init_sqlite();
663
+ init_postgres();
664
+ init_mysql();
665
+ }
666
+ });
667
+
668
+ // src/core/dialect.ts
669
+ function getDialect(type) {
670
+ switch (type) {
671
+ case "sqlite":
672
+ return new SqliteDialect();
673
+ case "postgres":
674
+ return new PostgresDialect();
675
+ case "mysql":
676
+ return new MysqlDialect();
677
+ default:
678
+ return new SqliteDialect();
679
+ }
680
+ }
681
+ var SqliteDialect, PostgresDialect, MysqlDialect;
682
+ var init_dialect = __esm({
683
+ "src/core/dialect.ts"() {
684
+ "use strict";
685
+ SqliteDialect = class {
686
+ quoteIdentifier(name) {
687
+ return `"${name}"`;
688
+ }
689
+ escapeString(val) {
690
+ return val.replace(/'/g, "''");
691
+ }
692
+ buildCreateTable(tableName, columns) {
693
+ const lines = columns.map((col) => {
694
+ let typeStr = "";
695
+ if (col.type === "Integer") typeStr = "INTEGER";
696
+ if (col.type === "Text") typeStr = "TEXT";
697
+ if (col.type === "Boolean") typeStr = "BOOLEAN";
698
+ if (col.type === "Decimal") typeStr = "REAL";
699
+ if (col.type === "DateTime") typeStr = "DATETIME";
700
+ if (col.type === "Enum" && col.enumValues) {
701
+ typeStr = `TEXT CHECK(${this.quoteIdentifier(col.name)} IN (${col.enumValues.map((v) => `'${this.escapeString(v)}'`).join(", ")}))`;
702
+ }
703
+ if (col.isPk && col.type === "Integer") typeStr = "INTEGER PRIMARY KEY AUTOINCREMENT";
704
+ else if (col.isPk) typeStr += " PRIMARY KEY";
705
+ else {
706
+ if (!col.nullable) typeStr += " NOT NULL";
707
+ }
708
+ if (col.defaultValue && col.defaultValue !== "AutoInc" && !col.defaultValue.startsWith("FK ->")) {
709
+ if (col.defaultValue === "Timestamp") {
710
+ typeStr += " DEFAULT CURRENT_TIMESTAMP";
711
+ } else {
712
+ typeStr += ` DEFAULT ${col.defaultValue}`;
713
+ }
714
+ }
715
+ return ` ${this.quoteIdentifier(col.name)} ${typeStr}`;
716
+ });
717
+ const fks = columns.filter((col) => col.fkTarget).map((col) => ` FOREIGN KEY (${this.quoteIdentifier(col.name)}) REFERENCES ${this.quoteIdentifier(col.fkTarget.table)}(${this.quoteIdentifier(col.fkTarget.column)})`);
718
+ if (fks.length > 0) {
719
+ lines.push(...fks);
720
+ }
721
+ return `CREATE TABLE ${this.quoteIdentifier(tableName)} (
722
+ ${lines.join(",\n")}
723
+ );`;
724
+ }
725
+ };
726
+ PostgresDialect = class {
727
+ quoteIdentifier(name) {
728
+ return `"${name}"`;
729
+ }
730
+ escapeString(val) {
731
+ return val.replace(/'/g, "''");
732
+ }
733
+ buildCreateTable(tableName, columns) {
734
+ const lines = columns.map((col) => {
735
+ let typeStr = "";
736
+ if (col.isPk && col.type === "Integer") typeStr = "SERIAL PRIMARY KEY";
737
+ else {
738
+ if (col.type === "Integer") typeStr = "INTEGER";
739
+ if (col.type === "Text") typeStr = "TEXT";
740
+ if (col.type === "Boolean") typeStr = "BOOLEAN";
741
+ if (col.type === "Decimal") typeStr = "NUMERIC";
742
+ if (col.type === "DateTime") typeStr = "TIMESTAMP";
743
+ if (col.type === "Enum" && col.enumValues) {
744
+ typeStr = `VARCHAR(255) CHECK(${this.quoteIdentifier(col.name)} IN (${col.enumValues.map((v) => `'${this.escapeString(v)}'`).join(", ")}))`;
745
+ }
746
+ if (col.isPk) typeStr += " PRIMARY KEY";
747
+ if (!col.nullable && !col.isPk) typeStr += " NOT NULL";
748
+ }
749
+ if (col.defaultValue && col.defaultValue !== "AutoInc" && !col.defaultValue.startsWith("FK ->")) {
750
+ if (col.defaultValue === "Timestamp") {
751
+ typeStr += " DEFAULT CURRENT_TIMESTAMP";
752
+ } else {
753
+ typeStr += ` DEFAULT ${col.defaultValue}`;
754
+ }
755
+ }
756
+ return ` ${this.quoteIdentifier(col.name)} ${typeStr}`;
757
+ });
758
+ const fks = columns.filter((col) => col.fkTarget).map((col) => ` FOREIGN KEY (${this.quoteIdentifier(col.name)}) REFERENCES ${this.quoteIdentifier(col.fkTarget.table)}(${this.quoteIdentifier(col.fkTarget.column)})`);
759
+ if (fks.length > 0) {
760
+ lines.push(...fks);
761
+ }
762
+ return `CREATE TABLE ${this.quoteIdentifier(tableName)} (
763
+ ${lines.join(",\n")}
764
+ );`;
765
+ }
766
+ };
767
+ MysqlDialect = class {
768
+ quoteIdentifier(name) {
769
+ return `\`${name}\``;
770
+ }
771
+ escapeString(val) {
772
+ return val.replace(/'/g, "''");
773
+ }
774
+ buildCreateTable(tableName, columns) {
775
+ const lines = columns.map((col) => {
776
+ let typeStr = "";
777
+ if (col.type === "Integer") typeStr = "INT";
778
+ if (col.type === "Text") typeStr = "VARCHAR(255)";
779
+ if (col.type === "Boolean") typeStr = "BOOLEAN";
780
+ if (col.type === "Decimal") typeStr = "DOUBLE";
781
+ if (col.type === "DateTime") typeStr = "DATETIME";
782
+ if (col.type === "Enum" && col.enumValues) {
783
+ typeStr = `VARCHAR(255) CHECK(${this.quoteIdentifier(col.name)} IN (${col.enumValues.map((v) => `'${this.escapeString(v)}'`).join(", ")}))`;
784
+ }
785
+ if (col.isPk && col.type === "Integer") typeStr += " AUTO_INCREMENT PRIMARY KEY";
786
+ else if (col.isPk) typeStr += " PRIMARY KEY";
787
+ if (!col.nullable && !col.isPk) typeStr += " NOT NULL";
788
+ if (col.defaultValue && col.defaultValue !== "AutoInc" && !col.defaultValue.startsWith("FK ->")) {
789
+ if (col.defaultValue === "Timestamp") {
790
+ typeStr += " DEFAULT CURRENT_TIMESTAMP";
791
+ } else {
792
+ typeStr += ` DEFAULT ${col.defaultValue}`;
793
+ }
794
+ }
795
+ return ` ${this.quoteIdentifier(col.name)} ${typeStr}`;
796
+ });
797
+ const fks = columns.filter((col) => col.fkTarget).map((col) => ` FOREIGN KEY (${this.quoteIdentifier(col.name)}) REFERENCES ${this.quoteIdentifier(col.fkTarget.table)}(${this.quoteIdentifier(col.fkTarget.column)})`);
798
+ if (fks.length > 0) {
799
+ lines.push(...fks);
800
+ }
801
+ return `CREATE TABLE ${this.quoteIdentifier(tableName)} (
802
+ ${lines.join(",\n")}
803
+ );`;
804
+ }
805
+ };
806
+ }
807
+ });
808
+
809
+ // src/cli/ui/table.ts
810
+ import pc4 from "picocolors";
811
+ function drawTable(columns, rows, options = {}) {
812
+ const { title, emptyMessage = "No records found", maxColWidth = 30 } = options;
813
+ const colWidths = {};
814
+ let totalMinWidth = 0;
815
+ for (const col of columns) {
816
+ let maxValLength = col.length;
817
+ for (const row of rows) {
818
+ const valStr = String(row[col] ?? "");
819
+ if (valStr.length > maxValLength) {
820
+ maxValLength = valStr.length;
821
+ }
822
+ }
823
+ const finalW = Math.max(col.length, Math.min(maxColWidth, maxValLength));
824
+ colWidths[col] = finalW;
825
+ totalMinWidth += finalW;
826
+ }
827
+ const dividerWidths = 3 * (columns.length - 1);
828
+ const dynamicWidth = totalMinWidth + dividerWidths + 4;
829
+ const currentTerminalWidth = Math.max(74, dynamicWidth);
830
+ const availableTextWidth = currentTerminalWidth - 4 - dividerWidths;
831
+ if (totalMinWidth < availableTextWidth && columns.length > 0) {
832
+ const lastCol = columns[columns.length - 1];
833
+ colWidths[lastCol] += availableTextWidth - totalMinWidth;
834
+ }
835
+ const truncate = (str, maxLen) => {
836
+ if (str.length > maxLen) {
837
+ return str.slice(0, maxLen - 3) + "...";
838
+ }
839
+ return str;
840
+ };
841
+ const dashes = "\u2550".repeat(currentTerminalWidth - 2);
842
+ console.log(pc4.cyan(`
843
+ \u2554${dashes}\u2557`));
844
+ if (title) {
845
+ const spacesNeeded = currentTerminalWidth - 2 - title.length;
846
+ const leftSpace = Math.max(0, Math.floor(spacesNeeded / 2));
847
+ const rightSpace = Math.max(0, spacesNeeded - leftSpace);
848
+ const titleContent = " ".repeat(leftSpace) + pc4.bold(pc4.white(title)) + " ".repeat(rightSpace);
849
+ console.log(pc4.cyan("\u2551") + titleContent + pc4.cyan("\u2551"));
850
+ console.log(pc4.cyan(`\u2560${dashes}\u2563`));
851
+ }
852
+ if (columns.length > 0) {
853
+ const headerRow = columns.map((col) => pc4.bold(pc4.white(truncate(col, colWidths[col]).padEnd(colWidths[col])))).join(pc4.cyan(" \u2551 "));
854
+ console.log(pc4.cyan("\u2551 ") + headerRow + pc4.cyan(" \u2551"));
855
+ console.log(pc4.cyan(`\u2560${dashes}\u2563`));
856
+ }
857
+ if (rows.length === 0) {
858
+ const emptySpaces = currentTerminalWidth - 4 - emptyMessage.length;
859
+ const leftPad = Math.max(0, Math.floor(emptySpaces / 2));
860
+ const rightPad = Math.max(0, emptySpaces - leftPad);
861
+ console.log(
862
+ pc4.cyan("\u2551 ") + " ".repeat(leftPad) + pc4.yellow(emptyMessage) + " ".repeat(rightPad) + pc4.cyan(" \u2551")
863
+ );
864
+ } else {
865
+ for (const row of rows) {
866
+ const rowContent = columns.map(
867
+ (col) => pc4.white(truncate(String(row[col] ?? ""), colWidths[col]).padEnd(colWidths[col]))
868
+ ).join(pc4.cyan(" \u2551 "));
869
+ console.log(pc4.cyan("\u2551 ") + rowContent + pc4.cyan(" \u2551"));
870
+ }
871
+ }
872
+ console.log(pc4.cyan(`\u255A${dashes}\u255D`));
873
+ }
874
+ var init_table = __esm({
875
+ "src/cli/ui/table.ts"() {
876
+ "use strict";
877
+ }
878
+ });
879
+
880
+ // subcommands/query.ts
881
+ var query_exports = {};
882
+ __export(query_exports, {
883
+ runQueryCommand: () => runQueryCommand
884
+ });
885
+ import pc9 from "picocolors";
886
+ async function runQueryCommand(dbConfig, args) {
887
+ if (dbConfig.type === "unknown") {
888
+ console.log(pc9.red("Error: No database connection found. Cannot run query."));
889
+ process.exit(1);
890
+ }
891
+ const adapter = createDBAdapter(dbConfig);
892
+ let sql = args[0];
893
+ if (!sql) {
894
+ const { input: input7 } = await import("@inquirer/prompts");
895
+ sql = await input7({
896
+ message: "Enter your SQL query:"
897
+ });
898
+ }
899
+ if (!sql || sql.trim() === "") {
900
+ console.log(pc9.yellow("No query provided. Exiting."));
901
+ process.exit(0);
902
+ }
903
+ try {
904
+ const result = await adapter.query(sql);
905
+ if (result.columns.length === 0) {
906
+ console.log(pc9.yellow("Query executed successfully. (No output)"));
907
+ process.exit(0);
908
+ }
909
+ drawTable(result.columns, result.rows, { title: "Query Result", maxColWidth: 50 });
910
+ console.log(pc9.dim(`
911
+ (${result.rows.length} rows)`));
912
+ await adapter.close();
913
+ process.exit(0);
914
+ } catch (e) {
915
+ console.log(pc9.red(`
916
+ Query Error: ${e.message}
917
+ `));
918
+ process.exit(1);
919
+ }
920
+ }
921
+ var init_query = __esm({
922
+ "subcommands/query.ts"() {
923
+ "use strict";
924
+ init_factory();
925
+ init_table();
926
+ }
927
+ });
928
+
929
+ // subcommands/export.ts
930
+ var export_exports = {};
931
+ __export(export_exports, {
932
+ runExportCommand: () => runExportCommand
933
+ });
934
+ import pc10 from "picocolors";
935
+ import fs3 from "fs/promises";
936
+ import path3 from "path";
937
+ async function runExportCommand(dbConfig, args, options) {
938
+ if (dbConfig.type === "unknown") {
939
+ console.log(pc10.red("Error: No database connection found. Cannot run export."));
940
+ process.exit(1);
941
+ }
942
+ const adapter = createDBAdapter(dbConfig);
943
+ let tableName = args[0];
944
+ let format = options.format;
945
+ const schemaOnly = options["schema-only"];
946
+ const { select: select11 } = await import("@inquirer/prompts");
947
+ if (!tableName) {
948
+ const allTables = await adapter.getTables();
949
+ if (allTables.length === 0) {
950
+ console.log(pc10.yellow("No tables found in the database."));
951
+ process.exit(0);
952
+ }
953
+ const tableChoices = [
954
+ { name: "All Tables (*)", value: "*" },
955
+ ...allTables.map((t) => ({ name: t, value: t }))
956
+ ];
957
+ tableName = await select11({
958
+ message: "Which table do you want to export?",
959
+ choices: tableChoices
960
+ });
961
+ }
962
+ if (!format) {
963
+ format = await select11({
964
+ message: "Which format do you want to export?",
965
+ choices: [
966
+ { name: "CSV", value: "csv" },
967
+ { name: "JSON", value: "json" }
968
+ ]
969
+ });
970
+ }
971
+ const exportDir = path3.join(process.cwd(), "drixio_exports");
972
+ await fs3.mkdir(exportDir, { recursive: true });
973
+ const tablesToExport = tableName === "*" ? await adapter.getTables() : [tableName];
974
+ console.log(pc10.cyan(`
975
+ Starting export to ${exportDir}...`));
976
+ for (const table of tablesToExport) {
977
+ try {
978
+ if (schemaOnly) {
979
+ const schema = await adapter.getSchema(table);
980
+ if (format === "json") {
981
+ const fp = path3.join(exportDir, `${table}_schema.json`);
982
+ await fs3.writeFile(fp, JSON.stringify(schema, null, 2), "utf-8");
983
+ console.log(pc10.green(`\u2714 Exported Schema (JSON): ${table}`));
984
+ } else {
985
+ const fp = path3.join(exportDir, `${table}_schema.csv`);
986
+ const headers = "Name,Type,IsPrimaryKey,Nullable\n";
987
+ const rows = schema.map((c) => `"${c.name}","${c.type}","${c.isPk}","${c.nullable}"`).join("\n");
988
+ await fs3.writeFile(fp, headers + rows, "utf-8");
989
+ console.log(pc10.green(`\u2714 Exported Schema (CSV): ${table}`));
990
+ }
991
+ } else {
992
+ const batchSize = 1e3;
993
+ let batchOffset = 0;
994
+ const allExportRows = [];
995
+ let exportColumns = [];
996
+ let lastBatch;
997
+ do {
998
+ lastBatch = await adapter.getData(table, batchSize, batchOffset);
999
+ if (exportColumns.length === 0) exportColumns = lastBatch.columns;
1000
+ allExportRows.push(...lastBatch.rows);
1001
+ batchOffset += batchSize;
1002
+ } while (lastBatch.rows.length === batchSize);
1003
+ const data = { columns: exportColumns, rows: allExportRows };
1004
+ if (format === "json") {
1005
+ const fp = path3.join(exportDir, `${table}_data.json`);
1006
+ await fs3.writeFile(fp, JSON.stringify(data.rows, null, 2), "utf-8");
1007
+ console.log(pc10.green(`\u2714 Exported Data (JSON): ${table} (${data.rows.length} rows)`));
1008
+ } else {
1009
+ const fp = path3.join(exportDir, `${table}_data.csv`);
1010
+ let csvStr = "";
1011
+ if (data.columns.length > 0) {
1012
+ const headers = data.columns.join(",") + "\n";
1013
+ const rowsStr = data.rows.map((r) => {
1014
+ return data.columns.map((c) => `"${String(r[c] ?? "").replace(/"/g, '""')}"`).join(",");
1015
+ }).join("\n");
1016
+ csvStr = headers + rowsStr;
1017
+ }
1018
+ await fs3.writeFile(fp, csvStr, "utf-8");
1019
+ console.log(pc10.green(`\u2714 Exported Data (CSV): ${table} (${data.rows.length} rows)`));
1020
+ }
1021
+ }
1022
+ } catch (e) {
1023
+ console.log(pc10.red(`\u2718 Failed to export table ${table}: ${e.message}`));
1024
+ }
1025
+ }
1026
+ await adapter.close();
1027
+ console.log(pc10.cyan("Export complete."));
1028
+ process.exit(0);
1029
+ }
1030
+ var init_export = __esm({
1031
+ "subcommands/export.ts"() {
1032
+ "use strict";
1033
+ init_factory();
1034
+ }
1035
+ });
1036
+
1037
+ // src/server/api.ts
1038
+ import { Hono } from "hono";
1039
+ import { spawn } from "child_process";
1040
+ function nodeToWebStream(nodeStream) {
1041
+ return new ReadableStream({
1042
+ start(controller) {
1043
+ nodeStream.on("data", (chunk) => controller.enqueue(chunk));
1044
+ nodeStream.on("end", () => controller.close());
1045
+ nodeStream.on("error", (err) => controller.error(err));
1046
+ },
1047
+ cancel() {
1048
+ nodeStream.destroy();
1049
+ }
1050
+ });
1051
+ }
1052
+ function registerApiRoutes(app, dbConfig) {
1053
+ const api = new Hono();
1054
+ const adapter = createDBAdapter(dbConfig);
1055
+ const getDbName = () => {
1056
+ let dbName = "database";
1057
+ if (dbConfig.targetUrl) {
1058
+ if (dbConfig.type === "sqlite") {
1059
+ dbName = dbConfig.targetUrl.replace("file:", "").split(/[/\\]/).pop() || dbName;
1060
+ } else {
1061
+ dbName = dbConfig.targetUrl.split("/").pop()?.split("?")[0] || dbName;
1062
+ }
1063
+ }
1064
+ dbName = dbName.replace(/\.sqlite$|\.db$/, "");
1065
+ return dbName.replace(/[^a-zA-Z0-9_-]/g, "");
1066
+ };
1067
+ const getDatetimeStr = () => {
1068
+ const d = /* @__PURE__ */ new Date();
1069
+ const pad = (n) => n.toString().padStart(2, "0");
1070
+ return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}_${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
1071
+ };
1072
+ api.get("/tables", async (c) => {
1073
+ try {
1074
+ const tables = await adapter.getTables();
1075
+ return c.json({ success: true, data: tables });
1076
+ } catch (e) {
1077
+ return c.json({ success: false, error: e.message }, 500);
1078
+ }
1079
+ });
1080
+ api.get("/config", (c) => {
1081
+ let dbName = "Database";
1082
+ if (dbConfig.targetUrl) {
1083
+ if (dbConfig.type === "sqlite") {
1084
+ dbName = dbConfig.targetUrl.replace("file:", "").split(/[/\\]/).pop() || dbName;
1085
+ } else {
1086
+ dbName = dbConfig.targetUrl.split("/").pop()?.split("?")[0] || "Database";
1087
+ }
1088
+ }
1089
+ return c.json({ success: true, data: { dbType: dbConfig.type, dbName } });
1090
+ });
1091
+ api.get("/status", async (c) => {
1092
+ try {
1093
+ const status = await adapter.getStatus();
1094
+ const tables = await adapter.getTables();
1095
+ status.totalTables = tables.length;
1096
+ if (status.dbType === "sqlite") {
1097
+ let modOs = "node:os";
1098
+ const os = await import(modOs);
1099
+ status.osMemTotal = os.totalmem();
1100
+ status.osMemUsed = os.totalmem() - os.freemem();
1101
+ status.osCpuUsage = os.loadavg()[0];
1102
+ }
1103
+ return c.json({ success: true, data: status });
1104
+ } catch (e) {
1105
+ return c.json({ success: false, error: e.message }, 500);
1106
+ }
1107
+ });
1108
+ api.get("/tables/stats", async (c) => {
1109
+ try {
1110
+ const tables = await adapter.getTables();
1111
+ const stats = {};
1112
+ for (const t of tables) {
1113
+ try {
1114
+ const res = await adapter.query(`SELECT COUNT(*) as c FROM ${adapter.quoteIdentifier(t)}`);
1115
+ if (res && res.rows && res.rows.length > 0) {
1116
+ const row = res.rows[0];
1117
+ const countVal = Object.values(row)[0];
1118
+ stats[t] = parseInt(String(countVal), 10) || 0;
1119
+ } else {
1120
+ stats[t] = 0;
1121
+ }
1122
+ } catch (e) {
1123
+ stats[t] = 0;
1124
+ }
1125
+ }
1126
+ return c.json({ success: true, data: stats });
1127
+ } catch (e) {
1128
+ return c.json({ success: false, error: e.message }, 500);
1129
+ }
1130
+ });
1131
+ api.get("/tables/:name/schema", async (c) => {
1132
+ const tableName = c.req.param("name");
1133
+ try {
1134
+ const schema = await adapter.getSchema(tableName);
1135
+ return c.json({ success: true, data: schema });
1136
+ } catch (e) {
1137
+ return c.json({ success: false, error: e.message }, 500);
1138
+ }
1139
+ });
1140
+ api.get("/tables/:name/indexes", async (c) => {
1141
+ const tableName = c.req.param("name");
1142
+ try {
1143
+ const indexes = await adapter.getIndexes(tableName);
1144
+ return c.json({ success: true, data: indexes });
1145
+ } catch (e) {
1146
+ return c.json({ success: false, error: e.message }, 500);
1147
+ }
1148
+ });
1149
+ api.get("/tables/:name/data", async (c) => {
1150
+ const tableName = c.req.param("name");
1151
+ const limit = parseInt(c.req.query("limit") || "50", 10);
1152
+ const offset = parseInt(c.req.query("offset") || "0", 10);
1153
+ const whereClause = c.req.query("where") || "";
1154
+ const orderCol = c.req.query("orderCol");
1155
+ const orderAscStr = c.req.query("orderAsc");
1156
+ let orderBy = void 0;
1157
+ if (orderCol) {
1158
+ orderBy = { col: orderCol, asc: orderAscStr !== "false" };
1159
+ }
1160
+ try {
1161
+ const data = await adapter.getData(tableName, limit, offset, whereClause, orderBy);
1162
+ return c.json({ success: true, data });
1163
+ } catch (e) {
1164
+ return c.json({ success: false, error: e.message }, 500);
1165
+ }
1166
+ });
1167
+ api.post("/query", async (c) => {
1168
+ try {
1169
+ const { sql } = await c.req.json();
1170
+ const result = await adapter.query(sql);
1171
+ return c.json({ success: true, data: result });
1172
+ } catch (e) {
1173
+ return c.json({ success: false, error: e.message }, 500);
1174
+ }
1175
+ });
1176
+ api.get("/tables/:name/export", async (c) => {
1177
+ const tableName = c.req.param("name");
1178
+ const format = c.req.query("format") || "csv";
1179
+ const whereClause = c.req.query("where") || "";
1180
+ const orderCol = c.req.query("orderCol");
1181
+ const orderAscStr = c.req.query("orderAsc");
1182
+ let orderBy = void 0;
1183
+ if (orderCol) {
1184
+ orderBy = { col: orderCol, asc: orderAscStr !== "false" };
1185
+ }
1186
+ try {
1187
+ const batchSize = 1e3;
1188
+ let batchOffset = 0;
1189
+ const allRows = [];
1190
+ let exportColumns = [];
1191
+ let lastBatch;
1192
+ do {
1193
+ lastBatch = await adapter.getData(tableName, batchSize, batchOffset, whereClause, orderBy);
1194
+ if (exportColumns.length === 0) exportColumns = lastBatch.columns;
1195
+ allRows.push(...lastBatch.rows);
1196
+ batchOffset += batchSize;
1197
+ } while (lastBatch.rows.length === batchSize);
1198
+ const rows = allRows;
1199
+ const timestamp = getDatetimeStr();
1200
+ const exportFilename = `${tableName}_export_${timestamp}`;
1201
+ if (format === "json") {
1202
+ const jsonStr = JSON.stringify(rows, null, 2);
1203
+ c.header("Content-Disposition", `attachment; filename="${exportFilename}.json"`);
1204
+ c.header("Content-Type", "application/json");
1205
+ return c.body(jsonStr);
1206
+ } else {
1207
+ let csvStr = "";
1208
+ if (rows.length > 0) {
1209
+ const headers = Object.keys(rows[0]);
1210
+ const headerStr = headers.join(",");
1211
+ const rowStrs = rows.map((r) => {
1212
+ return headers.map((h) => {
1213
+ let val = r[h];
1214
+ if (val === null || val === void 0) val = "";
1215
+ val = String(val);
1216
+ if (val.includes(",") || val.includes('"') || val.includes("\n")) {
1217
+ val = `"${val.replace(/"/g, '""')}"`;
1218
+ }
1219
+ return val;
1220
+ }).join(",");
1221
+ });
1222
+ csvStr = [headerStr, ...rowStrs].join("\n");
1223
+ }
1224
+ c.header("Content-Disposition", `attachment; filename="${exportFilename}.csv"`);
1225
+ c.header("Content-Type", "text/csv");
1226
+ return c.body(csvStr);
1227
+ }
1228
+ } catch (e) {
1229
+ return c.text(`Export Failed: ${e.message}`, 500);
1230
+ }
1231
+ });
1232
+ api.get("/database/export", async (c) => {
1233
+ try {
1234
+ const type = dbConfig.type;
1235
+ const url = dbConfig.targetUrl;
1236
+ let child;
1237
+ let filename = "backup.sql";
1238
+ const executeNativeDump = (cmd, args, envName) => {
1239
+ return new Promise((resolve, reject) => {
1240
+ const cp = spawn(cmd, args);
1241
+ let started = false;
1242
+ cp.on("error", (err) => {
1243
+ if (!started) reject(new Error(`Native tool '${cmd}' not found. Please install ${envName}.`));
1244
+ });
1245
+ setTimeout(() => {
1246
+ if (!cp.killed) {
1247
+ started = true;
1248
+ resolve(nodeToWebStream(cp.stdout));
1249
+ }
1250
+ }, 100);
1251
+ });
1252
+ };
1253
+ try {
1254
+ let stream;
1255
+ const baseName = getDbName();
1256
+ const timeStr = getDatetimeStr();
1257
+ filename = `${baseName}_backup_${timeStr}.sql`;
1258
+ if (type === "sqlite") {
1259
+ const dbPath = url.replace("file:", "");
1260
+ stream = await executeNativeDump("sqlite3", [dbPath, ".dump"], "SQLite CLI");
1261
+ } else if (type === "mysql") {
1262
+ const parsed = new URL(url);
1263
+ const user = parsed.username;
1264
+ const pass = parsed.password;
1265
+ const host = parsed.hostname;
1266
+ const port = parsed.port || "3306";
1267
+ const dbname = parsed.pathname.substring(1);
1268
+ stream = await executeNativeDump("mysqldump", ["-u", user, `-p${pass}`, "-h", host, "-P", port, dbname], "MySQL Client");
1269
+ } else if (type === "postgres") {
1270
+ stream = await executeNativeDump("pg_dump", [url], "PostgreSQL CLI");
1271
+ } else {
1272
+ throw new Error("Unsupported database type for native export");
1273
+ }
1274
+ c.header("Content-Disposition", `attachment; filename="${filename}"`);
1275
+ c.header("Content-Type", "application/sql");
1276
+ return c.body(stream);
1277
+ } catch (err) {
1278
+ if (err.message.includes("Native tool")) {
1279
+ let sql = "";
1280
+ const baseName = getDbName();
1281
+ const timeStr = getDatetimeStr();
1282
+ if (type === "sqlite") {
1283
+ sql = await generateSqliteDump(adapter);
1284
+ } else if (type === "mysql") {
1285
+ sql = await generateMysqlDump(adapter);
1286
+ } else {
1287
+ throw err;
1288
+ }
1289
+ c.header("Content-Disposition", `attachment; filename="${baseName}_backup_${timeStr}.sql"`);
1290
+ c.header("Content-Type", "application/sql");
1291
+ return c.body(sql);
1292
+ }
1293
+ throw err;
1294
+ }
1295
+ } catch (e) {
1296
+ const errorHtml = `<html><body><h3>Database Export Failed</h3><p>${e.message}</p></body></html>`;
1297
+ c.header("Content-Type", "text/html");
1298
+ return c.body(errorHtml);
1299
+ }
1300
+ });
1301
+ api.get("/database/dictionary", async (c) => {
1302
+ try {
1303
+ const dbName = getDbName();
1304
+ let md = `# Data Dictionary: ${dbName}
1305
+
1306
+ `;
1307
+ const tables = await adapter.getTables();
1308
+ for (const t of tables) {
1309
+ md += `## Table: \`${t}\`
1310
+
1311
+ `;
1312
+ md += `| Column | Type | PK | Nullable |
1313
+ |---|---|---|---|
1314
+ `;
1315
+ const schema = await adapter.getSchema(t);
1316
+ for (const col of schema) {
1317
+ md += `| ${col.name} | ${col.type} | ${col.isPk ? "Yes" : "No"} | ${col.nullable ? "Yes" : "No"} |
1318
+ `;
1319
+ }
1320
+ md += `
1321
+ `;
1322
+ }
1323
+ c.header("Content-Disposition", `attachment; filename="${dbName}_dictionary_${getDatetimeStr()}.md"`);
1324
+ c.header("Content-Type", "text/markdown");
1325
+ return c.body(md);
1326
+ } catch (e) {
1327
+ return c.text(`Data Dictionary Export Failed: ${e.message}`, 500);
1328
+ }
1329
+ });
1330
+ api.get("/database/schema-only", async (c) => {
1331
+ try {
1332
+ const type = dbConfig.type;
1333
+ const url = dbConfig.targetUrl;
1334
+ const filename = `${getDbName()}_schema_${getDatetimeStr()}.sql`;
1335
+ if (type === "sqlite") {
1336
+ let sqlDump = "-- Drixio SQLite Schema Dump\n\n";
1337
+ const tablesResult = await adapter.query("SELECT sql FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%';");
1338
+ for (const row of tablesResult.rows) {
1339
+ if (row.sql) sqlDump += `${row.sql};
1340
+
1341
+ `;
1342
+ }
1343
+ c.header("Content-Disposition", `attachment; filename="${filename}"`);
1344
+ c.header("Content-Type", "application/sql");
1345
+ return c.body(sqlDump);
1346
+ } else {
1347
+ return c.text("Schema-only export for this DB type currently requires manual DDL extraction. Coming soon!", 501);
1348
+ }
1349
+ } catch (e) {
1350
+ return c.text(`Schema Export Failed: ${e.message}`, 500);
1351
+ }
1352
+ });
1353
+ app.route("/api", api);
1354
+ }
1355
+ async function generateSqliteDump(adapter) {
1356
+ let sqlDump = "-- Drixio SQLite Fallback Backup\n\n";
1357
+ try {
1358
+ const tablesResult = await adapter.query("SELECT name, sql FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%';");
1359
+ for (const row of tablesResult.rows) {
1360
+ if (!row.sql) continue;
1361
+ sqlDump += `${row.sql};
1362
+
1363
+ `;
1364
+ const data = await adapter.query(`SELECT * FROM ${adapter.quoteIdentifier(row.name)}`);
1365
+ for (const d of data.rows) {
1366
+ const keys = Object.keys(d).map((k) => adapter.quoteIdentifier(k)).join(", ");
1367
+ const vals = Object.values(d).map((v) => {
1368
+ if (v === null) return "NULL";
1369
+ if (typeof v === "number") return v;
1370
+ return `'${String(v).replace(/'/g, "''")}'`;
1371
+ }).join(", ");
1372
+ sqlDump += `INSERT INTO ${adapter.quoteIdentifier(row.name)} (${keys}) VALUES (${vals});
1373
+ `;
1374
+ }
1375
+ sqlDump += "\n";
1376
+ }
1377
+ } catch (e) {
1378
+ sqlDump += `-- Error generating backup: ${e}
1379
+ `;
1380
+ }
1381
+ return sqlDump;
1382
+ }
1383
+ async function generateMysqlDump(adapter) {
1384
+ let sqlDump = "-- Drixio MySQL Fallback Backup\n\n";
1385
+ try {
1386
+ const tables = await adapter.getTables();
1387
+ for (const table of tables) {
1388
+ try {
1389
+ const createTableResult = await adapter.query(`SHOW CREATE TABLE ${adapter.quoteIdentifier(table)}`);
1390
+ if (createTableResult.rows && createTableResult.rows.length > 0) {
1391
+ const row = createTableResult.rows[0];
1392
+ const vals = Object.values(row);
1393
+ const createSql = row["Create Table"] || row["Create View"] || (vals.length > 1 ? vals[1] : null);
1394
+ if (createSql) {
1395
+ sqlDump += `${createSql};
1396
+
1397
+ `;
1398
+ }
1399
+ }
1400
+ const data = await adapter.query(`SELECT * FROM ${adapter.quoteIdentifier(table)}`);
1401
+ for (const d of data.rows) {
1402
+ const keys = Object.keys(d).map((k) => adapter.quoteIdentifier(k)).join(", ");
1403
+ const vals = Object.values(d).map((v) => {
1404
+ if (v === null) return "NULL";
1405
+ if (typeof v === "number") return v;
1406
+ let str = String(v);
1407
+ str = str.replace(/\\/g, "\\\\");
1408
+ str = str.replace(/'/g, "''");
1409
+ str = str.replace(/\n/g, "\\n");
1410
+ str = str.replace(/\r/g, "\\r");
1411
+ return `'${str}'`;
1412
+ }).join(", ");
1413
+ sqlDump += `INSERT INTO ${adapter.quoteIdentifier(table)} (${keys}) VALUES (${vals});
1414
+ `;
1415
+ }
1416
+ sqlDump += "\n";
1417
+ } catch (tableErr) {
1418
+ sqlDump += `-- Error backing up table ${table}: ${tableErr}
1419
+
1420
+ `;
1421
+ }
1422
+ }
1423
+ } catch (e) {
1424
+ sqlDump += `-- Error generating backup: ${e}
1425
+ `;
1426
+ }
1427
+ return sqlDump;
1428
+ }
1429
+ var init_api = __esm({
1430
+ "src/server/api.ts"() {
1431
+ "use strict";
1432
+ init_factory();
1433
+ }
1434
+ });
1435
+
1436
+ // src/server/index.ts
1437
+ var server_exports = {};
1438
+ __export(server_exports, {
1439
+ runStudio: () => runStudio
1440
+ });
1441
+ import { serve } from "@hono/node-server";
1442
+ import { Hono as Hono2 } from "hono";
1443
+ import { serveStatic } from "@hono/node-server/serve-static";
1444
+ import open from "open";
1445
+ import path4 from "path";
1446
+ import pc11 from "picocolors";
1447
+ import { fileURLToPath } from "url";
1448
+ async function runStudio(dbConfig) {
1449
+ const app = new Hono2();
1450
+ registerApiRoutes(app, dbConfig);
1451
+ const isDev = __dirname.includes("src") || __dirname.includes("server");
1452
+ const studioDistPath = isDev ? path4.resolve(__dirname, "../../dist/studio") : path4.resolve(__dirname, "./studio");
1453
+ const fs12 = await import("fs/promises");
1454
+ app.use("/*", serveStatic({ root: path4.relative(process.cwd(), studioDistPath) }));
1455
+ app.get("*", async (c) => {
1456
+ const indexPath = path4.join(studioDistPath, "index.html");
1457
+ try {
1458
+ const html = await fs12.readFile(indexPath, "utf-8");
1459
+ return c.html(html);
1460
+ } catch (e) {
1461
+ return c.text("Drixio Studio static files not found. Did you run build?", 404);
1462
+ }
1463
+ });
1464
+ const defaultPort = process.env.PORT ? parseInt(process.env.PORT, 10) : 51213;
1465
+ const port = await getAvailablePort(defaultPort);
1466
+ console.log(pc11.cyan(`
1467
+ Starting Drixio Studio on http://localhost:${port}...`));
1468
+ console.log(pc11.dim(`Press Ctrl+C to stop the server.
1469
+ `));
1470
+ serve({
1471
+ fetch: app.fetch,
1472
+ port
1473
+ });
1474
+ await open(`http://localhost:${port}`);
1475
+ }
1476
+ async function getAvailablePort(startPort) {
1477
+ const net = await import("net");
1478
+ let currentPort = startPort;
1479
+ while (true) {
1480
+ const isAvailable = await new Promise((resolve) => {
1481
+ const server = net.createServer();
1482
+ server.unref();
1483
+ server.on("error", () => resolve(false));
1484
+ server.listen(currentPort, () => {
1485
+ server.close(() => resolve(true));
1486
+ });
1487
+ });
1488
+ if (isAvailable) return currentPort;
1489
+ currentPort++;
1490
+ }
1491
+ }
1492
+ var __filename, __dirname;
1493
+ var init_server = __esm({
1494
+ "src/server/index.ts"() {
1495
+ "use strict";
1496
+ init_api();
1497
+ __filename = fileURLToPath(import.meta.url);
1498
+ __dirname = path4.dirname(__filename);
1499
+ }
1500
+ });
1501
+
1502
+ // subcommands/backup.ts
1503
+ var backup_exports = {};
1504
+ __export(backup_exports, {
1505
+ runBackupCommand: () => runBackupCommand
1506
+ });
1507
+ import pc12 from "picocolors";
1508
+ import fs4 from "fs/promises";
1509
+ import path5 from "path";
1510
+ async function runBackupCommand(dbConfig) {
1511
+ if (dbConfig.type === "unknown") {
1512
+ console.log(pc12.red("Error: No database connection found. Cannot run backup."));
1513
+ process.exit(1);
1514
+ }
1515
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1516
+ const backupDir = path5.join(process.cwd(), `drixio_backup_${timestamp}`);
1517
+ console.log(pc12.cyan(`
1518
+ Starting database backup...`));
1519
+ console.log(pc12.dim(`Database: ${dbConfig.type}`));
1520
+ console.log(pc12.dim(`Target: ${dbConfig.targetUrl}`));
1521
+ console.log(pc12.dim(`Backup Directory: ${backupDir}
1522
+ `));
1523
+ await fs4.mkdir(backupDir, { recursive: true });
1524
+ if (dbConfig.type === "sqlite") {
1525
+ try {
1526
+ const targetFileName = path5.basename(dbConfig.targetUrl) || "database.sqlite";
1527
+ const destFile = path5.join(backupDir, targetFileName);
1528
+ await fs4.copyFile(dbConfig.targetUrl, destFile);
1529
+ console.log(pc12.green(`\u2714 Copied raw SQLite file to ${destFile}`));
1530
+ } catch (e) {
1531
+ console.log(pc12.red(`\u2718 Failed to copy SQLite file: ${e.message}`));
1532
+ }
1533
+ }
1534
+ const adapter = createDBAdapter(dbConfig);
1535
+ try {
1536
+ const tables = await adapter.getTables();
1537
+ if (tables.length === 0) {
1538
+ console.log(pc12.yellow("No tables found to backup."));
1539
+ }
1540
+ for (const table of tables) {
1541
+ try {
1542
+ const schema = await adapter.getSchema(table);
1543
+ const batchSize = 1e3;
1544
+ let batchOffset = 0;
1545
+ const allRows = [];
1546
+ let lastBatch;
1547
+ do {
1548
+ lastBatch = await adapter.getData(table, batchSize, batchOffset);
1549
+ allRows.push(...lastBatch.rows);
1550
+ batchOffset += batchSize;
1551
+ } while (lastBatch.rows.length === batchSize);
1552
+ const dumpObj = {
1553
+ table,
1554
+ schema,
1555
+ totalRows: allRows.length,
1556
+ data: allRows
1557
+ };
1558
+ const fp = path5.join(backupDir, `${table}.json`);
1559
+ await fs4.writeFile(fp, JSON.stringify(dumpObj, null, 2), "utf-8");
1560
+ console.log(pc12.green(`\u2714 Dumped table: ${table} (${allRows.length} rows)`));
1561
+ } catch (e) {
1562
+ console.log(pc12.red(`\u2718 Failed to dump table ${table}: ${e.message}`));
1563
+ }
1564
+ }
1565
+ } catch (e) {
1566
+ console.log(pc12.red(`
1567
+ Backup Error: ${e.message}`));
1568
+ } finally {
1569
+ await adapter.close();
1570
+ }
1571
+ console.log(pc12.cyan(`
1572
+ Backup successfully completed at ${backupDir}`));
1573
+ process.exit(0);
1574
+ }
1575
+ var init_backup = __esm({
1576
+ "subcommands/backup.ts"() {
1577
+ "use strict";
1578
+ init_factory();
1579
+ }
1580
+ });
1581
+
1582
+ // subcommands/import.ts
1583
+ var import_exports = {};
1584
+ __export(import_exports, {
1585
+ runImportCommand: () => runImportCommand
1586
+ });
1587
+ import pc13 from "picocolors";
1588
+ import fs5 from "fs/promises";
1589
+ import path6 from "path";
1590
+ function parseCSV(text) {
1591
+ const lines = text.split(/\r?\n/).filter((line) => line.trim() !== "");
1592
+ if (lines.length === 0) return [];
1593
+ const parseLine = (line) => {
1594
+ const result = [];
1595
+ let current = "";
1596
+ let inQuotes = false;
1597
+ for (let i = 0; i < line.length; i++) {
1598
+ const char = line[i];
1599
+ if (char === '"' && line[i + 1] === '"') {
1600
+ current += '"';
1601
+ i++;
1602
+ } else if (char === '"') {
1603
+ inQuotes = !inQuotes;
1604
+ } else if (char === "," && !inQuotes) {
1605
+ result.push(current);
1606
+ current = "";
1607
+ } else {
1608
+ current += char;
1609
+ }
1610
+ }
1611
+ result.push(current);
1612
+ return result;
1613
+ };
1614
+ const headers = parseLine(lines[0]);
1615
+ const rows = [];
1616
+ for (let i = 1; i < lines.length; i++) {
1617
+ const values = parseLine(lines[i]);
1618
+ const row = {};
1619
+ headers.forEach((h, index) => {
1620
+ row[h] = values[index] ?? "";
1621
+ });
1622
+ rows.push(row);
1623
+ }
1624
+ return rows;
1625
+ }
1626
+ async function runImportCommand(dbConfig, args, options) {
1627
+ if (dbConfig.type === "unknown") {
1628
+ console.log(pc13.red("Error: No database connection found. Cannot run import."));
1629
+ process.exit(1);
1630
+ }
1631
+ const adapter = createDBAdapter(dbConfig);
1632
+ let filePath = args[0];
1633
+ let tableName = options.table;
1634
+ const { input: input7, select: select11 } = await import("@inquirer/prompts");
1635
+ if (!filePath) {
1636
+ filePath = await input7({ message: "Enter the path to your CSV or JSON file:" });
1637
+ }
1638
+ const resolvedPath = path6.resolve(process.cwd(), filePath);
1639
+ try {
1640
+ await fs5.access(resolvedPath);
1641
+ } catch {
1642
+ console.log(pc13.red(`Error: File not found at ${resolvedPath}`));
1643
+ process.exit(1);
1644
+ }
1645
+ if (!tableName) {
1646
+ const allTables = await adapter.getTables();
1647
+ if (allTables.length === 0) {
1648
+ console.log(pc13.yellow("No tables found. Please create a table first."));
1649
+ process.exit(1);
1650
+ }
1651
+ tableName = await select11({
1652
+ message: "Which table do you want to import data into?",
1653
+ choices: allTables.map((t) => ({ name: t, value: t }))
1654
+ });
1655
+ }
1656
+ console.log(pc13.cyan(`
1657
+ Reading file...`));
1658
+ const fileContent = await fs5.readFile(resolvedPath, "utf-8");
1659
+ let rowsToInsert = [];
1660
+ if (filePath.toLowerCase().endsWith(".json")) {
1661
+ try {
1662
+ rowsToInsert = JSON.parse(fileContent);
1663
+ if (!Array.isArray(rowsToInsert)) {
1664
+ throw new Error("JSON root must be an array of objects.");
1665
+ }
1666
+ } catch (e) {
1667
+ console.log(pc13.red(`Invalid JSON format: ${e.message}`));
1668
+ process.exit(1);
1669
+ }
1670
+ } else if (filePath.toLowerCase().endsWith(".csv")) {
1671
+ rowsToInsert = parseCSV(fileContent);
1672
+ } else {
1673
+ console.log(pc13.red("Error: Unsupported file extension. Please provide a .csv or .json file."));
1674
+ process.exit(1);
1675
+ }
1676
+ if (rowsToInsert.length === 0) {
1677
+ console.log(pc13.yellow("No data found to import."));
1678
+ process.exit(0);
1679
+ }
1680
+ console.log(pc13.cyan(`Importing ${rowsToInsert.length} rows into '${tableName}'...`));
1681
+ const CHUNK_SIZE = 500;
1682
+ let inserted = 0;
1683
+ try {
1684
+ for (let i = 0; i < rowsToInsert.length; i += CHUNK_SIZE) {
1685
+ const chunk = rowsToInsert.slice(i, i + CHUNK_SIZE);
1686
+ await adapter.insert(tableName, chunk);
1687
+ inserted += chunk.length;
1688
+ process.stdout.write(`\r${pc13.dim(`Progress: ${inserted} / ${rowsToInsert.length}`)}`);
1689
+ }
1690
+ console.log(pc13.green(`
1691
+
1692
+ \u2714 Successfully imported ${inserted} rows into ${tableName}!`));
1693
+ } catch (e) {
1694
+ console.log(pc13.red(`
1695
+ \u2718 Import failed at row ${inserted}: ${e.message}`));
1696
+ } finally {
1697
+ await adapter.close();
1698
+ }
1699
+ process.exit(0);
1700
+ }
1701
+ var init_import = __esm({
1702
+ "subcommands/import.ts"() {
1703
+ "use strict";
1704
+ init_factory();
1705
+ }
1706
+ });
1707
+
1708
+ // subcommands/seed.ts
1709
+ var seed_exports = {};
1710
+ __export(seed_exports, {
1711
+ runSeedCommand: () => runSeedCommand
1712
+ });
1713
+ import pc14 from "picocolors";
1714
+ function generateRandomString(length) {
1715
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
1716
+ let result = "";
1717
+ for (let i = 0; i < length; i++) result += chars.charAt(Math.floor(Math.random() * chars.length));
1718
+ return result;
1719
+ }
1720
+ function generateRandomEmail() {
1721
+ const domains = ["gmail.com", "yahoo.com", "outlook.com", "example.com"];
1722
+ const name = generateRandomString(8).toLowerCase();
1723
+ const domain = domains[Math.floor(Math.random() * domains.length)];
1724
+ return `${name}@${domain}`;
1725
+ }
1726
+ function generateFakeData(col) {
1727
+ const name = col.name.toLowerCase();
1728
+ if (name.includes("email")) return generateRandomEmail();
1729
+ if (name.includes("phone")) return `+1${Math.floor(Math.random() * 9e9 + 1e9)}`;
1730
+ if (name.includes("name")) return `User_${generateRandomString(5)}`;
1731
+ if (name.includes("url") || name.includes("link")) return `https://example.com/${generateRandomString(6)}`;
1732
+ if (name.includes("date") || name.includes("time") || name.includes("created") || name.includes("updated")) {
1733
+ const d = new Date(Date.now() - Math.floor(Math.random() * 1e10));
1734
+ return d.toISOString().replace("T", " ").slice(0, 19);
1735
+ }
1736
+ const type = col.type.toLowerCase();
1737
+ if (type.includes("int") || type.includes("num") || type.includes("float") || type.includes("double")) {
1738
+ return Math.floor(Math.random() * 1e3);
1739
+ }
1740
+ if (type.includes("bool") || type === "tinyint(1)") {
1741
+ return Math.random() > 0.5;
1742
+ }
1743
+ if (type.includes("char") || type.includes("text") || type.includes("string")) {
1744
+ return generateRandomString(10);
1745
+ }
1746
+ return generateRandomString(5);
1747
+ }
1748
+ async function runSeedCommand(dbConfig, args) {
1749
+ if (dbConfig.type === "unknown") {
1750
+ console.log(pc14.red("Error: No database connection found. Cannot run seed."));
1751
+ process.exit(1);
1752
+ }
1753
+ const adapter = createDBAdapter(dbConfig);
1754
+ let tableName = args[0];
1755
+ let countStr = args[1];
1756
+ const { input: input7, select: select11 } = await import("@inquirer/prompts");
1757
+ if (!tableName) {
1758
+ const allTables = await adapter.getTables();
1759
+ if (allTables.length === 0) {
1760
+ console.log(pc14.yellow("No tables found. Please create a table first."));
1761
+ process.exit(1);
1762
+ }
1763
+ tableName = await select11({
1764
+ message: "Which table do you want to seed with fake data?",
1765
+ choices: allTables.map((t) => ({ name: t, value: t }))
1766
+ });
1767
+ }
1768
+ let count = parseInt(countStr);
1769
+ if (isNaN(count) || count <= 0) {
1770
+ const res = await input7({ message: "How many rows to generate?", default: "50" });
1771
+ count = parseInt(res);
1772
+ if (isNaN(count) || count <= 0) {
1773
+ console.log(pc14.red("Error: Invalid count number."));
1774
+ process.exit(1);
1775
+ }
1776
+ }
1777
+ console.log(pc14.cyan(`
1778
+ Analyzing schema for table '${tableName}'...`));
1779
+ let schema;
1780
+ try {
1781
+ schema = await adapter.getSchema(tableName);
1782
+ } catch (e) {
1783
+ console.log(pc14.red(`Error: ${e.message}`));
1784
+ process.exit(1);
1785
+ }
1786
+ const targetCols = schema.filter((c) => !c.isPk);
1787
+ if (targetCols.length === 0) {
1788
+ console.log(pc14.yellow("Table only has Primary Key columns. Seeding might fail if they don't auto-increment."));
1789
+ }
1790
+ console.log(pc14.cyan(`Generating ${count} records...`));
1791
+ const rowsToInsert = [];
1792
+ for (let i = 0; i < count; i++) {
1793
+ const row = {};
1794
+ for (const col of targetCols.length > 0 ? targetCols : schema) {
1795
+ row[col.name] = generateFakeData(col);
1796
+ }
1797
+ rowsToInsert.push(row);
1798
+ }
1799
+ const CHUNK_SIZE = 500;
1800
+ let inserted = 0;
1801
+ try {
1802
+ if (dbConfig.type === "sqlite") await adapter.executeSql("PRAGMA foreign_keys = OFF;");
1803
+ else if (dbConfig.type === "mysql") await adapter.executeSql("SET FOREIGN_KEY_CHECKS = 0;");
1804
+ else if (dbConfig.type === "postgres") await adapter.executeSql("SET session_replication_role = replica;");
1805
+ for (let i = 0; i < rowsToInsert.length; i += CHUNK_SIZE) {
1806
+ const chunk = rowsToInsert.slice(i, i + CHUNK_SIZE);
1807
+ await adapter.insert(tableName, chunk);
1808
+ inserted += chunk.length;
1809
+ process.stdout.write(`\r${pc14.dim(`Progress: ${inserted} / ${count}`)}`);
1810
+ }
1811
+ console.log(pc14.green(`
1812
+
1813
+ \u2714 Successfully seeded ${inserted} fake records into ${tableName}!`));
1814
+ } catch (e) {
1815
+ console.log(pc14.red(`
1816
+ \u2718 Seed failed: ${e.message}`));
1817
+ } finally {
1818
+ try {
1819
+ if (dbConfig.type === "sqlite") await adapter.executeSql("PRAGMA foreign_keys = ON;");
1820
+ else if (dbConfig.type === "mysql") await adapter.executeSql("SET FOREIGN_KEY_CHECKS = 1;");
1821
+ else if (dbConfig.type === "postgres") await adapter.executeSql("SET session_replication_role = DEFAULT;");
1822
+ } catch (e) {
1823
+ }
1824
+ await adapter.close();
1825
+ }
1826
+ process.exit(0);
1827
+ }
1828
+ var init_seed = __esm({
1829
+ "subcommands/seed.ts"() {
1830
+ "use strict";
1831
+ init_factory();
1832
+ }
1833
+ });
1834
+
1835
+ // subcommands/diagram.ts
1836
+ var diagram_exports = {};
1837
+ __export(diagram_exports, {
1838
+ runDiagramCommand: () => runDiagramCommand
1839
+ });
1840
+ import pc15 from "picocolors";
1841
+ import fs6 from "fs/promises";
1842
+ import path7 from "path";
1843
+ async function runDiagramCommand(dbConfig) {
1844
+ if (dbConfig.type === "unknown") {
1845
+ console.log(pc15.red("Error: No database connection found. Cannot generate diagram."));
1846
+ process.exit(1);
1847
+ }
1848
+ const adapter = createDBAdapter(dbConfig);
1849
+ console.log(pc15.cyan(`
1850
+ Scanning database to generate ER diagram...`));
1851
+ try {
1852
+ const allTables = await adapter.getTables();
1853
+ if (allTables.length === 0) {
1854
+ console.log(pc15.yellow("No tables found in the database."));
1855
+ process.exit(0);
1856
+ }
1857
+ let mermaidCode = "erDiagram\n";
1858
+ for (const table of allTables) {
1859
+ mermaidCode += ` ${table} {
1860
+ `;
1861
+ const schema = await adapter.getSchema(table);
1862
+ for (const col of schema) {
1863
+ const safeType = col.type.replace(/\s+/g, "_").replace(/[^a-zA-Z0-9_]/g, "");
1864
+ const pk = col.isPk ? " PK" : "";
1865
+ mermaidCode += ` ${safeType} ${col.name}${pk}
1866
+ `;
1867
+ }
1868
+ mermaidCode += ` }
1869
+ `;
1870
+ }
1871
+ const markdownOutput = `
102
1872
  # Database Schema Diagram
103
1873
 
104
1874
  You can paste this Mermaid code directly into [Draw.io](https://app.diagrams.net/) (Arrange > Insert > Advanced > Mermaid) or view it on GitHub/GitLab!
105
1875
 
106
1876
  \`\`\`mermaid
107
- ${r}
1877
+ ${mermaidCode}
108
1878
  \`\`\`
109
- `,e=Ia.resolve(process.cwd(),"drixio_schema.md");await Na.writeFile(e,o.trim(),"utf-8"),console.log(Q.green(`
110
- \u2714 Diagram generated successfully!`)),console.log(Q.white(`Output saved to: ${Q.bold(e)}`)),console.log(Q.dim("Tip: Open this file in VSCode with a Markdown viewer or paste it into Draw.io."))}catch(t){console.log(Q.red(`
111
- \u2718 Failed to generate diagram: ${t.message}`))}finally{await a.close()}process.exit(0)}var bt=E(()=>{"use strict";A()});var St={};L(St,{runExecCommand:()=>Ba});import oe from"picocolors";import Et from"fs/promises";import Pa from"path";async function Ba(s,a){s.type==="unknown"&&(console.log(oe.red("Error: No database connection found. Cannot run exec.")),process.exit(1));let t=a[0],{input:r}=await import("@inquirer/prompts");t||(t=await r({message:"Enter the path to your .sql file:"}));let o=Pa.resolve(process.cwd(),t);try{await Et.access(o)}catch{console.log(oe.red(`Error: File not found at ${o}`)),process.exit(1)}let e=await Et.readFile(o,"utf-8");e.trim()||(console.log(oe.yellow("File is empty.")),process.exit(0));let n=h(s);console.log(oe.cyan(`
112
- Executing SQL script from ${t}...`));try{await n.executeSql(e),console.log(oe.green("\u2714 Script executed successfully!"))}catch(c){console.log(oe.red(`\u2718 Execution failed: ${c.message}`))}finally{await n.close()}process.exit(0)}var $t=E(()=>{"use strict";A()});var xt={};L(xt,{runGenerateTypesCommand:()=>Ua});import Y from"picocolors";import Ma from"fs/promises";import _a from"path";function Oa(s){let a=s.toLowerCase();return a.includes("int")||a.includes("num")||a.includes("float")||a.includes("double")||a.includes("real")?"number":a.includes("bool")||a==="tinyint(1)"?"boolean":a.includes("char")||a.includes("text")||a.includes("uuid")||a.includes("string")?"string":a.includes("date")||a.includes("time")?"Date":a.includes("json")?"any":"string"}async function Ua(s){s.type==="unknown"&&(console.log(Y.red("Error: No database connection found. Cannot generate types.")),process.exit(1));let a=h(s);console.log(Y.cyan(`
113
- Scanning database to generate TypeScript interfaces...`));try{let t=await a.getTables();t.length===0&&(console.log(Y.yellow("No tables found in the database.")),process.exit(0));let r=`// Generated by Drixio CLI
114
- // Database: ${s.type.toUpperCase()}
115
-
116
- `;for(let e of t){let n=e.charAt(0).toUpperCase()+e.slice(1).replace(/_([a-z])/g,i=>i[1].toUpperCase());r+=`export interface ${n} {
117
- `;let c=await a.getSchema(e);for(let i of c){let l=Oa(i.type),m=i.nullable?"?":"";r+=` ${i.name}${m}: ${l};
118
- `}r+=`}
119
-
120
- `}let o=_a.resolve(process.cwd(),"drixio-types.d.ts");await Ma.writeFile(o,r.trim()+`
121
- `,"utf-8"),console.log(Y.green("\u2714 TypeScript interfaces generated successfully!")),console.log(Y.white(`Output saved to: ${Y.bold(o)}`))}catch(t){console.log(Y.red(`
122
- \u2718 Failed to generate types: ${t.message}`))}finally{await a.close()}process.exit(0)}var Tt=E(()=>{"use strict";A()});var At={};L(At,{runInitCommand:()=>Fa});import S from"picocolors";import Re from"fs/promises";import Dt from"path";async function Fa(s){let{select:a,input:t,password:r}=await import("@inquirer/prompts"),o=s[0];(!o||!["sqlite","mysql","postgres"].includes(o.toLowerCase()))&&(o=await a({message:"Which database do you want to initialize locally?",choices:[{name:"SQLite (Local File)",value:"sqlite"},{name:"MySQL (Local Server)",value:"mysql"},{name:"PostgreSQL (Local Server)",value:"postgres"}]})),o=o.toLowerCase(),console.log(S.cyan(`
123
- Initializing a local ${o} database...`));let e="";if(o==="sqlite"){let i=s[1]||"database.sqlite";!i.endsWith(".sqlite")&&!i.endsWith(".db")&&(i+=".sqlite");let l=Dt.resolve(process.cwd(),i);try{await Re.access(l),console.log(S.yellow(`File ${i} already exists.`))}catch{await Re.writeFile(l,""),console.log(S.green(`\u2714 Created local database file: ${i}`))}e=`file:${i}`}else{console.log(S.dim("Please provide credentials for your local server (e.g. running via XAMPP, Homebrew, etc.)"));let i=await t({message:"Server Host:",default:"localhost"}),l=await t({message:"Server Port:",default:o==="mysql"?"3306":"5432"}),m=await t({message:"Username:",default:o==="mysql"?"root":"postgres"}),p=await r({message:"Password (leave empty if none):"}),u=s[1];u||(u=await t({message:"New Database Name (e.g. my_project):"})),(!u||!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(u))&&(console.log(S.red("Invalid database name. Please use only letters, numbers, and underscores.")),process.exit(1)),console.log(S.cyan(`
124
- Connecting to local server to create database '${u}'...`));try{if(o==="mysql"){let b=await(await import("mysql2/promise")).createConnection({host:i,port:parseInt(l),user:m,password:p});await b.query(`CREATE DATABASE IF NOT EXISTS \`${u}\``),await b.end()}else if(o==="postgres"){let y=await import("pg"),b=new y.Client({host:i,port:parseInt(l),user:m,password:p,database:"postgres"});await b.connect(),(await b.query("SELECT 1 FROM pg_database WHERE datname = $1",[u])).rowCount===0&&await b.query(`CREATE DATABASE "${u}"`),await b.end()}console.log(S.green(`\u2714 Created local database: ${u}`))}catch(y){console.log(S.red(`\u2718 Failed to create database on server: ${y.message}`)),console.log(S.dim(`
125
- Could not connect to the local ${o} server on ${i}:${l}.`)),(y.code==="ECONNREFUSED"||y.message.includes("ECONNREFUSED")||y.message.includes("connect"))&&(console.log(S.yellow(`
126
- \u{1F4A1} It seems you don't have ${o} installed or running locally.`)),o==="mysql"?console.log(S.white(`\u{1F449} Download MySQL here: ${S.cyan("https://dev.mysql.com/downloads/installer/")}`)):o==="postgres"&&(console.log(S.white(`\u{1F449} Download PostgreSQL here: ${S.cyan("https://www.postgresql.org/download/")}`)),console.log(S.white(`\u{1F449} Or use Postgres.app for Mac: ${S.cyan("https://postgresapp.com/")}`))),console.log(S.dim("(Alternatively, you can run 'drixio init sqlite' for a zero-install local database!)"))),process.exit(1)}let d=o==="postgres"?"postgres://":"mysql://",f=p?`${m}:${p}`:`${m}`;e=`${d}${f}@${i}:${l}/${u}`}let n=Dt.resolve(process.cwd(),".env"),c="";try{c=await Re.readFile(n,"utf-8")}catch{}c.includes("DATABASE_URL=")?(c=c.replace(/DATABASE_URL=.*/g,`DATABASE_URL=${e}`),console.log(S.yellow("\u2714 Updated existing .env file with new DATABASE_URL"))):(c+=(c.endsWith(`
127
- `)||c.length===0?"":`
128
- `)+`DATABASE_URL=${e}
129
- `,console.log(S.green("\u2714 Saved connection string to .env file"))),await Re.writeFile(n,c,"utf-8"),console.log(S.green(`
130
- \u{1F389} Initialization Complete!`)),console.log(S.white(`You can now run ${S.bold("npx drixio")} to manage it!`)),process.exit(0)}var Ct=E(()=>{"use strict"});var qt={};L(qt,{runDropDbCommand:()=>Qa});import V from"picocolors";import Wa from"fs/promises";import ja from"path";async function Qa(s){let{select:a,input:t,password:r,confirm:o}=await import("@inquirer/prompts"),e=s[0];(!e||!["sqlite","mysql","postgres"].includes(e.toLowerCase()))&&(e=await a({message:"Which database type do you want to drop?",choices:[{name:"SQLite (Local File)",value:"sqlite"},{name:"MySQL (Local Server)",value:"mysql"},{name:"PostgreSQL (Local Server)",value:"postgres"}]})),e=e.toLowerCase();let n=s[1];if(e==="sqlite"){n||(n=await t({message:"Enter the SQLite filename to delete (e.g. database.sqlite):",default:"database.sqlite"})),!n.endsWith(".sqlite")&&!n.endsWith(".db")&&(n+=".sqlite"),await o({message:`Are you absolutely sure you want to delete ${n}? This cannot be undone!`,default:!1})||(console.log(V.yellow("Aborted.")),process.exit(0));let i=ja.resolve(process.cwd(),n);try{await Wa.unlink(i),console.log(V.green(`\u2714 Deleted local database file: ${n}`))}catch(l){console.log(V.red(`\u2718 Failed to delete file: ${l.message}`))}}else{console.log(V.dim(`Please provide credentials for your local ${e} server to drop a database.`));let c=await t({message:"Server Host:",default:"localhost"}),i=await t({message:"Server Port:",default:e==="mysql"?"3306":"5432"}),l=await t({message:"Username:",default:e==="mysql"?"root":"postgres"}),m=await r({message:"Password (leave empty if none):"});n||(n=await t({message:"Which Database Name do you want to DROP?"})),await o({message:`Are you absolutely sure you want to DROP DATABASE '${n}' from ${c}? All data will be lost!`,default:!1})||(console.log(V.yellow("Aborted.")),process.exit(0)),console.log(V.cyan(`
131
- Connecting to local server to drop database '${n}'...`));try{if(e==="mysql"){let d=await(await import("mysql2/promise")).createConnection({host:c,port:parseInt(i),user:l,password:m});await d.query(`DROP DATABASE IF EXISTS \`${n}\``),await d.end()}else if(e==="postgres"){let u=await import("pg"),d=new u.Client({host:c,port:parseInt(i),user:l,password:m,database:"postgres"});await d.connect(),await d.query(`DROP DATABASE IF EXISTS "${n}"`),await d.end()}console.log(V.green(`\u2714 Dropped database: ${n}`))}catch(u){console.log(V.red(`\u2718 Failed to drop database on server: ${u.message}`)),process.exit(1)}}process.exit(0)}var vt=E(()=>{"use strict"});import{select as Va,Separator as Ka}from"@inquirer/prompts";import G from"picocolors";var Rt,Lt=E(()=>{"use strict";Rt=async()=>await Va({message:"Table Manager - Choose an action:",theme:{prefix:G.cyan("\u2713 "),icon:{cursor:G.cyan("\u203A ")},style:{message:s=>G.bold(G.white(s)),highlight:s=>{let a=s.replace(/\x1b\[[0-9;]*m/g,"");return a.includes(" Back")?G.red(a):G.cyan(a)}}},choices:[{name:" Create New Table",value:"create",description:"Build a new database table."},{name:" Modify Existing Table",value:"modify",description:"Modify an existing table's schema structure."},{name:" Drop Table",value:"drop",description:"Delete an entire table and its data."},new Ka,{name:G.dim(" Back"),value:"back",description:"Back to the main menu."}]})});import{input as ke,select as ne,confirm as Nt}from"@inquirer/prompts";import ge from"picocolors";async function fe(s,a,t=[],r=""){let o="";for(;!o;){if(o=await ke({message:"Column name (leave empty to cancel/finish):",default:r}),!o.trim())return{col:null,isPk:!1};/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(o)?t.includes(o)&&(console.log(ge.red("Column name already exists!")),o=""):(console.log(ge.red("Invalid column name. Use only letters, numbers, and underscores.")),o="")}let e=await ne({message:`Select type for column '${o}':`,choices:[{name:"Integer",value:"Integer"},{name:"Text (String)",value:"Text"},{name:"Boolean",value:"Boolean"},{name:"Decimal (Float)",value:"Decimal"},{name:"DateTime",value:"DateTime"},{name:"Enum",value:"Enum"}]}),n;e==="Enum"&&(n=(await ke({message:"Enter comma-separated Enum values (e.g. active, pending, deleted):"})).split(",").map(d=>d.trim()).filter(d=>d),n.length===0&&console.log(ge.yellow("Warning: No enum values provided.")));let c="";e==="DateTime"&&await Nt({message:"Set default to CURRENT_TIMESTAMP?",default:!1})&&(c="Timestamp");let i=!1,l;if(e!=="DateTime"){let u=[{name:"None",value:"none"}];a||u.push({name:"Primary Key",value:"pk"}),u.push({name:"Foreign Key",value:"fk"});let d=await ne({message:`Key type for '${o}':`,choices:u});if(d==="pk")i=!0,e==="Integer"&&(c="AutoInc");else if(d==="fk"){let f=await s.getTables();if(f.length>0){let y=await ne({message:"Select target table:",choices:f.map(x=>({name:x,value:x}))}),b=await s.getSchema(y);if(b.length>0){let x=await ne({message:"Select target column:",choices:b.map(k=>({name:`${k.name} (${k.type})`,value:k.name}))});l={table:y,column:x},c=`FK -> ${y}.${x}`}else console.log(ge.yellow("Target table has no columns."))}else console.log(ge.yellow("No other tables found to link to."))}}let m=!1;if(i||(m=await Nt({message:`Can '${o}' be NULL?`,default:!0})),!i&&!l&&e!=="DateTime")if(e==="Boolean")c=await ne({message:`Default value for '${o}':`,choices:[{name:"None",value:""},{name:"TRUE",value:"TRUE"},{name:"FALSE",value:"FALSE"}]});else if(e==="Enum"&&n)c=await ne({message:`Default value for '${o}':`,choices:[{name:"None",value:""},...n.map(d=>({name:d,value:`'${d}'`}))]});else{let u=await ke({message:`Default value for '${o}' (leave empty for none):`});u&&(e==="Text"?c=`'${u.replace(/'/g,"''")}'`:c=u)}return{col:{name:o,type:e,isPk:i,nullable:m,defaultValue:c,fkTarget:l,enumValues:n},isPk:i}}var Pe=E(()=>{"use strict"});import{input as Ha,confirm as It}from"@inquirer/prompts";import g from"picocolors";async function Pt(s){let a=h(s),t="";for(;!t;)t=await Ha({message:"Enter the new table name:"}),/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(t)||(console.log(g.red("Invalid table name. Use only letters, numbers, and underscores.")),t="");let r=[],o=!1,e=!0;for(;e;){kt(t,r);let l=r.map(u=>u.name),{col:m,isPk:p}=await fe(a,o,l);if(!m)break;p&&(o=!0),r.push(m),e=await It({theme:{prefix:g.cyan(`
132
- \u2713 `),style:{message:u=>g.bold(g.white(u)),highlight:u=>{let d=u.replace(/\x1b\[[0-9;]*m/g,"");return g.cyan(d)}}},message:"Add another column?",default:!0})}kt(t,r),console.log(g.dim("Generating SQL..."));let c=j(s.type).buildCreateTable(t,r);if(console.log(g.cyan(`
133
- --- SQL Preview ---`)),console.log(g.yellow(c)),console.log(g.cyan(`-------------------
134
- `)),await It({message:"Do you want to execute this SQL to build the table?",default:!0}))try{console.log(g.dim("Executing SQL...")),await a.executeSql(c),console.log(g.green(`
135
- \u2713 Table '${t}' created successfully!`))}catch(l){console.log(g.red(`
136
- x Error creating table: ${l.message}`))}finally{await a.close()}else console.log(g.yellow("Aborted table creation.")),await a.close();await Ya()}async function Ya(){let{input:s}=await import("@inquirer/prompts");await s({message:"Press Enter to continue..."})}function kt(s,a){console.clear(),console.log(`
137
- `);let t={name:20,type:18,key:8,nullable:10,defaultCol:16},r=t.name+t.type+t.key+t.nullable+t.defaultCol+14;console.log(g.cyan(`\u2554${"\u2550".repeat(r)}\u2557`));let o=r-s.length,e=Math.max(0,Math.floor(o/2)),n=Math.max(0,o-e),c=" ".repeat(e)+g.bold(g.white(s))+" ".repeat(n);console.log(g.cyan("\u2551")+c+g.cyan("\u2551"));let i=(l,m,p)=>g.cyan(l+"\u2550".repeat(t.name+2)+m+"\u2550".repeat(t.type+2)+m+"\u2550".repeat(t.key+2)+m+"\u2550".repeat(t.nullable+2)+m+"\u2550".repeat(t.defaultCol+2)+p);console.log(i("\u2560","\u2566","\u2563")),console.log(g.cyan("\u2551 ")+g.bold(g.white("Column Name".padEnd(t.name)))+g.cyan(" \u2551 ")+g.bold(g.white("Type".padEnd(t.type)))+g.cyan(" \u2551 ")+g.bold(g.white("Key".padEnd(t.key)))+g.cyan(" \u2551 ")+g.bold(g.white("Nullable".padEnd(t.nullable)))+g.cyan(" \u2551 ")+g.bold(g.white("Default".padEnd(t.defaultCol)))+g.cyan(" \u2551")),a.length>0&&console.log(i("\u2560","\u256C","\u2563"));for(let l=0;l<a.length;l++){let m=a[l],p=g.white(m.name.padEnd(t.name)),u=g.white(m.type.padEnd(t.type)),d=m.isPk?"PK":"-",f=m.isPk?g.green(d.padEnd(t.key)):g.dim(d.padEnd(t.key)),y=m.nullable?"Yes":"No",b=m.nullable?g.green(y.padEnd(t.nullable)):g.dim(y.padEnd(t.nullable)),x=m.defaultValue||"-",k=m.defaultValue?g.yellow(x.padEnd(t.defaultCol)):g.dim(x.padEnd(t.defaultCol));console.log(g.cyan("\u2551 ")+p+g.cyan(" \u2551 ")+u+g.cyan(" \u2551 ")+f+g.cyan(" \u2551 ")+b+g.cyan(" \u2551 ")+k+g.cyan(" \u2551"))}if(a.length===0){console.log(i("\u2560","\u2569","\u2563"));let l="No columns added yet",m=Math.max(0,Math.floor((r-l.length)/2)),p=Math.max(0,r-l.length-m);console.log(g.cyan("\u2551")+" ".repeat(m)+g.dim(l)+" ".repeat(p)+g.cyan("\u2551")),console.log(g.cyan(`\u255A${"\u2550".repeat(r)}\u255D`))}else console.log(i("\u255A","\u2569","\u255D"));console.log(g.dim(` Leave empty the Column Name to cancel.
138
- `))}var Bt=E(()=>{"use strict";Ae();A();Pe()});import Ga from"fs/promises";import za from"path";import{input as Ja}from"@inquirer/prompts";import K from"picocolors";async function Mt(s){if(console.log(K.green(`
139
- Initiating SQL table writer for experts. Please ensure database connection is properly configured.`)),s.type==="unknown"){console.log(K.yellow(`
140
- No database connection found. Please setup first.`));return}try{let a=await Ja({message:"Enter the path to your .md or .sql file:"});if(a&&a.trim()){let t=za.resolve(process.cwd(),a.trim()),r="";try{r=await Ga.readFile(t,"utf-8")}catch(e){console.log(K.red(`
141
- x Failed to read file: ${e.message}`));return}let o=r;if(t.toLowerCase().endsWith(".md")){let e=[...r.matchAll(/```(?:sql)?\n([\s\S]*?)```/gi)];e.length>0&&(o=e.map(n=>n[1].trim()).join(`
142
-
143
- `))}if(o.trim()){console.log(K.dim("Executing SQL..."));let e=h(s);try{await e.executeSql(o),console.log(K.green(`
144
- \u2713 SQL executed successfully!`))}finally{await e.close()}}else console.log(K.yellow(`
145
- \u26A0\uFE0F No SQL found in the file. Aborted.`))}else console.log(K.yellow(`
146
- No file path entered. Aborted.`))}catch(a){console.log(K.red(`
147
- Error executing SQL: ${a.message}`))}}var _t=E(()=>{"use strict";A()});import{select as ye,confirm as Ot,input as Xa}from"@inquirer/prompts";import C from"picocolors";async function Ut(s){let a=h(s);try{let t=await a.getTables();if(t.length===0){console.log(C.yellow("No tables found in the database."));return}let r=await ye({message:"Select a table to drop:",choices:t.map(e=>({name:e,value:e}))});if(await Ot({message:`Are you sure you want to DROP TABLE '${r}'? This will delete all its data!`,default:!1})){let e=`DROP TABLE ${r}`;s.type==="sqlite"||s.type==="postgres"?e=`DROP TABLE "${r}"`:s.type==="mysql"&&(e=`DROP TABLE \`${r}\``),await a.executeSql(e),console.log(C.green(`\u2713 Table '${r}' dropped successfully.`))}}catch(t){console.log(C.red(`Error: ${t.message}`))}finally{await a.close()}}async function he(s,a){let t=h(s),r=j(s.type);try{let o=await t.getTables();if(o.length===0){console.log(C.yellow("No tables found in the database."));return}let e=await ye({message:"Select a table to modify:",choices:o.map(c=>({name:c,value:c}))}),n=await t.getSchema(e);if(a==="add"){let c=n.some(m=>m.isPk),i=n.map(m=>m.name),{col:l}=await fe(t,c,i);if(l){let m=r.buildCreateTable("tmp",[l]).split(`
148
- `)[1].trim().replace(/,$/,""),p=`ALTER TABLE ${e} ADD COLUMN ${m}`;s.type==="sqlite"||s.type==="postgres"?p=`ALTER TABLE "${e}" ADD COLUMN ${m}`:s.type==="mysql"&&(p=`ALTER TABLE \`${e}\` ADD COLUMN ${m}`),await t.executeSql(p),console.log(C.green(`\u2713 Column '${l.name}' added successfully.`))}}else if(a==="rename"){if(n.length===0)return console.log(C.yellow("Table has no columns."));let c=await ye({message:"Select a column to rename:",choices:n.map(l=>({name:`${l.name} (${l.type})`,value:l.name}))}),i=await Xa({message:`New name for '${c}':`});if(i&&i!==c){let l=`ALTER TABLE ${e} RENAME COLUMN ${c} TO ${i}`;s.type==="sqlite"||s.type==="postgres"?l=`ALTER TABLE "${e}" RENAME COLUMN "${c}" TO "${i}"`:s.type==="mysql"&&(l=`ALTER TABLE \`${e}\` RENAME COLUMN \`${c}\` TO \`${i}\``),await t.executeSql(l),console.log(C.green(`\u2713 Column renamed to '${i}' successfully.`))}}else if(a==="modify"){if(n.length===0)return console.log(C.yellow("Table has no columns."));if(s.type==="sqlite"){console.log(C.yellow("SQLite does not support altering column types directly. Please recreate the table."));return}let c=await ye({message:"Select a column to modify:",choices:n.map(p=>({name:`${p.name} (${p.type})`,value:p.name}))}),i=n.some(p=>p.isPk&&p.name!==c),l=n.map(p=>p.name).filter(p=>p!==c),{col:m}=await fe(t,i,l,c);if(m){let p=r.buildCreateTable("tmp",[m]).split(`
149
- `)[1].trim().replace(/,$/,""),u="";s.type==="postgres"?(console.log(C.yellow("Note: Complex constraint modifications might require raw SQL in Postgres.")),u=`ALTER TABLE "${e}" ALTER COLUMN ${p.replace(m.name,`"${m.name}" TYPE`)}`):s.type==="mysql"&&(u=`ALTER TABLE \`${e}\` MODIFY COLUMN ${p}`);try{await t.executeSql(u),console.log(C.green(`\u2713 Column '${c}' modified successfully.`))}catch(d){console.log(C.red("x Could not modify column automatically. Try using Raw SQL.")),console.log(C.dim(d.message))}}}else if(a==="delete"){if(n.length===0)return console.log(C.yellow("Table has no columns."));let c=await ye({message:"Select a column to delete:",choices:n.map(l=>({name:`${l.name} (${l.type})`,value:l.name}))});if(await Ot({message:`Are you sure you want to delete column '${c}'? Data will be lost!`,default:!1})){let l=`ALTER TABLE ${e} DROP COLUMN ${c}`;s.type==="sqlite"||s.type==="postgres"?l=`ALTER TABLE "${e}" DROP COLUMN "${c}"`:s.type==="mysql"&&(l=`ALTER TABLE \`${e}\` DROP COLUMN \`${c}\``);try{await t.executeSql(l),console.log(C.green(`\u2713 Column '${c}' deleted successfully.`))}catch(m){console.log(C.red(`x Error deleting column: ${m.message}`))}}}}catch(o){console.log(C.red(`Error: ${o.message}`))}finally{await t.close()}}var Ft=E(()=>{"use strict";A();Ae();Pe()});var jt={};L(jt,{runTableManagerFlow:()=>eo});import W from"picocolors";import{select as Wt,Separator as Za}from"@inquirer/prompts";async function eo(s){if(s.type==="unknown"){console.log(W.yellow(`
150
- No database connection found. Please setup first.`)),await Be();return}let a=await Promise.resolve().then(()=>(A(),Ve)).then(r=>r.createDBAdapter(s)),t=!0;for(;t;){console.clear();let r="Scanning...";try{r=`${(await a.getTables()).length} tables detected`}catch(n){r=W.red(n.message)}let o=s.targetUrl;switch(se(` Table Builder & Manager \u2022 [${s.type.toUpperCase()}]`,[{label:"Database",value:s.type.toUpperCase()},{label:"Target",value:o.length>45?"..."+o.slice(-42):o},{label:"Tables",value:r}]),await Rt()){case"create":let n=await Wt({message:"How do you want to create the table?",theme:{prefix:W.cyan("\u2713 "),icon:{cursor:W.cyan("\u203A ")},style:{message:c=>W.bold(W.white(c)),highlight:c=>{let i=c.replace(/\x1b\[[0-9;]*m/g,"");return i.includes("Cancel")?W.red(i):W.cyan(i)}}},choices:[{name:" Interactive Wizard (Beginner Friendly)",value:"wizard",description:"Follow the step-by-step guide to build your table."},{name:" Raw SQL (Experts)",value:"sql",description:"Write / Import your own CREATE TABLE statement."},new Za,{name:W.dim(" Cancel"),value:"cancel",description:"Back to Table Manager"}]});n==="wizard"?await Pt(s):n==="sql"&&await Mt(s);break;case"modify":await to(s);break;case"drop":await Ut(s),await Be();break;case"back":t=!1;break}}await a.close()}async function to(s){let a=await Wt({message:"Modify Table Actions:",choices:[{name:"Add Column",value:"add_col"},{name:"Rename Column",value:"rename_col"},{name:"Modify Column Settings (MySQL/Postgres)",value:"mod_col",disabled:s.type==="sqlite"},{name:"Delete Column",value:"del_col"},{name:" Back",value:"back"}]});if(a!=="back"){switch(a){case"add_col":await he(s,"add");break;case"rename_col":await he(s,"rename");break;case"mod_col":await he(s,"modify");break;case"del_col":await he(s,"delete");break}await Be()}}async function Be(){let{input:s}=await import("@inquirer/prompts");await s({message:"Press Enter to continue..."})}var Qt=E(()=>{"use strict";Lt();Bt();_t();Ft();Se()});Se();import D from"picocolors";import re from"fs/promises";import X from"path";async function q(s){let a=process.cwd();if(s){let e="sqlite";return s.startsWith("postgres://")||s.startsWith("postgresql://")?e="postgres":s.startsWith("mysql://")?e="mysql":e="sqlite",{type:e,targetUrl:s.replace("file:",""),source:"manual"}}let t=[".","prisma","db","database","src/db","src/database"];for(let e of t)try{let n=X.join(a,e),i=(await re.readdir(n)).find(l=>l.endsWith(".db")||l.endsWith(".sqlite")||l.endsWith(".sqlite3"));if(i)return{type:"sqlite",targetUrl:X.join(n,i),source:"auto-detected"}}catch{}let r=X.join(a,"prisma","schema.prisma");try{let e=await re.readFile(r,"utf-8"),n=e.match(/provider\s*=\s*["']([^"']+)["']/),c=e.match(/url\s*=\s*(?:env\(["']([^"']+)["']\)|["']([^"']+)["'])/);if(n){let i=n[1];i==="postgresql"&&(i="postgres");let l="";if(c&&c[2]&&(l=c[2],l.startsWith("file:")&&(l=X.join(a,"prisma",l.replace("file:","")))),l&&(i==="sqlite"||i==="postgres"||i==="mysql"))return{type:i,targetUrl:l,source:".env"}}}catch{}let o=X.join(a,".env");try{let e=await re.readFile(o,"utf-8"),n=["DATABASE_URL","DB_URL","POSTGRES_URL","POSTGRES_PRISMA_URL","MYSQL_URL"];for(let c of n){let i=new RegExp(`${c}\\s*=\\s*["']?([^"'\\r\\n]+)["']?`),l=e.match(i);if(l){let m=l[1];if(m.startsWith("file:")||m.endsWith(".db")||m.includes(".sqlite"))return{type:"sqlite",targetUrl:m.replace("file:",""),source:".env"};if(m.startsWith("postgres://")||m.startsWith("postgresql://"))return{type:"postgres",targetUrl:m,source:".env"};if(m.startsWith("mysql://"))return{type:"mysql",targetUrl:m,source:".env"}}}}catch{}return{type:"unknown",targetUrl:"",source:"manual"}}async function Fe(s){let a=X.join(process.cwd(),".env"),t="";try{t=await re.readFile(a,"utf-8")}catch{}let r=/DATABASE_URL\s*=\s*["']?([^"'\r\n]+)["']?/;r.test(t)?t=t.replace(r,`DATABASE_URL="${s}"`):(t&&!t.endsWith(`
151
- `)&&(t+=`
152
- `),t+=`DATABASE_URL="${s}"
153
- `),await re.writeFile(a,t,"utf-8")}A();import{select as Je,Separator as le}from"@inquirer/prompts";import T from"picocolors";import{select as ra}from"@inquirer/prompts";import Z from"picocolors";var Ke=async s=>await ra({message:"Select a table to edit data:",theme:{prefix:Z.cyan("\u2713 "),icon:{cursor:Z.cyan("\u203A ")},style:{message:a=>Z.bold(Z.white(a)),highlight:a=>{let t=a.replace(/\x1b\[[0-9;]*m/g,"");return t.includes(" Back")?Z.red(t):Z.cyan(t)}}},choices:s});Ae();import{input as ie,select as ia}from"@inquirer/prompts";import w from"picocolors";import la from"fs/promises";import ca from"path";async function He(s,a,t,r){let o=j(a);console.log(w.cyan(`
154
- --- Add Data to [${t}] ---`)),console.log(w.dim("Leave a field completely empty (press Enter) to skip it (e.g. for AutoInc or NULL)"));let e=[],n=[];for(let i of r){if(i.isPk&&(i.type.toLowerCase().includes("int")||i.type.toLowerCase()==="integer")){console.log(w.dim(`Skipping '${i.name}' (Auto Increment Primary Key)`));continue}let l=await ie({message:`Value for '${i.name}' (${i.type}):`});l.trim()!==""&&(e.push(o.quoteIdentifier(i.name)),n.push(`'${o.escapeString(l)}'`))}if(e.length===0){console.log(w.yellow("No data entered. Aborted."));return}let c=`INSERT INTO ${o.quoteIdentifier(t)} (${e.join(", ")}) VALUES (${n.join(", ")});`;console.log(w.dim(`
155
- Executing: `)+w.yellow(c));try{await s.executeSql(c),console.log(w.green("\u2713 Data added successfully!"))}catch(i){console.log(w.red(`x Failed to add data: ${i.message}`))}}async function Ye(s,a,t,r){let o=j(a);if(console.log(w.cyan(`
156
- --- Edit Data in [${t}] ---`)),r.length===0)return;let n=(r.find(u=>u.isPk)||r[0]).name,c=await ie({message:`Enter the '${n}' of the record you want to edit:`});if(!c.trim()){console.log(w.yellow("Aborted."));return}let i=await ia({message:"Which column do you want to update?",choices:r.map(u=>({name:u.name,value:u.name}))}),l=await ie({message:`New value for '${i}' (leave empty for NULL):`}),m="NULL";l.trim()!==""&&(m=`'${o.escapeString(l)}'`);let p=`UPDATE ${o.quoteIdentifier(t)} SET ${o.quoteIdentifier(i)} = ${m} WHERE ${o.quoteIdentifier(n)} = '${o.escapeString(c)}';`;console.log(w.dim(`
157
- Executing: `)+w.yellow(p));try{await s.executeSql(p),console.log(w.green("\u2713 Data updated successfully!"))}catch(u){console.log(w.red(`x Failed to update data: ${u.message}`))}}async function Ge(s,a,t,r){let o=j(a);if(console.log(w.cyan(`
158
- --- Delete Data from [${t}] ---`)),r.length===0)return;let n=(r.find(l=>l.isPk)||r[0]).name,c=await ie({message:`Enter the '${n}' of the record you want to delete:`});if(!c.trim()){console.log(w.yellow("Aborted."));return}let i=`DELETE FROM ${o.quoteIdentifier(t)} WHERE ${o.quoteIdentifier(n)} = '${o.escapeString(c)}';`;console.log(w.dim(`
159
- Executing: `)+w.yellow(i));try{await s.executeSql(i),console.log(w.green("\u2713 Data deleted successfully!"))}catch(l){console.log(w.red(`x Failed to delete data: ${l.message}`))}}async function ze(s){console.log(w.cyan(`
160
- --- Expert Mode: Execute Raw SQL ---`));try{let a=await ie({message:"Enter the path to your .md or .sql file:"});if(a&&a.trim()){let t=ca.resolve(process.cwd(),a.trim()),r="";try{r=await la.readFile(t,"utf-8")}catch(e){console.log(w.red(`
161
- x Failed to read file: ${e.message}`));return}let o=r;if(t.toLowerCase().endsWith(".md")){let e=[...r.matchAll(/```(?:sql)?\n([\s\S]*?)```/gi)];e.length>0&&(o=e.map(n=>n[1].trim()).join(`
162
-
163
- `))}if(o.trim()){console.log(w.dim(`
164
- Executing SQL...`)),console.log(w.yellow(o));try{await s.executeSql(o),console.log(w.green(`
165
- \u2713 SQL executed successfully!`))}catch(e){console.log(w.red(`
166
- x Error executing SQL: ${e.message}`))}}else console.log(w.yellow(`
167
- \u26A0\uFE0F No SQL found in the file. Aborted.`))}else console.log(w.yellow(`
168
- No file path entered. Aborted.`))}catch(a){console.log(w.red(`
169
- Error: ${a.message}`))}}Ce();Se();async function Xe(s){let a=h(s),t=!0;for(;t;){console.clear();let r=[],o=0,e="Scanning...";try{r=await a.getTables();for(let i of r)try{let l=s.type==="mysql"?"`":'"',m=await a.query(`SELECT COUNT(*) as count FROM ${l}${i}${l}`);m.rows.length>0&&m.rows[0].count!=null&&(o+=Number(m.rows[0].count))}catch{}e=`${o.toLocaleString()} rows across ${r.length} tables`}catch(i){e=T.red(i.message)}let n=s.targetUrl,c=s.source===".env"?"Loaded from project .env file":s.source==="auto-detected"?"Auto-detected local SQLite file":"Manual connection config";se(` Data Browser & Editor \u2022 [${s.type.toUpperCase()}]`,[{label:"Database",value:s.type.toUpperCase()},{label:"Target",value:n.length>45?"..."+n.slice(-42):n},{label:"Source",value:c},{label:"Total Data",value:e}]);try{if(r.length===0){console.log(T.yellow("No tables found in this database.")),await te(),await a.close();return}let i=[...r.map(f=>({name:f,value:f})),new le,{name:T.dim(" Back"),value:"BACK"}],l=await Ke(i);if(l==="BACK"){t=!1;continue}let m=!0,p=1,u="",d=50;for(;m;){console.clear();let f=(p-1)*d,y=await a.getSchema(l),b=y.map(P=>P.name),x;try{x=await a.getData(l,d,f,u)}catch(P){console.log(T.red(`
170
- Error fetching data: ${P.message}
171
- `)),u="";let{input:R}=await import("@inquirer/prompts");await R({message:"Click Enter to continue..."});continue}let k=x.rows,Kt=` ${l} (Page ${p}) `;ee(b,k,{title:Kt,maxColWidth:30});let Ht=k.length===d,Yt=p>1,we=[{name:"Add Data",value:"add"},{name:"Edit Data",value:"edit"},{name:"Delete Data",value:"delete"},new le,{name:"Search Data",value:"search"},{name:u?"Clear Search":T.dim("Clear Search (disabled)"),value:u?"clear_search":"noop"},new le,{name:"Export to CSV",value:"exportCsv"},{name:"Export to JSON",value:"exportJson"},new le];Yt&&we.push({name:"Previous Page",value:"prev"}),Ht&&we.push({name:"Next Page",value:"next"}),we.push({name:T.dim(" Back"),value:"BACK"});let v=await Je({message:"Select an action for this table:",theme:{prefix:T.cyan("\u2713 "),icon:{cursor:T.cyan("\u203A ")},style:{message:P=>T.bold(T.white(P)),highlight:P=>{let R=P.replace(/\x1b\[[0-9;]*m/g,"");return R.includes(" Back")?T.red(R):T.cyan(R)}}},choices:we});if(v==="prev"){p--;continue}if(v==="next"){p++;continue}if(v==="noop")continue;if(v==="BACK"){m=!1;continue}if(v==="clear_search"){u="",p=1;continue}if(v==="search"){let{input:P}=await import("@inquirer/prompts"),O=(await P({message:"Enter Search (e.g. `age > 18` or `John` for fuzzy search):"})).trim();if(O){if(/[=<>]|LIKE|IN|AND|OR/i.test(O))u=O;else{let z=y.filter(U=>U.type.toLowerCase().includes("char")||U.type.toLowerCase().includes("text"));if(z.length>0){let U=s.type==="postgres"?"ILIKE":"LIKE";u=z.map(Ee=>`"${Ee.name}" ${U} '%${O.replace(/'/g,"''")}%'`).join(" OR ")}else u=`"${y[0].name}" = '${O}'`}p=1}continue}if(v==="exportCsv"||v==="exportJson"){try{console.log(T.yellow(`
172
- Exporting data...`));let R=await a.getData(l,9999999,0,u),O=await import("fs/promises"),be=await import("path"),z=be.join(process.cwd(),"drixio_exports");if(await O.mkdir(z,{recursive:!0}),v==="exportCsv"){let U=R.columns.join(",")+`
173
- `,_e=R.rows.map(Gt=>R.columns.map(zt=>`"${String(Gt[zt]??"").replace(/"/g,'""')}"`).join(",")).join(`
174
- `),Ee=be.join(z,`${l}.csv`);await O.writeFile(Ee,U+_e,"utf-8"),console.log(T.green(`
175
- Exported to ${Ee}`))}else{let U=be.join(z,`${l}.json`);await O.writeFile(U,JSON.stringify(R.rows,null,2),"utf-8"),console.log(T.green(`
176
- Exported to ${U}`))}}catch(R){console.log(T.red(`
177
- Export Error: ${R.message}`))}let{input:P}=await import("@inquirer/prompts");await P({message:"Click Enter to continue..."});continue}let Me=await Je({message:`Select mode for ${v} data:`,choices:[{name:"Beginner (Interactive Step-by-Step)",value:"beginner"},{name:"Expert (Load SQL from .sql or .md file)",value:"expert"},new le,{name:T.dim("Cancel"),value:"cancel"}]});if(Me!=="cancel"){if(Me==="expert"){await ze(a),await te();continue}v==="add"?(await He(a,s.type,l,y),await te()):v==="edit"?(await Ye(a,s.type,l,y),await te()):v==="delete"&&(await Ge(a,s.type,l,y),await te())}}}catch(i){console.log(T.red(`
178
- x Error: ${i.message}`)),await te(),t=!1}}await a.close()}async function te(){let{input:s}=await import("@inquirer/prompts");await s({message:"Press Enter to continue..."})}import{select as ma,input as ua}from"@inquirer/prompts";import ae from"picocolors";A();async function Ze(s){let a=await ma({message:"What method you want to use to setup database connection?",choices:[{name:" Auto-Config",value:"auto",description:"Automatically detect and setup database connection (May fail for some cases)."},{name:" Manual Config",value:"manual",description:"Manually setup database connection by entering parameters step by step."}]}),t=s;switch(a){case"auto":let r=await q();r.type!=="unknown"?(t=r,console.log(ae.green(`
179
- Database connection setup successfully.`))):console.log(ae.red(`
180
- Failed to auto-detect database connection. Please try manual configuration.`));break;case"manual":let o=await ua({message:"Enter the database connection URL: "});if(o){let e=await q(o);console.log(ae.dim(`
181
- Testing connection...`));try{let n=h(e);await n.getTables(),await n.close(),t=e,await Fe(o),console.log(ae.green("\u2713 Database connection setup successfully and saved to .env!"))}catch(n){console.log(ae.red(`
182
- x Connection failed: ${n.message}
183
- Please enter a valid database URL or check your database status.`))}}else console.log(ae.red(`
184
- Failed to detect database connection. Please enter valid database URL.`));break}return t}import{select as pa,Separator as da}from"@inquirer/prompts";import H from"picocolors";var et=async s=>await pa({message:"Select an action:",theme:{prefix:H.cyan("\u2713 "),icon:{cursor:H.cyan("\u203A ")},style:{message:a=>H.bold(H.white(a)),highlight:a=>{let t=a.replace(/\x1b\[[0-9;]*m/g,"");return t.includes("Exit")?H.red(t):H.cyan(t)}}},choices:[{name:" Data Browser & Editor",value:"editor",description:"View, insert, update, and delete row data in your tables.",disabled:s.type==="unknown"},{name:" Run Raw SQL (REPL)",value:"repl",description:"Execute arbitrary SQL queries interactively.",disabled:s.type==="unknown"},{name:" Table Builder & Manager",value:"table",description:"Create new tables, or modify/drop existing tables.",disabled:s.type==="unknown"},new da,{name:s.type==="unknown"?" Setup Connection":" Connection Settings",value:s.type==="unknown"?"setup":"re-configure",description:s.type==="unknown"?"Setup the database connection manually.":"Re-configure the database connection."},{name:H.dim(" Exit"),value:"exit",description:"Exit the Drixio CLI application."}]});A();Ce();import B from"picocolors";async function tt(s){if(s.type==="unknown")return;let a=h(s),t=!0,{input:r}=await import("@inquirer/prompts");for(console.clear(),console.log(B.cyan(`
185
- \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`)),console.log(B.cyan("\u2551 Interactive SQL REPL \u2551")),console.log(B.cyan("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563")),console.log(B.cyan("\u2551 ")+B.dim("Type your SQL queries directly. Type 'exit' or 'quit' to Back.")+" "+B.cyan("\u2551")),console.log(B.cyan(`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
186
- `));t;)try{let e=(await r({message:B.green(`${s.type}>`)})).trim();if(e.toLowerCase()==="exit"||e.toLowerCase()==="quit"){t=!1;continue}if(e==="")continue;let n=await a.query(e);if(n.columns.length===0){console.log(B.yellow("Query executed successfully. (No output)"));continue}ee(n.columns,n.rows,{title:"Query Result",maxColWidth:50}),console.log(B.dim(`
187
- (${n.rows.length} rows)`)),console.log()}catch(o){o.name==="ExitPromptError"?t=!1:console.log(B.red(`
188
- Error: ${o.message}
189
- `))}}import{parseArgs as ao}from"util";async function Vt(){let s=process.argv.slice(2),a;s.length>0&&(s[0].startsWith("postgres://")||s[0].startsWith("postgresql://")||s[0].startsWith("mysql://")||s[0].startsWith("file:"))&&(a=s[0]);let{positionals:t,values:r}=ao({args:s,options:{format:{type:"string"},"schema-only":{type:"boolean"},table:{type:"string"},help:{type:"boolean"}},strict:!1,allowPositionals:!0});r.help&&(console.log(D.cyan(`
1879
+ `;
1880
+ const outPath = path7.resolve(process.cwd(), "drixio_schema.md");
1881
+ await fs6.writeFile(outPath, markdownOutput.trim(), "utf-8");
1882
+ console.log(pc15.green(`
1883
+ \u2714 Diagram generated successfully!`));
1884
+ console.log(pc15.white(`Output saved to: ${pc15.bold(outPath)}`));
1885
+ console.log(pc15.dim("Tip: Open this file in VSCode with a Markdown viewer or paste it into Draw.io."));
1886
+ } catch (e) {
1887
+ console.log(pc15.red(`
1888
+ \u2718 Failed to generate diagram: ${e.message}`));
1889
+ } finally {
1890
+ await adapter.close();
1891
+ }
1892
+ process.exit(0);
1893
+ }
1894
+ var init_diagram = __esm({
1895
+ "subcommands/diagram.ts"() {
1896
+ "use strict";
1897
+ init_factory();
1898
+ }
1899
+ });
1900
+
1901
+ // subcommands/exec.ts
1902
+ var exec_exports = {};
1903
+ __export(exec_exports, {
1904
+ runExecCommand: () => runExecCommand
1905
+ });
1906
+ import pc16 from "picocolors";
1907
+ import fs7 from "fs/promises";
1908
+ import path8 from "path";
1909
+ async function runExecCommand(dbConfig, args) {
1910
+ if (dbConfig.type === "unknown") {
1911
+ console.log(pc16.red("Error: No database connection found. Cannot run exec."));
1912
+ process.exit(1);
1913
+ }
1914
+ let filePath = args[0];
1915
+ const { input: input7 } = await import("@inquirer/prompts");
1916
+ if (!filePath) {
1917
+ filePath = await input7({ message: "Enter the path to your .sql file:" });
1918
+ }
1919
+ const resolvedPath = path8.resolve(process.cwd(), filePath);
1920
+ try {
1921
+ await fs7.access(resolvedPath);
1922
+ } catch {
1923
+ console.log(pc16.red(`Error: File not found at ${resolvedPath}`));
1924
+ process.exit(1);
1925
+ }
1926
+ const sqlContent = await fs7.readFile(resolvedPath, "utf-8");
1927
+ if (!sqlContent.trim()) {
1928
+ console.log(pc16.yellow("File is empty."));
1929
+ process.exit(0);
1930
+ }
1931
+ const adapter = createDBAdapter(dbConfig);
1932
+ console.log(pc16.cyan(`
1933
+ Executing SQL script from ${filePath}...`));
1934
+ try {
1935
+ await adapter.executeSql(sqlContent);
1936
+ console.log(pc16.green(`\u2714 Script executed successfully!`));
1937
+ } catch (e) {
1938
+ console.log(pc16.red(`\u2718 Execution failed: ${e.message}`));
1939
+ } finally {
1940
+ await adapter.close();
1941
+ }
1942
+ process.exit(0);
1943
+ }
1944
+ var init_exec = __esm({
1945
+ "subcommands/exec.ts"() {
1946
+ "use strict";
1947
+ init_factory();
1948
+ }
1949
+ });
1950
+
1951
+ // subcommands/generateTypes.ts
1952
+ var generateTypes_exports = {};
1953
+ __export(generateTypes_exports, {
1954
+ runGenerateTypesCommand: () => runGenerateTypesCommand
1955
+ });
1956
+ import pc17 from "picocolors";
1957
+ import fs8 from "fs/promises";
1958
+ import path9 from "path";
1959
+ function mapSqlTypeToTs(sqlType) {
1960
+ const type = sqlType.toLowerCase();
1961
+ if (type.includes("int") || type.includes("num") || type.includes("float") || type.includes("double") || type.includes("real")) {
1962
+ return "number";
1963
+ }
1964
+ if (type.includes("bool") || type === "tinyint(1)") {
1965
+ return "boolean";
1966
+ }
1967
+ if (type.includes("char") || type.includes("text") || type.includes("uuid") || type.includes("string")) {
1968
+ return "string";
1969
+ }
1970
+ if (type.includes("date") || type.includes("time")) {
1971
+ return "Date";
1972
+ }
1973
+ if (type.includes("json")) {
1974
+ return "any";
1975
+ }
1976
+ return "string";
1977
+ }
1978
+ async function runGenerateTypesCommand(dbConfig) {
1979
+ if (dbConfig.type === "unknown") {
1980
+ console.log(pc17.red("Error: No database connection found. Cannot generate types."));
1981
+ process.exit(1);
1982
+ }
1983
+ const adapter = createDBAdapter(dbConfig);
1984
+ console.log(pc17.cyan(`
1985
+ Scanning database to generate TypeScript interfaces...`));
1986
+ try {
1987
+ const allTables = await adapter.getTables();
1988
+ if (allTables.length === 0) {
1989
+ console.log(pc17.yellow("No tables found in the database."));
1990
+ process.exit(0);
1991
+ }
1992
+ let tsCode = `// Generated by Drixio CLI
1993
+ // Database: ${dbConfig.type.toUpperCase()}
1994
+
1995
+ `;
1996
+ for (const table of allTables) {
1997
+ const interfaceName = table.charAt(0).toUpperCase() + table.slice(1).replace(/_([a-z])/g, (g) => g[1].toUpperCase());
1998
+ tsCode += `export interface ${interfaceName} {
1999
+ `;
2000
+ const schema = await adapter.getSchema(table);
2001
+ for (const col of schema) {
2002
+ const tsType = mapSqlTypeToTs(col.type);
2003
+ const optionalFlag = col.nullable ? "?" : "";
2004
+ tsCode += ` ${col.name}${optionalFlag}: ${tsType};
2005
+ `;
2006
+ }
2007
+ tsCode += `}
2008
+
2009
+ `;
2010
+ }
2011
+ const outPath = path9.resolve(process.cwd(), "drixio-types.d.ts");
2012
+ await fs8.writeFile(outPath, tsCode.trim() + "\n", "utf-8");
2013
+ console.log(pc17.green(`\u2714 TypeScript interfaces generated successfully!`));
2014
+ console.log(pc17.white(`Output saved to: ${pc17.bold(outPath)}`));
2015
+ } catch (e) {
2016
+ console.log(pc17.red(`
2017
+ \u2718 Failed to generate types: ${e.message}`));
2018
+ } finally {
2019
+ await adapter.close();
2020
+ }
2021
+ process.exit(0);
2022
+ }
2023
+ var init_generateTypes = __esm({
2024
+ "subcommands/generateTypes.ts"() {
2025
+ "use strict";
2026
+ init_factory();
2027
+ }
2028
+ });
2029
+
2030
+ // subcommands/init.ts
2031
+ var init_exports = {};
2032
+ __export(init_exports, {
2033
+ runInitCommand: () => runInitCommand
2034
+ });
2035
+ import pc18 from "picocolors";
2036
+ import fs9 from "fs/promises";
2037
+ import path10 from "path";
2038
+ async function runInitCommand(args) {
2039
+ const { select: select11, input: input7, password } = await import("@inquirer/prompts");
2040
+ let dialect = args[0];
2041
+ if (!dialect || !["sqlite", "mysql", "postgres"].includes(dialect.toLowerCase())) {
2042
+ dialect = await select11({
2043
+ message: "Which database do you want to initialize locally?",
2044
+ choices: [
2045
+ { name: "SQLite (Local File)", value: "sqlite" },
2046
+ { name: "MySQL (Local Server)", value: "mysql" },
2047
+ { name: "PostgreSQL (Local Server)", value: "postgres" }
2048
+ ]
2049
+ });
2050
+ }
2051
+ dialect = dialect.toLowerCase();
2052
+ console.log(pc18.cyan(`
2053
+ Initializing a local ${dialect} database...`));
2054
+ let dbUrl = "";
2055
+ if (dialect === "sqlite") {
2056
+ let dbName = args[1] || "database.sqlite";
2057
+ if (!dbName.endsWith(".sqlite") && !dbName.endsWith(".db")) {
2058
+ dbName += ".sqlite";
2059
+ }
2060
+ const dbPath = path10.resolve(process.cwd(), dbName);
2061
+ try {
2062
+ await fs9.access(dbPath);
2063
+ console.log(pc18.yellow(`File ${dbName} already exists.`));
2064
+ } catch {
2065
+ await fs9.writeFile(dbPath, "");
2066
+ console.log(pc18.green(`\u2714 Created local database file: ${dbName}`));
2067
+ }
2068
+ dbUrl = `file:${dbName}`;
2069
+ } else {
2070
+ console.log(
2071
+ pc18.dim(
2072
+ "Please provide credentials for your local server (e.g. running via XAMPP, Homebrew, etc.)"
2073
+ )
2074
+ );
2075
+ const host = await input7({ message: "Server Host:", default: "localhost" });
2076
+ const port = await input7({
2077
+ message: "Server Port:",
2078
+ default: dialect === "mysql" ? "3306" : "5432"
2079
+ });
2080
+ const user = await input7({
2081
+ message: "Username:",
2082
+ default: dialect === "mysql" ? "root" : "postgres"
2083
+ });
2084
+ const pass = await password({ message: "Password (leave empty if none):" });
2085
+ let dbName = args[1];
2086
+ if (!dbName) {
2087
+ dbName = await input7({ message: "New Database Name (e.g. my_project):" });
2088
+ }
2089
+ if (!dbName || !/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(dbName)) {
2090
+ console.log(
2091
+ pc18.red(
2092
+ "Invalid database name. Please use only letters, numbers, and underscores."
2093
+ )
2094
+ );
2095
+ process.exit(1);
2096
+ }
2097
+ console.log(
2098
+ pc18.cyan(`
2099
+ Connecting to local server to create database '${dbName}'...`)
2100
+ );
2101
+ try {
2102
+ if (dialect === "mysql") {
2103
+ const mysql2 = await import("mysql2/promise");
2104
+ const conn = await mysql2.createConnection({
2105
+ host,
2106
+ port: parseInt(port),
2107
+ user,
2108
+ password: pass
2109
+ });
2110
+ await conn.query(`CREATE DATABASE IF NOT EXISTS \`${dbName}\``);
2111
+ await conn.end();
2112
+ } else if (dialect === "postgres") {
2113
+ const pg2 = await import("pg");
2114
+ const conn = new pg2.Client({
2115
+ host,
2116
+ port: parseInt(port),
2117
+ user,
2118
+ password: pass,
2119
+ database: "postgres"
2120
+ // Explicitly connect to default maintenance db
2121
+ });
2122
+ await conn.connect();
2123
+ const res = await conn.query(
2124
+ "SELECT 1 FROM pg_database WHERE datname = $1",
2125
+ [dbName]
2126
+ );
2127
+ if (res.rowCount === 0) {
2128
+ await conn.query(`CREATE DATABASE "${dbName}"`);
2129
+ }
2130
+ await conn.end();
2131
+ }
2132
+ console.log(pc18.green(`\u2714 Created local database: ${dbName}`));
2133
+ } catch (e) {
2134
+ console.log(
2135
+ pc18.red(`\u2718 Failed to create database on server: ${e.message}`)
2136
+ );
2137
+ console.log(
2138
+ pc18.dim(
2139
+ `
2140
+ Could not connect to the local ${dialect} server on ${host}:${port}.`
2141
+ )
2142
+ );
2143
+ if (e.code === "ECONNREFUSED" || e.message.includes("ECONNREFUSED") || e.message.includes("connect")) {
2144
+ console.log(
2145
+ pc18.yellow(
2146
+ `
2147
+ \u{1F4A1} It seems you don't have ${dialect} installed or running locally.`
2148
+ )
2149
+ );
2150
+ if (dialect === "mysql") {
2151
+ console.log(
2152
+ pc18.white(
2153
+ `\u{1F449} Download MySQL here: ${pc18.cyan("https://dev.mysql.com/downloads/installer/")}`
2154
+ )
2155
+ );
2156
+ } else if (dialect === "postgres") {
2157
+ console.log(
2158
+ pc18.white(
2159
+ `\u{1F449} Download PostgreSQL here: ${pc18.cyan("https://www.postgresql.org/download/")}`
2160
+ )
2161
+ );
2162
+ console.log(
2163
+ pc18.white(
2164
+ `\u{1F449} Or use Postgres.app for Mac: ${pc18.cyan("https://postgresapp.com/")}`
2165
+ )
2166
+ );
2167
+ }
2168
+ console.log(
2169
+ pc18.dim(
2170
+ `(Alternatively, you can run 'drixio init sqlite' for a zero-install local database!)`
2171
+ )
2172
+ );
2173
+ }
2174
+ process.exit(1);
2175
+ }
2176
+ const scheme = dialect === "postgres" ? "postgres://" : "mysql://";
2177
+ const auth = pass ? `${user}:${pass}` : `${user}`;
2178
+ dbUrl = `${scheme}${auth}@${host}:${port}/${dbName}`;
2179
+ }
2180
+ const envPath = path10.resolve(process.cwd(), ".env");
2181
+ let envContent = "";
2182
+ try {
2183
+ envContent = await fs9.readFile(envPath, "utf-8");
2184
+ } catch {
2185
+ }
2186
+ if (envContent.includes("DATABASE_URL=")) {
2187
+ envContent = envContent.replace(
2188
+ /DATABASE_URL=.*/g,
2189
+ `DATABASE_URL=${dbUrl}`
2190
+ );
2191
+ console.log(
2192
+ pc18.yellow(`\u2714 Updated existing .env file with new DATABASE_URL`)
2193
+ );
2194
+ } else {
2195
+ envContent += (envContent.endsWith("\n") || envContent.length === 0 ? "" : "\n") + `DATABASE_URL=${dbUrl}
2196
+ `;
2197
+ console.log(pc18.green(`\u2714 Saved connection string to .env file`));
2198
+ }
2199
+ await fs9.writeFile(envPath, envContent, "utf-8");
2200
+ console.log(pc18.green(`
2201
+ \u{1F389} Initialization Complete!`));
2202
+ console.log(
2203
+ pc18.white(
2204
+ `You can now run ${pc18.bold("npx drixio")} to manage it!`
2205
+ )
2206
+ );
2207
+ process.exit(0);
2208
+ }
2209
+ var init_init = __esm({
2210
+ "subcommands/init.ts"() {
2211
+ "use strict";
2212
+ }
2213
+ });
2214
+
2215
+ // subcommands/drop.ts
2216
+ var drop_exports = {};
2217
+ __export(drop_exports, {
2218
+ runDropDbCommand: () => runDropDbCommand
2219
+ });
2220
+ import pc19 from "picocolors";
2221
+ import fs10 from "fs/promises";
2222
+ import path11 from "path";
2223
+ async function runDropDbCommand(args) {
2224
+ const { select: select11, input: input7, password, confirm: confirm4 } = await import("@inquirer/prompts");
2225
+ let dialect = args[0];
2226
+ if (!dialect || !["sqlite", "mysql", "postgres"].includes(dialect.toLowerCase())) {
2227
+ dialect = await select11({
2228
+ message: "Which database type do you want to drop?",
2229
+ choices: [
2230
+ { name: "SQLite (Local File)", value: "sqlite" },
2231
+ { name: "MySQL (Local Server)", value: "mysql" },
2232
+ { name: "PostgreSQL (Local Server)", value: "postgres" }
2233
+ ]
2234
+ });
2235
+ }
2236
+ dialect = dialect.toLowerCase();
2237
+ let dbName = args[1];
2238
+ if (dialect === "sqlite") {
2239
+ if (!dbName) {
2240
+ dbName = await input7({ message: "Enter the SQLite filename to delete (e.g. database.sqlite):", default: "database.sqlite" });
2241
+ }
2242
+ if (!dbName.endsWith(".sqlite") && !dbName.endsWith(".db")) {
2243
+ dbName += ".sqlite";
2244
+ }
2245
+ const sure = await confirm4({ message: `Are you absolutely sure you want to delete ${dbName}? This cannot be undone!`, default: false });
2246
+ if (!sure) {
2247
+ console.log(pc19.yellow("Aborted."));
2248
+ process.exit(0);
2249
+ }
2250
+ const dbPath = path11.resolve(process.cwd(), dbName);
2251
+ try {
2252
+ await fs10.unlink(dbPath);
2253
+ console.log(pc19.green(`\u2714 Deleted local database file: ${dbName}`));
2254
+ } catch (e) {
2255
+ console.log(pc19.red(`\u2718 Failed to delete file: ${e.message}`));
2256
+ }
2257
+ } else {
2258
+ console.log(pc19.dim(`Please provide credentials for your local ${dialect} server to drop a database.`));
2259
+ const host = await input7({ message: "Server Host:", default: "localhost" });
2260
+ const port = await input7({ message: "Server Port:", default: dialect === "mysql" ? "3306" : "5432" });
2261
+ const user = await input7({ message: "Username:", default: dialect === "mysql" ? "root" : "postgres" });
2262
+ const pass = await password({ message: "Password (leave empty if none):" });
2263
+ if (!dbName) {
2264
+ dbName = await input7({ message: "Which Database Name do you want to DROP?" });
2265
+ }
2266
+ const sure = await confirm4({ message: `Are you absolutely sure you want to DROP DATABASE '${dbName}' from ${host}? All data will be lost!`, default: false });
2267
+ if (!sure) {
2268
+ console.log(pc19.yellow("Aborted."));
2269
+ process.exit(0);
2270
+ }
2271
+ console.log(pc19.cyan(`
2272
+ Connecting to local server to drop database '${dbName}'...`));
2273
+ try {
2274
+ if (dialect === "mysql") {
2275
+ const mysql2 = await import("mysql2/promise");
2276
+ const conn = await mysql2.createConnection({ host, port: parseInt(port), user, password: pass });
2277
+ await conn.query(`DROP DATABASE IF EXISTS \`${dbName}\``);
2278
+ await conn.end();
2279
+ } else if (dialect === "postgres") {
2280
+ const pg2 = await import("pg");
2281
+ const conn = new pg2.Client({ host, port: parseInt(port), user, password: pass, database: "postgres" });
2282
+ await conn.connect();
2283
+ await conn.query(`DROP DATABASE IF EXISTS "${dbName}"`);
2284
+ await conn.end();
2285
+ }
2286
+ console.log(pc19.green(`\u2714 Dropped database: ${dbName}`));
2287
+ } catch (e) {
2288
+ console.log(pc19.red(`\u2718 Failed to drop database on server: ${e.message}`));
2289
+ process.exit(1);
2290
+ }
2291
+ }
2292
+ process.exit(0);
2293
+ }
2294
+ var init_drop = __esm({
2295
+ "subcommands/drop.ts"() {
2296
+ "use strict";
2297
+ }
2298
+ });
2299
+
2300
+ // src/cli/menus/tableManager.ts
2301
+ import { select as select6, Separator as Separator3 } from "@inquirer/prompts";
2302
+ import pc20 from "picocolors";
2303
+ var selectTableManager;
2304
+ var init_tableManager = __esm({
2305
+ "src/cli/menus/tableManager.ts"() {
2306
+ "use strict";
2307
+ selectTableManager = async () => await select6({
2308
+ message: "Table Manager - Choose an action:",
2309
+ theme: {
2310
+ prefix: pc20.cyan("\u2713 "),
2311
+ icon: {
2312
+ cursor: pc20.cyan("\u203A ")
2313
+ },
2314
+ style: {
2315
+ message: (text) => pc20.bold(pc20.white(text)),
2316
+ highlight: (text) => {
2317
+ const clean = text.replace(/\x1b\[[0-9;]*m/g, "");
2318
+ return clean.includes(" Back") ? pc20.red(clean) : pc20.cyan(clean);
2319
+ }
2320
+ }
2321
+ },
2322
+ choices: [
2323
+ {
2324
+ name: " Create New Table",
2325
+ value: "create",
2326
+ description: "Build a new database table."
2327
+ },
2328
+ {
2329
+ name: " Modify Existing Table",
2330
+ value: "modify",
2331
+ description: "Modify an existing table's schema structure."
2332
+ },
2333
+ {
2334
+ name: " Drop Table",
2335
+ value: "drop",
2336
+ description: "Delete an entire table and its data."
2337
+ },
2338
+ new Separator3(),
2339
+ {
2340
+ name: pc20.dim(" Back"),
2341
+ value: "back",
2342
+ description: "Back to the main menu."
2343
+ }
2344
+ ]
2345
+ });
2346
+ }
2347
+ });
2348
+
2349
+ // src/cli/wizards/columnWizard.ts
2350
+ import { input as input3, select as select7, confirm } from "@inquirer/prompts";
2351
+ import pc21 from "picocolors";
2352
+ async function promptColumnSchema(adapter, hasPkSoFar, existingColumns = [], defaultName = "") {
2353
+ let colName = "";
2354
+ while (!colName) {
2355
+ colName = await input3({
2356
+ message: "Column name (leave empty to cancel/finish):",
2357
+ default: defaultName
2358
+ });
2359
+ if (!colName.trim()) {
2360
+ return { col: null, isPk: false };
2361
+ }
2362
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(colName)) {
2363
+ console.log(pc21.red("Invalid column name. Use only letters, numbers, and underscores."));
2364
+ colName = "";
2365
+ } else if (existingColumns.includes(colName)) {
2366
+ console.log(pc21.red("Column name already exists!"));
2367
+ colName = "";
2368
+ }
2369
+ }
2370
+ const colType = await select7({
2371
+ message: `Select type for column '${colName}':`,
2372
+ choices: [
2373
+ { name: "Integer", value: "Integer" },
2374
+ { name: "Text (String)", value: "Text" },
2375
+ { name: "Boolean", value: "Boolean" },
2376
+ { name: "Decimal (Float)", value: "Decimal" },
2377
+ { name: "DateTime", value: "DateTime" },
2378
+ { name: "Enum", value: "Enum" }
2379
+ ]
2380
+ });
2381
+ let enumValues = void 0;
2382
+ if (colType === "Enum") {
2383
+ const enumStr = await input3({
2384
+ message: `Enter comma-separated Enum values (e.g. active, pending, deleted):`
2385
+ });
2386
+ enumValues = enumStr.split(",").map((s) => s.trim()).filter((s) => s);
2387
+ if (enumValues.length === 0) {
2388
+ console.log(pc21.yellow("Warning: No enum values provided."));
2389
+ }
2390
+ }
2391
+ let defaultValue = "";
2392
+ if (colType === "DateTime") {
2393
+ const isTimestamp = await confirm({
2394
+ message: `Set default to CURRENT_TIMESTAMP?`,
2395
+ default: false
2396
+ });
2397
+ if (isTimestamp) {
2398
+ defaultValue = "Timestamp";
2399
+ }
2400
+ }
2401
+ let isPk = false;
2402
+ let fkTarget = void 0;
2403
+ if (colType !== "DateTime") {
2404
+ const keyChoices = [{ name: "None", value: "none" }];
2405
+ if (!hasPkSoFar) {
2406
+ keyChoices.push({ name: "Primary Key", value: "pk" });
2407
+ }
2408
+ keyChoices.push({ name: "Foreign Key", value: "fk" });
2409
+ const keyType = await select7({
2410
+ message: `Key type for '${colName}':`,
2411
+ choices: keyChoices
2412
+ });
2413
+ if (keyType === "pk") {
2414
+ isPk = true;
2415
+ if (colType === "Integer") defaultValue = "AutoInc";
2416
+ } else if (keyType === "fk") {
2417
+ const allTables = await adapter.getTables();
2418
+ if (allTables.length > 0) {
2419
+ const targetTable = await select7({
2420
+ message: "Select target table:",
2421
+ choices: allTables.map((t) => ({ name: t, value: t }))
2422
+ });
2423
+ const targetSchema = await adapter.getSchema(targetTable);
2424
+ if (targetSchema.length > 0) {
2425
+ const targetCol = await select7({
2426
+ message: "Select target column:",
2427
+ choices: targetSchema.map((c) => ({
2428
+ name: `${c.name} (${c.type})`,
2429
+ value: c.name
2430
+ }))
2431
+ });
2432
+ fkTarget = { table: targetTable, column: targetCol };
2433
+ defaultValue = `FK -> ${targetTable}.${targetCol}`;
2434
+ } else {
2435
+ console.log(pc21.yellow("Target table has no columns."));
2436
+ }
2437
+ } else {
2438
+ console.log(pc21.yellow("No other tables found to link to."));
2439
+ }
2440
+ }
2441
+ }
2442
+ let nullable = false;
2443
+ if (!isPk) {
2444
+ nullable = await confirm({
2445
+ message: `Can '${colName}' be NULL?`,
2446
+ default: true
2447
+ });
2448
+ }
2449
+ if (!isPk && !fkTarget && colType !== "DateTime") {
2450
+ if (colType === "Boolean") {
2451
+ const defaultBool = await select7({
2452
+ message: `Default value for '${colName}':`,
2453
+ choices: [
2454
+ { name: "None", value: "" },
2455
+ { name: "TRUE", value: "TRUE" },
2456
+ { name: "FALSE", value: "FALSE" }
2457
+ ]
2458
+ });
2459
+ defaultValue = defaultBool;
2460
+ } else if (colType === "Enum" && enumValues) {
2461
+ const defaultEnum = await select7({
2462
+ message: `Default value for '${colName}':`,
2463
+ choices: [
2464
+ { name: "None", value: "" },
2465
+ ...enumValues.map((v) => ({ name: v, value: `'${v}'` }))
2466
+ ]
2467
+ });
2468
+ defaultValue = defaultEnum;
2469
+ } else {
2470
+ const defaultStr = await input3({
2471
+ message: `Default value for '${colName}' (leave empty for none):`
2472
+ });
2473
+ if (defaultStr) {
2474
+ if (colType === "Text") {
2475
+ defaultValue = `'${defaultStr.replace(/'/g, "''")}'`;
2476
+ } else {
2477
+ defaultValue = defaultStr;
2478
+ }
2479
+ }
2480
+ }
2481
+ }
2482
+ const col = {
2483
+ name: colName,
2484
+ type: colType,
2485
+ isPk,
2486
+ nullable,
2487
+ defaultValue,
2488
+ fkTarget,
2489
+ enumValues
2490
+ };
2491
+ return { col, isPk };
2492
+ }
2493
+ var init_columnWizard = __esm({
2494
+ "src/cli/wizards/columnWizard.ts"() {
2495
+ "use strict";
2496
+ }
2497
+ });
2498
+
2499
+ // src/cli/wizards/buildTable.ts
2500
+ import { input as input4, confirm as confirm2 } from "@inquirer/prompts";
2501
+ import pc22 from "picocolors";
2502
+ async function runWizard(dbConfig) {
2503
+ const adapter = createDBAdapter(dbConfig);
2504
+ let tableName = "";
2505
+ while (!tableName) {
2506
+ tableName = await input4({
2507
+ message: "Enter the new table name:"
2508
+ });
2509
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tableName)) {
2510
+ console.log(
2511
+ pc22.red(
2512
+ "Invalid table name. Use only letters, numbers, and underscores."
2513
+ )
2514
+ );
2515
+ tableName = "";
2516
+ }
2517
+ }
2518
+ const columns = [];
2519
+ let hasPk = false;
2520
+ let addMore = true;
2521
+ while (addMore) {
2522
+ printCurrentColumns(tableName, columns);
2523
+ const existingColumns = columns.map((c) => c.name);
2524
+ const { col, isPk } = await promptColumnSchema(adapter, hasPk, existingColumns);
2525
+ if (!col) {
2526
+ break;
2527
+ }
2528
+ if (isPk) hasPk = true;
2529
+ columns.push(col);
2530
+ addMore = await confirm2({
2531
+ theme: {
2532
+ prefix: pc22.cyan("\n\u2713 "),
2533
+ style: {
2534
+ message: (text) => pc22.bold(pc22.white(text)),
2535
+ highlight: (text) => {
2536
+ const clean = text.replace(/\x1b\[[0-9;]*m/g, "");
2537
+ return pc22.cyan(clean);
2538
+ }
2539
+ }
2540
+ },
2541
+ message: "Add another column?",
2542
+ default: true
2543
+ });
2544
+ }
2545
+ printCurrentColumns(tableName, columns);
2546
+ console.log(pc22.dim("Generating SQL..."));
2547
+ const dialect = getDialect(dbConfig.type);
2548
+ const sql = dialect.buildCreateTable(tableName, columns);
2549
+ console.log(pc22.cyan("\n--- SQL Preview ---"));
2550
+ console.log(pc22.yellow(sql));
2551
+ console.log(pc22.cyan("-------------------\n"));
2552
+ const proceed = await confirm2({
2553
+ message: "Do you want to execute this SQL to build the table?",
2554
+ default: true
2555
+ });
2556
+ if (proceed) {
2557
+ try {
2558
+ console.log(pc22.dim("Executing SQL..."));
2559
+ await adapter.executeSql(sql);
2560
+ console.log(pc22.green(`
2561
+ \u2713 Table '${tableName}' created successfully!`));
2562
+ } catch (e) {
2563
+ console.log(pc22.red(`
2564
+ x Error creating table: ${e.message}`));
2565
+ } finally {
2566
+ await adapter.close();
2567
+ }
2568
+ } else {
2569
+ console.log(pc22.yellow("Aborted table creation."));
2570
+ await adapter.close();
2571
+ }
2572
+ await waitForEnter2();
2573
+ }
2574
+ async function waitForEnter2() {
2575
+ const { input: input7 } = await import("@inquirer/prompts");
2576
+ await input7({
2577
+ message: "Press Enter to continue..."
2578
+ });
2579
+ }
2580
+ function printCurrentColumns(tableName, columns) {
2581
+ console.clear();
2582
+ console.log("\n");
2583
+ const colWidths = {
2584
+ name: 20,
2585
+ type: 18,
2586
+ key: 8,
2587
+ nullable: 10,
2588
+ defaultCol: 16
2589
+ };
2590
+ const totalWidth = colWidths.name + colWidths.type + colWidths.key + colWidths.nullable + colWidths.defaultCol + 14;
2591
+ console.log(pc22.cyan(`\u2554${"\u2550".repeat(totalWidth)}\u2557`));
2592
+ const spacesNeeded = totalWidth - tableName.length;
2593
+ const leftSpace = Math.max(0, Math.floor(spacesNeeded / 2));
2594
+ const rightSpace = Math.max(0, spacesNeeded - leftSpace);
2595
+ const titleContent = " ".repeat(leftSpace) + pc22.bold(pc22.white(tableName)) + " ".repeat(rightSpace);
2596
+ console.log(pc22.cyan("\u2551") + titleContent + pc22.cyan("\u2551"));
2597
+ const drawDivider = (left, mid, right) => {
2598
+ return pc22.cyan(
2599
+ left + "\u2550".repeat(colWidths.name + 2) + mid + "\u2550".repeat(colWidths.type + 2) + mid + "\u2550".repeat(colWidths.key + 2) + mid + "\u2550".repeat(colWidths.nullable + 2) + mid + "\u2550".repeat(colWidths.defaultCol + 2) + right
2600
+ );
2601
+ };
2602
+ console.log(drawDivider("\u2560", "\u2566", "\u2563"));
2603
+ console.log(
2604
+ pc22.cyan("\u2551 ") + pc22.bold(pc22.white("Column Name".padEnd(colWidths.name))) + pc22.cyan(" \u2551 ") + pc22.bold(pc22.white("Type".padEnd(colWidths.type))) + pc22.cyan(" \u2551 ") + pc22.bold(pc22.white("Key".padEnd(colWidths.key))) + pc22.cyan(" \u2551 ") + pc22.bold(pc22.white("Nullable".padEnd(colWidths.nullable))) + pc22.cyan(" \u2551 ") + pc22.bold(pc22.white("Default".padEnd(colWidths.defaultCol))) + pc22.cyan(" \u2551")
2605
+ );
2606
+ if (columns.length > 0) {
2607
+ console.log(drawDivider("\u2560", "\u256C", "\u2563"));
2608
+ }
2609
+ for (let i = 0; i < columns.length; i++) {
2610
+ const col = columns[i];
2611
+ const nameStr = pc22.white(col.name.padEnd(colWidths.name));
2612
+ const typeStr = pc22.white(col.type.padEnd(colWidths.type));
2613
+ const pkRaw = col.isPk ? "PK" : "-";
2614
+ const pkStr = col.isPk ? pc22.green(pkRaw.padEnd(colWidths.key)) : pc22.dim(pkRaw.padEnd(colWidths.key));
2615
+ const nullRaw = col.nullable ? "Yes" : "No";
2616
+ const nullStr = col.nullable ? pc22.green(nullRaw.padEnd(colWidths.nullable)) : pc22.dim(nullRaw.padEnd(colWidths.nullable));
2617
+ const extraRaw = col.defaultValue || "-";
2618
+ const extraStr = col.defaultValue ? pc22.yellow(extraRaw.padEnd(colWidths.defaultCol)) : pc22.dim(extraRaw.padEnd(colWidths.defaultCol));
2619
+ console.log(
2620
+ pc22.cyan("\u2551 ") + nameStr + pc22.cyan(" \u2551 ") + typeStr + pc22.cyan(" \u2551 ") + pkStr + pc22.cyan(" \u2551 ") + nullStr + pc22.cyan(" \u2551 ") + extraStr + pc22.cyan(" \u2551")
2621
+ );
2622
+ }
2623
+ if (columns.length === 0) {
2624
+ console.log(drawDivider("\u2560", "\u2569", "\u2563"));
2625
+ const emptyMsg = "No columns added yet";
2626
+ const emptyLeft = Math.max(
2627
+ 0,
2628
+ Math.floor((totalWidth - emptyMsg.length) / 2)
2629
+ );
2630
+ const emptyRight = Math.max(0, totalWidth - emptyMsg.length - emptyLeft);
2631
+ console.log(
2632
+ pc22.cyan("\u2551") + " ".repeat(emptyLeft) + pc22.dim(emptyMsg) + " ".repeat(emptyRight) + pc22.cyan("\u2551")
2633
+ );
2634
+ console.log(pc22.cyan(`\u255A${"\u2550".repeat(totalWidth)}\u255D`));
2635
+ } else {
2636
+ console.log(drawDivider("\u255A", "\u2569", "\u255D"));
2637
+ }
2638
+ console.log(pc22.dim(" Leave empty the Column Name to cancel.\n"));
2639
+ }
2640
+ var init_buildTable = __esm({
2641
+ "src/cli/wizards/buildTable.ts"() {
2642
+ "use strict";
2643
+ init_dialect();
2644
+ init_factory();
2645
+ init_columnWizard();
2646
+ }
2647
+ });
2648
+
2649
+ // src/cli/views/sqlRunner.ts
2650
+ import fs11 from "fs/promises";
2651
+ import path12 from "path";
2652
+ import { input as input5 } from "@inquirer/prompts";
2653
+ import pc23 from "picocolors";
2654
+ async function runSqlRunner(dbConfig) {
2655
+ console.log(
2656
+ pc23.green(
2657
+ "\nInitiating SQL runner for experts. Please ensure database connection is properly configured."
2658
+ )
2659
+ );
2660
+ if (dbConfig.type === "unknown") {
2661
+ console.log(
2662
+ pc23.yellow(`
2663
+ No database connection found. Please setup first.`)
2664
+ );
2665
+ return;
2666
+ }
2667
+ try {
2668
+ const filePath = await input5({
2669
+ message: "Enter the path to your .md or .sql file:"
2670
+ });
2671
+ if (filePath && filePath.trim()) {
2672
+ const absolutePath = path12.resolve(process.cwd(), filePath.trim());
2673
+ let fileContent = "";
2674
+ try {
2675
+ fileContent = await fs11.readFile(absolutePath, "utf-8");
2676
+ } catch (err) {
2677
+ console.log(pc23.red(`
2678
+ x Failed to read file: ${err.message}`));
2679
+ return;
2680
+ }
2681
+ let sqlToExecute = fileContent;
2682
+ if (absolutePath.toLowerCase().endsWith(".md")) {
2683
+ const matches = [
2684
+ ...fileContent.matchAll(/```(?:sql)?\n([\s\S]*?)```/gi)
2685
+ ];
2686
+ if (matches.length > 0) {
2687
+ sqlToExecute = matches.map((m) => m[1].trim()).join("\n\n");
2688
+ }
2689
+ }
2690
+ if (sqlToExecute.trim()) {
2691
+ console.log(pc23.dim("Executing SQL..."));
2692
+ const adapter = createDBAdapter(dbConfig);
2693
+ try {
2694
+ await adapter.executeSql(sqlToExecute);
2695
+ console.log(pc23.green("\n\u2713 SQL executed successfully!"));
2696
+ } finally {
2697
+ await adapter.close();
2698
+ }
2699
+ } else {
2700
+ console.log(pc23.yellow("\n\u26A0\uFE0F No SQL found in the file. Aborted."));
2701
+ }
2702
+ } else {
2703
+ console.log(pc23.yellow("\nNo file path entered. Aborted."));
2704
+ }
2705
+ } catch (e) {
2706
+ console.log(pc23.red(`
2707
+ Error executing SQL: ${e.message}`));
2708
+ }
2709
+ }
2710
+ var init_sqlRunner = __esm({
2711
+ "src/cli/views/sqlRunner.ts"() {
2712
+ "use strict";
2713
+ init_factory();
2714
+ }
2715
+ });
2716
+
2717
+ // src/cli/wizards/modifyTable.ts
2718
+ import { select as select9, confirm as confirm3, input as input6 } from "@inquirer/prompts";
2719
+ import pc24 from "picocolors";
2720
+ async function runDropTable(dbConfig) {
2721
+ const adapter = createDBAdapter(dbConfig);
2722
+ try {
2723
+ const tables = await adapter.getTables();
2724
+ if (tables.length === 0) {
2725
+ console.log(pc24.yellow("No tables found in the database."));
2726
+ return;
2727
+ }
2728
+ const targetTable = await select9({
2729
+ message: "Select a table to drop:",
2730
+ choices: tables.map((t) => ({ name: t, value: t }))
2731
+ });
2732
+ const sure = await confirm3({
2733
+ message: `Are you sure you want to DROP TABLE '${targetTable}'? This will delete all its data!`,
2734
+ default: false
2735
+ });
2736
+ if (sure) {
2737
+ let sql = `DROP TABLE ${targetTable}`;
2738
+ if (dbConfig.type === "sqlite" || dbConfig.type === "postgres") {
2739
+ sql = `DROP TABLE "${targetTable}"`;
2740
+ } else if (dbConfig.type === "mysql") {
2741
+ sql = `DROP TABLE \`${targetTable}\``;
2742
+ }
2743
+ await adapter.executeSql(sql);
2744
+ console.log(pc24.green(`\u2713 Table '${targetTable}' dropped successfully.`));
2745
+ }
2746
+ } catch (e) {
2747
+ console.log(pc24.red(`Error: ${e.message}`));
2748
+ } finally {
2749
+ await adapter.close();
2750
+ }
2751
+ }
2752
+ async function runModifyTable(dbConfig, action) {
2753
+ const adapter = createDBAdapter(dbConfig);
2754
+ try {
2755
+ const tables = await adapter.getTables();
2756
+ if (tables.length === 0) {
2757
+ console.log(pc24.yellow("No tables found in the database."));
2758
+ return;
2759
+ }
2760
+ const targetTable = await select9({
2761
+ message: "Select a table to modify:",
2762
+ choices: tables.map((t) => ({ name: t, value: t }))
2763
+ });
2764
+ const schema = await adapter.getSchema(targetTable);
2765
+ if (action === "add") {
2766
+ const hasPk = schema.some((c) => c.isPk);
2767
+ const existingColumns = schema.map((c) => c.name);
2768
+ const { col } = await promptColumnSchema(adapter, hasPk, existingColumns);
2769
+ if (col) {
2770
+ const dialect = getDialect(dbConfig.type);
2771
+ const colSql = dialect.buildCreateTable("tmp", [col]).split("\n")[1].trim().replace(/,$/, "");
2772
+ let sql = `ALTER TABLE ${targetTable} ADD COLUMN ${colSql}`;
2773
+ if (dbConfig.type === "sqlite" || dbConfig.type === "postgres") {
2774
+ sql = `ALTER TABLE "${targetTable}" ADD COLUMN ${colSql}`;
2775
+ } else if (dbConfig.type === "mysql") {
2776
+ sql = `ALTER TABLE \`${targetTable}\` ADD COLUMN ${colSql}`;
2777
+ }
2778
+ await adapter.executeSql(sql);
2779
+ console.log(pc24.green(`\u2713 Column '${col.name}' added successfully.`));
2780
+ }
2781
+ } else if (action === "rename") {
2782
+ if (schema.length === 0) return console.log(pc24.yellow("Table has no columns."));
2783
+ const oldCol = await select9({
2784
+ message: "Select a column to rename:",
2785
+ choices: schema.map((c) => ({ name: `${c.name} (${c.type})`, value: c.name }))
2786
+ });
2787
+ const newCol = await input6({ message: `New name for '${oldCol}':` });
2788
+ if (newCol && newCol !== oldCol) {
2789
+ let sql = `ALTER TABLE ${targetTable} RENAME COLUMN ${oldCol} TO ${newCol}`;
2790
+ if (dbConfig.type === "sqlite" || dbConfig.type === "postgres") {
2791
+ sql = `ALTER TABLE "${targetTable}" RENAME COLUMN "${oldCol}" TO "${newCol}"`;
2792
+ } else if (dbConfig.type === "mysql") {
2793
+ sql = `ALTER TABLE \`${targetTable}\` RENAME COLUMN \`${oldCol}\` TO \`${newCol}\``;
2794
+ }
2795
+ await adapter.executeSql(sql);
2796
+ console.log(pc24.green(`\u2713 Column renamed to '${newCol}' successfully.`));
2797
+ }
2798
+ } else if (action === "modify") {
2799
+ if (schema.length === 0) return console.log(pc24.yellow("Table has no columns."));
2800
+ if (dbConfig.type === "sqlite") {
2801
+ console.log(pc24.yellow("SQLite does not support altering column types directly. Please recreate the table."));
2802
+ return;
2803
+ }
2804
+ const targetCol = await select9({
2805
+ message: "Select a column to modify:",
2806
+ choices: schema.map((c) => ({ name: `${c.name} (${c.type})`, value: c.name }))
2807
+ });
2808
+ const hasPk = schema.some((c) => c.isPk && c.name !== targetCol);
2809
+ const existingColumns = schema.map((c) => c.name).filter((n) => n !== targetCol);
2810
+ const { col } = await promptColumnSchema(adapter, hasPk, existingColumns, targetCol);
2811
+ if (col) {
2812
+ let sql = "";
2813
+ if (dbConfig.type === "postgres") {
2814
+ const pgTypeMap = {
2815
+ Integer: "INTEGER",
2816
+ Text: "TEXT",
2817
+ Boolean: "BOOLEAN",
2818
+ Decimal: "NUMERIC",
2819
+ DateTime: "TIMESTAMP",
2820
+ Enum: "TEXT"
2821
+ };
2822
+ const pgType = pgTypeMap[col.type] ?? "TEXT";
2823
+ const statements = [
2824
+ `ALTER TABLE "${targetTable}" ALTER COLUMN "${col.name}" TYPE ${pgType} USING "${col.name}"::${pgType}`
2825
+ ];
2826
+ if (!col.nullable && !col.isPk) {
2827
+ statements.push(`ALTER TABLE "${targetTable}" ALTER COLUMN "${col.name}" SET NOT NULL`);
2828
+ } else {
2829
+ statements.push(`ALTER TABLE "${targetTable}" ALTER COLUMN "${col.name}" DROP NOT NULL`);
2830
+ }
2831
+ if (col.defaultValue && col.defaultValue !== "AutoInc" && !col.defaultValue.startsWith("FK ->")) {
2832
+ const defVal = col.defaultValue === "Timestamp" ? "CURRENT_TIMESTAMP" : col.defaultValue;
2833
+ statements.push(`ALTER TABLE "${targetTable}" ALTER COLUMN "${col.name}" SET DEFAULT ${defVal}`);
2834
+ } else {
2835
+ statements.push(`ALTER TABLE "${targetTable}" ALTER COLUMN "${col.name}" DROP DEFAULT`);
2836
+ }
2837
+ sql = statements.join(";\n");
2838
+ console.log(pc24.yellow("Note: Complex constraint modifications might require raw SQL in Postgres."));
2839
+ } else if (dbConfig.type === "mysql") {
2840
+ const dialect = getDialect(dbConfig.type);
2841
+ const colDef = dialect.buildCreateTable("tmp", [col]).split("\n")[1].trim().replace(/,$/, "");
2842
+ sql = `ALTER TABLE \`${targetTable}\` MODIFY COLUMN ${colDef}`;
2843
+ }
2844
+ try {
2845
+ await adapter.executeSql(sql);
2846
+ console.log(pc24.green(`\u2713 Column '${targetCol}' modified successfully.`));
2847
+ } catch (e) {
2848
+ console.log(pc24.red(`x Could not modify column automatically. Try using Raw SQL.`));
2849
+ console.log(pc24.dim(e.message));
2850
+ }
2851
+ }
2852
+ } else if (action === "delete") {
2853
+ if (schema.length === 0) return console.log(pc24.yellow("Table has no columns."));
2854
+ const targetCol = await select9({
2855
+ message: "Select a column to delete:",
2856
+ choices: schema.map((c) => ({ name: `${c.name} (${c.type})`, value: c.name }))
2857
+ });
2858
+ const sure = await confirm3({
2859
+ message: `Are you sure you want to delete column '${targetCol}'? Data will be lost!`,
2860
+ default: false
2861
+ });
2862
+ if (sure) {
2863
+ let sql = `ALTER TABLE ${targetTable} DROP COLUMN ${targetCol}`;
2864
+ if (dbConfig.type === "sqlite" || dbConfig.type === "postgres") {
2865
+ sql = `ALTER TABLE "${targetTable}" DROP COLUMN "${targetCol}"`;
2866
+ } else if (dbConfig.type === "mysql") {
2867
+ sql = `ALTER TABLE \`${targetTable}\` DROP COLUMN \`${targetCol}\``;
2868
+ }
2869
+ try {
2870
+ await adapter.executeSql(sql);
2871
+ console.log(pc24.green(`\u2713 Column '${targetCol}' deleted successfully.`));
2872
+ } catch (e) {
2873
+ console.log(pc24.red(`x Error deleting column: ${e.message}`));
2874
+ }
2875
+ }
2876
+ }
2877
+ } catch (e) {
2878
+ console.log(pc24.red(`Error: ${e.message}`));
2879
+ } finally {
2880
+ await adapter.close();
2881
+ }
2882
+ }
2883
+ var init_modifyTable = __esm({
2884
+ "src/cli/wizards/modifyTable.ts"() {
2885
+ "use strict";
2886
+ init_factory();
2887
+ init_dialect();
2888
+ init_columnWizard();
2889
+ }
2890
+ });
2891
+
2892
+ // src/cli/wizards/tableManagerFlow.ts
2893
+ var tableManagerFlow_exports = {};
2894
+ __export(tableManagerFlow_exports, {
2895
+ runTableManagerFlow: () => runTableManagerFlow
2896
+ });
2897
+ import pc25 from "picocolors";
2898
+ import { select as select10, Separator as Separator4 } from "@inquirer/prompts";
2899
+ async function runTableManagerFlow(dbConfig) {
2900
+ if (dbConfig.type === "unknown") {
2901
+ console.log(
2902
+ pc25.yellow(`
2903
+ No database connection found. Please setup first.`)
2904
+ );
2905
+ await waitForEnter3();
2906
+ return;
2907
+ }
2908
+ const adapter = await Promise.resolve().then(() => (init_factory(), factory_exports)).then(
2909
+ (m) => m.createDBAdapter(dbConfig)
2910
+ );
2911
+ let running = true;
2912
+ while (running) {
2913
+ console.clear();
2914
+ let tablesCount = "Scanning...";
2915
+ try {
2916
+ const tables = await adapter.getTables();
2917
+ tablesCount = `${tables.length} tables detected`;
2918
+ } catch (e) {
2919
+ tablesCount = pc25.red(e.message);
2920
+ }
2921
+ const targetVal = dbConfig.targetUrl;
2922
+ printCustomDashboard(` Table Builder & Manager \u2022 [${dbConfig.type.toUpperCase()}]`, [
2923
+ { label: "Database", value: dbConfig.type.toUpperCase() },
2924
+ { label: "Target", value: targetVal.length > 45 ? "..." + targetVal.slice(-42) : targetVal },
2925
+ { label: "Tables", value: tablesCount }
2926
+ ]);
2927
+ const action = await selectTableManager();
2928
+ switch (action) {
2929
+ case "create":
2930
+ const createMethod = await select10({
2931
+ message: "How do you want to create the table?",
2932
+ theme: {
2933
+ prefix: pc25.cyan("\u2713 "),
2934
+ icon: {
2935
+ cursor: pc25.cyan("\u203A ")
2936
+ },
2937
+ style: {
2938
+ message: (text) => pc25.bold(pc25.white(text)),
2939
+ highlight: (text) => {
2940
+ const clean = text.replace(/\x1b\[[0-9;]*m/g, "");
2941
+ return clean.includes("Cancel") ? pc25.red(clean) : pc25.cyan(clean);
2942
+ }
2943
+ }
2944
+ },
2945
+ choices: [
2946
+ {
2947
+ name: " Interactive Wizard (Beginner Friendly)",
2948
+ value: "wizard",
2949
+ description: "Follow the step-by-step guide to build your table."
2950
+ },
2951
+ {
2952
+ name: " Raw SQL (Experts)",
2953
+ value: "sql",
2954
+ description: "Write / Import your own CREATE TABLE statement."
2955
+ },
2956
+ new Separator4(),
2957
+ {
2958
+ name: pc25.dim(" Cancel"),
2959
+ value: "cancel",
2960
+ description: "Back to Table Manager"
2961
+ }
2962
+ ]
2963
+ });
2964
+ if (createMethod === "wizard") await runWizard(dbConfig);
2965
+ else if (createMethod === "sql") await runSqlRunner(dbConfig);
2966
+ break;
2967
+ case "modify":
2968
+ await handleModifyMenu(dbConfig);
2969
+ break;
2970
+ case "drop":
2971
+ await runDropTable(dbConfig);
2972
+ await waitForEnter3();
2973
+ break;
2974
+ case "back":
2975
+ running = false;
2976
+ break;
2977
+ }
2978
+ }
2979
+ await adapter.close();
2980
+ }
2981
+ async function handleModifyMenu(dbConfig) {
2982
+ const action = await select10({
2983
+ message: "Modify Table Actions:",
2984
+ choices: [
2985
+ { name: "Add Column", value: "add_col" },
2986
+ { name: "Rename Column", value: "rename_col" },
2987
+ {
2988
+ name: "Modify Column Settings (MySQL/Postgres)",
2989
+ value: "mod_col",
2990
+ disabled: dbConfig.type === "sqlite"
2991
+ },
2992
+ { name: "Delete Column", value: "del_col" },
2993
+ { name: " Back", value: "back" }
2994
+ ]
2995
+ });
2996
+ if (action === "back") return;
2997
+ switch (action) {
2998
+ case "add_col":
2999
+ await runModifyTable(dbConfig, "add");
3000
+ break;
3001
+ case "rename_col":
3002
+ await runModifyTable(dbConfig, "rename");
3003
+ break;
3004
+ case "mod_col":
3005
+ await runModifyTable(dbConfig, "modify");
3006
+ break;
3007
+ case "del_col":
3008
+ await runModifyTable(dbConfig, "delete");
3009
+ break;
3010
+ }
3011
+ await waitForEnter3();
3012
+ }
3013
+ async function waitForEnter3() {
3014
+ const { input: input7 } = await import("@inquirer/prompts");
3015
+ await input7({
3016
+ message: "Press Enter to continue..."
3017
+ });
3018
+ }
3019
+ var init_tableManagerFlow = __esm({
3020
+ "src/cli/wizards/tableManagerFlow.ts"() {
3021
+ "use strict";
3022
+ init_tableManager();
3023
+ init_buildTable();
3024
+ init_sqlRunner();
3025
+ init_modifyTable();
3026
+ init_logo();
3027
+ }
3028
+ });
3029
+
3030
+ // src/cli/main.ts
3031
+ init_logo();
3032
+ import pc26 from "picocolors";
3033
+
3034
+ // src/core/loader.ts
3035
+ import fs from "fs/promises";
3036
+ import path from "path";
3037
+ async function detectDatabase(databaseUrl) {
3038
+ const cwd = process.cwd();
3039
+ if (databaseUrl) {
3040
+ let type = "sqlite";
3041
+ if (databaseUrl.startsWith("postgres://") || databaseUrl.startsWith("postgresql://")) {
3042
+ type = "postgres";
3043
+ } else if (databaseUrl.startsWith("mysql://")) {
3044
+ type = "mysql";
3045
+ } else {
3046
+ type = "sqlite";
3047
+ }
3048
+ let targetUrl = databaseUrl.replace("file:", "");
3049
+ if (type === "sqlite" && !path.isAbsolute(targetUrl)) {
3050
+ targetUrl = path.resolve(cwd, targetUrl);
3051
+ }
3052
+ return {
3053
+ type,
3054
+ targetUrl,
3055
+ source: "manual"
3056
+ };
3057
+ }
3058
+ const searchDirs = [
3059
+ ".",
3060
+ "prisma",
3061
+ "db",
3062
+ "database",
3063
+ "src/db",
3064
+ "src/database"
3065
+ ];
3066
+ for (const dir of searchDirs) {
3067
+ try {
3068
+ const targetDir = path.join(cwd, dir);
3069
+ const files = await fs.readdir(targetDir);
3070
+ const sqliteFile = files.find(
3071
+ (file) => file.endsWith(".db") || file.endsWith(".sqlite") || file.endsWith(".sqlite3")
3072
+ );
3073
+ if (sqliteFile) {
3074
+ return {
3075
+ type: "sqlite",
3076
+ targetUrl: path.join(targetDir, sqliteFile),
3077
+ source: "auto-detected"
3078
+ };
3079
+ }
3080
+ } catch {
3081
+ }
3082
+ }
3083
+ const prismaSchemaPath = path.join(cwd, "prisma", "schema.prisma");
3084
+ try {
3085
+ const schemaContent = await fs.readFile(prismaSchemaPath, "utf-8");
3086
+ const providerMatch = schemaContent.match(
3087
+ /provider\s*=\s*["']([^"']+)["']/
3088
+ );
3089
+ const urlMatch = schemaContent.match(
3090
+ /url\s*=\s*(?:env\(["']([^"']+)["']\)|["']([^"']+)["'])/
3091
+ );
3092
+ if (providerMatch) {
3093
+ let provider = providerMatch[1];
3094
+ if (provider === "postgresql") provider = "postgres";
3095
+ let targetUrl = "";
3096
+ if (urlMatch && urlMatch[2]) {
3097
+ targetUrl = urlMatch[2];
3098
+ if (targetUrl.startsWith("file:")) {
3099
+ targetUrl = path.join(cwd, "prisma", targetUrl.replace("file:", ""));
3100
+ }
3101
+ }
3102
+ if (targetUrl && (provider === "sqlite" || provider === "postgres" || provider === "mysql")) {
3103
+ return {
3104
+ type: provider,
3105
+ targetUrl,
3106
+ source: ".env"
3107
+ // Treats static config as .env source or configuration
3108
+ };
3109
+ }
3110
+ }
3111
+ } catch {
3112
+ }
3113
+ const envPath = path.join(cwd, ".env");
3114
+ try {
3115
+ const envContent = await fs.readFile(envPath, "utf-8");
3116
+ const dbKeys = [
3117
+ "DATABASE_URL",
3118
+ "DB_URL",
3119
+ "POSTGRES_URL",
3120
+ "POSTGRES_PRISMA_URL",
3121
+ "MYSQL_URL"
3122
+ ];
3123
+ for (const key of dbKeys) {
3124
+ const regex = new RegExp(`${key}\\s*=\\s*["']?([^"'\\r\\n]+)["']?`);
3125
+ const match = envContent.match(regex);
3126
+ if (match) {
3127
+ const url = match[1];
3128
+ if (url.startsWith("file:") || url.endsWith(".db") || url.includes(".sqlite")) {
3129
+ let targetUrl = url.replace("file:", "");
3130
+ if (!path.isAbsolute(targetUrl)) {
3131
+ targetUrl = path.resolve(cwd, targetUrl);
3132
+ }
3133
+ return {
3134
+ type: "sqlite",
3135
+ targetUrl,
3136
+ source: ".env"
3137
+ };
3138
+ } else if (url.startsWith("postgres://") || url.startsWith("postgresql://")) {
3139
+ return {
3140
+ type: "postgres",
3141
+ targetUrl: url,
3142
+ source: ".env"
3143
+ };
3144
+ } else if (url.startsWith("mysql://")) {
3145
+ return {
3146
+ type: "mysql",
3147
+ targetUrl: url,
3148
+ source: ".env"
3149
+ };
3150
+ }
3151
+ }
3152
+ }
3153
+ } catch (error) {
3154
+ }
3155
+ return {
3156
+ type: "unknown",
3157
+ targetUrl: "",
3158
+ source: "manual"
3159
+ };
3160
+ }
3161
+ async function saveDatabaseUrl(url) {
3162
+ const envPath = path.join(process.cwd(), ".env");
3163
+ let envContent = "";
3164
+ try {
3165
+ envContent = await fs.readFile(envPath, "utf-8");
3166
+ } catch (e) {
3167
+ }
3168
+ const regex = /DATABASE_URL\s*=\s*["']?([^"'\r\n]+)["']?/;
3169
+ if (regex.test(envContent)) {
3170
+ envContent = envContent.replace(regex, `DATABASE_URL="${url}"`);
3171
+ } else {
3172
+ if (envContent && !envContent.endsWith("\n")) {
3173
+ envContent += "\n";
3174
+ }
3175
+ envContent += `DATABASE_URL="${url}"
3176
+ `;
3177
+ }
3178
+ await fs.writeFile(envPath, envContent, "utf-8");
3179
+ }
3180
+
3181
+ // src/cli/views/editor.ts
3182
+ init_factory();
3183
+ import { select as select3, Separator } from "@inquirer/prompts";
3184
+ import pc5 from "picocolors";
3185
+
3186
+ // src/cli/menus/table.ts
3187
+ import { select } from "@inquirer/prompts";
3188
+ import pc2 from "picocolors";
3189
+ var selectTable = async (tableChoices) => await select({
3190
+ message: "Select a table to edit data:",
3191
+ theme: {
3192
+ prefix: pc2.cyan("\u2713 "),
3193
+ icon: {
3194
+ cursor: pc2.cyan("\u203A ")
3195
+ },
3196
+ style: {
3197
+ message: (text) => pc2.bold(pc2.white(text)),
3198
+ highlight: (text) => {
3199
+ const clean = text.replace(/\x1b\[[0-9;]*m/g, "");
3200
+ return clean.includes(" Back") ? pc2.red(clean) : pc2.cyan(clean);
3201
+ }
3202
+ }
3203
+ },
3204
+ choices: tableChoices
3205
+ });
3206
+
3207
+ // src/cli/wizards/dataEditor.ts
3208
+ init_dialect();
3209
+ import { input, select as select2 } from "@inquirer/prompts";
3210
+ import pc3 from "picocolors";
3211
+ import fs2 from "fs/promises";
3212
+ import path2 from "path";
3213
+ async function runBeginnerAdd(adapter, dbType, tableName, columns) {
3214
+ const dialect = getDialect(dbType);
3215
+ console.log(pc3.cyan(`
3216
+ --- Add Data to [${tableName}] ---`));
3217
+ console.log(pc3.dim("Leave a field completely empty (press Enter) to skip it (e.g. for AutoInc or NULL)"));
3218
+ const insertCols = [];
3219
+ const insertVals = [];
3220
+ for (const col of columns) {
3221
+ if (col.isPk && (col.type.toLowerCase().includes("int") || col.type.toLowerCase() === "integer")) {
3222
+ console.log(pc3.dim(`Skipping '${col.name}' (Auto Increment Primary Key)`));
3223
+ continue;
3224
+ }
3225
+ const val = await input({ message: `Value for '${col.name}' (${col.type}):` });
3226
+ if (val.trim() !== "") {
3227
+ insertCols.push(dialect.quoteIdentifier(col.name));
3228
+ insertVals.push(`'${dialect.escapeString(val)}'`);
3229
+ }
3230
+ }
3231
+ if (insertCols.length === 0) {
3232
+ console.log(pc3.yellow("No data entered. Aborted."));
3233
+ return;
3234
+ }
3235
+ const sql = `INSERT INTO ${dialect.quoteIdentifier(tableName)} (${insertCols.join(", ")}) VALUES (${insertVals.join(", ")});`;
3236
+ console.log(pc3.dim("\nExecuting: ") + pc3.yellow(sql));
3237
+ try {
3238
+ await adapter.executeSql(sql);
3239
+ console.log(pc3.green("\u2713 Data added successfully!"));
3240
+ } catch (e) {
3241
+ console.log(pc3.red(`x Failed to add data: ${e.message}`));
3242
+ }
3243
+ }
3244
+ async function runBeginnerEdit(adapter, dbType, tableName, columns) {
3245
+ const dialect = getDialect(dbType);
3246
+ console.log(pc3.cyan(`
3247
+ --- Edit Data in [${tableName}] ---`));
3248
+ if (columns.length === 0) return;
3249
+ const pkColSchema = columns.find((c) => c.isPk) || columns[0];
3250
+ const pkCol = pkColSchema.name;
3251
+ const pkVal = await input({
3252
+ message: `Enter the '${pkCol}' of the record you want to edit:`
3253
+ });
3254
+ if (!pkVal.trim()) {
3255
+ console.log(pc3.yellow("Aborted."));
3256
+ return;
3257
+ }
3258
+ const targetCol = await select2({
3259
+ message: "Which column do you want to update?",
3260
+ choices: columns.map((c) => ({ name: c.name, value: c.name }))
3261
+ });
3262
+ const newVal = await input({
3263
+ message: `New value for '${targetCol}' (leave empty for NULL):`
3264
+ });
3265
+ let valStr = "NULL";
3266
+ if (newVal.trim() !== "") {
3267
+ valStr = `'${dialect.escapeString(newVal)}'`;
3268
+ }
3269
+ const sql = `UPDATE ${dialect.quoteIdentifier(tableName)} SET ${dialect.quoteIdentifier(targetCol)} = ${valStr} WHERE ${dialect.quoteIdentifier(pkCol)} = '${dialect.escapeString(pkVal)}';`;
3270
+ console.log(pc3.dim("\nExecuting: ") + pc3.yellow(sql));
3271
+ try {
3272
+ await adapter.executeSql(sql);
3273
+ console.log(pc3.green("\u2713 Data updated successfully!"));
3274
+ } catch (e) {
3275
+ console.log(pc3.red(`x Failed to update data: ${e.message}`));
3276
+ }
3277
+ }
3278
+ async function runBeginnerDelete(adapter, dbType, tableName, columns) {
3279
+ const dialect = getDialect(dbType);
3280
+ console.log(pc3.cyan(`
3281
+ --- Delete Data from [${tableName}] ---`));
3282
+ if (columns.length === 0) return;
3283
+ const pkColSchema = columns.find((c) => c.isPk) || columns[0];
3284
+ const pkCol = pkColSchema.name;
3285
+ const pkVal = await input({
3286
+ message: `Enter the '${pkCol}' of the record you want to delete:`
3287
+ });
3288
+ if (!pkVal.trim()) {
3289
+ console.log(pc3.yellow("Aborted."));
3290
+ return;
3291
+ }
3292
+ const sql = `DELETE FROM ${dialect.quoteIdentifier(tableName)} WHERE ${dialect.quoteIdentifier(pkCol)} = '${dialect.escapeString(pkVal)}';`;
3293
+ console.log(pc3.dim("\nExecuting: ") + pc3.yellow(sql));
3294
+ try {
3295
+ await adapter.executeSql(sql);
3296
+ console.log(pc3.green("\u2713 Data deleted successfully!"));
3297
+ } catch (e) {
3298
+ console.log(pc3.red(`x Failed to delete data: ${e.message}`));
3299
+ }
3300
+ }
3301
+ async function runExpertMode(adapter) {
3302
+ console.log(pc3.cyan(`
3303
+ --- Expert Mode: Execute Raw SQL ---`));
3304
+ try {
3305
+ const filePath = await input({
3306
+ message: "Enter the path to your .md or .sql file:"
3307
+ });
3308
+ if (filePath && filePath.trim()) {
3309
+ const absolutePath = path2.resolve(process.cwd(), filePath.trim());
3310
+ let fileContent = "";
3311
+ try {
3312
+ fileContent = await fs2.readFile(absolutePath, "utf-8");
3313
+ } catch (err) {
3314
+ console.log(pc3.red(`
3315
+ x Failed to read file: ${err.message}`));
3316
+ return;
3317
+ }
3318
+ let sqlToExecute = fileContent;
3319
+ if (absolutePath.toLowerCase().endsWith(".md")) {
3320
+ const matches = [
3321
+ ...fileContent.matchAll(/```(?:sql)?\n([\s\S]*?)```/gi)
3322
+ ];
3323
+ if (matches.length > 0) {
3324
+ sqlToExecute = matches.map((m) => m[1].trim()).join("\n\n");
3325
+ }
3326
+ }
3327
+ if (sqlToExecute.trim()) {
3328
+ console.log(pc3.dim("\nExecuting SQL..."));
3329
+ console.log(pc3.yellow(sqlToExecute));
3330
+ try {
3331
+ await adapter.executeSql(sqlToExecute);
3332
+ console.log(pc3.green("\n\u2713 SQL executed successfully!"));
3333
+ } catch (e) {
3334
+ console.log(pc3.red(`
3335
+ x Error executing SQL: ${e.message}`));
3336
+ }
3337
+ } else {
3338
+ console.log(pc3.yellow("\n\u26A0\uFE0F No SQL found in the file. Aborted."));
3339
+ }
3340
+ } else {
3341
+ console.log(pc3.yellow("\nNo file path entered. Aborted."));
3342
+ }
3343
+ } catch (e) {
3344
+ console.log(pc3.red(`
3345
+ Error: ${e.message}`));
3346
+ }
3347
+ }
3348
+
3349
+ // src/cli/views/editor.ts
3350
+ init_table();
3351
+ init_logo();
3352
+ async function viewTables(dbConfig) {
3353
+ const adapter = createDBAdapter(dbConfig);
3354
+ let viewing = true;
3355
+ while (viewing) {
3356
+ console.clear();
3357
+ let tables = [];
3358
+ let totalRows = 0;
3359
+ let totalDataCount = "Scanning...";
3360
+ try {
3361
+ tables = await adapter.getTables();
3362
+ for (const table of tables) {
3363
+ try {
3364
+ const quote = dbConfig.type === "mysql" ? "`" : '"';
3365
+ const res = await adapter.query(`SELECT COUNT(*) as count FROM ${quote}${table}${quote}`);
3366
+ if (res.rows.length > 0 && res.rows[0].count != null) {
3367
+ totalRows += Number(res.rows[0].count);
3368
+ }
3369
+ } catch (e) {
3370
+ }
3371
+ }
3372
+ totalDataCount = `${totalRows.toLocaleString()} rows across ${tables.length} tables`;
3373
+ } catch (e) {
3374
+ totalDataCount = pc5.red(e.message);
3375
+ }
3376
+ const targetVal = dbConfig.targetUrl;
3377
+ const sourceVal = dbConfig.source === ".env" ? "Loaded from project .env file" : dbConfig.source === "auto-detected" ? "Auto-detected local SQLite file" : "Manual connection config";
3378
+ printCustomDashboard(` Data Browser & Editor \u2022 [${dbConfig.type.toUpperCase()}]`, [
3379
+ { label: "Database", value: dbConfig.type.toUpperCase() },
3380
+ { label: "Target", value: targetVal.length > 45 ? "..." + targetVal.slice(-42) : targetVal },
3381
+ { label: "Source", value: sourceVal },
3382
+ { label: "Total Data", value: totalDataCount }
3383
+ ]);
3384
+ try {
3385
+ if (tables.length === 0) {
3386
+ console.log(pc5.yellow("No tables found in this database."));
3387
+ await waitForEnter();
3388
+ await adapter.close();
3389
+ return;
3390
+ }
3391
+ const tableChoices = [
3392
+ ...tables.map((table) => ({
3393
+ name: table,
3394
+ value: table
3395
+ })),
3396
+ new Separator(),
3397
+ {
3398
+ name: pc5.dim(" Back"),
3399
+ value: "BACK"
3400
+ }
3401
+ ];
3402
+ const selectedTable = await selectTable(tableChoices);
3403
+ if (selectedTable === "BACK") {
3404
+ viewing = false;
3405
+ continue;
3406
+ }
3407
+ let tableLoop = true;
3408
+ let currentPage = 1;
3409
+ let currentWhere = "";
3410
+ const limit = 50;
3411
+ while (tableLoop) {
3412
+ console.clear();
3413
+ const offset = (currentPage - 1) * limit;
3414
+ const schema = await adapter.getSchema(selectedTable);
3415
+ const colNames = schema.map((c) => c.name);
3416
+ let data;
3417
+ try {
3418
+ data = await adapter.getData(
3419
+ selectedTable,
3420
+ limit,
3421
+ offset,
3422
+ currentWhere
3423
+ );
3424
+ } catch (e) {
3425
+ console.log(pc5.red(`
3426
+ Error fetching data: ${e.message}
3427
+ `));
3428
+ currentWhere = "";
3429
+ const { input: input7 } = await import("@inquirer/prompts");
3430
+ await input7({ message: "Click Enter to continue..." });
3431
+ continue;
3432
+ }
3433
+ const rows = data.rows;
3434
+ const detailHeaderTitle = ` ${selectedTable} (Page ${currentPage}) `;
3435
+ drawTable(colNames, rows, {
3436
+ title: detailHeaderTitle,
3437
+ maxColWidth: 30
3438
+ });
3439
+ const hasNextPage = rows.length === limit;
3440
+ const hasPrevPage = currentPage > 1;
3441
+ const actionChoices = [
3442
+ { name: "Add Data", value: "add" },
3443
+ { name: "Edit Data", value: "edit" },
3444
+ { name: "Delete Data", value: "delete" },
3445
+ new Separator(),
3446
+ { name: "Search Data", value: "search" },
3447
+ {
3448
+ name: currentWhere ? "Clear Search" : pc5.dim("Clear Search (disabled)"),
3449
+ value: currentWhere ? "clear_search" : "noop"
3450
+ },
3451
+ new Separator(),
3452
+ { name: "Export to CSV", value: "exportCsv" },
3453
+ { name: "Export to JSON", value: "exportJson" },
3454
+ new Separator()
3455
+ ];
3456
+ if (hasPrevPage)
3457
+ actionChoices.push({ name: "Previous Page", value: "prev" });
3458
+ if (hasNextPage)
3459
+ actionChoices.push({ name: "Next Page", value: "next" });
3460
+ actionChoices.push({ name: pc5.dim(" Back"), value: "BACK" });
3461
+ const action = await select3({
3462
+ message: "Select an action for this table:",
3463
+ theme: {
3464
+ prefix: pc5.cyan("\u2713 "),
3465
+ icon: {
3466
+ cursor: pc5.cyan("\u203A ")
3467
+ },
3468
+ style: {
3469
+ message: (text) => pc5.bold(pc5.white(text)),
3470
+ highlight: (text) => {
3471
+ const clean = text.replace(/\x1b\[[0-9;]*m/g, "");
3472
+ return clean.includes(" Back") ? pc5.red(clean) : pc5.cyan(clean);
3473
+ }
3474
+ }
3475
+ },
3476
+ choices: actionChoices
3477
+ });
3478
+ if (action === "prev") {
3479
+ currentPage--;
3480
+ continue;
3481
+ }
3482
+ if (action === "next") {
3483
+ currentPage++;
3484
+ continue;
3485
+ }
3486
+ if (action === "noop") {
3487
+ continue;
3488
+ }
3489
+ if (action === "BACK") {
3490
+ tableLoop = false;
3491
+ continue;
3492
+ }
3493
+ if (action === "clear_search") {
3494
+ currentWhere = "";
3495
+ currentPage = 1;
3496
+ continue;
3497
+ }
3498
+ if (action === "search") {
3499
+ const { input: input7 } = await import("@inquirer/prompts");
3500
+ const searchInput = await input7({
3501
+ message: "Enter Search (e.g. `age > 18` or `John` for fuzzy search):"
3502
+ });
3503
+ const searchVal = searchInput.trim();
3504
+ if (searchVal) {
3505
+ const isSqlCondition = /[=<>]|LIKE|IN|AND|OR/i.test(searchVal);
3506
+ if (isSqlCondition) {
3507
+ currentWhere = searchVal;
3508
+ } else {
3509
+ const strCols = schema.filter(
3510
+ (c) => c.type.toLowerCase().includes("char") || c.type.toLowerCase().includes("text")
3511
+ );
3512
+ if (strCols.length > 0) {
3513
+ const likeOp = dbConfig.type === "postgres" ? "ILIKE" : "LIKE";
3514
+ const conditions = strCols.map(
3515
+ (c) => `${adapter.quoteIdentifier(c.name)} ${likeOp} '%${searchVal.replace(/'/g, "''")}%'`
3516
+ );
3517
+ currentWhere = conditions.join(" OR ");
3518
+ } else {
3519
+ currentWhere = `${adapter.quoteIdentifier(schema[0].name)} = '${searchVal}'`;
3520
+ }
3521
+ }
3522
+ currentPage = 1;
3523
+ }
3524
+ continue;
3525
+ }
3526
+ if (action === "exportCsv" || action === "exportJson") {
3527
+ try {
3528
+ console.log(pc5.yellow("\nExporting data..."));
3529
+ const batchSize = 1e3;
3530
+ let batchOffset = 0;
3531
+ const allExportRows = [];
3532
+ let exportColumns = [];
3533
+ let lastBatch;
3534
+ do {
3535
+ lastBatch = await adapter.getData(
3536
+ selectedTable,
3537
+ batchSize,
3538
+ batchOffset,
3539
+ currentWhere
3540
+ );
3541
+ if (exportColumns.length === 0) exportColumns = lastBatch.columns;
3542
+ allExportRows.push(...lastBatch.rows);
3543
+ batchOffset += batchSize;
3544
+ } while (lastBatch.rows.length === batchSize);
3545
+ const allData = { columns: exportColumns, rows: allExportRows };
3546
+ const fs12 = await import("fs/promises");
3547
+ const path13 = await import("path");
3548
+ const exportDir = path13.join(process.cwd(), "drixio_exports");
3549
+ await fs12.mkdir(exportDir, { recursive: true });
3550
+ if (action === "exportCsv") {
3551
+ const headers = allData.columns.join(",") + "\n";
3552
+ const rowsStr = allData.rows.map((r) => {
3553
+ return allData.columns.map((c) => {
3554
+ const val = String(r[c] ?? "").replace(/"/g, '""');
3555
+ return `"${val}"`;
3556
+ }).join(",");
3557
+ }).join("\n");
3558
+ const fp = path13.join(exportDir, `${selectedTable}.csv`);
3559
+ await fs12.writeFile(fp, headers + rowsStr, "utf-8");
3560
+ console.log(pc5.green(`
3561
+ Exported to ${fp}`));
3562
+ } else {
3563
+ const fp = path13.join(exportDir, `${selectedTable}.json`);
3564
+ await fs12.writeFile(
3565
+ fp,
3566
+ JSON.stringify(allData.rows, null, 2),
3567
+ "utf-8"
3568
+ );
3569
+ console.log(pc5.green(`
3570
+ Exported to ${fp}`));
3571
+ }
3572
+ } catch (e) {
3573
+ console.log(pc5.red(`
3574
+ Export Error: ${e.message}`));
3575
+ }
3576
+ const { input: input7 } = await import("@inquirer/prompts");
3577
+ await input7({ message: "Click Enter to continue..." });
3578
+ continue;
3579
+ }
3580
+ const mode = await select3({
3581
+ message: `Select mode for ${action} data:`,
3582
+ choices: [
3583
+ { name: "Beginner (Interactive Step-by-Step)", value: "beginner" },
3584
+ {
3585
+ name: "Expert (Load SQL from .sql or .md file)",
3586
+ value: "expert"
3587
+ },
3588
+ new Separator(),
3589
+ { name: pc5.dim("Cancel"), value: "cancel" }
3590
+ ]
3591
+ });
3592
+ if (mode === "cancel") {
3593
+ continue;
3594
+ }
3595
+ if (mode === "expert") {
3596
+ await runExpertMode(adapter);
3597
+ await waitForEnter();
3598
+ continue;
3599
+ }
3600
+ if (action === "add") {
3601
+ await runBeginnerAdd(adapter, dbConfig.type, selectedTable, schema);
3602
+ await waitForEnter();
3603
+ } else if (action === "edit") {
3604
+ await runBeginnerEdit(adapter, dbConfig.type, selectedTable, schema);
3605
+ await waitForEnter();
3606
+ } else if (action === "delete") {
3607
+ await runBeginnerDelete(
3608
+ adapter,
3609
+ dbConfig.type,
3610
+ selectedTable,
3611
+ schema
3612
+ );
3613
+ await waitForEnter();
3614
+ }
3615
+ }
3616
+ } catch (error) {
3617
+ console.log(pc5.red(`
3618
+ x Error: ${error.message}`));
3619
+ await waitForEnter();
3620
+ viewing = false;
3621
+ }
3622
+ }
3623
+ await adapter.close();
3624
+ }
3625
+ async function waitForEnter() {
3626
+ const { input: input7 } = await import("@inquirer/prompts");
3627
+ await input7({
3628
+ message: "Press Enter to continue..."
3629
+ });
3630
+ }
3631
+
3632
+ // src/cli/wizards/setupConn.ts
3633
+ import { select as select4, input as input2 } from "@inquirer/prompts";
3634
+ import pc6 from "picocolors";
3635
+ init_factory();
3636
+ async function runSetup(currentConfig) {
3637
+ const setupMethod = await select4({
3638
+ message: "What method you want to use to setup database connection?",
3639
+ choices: [
3640
+ {
3641
+ name: " Auto-Config",
3642
+ value: "auto",
3643
+ description: "Automatically detect and setup database connection (May fail for some cases)."
3644
+ },
3645
+ {
3646
+ name: " Manual Config",
3647
+ value: "manual",
3648
+ description: "Manually setup database connection by entering parameters step by step."
3649
+ }
3650
+ ]
3651
+ });
3652
+ let newConfig = currentConfig;
3653
+ switch (setupMethod) {
3654
+ case "auto":
3655
+ const retryConfig = await detectDatabase();
3656
+ if (retryConfig.type !== "unknown") {
3657
+ newConfig = retryConfig;
3658
+ console.log(pc6.green("\nDatabase connection setup successfully."));
3659
+ } else {
3660
+ console.log(
3661
+ pc6.red(
3662
+ "\nFailed to auto-detect database connection. Please try manual configuration."
3663
+ )
3664
+ );
3665
+ }
3666
+ break;
3667
+ case "manual":
3668
+ const databaseUrl = await input2({
3669
+ message: "Enter the database connection URL: "
3670
+ });
3671
+ if (databaseUrl) {
3672
+ const tempConfig = await detectDatabase(databaseUrl);
3673
+ console.log(pc6.dim("\nTesting connection..."));
3674
+ try {
3675
+ const adapter = createDBAdapter(tempConfig);
3676
+ await adapter.getTables();
3677
+ await adapter.close();
3678
+ newConfig = tempConfig;
3679
+ await saveDatabaseUrl(databaseUrl);
3680
+ console.log(
3681
+ pc6.green(
3682
+ "\u2713 Database connection setup successfully and saved to .env!"
3683
+ )
3684
+ );
3685
+ } catch (e) {
3686
+ console.log(
3687
+ pc6.red(
3688
+ `
3689
+ x Connection failed: ${e.message}
3690
+ Please enter a valid database URL or check your database status.`
3691
+ )
3692
+ );
3693
+ }
3694
+ } else {
3695
+ console.log(
3696
+ pc6.red(
3697
+ "\nFailed to detect database connection. Please enter valid database URL."
3698
+ )
3699
+ );
3700
+ }
3701
+ break;
3702
+ }
3703
+ return newConfig;
3704
+ }
3705
+
3706
+ // src/cli/menus/action.ts
3707
+ import { select as select5, Separator as Separator2 } from "@inquirer/prompts";
3708
+ import pc7 from "picocolors";
3709
+ var selectAction = async (dbConfig) => await select5({
3710
+ message: "Select an action:",
3711
+ theme: {
3712
+ prefix: pc7.cyan("\u2713 "),
3713
+ icon: {
3714
+ cursor: pc7.cyan("\u203A ")
3715
+ },
3716
+ style: {
3717
+ message: (text) => pc7.bold(pc7.white(text)),
3718
+ highlight: (text) => {
3719
+ const clean = text.replace(/\x1b\[[0-9;]*m/g, "");
3720
+ return clean.includes("Exit") ? pc7.red(clean) : pc7.cyan(clean);
3721
+ }
3722
+ }
3723
+ },
3724
+ choices: [
3725
+ {
3726
+ name: " Data Browser & Editor",
3727
+ value: "editor",
3728
+ description: "View, insert, update, and delete row data in your tables.",
3729
+ disabled: dbConfig.type === "unknown"
3730
+ },
3731
+ {
3732
+ name: " Run Raw SQL (REPL)",
3733
+ value: "repl",
3734
+ description: "Execute arbitrary SQL queries interactively.",
3735
+ disabled: dbConfig.type === "unknown"
3736
+ },
3737
+ {
3738
+ name: " Table Builder & Manager",
3739
+ value: "table",
3740
+ description: "Create new tables, or modify/drop existing tables.",
3741
+ disabled: dbConfig.type === "unknown"
3742
+ },
3743
+ new Separator2(),
3744
+ {
3745
+ name: dbConfig.type === "unknown" ? " Setup Connection" : " Connection Settings",
3746
+ value: dbConfig.type === "unknown" ? "setup" : "re-configure",
3747
+ description: dbConfig.type === "unknown" ? "Setup the database connection manually." : "Re-configure the database connection."
3748
+ },
3749
+ {
3750
+ name: pc7.dim(" Exit"),
3751
+ value: "exit",
3752
+ description: "Exit the Drixio CLI application."
3753
+ }
3754
+ ]
3755
+ });
3756
+
3757
+ // src/cli/views/repl.ts
3758
+ init_factory();
3759
+ init_table();
3760
+ import pc8 from "picocolors";
3761
+ async function runRepl(dbConfig) {
3762
+ if (dbConfig.type === "unknown") return;
3763
+ const dbAdapter = createDBAdapter(dbConfig);
3764
+ let running = true;
3765
+ const { input: input7 } = await import("@inquirer/prompts");
3766
+ console.clear();
3767
+ console.log(
3768
+ pc8.cyan(
3769
+ `
3770
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`
3771
+ )
3772
+ );
3773
+ console.log(
3774
+ pc8.cyan(
3775
+ `\u2551 Interactive SQL REPL \u2551`
3776
+ )
3777
+ );
3778
+ console.log(
3779
+ pc8.cyan(
3780
+ `\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563`
3781
+ )
3782
+ );
3783
+ console.log(
3784
+ pc8.cyan(`\u2551 `) + pc8.dim(
3785
+ `Type your SQL queries directly. Type 'exit' or 'quit' to Back.`
3786
+ ) + ` ` + pc8.cyan(`\u2551`)
3787
+ );
3788
+ console.log(
3789
+ pc8.cyan(
3790
+ `\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
3791
+ `
3792
+ )
3793
+ );
3794
+ while (running) {
3795
+ try {
3796
+ const queryStr = await input7({
3797
+ message: pc8.green(`${dbConfig.type}>`)
3798
+ });
3799
+ const sql = queryStr.trim();
3800
+ if (sql.toLowerCase() === "exit" || sql.toLowerCase() === "quit") {
3801
+ running = false;
3802
+ continue;
3803
+ }
3804
+ if (sql === "") continue;
3805
+ const result = await dbAdapter.query(sql);
3806
+ if (result.columns.length === 0) {
3807
+ console.log(pc8.yellow("Query executed successfully. (No output)"));
3808
+ continue;
3809
+ }
3810
+ drawTable(result.columns, result.rows, {
3811
+ title: "Query Result",
3812
+ maxColWidth: 50
3813
+ });
3814
+ console.log(pc8.dim(`
3815
+ (${result.rows.length} rows)`));
3816
+ console.log();
3817
+ } catch (e) {
3818
+ if (e.name === "ExitPromptError") {
3819
+ running = false;
3820
+ } else {
3821
+ console.log(pc8.red(`
3822
+ Error: ${e.message}
3823
+ `));
3824
+ }
3825
+ }
3826
+ }
3827
+ }
3828
+
3829
+ // src/cli/main.ts
3830
+ import { parseArgs } from "util";
3831
+ async function main() {
3832
+ const args = process.argv.slice(2);
3833
+ let customUrl;
3834
+ if (args.length > 0 && (args[0].startsWith("postgres://") || args[0].startsWith("postgresql://") || args[0].startsWith("mysql://") || args[0].startsWith("file:"))) {
3835
+ customUrl = args[0];
3836
+ }
3837
+ const { positionals, values } = parseArgs({
3838
+ args,
3839
+ options: {
3840
+ format: { type: "string" },
3841
+ "schema-only": { type: "boolean" },
3842
+ table: { type: "string" },
3843
+ help: { type: "boolean" }
3844
+ },
3845
+ strict: false,
3846
+ allowPositionals: true
3847
+ });
3848
+ if (values.help) {
3849
+ console.log(pc26.cyan(`
190
3850
  Drixio CLI - Modern Database Manager
191
- `)),console.log(`${D.bold("Usage:")} npx drixio [command] [options]
192
- `),console.log(`${D.bold("Commands:")}`),console.log(` ${D.green("query")} "<sql>" Run a quick SQL query`),console.log(` ${D.green("exec")} <file.sql> Execute a SQL script file`),console.log(` ${D.green("export")} [table] Export table(s) to CSV/JSON`),console.log(` ${D.green("import")} [file] Import JSON/CSV into a table`),console.log(` ${D.green("seed")} [table] [count] Generate fake data for a table`),console.log(` ${D.green("diagram")} Generate a Mermaid ER diagram`),console.log(` ${D.green("generate-types")} Generate TypeScript interfaces`),console.log(` ${D.green("backup")} Backup the entire database`),console.log(` ${D.green("studio")} Launch the Web UI Studio`),console.log(` ${D.green("init")} [db_type] Initialize a local database & .env`),console.log(` ${D.green("drop-db")} [db_type] Drop a local database`),console.log(`
193
- ${D.bold("Options:")}`),console.log(" --help Show this help message"),console.log(" --format <type> Specify export format (csv|json)"),console.log(" --schema-only Export schema without data"),console.log(" --table <name> Specify table for import"),console.log(`
194
- If you don't provide a command, Drixio will launch the Interactive UI!`),process.exit(0));let o=a?void 0:t[0];if(o==="query"){let{runQueryCommand:i}=await Promise.resolve().then(()=>(ot(),at)),l=await q();await i(l,t.slice(1));return}if(o==="export"){let{runExportCommand:i}=await Promise.resolve().then(()=>(st(),nt)),l=await q();await i(l,t.slice(1),r);return}if(o==="studio"){let{runStudio:i}=await Promise.resolve().then(()=>(mt(),ct)),l=await q();await i(l);return}if(o==="backup"){let{runBackupCommand:i}=await Promise.resolve().then(()=>(pt(),ut)),l=await q();await i(l);return}if(o==="import"){let{runImportCommand:i}=await Promise.resolve().then(()=>(ft(),gt)),l=await q();await i(l,t.slice(1),r);return}if(o==="seed"){let{runSeedCommand:i}=await Promise.resolve().then(()=>(ht(),yt)),l=await q();await i(l,t.slice(1));return}if(o==="diagram"){let{runDiagramCommand:i}=await Promise.resolve().then(()=>(bt(),wt)),l=await q();await i(l);return}if(o==="exec"){let{runExecCommand:i}=await Promise.resolve().then(()=>($t(),St)),l=await q();await i(l,t.slice(1));return}if(o==="generate-types"){let{runGenerateTypesCommand:i}=await Promise.resolve().then(()=>(Tt(),xt)),l=await q();await i(l);return}if(o==="init"){let{runInitCommand:i}=await Promise.resolve().then(()=>(Ct(),At));await i(t.slice(1));return}if(o==="drop-db"){let{runDropDbCommand:i}=await Promise.resolve().then(()=>(vt(),qt));await i(t.slice(1));return}let e=!0,n=await q(a);for(;e;)switch(console.clear(),Oe(),Ue(n),await et(n)){case"editor":n.type==="unknown"?(console.log(D.yellow(`
195
- No database connection found. Please run ${D.bold("drixio check")} first.`)),await c()):await Xe(n);break;case"table":let{runTableManagerFlow:l}=await Promise.resolve().then(()=>(Qt(),jt));await l(n);break;case"repl":await tt(n);break;case"setup":case"re-configure":n=await Ze(n),await c();break;case"exit":e=!1,console.log(D.dim(`
196
- Thanks for using Drixio. Goodbye!`));break}async function c(){let{input:i}=await import("@inquirer/prompts");await i({message:"Click Enter to continue..."})}}Vt().catch(s=>{s.name==="ExitPromptError"&&(console.log(`
197
- Exit Drixio.`),process.exit(0)),console.error(`
198
- Error: `,s),process.exit(1)});
3851
+ `));
3852
+ console.log(`${pc26.bold("Usage:")} npx drixio [command] [options]
3853
+ `);
3854
+ console.log(`${pc26.bold("Commands:")}`);
3855
+ console.log(` ${pc26.green("query")} "<sql>" Run a quick SQL query`);
3856
+ console.log(
3857
+ ` ${pc26.green("exec")} <file.sql> Execute a SQL script file`
3858
+ );
3859
+ console.log(
3860
+ ` ${pc26.green("export")} [table] Export table(s) to CSV/JSON`
3861
+ );
3862
+ console.log(
3863
+ ` ${pc26.green("import")} [file] Import JSON/CSV into a table`
3864
+ );
3865
+ console.log(
3866
+ ` ${pc26.green("seed")} [table] [count] Generate fake data for a table`
3867
+ );
3868
+ console.log(
3869
+ ` ${pc26.green("diagram")} Generate a Mermaid ER diagram`
3870
+ );
3871
+ console.log(
3872
+ ` ${pc26.green("generate-types")} Generate TypeScript interfaces`
3873
+ );
3874
+ console.log(
3875
+ ` ${pc26.green("backup")} Backup the entire database`
3876
+ );
3877
+ console.log(
3878
+ ` ${pc26.green("studio")} Launch the Web UI Studio`
3879
+ );
3880
+ console.log(
3881
+ ` ${pc26.green("init")} [db_type] Initialize a local database & .env`
3882
+ );
3883
+ console.log(` ${pc26.green("drop-db")} [db_type] Drop a local database`);
3884
+ console.log(`
3885
+ ${pc26.bold("Options:")}`);
3886
+ console.log(` --help Show this help message`);
3887
+ console.log(` --format <type> Specify export format (csv|json)`);
3888
+ console.log(` --schema-only Export schema without data`);
3889
+ console.log(` --table <name> Specify table for import`);
3890
+ console.log(
3891
+ `
3892
+ If you don't provide a command, Drixio will launch the Interactive UI!`
3893
+ );
3894
+ process.exit(0);
3895
+ }
3896
+ const command = customUrl ? void 0 : positionals[0];
3897
+ if (command === "query") {
3898
+ const { runQueryCommand: runQueryCommand2 } = await Promise.resolve().then(() => (init_query(), query_exports));
3899
+ const dbConfig2 = await detectDatabase();
3900
+ await runQueryCommand2(dbConfig2, positionals.slice(1));
3901
+ return;
3902
+ }
3903
+ if (command === "export") {
3904
+ const { runExportCommand: runExportCommand2 } = await Promise.resolve().then(() => (init_export(), export_exports));
3905
+ const dbConfig2 = await detectDatabase();
3906
+ await runExportCommand2(dbConfig2, positionals.slice(1), values);
3907
+ return;
3908
+ }
3909
+ if (command === "studio") {
3910
+ const { runStudio: runStudio2 } = await Promise.resolve().then(() => (init_server(), server_exports));
3911
+ const dbConfig2 = await detectDatabase();
3912
+ await runStudio2(dbConfig2);
3913
+ return;
3914
+ }
3915
+ if (command === "backup") {
3916
+ const { runBackupCommand: runBackupCommand2 } = await Promise.resolve().then(() => (init_backup(), backup_exports));
3917
+ const dbConfig2 = await detectDatabase();
3918
+ await runBackupCommand2(dbConfig2);
3919
+ return;
3920
+ }
3921
+ if (command === "import") {
3922
+ const { runImportCommand: runImportCommand2 } = await Promise.resolve().then(() => (init_import(), import_exports));
3923
+ const dbConfig2 = await detectDatabase();
3924
+ await runImportCommand2(dbConfig2, positionals.slice(1), values);
3925
+ return;
3926
+ }
3927
+ if (command === "seed") {
3928
+ const { runSeedCommand: runSeedCommand2 } = await Promise.resolve().then(() => (init_seed(), seed_exports));
3929
+ const dbConfig2 = await detectDatabase();
3930
+ await runSeedCommand2(dbConfig2, positionals.slice(1));
3931
+ return;
3932
+ }
3933
+ if (command === "diagram") {
3934
+ const { runDiagramCommand: runDiagramCommand2 } = await Promise.resolve().then(() => (init_diagram(), diagram_exports));
3935
+ const dbConfig2 = await detectDatabase();
3936
+ await runDiagramCommand2(dbConfig2);
3937
+ return;
3938
+ }
3939
+ if (command === "exec") {
3940
+ const { runExecCommand: runExecCommand2 } = await Promise.resolve().then(() => (init_exec(), exec_exports));
3941
+ const dbConfig2 = await detectDatabase();
3942
+ await runExecCommand2(dbConfig2, positionals.slice(1));
3943
+ return;
3944
+ }
3945
+ if (command === "generate-types") {
3946
+ const { runGenerateTypesCommand: runGenerateTypesCommand2 } = await Promise.resolve().then(() => (init_generateTypes(), generateTypes_exports));
3947
+ const dbConfig2 = await detectDatabase();
3948
+ await runGenerateTypesCommand2(dbConfig2);
3949
+ return;
3950
+ }
3951
+ if (command === "init") {
3952
+ const { runInitCommand: runInitCommand2 } = await Promise.resolve().then(() => (init_init(), init_exports));
3953
+ await runInitCommand2(positionals.slice(1));
3954
+ return;
3955
+ }
3956
+ if (command === "drop-db") {
3957
+ const { runDropDbCommand: runDropDbCommand2 } = await Promise.resolve().then(() => (init_drop(), drop_exports));
3958
+ await runDropDbCommand2(positionals.slice(1));
3959
+ return;
3960
+ }
3961
+ let running = true;
3962
+ let dbConfig = await detectDatabase(customUrl);
3963
+ while (running) {
3964
+ console.clear();
3965
+ printLogo();
3966
+ printDashboard(dbConfig);
3967
+ const action = await selectAction(dbConfig);
3968
+ switch (action) {
3969
+ case "editor":
3970
+ if (dbConfig.type === "unknown") {
3971
+ console.log(
3972
+ pc26.yellow(
3973
+ `
3974
+ No database connection found. Please run ${pc26.bold("drixio check")} first.`
3975
+ )
3976
+ );
3977
+ await waitForEnter4();
3978
+ } else {
3979
+ await viewTables(dbConfig);
3980
+ }
3981
+ break;
3982
+ case "table":
3983
+ const { runTableManagerFlow: runTableManagerFlow2 } = await Promise.resolve().then(() => (init_tableManagerFlow(), tableManagerFlow_exports));
3984
+ await runTableManagerFlow2(dbConfig);
3985
+ break;
3986
+ case "repl":
3987
+ await runRepl(dbConfig);
3988
+ break;
3989
+ case "setup":
3990
+ case "re-configure":
3991
+ dbConfig = await runSetup(dbConfig);
3992
+ await waitForEnter4();
3993
+ break;
3994
+ case "exit":
3995
+ running = false;
3996
+ console.log(pc26.dim("\nThanks for using Drixio. Goodbye!"));
3997
+ break;
3998
+ }
3999
+ }
4000
+ async function waitForEnter4() {
4001
+ const { input: input7 } = await import("@inquirer/prompts");
4002
+ await input7({
4003
+ message: "Click Enter to continue..."
4004
+ });
4005
+ }
4006
+ }
4007
+
4008
+ // bin/cli.ts
4009
+ main().catch((error) => {
4010
+ if (error.name === "ExitPromptError") {
4011
+ console.log("\n Exit Drixio.");
4012
+ process.exit(0);
4013
+ }
4014
+ console.error("\n Error: ", error);
4015
+ process.exit(1);
4016
+ });