@mohxmd/dbstudio 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,275 @@
1
+ #!/usr/bin/env bun
2
+ // @bun
3
+
4
+ // lib/constants/help.ts
5
+ var HELP = `
6
+ dbstudio \u2014 spin up Drizzle Studio from any database URL
7
+
8
+ Usage:
9
+ dbstudio <database-url> [options]
10
+
11
+ Examples:
12
+ dbstudio postgresql://user:pass@localhost:5432/mydb
13
+ dbstudio mysql://user:pass@localhost:3306/mydb
14
+ dbstudio sqlite:./local.db
15
+
16
+ # Quick share (no CF account needed, temporary URL)
17
+ dbstudio postgresql://... --share
18
+
19
+ # Named tunnel (persistent URL, needs CF account + domain)
20
+ dbstudio postgresql://... --share --tunnel dbstudio --hostname db.yourteam.com
21
+
22
+ Options:
23
+ --port <number> Port to run studio on (default: 4983)
24
+ --host <string> Host to bind to (default: 127.0.0.1, or 0.0.0.0 with --share)
25
+ --open Auto-open in browser
26
+ --share Expose via Cloudflare Tunnel
27
+ --tunnel <name> Named CF tunnel (requires --hostname)
28
+ --hostname <domain> Public hostname for named tunnel
29
+ -h, --help Show this help
30
+ -v, --version Show version
31
+
32
+ Tunnel setup (one time):
33
+ yay -S cloudflared
34
+ cloudflared tunnel login
35
+ cloudflared tunnel create <name>
36
+ # add CNAME in Cloudflare dashboard \u2192 <tunnel-id>.cfargotunnel.com
37
+ `;
38
+
39
+ // lib/utils/args.ts
40
+ function getFlag(args, flag) {
41
+ const idx = args.indexOf(flag);
42
+ if (idx === -1)
43
+ return null;
44
+ const val = args[idx + 1];
45
+ return val && !val.startsWith("--") ? val : null;
46
+ }
47
+ function parseStudioOptions(args) {
48
+ const dbUrl = args[0];
49
+ if (!dbUrl || dbUrl.startsWith("--")) {
50
+ throw new Error("Please provide a database URL as the first argument.");
51
+ }
52
+ const shouldShare = args.includes("--share");
53
+ const tunnelName = getFlag(args, "--tunnel");
54
+ const publicHostname = getFlag(args, "--hostname");
55
+ if (tunnelName && !publicHostname) {
56
+ throw new Error(`--tunnel requires --hostname <your-domain>
57
+ Example: --tunnel dbstudio --hostname db.yourteam.com`);
58
+ }
59
+ const explicitHost = getFlag(args, "--host");
60
+ const host = explicitHost ?? (shouldShare ? "0.0.0.0" : "127.0.0.1");
61
+ return {
62
+ dbUrl,
63
+ port: getFlag(args, "--port") ?? "4983",
64
+ host,
65
+ shouldOpen: args.includes("--open"),
66
+ shouldShare,
67
+ tunnelName,
68
+ publicHostname
69
+ };
70
+ }
71
+
72
+ // lib/utils/dialect.ts
73
+ function detectDialect(url) {
74
+ if (url.startsWith("postgresql://") || url.startsWith("postgres://"))
75
+ return "postgresql";
76
+ if (url.startsWith("mysql://") || url.startsWith("mysql2://"))
77
+ return "mysql";
78
+ if (url.startsWith("sqlite:") || url.startsWith("file:") || url === ":memory:" || url.endsWith(".db") || url.endsWith(".sqlite"))
79
+ return "sqlite";
80
+ throw new Error(`Could not detect dialect from URL: ${url}
81
+ Supported: postgresql://, mysql://, sqlite:, file:`);
82
+ }
83
+
84
+ // lib/utils/config.ts
85
+ import { writeFileSync, unlinkSync, existsSync } from "fs";
86
+ import { tmpdir, homedir } from "os";
87
+ import { join } from "path";
88
+ var tempFiles = [];
89
+ function cleanupAll() {
90
+ for (const f of tempFiles) {
91
+ if (existsSync(f))
92
+ unlinkSync(f);
93
+ }
94
+ }
95
+ function writeTempFile(name, content) {
96
+ const path = join(tmpdir(), `${name}-${Date.now()}`);
97
+ writeFileSync(path, content, "utf8");
98
+ tempFiles.push(path);
99
+ return path;
100
+ }
101
+ function generateDrizzleConfig(url, dialect) {
102
+ if (dialect === "sqlite") {
103
+ const filePath = url.replace(/^sqlite:\/\//, "").replace(/^sqlite:/, "").replace(/^file:\/\//, "").replace(/^file:/, "");
104
+ return `export default {
105
+ dialect: "sqlite",
106
+ dbCredentials: { url: "${filePath}" },
107
+ };`;
108
+ }
109
+ return `export default {
110
+ dialect: "${dialect}",
111
+ dbCredentials: { url: "${url}" },
112
+ };`;
113
+ }
114
+ function createDrizzleConfig(url, dialect) {
115
+ return writeTempFile("dbstudio.config.ts", generateDrizzleConfig(url, dialect));
116
+ }
117
+ function generateTunnelConfig(tunnelName, hostname, port) {
118
+ const credPath = join(homedir(), ".cloudflared");
119
+ return `tunnel: ${tunnelName}
120
+ credentials-file: ${credPath}/${tunnelName}.json
121
+
122
+ ingress:
123
+ - hostname: ${hostname}
124
+ service: http://localhost:${port}
125
+ - service: http_status:404
126
+ `;
127
+ }
128
+ function createTunnelConfig(tunnelName, hostname, port) {
129
+ return writeTempFile("dbstudio-tunnel.yml", generateTunnelConfig(tunnelName, hostname, port));
130
+ }
131
+
132
+ // lib/commands/tunnel.ts
133
+ import { spawn } from "child_process";
134
+ function launchTunnel(options) {
135
+ const { port, tunnelName, publicHostname, shouldOpen } = options;
136
+ let tunnelArgs;
137
+ if (tunnelName && publicHostname) {
138
+ const tunnelConfigPath = createTunnelConfig(tunnelName, publicHostname, port);
139
+ tunnelArgs = ["tunnel", "--config", tunnelConfigPath, "run", tunnelName];
140
+ } else {
141
+ tunnelArgs = ["tunnel", "--url", `http://localhost:${port}`];
142
+ }
143
+ const tunnel = spawn("cloudflared", tunnelArgs, {
144
+ stdio: ["ignore", "pipe", "pipe"],
145
+ shell: false
146
+ });
147
+ tunnel.stderr?.on("data", (data) => {
148
+ const line = data.toString();
149
+ process.stderr.write(data);
150
+ if (!tunnelName) {
151
+ const match = line.match(/https:\/\/[a-z0-9-]+\.trycloudflare\.com/);
152
+ if (match) {
153
+ console.log(`
154
+ \u2705 Public URL: ${match[0]}`);
155
+ console.log(` Share this link with your team
156
+ `);
157
+ if (shouldOpen) {
158
+ spawn("xdg-open", [match[0]], { stdio: "ignore" });
159
+ }
160
+ }
161
+ }
162
+ });
163
+ tunnel.on("error", (err) => {
164
+ console.error(`
165
+ \u274C Failed to start cloudflared: ${err.message}`);
166
+ console.error(` Install it with: yay -S cloudflared`);
167
+ });
168
+ tunnel.on("exit", (code) => {
169
+ if (code !== 0)
170
+ console.error(`
171
+ \u26A0\uFE0F Tunnel exited with code ${code}`);
172
+ });
173
+ return tunnel;
174
+ }
175
+
176
+ // lib/commands/studio.ts
177
+ import { spawn as spawn2 } from "child_process";
178
+ var procs = [];
179
+ function killAll() {
180
+ for (const p of procs) {
181
+ if (!p.killed)
182
+ p.kill("SIGTERM");
183
+ }
184
+ }
185
+ function registerCleanup() {
186
+ process.on("exit", cleanupAll);
187
+ process.on("SIGINT", () => {
188
+ console.log(`
189
+
190
+ \uD83D\uDC4B Shutting down dbstudio...`);
191
+ killAll();
192
+ cleanupAll();
193
+ process.exit(0);
194
+ });
195
+ process.on("SIGTERM", () => {
196
+ killAll();
197
+ cleanupAll();
198
+ process.exit(0);
199
+ });
200
+ }
201
+ async function runStudioCommand(options) {
202
+ const { dbUrl, port, host, shouldOpen, shouldShare, tunnelName, publicHostname } = options;
203
+ const dialect = detectDialect(dbUrl);
204
+ const drizzleConfig = createDrizzleConfig(dbUrl, dialect);
205
+ const safeUrl = dbUrl.replace(/:\/\/.*@/, "://<credentials>@");
206
+ registerCleanup();
207
+ console.log(`
208
+ \uD83D\uDE80 dbstudio`);
209
+ console.log(` Dialect : ${dialect}`);
210
+ console.log(` URL : ${safeUrl}`);
211
+ console.log(` Studio : https://local.drizzle.studio`);
212
+ if (shouldShare && !tunnelName) {
213
+ console.log(` Tunnel : starting... public URL coming shortly`);
214
+ }
215
+ if (tunnelName && publicHostname) {
216
+ console.log(` Tunnel : https://${publicHostname}`);
217
+ }
218
+ console.log();
219
+ const studioArgs = [
220
+ "drizzle-kit",
221
+ "studio",
222
+ `--config=${drizzleConfig}`,
223
+ `--port=${port}`,
224
+ `--host=${host}`
225
+ ];
226
+ if (shouldOpen && !shouldShare)
227
+ studioArgs.push("--open");
228
+ const studio = Bun.spawn(["bunx", ...studioArgs], {
229
+ stdin: "ignore",
230
+ stdout: "inherit",
231
+ stderr: "inherit"
232
+ });
233
+ if (shouldShare) {
234
+ await Bun.sleep(2000);
235
+ const tunnel = launchTunnel({ port, tunnelName, publicHostname, shouldOpen });
236
+ procs.push(tunnel);
237
+ if (shouldOpen && tunnelName && publicHostname) {
238
+ spawn2("xdg-open", [`https://${publicHostname}`], { stdio: "ignore" });
239
+ }
240
+ }
241
+ const exitCode = await studio.exited;
242
+ killAll();
243
+ cleanupAll();
244
+ return exitCode ?? 0;
245
+ }
246
+
247
+ // main.ts
248
+ var args = process.argv.slice(2);
249
+ if (args.length === 0 || args.includes("-h") || args.includes("--help")) {
250
+ console.log(HELP);
251
+ process.exit(0);
252
+ }
253
+ if (args.includes("-v") || args.includes("--version")) {
254
+ const pkg = await Bun.file(new URL("../package.json", import.meta.url)).json();
255
+ console.log(`dbstudio v${pkg.version}`);
256
+ process.exit(0);
257
+ }
258
+ try {
259
+ const options = parseStudioOptions(args);
260
+ const code = await runStudioCommand(options);
261
+ process.exit(code);
262
+ } catch (error) {
263
+ const msg = error instanceof Error ? error.message : String(error);
264
+ if (msg.startsWith("Please provide")) {
265
+ console.error(`\u274C ${msg}`);
266
+ console.error(" Run dbstudio --help for usage.");
267
+ } else if (msg.startsWith("Could not detect")) {
268
+ console.error(`\u274C ${msg}`);
269
+ } else if (msg.startsWith("--tunnel requires")) {
270
+ console.error(`\u274C ${msg}`);
271
+ } else {
272
+ console.error(`\u274C Unexpected error: ${msg}`);
273
+ }
274
+ process.exit(1);
275
+ }
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@mohxmd/dbstudio",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Spin up Drizzle Studio from any database URL",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
- "module": "main.ts",
8
7
  "bin": {
9
- "dbstudio": "./bin/dbstudio"
8
+ "dbstudio": "./dist/index.js"
10
9
  },
11
10
  "scripts": {
12
11
  "start": "bun main.ts",
13
12
  "dev": "bun --watch main.ts",
13
+ "build": "bun build main.ts --outfile dist/index.js --target bun",
14
14
  "compile": "bun build main.ts --compile --outfile bin/dbstudio",
15
15
  "compile:linux": "bun build main.ts --compile --target=bun-linux-x64 --outfile bin/dbstudio-linux",
16
16
  "compile:mac": "bun build main.ts --compile --target=bun-darwin-arm64 --outfile bin/dbstudio-mac"
@@ -19,8 +19,7 @@
19
19
  "bun": ">=1.0.0"
20
20
  },
21
21
  "files": [
22
- "main.ts",
23
- "lib",
22
+ "dist",
24
23
  "README.md",
25
24
  "LICENSE"
26
25
  ],
@@ -30,8 +29,5 @@
30
29
  },
