dbcat 0.0.3 → 0.0.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 (3) hide show
  1. package/README.md +10 -9
  2. package/package.json +1 -1
  3. package/src/index.ts +62 -14
package/README.md CHANGED
@@ -11,32 +11,33 @@ bunx dbcat ./data.db
11
11
  Connect to a database to browse all tables:
12
12
 
13
13
  ```sh
14
- # SQLite file
14
+ # No argument - uses DATABASE_URL environment variable
15
+ bunx dbcat
16
+
17
+ # SQLite
15
18
  bunx dbcat ./database.sqlite
19
+ bunx dbcat https://example.com/data.db
16
20
 
17
21
  # PostgreSQL
18
22
  bunx dbcat postgres://user:pass@localhost:5432/mydb
19
23
 
20
24
  # MySQL
21
25
  bunx dbcat mysql://user:pass@localhost:3306/mydb
22
-
23
- # No argument - uses DATABASE_URL environment variable
24
- bunx dbcat
25
26
  ```
26
27
 
27
28
  Run a query by piping SQL:
28
29
 
29
30
  ```sh
30
- echo "SELECT * FROM users WHERE active = true" | bunx dbcat ./data.db
31
+ echo "SELECT * FROM users" | bunx dbcat ./data.db
31
32
  ```
32
33
 
33
34
  ### Options
34
35
 
35
- | Flag | Description |
36
- |------|-------------|
36
+ | Flag | Description |
37
+ |----------------|---------------------------------------------------|
37
38
  | `--full`, `-f` | Show all rows when browsing tables (default: 100) |
38
- | `--json` | Output as JSON (indented if TTY) |
39
- | `--json=color` | Output as normal object console.log |
39
+ | `--json` | Output as JSON (indented if TTY) |
40
+ | `--json=color` | Output as normal object console.log |
40
41
 
41
42
  Piped queries always return all rows.
42
43
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dbcat",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "A simple CLI to view database tables. Supports PostgreSQL, MySQL, and SQLite.",
5
5
  "author": "RiskyMH",
6
6
  "license": "MIT",
package/src/index.ts CHANGED
@@ -3,6 +3,11 @@
3
3
  if (typeof Bun !== "object") throw new Error("Please install & use bun!");
4
4
 
5
5
  import { printTable } from "./table.ts";
6
+ import { tmpdir } from "node:os";
7
+ import { join } from "node:path";
8
+
9
+ const SQLITE_EXTENSIONS = [".db", ".sqlite", ".sqlite3", ".db3", ".s3db"];
10
+ const REMOTE_PROTOCOLS = ["http://", "https://", "s3://"];
6
11
 
