dbcat 0.0.11 → 0.0.13

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
@@ -33,11 +33,11 @@ echo "SELECT * FROM users" | bunx dbcat ./data.db
33
33
 
34
34
  ### Options
35
35
 
36
- | Flag | Description |
37
- |----------------|---------------------------------------------------|
38
- | `--full`, `-f` | Show all rows when browsing tables (default: 100) |
39
- | `--json` | Output as JSON (indented if TTY) |
40
- | `--json=color` | Output as normal object console.log |
36
+ | Flag | Description |
37
+ |----------------|---------------------------------------------------------------|
38
+ | `--full`, `-f` | Show all rows & data when browsing tables (default: 100 rows) |
39
+ | `--json` | Output as JSON (indented if TTY) |
40
+ | `--json=color` | Output as normal object console.log |
41
41
 
42
42
  Piped queries always return all rows.
43
43
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dbcat",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
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
@@ -121,7 +121,7 @@ export function getTableData(
121
121
  tableName: string,
122
122
  limit?: number
123
123
  ): Promise<Record<string, unknown>[]> {
124
- if (limit === undefined) {
124
+ if (limit === undefined || limit === Infinity) {
125
125
  return sql`SELECT * FROM ${sql(tableName)}`;
126
126
  }
127
127
  return sql`SELECT * FROM ${sql(tableName)} LIMIT ${limit}`;
@@ -135,14 +135,15 @@ function displayTable(
135
135
  tableName: string,
136
136
  rows: Record<string, unknown>[],
137
137
  totalRows?: number,
138
- maxRows?: number
138
+ maxRows?: number,
139
+ fullContent?: boolean
139
140
  ) {
140
- console.log();
141
- printTable(rows, { title: tableName, totalRows, maxRows });
141
+ console.log("");
142
+ printTable(rows, { title: tableName, totalRows, maxRows, fullContent });
142
143
  }
143
144
 
144
145
  function displayQueryResult(rows: Record<string, unknown>[]) {
145
- printTable(rows, { title: "Result", maxRows: Infinity });
146
+ printTable(rows, { title: "Result", maxRows: Infinity, fullContent: true });
146
147
  }
147
148
 
148
149
  async function readStdin(): Promise<string | null> {
@@ -162,12 +163,18 @@ function parseArgs() {
162
163
  const args = process.argv.slice(2);
163
164
  let input: string | undefined;
164
165
  let full = false;
165
- let help = false
166
+ let help = false;
166
167
  let json: false | "plain" | "color" = false;
168
+ let fullRows = false;
169
+ let fullContent = false;
167
170
 
168
171
  for (const arg of args) {
169
172
  if (arg === "--full" || arg === "-f") {
170
173
  full = true;
174
+ } else if (arg === "--full-rows") {
175
+ fullRows = true;
176
+ } else if (arg === "--full-content") {
177
+ fullContent = true;
171
178
  } else if (arg === "--help" || arg === "-h") {
172
179
  help = true;
173
180
  } else if (arg === "--json") {
@@ -179,7 +186,7 @@ function parseArgs() {
179
186
  }
180
187
  }
181
188
 
182
- return { input, full, json, help };
189
+ return { input, full, json, help, fullRows, fullContent };
183
190
  }
184
191
 
185
192
  const DEFAULT_LIMIT = 100;
@@ -194,6 +201,11 @@ function showUsageAndExit(): never {
194
201
  console.error(" dbcat https://example.com/data.db");
195
202
  console.error("");
196
203
  console.error("Or set DATABASE_URL environment variable.");
204
+ console.error("");
205
+ console.error("Flags:");
206
+ console.error(" --full : Show the entire table and wrap cell values to fit column widths");
207
+ console.error(" --json : Output as JSON instead of a visual table");
208
+ console.error(" --help : Show this help message");
197
209
  process.exit(1);
198
210
  }
199
211
 
@@ -207,7 +219,7 @@ function outputJson(data: unknown, color: boolean) {
207
219
  }
208
220
 
209
221
  async function main() {
210
- const { input, full, json, help } = parseArgs();
222
+ const { input, full, json, help, fullRows, fullContent } = parseArgs();
211
223
 
212
224
  if ((!input && !process.env.DATABASE_URL) || help) {
213
225
  showUsageAndExit();
@@ -295,15 +307,18 @@ async function main() {
295
307
  console.log(`${dim}No tables found.${reset}`);
296
308
  } else {
297
309
  for (const table of tables) {
298
- if (full) {
310
+ const maxRows = (full || fullRows) ? Infinity : DEFAULT_LIMIT;
311
+ const fullContentFlag = full || fullContent;
312
+
313
+ if (maxRows === Infinity) {
299
314
  const data = await getTableData(sql, table.name);
300
- displayTable(table.name, data, undefined, Infinity);
315
+ displayTable(table.name, data, undefined, Infinity, fullContentFlag);
301
316
  } else {
302
317
  const [count, data] = await Promise.all([
303
318
  getTableCount(sql, table.name),
304
- getTableData(sql, table.name, DEFAULT_LIMIT),
319
+ getTableData(sql, table.name, maxRows),
305
320
  ]);
306
- displayTable(table.name, data, count, DEFAULT_LIMIT);
321
+ displayTable(table.name, data, count, maxRows, fullContentFlag);
307
322
  }
308
323
  }
309
324
  }
package/src/table.ts CHANGED
@@ -74,6 +74,49 @@ export interface TableOptions {
74
74
  maxRows?: number;
75
75
  title?: string;
76
76
  totalRows?: number;
77
+ fullContent?: boolean;
78
+ }
79
+
80
+ function wrapLines(str: string, maxWidth: number): string[] {
81
+ // Split input into logical words, wrap to fit maxWidth
82
+ const raw = str.split(/\n/g).join(" ");
83
+ if (raw === "") return [""];
84
+
85
+ let lines: string[] = [];
86
+ let current = "";
87
+
88
+ const tokens = raw.split(/([ ]+)/);
89
+ for (const token of tokens) {
90
+ let fragment = token;
91
+ while (fragment.length > 0) {
92
+ const width = Bun.stringWidth(current + fragment);
93
+ if (width > maxWidth) {
94
+ if (current.length > 0) {
95
+ lines.push(current);
96
+ current = "";
97
+ } else {
98
+ let cutPoint = 1;
99
+ while (cutPoint < fragment.length && Bun.stringWidth(fragment.slice(0, cutPoint)) < maxWidth) {
100
+ cutPoint++;
101
+ }
102
+ lines.push(fragment.slice(0, cutPoint));
103
+ fragment = fragment.slice(cutPoint);
104
+ }
105
+ } else {
106
+ current += fragment;
107
+ fragment = "";
108
+ }
109
+ }
110
+ if (Bun.stringWidth(current) >= maxWidth) {
111
+ lines.push(current);
112
+ current = "";
113
+ }
114
+ }
115
+ if (current.length > 0) {
116
+ lines.push(current);
117
+ }
118
+
119
+ return lines;
77
120
  }
78
121
 
79
122
  export function printTable(
@@ -138,7 +181,7 @@ export function printTable(
138
181
  const colWidths: number[] = allColumns.map((col) => Bun.stringWidth(col));
139
182
  const formattedRows: string[][] = displayRows.map((row) =>
140
183
  allColumns.map((col, i) => {
141
- const formatted = formatValue(row[col]).replace(/\n/g, " ");
184
+ const formatted = formatValue(row[col]).replace(/\n/g, "\\n");
142
185
  colWidths[i] = Math.max(colWidths[i]!, Bun.stringWidth(formatted));
143
186
  return formatted;
144
187
  }),
@@ -261,18 +304,42 @@ export function printTable(
261
304
  `${dim}${BOX.headerLeft}${BOX.horizontal}${headerSep}${BOX.horizontal}${BOX.headerRight}${reset}`,
262
305
  );
263
306
 
264
- for (const row of visibleFormattedRows) {
265
- const line = row
266
- .map((val, i) => {
267
- const truncated = truncate(val, visibleColWidths[i]!);
268
- return visibleIsNumeric[i]
269
- ? padLeft(truncated, visibleColWidths[i]!)
270
- : padRight(truncated, visibleColWidths[i]!);
271
- })
272
- .join(` ${dim}${BOX.vertical}${reset} `);
273
- console.log(
274
- `${dim}${BOX.vertical}${reset} ${line} ${dim}${BOX.vertical}${reset}`,
275
- );
307
+ if (options.fullContent) {
308
+ for (let rowIdx = 0; rowIdx < visibleFormattedRows.length; rowIdx++) {
309
+ const row = visibleFormattedRows[rowIdx] ?? [];
310
+ const wrapped = row.map((val, i) => wrapLines(val, visibleColWidths[i]!));
311
+ const rowHeight = Math.max(...wrapped.map(x => x.length));
312
+ for (let lineIdx = 0; lineIdx < rowHeight; lineIdx++) {
313
+ const pieces = wrapped.map((cellLines, i) => {
314
+ const part: string = typeof cellLines[lineIdx] === "string" ? cellLines[lineIdx]! : "";
315
+ return visibleIsNumeric[i]
316
+ ? padLeft(truncate(part, visibleColWidths[i]!), visibleColWidths[i]!)
317
+ : padRight(truncate(part, visibleColWidths[i]!), visibleColWidths[i]!);
318
+ });
319
+ const line = pieces.join(` ${dim}${BOX.vertical}${reset} `);
320
+ console.log(`${dim}${BOX.vertical}${reset} ${line} ${dim}${BOX.vertical}${reset}`);
321
+ }
322
+ if (rowIdx < visibleFormattedRows.length - 1) {
323
+ const rowSep = visibleColWidths
324
+ .map((w) => BOX.horizontal.repeat(w))
325
+ .join(`${BOX.horizontal}${BOX.headerCross}${BOX.horizontal}`);
326
+ console.log(`${dim}${BOX.headerLeft}${BOX.horizontal}${rowSep}${BOX.horizontal}${BOX.headerRight}${reset}`);
327
+ }
328
+ }
329
+ } else {
330
+ for (const row of visibleFormattedRows) {
331
+ const line = row
332
+ .map((val, i) => {
333
+ const truncated = truncate(val, visibleColWidths[i]!);
334
+ return visibleIsNumeric[i]
335
+ ? padLeft(truncated, visibleColWidths[i]!)
336
+ : padRight(truncated, visibleColWidths[i]!);
337
+ })
338
+ .join(` ${dim}${BOX.vertical}${reset} `);
339
+ console.log(
340
+ `${dim}${BOX.vertical}${reset} ${line} ${dim}${BOX.vertical}${reset}`,
341
+ );
342
+ }
276
343
  }
277
344
 
278
345
  if (infoText) {