db-studio 1.9.4 → 1.9.5

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.
Files changed (23) hide show
  1. package/README.md +5 -5
  2. package/dist/index.js +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/web-dist/assets/{_pathlessLayout-DhK1pVQZ.js → _pathlessLayout-beFslGvE.js} +1 -1
  5. package/dist/web-dist/assets/{_pathlessLayout-DhK1pVQZ.js.map → _pathlessLayout-beFslGvE.js.map} +1 -1
  6. package/dist/web-dist/assets/{_queryId-PHL0xcKj.js → _queryId-C4zyyS64.js} +1 -1
  7. package/dist/web-dist/assets/{_queryId-PHL0xcKj.js.map → _queryId-C4zyyS64.js.map} +1 -1
  8. package/dist/web-dist/assets/{_table-CThT7wMT.js → _table-DlrGH9zB.js} +1 -1
  9. package/dist/web-dist/assets/{_table-CThT7wMT.js.map → _table-DlrGH9zB.js.map} +1 -1
  10. package/dist/web-dist/assets/{_table-Ud147_vw.js → _table-aNWwwcm1.js} +1 -1
  11. package/dist/web-dist/assets/{_table-Ud147_vw.js.map → _table-aNWwwcm1.js.map} +1 -1
  12. package/dist/web-dist/assets/index-qlp0O2jT.js +86 -0
  13. package/dist/web-dist/assets/index-qlp0O2jT.js.map +1 -0
  14. package/dist/web-dist/assets/{queries.store-DruKauzK.js → queries.store-Cbuh20_d.js} +1 -1
  15. package/dist/web-dist/assets/{queries.store-DruKauzK.js.map → queries.store-Cbuh20_d.js.map} +1 -1
  16. package/dist/web-dist/assets/{query-runner-Db56mYGQ.js → query-runner-CRvSuMh-.js} +1 -1
  17. package/dist/web-dist/assets/{query-runner-Db56mYGQ.js.map → query-runner-CRvSuMh-.js.map} +1 -1
  18. package/dist/web-dist/assets/{runner-DAA3MnAn.js → runner-CNit1IxV.js} +1 -1
  19. package/dist/web-dist/assets/{runner-DAA3MnAn.js.map → runner-CNit1IxV.js.map} +1 -1
  20. package/dist/web-dist/index.html +1 -1
  21. package/package.json +1 -1
  22. package/dist/web-dist/assets/index-GnOMl7Md.js +0 -86
  23. package/dist/web-dist/assets/index-GnOMl7Md.js.map +0 -1
package/README.md CHANGED
@@ -6,14 +6,14 @@ A modern, universal (pgAdmin alternative) database management studio for any dat
6
6
  <a href="https://github.com/husamql3/db-studio/stargazers">
7
7
  <img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/husamql3/db-studio">
8
8
  </a>
9
- <a href="https://www.npmjs.com/package/dbstudio">
10
- <img src="https://img.shields.io/npm/v/dbstudio?style=flat-rounded" />
9
+ <a href="https://www.npmjs.com/package/db-studio">
10
+ <img src="https://img.shields.io/npm/v/db-studio?style=flat-rounded" />
11
11
  </a>
12
- <a href="https://www.npmjs.com/package/dbstudio">
13
- <img src="https://img.shields.io/npm/dm/dbstudio?style=flat-rounded" />
12
+ <a href="https://www.npmjs.com/package/db-studio">
13
+ <img src="https://img.shields.io/npm/dm/db-studio?style=flat-rounded" />
14
14
  </a>
15
15
  <a href="https://github.com/husamql3/db-studio/blob/main/LICENSE">
16
- <img alt="License" src="https://img.shields.io/github/license/husamql3/pstrack">
16
+ <img alt="License" src="https://img.shields.io/github/license/husamql3/db-studio">
17
17
  </a>
18
18
  <a href="https://deepwiki.com/husamql3/db-studio">
19
19
  <img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki">
package/dist/index.js CHANGED
@@ -414,5 +414,5 @@ This will return the 5 customers with the highest total order value. You might a
414
414
  `)}if(a.relationships&&a.relationships.length>0){t+=`
415
415
  **Relationships:**
416
416
  `;for(let r of a.relationships)t+=` - ${r.fromTable}.${r.fromColumn} -> ${r.toTable}.${r.toColumn}