7
12
  export function createConnection(input?: string): InstanceType<typeof Bun.SQL> {
8
13
  if (!input) {
@@ -11,9 +16,7 @@ export function createConnection(input?: string): InstanceType<typeof Bun.SQL> {
11
16
 
12
17
  if (
13
18
  !input.includes("://") &&
14
- (input.endsWith(".db") ||
15
- input.endsWith(".sqlite") ||
16
- input.endsWith(".sqlite3"))
19
+ SQLITE_EXTENSIONS.some((ext) => input.endsWith(ext))
17
20
  ) {
18
21
  return new Bun.SQL(`sqlite://${input}`);
19
22
  }
@@ -21,6 +24,22 @@ export function createConnection(input?: string): InstanceType<typeof Bun.SQL> {
21
24
  return new Bun.SQL(input);
22
25
  }
23
26
 
27
+ async function downloadToTmp(url: string): Promise<string> {
28
+ const response = await fetch(url);
29
+ if (!response.ok) {
30
+ throw new Error(
31
+ `Failed to download: ${response.status} ${response.statusText}`
32
+ );
33
+ }
34
+
35
+ const urlPath = new URL(url).pathname;
36
+ const filename = urlPath.split("/").at(-1) || "downloaded.db";
37
+ const tmpPath = join(tmpdir(), `dbcat-${Bun.hash(url)}-${filename}`);
38
+ await Bun.write(tmpPath, response);
39
+
40
+ return tmpPath;
41
+ }
42
+
24
43
  export async function getDatabaseName(
25
44
  sql: InstanceType<typeof Bun.SQL>
26
45
  ): Promise<string | null> {
@@ -53,11 +72,14 @@ export function getAllTables(sql: InstanceType<typeof Bun.SQL>) {
53
72
  switch (adapter) {
54
73
  case "postgres":
55
74
  return sql`
56
- SELECT table_name as name
75
+ SELECT CASE
76
+ WHEN table_schema = 'public' THEN table_name
77
+ ELSE table_schema || '.' || table_name
78
+ END as name
57
79
  FROM information_schema.tables
58
- WHERE table_schema = 'public'
80
+ WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
59
81
  AND table_type = 'BASE TABLE'
60
- ORDER BY table_name
82
+ ORDER BY table_schema, table_name
61
83
  ` as Promise<{ name: string }[]>;
62
84
  case "mysql":
63
85
  case "mariadb":
@@ -161,6 +183,7 @@ function showUsageAndExit(): never {
161
183
  console.error(" dbcat ./data.db");
162
184
  console.error(" dbcat postgres://user:pass@localhost/mydb");
163
185
  console.error(" dbcat mysql://user:pass@localhost/mydb");
186
+ console.error(" dbcat https://example.com/data.db");
164
187
  console.error("");
165
188
  console.error("Or set DATABASE_URL environment variable.");
166
189
  process.exit(1);
@@ -182,21 +205,46 @@ async function main() {
182
205
  showUsageAndExit();
183
206
  }
184
207
 
208
+ const isTTY = process.stdout.isTTY;
209
+ const dim = Bun.enableANSIColors ? "\x1b[2m" : "";
210
+ const reset = Bun.enableANSIColors ? "\x1b[0m" : "";
211
+ const bold = Bun.enableANSIColors ? "\x1b[1m" : "";
212
+ const clearLine = Bun.enableANSIColors ? "\x1b[2K\r" : "";
213
+
214
+ // Handle remote URLs pointing to SQLite files
215
+ let connectionInput = input;
216
+ if (
217
+ input &&
218
+ REMOTE_PROTOCOLS.some((p) => input.startsWith(p)) &&
219
+ SQLITE_EXTENSIONS.some((ext) => input.endsWith(ext))
220
+ ) {
221
+ if (Bun.enableANSIColors && isTTY && !json) {
222
+ process.stdout.write(`${dim}Downloading SQLite database...${reset}`);
223
+ }
224
+ try {
225
+ connectionInput = `sqlite://${await downloadToTmp(input)}`;
226
+ } catch (error) {
227
+ if (Bun.enableANSIColors && isTTY && !json) {
228
+ process.stdout.write("\n");
229
+ }
230
+ console.error("Failed to download:");
231
+ console.error(error instanceof Error ? error.message : String(error));
232
+ process.exit(1);
233
+ }
234
+ if (Bun.enableANSIColors && isTTY && !json) {
235
+ process.stdout.write(clearLine);
236
+ }
237
+ }
238
+
185
239
  let sql: InstanceType<typeof Bun.SQL>;
186
240
  try {
187
- sql = createConnection(input);
241
+ sql = createConnection(connectionInput);
188
242
  } catch (error) {
189
243
  console.error("Failed to connect:");
190
244
  console.error(error instanceof Error ? error.message : String(error));
191
245
  process.exit(1);
192
246
  }
193
247
 
194
- const isTTY = process.stdout.isTTY;
195
- const dim = Bun.enableANSIColors ? "\x1b[2m" : "";
196
- const reset = Bun.enableANSIColors ? "\x1b[0m" : "";
197
- const bold = Bun.enableANSIColors ? "\x1b[1m" : "";
198
- const clearLine = Bun.enableANSIColors ? "\x1b[2K\r" : "";
199
-
200
248
  try {
201
249
  const stdinQuery = await readStdin();
202
250
 
@@ -255,7 +303,7 @@ async function main() {
255
303
  }
256
304
  } catch (error) {
257
305
  if (Bun.enableANSIColors && isTTY && !json) {
258
- process.stdout.write(clearLine);
306
+ process.stdout.write("\n");
259
307
  }
260
308
  const message = error instanceof Error ? error.message : String(error);
261
309