drixio 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +105 -0
- package/dist/cli.js +162 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# π Drix - The Ultimate Zero-Dependency Database CLI & TUI
|
|
2
|
+
|
|
3
|
+
**Drix** is a lightning-fast, zero-dependency Database Manager designed entirely for your terminal. It supports **SQLite**, **PostgreSQL**, and **MySQL**.
|
|
4
|
+
|
|
5
|
+
Say goodbye to heavy GUI tools like DBeaver or TablePlus. Drix allows you to instantly view, edit, query, backup, and visually diagram your databases right from your CLI!
|
|
6
|
+
|
|
7
|
+
## β¨ Features
|
|
8
|
+
|
|
9
|
+
- **π± Interactive TUI**: A beautiful, mouse-free Terminal User Interface.
|
|
10
|
+
- **β‘ Zero Dependencies**: Blazing fast, runs instantly via `npx`.
|
|
11
|
+
- **π οΈ Multi-Database Support**: Connects seamlessly to SQLite, PostgreSQL, and MySQL.
|
|
12
|
+
- **π¦ Smart Data Importer & Exporter**: Import CSV/JSON safely, or export your tables in seconds.
|
|
13
|
+
- **π± Intelligent Data Seeder**: Automatically generates realistic fake data (emails, phones, dates) to populate your tables for testing.
|
|
14
|
+
- **πΊοΈ ER Diagram Generator**: Scans your database and generates a Mermaid ER diagram that can be instantly imported into Draw.io or viewed on GitHub!
|
|
15
|
+
- **π§© TypeScript Types Generator**: Instantly generate TypeScript interfaces (`.d.ts`) directly from your database schema!
|
|
16
|
+
|
|
17
|
+
## π Quick Start
|
|
18
|
+
|
|
19
|
+
You don't need to install anything. Just run:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx @terks.dev/drix
|
|
23
|
+
```
|
|
24
|
+
Drix will automatically scan your project for a `.env` file containing a `DATABASE_URL` (e.g., `DATABASE_URL=postgres://user:pass@localhost:5432/mydb`). If it doesn't find one, it will launch a setup wizard to help you connect!
|
|
25
|
+
|
|
26
|
+
You can also pass a connection string directly:
|
|
27
|
+
```bash
|
|
28
|
+
npx @terks.dev/drix "mysql://user:pass@localhost:3306/mydb"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## π οΈ Powerful Subcommands
|
|
34
|
+
|
|
35
|
+
Drix is not just a TUI; it comes with powerful quick-commands for your CI/CD pipelines or rapid local development.
|
|
36
|
+
|
|
37
|
+
### π 1. Initialize a Local Database
|
|
38
|
+
Don't have a database yet? Drix can create one for you!
|
|
39
|
+
```bash
|
|
40
|
+
npx @terks.dev/drix init sqlite
|
|
41
|
+
npx @terks.dev/drix init postgres
|
|
42
|
+
npx @terks.dev/drix init mysql
|
|
43
|
+
```
|
|
44
|
+
*(For Postgres and MySQL, it connects to your local server and runs `CREATE DATABASE`, then automatically drops a configured `.env` file in your workspace!)*
|
|
45
|
+
|
|
46
|
+
### π 2. Quick Query
|
|
47
|
+
Run SQL instantly and get a beautifully formatted ASCII table result.
|
|
48
|
+
```bash
|
|
49
|
+
npx @terks.dev/drix query "SELECT * FROM users WHERE age > 18"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### π₯ 3. Import Data
|
|
53
|
+
Import massive CSV or JSON files safely. Drix uses smart chunking to prevent memory overload.
|
|
54
|
+
```bash
|
|
55
|
+
npx @terks.dev/drix import data.csv --table users
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### π€ 4. Export Data
|
|
59
|
+
Export your tables to CSV or JSON.
|
|
60
|
+
```bash
|
|
61
|
+
npx @terks.dev/drix export users --format csv
|
|
62
|
+
npx @terks.dev/drix export * --format json --schema-only
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### π± 5. Generate Fake Data (Seed)
|
|
66
|
+
Need 500 fake users for testing? Easy. Drix intelligently analyzes your column names and types to generate realistic data.
|
|
67
|
+
```bash
|
|
68
|
+
npx @terks.dev/drix seed users 500
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### πΊοΈ 6. Generate ER Diagram
|
|
72
|
+
Automatically generates a `drix_schema.md` containing a Mermaid.js diagram of your database.
|
|
73
|
+
```bash
|
|
74
|
+
npx @terks.dev/drix diagram
|
|
75
|
+
```
|
|
76
|
+
*Tip: Paste the output into Draw.io (Arrange > Insert > Advanced > Mermaid) for a stunning visual layout!*
|
|
77
|
+
|
|
78
|
+
### π§© 7. Generate TypeScript Interfaces
|
|
79
|
+
Tired of manually writing types? Auto-generate them from your schema!
|
|
80
|
+
```bash
|
|
81
|
+
npx @terks.dev/drix generate-types
|
|
82
|
+
```
|
|
83
|
+
*(Outputs `drix-types.d.ts` with all your table interfaces)*
|
|
84
|
+
|
|
85
|
+
### π 8. Execute SQL Script
|
|
86
|
+
Run entire `.sql` files instantly. Perfect for database migrations.
|
|
87
|
+
```bash
|
|
88
|
+
npx @terks.dev/drix exec ./migrations/init.sql
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### π¦ 9. Database Backup
|
|
92
|
+
Backup your entire database. For SQLite, it performs a secure binary copy. For Postgres/MySQL, it dumps schema and data into JSON.
|
|
93
|
+
```bash
|
|
94
|
+
npx @terks.dev/drix backup
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## β Help
|
|
98
|
+
To view all commands and options:
|
|
99
|
+
```bash
|
|
100
|
+
npx @terks.dev/drix --help
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
**Built with β€οΈ for Developers who love the Terminal.**
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var Zt=Object.defineProperty;var E=(n,t)=>()=>(n&&(t=n(n=0)),t);var L=(n,t)=>{for(var a in t)Zt(n,a,{get:t[a],enumerable:!0})};import k from"picocolors";function _e(){console.log(`
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
`+k.bold(Jt(` \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557
|
|
6
|
+
`)+Xt(` \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
|
|
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
|
|
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
|
|
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
|
|
10
|
+
`)+oa(" \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D")))}function se(n,t){let r="\u2550".repeat(72);console.log(k.cyan(`
|
|
11
|
+
\u2554${r}\u2557`));let o=72-n.length,e=Math.max(0,Math.floor(o/2)),s=Math.max(0,o-e),c=" ".repeat(e)+k.bold(k.white(n))+" ".repeat(s);console.log(k.cyan("\u2551")+c+k.cyan("\u2551")),console.log(k.cyan(`\u2560${"\u2550".repeat(72)}\u2563`));let i=(l,m)=>{let p=l.padEnd(12),u=` ${k.bold(p)}: ${m}`,d=` ${p}: `+m.replace(/\x1b\[[0-9;]*m/g,""),f=" ".repeat(Math.max(0,72-d.length));console.log(k.cyan("\u2551")+u+f+k.cyan("\u2551"))};for(let l of t)i(l.label,l.value);console.log(k.cyan(`\u255A${"\u2550".repeat(72)}\u255D`)),console.log(k.dim(` Use arrow keys to navigate \u2022 Enter to select \u2022 Ctrl+C to exit
|
|
12
|
+
`))}function Fe(n){let t=" Lightweight Interactive TUI Database Client \u2022 v1.0.1-beta.4 ",a="None",r="-",o="None";n.type==="unknown"?o=k.dim("No configuration found. Please run check to configure."):(a=n.type.toUpperCase(),r=n.targetUrl,o=n.source===".env"?"Loaded from project .env file":n.source==="auto-detected"?"Auto-detected local SQLite file":"Manual connection config"),se(t,[{label:"Database",value:a},{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 Z,Jt,Xt,ea,ta,aa,oa,$e=E(()=>{"use strict";Z=(n,t,a)=>r=>`\x1B[38;2;${n};${t};${a}m${r}\x1B[39m`,Jt=Z(0,255,255),Xt=Z(0,230,245),ea=Z(0,210,235),ta=Z(0,188,220),aa=Z(0,168,205),oa=Z(0,148,188)});var Se,We=E(()=>{"use strict";Se=class{dbPath;db=null;constructor(t){this.dbPath=t}async getDb(){if(!this.db){if(!(await import("fs")).existsSync(this.dbPath))throw new Error(`Failed to found database file at: ${this.dbPath}`);let a=await import("sqlite");this.db=new a.DatabaseSync(this.dbPath)}return this.db}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(t){let a=await this.getDb();if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(t))throw new Error(`Invalid table name: ${t}`);return a.prepare(`PRAGMA table_info("${t}")`).all().map(e=>({name:e.name,type:e.type,isPk:e.pk>0,nullable:e.notnull===0}))}async getData(t,a=50,r=0,o,e){let s=await this.getDb();if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(t))throw new Error(`Invalid table name: ${t}`);let i=(await this.getSchema(t)).map(u=>u.name),l=`SELECT * FROM "${t}"`;o&&(l+=` WHERE ${o}`),e&&(l+=` ORDER BY "${e.col}" ${e.asc?"ASC":"DESC"}`),l+=` LIMIT ${a} OFFSET ${r}`;let p=s.prepare(l).all();return{columns:i,rows:p}}async query(t){let r=(await this.getDb()).prepare(t);if(t.trim().toUpperCase().startsWith("SELECT")||t.trim().toUpperCase().startsWith("PRAGMA")){let e=r.all(),s=[];return e.length>0&&(s=Object.keys(e[0])),{columns:s,rows:e}}else return r.run(),{columns:["Result"],rows:[{Result:"Success"}]}}async executeSql(t){(await this.getDb()).exec(t)}async close(){this.db&&(this.db.close(),this.db=null)}async insert(t,a){if(a.length===0)return;let r=await this.getDb(),o=Object.keys(a[0]),e=o.map(()=>"?").join(", "),s=`INSERT INTO "${t}" ("${o.join('", "')}") VALUES (${e})`,c=r.prepare(s);for(let i of a){let l=o.map(m=>i[m]);c.run(...l)}}}});import na from"pg";var xe,je=E(()=>{"use strict";xe=class{client;connected=!1;constructor(t){this.client=new na.Client({connectionString:t})}async connectIfNecessary(){this.connected||(await this.client.connect(),this.connected=!0)}async getTables(){return await this.connectIfNecessary(),(await this.client.query(`
|
|
13
|
+
SELECT tablename
|
|
14
|
+
FROM pg_catalog.pg_tables
|
|
15
|
+
WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'
|
|
16
|
+
ORDER BY tablename;
|
|
17
|
+
`)).rows.map(r=>r.tablename)}async getSchema(t){if(await this.connectIfNecessary(),!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(t))throw new Error(`Invalid table name: ${t}`);return(await this.client.query(`
|
|
18
|
+
SELECT c.column_name, c.data_type, c.is_nullable,
|
|
19
|
+
(SELECT count(*)
|
|
20
|
+
FROM information_schema.key_column_usage kcu
|
|
21
|
+
JOIN information_schema.table_constraints tc
|
|
22
|
+
ON kcu.constraint_name = tc.constraint_name
|
|
23
|
+
WHERE tc.constraint_type = 'PRIMARY KEY'
|
|
24
|
+
AND kcu.table_name = c.table_name
|
|
25
|
+
AND kcu.column_name = c.column_name) as is_pk
|
|
26
|
+
FROM information_schema.columns c
|
|
27
|
+
WHERE c.table_name = $1;
|
|
28
|
+
`,[t])).rows.map(o=>({name:o.column_name,type:o.data_type,isPk:parseInt(o.is_pk)>0,nullable:o.is_nullable==="YES"}))}async getData(t,a=50,r=0,o,e){if(await this.connectIfNecessary(),!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(t))throw new Error(`Invalid table name: ${t}`);let c=(await this.getSchema(t)).map(m=>m.name),i=`SELECT * FROM "${t}"`;o&&(i+=` WHERE ${o}`),e&&(i+=` ORDER BY "${e.col}" ${e.asc?"ASC":"DESC"}`),i+=" LIMIT $1 OFFSET $2";let l=await this.client.query(i,[a,r]);return{columns:c,rows:l.rows}}async query(t){await this.connectIfNecessary();let a=await this.client.query(t),r=[];return a.fields&&(r=a.fields.map(o=>o.name)),{columns:r,rows:a.rows||[]}}async executeSql(t){await this.connectIfNecessary(),await this.client.query(t)}async close(){this.connected&&(await this.client.end(),this.connected=!1)}async insert(t,a){if(a.length===0)return;await this.connectIfNecessary();let r=Object.keys(a[0]);for(let o of a){let e=r.map((i,l)=>`$${l+1}`).join(", "),s=`INSERT INTO "${t}" ("${r.join('", "')}") VALUES (${e})`,c=r.map(i=>o[i]);await this.client.query(s,c)}}}});import sa from"mysql2/promise";var Te,Ve=E(()=>{"use strict";Te=class{connection;pool=null;constructor(t){this.connection=t}async getPool(){return this.pool||(this.pool=sa.createPool(this.connection)),this.pool}async getTables(){let t=await this.getPool(),[a]=await t.query("SHOW TABLES;");return a.map(r=>Object.values(r)[0])}async getSchema(t){let a=await this.getPool();if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(t))throw new Error(`Invalid table name: ${t}`);let[r]=await a.query(`SHOW COLUMNS FROM \`${t}\``);return r.map(e=>({name:e.Field,type:e.Type,isPk:e.Key==="PRI",nullable:e.Null==="YES",extra:e.Extra}))}async getData(t,a=50,r=0,o,e){let s=await this.getPool();if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(t))throw new Error(`Invalid table name: ${t}`);let i=(await this.getSchema(t)).map(p=>p.name),l=`SELECT * FROM \`${t}\``;o&&(l+=` WHERE ${o}`),e&&(l+=` ORDER BY \`${e.col}\` ${e.asc?"ASC":"DESC"}`),l+=` LIMIT ${a} OFFSET ${r}`;let[m]=await s.query(l);return{columns:i,rows:m}}async query(t){let a=await this.getPool(),[r,o]=await a.query(t),e=[],s=[];return o&&Array.isArray(o)?(e=o.map(c=>c.name),s=r):(e=["Result"],s=[{Result:"Success",AffectedRows:r.affectedRows}]),{columns:e,rows:s}}async executeSql(t){await(await this.getPool()).query(t)}async close(){this.pool&&(await this.pool.end(),this.pool=null)}async insert(t,a){if(a.length===0)return;let r=await this.getPool(),o=Object.keys(a[0]);for(let e of a){let s=o.map(()=>"?").join(", "),c=`INSERT INTO \`${t}\` (\`${o.join("`, `")}\`) VALUES (${s})`,i=o.map(l=>e[l]);await r.query(c,i)}}}});var Qe={};L(Qe,{createDBAdapter:()=>h});function h(n){switch(n.type){case"sqlite":return new Se(n.targetUrl);case"postgres":return new xe(n.targetUrl);case"mysql":return new Te(n.targetUrl);default:throw new Error(`Unsupported database type: ${n.type}`)}}var C=E(()=>{"use strict";We();je();Ve()});function j(n){switch(n){case"sqlite":return new De;case"postgres":return new Le;case"mysql":return new ke;default:return new De}}var De,Le,ke,Ce=E(()=>{"use strict";De=class{quoteIdentifier(t){return`"${t}"`}escapeString(t){return t.replace(/'/g,"''")}buildCreateTable(t,a){let r=a.map(e=>{let s="";return e.type==="Integer"&&(s="INTEGER"),e.type==="Text"&&(s="TEXT"),e.type==="Boolean"&&(s="BOOLEAN"),e.type==="Decimal"&&(s="REAL"),e.type==="DateTime"&&(s="DATETIME"),e.type==="Enum"&&e.enumValues&&(s=`TEXT CHECK(${this.quoteIdentifier(e.name)} IN (${e.enumValues.map(c=>`'${this.escapeString(c)}'`).join(", ")}))`),e.isPk&&e.type==="Integer"?s="INTEGER PRIMARY KEY AUTOINCREMENT":e.isPk?s+=" PRIMARY KEY":e.nullable||(s+=" NOT NULL"),e.defaultValue&&e.defaultValue!=="AutoInc"&&!e.defaultValue.startsWith("FK ->")&&(e.defaultValue==="Timestamp"?s+=" DEFAULT CURRENT_TIMESTAMP":s+=` DEFAULT ${e.defaultValue}`),` ${this.quoteIdentifier(e.name)} ${s}`}),o=a.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(t)} (
|
|
29
|
+
${r.join(`,
|
|
30
|
+
`)}
|
|
31
|
+
);`}},Le=class{quoteIdentifier(t){return`"${t}"`}escapeString(t){return t.replace(/'/g,"''")}buildCreateTable(t,a){let r=a.map(e=>{let s="";return e.isPk&&e.type==="Integer"?s="SERIAL PRIMARY KEY":(e.type==="Integer"&&(s="INTEGER"),e.type==="Text"&&(s="TEXT"),e.type==="Boolean"&&(s="BOOLEAN"),e.type==="Decimal"&&(s="NUMERIC"),e.type==="DateTime"&&(s="TIMESTAMP"),e.type==="Enum"&&e.enumValues&&(s=`VARCHAR(255) CHECK(${this.quoteIdentifier(e.name)} IN (${e.enumValues.map(c=>`'${this.escapeString(c)}'`).join(", ")}))`),e.isPk&&(s+=" PRIMARY KEY"),!e.nullable&&!e.isPk&&(s+=" NOT NULL")),e.defaultValue&&e.defaultValue!=="AutoInc"&&!e.defaultValue.startsWith("FK ->")&&(e.defaultValue==="Timestamp"?s+=" DEFAULT CURRENT_TIMESTAMP":s+=` DEFAULT ${e.defaultValue}`),` ${this.quoteIdentifier(e.name)} ${s}`}),o=a.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(t)} (
|
|
32
|
+
${r.join(`,
|
|
33
|
+
`)}
|
|
34
|
+
);`}},ke=class{quoteIdentifier(t){return`\`${t}\``}escapeString(t){return t.replace(/'/g,"''")}buildCreateTable(t,a){let r=a.map(e=>{let s="";return e.type==="Integer"&&(s="INT"),e.type==="Text"&&(s="VARCHAR(255)"),e.type==="Boolean"&&(s="BOOLEAN"),e.type==="Decimal"&&(s="DOUBLE"),e.type==="DateTime"&&(s="DATETIME"),e.type==="Enum"&&e.enumValues&&(s=`VARCHAR(255) CHECK(${this.quoteIdentifier(e.name)} IN (${e.enumValues.map(c=>`'${this.escapeString(c)}'`).join(", ")}))`),e.isPk&&e.type==="Integer"?s+=" AUTO_INCREMENT PRIMARY KEY":e.isPk&&(s+=" PRIMARY KEY"),!e.nullable&&!e.isPk&&(s+=" NOT NULL"),e.defaultValue&&e.defaultValue!=="AutoInc"&&!e.defaultValue.startsWith("FK ->")&&(e.defaultValue==="Timestamp"?s+=" DEFAULT CURRENT_TIMESTAMP":s+=` DEFAULT ${e.defaultValue}`),` ${this.quoteIdentifier(e.name)} ${s}`}),o=a.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(t)} (
|
|
35
|
+
${r.join(`,
|
|
36
|
+
`)}
|
|
37
|
+
);`}}});import S from"picocolors";function ee(n,t,a={}){let{title:r,emptyMessage:o="No records found",maxColWidth:e=30}=a,s={},c=0;for(let f of n){let y=f.length;for(let x of t){let I=String(x[f]??"");I.length>y&&(y=I.length)}let b=Math.max(f.length,Math.min(e,y));s[f]=b,c+=b}let i=3*(n.length-1),l=c+i+4,m=Math.max(74,l),p=m-4-i;if(c<p&&n.length>0){let f=n[n.length-1];s[f]+=p-c}let u=(f,y)=>f.length>y?f.slice(0,y-3)+"...":f,d="\u2550".repeat(m-2);if(console.log(S.cyan(`
|
|
38
|
+
\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)+S.bold(S.white(r))+" ".repeat(b);console.log(S.cyan("\u2551")+x+S.cyan("\u2551")),console.log(S.cyan(`\u2560${d}\u2563`))}if(n.length>0){let f=n.map(y=>S.bold(S.white(u(y,s[y]).padEnd(s[y])))).join(S.cyan(" \u2551 "));console.log(S.cyan("\u2551 ")+f+S.cyan(" \u2551")),console.log(S.cyan(`\u2560${d}\u2563`))}if(t.length===0){let f=m-4-o.length,y=Math.max(0,Math.floor(f/2)),b=Math.max(0,f-y);console.log(S.cyan("\u2551 ")+" ".repeat(y)+S.yellow(o)+" ".repeat(b)+S.cyan(" \u2551"))}else for(let f of t){let y=n.map(b=>S.white(u(String(f[b]??""),s[b]).padEnd(s[b]))).join(S.cyan(" \u2551 "));console.log(S.cyan("\u2551 ")+y+S.cyan(" \u2551"))}console.log(S.cyan(`\u255A${d}\u255D`))}var ve=E(()=>{"use strict"});var at={};L(at,{runQueryCommand:()=>ga});import ce from"picocolors";async function ga(n,t){n.type==="unknown"&&(console.log(ce.red("Error: No database connection found. Cannot run query.")),process.exit(1));let a=h(n),r=t[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 a.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(`
|
|
39
|
+
(${o.rows.length} rows)`)),await a.close(),process.exit(0)}catch(o){console.log(ce.red(`
|
|
40
|
+
Query Error: ${o.message}
|
|
41
|
+
`)),process.exit(1)}}var ot=E(()=>{"use strict";C();ve()});var nt={};L(nt,{runExportCommand:()=>fa});import U from"picocolors";import me from"fs/promises";import ue from"path";async function fa(n,t,a){n.type==="unknown"&&(console.log(U.red("Error: No database connection found. Cannot run export.")),process.exit(1));let r=h(n),o=t[0],e=a.format,s=a["schema-only"],{select:c}=await import("@inquirer/prompts");if(!o){let m=await r.getTables();m.length===0&&(console.log(U.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(),"drix_exports");await me.mkdir(i,{recursive:!0});let l=o==="*"?await r.getTables():[o];console.log(U.cyan(`
|
|
42
|
+
Starting export to ${i}...`));for(let m of l)try{if(s){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(U.green(`\u2714 Exported Schema (JSON): ${m}`))}else{let u=ue.join(i,`${m}_schema.csv`),d=`Name,Type,IsPrimaryKey,Nullable
|
|
43
|
+
`,f=p.map(y=>`"${y.name}","${y.type}","${y.isPk}","${y.nullable}"`).join(`
|
|
44
|
+
`);await me.writeFile(u,d+f,"utf-8"),console.log(U.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(U.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(",")+`
|
|
45
|
+
`,y=p.rows.map(b=>p.columns.map(x=>`"${String(b[x]??"").replace(/"/g,'""')}"`).join(",")).join(`
|
|
46
|
+
`);d=f+y}await me.writeFile(u,d,"utf-8"),console.log(U.green(`\u2714 Exported Data (CSV): ${m} (${p.rows.length} rows)`))}}}catch(p){console.log(U.red(`\u2718 Failed to export table ${m}: ${p.message}`))}await r.close(),console.log(U.cyan("Export complete.")),process.exit(0)}var st=E(()=>{"use strict";C()});import{Hono as ya}from"hono";function rt(n,t){let a=new ya,r=h(t);a.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)}}),a.get("/tables/stats",async o=>{try{let e=await r.getTables(),s={};for(let c of e)try{let i=await r.query(`SELECT COUNT(*) as c FROM "${c}"`);if(i&&i.rows&&i.rows.length>0){let l=i.rows[0],m=Object.values(l)[0];s[c]=parseInt(String(m),10)||0}else s[c]=0}catch{s[c]=0}return o.json({success:!0,data:s})}catch(e){return o.json({success:!1,error:e.message},500)}}),a.get("/tables/:name/schema",async o=>{let e=o.req.param("name");try{let s=await r.getSchema(e);return o.json({success:!0,data:s})}catch(s){return o.json({success:!1,error:s.message},500)}}),a.get("/tables/:name/data",async o=>{let e=o.req.param("name"),s=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,s,c,i,p);return o.json({success:!0,data:u})}catch(u){return o.json({success:!1,error:u.message},500)}}),a.post("/query",async o=>{try{let{sql:e}=await o.req.json(),s=await r.query(e);return o.json({success:!0,data:s})}catch(e){return o.json({success:!1,error:e.message},500)}}),n.route("/api",a)}var it=E(()=>{"use strict";C()});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 $a}from"url";async function xa(n){let t=new wa;rt(t,n);let r=Ae.includes("src")||Ae.includes("server")?pe.resolve(Ae,"../../dist/studio"):pe.resolve(Ae,"./studio"),o=await import("fs/promises");t.use("/*",ba({root:pe.relative(process.cwd(),r)})),t.get("*",async s=>{let c=pe.join(r,"index.html");try{let i=await o.readFile(c,"utf-8");return s.html(i)}catch{return s.text("Drix Studio static files not found. Did you run build?",404)}});let e=3e3;console.log(lt.cyan(`
|
|
47
|
+
Starting Drix Studio on http://localhost:${e}...`)),console.log(lt.dim(`Press Ctrl+C to stop the server.
|
|
48
|
+
`)),ha({fetch:t.fetch,port:e}),await Ea(`http://localhost:${e}`)}var Sa,Ae,mt=E(()=>{"use strict";it();Sa=$a(import.meta.url),Ae=pe.dirname(Sa)});var ut={};L(ut,{runBackupCommand:()=>Ta});import P from"picocolors";import Pe from"fs/promises";import qe from"path";async function Ta(n){n.type==="unknown"&&(console.log(P.red("Error: No database connection found. Cannot run backup.")),process.exit(1));let t=new Date().toISOString().replace(/[:.]/g,"-"),a=qe.join(process.cwd(),`drix_backup_${t}`);if(console.log(P.cyan(`
|
|
49
|
+
Starting database backup...`)),console.log(P.dim(`Database: ${n.type}`)),console.log(P.dim(`Target: ${n.targetUrl}`)),console.log(P.dim(`Backup Directory: ${a}
|
|
50
|
+
`)),await Pe.mkdir(a,{recursive:!0}),n.type==="sqlite")try{let o=qe.basename(n.targetUrl)||"database.sqlite",e=qe.join(a,o);await Pe.copyFile(n.targetUrl,e),console.log(P.green(`\u2714 Copied raw SQLite file to ${e}`))}catch(o){console.log(P.red(`\u2718 Failed to copy SQLite file: ${o.message}`))}let r=h(n);try{let o=await r.getTables();o.length===0&&console.log(P.yellow("No tables found to backup."));for(let e of o)try{let s=await r.getSchema(e),c=await r.getData(e,9999999,0),i={table:e,schema:s,totalRows:c.rows.length,data:c.rows},l=qe.join(a,`${e}.json`);await Pe.writeFile(l,JSON.stringify(i,null,2),"utf-8"),console.log(P.green(`\u2714 Dumped table: ${e} (${c.rows.length} rows)`))}catch(s){console.log(P.red(`\u2718 Failed to dump table ${e}: ${s.message}`))}}catch(o){console.log(P.red(`
|
|
51
|
+
Backup Error: ${o.message}`))}finally{await r.close()}console.log(P.cyan(`
|
|
52
|
+
Backup successfully completed at ${a}`)),process.exit(0)}var pt=E(()=>{"use strict";C()});var gt={};L(gt,{runImportCommand:()=>va});import M from"picocolors";import dt from"fs/promises";import Da from"path";function Ca(n){let t=n.split(/\r?\n/).filter(e=>e.trim()!=="");if(t.length===0)return[];let a=e=>{let s=[],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?(s.push(c),c=""):c+=m}return s.push(c),s},r=a(t[0]),o=[];for(let e=1;e<t.length;e++){let s=a(t[e]),c={};r.forEach((i,l)=>{c[i]=s[l]??""}),o.push(c)}return o}async function va(n,t,a){n.type==="unknown"&&(console.log(M.red("Error: No database connection found. Cannot run import.")),process.exit(1));let r=h(n),o=t[0],e=a.table,{input:s,select:c}=await import("@inquirer/prompts");o||(o=await s({message:"Enter the path to your CSV or JSON file:"}));let i=Da.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(`
|
|
53
|
+
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(`
|
|
54
|
+
|
|
55
|
+
\u2714 Successfully imported ${u} rows into ${e}!`))}catch(d){console.log(M.red(`
|
|
56
|
+
\u2718 Import failed at row ${u}: ${d.message}`))}finally{await r.close()}process.exit(0)}var ft=E(()=>{"use strict";C()});var yt={};L(yt,{runSeedCommand:()=>Ra});import O from"picocolors";function de(n){let t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",a="";for(let r=0;r<n;r++)a+=t.charAt(Math.floor(Math.random()*t.length));return a}function Aa(){let n=["gmail.com","yahoo.com","outlook.com","example.com"],t=de(8).toLowerCase(),a=n[Math.floor(Math.random()*n.length)];return`${t}@${a}`}function qa(n){let t=n.name.toLowerCase();if(t.includes("email"))return Aa();if(t.includes("phone"))return`+1${Math.floor(Math.random()*9e9+1e9)}`;if(t.includes("name"))return`User_${de(5)}`;if(t.includes("url")||t.includes("link"))return`https://example.com/${de(6)}`;if(t.includes("date")||t.includes("time")||t.includes("created")||t.includes("updated"))return new Date(Date.now()-Math.floor(Math.random()*1e10)).toISOString().replace("T"," ").slice(0,19);let a=n.type.toLowerCase();return a.includes("int")||a.includes("num")||a.includes("float")||a.includes("double")?Math.floor(Math.random()*1e3):a.includes("bool")||a==="tinyint(1)"?Math.random()>.5:a.includes("char")||a.includes("text")||a.includes("string")?de(10):de(5)}async function Ra(n,t){n.type==="unknown"&&(console.log(O.red("Error: No database connection found. Cannot run seed.")),process.exit(1));let a=h(n),r=t[0],o=t[1],{input:e,select:s}=await import("@inquirer/prompts");if(!r){let d=await a.getTables();d.length===0&&(console.log(O.yellow("No tables found. Please create a table first.")),process.exit(1)),r=await s({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(O.red("Error: Invalid count number.")),process.exit(1))}console.log(O.cyan(`
|
|
57
|
+
Analyzing schema for table '${r}'...`));let i;try{i=await a.getSchema(r)}catch(d){console.log(O.red(`Error: ${d.message}`)),process.exit(1)}let l=i.filter(d=>!d.isPk);l.length===0&&console.log(O.yellow("Table only has Primary Key columns. Seeding might fail if they don't auto-increment.")),console.log(O.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]=qa(y);m.push(f)}let p=500,u=0;try{n.type==="sqlite"?await a.executeSql("PRAGMA foreign_keys = OFF;"):n.type==="mysql"?await a.executeSql("SET FOREIGN_KEY_CHECKS = 0;"):n.type==="postgres"&&await a.executeSql("SET session_replication_role = replica;");for(let d=0;d<m.length;d+=p){let f=m.slice(d,d+p);await a.insert(r,f),u+=f.length,process.stdout.write(`\r${O.dim(`Progress: ${u} / ${c}`)}`)}console.log(O.green(`
|
|
58
|
+
|
|
59
|
+
\u2714 Successfully seeded ${u} fake records into ${r}!`))}catch(d){console.log(O.red(`
|
|
60
|
+
\u2718 Seed failed: ${d.message}`))}finally{try{n.type==="sqlite"?await a.executeSql("PRAGMA foreign_keys = ON;"):n.type==="mysql"?await a.executeSql("SET FOREIGN_KEY_CHECKS = 1;"):n.type==="postgres"&&await a.executeSql("SET session_replication_role = DEFAULT;")}catch{}await a.close()}process.exit(0)}var ht=E(()=>{"use strict";C()});var wt={};L(wt,{runDiagramCommand:()=>Pa});import V from"picocolors";import La from"fs/promises";import ka from"path";async function Pa(n){n.type==="unknown"&&(console.log(V.red("Error: No database connection found. Cannot generate diagram.")),process.exit(1));let t=h(n);console.log(V.cyan(`
|
|
61
|
+
Scanning database to generate ER diagram...`));try{let a=await t.getTables();a.length===0&&(console.log(V.yellow("No tables found in the database.")),process.exit(0));let r=`erDiagram
|
|
62
|
+
`;for(let s of a){r+=` ${s} {
|
|
63
|
+
`;let c=await t.getSchema(s);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}
|
|
64
|
+
`}r+=` }
|
|
65
|
+
`}let o=`
|
|
66
|
+
# Database Schema Diagram
|
|
67
|
+
|
|
68
|
+
You can paste this Mermaid code directly into [Draw.io](https://app.diagrams.net/) (Arrange > Insert > Advanced > Mermaid) or view it on GitHub/GitLab!
|
|
69
|
+
|
|
70
|
+
\`\`\`mermaid
|
|
71
|
+
${r}
|
|
72
|
+
\`\`\`
|
|
73
|
+
`,e=ka.resolve(process.cwd(),"drix_schema.md");await La.writeFile(e,o.trim(),"utf-8"),console.log(V.green(`
|
|
74
|
+
\u2714 Diagram generated successfully!`)),console.log(V.white(`Output saved to: ${V.bold(e)}`)),console.log(V.dim("Tip: Open this file in VSCode with a Markdown viewer or paste it into Draw.io."))}catch(a){console.log(V.red(`
|
|
75
|
+
\u2718 Failed to generate diagram: ${a.message}`))}finally{await t.close()}process.exit(0)}var bt=E(()=>{"use strict";C()});var $t={};L($t,{runExecCommand:()=>Na});import oe from"picocolors";import Et from"fs/promises";import Ia from"path";async function Na(n,t){n.type==="unknown"&&(console.log(oe.red("Error: No database connection found. Cannot run exec.")),process.exit(1));let a=t[0],{input:r}=await import("@inquirer/prompts");a||(a=await r({message:"Enter the path to your .sql file:"}));let o=Ia.resolve(process.cwd(),a);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 s=h(n);console.log(oe.cyan(`
|
|
76
|
+
Executing SQL script from ${a}...`));try{await s.executeSql(e),console.log(oe.green("\u2714 Script executed successfully!"))}catch(c){console.log(oe.red(`\u2718 Execution failed: ${c.message}`))}finally{await s.close()}process.exit(0)}var St=E(()=>{"use strict";C()});var xt={};L(xt,{runGenerateTypesCommand:()=>_a});import H from"picocolors";import Ba from"fs/promises";import Ma from"path";function Oa(n){let t=n.toLowerCase();return t.includes("int")||t.includes("num")||t.includes("float")||t.includes("double")||t.includes("real")?"number":t.includes("bool")||t==="tinyint(1)"?"boolean":t.includes("char")||t.includes("text")||t.includes("uuid")||t.includes("string")?"string":t.includes("date")||t.includes("time")?"Date":t.includes("json")?"any":"string"}async function _a(n){n.type==="unknown"&&(console.log(H.red("Error: No database connection found. Cannot generate types.")),process.exit(1));let t=h(n);console.log(H.cyan(`
|
|
77
|
+
Scanning database to generate TypeScript interfaces...`));try{let a=await t.getTables();a.length===0&&(console.log(H.yellow("No tables found in the database.")),process.exit(0));let r=`// Generated by Drix CLI
|
|
78
|
+
// Database: ${n.type.toUpperCase()}
|
|
79
|
+
|
|
80
|
+
`;for(let e of a){let s=e.charAt(0).toUpperCase()+e.slice(1).replace(/_([a-z])/g,i=>i[1].toUpperCase());r+=`export interface ${s} {
|
|
81
|
+
`;let c=await t.getSchema(e);for(let i of c){let l=Oa(i.type),m=i.nullable?"?":"";r+=` ${i.name}${m}: ${l};
|
|
82
|
+
`}r+=`}
|
|
83
|
+
|
|
84
|
+
`}let o=Ma.resolve(process.cwd(),"drix-types.d.ts");await Ba.writeFile(o,r.trim()+`
|
|
85
|
+
`,"utf-8"),console.log(H.green("\u2714 TypeScript interfaces generated successfully!")),console.log(H.white(`Output saved to: ${H.bold(o)}`))}catch(a){console.log(H.red(`
|
|
86
|
+
\u2718 Failed to generate types: ${a.message}`))}finally{await t.close()}process.exit(0)}var Tt=E(()=>{"use strict";C()});var Ct={};L(Ct,{runInitCommand:()=>Fa});import $ from"picocolors";import Re from"fs/promises";import Dt from"path";async function Fa(n){let{select:t,input:a,password:r}=await import("@inquirer/prompts"),o=n[0];(!o||!["sqlite","mysql","postgres"].includes(o.toLowerCase()))&&(o=await t({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($.cyan(`
|
|
87
|
+
Initializing a local ${o} database...`));let e="";if(o==="sqlite"){let i=n[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($.yellow(`File ${i} already exists.`))}catch{await Re.writeFile(l,""),console.log($.green(`\u2714 Created local database file: ${i}`))}e=`file:${i}`}else{console.log($.dim("Please provide credentials for your local server (e.g. running via XAMPP, Homebrew, etc.)"));let i=await a({message:"Server Host:",default:"localhost"}),l=await a({message:"Server Port:",default:o==="mysql"?"3306":"5432"}),m=await a({message:"Username:",default:o==="mysql"?"root":"postgres"}),p=await r({message:"Password (leave empty if none):"}),u=n[1];u||(u=await a({message:"New Database Name (e.g. my_project):"})),(!u||!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(u))&&(console.log($.red("Invalid database name. Please use only letters, numbers, and underscores.")),process.exit(1)),console.log($.cyan(`
|
|
88
|
+
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($.green(`\u2714 Created local database: ${u}`))}catch(y){console.log($.red(`\u2718 Failed to create database on server: ${y.message}`)),console.log($.dim(`
|
|
89
|
+
Could not connect to the local ${o} server on ${i}:${l}.`)),(y.code==="ECONNREFUSED"||y.message.includes("ECONNREFUSED")||y.message.includes("connect"))&&(console.log($.yellow(`
|
|
90
|
+
\u{1F4A1} It seems you don't have ${o} installed or running locally.`)),o==="mysql"?console.log($.white(`\u{1F449} Download MySQL here: ${$.cyan("https://dev.mysql.com/downloads/installer/")}`)):o==="postgres"&&(console.log($.white(`\u{1F449} Download PostgreSQL here: ${$.cyan("https://www.postgresql.org/download/")}`)),console.log($.white(`\u{1F449} Or use Postgres.app for Mac: ${$.cyan("https://postgresapp.com/")}`))),console.log($.dim("(Alternatively, you can run 'drix 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 s=Dt.resolve(process.cwd(),".env"),c="";try{c=await Re.readFile(s,"utf-8")}catch{}c.includes("DATABASE_URL=")?(c=c.replace(/DATABASE_URL=.*/g,`DATABASE_URL=${e}`),console.log($.yellow("\u2714 Updated existing .env file with new DATABASE_URL"))):(c+=(c.endsWith(`
|
|
91
|
+
`)||c.length===0?"":`
|
|
92
|
+
`)+`DATABASE_URL=${e}
|
|
93
|
+
`,console.log($.green("\u2714 Saved connection string to .env file"))),await Re.writeFile(s,c,"utf-8"),console.log($.green(`
|
|
94
|
+
\u{1F389} Initialization Complete!`)),console.log($.white(`You can now run ${$.bold("npx @terks.dev/drix")} to manage it!`)),process.exit(0)}var vt=E(()=>{"use strict"});var At={};L(At,{runDropDbCommand:()=>ja});import Q from"picocolors";import Ua from"fs/promises";import Wa from"path";async function ja(n){let{select:t,input:a,password:r,confirm:o}=await import("@inquirer/prompts"),e=n[0];(!e||!["sqlite","mysql","postgres"].includes(e.toLowerCase()))&&(e=await t({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 s=n[1];if(e==="sqlite"){s||(s=await a({message:"Enter the SQLite filename to delete (e.g. database.sqlite):",default:"database.sqlite"})),!s.endsWith(".sqlite")&&!s.endsWith(".db")&&(s+=".sqlite"),await o({message:`Are you absolutely sure you want to delete ${s}? This cannot be undone!`,default:!1})||(console.log(Q.yellow("Aborted.")),process.exit(0));let i=Wa.resolve(process.cwd(),s);try{await Ua.unlink(i),console.log(Q.green(`\u2714 Deleted local database file: ${s}`))}catch(l){console.log(Q.red(`\u2718 Failed to delete file: ${l.message}`))}}else{console.log(Q.dim(`Please provide credentials for your local ${e} server to drop a database.`));let c=await a({message:"Server Host:",default:"localhost"}),i=await a({message:"Server Port:",default:e==="mysql"?"3306":"5432"}),l=await a({message:"Username:",default:e==="mysql"?"root":"postgres"}),m=await r({message:"Password (leave empty if none):"});s||(s=await a({message:"Which Database Name do you want to DROP?"})),await o({message:`Are you absolutely sure you want to DROP DATABASE '${s}' from ${c}? All data will be lost!`,default:!1})||(console.log(Q.yellow("Aborted.")),process.exit(0)),console.log(Q.cyan(`
|
|
95
|
+
Connecting to local server to drop database '${s}'...`));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 \`${s}\``),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 "${s}"`),await d.end()}console.log(Q.green(`\u2714 Dropped database: ${s}`))}catch(u){console.log(Q.red(`\u2718 Failed to drop database on server: ${u.message}`)),process.exit(1)}}process.exit(0)}var qt=E(()=>{"use strict"});import{select as Va,Separator as Qa}from"@inquirer/prompts";import Y from"picocolors";var Rt,Lt=E(()=>{"use strict";Rt=async()=>await Va({message:"Table Manager - Choose an action:",theme:{prefix:Y.cyan("\u2713 "),icon:{cursor:Y.cyan("\u203A ")},style:{message:n=>Y.bold(Y.white(n)),highlight:n=>{let t=n.replace(/\x1b\[[0-9;]*m/g,"");return t.includes(" Back")?Y.red(t):Y.cyan(t)}}},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 Qa,{name:Y.dim(" Back"),value:"back",description:"Back to the main menu."}]})});import{input as Ie,select as ne,confirm as kt}from"@inquirer/prompts";import ge from"picocolors";async function fe(n,t,a=[],r=""){let o="";for(;!o;){if(o=await Ie({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)?a.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"}]}),s;e==="Enum"&&(s=(await Ie({message:"Enter comma-separated Enum values (e.g. active, pending, deleted):"})).split(",").map(d=>d.trim()).filter(d=>d),s.length===0&&console.log(ge.yellow("Warning: No enum values provided.")));let c="";e==="DateTime"&&await kt({message:"Set default to CURRENT_TIMESTAMP?",default:!1})&&(c="Timestamp");let i=!1,l;if(e!=="DateTime"){let u=[{name:"None",value:"none"}];t||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 n.getTables();if(f.length>0){let y=await ne({message:"Select target table:",choices:f.map(x=>({name:x,value:x}))}),b=await n.getSchema(y);if(b.length>0){let x=await ne({message:"Select target column:",choices:b.map(I=>({name:`${I.name} (${I.type})`,value:I.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 kt({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"&&s)c=await ne({message:`Default value for '${o}':`,choices:[{name:"None",value:""},...s.map(d=>({name:d,value:`'${d}'`}))]});else{let u=await Ie({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:s},isPk:i}}var Ne=E(()=>{"use strict"});import{input as Ka,confirm as Pt}from"@inquirer/prompts";import g from"picocolors";async function Nt(n){let t=h(n),a="";for(;!a;)a=await Ka({message:"Enter the new table name:"}),/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(a)||(console.log(g.red("Invalid table name. Use only letters, numbers, and underscores.")),a="");let r=[],o=!1,e=!0;for(;e;){It(a,r);let l=r.map(u=>u.name),{col:m,isPk:p}=await fe(t,o,l);if(!m)break;p&&(o=!0),r.push(m),e=await Pt({theme:{prefix:g.cyan(`
|
|
96
|
+
\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})}It(a,r),console.log(g.dim("Generating SQL..."));let c=j(n.type).buildCreateTable(a,r);if(console.log(g.cyan(`
|
|
97
|
+
--- SQL Preview ---`)),console.log(g.yellow(c)),console.log(g.cyan(`-------------------
|
|
98
|
+
`)),await Pt({message:"Do you want to execute this SQL to build the table?",default:!0}))try{console.log(g.dim("Executing SQL...")),await t.executeSql(c),console.log(g.green(`
|
|
99
|
+
\u2713 Table '${a}' created successfully!`))}catch(l){console.log(g.red(`
|
|
100
|
+
x Error creating table: ${l.message}`))}finally{await t.close()}else console.log(g.yellow("Aborted table creation.")),await t.close();await za()}async function za(){let{input:n}=await import("@inquirer/prompts");await n({message:"Press Enter to continue..."})}function It(n,t){console.clear(),console.log(`
|
|
101
|
+
`);let a={name:20,type:18,key:8,nullable:10,defaultCol:16},r=a.name+a.type+a.key+a.nullable+a.defaultCol+14;console.log(g.cyan(`\u2554${"\u2550".repeat(r)}\u2557`));let o=r-n.length,e=Math.max(0,Math.floor(o/2)),s=Math.max(0,o-e),c=" ".repeat(e)+g.bold(g.white(n))+" ".repeat(s);console.log(g.cyan("\u2551")+c+g.cyan("\u2551"));let i=(l,m,p)=>g.cyan(l+"\u2550".repeat(a.name+2)+m+"\u2550".repeat(a.type+2)+m+"\u2550".repeat(a.key+2)+m+"\u2550".repeat(a.nullable+2)+m+"\u2550".repeat(a.defaultCol+2)+p);console.log(i("\u2560","\u2566","\u2563")),console.log(g.cyan("\u2551 ")+g.bold(g.white("Column Name".padEnd(a.name)))+g.cyan(" \u2551 ")+g.bold(g.white("Type".padEnd(a.type)))+g.cyan(" \u2551 ")+g.bold(g.white("Key".padEnd(a.key)))+g.cyan(" \u2551 ")+g.bold(g.white("Nullable".padEnd(a.nullable)))+g.cyan(" \u2551 ")+g.bold(g.white("Default".padEnd(a.defaultCol)))+g.cyan(" \u2551")),t.length>0&&console.log(i("\u2560","\u256C","\u2563"));for(let l=0;l<t.length;l++){let m=t[l],p=g.white(m.name.padEnd(a.name)),u=g.white(m.type.padEnd(a.type)),d=m.isPk?"PK":"-",f=m.isPk?g.green(d.padEnd(a.key)):g.dim(d.padEnd(a.key)),y=m.nullable?"Yes":"No",b=m.nullable?g.green(y.padEnd(a.nullable)):g.dim(y.padEnd(a.nullable)),x=m.defaultValue||"-",I=m.defaultValue?g.yellow(x.padEnd(a.defaultCol)):g.dim(x.padEnd(a.defaultCol));console.log(g.cyan("\u2551 ")+p+g.cyan(" \u2551 ")+u+g.cyan(" \u2551 ")+f+g.cyan(" \u2551 ")+b+g.cyan(" \u2551 ")+I+g.cyan(" \u2551"))}if(t.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.
|
|
102
|
+
`))}var Bt=E(()=>{"use strict";Ce();C();Ne()});import Ha from"fs/promises";import Ya from"path";import{input as Ga}from"@inquirer/prompts";import K from"picocolors";async function Mt(n){if(console.log(K.green(`
|
|
103
|
+
Initiating SQL table writer for experts. Please ensure database connection is properly configured.`)),n.type==="unknown"){console.log(K.yellow(`
|
|
104
|
+
No database connection found. Please setup first.`));return}try{let t=await Ga({message:"Enter the path to your .md or .sql file:"});if(t&&t.trim()){let a=Ya.resolve(process.cwd(),t.trim()),r="";try{r=await Ha.readFile(a,"utf-8")}catch(e){console.log(K.red(`
|
|
105
|
+
x Failed to read file: ${e.message}`));return}let o=r;if(a.toLowerCase().endsWith(".md")){let e=[...r.matchAll(/```(?:sql)?\n([\s\S]*?)```/gi)];e.length>0&&(o=e.map(s=>s[1].trim()).join(`
|
|
106
|
+
|
|
107
|
+
`))}if(o.trim()){console.log(K.dim("Executing SQL..."));let e=h(n);try{await e.executeSql(o),console.log(K.green(`
|
|
108
|
+
\u2713 SQL executed successfully!`))}finally{await e.close()}}else console.log(K.yellow(`
|
|
109
|
+
\u26A0\uFE0F No SQL found in the file. Aborted.`))}else console.log(K.yellow(`
|
|
110
|
+
No file path entered. Aborted.`))}catch(t){console.log(K.red(`
|
|
111
|
+
Error executing SQL: ${t.message}`))}}var Ot=E(()=>{"use strict";C()});import{select as ye,confirm as _t,input as Za}from"@inquirer/prompts";import v from"picocolors";async function Ft(n){let t=h(n);try{let a=await t.getTables();if(a.length===0){console.log(v.yellow("No tables found in the database."));return}let r=await ye({message:"Select a table to drop:",choices:a.map(e=>({name:e,value:e}))});if(await _t({message:`Are you sure you want to DROP TABLE '${r}'? This will delete all its data!`,default:!1})){let e=`DROP TABLE ${r}`;n.type==="sqlite"||n.type==="postgres"?e=`DROP TABLE "${r}"`:n.type==="mysql"&&(e=`DROP TABLE \`${r}\``),await t.executeSql(e),console.log(v.green(`\u2713 Table '${r}' dropped successfully.`))}}catch(a){console.log(v.red(`Error: ${a.message}`))}finally{await t.close()}}async function he(n,t){let a=h(n),r=j(n.type);try{let o=await a.getTables();if(o.length===0){console.log(v.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}))}),s=await a.getSchema(e);if(t==="add"){let c=s.some(m=>m.isPk),i=s.map(m=>m.name),{col:l}=await fe(a,c,i);if(l){let m=r.buildCreateTable("tmp",[l]).split(`
|
|
112
|
+
`)[1].trim().replace(/,$/,""),p=`ALTER TABLE ${e} ADD COLUMN ${m}`;n.type==="sqlite"||n.type==="postgres"?p=`ALTER TABLE "${e}" ADD COLUMN ${m}`:n.type==="mysql"&&(p=`ALTER TABLE \`${e}\` ADD COLUMN ${m}`),await a.executeSql(p),console.log(v.green(`\u2713 Column '${l.name}' added successfully.`))}}else if(t==="rename"){if(s.length===0)return console.log(v.yellow("Table has no columns."));let c=await ye({message:"Select a column to rename:",choices:s.map(l=>({name:`${l.name} (${l.type})`,value:l.name}))}),i=await Za({message:`New name for '${c}':`});if(i&&i!==c){let l=`ALTER TABLE ${e} RENAME COLUMN ${c} TO ${i}`;n.type==="sqlite"||n.type==="postgres"?l=`ALTER TABLE "${e}" RENAME COLUMN "${c}" TO "${i}"`:n.type==="mysql"&&(l=`ALTER TABLE \`${e}\` RENAME COLUMN \`${c}\` TO \`${i}\``),await a.executeSql(l),console.log(v.green(`\u2713 Column renamed to '${i}' successfully.`))}}else if(t==="modify"){if(s.length===0)return console.log(v.yellow("Table has no columns."));if(n.type==="sqlite"){console.log(v.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:s.map(p=>({name:`${p.name} (${p.type})`,value:p.name}))}),i=s.some(p=>p.isPk&&p.name!==c),l=s.map(p=>p.name).filter(p=>p!==c),{col:m}=await fe(a,i,l,c);if(m){let p=r.buildCreateTable("tmp",[m]).split(`
|
|
113
|
+
`)[1].trim().replace(/,$/,""),u="";n.type==="postgres"?(console.log(v.yellow("Note: Complex constraint modifications might require raw SQL in Postgres.")),u=`ALTER TABLE "${e}" ALTER COLUMN ${p.replace(m.name,`"${m.name}" TYPE`)}`):n.type==="mysql"&&(u=`ALTER TABLE \`${e}\` MODIFY COLUMN ${p}`);try{await a.executeSql(u),console.log(v.green(`\u2713 Column '${c}' modified successfully.`))}catch(d){console.log(v.red("x Could not modify column automatically. Try using Raw SQL.")),console.log(v.dim(d.message))}}}else if(t==="delete"){if(s.length===0)return console.log(v.yellow("Table has no columns."));let c=await ye({message:"Select a column to delete:",choices:s.map(l=>({name:`${l.name} (${l.type})`,value:l.name}))});if(await _t({message:`Are you sure you want to delete column '${c}'? Data will be lost!`,default:!1})){let l=`ALTER TABLE ${e} DROP COLUMN ${c}`;n.type==="sqlite"||n.type==="postgres"?l=`ALTER TABLE "${e}" DROP COLUMN "${c}"`:n.type==="mysql"&&(l=`ALTER TABLE \`${e}\` DROP COLUMN \`${c}\``);try{await a.executeSql(l),console.log(v.green(`\u2713 Column '${c}' deleted successfully.`))}catch(m){console.log(v.red(`x Error deleting column: ${m.message}`))}}}}catch(o){console.log(v.red(`Error: ${o.message}`))}finally{await a.close()}}var Ut=E(()=>{"use strict";C();Ce();Ne()});var jt={};L(jt,{runTableManagerFlow:()=>Xa});import W from"picocolors";import{select as Wt,Separator as Ja}from"@inquirer/prompts";async function Xa(n){if(n.type==="unknown"){console.log(W.yellow(`
|
|
114
|
+
No database connection found. Please setup first.`)),await Be();return}let t=await Promise.resolve().then(()=>(C(),Qe)).then(r=>r.createDBAdapter(n)),a=!0;for(;a;){console.clear();let r="Scanning...";try{r=`${(await t.getTables()).length} tables detected`}catch(s){r=W.red(s.message)}let o=n.targetUrl;switch(se(` Table Builder & Manager \u2022 [${n.type.toUpperCase()}]`,[{label:"Database",value:n.type.toUpperCase()},{label:"Target",value:o.length>45?"..."+o.slice(-42):o},{label:"Tables",value:r}]),await Rt()){case"create":let s=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 Ja,{name:W.dim(" Cancel"),value:"cancel",description:"Back to Table Manager"}]});s==="wizard"?await Nt(n):s==="sql"&&await Mt(n);break;case"modify":await eo(n);break;case"drop":await Ft(n),await Be();break;case"back":a=!1;break}}await t.close()}async function eo(n){let t=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:n.type==="sqlite"},{name:"Delete Column",value:"del_col"},{name:" Back",value:"back"}]});if(t!=="back"){switch(t){case"add_col":await he(n,"add");break;case"rename_col":await he(n,"rename");break;case"mod_col":await he(n,"modify");break;case"del_col":await he(n,"delete");break}await Be()}}async function Be(){let{input:n}=await import("@inquirer/prompts");await n({message:"Press Enter to continue..."})}var Vt=E(()=>{"use strict";Lt();Bt();Ot();Ut();$e()});$e();import D from"picocolors";import re from"fs/promises";import J from"path";async function A(n){let t=process.cwd();if(n){let e="sqlite";return n.startsWith("postgres://")||n.startsWith("postgresql://")?e="postgres":n.startsWith("mysql://")?e="mysql":e="sqlite",{type:e,targetUrl:n.replace("file:",""),source:"manual"}}let a=[".","prisma","db","database","src/db","src/database"];for(let e of a)try{let s=J.join(t,e),i=(await re.readdir(s)).find(l=>l.endsWith(".db")||l.endsWith(".sqlite")||l.endsWith(".sqlite3"));if(i)return{type:"sqlite",targetUrl:J.join(s,i),source:"auto-detected"}}catch{}let r=J.join(t,"prisma","schema.prisma");try{let e=await re.readFile(r,"utf-8"),s=e.match(/provider\s*=\s*["']([^"']+)["']/),c=e.match(/url\s*=\s*(?:env\(["']([^"']+)["']\)|["']([^"']+)["'])/);if(s){let i=s[1];i==="postgresql"&&(i="postgres");let l="";if(c&&c[2]&&(l=c[2],l.startsWith("file:")&&(l=J.join(t,"prisma",l.replace("file:","")))),l&&(i==="sqlite"||i==="postgres"||i==="mysql"))return{type:i,targetUrl:l,source:".env"}}}catch{}let o=J.join(t,".env");try{let e=await re.readFile(o,"utf-8"),s=["DATABASE_URL","DB_URL","POSTGRES_URL","POSTGRES_PRISMA_URL","MYSQL_URL"];for(let c of s){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 Ue(n){let t=J.join(process.cwd(),".env"),a="";try{a=await re.readFile(t,"utf-8")}catch{}let r=/DATABASE_URL\s*=\s*["']?([^"'\r\n]+)["']?/;r.test(a)?a=a.replace(r,`DATABASE_URL="${n}"`):(a&&!a.endsWith(`
|
|
115
|
+
`)&&(a+=`
|
|
116
|
+
`),a+=`DATABASE_URL="${n}"
|
|
117
|
+
`),await re.writeFile(t,a,"utf-8")}C();import{select as Ze,Separator as le}from"@inquirer/prompts";import T from"picocolors";import{select as ra}from"@inquirer/prompts";import X from"picocolors";var Ke=async n=>await ra({message:"Select a table to edit data:",theme:{prefix:X.cyan("\u2713 "),icon:{cursor:X.cyan("\u203A ")},style:{message:t=>X.bold(X.white(t)),highlight:t=>{let a=t.replace(/\x1b\[[0-9;]*m/g,"");return a.includes(" Back")?X.red(a):X.cyan(a)}}},choices:n});Ce();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 ze(n,t,a,r){let o=j(t);console.log(w.cyan(`
|
|
118
|
+
--- Add Data to [${a}] ---`)),console.log(w.dim("Leave a field completely empty (press Enter) to skip it (e.g. for AutoInc or NULL)"));let e=[],s=[];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)),s.push(`'${o.escapeString(l)}'`))}if(e.length===0){console.log(w.yellow("No data entered. Aborted."));return}let c=`INSERT INTO ${o.quoteIdentifier(a)} (${e.join(", ")}) VALUES (${s.join(", ")});`;console.log(w.dim(`
|
|
119
|
+
Executing: `)+w.yellow(c));try{await n.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 He(n,t,a,r){let o=j(t);if(console.log(w.cyan(`
|
|
120
|
+
--- Edit Data in [${a}] ---`)),r.length===0)return;let s=(r.find(u=>u.isPk)||r[0]).name,c=await ie({message:`Enter the '${s}' 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(a)} SET ${o.quoteIdentifier(i)} = ${m} WHERE ${o.quoteIdentifier(s)} = '${o.escapeString(c)}';`;console.log(w.dim(`
|
|
121
|
+
Executing: `)+w.yellow(p));try{await n.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 Ye(n,t,a,r){let o=j(t);if(console.log(w.cyan(`
|
|
122
|
+
--- Delete Data from [${a}] ---`)),r.length===0)return;let s=(r.find(l=>l.isPk)||r[0]).name,c=await ie({message:`Enter the '${s}' of the record you want to delete:`});if(!c.trim()){console.log(w.yellow("Aborted."));return}let i=`DELETE FROM ${o.quoteIdentifier(a)} WHERE ${o.quoteIdentifier(s)} = '${o.escapeString(c)}';`;console.log(w.dim(`
|
|
123
|
+
Executing: `)+w.yellow(i));try{await n.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 Ge(n){console.log(w.cyan(`
|
|
124
|
+
--- Expert Mode: Execute Raw SQL ---`));try{let t=await ie({message:"Enter the path to your .md or .sql file:"});if(t&&t.trim()){let a=ca.resolve(process.cwd(),t.trim()),r="";try{r=await la.readFile(a,"utf-8")}catch(e){console.log(w.red(`
|
|
125
|
+
x Failed to read file: ${e.message}`));return}let o=r;if(a.toLowerCase().endsWith(".md")){let e=[...r.matchAll(/```(?:sql)?\n([\s\S]*?)```/gi)];e.length>0&&(o=e.map(s=>s[1].trim()).join(`
|
|
126
|
+
|
|
127
|
+
`))}if(o.trim()){console.log(w.dim(`
|
|
128
|
+
Executing SQL...`)),console.log(w.yellow(o));try{await n.executeSql(o),console.log(w.green(`
|
|
129
|
+
\u2713 SQL executed successfully!`))}catch(e){console.log(w.red(`
|
|
130
|
+
x Error executing SQL: ${e.message}`))}}else console.log(w.yellow(`
|
|
131
|
+
\u26A0\uFE0F No SQL found in the file. Aborted.`))}else console.log(w.yellow(`
|
|
132
|
+
No file path entered. Aborted.`))}catch(t){console.log(w.red(`
|
|
133
|
+
Error: ${t.message}`))}}ve();$e();async function Je(n){let t=h(n),a=!0;for(;a;){console.clear();let r=[],o=0,e="Scanning...";try{r=await t.getTables();for(let i of r)try{let l=n.type==="mysql"?"`":'"',m=await t.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 s=n.targetUrl,c=n.source===".env"?"Loaded from project .env file":n.source==="auto-detected"?"Auto-detected local SQLite file":"Manual connection config";se(` Data Browser & Editor \u2022 [${n.type.toUpperCase()}]`,[{label:"Database",value:n.type.toUpperCase()},{label:"Target",value:s.length>45?"..."+s.slice(-42):s},{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 t.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"){a=!1;continue}let m=!0,p=1,u="",d=50;for(;m;){console.clear();let f=(p-1)*d,y=await t.getSchema(l),b=y.map(N=>N.name),x;try{x=await t.getData(l,d,f,u)}catch(N){console.log(T.red(`
|
|
134
|
+
Error fetching data: ${N.message}
|
|
135
|
+
`)),u="";let{input:R}=await import("@inquirer/prompts");await R({message:"Click Enter to continue..."});continue}let I=x.rows,Kt=` ${l} (Page ${p}) `;ee(b,I,{title:Kt,maxColWidth:30});let zt=I.length===d,Ht=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];Ht&&we.push({name:"Previous Page",value:"prev"}),zt&&we.push({name:"Next Page",value:"next"}),we.push({name:T.dim(" Back"),value:"BACK"});let q=await Ze({message:"Select an action for this table:",theme:{prefix:T.cyan("\u2713 "),icon:{cursor:T.cyan("\u203A ")},style:{message:N=>T.bold(T.white(N)),highlight:N=>{let R=N.replace(/\x1b\[[0-9;]*m/g,"");return R.includes(" Back")?T.red(R):T.cyan(R)}}},choices:we});if(q==="prev"){p--;continue}if(q==="next"){p++;continue}if(q==="noop")continue;if(q==="BACK"){m=!1;continue}if(q==="clear_search"){u="",p=1;continue}if(q==="search"){let{input:N}=await import("@inquirer/prompts"),_=(await N({message:"Enter Search (e.g. `age > 18` or `John` for fuzzy search):"})).trim();if(_){if(/[=<>]|LIKE|IN|AND|OR/i.test(_))u=_;else{let G=y.filter(F=>F.type.toLowerCase().includes("char")||F.type.toLowerCase().includes("text"));if(G.length>0){let F=n.type==="postgres"?"ILIKE":"LIKE";u=G.map(Ee=>`"${Ee.name}" ${F} '%${_.replace(/'/g,"''")}%'`).join(" OR ")}else u=`"${y[0].name}" = '${_}'`}p=1}continue}if(q==="exportCsv"||q==="exportJson"){try{console.log(T.yellow(`
|
|
136
|
+
Exporting data...`));let R=await t.getData(l,9999999,0,u),_=await import("fs/promises"),be=await import("path"),G=be.join(process.cwd(),"drix_exports");if(await _.mkdir(G,{recursive:!0}),q==="exportCsv"){let F=R.columns.join(",")+`
|
|
137
|
+
`,Oe=R.rows.map(Yt=>R.columns.map(Gt=>`"${String(Yt[Gt]??"").replace(/"/g,'""')}"`).join(",")).join(`
|
|
138
|
+
`),Ee=be.join(G,`${l}.csv`);await _.writeFile(Ee,F+Oe,"utf-8"),console.log(T.green(`
|
|
139
|
+
Exported to ${Ee}`))}else{let F=be.join(G,`${l}.json`);await _.writeFile(F,JSON.stringify(R.rows,null,2),"utf-8"),console.log(T.green(`
|
|
140
|
+
Exported to ${F}`))}}catch(R){console.log(T.red(`
|
|
141
|
+
Export Error: ${R.message}`))}let{input:N}=await import("@inquirer/prompts");await N({message:"Click Enter to continue..."});continue}let Me=await Ze({message:`Select mode for ${q} 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 Ge(t),await te();continue}q==="add"?(await ze(t,n.type,l,y),await te()):q==="edit"?(await He(t,n.type,l,y),await te()):q==="delete"&&(await Ye(t,n.type,l,y),await te())}}}catch(i){console.log(T.red(`
|
|
142
|
+
x Error: ${i.message}`)),await te(),a=!1}}await t.close()}async function te(){let{input:n}=await import("@inquirer/prompts");await n({message:"Press Enter to continue..."})}import{select as ma,input as ua}from"@inquirer/prompts";import ae from"picocolors";C();async function Xe(n){let t=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."}]}),a=n;switch(t){case"auto":let r=await A();r.type!=="unknown"?(a=r,console.log(ae.green(`
|
|
143
|
+
Database connection setup successfully.`))):console.log(ae.red(`
|
|
144
|
+
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 A(o);console.log(ae.dim(`
|
|
145
|
+
Testing connection...`));try{let s=h(e);await s.getTables(),await s.close(),a=e,await Ue(o),console.log(ae.green("\u2713 Database connection setup successfully and saved to .env!"))}catch(s){console.log(ae.red(`
|
|
146
|
+
x Connection failed: ${s.message}
|
|
147
|
+
Please enter a valid database URL or check your database status.`))}}else console.log(ae.red(`
|
|
148
|
+
Failed to detect database connection. Please enter valid database URL.`));break}return a}import{select as pa,Separator as da}from"@inquirer/prompts";import z from"picocolors";var et=async n=>await pa({message:"Select an action:",theme:{prefix:z.cyan("\u2713 "),icon:{cursor:z.cyan("\u203A ")},style:{message:t=>z.bold(z.white(t)),highlight:t=>{let a=t.replace(/\x1b\[[0-9;]*m/g,"");return a.includes("Exit")?z.red(a):z.cyan(a)}}},choices:[{name:" Data Browser & Editor",value:"editor",description:"View, insert, update, and delete row data in your tables.",disabled:n.type==="unknown"},{name:" Run Raw SQL (REPL)",value:"repl",description:"Execute arbitrary SQL queries interactively.",disabled:n.type==="unknown"},{name:" Table Builder & Manager",value:"table",description:"Create new tables, or modify/drop existing tables.",disabled:n.type==="unknown"},new da,{name:n.type==="unknown"?" Setup Connection":" Connection Settings",value:n.type==="unknown"?"setup":"re-configure",description:n.type==="unknown"?"Setup the database connection manually.":"Re-configure the database connection."},{name:z.dim(" Exit"),value:"exit",description:"Exit the Drix CLI application."}]});C();ve();import B from"picocolors";async function tt(n){if(n.type==="unknown")return;let t=h(n),a=!0,{input:r}=await import("@inquirer/prompts");for(console.clear(),console.log(B.cyan(`
|
|
149
|
+
\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
|
|
150
|
+
`));a;)try{let e=(await r({message:B.green(`${n.type}>`)})).trim();if(e.toLowerCase()==="exit"||e.toLowerCase()==="quit"){a=!1;continue}if(e==="")continue;let s=await t.query(e);if(s.columns.length===0){console.log(B.yellow("Query executed successfully. (No output)"));continue}ee(s.columns,s.rows,{title:"Query Result",maxColWidth:50}),console.log(B.dim(`
|
|
151
|
+
(${s.rows.length} rows)`)),console.log()}catch(o){o.name==="ExitPromptError"?a=!1:console.log(B.red(`
|
|
152
|
+
Error: ${o.message}
|
|
153
|
+
`))}}import{parseArgs as to}from"util";async function Qt(){let n=process.argv.slice(2),t;n.length>0&&(n[0].startsWith("postgres://")||n[0].startsWith("postgresql://")||n[0].startsWith("mysql://")||n[0].startsWith("file:"))&&(t=n[0]);let{positionals:a,values:r}=to({args:n,options:{format:{type:"string"},"schema-only":{type:"boolean"},table:{type:"string"},help:{type:"boolean"}},strict:!1,allowPositionals:!0});r.help&&(console.log(D.cyan(`
|
|
154
|
+
Drix CLI - Modern Database Manager
|
|
155
|
+
`)),console.log(`${D.bold("Usage:")} npx @terks.dev/drix [command] [options]
|
|
156
|
+
`),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(`
|
|
157
|
+
${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(`
|
|
158
|
+
If you don't provide a command, Drix will launch the Interactive UI!`),process.exit(0));let o=t?void 0:a[0];if(o==="query"){let{runQueryCommand:i}=await Promise.resolve().then(()=>(ot(),at)),l=await A();await i(l,a.slice(1));return}if(o==="export"){let{runExportCommand:i}=await Promise.resolve().then(()=>(st(),nt)),l=await A();await i(l,a.slice(1),r);return}if(o==="studio"){let{runStudio:i}=await Promise.resolve().then(()=>(mt(),ct)),l=await A();await i(l);return}if(o==="backup"){let{runBackupCommand:i}=await Promise.resolve().then(()=>(pt(),ut)),l=await A();await i(l);return}if(o==="import"){let{runImportCommand:i}=await Promise.resolve().then(()=>(ft(),gt)),l=await A();await i(l,a.slice(1),r);return}if(o==="seed"){let{runSeedCommand:i}=await Promise.resolve().then(()=>(ht(),yt)),l=await A();await i(l,a.slice(1));return}if(o==="diagram"){let{runDiagramCommand:i}=await Promise.resolve().then(()=>(bt(),wt)),l=await A();await i(l);return}if(o==="exec"){let{runExecCommand:i}=await Promise.resolve().then(()=>(St(),$t)),l=await A();await i(l,a.slice(1));return}if(o==="generate-types"){let{runGenerateTypesCommand:i}=await Promise.resolve().then(()=>(Tt(),xt)),l=await A();await i(l);return}if(o==="init"){let{runInitCommand:i}=await Promise.resolve().then(()=>(vt(),Ct));await i(a.slice(1));return}if(o==="drop-db"){let{runDropDbCommand:i}=await Promise.resolve().then(()=>(qt(),At));await i(a.slice(1));return}let e=!0,s=await A(t);for(;e;)switch(console.clear(),_e(),Fe(s),await et(s)){case"editor":s.type==="unknown"?(console.log(D.yellow(`
|
|
159
|
+
No database connection found. Please run ${D.bold("drix check")} first.`)),await c()):await Je(s);break;case"table":let{runTableManagerFlow:l}=await Promise.resolve().then(()=>(Vt(),jt));await l(s);break;case"repl":await tt(s);break;case"setup":case"re-configure":s=await Xe(s),await c();break;case"exit":e=!1,console.log(D.dim(`
|
|
160
|
+
Thanks for using Drix. Goodbye!`));break}async function c(){let{input:i}=await import("@inquirer/prompts");await i({message:"Click Enter to continue..."})}}Qt().catch(n=>{n.name==="ExitPromptError"&&(console.log(`
|
|
161
|
+
Exit Drix.`),process.exit(0)),console.error(`
|
|
162
|
+
Error: `,n),process.exit(1)});
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "drixio",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "A lightweight interactive TUI database client",
|
|
5
|
+
"bin": {
|
|
6
|
+
"drix": "dist/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"keywords": [
|
|
12
|
+
"database",
|
|
13
|
+
"tui",
|
|
14
|
+
"cli",
|
|
15
|
+
"drix"
|
|
16
|
+
],
|
|
17
|
+
"author": "TerKSDev",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"type": "module",
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^26.1.2",
|
|
25
|
+
"@types/pg": "^8.20.0",
|
|
26
|
+
"tsup": "^8.5.1",
|
|
27
|
+
"tsx": "^4.23.1",
|
|
28
|
+
"typescript": "^7.0.2",
|
|
29
|
+
"vite": "^8.1.5",
|
|
30
|
+
"vitest": "^4.1.10"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@hono/node-server": "^2.0.12",
|
|
34
|
+
"@inquirer/prompts": "^8.5.2",
|
|
35
|
+
"hono": "^4.12.32",
|
|
36
|
+
"mysql2": "^3.23.2",
|
|
37
|
+
"open": "^11.0.0",
|
|
38
|
+
"pg": "^8.22.0",
|
|
39
|
+
"picocolors": "^1.1.1"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"dev": "tsx bin/cli.ts",
|
|
43
|
+
"build:cli": "tsup",
|
|
44
|
+
"build:studio": "vite build --config studio/vite.config.ts",
|
|
45
|
+
"build": "pnpm build:cli && pnpm build:studio"
|
|
46
|
+
}
|
|
47
|
+
}
|