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 +5 -5
- package/package.json +1 -1
- package/src/index.ts +27 -12
- package/src/table.ts +80 -13
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
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
|
-
|
|
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,
|
|
319
|
+
getTableData(sql, table.name, maxRows),
|
|
305
320
|
]);
|
|
306
|
-
displayTable(table.name, data, count,
|
|
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
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
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) {
|