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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dbcat",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
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
@@ -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
- export function createConnection(input?: string): InstanceType<typeof SQL> {
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 = (sql.options as { filename?: string }).filename;
40
+ const filename = sql.options.filename;
42
41
  if (!filename || filename === ":memory:") return null;
43
42
 
44
- return filename.split("/").pop() ?? null;
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 table_name as name
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 = 'public'
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)}` as Promise<
100
- Record<string, unknown>[]
101
- >;
101
+ return sql`SELECT * FROM ${sql(tableName)}`;
102
102
  }
103
- return sql`SELECT * FROM ${sql(tableName)} LIMIT ${limit}` as Promise<
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: JsonMode = false;
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
- if (isTTY) {
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);