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.
- package/README.md +10 -9
- package/package.json +1 -1
- 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
|
-
#
|
|
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
|
|
31
|
+
echo "SELECT * FROM users" | bunx dbcat ./data.db
|
|
31
32
|
```
|
|
32
33
|
|
|
33
34
|
### Options
|
|
34
35
|
|
|
35
|
-
| Flag
|
|
36
|
-
|
|
36
|
+
| Flag | Description |
|
|
37
|
+
|----------------|---------------------------------------------------|
|
|
37
38
|
| `--full`, `-f` | Show all rows when browsing tables (default: 100) |
|
|
38
|
-
| `--json`
|
|
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
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(
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
306
|
+
process.stdout.write("\n");
|
|
259
307
|
}
|
|
260
308
|
const message = error instanceof Error ? error.message : String(error);
|
|
261
309
|
|