31
30
  "devDependencies": {
32
31
  "@types/bun": "latest"
33
- },
34
- "peerDependencies": {
35
- "typescript": "latest"
36
32
  }
37
33
  }
package/bin/dbstudio DELETED
Binary file
@@ -1,98 +0,0 @@
1
- import type { StudioOptions } from "../utils/args";
2
- import { detectDialect } from "../utils/dialect";
3
- import { createDrizzleConfig, cleanupAll } from "../utils/config";
4
- import { launchTunnel } from "./tunnel";
5
- import type { ChildProcess } from "child_process";
6
-
7
- const procs: ChildProcess[] = [];
8
-
9
- function killAll() {
10
- for (const p of procs) {
11
- if (!p.killed) p.kill("SIGTERM");
12
- }
13
- }
14
-
15
- function registerCleanup() {
16
- process.on("exit", cleanupAll);
17
- process.on("SIGINT", () => {
18
- console.log("\n\n👋 Shutting down dbstudio...");
19
- killAll();
20
- cleanupAll();
21
- process.exit(0);
22
- });
23
- process.on("SIGTERM", () => {
24
- killAll();
25
- cleanupAll();
26
- process.exit(0);
27
- });
28
- }
29
-
30
- export async function runStudioCommand(options: StudioOptions): Promise<number> {
31
- const { dbUrl, port, host, shouldOpen, shouldShare, tunnelName, publicHostname } = options;
32
-
33
- const dialect = detectDialect(dbUrl);
34
- const drizzleConfig = createDrizzleConfig(dbUrl, dialect);
35
- const safeUrl = dbUrl.replace(/:\/\/.*@/, "://<credentials>@");
36
-
37
- registerCleanup();
38
-
39
- // Print startup info
40
-
41
- console.log(`\n🚀 dbstudio`);
42
- console.log(` Dialect : ${dialect}`);
43
- console.log(` URL : ${safeUrl}`);
44
- console.log(` Studio : https://local.drizzle.studio`);
45
-
46
- if (shouldShare && !tunnelName) {
47
- console.log(` Tunnel : starting... public URL coming shortly`);
48
- }
49
- if (tunnelName && publicHostname) {
50
- console.log(` Tunnel : https://${publicHostname}`);
51
- }
52
- console.log();
53
-
54
- // Spawn drizzle-kit studio via bunx
55
- // bunx is used so the binary works after `bun build --compile`
56
- // without needing a local node_modules at runtime
57
-
58
- const studioArgs = [
59
- "drizzle-kit",
60
- "studio",
61
- `--config=${drizzleConfig}`,
62
- `--port=${port}`,
63
- `--host=${host}`,
64
- ];
65
-
66
- if (shouldOpen && !shouldShare) studioArgs.push("--open");
67
-
68
- const studio = Bun.spawn(["bunx", ...studioArgs], {
69
- stdin: "ignore",
70
- stdout: "inherit",
71
- stderr: "inherit",
72
- });
73
-
74
- // Launch tunnel after studio boots
75
-
76
- if (shouldShare) {
77
- // give drizzle-kit studio a moment to start before tunnel connects
78
- await Bun.sleep(2000);
79
-
80
- const tunnel = launchTunnel({ port, tunnelName, publicHostname, shouldOpen });
81
- // cast needed since launchTunnel returns node ChildProcess
82
- procs.push(tunnel as unknown as ChildProcess);
83
-
84
- if (shouldOpen && tunnelName && publicHostname) {
85
- spawn("xdg-open", [`https://${publicHostname}`], { stdio: "ignore" });
86
- }
87
- }
88
-
89
- // Wait for studio to exit
90
-
91
- const exitCode = await studio.exited;
92
- killAll();
93
- cleanupAll();
94
- return exitCode ?? 0;
95
- }
96
-
97
- // node spawn needed for tunnel (pipe stdio support)
98
- import { spawn } from "child_process";
@@ -1,60 +0,0 @@
1
- import { spawn } from "child_process";
2
- import { createTunnelConfig } from "../utils/config";
3
-
4
- export function launchTunnel(options: {
5
- port: string;
6
- tunnelName: string | null;
7
- publicHostname: string | null;
8
- shouldOpen: boolean;
9
- }) {
10
- const { port, tunnelName, publicHostname, shouldOpen } = options;
11
-
12
- let tunnelArgs: string[];
13
-
14
- if (tunnelName && publicHostname) {
15
- // named tunnel — generate temp config.yml, never touches ~/.cloudflared/config.yml
16
- const tunnelConfigPath = createTunnelConfig(
17
- tunnelName,
18
- publicHostname,
19
- port,
20
- );
21
- tunnelArgs = ["tunnel", "--config", tunnelConfigPath, "run", tunnelName];
22
- } else {
23
- // quick share — zero setup, temporary trycloudflare.com URL
24
- tunnelArgs = ["tunnel", "--url", `http://localhost:${port}`];
25
- }
26
-
27
- const tunnel = spawn("cloudflared", tunnelArgs, {
28
- stdio: ["ignore", "pipe", "pipe"],
29
- shell: false,
30
- });
31
-
32
- // cloudflared prints the public URL to stderr
33
- tunnel.stderr?.on("data", (data: Buffer) => {
34
- const line = data.toString();
35
- process.stderr.write(data);
36
-
37
- // quick share: extract and highlight the URL when cloudflared prints it
38
- if (!tunnelName) {
39
- const match = line.match(/https:\/\/[a-z0-9-]+\.trycloudflare\.com/);
40
- if (match) {
41
- console.log(`\n✅ Public URL: ${match[0]}`);
42
- console.log(` Share this link with your team\n`);
43
- if (shouldOpen) {
44
- spawn("xdg-open", [match[0]], { stdio: "ignore" });
45
- }
46
- }
47
- }
48
- });
49
-
50
- tunnel.on("error", (err) => {
51
- console.error(`\n❌ Failed to start cloudflared: ${err.message}`);
52
- console.error(` Install it with: yay -S cloudflared`);
53
- });
54
-
55
- tunnel.on("exit", (code) => {
56
- if (code !== 0) console.error(`\n⚠️ Tunnel exited with code ${code}`);
57
- });
58
-
59
- return tunnel;
60
- }
@@ -1,33 +0,0 @@
1
- export const HELP = `
2
- dbstudio — spin up Drizzle Studio from any database URL
3
-
4
- Usage:
5
- dbstudio <database-url> [options]
6
-
7
- Examples:
8
- dbstudio postgresql://user:pass@localhost:5432/mydb
9
- dbstudio mysql://user:pass@localhost:3306/mydb
10
- dbstudio sqlite:./local.db
11
-
12
- # Quick share (no CF account needed, temporary URL)
13
- dbstudio postgresql://... --share
14
-
15
- # Named tunnel (persistent URL, needs CF account + domain)
16
- dbstudio postgresql://... --share --tunnel dbstudio --hostname db.yourteam.com
17
-
18
- Options:
19
- --port <number> Port to run studio on (default: 4983)
20
- --host <string> Host to bind to (default: 127.0.0.1, or 0.0.0.0 with --share)
21
- --open Auto-open in browser
22
- --share Expose via Cloudflare Tunnel
23
- --tunnel <name> Named CF tunnel (requires --hostname)
24
- --hostname <domain> Public hostname for named tunnel
25
- -h, --help Show this help
26
- -v, --version Show version
27
-
28
- Tunnel setup (one time):
29
- yay -S cloudflared
30
- cloudflared tunnel login
31
- cloudflared tunnel create <name>
32
- # add CNAME in Cloudflare dashboard → <tunnel-id>.cfargotunnel.com
33
- `;
package/lib/utils/args.ts DELETED
@@ -1,50 +0,0 @@
1
- export type StudioOptions = {
2
- dbUrl: string;
3
- port: string;
4
- host: string;
5
- shouldOpen: boolean;
6
- shouldShare: boolean;
7
- tunnelName: string | null;
8
- publicHostname: string | null;
9
- };
10
-
11
- /** Read a flag value in the form `--flag <value>`. */
12
- function getFlag(args: string[], flag: string): string | null {
13
- const idx = args.indexOf(flag);
14
- if (idx === -1) return null;
15
- const val = args[idx + 1];
16
- return val && !val.startsWith("--") ? val : null;
17
- }
18
-
19
- /** Parse and validate CLI arguments into studio runtime options. */
20
- export function parseStudioOptions(args: string[]): StudioOptions {
21
- const dbUrl = args[0];
22
-
23
- if (!dbUrl || dbUrl.startsWith("--")) {
24
- throw new Error("Please provide a database URL as the first argument.");
25
- }
26
-
27
- const shouldShare = args.includes("--share");
28
- const tunnelName = getFlag(args, "--tunnel");
29
- const publicHostname = getFlag(args, "--hostname");
30
-
31
- if (tunnelName && !publicHostname) {
32
- throw new Error(
33
- "--tunnel requires --hostname <your-domain>\n Example: --tunnel dbstudio --hostname db.yourteam.com",
34
- );
35
- }
36
-
37
- // when sharing, bind to all interfaces so cloudflared can reach studio
38
- const explicitHost = getFlag(args, "--host");
39
- const host = explicitHost ?? (shouldShare ? "0.0.0.0" : "127.0.0.1");
40
-
41
- return {
42
- dbUrl,
43
- port: getFlag(args, "--port") ?? "4983",
44
- host,
45
- shouldOpen: args.includes("--open"),
46
- shouldShare,
47
- tunnelName,
48
- publicHostname,
49
- };
50
- }
@@ -1,70 +0,0 @@
1
- import { writeFileSync, unlinkSync, existsSync } from "fs";
2
- import { tmpdir, homedir } from "os";
3
- import { join } from "path";
4
- import type { Dialect } from "./dialect";
5
-
6
- // Temp file registry
7
-
8
- const tempFiles: string[] = [];
9
-
10
- /** Remove all temporary files created by this process. */
11
- export function cleanupAll() {
12
- for (const f of tempFiles) {
13
- if (existsSync(f)) unlinkSync(f);
14
- }
15
- }
16
-
17
- /** Write content to a uniquely named file in the OS temp directory. */
18
- function writeTempFile(name: string, content: string): string {
19
- const path = join(tmpdir(), `${name}-${Date.now()}`);
20
- writeFileSync(path, content, "utf8");
21
- tempFiles.push(path);
22
- return path;
23
- }
24
-
25
- // Drizzle config
26
-
27
- /** Build a minimal drizzle.config.ts string for the provided database URL. */
28
- function generateDrizzleConfig(url: string, dialect: Dialect): string {
29
- if (dialect === "sqlite") {
30
- const filePath = url
31
- .replace(/^sqlite:\/\//, "")
32
- .replace(/^sqlite:/, "")
33
- .replace(/^file:\/\//, "")
34
- .replace(/^file:/, "");
35
- return `export default {
36
- dialect: "sqlite",
37
- dbCredentials: { url: "${filePath}" },
38
- };`;
39
- }
40
-
41
- return `export default {
42
- dialect: "${dialect}",
43
- dbCredentials: { url: "${url}" },
44
- };`;
45
- }
46
-
47
- /** Create a temp drizzle config file and return its path. */
48
- export function createDrizzleConfig(url: string, dialect: Dialect): string {
49
- return writeTempFile("dbstudio.config.ts", generateDrizzleConfig(url, dialect));
50
- }
51
-
52
- // Cloudflare tunnel config
53
-
54
- /** Build a temporary cloudflared config for a named tunnel run. */
55
- function generateTunnelConfig(tunnelName: string, hostname: string, port: string): string {
56
- const credPath = join(homedir(), ".cloudflared");
57
- return `tunnel: ${tunnelName}
58
- credentials-file: ${credPath}/${tunnelName}.json
59
-
60
- ingress:
61
- - hostname: ${hostname}
62
- service: http://localhost:${port}
63
- - service: http_status:404
64
- `;
65
- }
66
-
67
- /** Create a temp cloudflared config file for a named tunnel and return its path. */
68
- export function createTunnelConfig(tunnelName: string, hostname: string, port: string): string {
69
- return writeTempFile("dbstudio-tunnel.yml", generateTunnelConfig(tunnelName, hostname, port));
70
- }
@@ -1,20 +0,0 @@
1
- export type Dialect = "postgresql" | "mysql" | "sqlite";
2
-
3
- /** Detect Drizzle dialect from a database URL or SQLite-style path. */
4
- export function detectDialect(url: string): Dialect {
5
- if (url.startsWith("postgresql://") || url.startsWith("postgres://"))
6
- return "postgresql";
7
- if (url.startsWith("mysql://") || url.startsWith("mysql2://")) return "mysql";
8
- if (
9
- url.startsWith("sqlite:") ||
10
- url.startsWith("file:") ||
11
- url === ":memory:" ||
12
- url.endsWith(".db") ||
13
- url.endsWith(".sqlite")
14
- )
15
- return "sqlite";
16
-
17
- throw new Error(
18
- `Could not detect dialect from URL: ${url}\nSupported: postgresql://, mysql://, sqlite:, file:`,
19
- );
20
- }
package/main.ts DELETED
@@ -1,41 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- import { HELP } from "./lib/constants/help";
4
- import { parseStudioOptions } from "./lib/utils/args";
5
- import { runStudioCommand } from "./lib/commands/studio";
6
-
7
- const args = process.argv.slice(2);
8
-
9
- if (args.length === 0 || args.includes("-h") || args.includes("--help")) {
10
- console.log(HELP);
11
- process.exit(0);
12
- }
13
-
14
- if (args.includes("-v") || args.includes("--version")) {
15
- const pkg = await Bun.file(
16
- new URL("../package.json", import.meta.url),
17
- ).json();
18
- console.log(`dbstudio v${pkg.version}`);
19
- process.exit(0);
20
- }
21
-
22
- try {
23
- const options = parseStudioOptions(args);
24
- const code = await runStudioCommand(options);
25
- process.exit(code);
26
- } catch (error) {
27
- const msg = error instanceof Error ? error.message : String(error);
28
-
29
- if (msg.startsWith("Please provide")) {
30
- console.error(`❌ ${msg}`);
31
- console.error(" Run dbstudio --help for usage.");
32
- } else if (msg.startsWith("Could not detect")) {
33
- console.error(`❌ ${msg}`);
34
- } else if (msg.startsWith("--tunnel requires")) {
35
- console.error(`❌ ${msg}`);
36
- } else {
37
- console.error(`❌ Unexpected error: ${msg}`);
38
- }
39
-
40
- process.exit(1);
41
- }