dbcat 0.0.2 → 0.0.4
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 +8 -0
- package/package.json +1 -1
- package/src/index.ts +29 -86
- package/src/table.ts +2 -5
package/README.md
CHANGED
|
@@ -56,6 +56,14 @@ Connected to demo.sqlite
|
|
|
56
56
|
╰──────────────────────────────────────────────────────────────╯
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
+
### Scrollable Output
|
|
60
|
+
|
|
61
|
+
Pipe to `less -R` for scrollable output with colors:
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
bunx dbcat ./data.db --full | less -R
|
|
65
|
+
```
|
|
66
|
+
|
|
59
67
|
## Requirements
|
|
60
68
|
|
|
61
69
|
[Bun](https://bun.sh) v1.3+
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
import { SQL } from "bun";
|
|
4
|
-
import { printTable } from "./table.ts";
|
|
5
|
-
|
|
6
3
|
if (typeof Bun !== "object") throw new Error("Please install & use bun!");
|
|
7
4
|
|
|
8
|
-
|
|
5
|
+
import { printTable } from "./table.ts";
|
|
6
|
+
|
|
7
|
+
export function createConnection(input?: string): InstanceType<typeof Bun.SQL> {
|
|
9
8
|
if (!input) {
|
|
10
|
-
return new SQL();
|
|
9
|
+
return new Bun.SQL();
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
if (
|
|
@@ -16,14 +15,14 @@ export function createConnection(input?: string): InstanceType<typeof SQL> {
|
|
|
16
15
|
input.endsWith(".sqlite") ||
|
|
17
16
|
input.endsWith(".sqlite3"))
|
|
18
17
|
) {
|
|
19
|
-
return new SQL(`sqlite://${input}`);
|
|
18
|
+
return new Bun.SQL(`sqlite://${input}`);
|
|
20
19
|
}
|
|
21
20
|
|
|
22
|
-
return new SQL(input);
|
|
21
|
+
return new Bun.SQL(input);
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
export async function getDatabaseName(
|
|
26
|
-
sql: InstanceType<typeof SQL>
|
|
25
|
+
sql: InstanceType<typeof Bun.SQL>
|
|
27
26
|
): Promise<string | null> {
|
|
28
27
|
const adapter = sql.options.adapter;
|
|
29
28
|
|
|
@@ -38,27 +37,30 @@ export async function getDatabaseName(
|
|
|
38
37
|
return result[0]?.name ?? null;
|
|
39
38
|
}
|
|
40
39
|
case "sqlite": {
|
|
41
|
-
const filename =
|
|
40
|
+
const filename = sql.options.filename;
|
|
42
41
|
if (!filename || filename === ":memory:") return null;
|
|
43
42
|
|
|
44
|
-
return filename.split("/").
|
|
43
|
+
return filename.toString().split("/").at(-1) ?? null;
|
|
45
44
|
}
|
|
46
45
|
default:
|
|
47
46
|
return null;
|
|
48
47
|
}
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
export function getAllTables(sql: InstanceType<typeof SQL>) {
|
|
50
|
+
export function getAllTables(sql: InstanceType<typeof Bun.SQL>) {
|
|
52
51
|
const adapter = sql.options.adapter;
|
|
53
52
|
|
|
54
53
|
switch (adapter) {
|
|
55
54
|
case "postgres":
|
|
56
55
|
return sql`
|
|
57
|
-
SELECT
|
|
56
|
+
SELECT CASE
|
|
57
|
+
WHEN table_schema = 'public' THEN table_name
|
|
58
|
+
ELSE table_schema || '.' || table_name
|
|
59
|
+
END as name
|
|
58
60
|
FROM information_schema.tables
|
|
59
|
-
WHERE table_schema
|
|
61
|
+
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
|
|
60
62
|
AND table_type = 'BASE TABLE'
|
|
61
|
-
ORDER BY table_name
|
|
63
|
+
ORDER BY table_schema, table_name
|
|
62
64
|
` as Promise<{ name: string }[]>;
|
|
63
65
|
case "mysql":
|
|
64
66
|
case "mariadb":
|
|
@@ -83,7 +85,7 @@ export function getAllTables(sql: InstanceType<typeof SQL>) {
|
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
export async function getTableCount(
|
|
86
|
-
sql: InstanceType<typeof SQL>,
|
|
88
|
+
sql: InstanceType<typeof Bun.SQL>,
|
|
87
89
|
tableName: string
|
|
88
90
|
): Promise<number> {
|
|
89
91
|
const result = await sql`SELECT COUNT(*) as count FROM ${sql(tableName)}`;
|
|
@@ -91,72 +93,20 @@ export async function getTableCount(
|
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
export function getTableData(
|
|
94
|
-
sql: InstanceType<typeof SQL>,
|
|
96
|
+
sql: InstanceType<typeof Bun.SQL>,
|
|
95
97
|
tableName: string,
|
|
96
98
|
limit?: number
|
|
97
|
-
) {
|
|
99
|
+
): Promise<Record<string, unknown>[]> {
|
|
98
100
|
if (limit === undefined) {
|
|
99
|
-
return sql`SELECT * FROM ${sql(tableName)}
|
|
100
|
-
Record<string, unknown>[]
|
|
101
|
-
>;
|
|
101
|
+
return sql`SELECT * FROM ${sql(tableName)}`;
|
|
102
102
|
}
|
|
103
|
-
return sql`SELECT * FROM ${sql(tableName)} LIMIT ${limit}
|
|
104
|
-
Record<string, unknown>[]
|
|
105
|
-
>;
|
|
103
|
+
return sql`SELECT * FROM ${sql(tableName)} LIMIT ${limit}`;
|
|
106
104
|
}
|
|
107
105
|
|
|
108
|
-
export function runQuery(sql: InstanceType<typeof SQL>, query: string) {
|
|
106
|
+
export function runQuery(sql: InstanceType<typeof Bun.SQL>, query: string) {
|
|
109
107
|
return sql.unsafe(query) as Promise<Record<string, unknown>[]>;
|
|
110
108
|
}
|
|
111
109
|
|
|
112
|
-
export function formatTableOutput(
|
|
113
|
-
tableName: string,
|
|
114
|
-
rows: Record<string, unknown>[]
|
|
115
|
-
): string {
|
|
116
|
-
const lines: string[] = [];
|
|
117
|
-
lines.push(`\n=== ${tableName} ===`);
|
|
118
|
-
|
|
119
|
-
if (rows.length === 0) {
|
|
120
|
-
lines.push("(empty table)");
|
|
121
|
-
return lines.join("\n");
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const maxRows = 100;
|
|
125
|
-
const displayRows = rows.slice(0, maxRows);
|
|
126
|
-
|
|
127
|
-
if (displayRows.length > 0) {
|
|
128
|
-
const keys = Object.keys(displayRows[0]!);
|
|
129
|
-
lines.push(keys.join(" | "));
|
|
130
|
-
lines.push("-".repeat(keys.join(" | ").length));
|
|
131
|
-
for (const row of displayRows) {
|
|
132
|
-
lines.push(keys.map((k) => String(row[k] ?? "NULL")).join(" | "));
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (rows.length > maxRows) {
|
|
137
|
-
lines.push(`... and ${rows.length - maxRows} more rows`);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return lines.join("\n");
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export function formatQueryResult(rows: Record<string, unknown>[]): string {
|
|
144
|
-
if (rows.length === 0) {
|
|
145
|
-
return "(no results)";
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const lines: string[] = [];
|
|
149
|
-
const keys = Object.keys(rows[0]!);
|
|
150
|
-
lines.push(keys.join(" | "));
|
|
151
|
-
lines.push("-".repeat(keys.join(" | ").length));
|
|
152
|
-
for (const row of rows) {
|
|
153
|
-
lines.push(keys.map((k) => String(row[k] ?? "NULL")).join(" | "));
|
|
154
|
-
}
|
|
155
|
-
lines.push(`\n${rows.length} row(s)`);
|
|
156
|
-
|
|
157
|
-
return lines.join("\n");
|
|
158
|
-
}
|
|
159
|
-
|
|
160
110
|
function displayTable(
|
|
161
111
|
tableName: string,
|
|
162
112
|
rows: Record<string, unknown>[],
|
|
@@ -184,13 +134,11 @@ async function readStdin(): Promise<string | null> {
|
|
|
184
134
|
}
|
|
185
135
|
}
|
|
186
136
|
|
|
187
|
-
type JsonMode = false | "plain" | "color";
|
|
188
|
-
|
|
189
137
|
function parseArgs() {
|
|
190
138
|
const args = process.argv.slice(2);
|
|
191
139
|
let input: string | undefined;
|
|
192
140
|
let full = false;
|
|
193
|
-
let json:
|
|
141
|
+
let json: false | "plain" | "color" = false;
|
|
194
142
|
|
|
195
143
|
for (const arg of args) {
|
|
196
144
|
if (arg === "--full" || arg === "-f") {
|
|
@@ -237,7 +185,7 @@ async function main() {
|
|
|
237
185
|
showUsageAndExit();
|
|
238
186
|
}
|
|
239
187
|
|
|
240
|
-
let sql: InstanceType<typeof SQL>;
|
|
188
|
+
let sql: InstanceType<typeof Bun.SQL>;
|
|
241
189
|
try {
|
|
242
190
|
sql = createConnection(input);
|
|
243
191
|
} catch (error) {
|
|
@@ -250,7 +198,7 @@ async function main() {
|
|
|
250
198
|
const dim = Bun.enableANSIColors ? "\x1b[2m" : "";
|
|
251
199
|
const reset = Bun.enableANSIColors ? "\x1b[0m" : "";
|
|
252
200
|
const bold = Bun.enableANSIColors ? "\x1b[1m" : "";
|
|
253
|
-
const clearLine = "\x1b[2K\r";
|
|
201
|
+
const clearLine = Bun.enableANSIColors ? "\x1b[2K\r" : "";
|
|
254
202
|
|
|
255
203
|
try {
|
|
256
204
|
const stdinQuery = await readStdin();
|
|
@@ -263,7 +211,7 @@ async function main() {
|
|
|
263
211
|
displayQueryResult(results);
|
|
264
212
|
}
|
|
265
213
|
} else {
|
|
266
|
-
if (isTTY && !json) {
|
|
214
|
+
if (Bun.enableANSIColors && isTTY && !json) {
|
|
267
215
|
process.stdout.write(
|
|
268
216
|
`${dim}Connecting to ${sql.options.adapter}...${reset}`
|
|
269
217
|
);
|
|
@@ -284,16 +232,11 @@ async function main() {
|
|
|
284
232
|
}
|
|
285
233
|
outputJson(result, json === "color");
|
|
286
234
|
} else {
|
|
235
|
+
if (Bun.enableANSIColors && isTTY) process.stdout.write(clearLine);
|
|
287
236
|
const dbDisplay = dbName
|
|
288
237
|
? `${bold}${dbName}${reset}`
|
|
289
238
|
: `${bold}${sql.options.adapter}${reset} ${dim}database${reset}`;
|
|
290
|
-
|
|
291
|
-
process.stdout.write(
|
|
292
|
-
`${clearLine}${dim}Connected to${reset} ${dbDisplay}\n`
|
|
293
|
-
);
|
|
294
|
-
} else {
|
|
295
|
-
console.log(`Connected to ${dbName || sql.options.adapter}`);
|
|
296
|
-
}
|
|
239
|
+
console.log(`${dim}Connected to${reset} ${dbDisplay}`);
|
|
297
240
|
|
|
298
241
|
if (tables.length === 0) {
|
|
299
242
|
console.log(`${dim}No tables found.${reset}`);
|
|
@@ -314,7 +257,7 @@ async function main() {
|
|
|
314
257
|
}
|
|
315
258
|
}
|
|
316
259
|
} catch (error) {
|
|
317
|
-
if (isTTY && !json) {
|
|
260
|
+
if (Bun.enableANSIColors && isTTY && !json) {
|
|
318
261
|
process.stdout.write(clearLine);
|
|
319
262
|
}
|
|
320
263
|
const message = error instanceof Error ? error.message : String(error);
|
package/src/table.ts
CHANGED
|
@@ -17,7 +17,7 @@ const BOX = {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
function getTerminalWidth(): number {
|
|
20
|
-
return process.stdout.columns || 120;
|
|
20
|
+
return process.stdout.columns || process.stderr.columns || 120;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
function formatValue(value: unknown): string {
|
|
@@ -87,10 +87,7 @@ export function printTable(
|
|
|
87
87
|
|
|
88
88
|
if (rows.length === 0) {
|
|
89
89
|
if (title) {
|
|
90
|
-
const contentWidth = Math.max(
|
|
91
|
-
Bun.stringWidth("(empty)"),
|
|
92
|
-
Bun.stringWidth(title)
|
|
93
|
-
);
|
|
90
|
+
const contentWidth = Math.max("(empty)".length, Bun.stringWidth(title));
|
|
94
91
|
const innerWidth = contentWidth + 2;
|
|
95
92
|
const titleDisplay = ` ${title} `;
|
|
96
93
|
const titleWidth = Bun.stringWidth(titleDisplay);
|