Add CLI entry point with Commander.js, config management (~/.mcpctl/config.json with Zod validation), output formatters (table/json/yaml), config and status commands with dependency injection for testing. Fix sanitizeString regex ordering. 67 tests passing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
45 lines
1.5 KiB
TypeScript
45 lines
1.5 KiB
TypeScript
export interface Column<T> {
|
|
header: string;
|
|
key: keyof T | ((row: T) => string);
|
|
width?: number;
|
|
align?: 'left' | 'right';
|
|
}
|
|
|
|
export function formatTable<T>(rows: T[], columns: Column<T>[]): string {
|
|
if (rows.length === 0) {
|
|
return 'No results found.';
|
|
}
|
|
|
|
const getValue = (row: T, col: Column<T>): string => {
|
|
if (typeof col.key === 'function') {
|
|
return col.key(row);
|
|
}
|
|
const val = row[col.key];
|
|
return val == null ? '' : String(val);
|
|
};
|
|
|
|
// Calculate column widths
|
|
const widths = columns.map((col) => {
|
|
if (col.width !== undefined) return col.width;
|
|
const headerLen = col.header.length;
|
|
const maxDataLen = rows.reduce((max, row) => {
|
|
const val = getValue(row, col);
|
|
return Math.max(max, val.length);
|
|
}, 0);
|
|
return Math.max(headerLen, maxDataLen);
|
|
});
|
|
|
|
const pad = (text: string, width: number, align: 'left' | 'right' = 'left'): string => {
|
|
const truncated = text.length > width ? text.slice(0, width - 1) + '\u2026' : text;
|
|
return align === 'right' ? truncated.padStart(width) : truncated.padEnd(width);
|
|
};
|
|
|
|
const headerLine = columns.map((col, i) => pad(col.header, widths[i] ?? 0, col.align ?? 'left')).join(' ');
|
|
const separator = widths.map((w) => '-'.repeat(w)).join(' ');
|
|
const dataLines = rows.map((row) =>
|
|
columns.map((col, i) => pad(getValue(row, col), widths[i] ?? 0, col.align ?? 'left')).join(' '),
|
|
);
|
|
|
|
return [headerLine, separator, ...dataLines].join('\n');
|
|
}
|