417
- `}return t}var ia=E(()=>{"use strict"});function rs(a){let e={name:a.columnName,type:a.dataTypeLabel,nullable:a.isNullable};return a.isPrimaryKey&&(e.isPrimaryKey=!0),a.isForeignKey&&a.referencedTable&&a.referencedColumn&&(e.foreignKey=`${a.referencedTable}.${a.referencedColumn}`),a.enumValues&&a.enumValues.length>0&&(e.enumValues=a.enumValues,e.description=`Enum values: ${a.enumValues.join(", ")}`),e}function ns(a){let e=[];for(let t of a)for(let r of t.columns)if(r.foreignKey){let[n,o]=r.foreignKey.split(".");e.push({fromTable:t.name,fromColumn:r.name,toTable:n,toColumn:o})}return e}function as(a){if(a==null)return"";if(a instanceof Date)return a.toISOString();if(typeof a=="bigint")return a.toString();if(typeof a=="object")try{return JSON.stringify(a)}catch{return String(a)}return String(a)}async function os(a,e,t){try{return(await a.getTableData({tableName:e,db:t,limit:3})).data.map(n=>Object.fromEntries(Object.entries(n).map(([o,s])=>[o,as(s)])))}catch{return[]}}async function ss(a,e={}){let t=Le(),{includeSampleData:r=!1,maxTables:n}=e,o=q(t);try{let s=await o.getTablesList(a),i=s.slice(0,n??s.length).map(m=>m.tableName).map(async m=>{let[p,d]=await Promise.all([o.getTableColumns({tableName:m,db:a}),r?os(o,m,a):Promise.resolve([])]),f={name:m,columns:p.map(rs)};return d.length>0&&(f.sampleData=d),f}),l=await Promise.all(i),u=ns(l);return{dbType:t,tables:l,relationships:u}}catch(s){throw new Error(`Failed to fetch database schema: ${s instanceof Error?s.message:"Unknown error"}`)}}async function la(a){return ss(a,{includeSampleData:!0,includeDescriptions:!0})}var ca=E(()=>{"use strict";Re();Pe()});import{zValidator as is}from"@hono/zod-validator";import{Hono as ls}from"hono";var ua,ma=E(()=>{"use strict";Ze();he();ia();ca();ua=new ls().basePath("/chat").get("/limit",async a=>{let e=await fetch(`${we.PROXY_URL}/chat/limit`,{headers:{"cf-connecting-ip":a.req.header("cf-connecting-ip")??"","x-real-ip":a.req.header("x-real-ip")??"","x-forwarded-for":a.req.header("x-forwarded-for")??"","x-api-key":a.req.header("x-api-key")??""}}),t=await e.json();return a.json(t,e.status)}).post("/",is("json",kt),async a=>{let{messages:e,data:t}=a.req.valid("json"),{db:r,conversationId:n}=t,o=await la(r),s=sa(o),c={messages:e,conversationId:n,systemPrompt:s},i=await fetch(`${we.PROXY_URL}/chat`,{method:"POST",headers:{"Content-Type":"application/json","cf-connecting-ip":a.req.header("cf-connecting-ip")??"","x-real-ip":a.req.header("x-real-ip")??"","x-forwarded-for":a.req.header("x-forwarded-for")??"","x-api-key":a.req.header("x-api-key")??""},body:JSON.stringify(c)});if(!i.ok){let m=await i.json();return a.json({error:m.error||"Proxy request failed"},i.status)}let{readable:l,writable:u}=new TransformStream;return i.body?.pipeTo(u),new Response(l,{headers:{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"}})})});import{Hono as cs}from"hono";var da,pa=E(()=>{"use strict";Re();Pe();da=new cs().basePath("/databases").get("/",async a=>{let e=Le(),r=await q(e).getDatabasesList();return a.json({data:{databases:r,dbType:e}},200)}).get("/current",async a=>{let e=Le(),r=await q(e).getCurrentDatabase();return a.json({data:{db:r.db,dbType:e}},200)}).get("/connection",async a=>{let e=Le(),r=await q(e).getDatabaseConnectionInfo();return a.json({data:r},200)})});import{zValidator as fa}from"@hono/zod-validator";import{Hono as us}from"hono";var ba,ya=E(()=>{"use strict";he();Re();ba=new us().basePath("/query").post("/",fa("query",D),fa("json",Wt),async a=>{let{query:e}=a.req.valid("json"),{db:t}=a.req.valid("query"),r=a.get("dbType"),o=await q(r).executeQuery({query:e,db:t});return a.json({data:o},200)})});import{zValidator as Ce}from"@hono/zod-validator";import{Hono as ms}from"hono";var ga,ha=E(()=>{"use strict";he();Re();ga=new ms().basePath("/records").post("/",Ce("query",D),Ce("json",It),async a=>{let{db:e}=a.req.valid("query"),{tableName:t,data:r}=a.req.valid("json"),n=a.get("dbType"),o=q(n),{insertedCount:s}=await o.addRecord({db:e,params:{tableName:t,data:r}});return a.json({data:`Record inserted into "${t}" with ${s} rows inserted`},200)}).patch("/",Ce("query",D),Ce("json",Qt),async a=>{let{db:e}=a.req.valid("query"),{tableName:t,primaryKey:r,updates:n}=a.req.valid("json"),o=a.get("dbType"),s=q(o),{updatedCount:c}=await s.updateRecords({params:{tableName:t,primaryKey:r,updates:n},db:e});return a.json({data:`Updated ${c} records in "${t}"`},200)}).delete("/",Ce("query",D),Ce("json",mt),async a=>{let{db:e}=a.req.valid("query"),{tableName:t,primaryKeys:r}=a.req.valid("json"),n=a.get("dbType"),o=q(n),{deletedCount:s,fkViolation:c,relatedRecords:i}=await o.deleteRecords({tableName:t,primaryKeys:r,db:e});return c?a.json({data:{deletedCount:0,fkViolation:!0,relatedRecords:i}},409):a.json({data:{deletedCount:s,fkViolation:!1,relatedRecords:[]}},200)}).delete("/force",Ce("query",D),Ce("json",mt),async a=>{let{db:e}=a.req.valid("query"),{tableName:t,primaryKeys:r}=a.req.valid("json"),n=a.get("dbType"),s=await q(n).forceDeleteRecords({tableName:t,primaryKeys:r,db:e});return a.json({data:s},200)}).post("/bulk",Ce("query",D),Ce("json",vt),async a=>{let{db:e}=a.req.valid("query"),{tableName:t,records:r}=a.req.valid("json"),n=a.get("dbType"),s=await q(n).bulkInsertRecords({tableName:t,records:r,db:e});return a.json({data:s},200)})});import{utils as Qe,write as ds}from"xlsx";function Ta({cols:a,rows:e,format:t,tableName:r}){switch(t){case"json":{let n=JSON.stringify(e??[],null,2);return new Uint8Array(Buffer.from(n,"utf-8"))}case"csv":{let n=[a,...e?.map(c=>a?.map(i=>c[i]))??[]],o=Qe.aoa_to_sheet(n),s=Qe.sheet_to_csv(o);return new Uint8Array(Buffer.from(s,"utf-8"))}case"xlsx":{let n=[a,...e?.map(i=>a?.map(l=>i[l]))??[]],o=Qe.aoa_to_sheet(n),s=Qe.book_new();Qe.book_append_sheet(s,o,r.slice(0,31));let c=ds(s,{bookType:"xlsx",type:"buffer"});return new Uint8Array(c)}}}var Ea=E(()=>{"use strict"});import{zValidator as K}from"@hono/zod-validator";import{Hono as ps}from"hono";var Ca,wa=E(()=>{"use strict";he();Re();Ea();Ca=new ps().basePath("/tables").get("/",K("query",D),async a=>{let{db:e}=a.req.valid("query"),t=a.get("dbType"),n=await q(t).getTablesList(e);return a.json({data:n},200)}).post("/",K("query",D),K("json",Kt),async a=>{let{db:e}=a.req.valid("query"),t=a.req.valid("json"),r=a.get("dbType");return await q(r).createTable({tableData:t,db:e}),a.json({data:`Table ${t.tableName} created successfully`},200)}).delete("/:tableName",K("query",Vt),K("param",ce),async a=>{let{db:e,cascade:t}=a.req.valid("query"),{tableName:r}=a.req.valid("param"),n=a.get("dbType"),s=await q(n).deleteTable({tableName:r,db:e,cascade:t});return a.json({data:s},200)}).delete("/:tableName/columns/:columnName",K("query",xt),K("param",te),async a=>{let{db:e,cascade:t}=a.req.valid("query"),{tableName:r,columnName:n}=a.req.valid("param"),o=a.get("dbType"),s=q(o),{deletedCount:c}=await s.deleteColumn({tableName:r,columnName:n,cascade:t,db:e});return a.json({data:`Column "${n}" deleted successfully from table "${r}" with ${c} rows deleted`},200)}).post("/:tableName/columns",K("query",D),K("param",ce),K("json",me),async a=>{let{db:e}=a.req.valid("query"),{tableName:t}=a.req.valid("param"),r=a.req.valid("json"),n=a.get("dbType");return await q(n).addColumn({tableName:t,db:e,...r}),a.json({data:`Column "${r.columnName}" added successfully to table "${t}"`},200)}).patch("/:tableName/columns/:columnName/rename",K("query",D),K("param",te),K("json",dt),async a=>{let{db:e}=a.req.valid("query"),{tableName:t,columnName:r}=a.req.valid("param"),n=a.req.valid("json"),o=a.get("dbType");return await q(o).renameColumn({tableName:t,columnName:r,db:e,...n}),a.json({data:`Column "${r}" renamed to "${n.newColumnName}" in table "${t}"`},200)}).patch("/:tableName/columns/:columnName",K("query",D),K("param",te),K("json",ke),async a=>{let{db:e}=a.req.valid("query"),{tableName:t,columnName:r}=a.req.valid("param"),n=a.req.valid("json"),o=a.get("dbType");return await q(o).alterColumn({tableName:t,columnName:r,db:e,...n}),a.json({data:`Column "${r}" updated successfully in table "${t}"`},200)}).get("/:tableName/columns",K("query",D),K("param",ce),async a=>{let{db:e}=a.req.valid("query"),{tableName:t}=a.req.valid("param"),r=a.get("dbType"),o=await q(r).getTableColumns({tableName:t,db:e});return a.json({data:o},200)}).get("/:tableName/schema",K("query",D),K("param",ce),async a=>{let{db:e}=a.req.valid("query"),{tableName:t}=a.req.valid("param"),r=a.get("dbType"),o=await q(r).getTableSchema({tableName:t,db:e});return a.json({data:{schema:o}},200)}).get("/:tableName/data",K("param",ce),K("query",Gt),async a=>{let{tableName:e}=a.req.valid("param"),{cursor:t,limit:r,direction:n,sort:o,order:s,filters:c,db:i}=a.req.valid("query"),l=a.get("dbType"),m=await q(l).getTableData({tableName:e,cursor:t,limit:r,direction:n,sort:o,order:s,filters:c,db:i});return a.json({data:m},200)}).get("/:tableName/export",K("param",ce),K("query",zt),async a=>{let{tableName:e}=a.req.valid("param"),{db:t,format:r}=a.req.valid("query"),n=a.get("dbType"),o=q(n),{cols:s,rows:c}=await o.exportTableData({tableName:e,db:t}),i=Ta({cols:s,rows:c,format:r,tableName:e}),l;switch(r){case"csv":l="text/csv";break;case"xlsx":l="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";break;case"json":l="application/json";break}return new Response(i,{headers:{"Content-Type":l??"","Content-Disposition":`attachment; filename="${e}_export.${r}"`}})})});var Na={};pr(Na,{createServer:()=>ws});import{existsSync as Ra}from"fs";import Ae from"path";import{fileURLToPath as fs}from"url";import{serveStatic as Je}from"@hono/node-server/serve-static";import{zValidator as bs}from"@hono/zod-validator";import{Hono as ys}from"hono";import{cors as gs}from"hono/cors";import{HTTPException as hs}from"hono/http-exception";import{logger as Ts}from"hono/logger";import{prettyJSON as Es}from"hono/pretty-json";import{z as Sa}from"zod";var Xe,Cs,ws,Aa=E(()=>{"use strict";Re();ra();aa();ma();pa();ya();ha();wa();Xe=()=>{if(process.env.NODE_ENV==="development")return Ae.resolve(process.cwd(),"../web/dist");let a=Ae.dirname(fs(import.meta.url)),e=Ae.resolve(a,"./web-dist"),t=Ae.resolve(process.cwd(),"../web/dist"),r=Ae.resolve(e,"index.html"),n=Ae.resolve(t,"index.html");return!Ra(r)&&Ra(n)?t:e},Cs=Sa.object({dbType:Sa.string().refine(a=>se.getSupportedTypes().includes(a),{message:`Invalid database type. Supported types: ${se.getSupportedTypes().join(", ")}`})}),ws=()=>{ta();let a=new ys({strict:!1}).use("/*",gs()).use(Es({space:2})).use(process.env.NODE_ENV==="development"?Ts():(e,t)=>t()).use("/favicon.ico",Je({path:Ae.resolve(Xe(),"favicon.ico")})).use("*",async(e,t)=>{e.header("Access-Control-Allow-Origin","*"),e.header("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, OPTIONS"),e.header("Access-Control-Allow-Headers","Content-Type"),await t()}).onError(na).route("/",da).route("/",ua).use("/assets/*",Je({root:Xe()})).use("/image.png",Je({root:Xe()})).use("/:dbType/*",bs("param",Cs,(e,t)=>{if(!e.success){let r=t.req.param("dbType");throw new hs(400,{message:`Invalid database type: "${r}". Supported types: ${se.getSupportedTypes().join(", ")}`})}}),async(e,t)=>{let{dbType:r}=e.req.valid("param");e.set("dbType",r),await t()}).route("/:dbType",Ca).route("/:dbType",ga).route("/:dbType",ba);return process.env.NODE_ENV!=="test"&&(a.use("/*",Je({root:Xe()})),a.get("/*",Je({path:Ae.resolve(Xe(),"index.html")}))),{app:a}}});Ze();import{intro as Rs,outro as Ss}from"@clack/prompts";import{serve as Ns}from"@hono/node-server";import dr from"picocolors";import{program as Rr}from"commander";var Sr=()=>(Rr.name("db-studio").option("-e, --env <path>","Path to custom .env file").option("-p, --port <port>","Port to run the server on").option("-d, --database-url <url>","Database URL to use").option("-n, --var-name <name>","Custom environment variable name (default: DATABASE_URL)").option("-s, --status","Show status of the server").option("-h, --help","Show help").option("-v, --version","Show version").parse(process.argv),Rr.opts());import{readFile as _a}from"fs/promises";import{resolve as $a}from"path";import{cancel as et,isCancel as Rt,note as Nr,select as Oa,spinner as La,text as Ar}from"@clack/prompts";import{parse as Pa}from"dotenv";import St from"picocolors";var Dr=async(a,e)=>{let t=e||"DATABASE_URL";if(a?.[t])return a[t];if(process.env[t])return process.env[t];let r=La();r.start("Looking for database connection..."),a?Nr(St.red(`${t} not found in .env or process.env`)):Nr(St.red(`No .env file found and ${t} not set in process.env`));let n=await Oa({message:`How do you want to provide ${t}?`,options:[{value:"manual",label:"Enter connection string manually"},{value:"other-env",label:"Use different .env file"},{value:"cancel",label:"Cancel / Exit"}],initialValue:"manual"});if((Rt(n)||n==="cancel")&&(et("No database connection provided. Exiting..."),process.exit(0)),n==="other-env"){r.start("Waiting for path...");let s=await Ar({message:"Enter path to .env file",placeholder:"~/projects/myapp/.env.local or ./special.env",validate(i){if(!i?.trim())return"Path is required"}});Rt(s)&&(et("Cancelled."),process.exit(0)),r.stop("Trying custom .env...");let c=$a(s);try{let i=await _a(c,"utf-8"),l=Pa(i);if(l[t])return l[t];throw new Error(`${t} still missing in custom file`)}catch(i){let l=i;et(`Cannot read or parse file: ${St.dim(l.message)}`),process.exit(1)}}r.stop("Manual input...");let o=await Ar({message:`Paste your ${t}`,placeholder:"postgresql://user:password@localhost:5432/mydb or sqlite:///path/to/db.sqlite",validate(s){if(!s?.trim())return"Connection string is required!";if(!s.trim().startsWith("sqlite://"))try{new URL(s);return}catch{return"Must be a valid URL format"}}});return Rt(o)&&(et("Cancelled."),process.exit(0)),o.trim()};import{access as Ma,readFile as Ia}from"fs/promises";import{dirname as xa,resolve as Nt}from"path";import{parse as va}from"dotenv";var ka=async a=>{let e=Nt(a);for(;;){let t=Nt(e,".env");try{return await Ma(t),t}catch{}let r=xa(e);if(r===e)return null;e=r}},$e=async a=>{let e;if(a?e=Nt(a):e=await ka(process.cwd()),!e)return null;try{let t=await Ia(e,"utf-8");return va(t)}catch(t){if(t instanceof Error&&t.message.includes("ENOENT"))return null;throw t}};wt();import{intro as qa,outro as Ua}from"@clack/prompts";import _r from"picocolors";var $r=()=>{qa(_r.inverse(" db-studio ")),Ua(_r.green(`For more information, visit: ${Cr.SITE_DOCS_LINK}`))};Ze();import{intro as Fa,note as Ba,outro as Or}from"@clack/prompts";import tt from"picocolors";var Lr=async(a,e,t)=>{Fa(tt.inverse(" db-studio "));let r=t||we.VAR_NAME,n=null;if(e)n=e;else{let o=a?await $e(a):await $e();o?.[r]?n=o[r]:process.env[r]&&(n=process.env[r]??null)}n?Or(tt.green(`\u2713 Database connection configured (using ${r})`)):(Ba(tt.red(`\u2717 ${r} not found`),"Status"),Or(tt.yellow("\u26A0 No database connection configured")))};import{intro as Ha,outro as Ka}from"@clack/prompts";import Mr from"picocolors";var Pr={name:"db-studio",type:"module",version:"1.9.4",description:"Modern database client for PostgreSQL with spreadsheet-like grid, AI-powered SQL assistance, ER diagrams, fast data browsing and editing. CLI tool, upcoming desktop & web versions.",keywords:["database client","database gui","database browser","sql client","sql editor","query tool","table editor","data editor","postgres","postgresql","postgresql client","postgresql gui","pgadmin alternative","mysql","mysql client","ai sql","er diagram","database management","database studio"],author:"H\xFCsam \u{1F951} <devhsmq@gmail.com>",homepage:"https://dbstudio.sh",repository:{type:"git",url:"git+https://github.com/husamql3/db-studio.git"},bugs:{url:"https://github.com/husamql3/db-studio/issues"},license:"BSL-1.1",bin:{"db-studio":"./dist/index.js"},files:["dist"],scripts:{dev:"portless run","dev:app":"NODE_ENV=development tsx watch src/index.ts",build:"tsup --minify --sourcemap",prepack:"cd ../web && bun run build && cd ../server && bun run build",start:`sh -c 'APP_PORT=\${PORT:-3333}; portless proxy start >/dev/null && portless alias db-studio "$APP_PORT" --force >/dev/null && HOST=127.0.0.1 PORT="$APP_PORT" DB_STUDIO_SERVER_URL=https://db-studio.localhost node dist/index.js'`,format:"biome check --write --unsafe",test:"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage"},portless:{name:"api.dbstudio",script:"dev:app"},dependencies:{"@clack/prompts":"^1.0.1","@hono/node-server":"^2.0.0","@hono/zod-validator":"^0.8.0","better-sqlite3":"^12.9.0",commander:"^15.0.0",dotenv:"^17.3.1",hono:"^4.10.4",ioredis:"^5.4.1",mongodb:"^7.1.1",mssql:"^12.2.0",mysql2:"^3.18.2",pg:"^8.13.1",picocolors:"^1.1.1",xlsx:"^0.18.5",zod:"^4.2.1"},devDependencies:{"@biomejs/biome":"^2.2.6","@db-studio/shared":"workspace:*","@types/better-sqlite3":"^7.6.13","@types/mssql":"^12.3.0","@types/node":"^26.0.0","@types/pg":"^8.16.0","@vitest/coverage-v8":"^4.0.17",tsup:"^8.5.1",tsx:"^4.7.1",typescript:"^6.0.3",vitest:"^4.0.17"}};var Ir=()=>{Ha(Mr.inverse(" db-studio ")),Ka(Mr.green(`\u{1F680} db-studio v${Pr.version}`))};var As=async()=>{let{env:a,port:e,databaseUrl:t,varName:r,status:n,help:o,version:s}=Sr();o&&($r(),process.exit(0)),s&&(Ir(),process.exit(0)),n&&(await Lr(a,t,r),process.exit(0)),Rs(dr.inverse(" db-studio "));let c=Number.parseInt(e??process.env.PORT??String(we.PORT),10),i=process.env.HOST,l=r||we.VAR_NAME,u=a?await $e(a):await $e(),m=t||await Dr(u,l);process.env.DATABASE_URL=m;let{createServer:p}=await Promise.resolve().then(()=>(Aa(),Na)),{app:d}=p();Ns({fetch:d.fetch,hostname:i,port:c});let f=process.env.DB_STUDIO_SERVER_URL??process.env.PORTLESS_URL??`http://localhost:${c}`;Ss(dr.green(`Server running at ${dr.cyan(f)}`))};As().catch(a=>{process.exit(1)});export{As as main};
417
+ `}return t}var ia=E(()=>{"use strict"});function rs(a){let e={name:a.columnName,type:a.dataTypeLabel,nullable:a.isNullable};return a.isPrimaryKey&&(e.isPrimaryKey=!0),a.isForeignKey&&a.referencedTable&&a.referencedColumn&&(e.foreignKey=`${a.referencedTable}.${a.referencedColumn}`),a.enumValues&&a.enumValues.length>0&&(e.enumValues=a.enumValues,e.description=`Enum values: ${a.enumValues.join(", ")}`),e}function ns(a){let e=[];for(let t of a)for(let r of t.columns)if(r.foreignKey){let[n,o]=r.foreignKey.split(".");e.push({fromTable:t.name,fromColumn:r.name,toTable:n,toColumn:o})}return e}function as(a){if(a==null)return"";if(a instanceof Date)return a.toISOString();if(typeof a=="bigint")return a.toString();if(typeof a=="object")try{return JSON.stringify(a)}catch{return String(a)}return String(a)}async function os(a,e,t){try{return(await a.getTableData({tableName:e,db:t,limit:3})).data.map(n=>Object.fromEntries(Object.entries(n).map(([o,s])=>[o,as(s)])))}catch{return[]}}async function ss(a,e={}){let t=Le(),{includeSampleData:r=!1,maxTables:n}=e,o=q(t);try{let s=await o.getTablesList(a),i=s.slice(0,n??s.length).map(m=>m.tableName).map(async m=>{let[p,d]=await Promise.all([o.getTableColumns({tableName:m,db:a}),r?os(o,m,a):Promise.resolve([])]),f={name:m,columns:p.map(rs)};return d.length>0&&(f.sampleData=d),f}),l=await Promise.all(i),u=ns(l);return{dbType:t,tables:l,relationships:u}}catch(s){throw new Error(`Failed to fetch database schema: ${s instanceof Error?s.message:"Unknown error"}`)}}async function la(a){return ss(a,{includeSampleData:!0,includeDescriptions:!0})}var ca=E(()=>{"use strict";Re();Pe()});import{zValidator as is}from"@hono/zod-validator";import{Hono as ls}from"hono";var ua,ma=E(()=>{"use strict";Ze();he();ia();ca();ua=new ls().basePath("/chat").get("/limit",async a=>{let e=await fetch(`${we.PROXY_URL}/chat/limit`,{headers:{"cf-connecting-ip":a.req.header("cf-connecting-ip")??"","x-real-ip":a.req.header("x-real-ip")??"","x-forwarded-for":a.req.header("x-forwarded-for")??"","x-api-key":a.req.header("x-api-key")??""}}),t=await e.json();return a.json(t,e.status)}).post("/",is("json",kt),async a=>{let{messages:e,data:t}=a.req.valid("json"),{db:r,conversationId:n}=t,o=await la(r),s=sa(o),c={messages:e,conversationId:n,systemPrompt:s},i=await fetch(`${we.PROXY_URL}/chat`,{method:"POST",headers:{"Content-Type":"application/json","cf-connecting-ip":a.req.header("cf-connecting-ip")??"","x-real-ip":a.req.header("x-real-ip")??"","x-forwarded-for":a.req.header("x-forwarded-for")??"","x-api-key":a.req.header("x-api-key")??""},body:JSON.stringify(c)});if(!i.ok){let m=await i.json();return a.json({error:m.error||"Proxy request failed"},i.status)}let{readable:l,writable:u}=new TransformStream;return i.body?.pipeTo(u),new Response(l,{headers:{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"}})})});import{Hono as cs}from"hono";var da,pa=E(()=>{"use strict";Re();Pe();da=new cs().basePath("/databases").get("/",async a=>{let e=Le(),r=await q(e).getDatabasesList();return a.json({data:{databases:r,dbType:e}},200)}).get("/current",async a=>{let e=Le(),r=await q(e).getCurrentDatabase();return a.json({data:{db:r.db,dbType:e}},200)}).get("/connection",async a=>{let e=Le(),r=await q(e).getDatabaseConnectionInfo();return a.json({data:r},200)})});import{zValidator as fa}from"@hono/zod-validator";import{Hono as us}from"hono";var ba,ya=E(()=>{"use strict";he();Re();ba=new us().basePath("/query").post("/",fa("query",D),fa("json",Wt),async a=>{let{query:e}=a.req.valid("json"),{db:t}=a.req.valid("query"),r=a.get("dbType"),o=await q(r).executeQuery({query:e,db:t});return a.json({data:o},200)})});import{zValidator as Ce}from"@hono/zod-validator";import{Hono as ms}from"hono";var ga,ha=E(()=>{"use strict";he();Re();ga=new ms().basePath("/records").post("/",Ce("query",D),Ce("json",It),async a=>{let{db:e}=a.req.valid("query"),{tableName:t,data:r}=a.req.valid("json"),n=a.get("dbType"),o=q(n),{insertedCount:s}=await o.addRecord({db:e,params:{tableName:t,data:r}});return a.json({data:`Record inserted into "${t}" with ${s} rows inserted`},200)}).patch("/",Ce("query",D),Ce("json",Qt),async a=>{let{db:e}=a.req.valid("query"),{tableName:t,primaryKey:r,updates:n}=a.req.valid("json"),o=a.get("dbType"),s=q(o),{updatedCount:c}=await s.updateRecords({params:{tableName:t,primaryKey:r,updates:n},db:e});return a.json({data:`Updated ${c} records in "${t}"`},200)}).delete("/",Ce("query",D),Ce("json",mt),async a=>{let{db:e}=a.req.valid("query"),{tableName:t,primaryKeys:r}=a.req.valid("json"),n=a.get("dbType"),o=q(n),{deletedCount:s,fkViolation:c,relatedRecords:i}=await o.deleteRecords({tableName:t,primaryKeys:r,db:e});return c?a.json({data:{deletedCount:0,fkViolation:!0,relatedRecords:i}},409):a.json({data:{deletedCount:s,fkViolation:!1,relatedRecords:[]}},200)}).delete("/force",Ce("query",D),Ce("json",mt),async a=>{let{db:e}=a.req.valid("query"),{tableName:t,primaryKeys:r}=a.req.valid("json"),n=a.get("dbType"),s=await q(n).forceDeleteRecords({tableName:t,primaryKeys:r,db:e});return a.json({data:s},200)}).post("/bulk",Ce("query",D),Ce("json",vt),async a=>{let{db:e}=a.req.valid("query"),{tableName:t,records:r}=a.req.valid("json"),n=a.get("dbType"),s=await q(n).bulkInsertRecords({tableName:t,records:r,db:e});return a.json({data:s},200)})});import{utils as Qe,write as ds}from"xlsx";function Ta({cols:a,rows:e,format:t,tableName:r}){switch(t){case"json":{let n=JSON.stringify(e??[],null,2);return new Uint8Array(Buffer.from(n,"utf-8"))}case"csv":{let n=[a,...e?.map(c=>a?.map(i=>c[i]))??[]],o=Qe.aoa_to_sheet(n),s=Qe.sheet_to_csv(o);return new Uint8Array(Buffer.from(s,"utf-8"))}case"xlsx":{let n=[a,...e?.map(i=>a?.map(l=>i[l]))??[]],o=Qe.aoa_to_sheet(n),s=Qe.book_new();Qe.book_append_sheet(s,o,r.slice(0,31));let c=ds(s,{bookType:"xlsx",type:"buffer"});return new Uint8Array(c)}}}var Ea=E(()=>{"use strict"});import{zValidator as K}from"@hono/zod-validator";import{Hono as ps}from"hono";var Ca,wa=E(()=>{"use strict";he();Re();Ea();Ca=new ps().basePath("/tables").get("/",K("query",D),async a=>{let{db:e}=a.req.valid("query"),t=a.get("dbType"),n=await q(t).getTablesList(e);return a.json({data:n},200)}).post("/",K("query",D),K("json",Kt),async a=>{let{db:e}=a.req.valid("query"),t=a.req.valid("json"),r=a.get("dbType");return await q(r).createTable({tableData:t,db:e}),a.json({data:`Table ${t.tableName} created successfully`},200)}).delete("/:tableName",K("query",Vt),K("param",ce),async a=>{let{db:e,cascade:t}=a.req.valid("query"),{tableName:r}=a.req.valid("param"),n=a.get("dbType"),s=await q(n).deleteTable({tableName:r,db:e,cascade:t});return a.json({data:s},200)}).delete("/:tableName/columns/:columnName",K("query",xt),K("param",te),async a=>{let{db:e,cascade:t}=a.req.valid("query"),{tableName:r,columnName:n}=a.req.valid("param"),o=a.get("dbType"),s=q(o),{deletedCount:c}=await s.deleteColumn({tableName:r,columnName:n,cascade:t,db:e});return a.json({data:`Column "${n}" deleted successfully from table "${r}" with ${c} rows deleted`},200)}).post("/:tableName/columns",K("query",D),K("param",ce),K("json",me),async a=>{let{db:e}=a.req.valid("query"),{tableName:t}=a.req.valid("param"),r=a.req.valid("json"),n=a.get("dbType");return await q(n).addColumn({tableName:t,db:e,...r}),a.json({data:`Column "${r.columnName}" added successfully to table "${t}"`},200)}).patch("/:tableName/columns/:columnName/rename",K("query",D),K("param",te),K("json",dt),async a=>{let{db:e}=a.req.valid("query"),{tableName:t,columnName:r}=a.req.valid("param"),n=a.req.valid("json"),o=a.get("dbType");return await q(o).renameColumn({tableName:t,columnName:r,db:e,...n}),a.json({data:`Column "${r}" renamed to "${n.newColumnName}" in table "${t}"`},200)}).patch("/:tableName/columns/:columnName",K("query",D),K("param",te),K("json",ke),async a=>{let{db:e}=a.req.valid("query"),{tableName:t,columnName:r}=a.req.valid("param"),n=a.req.valid("json"),o=a.get("dbType");return await q(o).alterColumn({tableName:t,columnName:r,db:e,...n}),a.json({data:`Column "${r}" updated successfully in table "${t}"`},200)}).get("/:tableName/columns",K("query",D),K("param",ce),async a=>{let{db:e}=a.req.valid("query"),{tableName:t}=a.req.valid("param"),r=a.get("dbType"),o=await q(r).getTableColumns({tableName:t,db:e});return a.json({data:o},200)}).get("/:tableName/schema",K("query",D),K("param",ce),async a=>{let{db:e}=a.req.valid("query"),{tableName:t}=a.req.valid("param"),r=a.get("dbType"),o=await q(r).getTableSchema({tableName:t,db:e});return a.json({data:{schema:o}},200)}).get("/:tableName/data",K("param",ce),K("query",Gt),async a=>{let{tableName:e}=a.req.valid("param"),{cursor:t,limit:r,direction:n,sort:o,order:s,filters:c,db:i}=a.req.valid("query"),l=a.get("dbType"),m=await q(l).getTableData({tableName:e,cursor:t,limit:r,direction:n,sort:o,order:s,filters:c,db:i});return a.json({data:m},200)}).get("/:tableName/export",K("param",ce),K("query",zt),async a=>{let{tableName:e}=a.req.valid("param"),{db:t,format:r}=a.req.valid("query"),n=a.get("dbType"),o=q(n),{cols:s,rows:c}=await o.exportTableData({tableName:e,db:t}),i=Ta({cols:s,rows:c,format:r,tableName:e}),l;switch(r){case"csv":l="text/csv";break;case"xlsx":l="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";break;case"json":l="application/json";break}return new Response(i,{headers:{"Content-Type":l??"","Content-Disposition":`attachment; filename="${e}_export.${r}"`}})})});var Na={};pr(Na,{createServer:()=>ws});import{existsSync as Ra}from"fs";import Ae from"path";import{fileURLToPath as fs}from"url";import{serveStatic as Je}from"@hono/node-server/serve-static";import{zValidator as bs}from"@hono/zod-validator";import{Hono as ys}from"hono";import{cors as gs}from"hono/cors";import{HTTPException as hs}from"hono/http-exception";import{logger as Ts}from"hono/logger";import{prettyJSON as Es}from"hono/pretty-json";import{z as Sa}from"zod";var Xe,Cs,ws,Aa=E(()=>{"use strict";Re();ra();aa();ma();pa();ya();ha();wa();Xe=()=>{if(process.env.NODE_ENV==="development")return Ae.resolve(process.cwd(),"../web/dist");let a=Ae.dirname(fs(import.meta.url)),e=Ae.resolve(a,"./web-dist"),t=Ae.resolve(process.cwd(),"../web/dist"),r=Ae.resolve(e,"index.html"),n=Ae.resolve(t,"index.html");return!Ra(r)&&Ra(n)?t:e},Cs=Sa.object({dbType:Sa.string().refine(a=>se.getSupportedTypes().includes(a),{message:`Invalid database type. Supported types: ${se.getSupportedTypes().join(", ")}`})}),ws=()=>{ta();let a=new ys({strict:!1}).use("/*",gs()).use(Es({space:2})).use(process.env.NODE_ENV==="development"?Ts():(e,t)=>t()).use("/favicon.ico",Je({path:Ae.resolve(Xe(),"favicon.ico")})).use("*",async(e,t)=>{e.header("Access-Control-Allow-Origin","*"),e.header("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, OPTIONS"),e.header("Access-Control-Allow-Headers","Content-Type"),await t()}).onError(na).route("/",da).route("/",ua).use("/assets/*",Je({root:Xe()})).use("/image.png",Je({root:Xe()})).use("/:dbType/*",bs("param",Cs,(e,t)=>{if(!e.success){let r=t.req.param("dbType");throw new hs(400,{message:`Invalid database type: "${r}". Supported types: ${se.getSupportedTypes().join(", ")}`})}}),async(e,t)=>{let{dbType:r}=e.req.valid("param");e.set("dbType",r),await t()}).route("/:dbType",Ca).route("/:dbType",ga).route("/:dbType",ba);return process.env.NODE_ENV!=="test"&&(a.use("/*",Je({root:Xe()})),a.get("/*",Je({path:Ae.resolve(Xe(),"index.html")}))),{app:a}}});Ze();import{intro as Rs,outro as Ss}from"@clack/prompts";import{serve as Ns}from"@hono/node-server";import dr from"picocolors";import{program as Rr}from"commander";var Sr=()=>(Rr.name("db-studio").option("-e, --env <path>","Path to custom .env file").option("-p, --port <port>","Port to run the server on").option("-d, --database-url <url>","Database URL to use").option("-n, --var-name <name>","Custom environment variable name (default: DATABASE_URL)").option("-s, --status","Show status of the server").option("-h, --help","Show help").option("-v, --version","Show version").parse(process.argv),Rr.opts());import{readFile as _a}from"fs/promises";import{resolve as $a}from"path";import{cancel as et,isCancel as Rt,note as Nr,select as Oa,spinner as La,text as Ar}from"@clack/prompts";import{parse as Pa}from"dotenv";import St from"picocolors";var Dr=async(a,e)=>{let t=e||"DATABASE_URL";if(a?.[t])return a[t];if(process.env[t])return process.env[t];let r=La();r.start("Looking for database connection..."),a?Nr(St.red(`${t} not found in .env or process.env`)):Nr(St.red(`No .env file found and ${t} not set in process.env`));let n=await Oa({message:`How do you want to provide ${t}?`,options:[{value:"manual",label:"Enter connection string manually"},{value:"other-env",label:"Use different .env file"},{value:"cancel",label:"Cancel / Exit"}],initialValue:"manual"});if((Rt(n)||n==="cancel")&&(et("No database connection provided. Exiting..."),process.exit(0)),n==="other-env"){r.start("Waiting for path...");let s=await Ar({message:"Enter path to .env file",placeholder:"~/projects/myapp/.env.local or ./special.env",validate(i){if(!i?.trim())return"Path is required"}});Rt(s)&&(et("Cancelled."),process.exit(0)),r.stop("Trying custom .env...");let c=$a(s);try{let i=await _a(c,"utf-8"),l=Pa(i);if(l[t])return l[t];throw new Error(`${t} still missing in custom file`)}catch(i){let l=i;et(`Cannot read or parse file: ${St.dim(l.message)}`),process.exit(1)}}r.stop("Manual input...");let o=await Ar({message:`Paste your ${t}`,placeholder:"postgresql://user:password@localhost:5432/mydb or sqlite:///path/to/db.sqlite",validate(s){if(!s?.trim())return"Connection string is required!";if(!s.trim().startsWith("sqlite://"))try{new URL(s);return}catch{return"Must be a valid URL format"}}});return Rt(o)&&(et("Cancelled."),process.exit(0)),o.trim()};import{access as Ma,readFile as Ia}from"fs/promises";import{dirname as xa,resolve as Nt}from"path";import{parse as va}from"dotenv";var ka=async a=>{let e=Nt(a);for(;;){let t=Nt(e,".env");try{return await Ma(t),t}catch{}let r=xa(e);if(r===e)return null;e=r}},$e=async a=>{let e;if(a?e=Nt(a):e=await ka(process.cwd()),!e)return null;try{let t=await Ia(e,"utf-8");return va(t)}catch(t){if(t instanceof Error&&t.message.includes("ENOENT"))return null;throw t}};wt();import{intro as qa,outro as Ua}from"@clack/prompts";import _r from"picocolors";var $r=()=>{qa(_r.inverse(" db-studio ")),Ua(_r.green(`For more information, visit: ${Cr.SITE_DOCS_LINK}`))};Ze();import{intro as Fa,note as Ba,outro as Or}from"@clack/prompts";import tt from"picocolors";var Lr=async(a,e,t)=>{Fa(tt.inverse(" db-studio "));let r=t||we.VAR_NAME,n=null;if(e)n=e;else{let o=a?await $e(a):await $e();o?.[r]?n=o[r]:process.env[r]&&(n=process.env[r]??null)}n?Or(tt.green(`\u2713 Database connection configured (using ${r})`)):(Ba(tt.red(`\u2717 ${r} not found`),"Status"),Or(tt.yellow("\u26A0 No database connection configured")))};import{intro as Ha,outro as Ka}from"@clack/prompts";import Mr from"picocolors";var Pr={name:"db-studio",type:"module",version:"1.9.5",description:"Modern database client for PostgreSQL with spreadsheet-like grid, AI-powered SQL assistance, ER diagrams, fast data browsing and editing. CLI tool, upcoming desktop & web versions.",keywords:["database client","database gui","database browser","sql client","sql editor","query tool","table editor","data editor","postgres","postgresql","postgresql client","postgresql gui","pgadmin alternative","mysql","mysql client","ai sql","er diagram","database management","database studio"],author:"H\xFCsam \u{1F951} <devhsmq@gmail.com>",homepage:"https://dbstudio.sh",repository:{type:"git",url:"git+https://github.com/husamql3/db-studio.git"},bugs:{url:"https://github.com/husamql3/db-studio/issues"},license:"BSL-1.1",bin:{"db-studio":"./dist/index.js"},files:["dist"],scripts:{dev:"portless run","dev:app":"NODE_ENV=development tsx watch src/index.ts",build:"tsup --minify --sourcemap",prepack:"cd ../web && bun run build && cd ../server && bun run build",start:`sh -c 'APP_PORT=\${PORT:-3333}; portless proxy start >/dev/null && portless alias db-studio "$APP_PORT" --force >/dev/null && HOST=127.0.0.1 PORT="$APP_PORT" DB_STUDIO_SERVER_URL=https://db-studio.localhost node dist/index.js'`,format:"biome check --write --unsafe",test:"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage"},portless:{name:"api.dbstudio",script:"dev:app"},dependencies:{"@clack/prompts":"^1.0.1","@hono/node-server":"^2.0.0","@hono/zod-validator":"^0.8.0","better-sqlite3":"^12.9.0",commander:"^15.0.0",dotenv:"^17.3.1",hono:"^4.10.4",ioredis:"^5.4.1",mongodb:"^7.1.1",mssql:"^12.2.0",mysql2:"^3.18.2",pg:"^8.13.1",picocolors:"^1.1.1",xlsx:"^0.18.5",zod:"^4.2.1"},devDependencies:{"@biomejs/biome":"^2.2.6","@db-studio/shared":"workspace:*","@types/better-sqlite3":"^7.6.13","@types/mssql":"^12.3.0","@types/node":"^26.0.0","@types/pg":"^8.16.0","@vitest/coverage-v8":"^4.0.17",tsup:"^8.5.1",tsx:"^4.7.1",typescript:"^6.0.3",vitest:"^4.0.17"}};var Ir=()=>{Ha(Mr.inverse(" db-studio ")),Ka(Mr.green(`\u{1F680} db-studio v${Pr.version}`))};var As=async()=>{let{env:a,port:e,databaseUrl:t,varName:r,status:n,help:o,version:s}=Sr();o&&($r(),process.exit(0)),s&&(Ir(),process.exit(0)),n&&(await Lr(a,t,r),process.exit(0)),Rs(dr.inverse(" db-studio "));let c=Number.parseInt(e??process.env.PORT??String(we.PORT),10),i=process.env.HOST,l=r||we.VAR_NAME,u=a?await $e(a):await $e(),m=t||await Dr(u,l);process.env.DATABASE_URL=m;let{createServer:p}=await Promise.resolve().then(()=>(Aa(),Na)),{app:d}=p();Ns({fetch:d.fetch,hostname:i,port:c});let f=process.env.DB_STUDIO_SERVER_URL??process.env.PORTLESS_URL??`http://localhost:${c}`;Ss(dr.green(`Server running at ${dr.cyan(f)}`))};As().catch(a=>{process.exit(1)});export{As as main};
418
418
  //# sourceMappingURL=index.js.map