@mks2508/better-logger 4.0.0 → 5.0.1
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/CHANGELOG.json +67 -1
- package/package.json +28 -3
- package/packages/core/package.json +1 -1
- package/packages/exports/package.json +2 -2
- package/packages/styling/package.json +2 -2
- package/playground/demo-all.ts +95 -0
- package/playground/demo-box.ts +85 -0
- package/playground/demo-levels.ts +56 -0
- package/playground/demo-real-world.ts +98 -0
- package/playground/demo-spinner.ts +77 -0
- package/playground/demo-steps.ts +72 -0
- package/playground/demo-table.ts +83 -0
- package/src/Logger.ts +213 -8
- package/src/ScopedLogger.ts +43 -1
- package/src/cli-module.ts +21 -0
- package/src/cli-primitives/box.ts +86 -0
- package/src/cli-primitives/cli-table.ts +62 -0
- package/src/cli-primitives/divider.ts +17 -0
- package/src/cli-primitives/header.ts +18 -0
- package/src/cli-primitives/index.ts +13 -0
- package/src/cli-primitives/server-fallback.ts +54 -0
- package/src/cli-primitives/spinner.ts +133 -0
- package/src/cli-primitives/step.ts +22 -0
- package/src/constants.ts +14 -2
- package/src/index.ts +18 -1
- package/src/terminal/formatter.ts +16 -10
- package/src/types/core.ts +53 -0
- package/src/types/index.ts +4 -0
- package/vite.config.ts +33 -0
- package/bun.lock +0 -333
- package/dist/Logger.d.ts +0 -863
- package/dist/Logger.d.ts.map +0 -1
- package/dist/ScopedLogger.d.ts +0 -57
- package/dist/ScopedLogger.d.ts.map +0 -1
- package/dist/chunks/Logger-D-gmmgR2.js +0 -4458
- package/dist/chunks/Logger-D-gmmgR2.js.map +0 -1
- package/dist/chunks/Logger-D7cfaz15.js +0 -2
- package/dist/chunks/Logger-D7cfaz15.js.map +0 -1
- package/dist/chunks/RemoteLogHandler-CjWpWZGl.js +0 -33
- package/dist/chunks/RemoteLogHandler-CjWpWZGl.js.map +0 -1
- package/dist/chunks/RemoteLogHandler-ymkQ97xl.js +0 -2
- package/dist/chunks/RemoteLogHandler-ymkQ97xl.js.map +0 -1
- package/dist/chunks/environment-COWvu6Wz.js +0 -1577
- package/dist/chunks/environment-COWvu6Wz.js.map +0 -1
- package/dist/chunks/environment-C_8J-zQ_.js +0 -4
- package/dist/chunks/environment-C_8J-zQ_.js.map +0 -1
- package/dist/chunks/formatting-CYjT9yhO.js +0 -380
- package/dist/chunks/formatting-CYjT9yhO.js.map +0 -1
- package/dist/chunks/formatting-Cg5YhB9Y.js +0 -2
- package/dist/chunks/formatting-Cg5YhB9Y.js.map +0 -1
- package/dist/cli/CommandProcessor.d.ts +0 -100
- package/dist/cli/CommandProcessor.d.ts.map +0 -1
- package/dist/cli/commands/ConfigCommand.d.ts +0 -14
- package/dist/cli/commands/ConfigCommand.d.ts.map +0 -1
- package/dist/cli/commands/ExportCommand.d.ts +0 -48
- package/dist/cli/commands/ExportCommand.d.ts.map +0 -1
- package/dist/cli/commands/HistoryCommand.d.ts +0 -47
- package/dist/cli/commands/HistoryCommand.d.ts.map +0 -1
- package/dist/cli/commands/StatusCommand.d.ts +0 -30
- package/dist/cli/commands/StatusCommand.d.ts.map +0 -1
- package/dist/cli/commands/ThemeCommand.d.ts +0 -30
- package/dist/cli/commands/ThemeCommand.d.ts.map +0 -1
- package/dist/cli/help.d.ts +0 -12
- package/dist/cli/help.d.ts.map +0 -1
- package/dist/cli/index.d.ts +0 -15
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/constants.d.ts +0 -227
- package/dist/constants.d.ts.map +0 -1
- package/dist/core.cjs +0 -2
- package/dist/core.cjs.map +0 -1
- package/dist/core.d.ts +0 -127
- package/dist/core.d.ts.map +0 -1
- package/dist/core.js +0 -291
- package/dist/core.js.map +0 -1
- package/dist/example.d.ts +0 -18
- package/dist/example.d.ts.map +0 -1
- package/dist/exports-module.d.ts +0 -196
- package/dist/exports-module.d.ts.map +0 -1
- package/dist/exports.cjs +0 -2
- package/dist/exports.cjs.map +0 -1
- package/dist/exports.js +0 -238
- package/dist/exports.js.map +0 -1
- package/dist/handlers/AnalyticsLogHandler.d.ts +0 -8
- package/dist/handlers/AnalyticsLogHandler.d.ts.map +0 -1
- package/dist/handlers/ExportLogHandler.d.ts +0 -100
- package/dist/handlers/ExportLogHandler.d.ts.map +0 -1
- package/dist/handlers/FileLogHandler.d.ts +0 -33
- package/dist/handlers/FileLogHandler.d.ts.map +0 -1
- package/dist/handlers/RemoteLogHandler.d.ts +0 -11
- package/dist/handlers/RemoteLogHandler.d.ts.map +0 -1
- package/dist/handlers/index.d.ts +0 -8
- package/dist/handlers/index.d.ts.map +0 -1
- package/dist/hooks/HookManager.d.ts +0 -21
- package/dist/hooks/HookManager.d.ts.map +0 -1
- package/dist/hooks/index.d.ts +0 -2
- package/dist/hooks/index.d.ts.map +0 -1
- package/dist/index.cjs +0 -2
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.ts +0 -153
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -609
- package/dist/index.js.map +0 -1
- package/dist/main.d.ts +0 -2
- package/dist/main.d.ts.map +0 -1
- package/dist/serializers/SerializerRegistry.d.ts +0 -16
- package/dist/serializers/SerializerRegistry.d.ts.map +0 -1
- package/dist/serializers/index.d.ts +0 -2
- package/dist/serializers/index.d.ts.map +0 -1
- package/dist/styling/LogStyleBuilder.d.ts +0 -146
- package/dist/styling/LogStyleBuilder.d.ts.map +0 -1
- package/dist/styling/SemanticStyles.d.ts +0 -178
- package/dist/styling/SemanticStyles.d.ts.map +0 -1
- package/dist/styling/SmartPresets.d.ts +0 -22
- package/dist/styling/SmartPresets.d.ts.map +0 -1
- package/dist/styling/StyleBuilder.d.ts +0 -140
- package/dist/styling/StyleBuilder.d.ts.map +0 -1
- package/dist/styling/StyleCache.d.ts +0 -42
- package/dist/styling/StyleCache.d.ts.map +0 -1
- package/dist/styling/banners.d.ts +0 -42
- package/dist/styling/banners.d.ts.map +0 -1
- package/dist/styling/index.d.ts +0 -10
- package/dist/styling/index.d.ts.map +0 -1
- package/dist/styling/themes.d.ts +0 -7
- package/dist/styling/themes.d.ts.map +0 -1
- package/dist/styling-module.d.ts +0 -178
- package/dist/styling-module.d.ts.map +0 -1
- package/dist/styling.cjs +0 -2
- package/dist/styling.cjs.map +0 -1
- package/dist/styling.js +0 -146
- package/dist/styling.js.map +0 -1
- package/dist/terminal/color-converter.d.ts +0 -73
- package/dist/terminal/color-converter.d.ts.map +0 -1
- package/dist/terminal/formatter.d.ts +0 -20
- package/dist/terminal/formatter.d.ts.map +0 -1
- package/dist/terminal/terminal-renderer.d.ts +0 -77
- package/dist/terminal/terminal-renderer.d.ts.map +0 -1
- package/dist/transports/ConsoleTransport.d.ts +0 -9
- package/dist/transports/ConsoleTransport.d.ts.map +0 -1
- package/dist/transports/FileTransport.d.ts +0 -15
- package/dist/transports/FileTransport.d.ts.map +0 -1
- package/dist/transports/HttpTransport.d.ts +0 -16
- package/dist/transports/HttpTransport.d.ts.map +0 -1
- package/dist/transports/TransportManager.d.ts +0 -14
- package/dist/transports/TransportManager.d.ts.map +0 -1
- package/dist/transports/index.d.ts +0 -5
- package/dist/transports/index.d.ts.map +0 -1
- package/dist/types/core.d.ts +0 -333
- package/dist/types/core.d.ts.map +0 -1
- package/dist/types/handlers.d.ts +0 -85
- package/dist/types/handlers.d.ts.map +0 -1
- package/dist/types/hooks.d.ts +0 -36
- package/dist/types/hooks.d.ts.map +0 -1
- package/dist/types/index.d.ts +0 -11
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/serializers.d.ts +0 -25
- package/dist/types/serializers.d.ts.map +0 -1
- package/dist/types/transports.d.ts +0 -47
- package/dist/types/transports.d.ts.map +0 -1
- package/dist/utils/adapter.d.ts +0 -48
- package/dist/utils/adapter.d.ts.map +0 -1
- package/dist/utils/ansi-colors.d.ts +0 -156
- package/dist/utils/ansi-colors.d.ts.map +0 -1
- package/dist/utils/environment-detector.d.ts +0 -45
- package/dist/utils/environment-detector.d.ts.map +0 -1
- package/dist/utils/environment.d.ts +0 -47
- package/dist/utils/environment.d.ts.map +0 -1
- package/dist/utils/formatting.d.ts +0 -58
- package/dist/utils/formatting.d.ts.map +0 -1
- package/dist/utils/index.d.ts +0 -8
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/opentui-detection.d.ts +0 -34
- package/dist/utils/opentui-detection.d.ts.map +0 -1
- package/dist/utils/output.d.ts +0 -46
- package/dist/utils/output.d.ts.map +0 -1
- package/dist/utils/stackTrace.d.ts +0 -6
- package/dist/utils/stackTrace.d.ts.map +0 -1
- package/dist/utils/timestamps.d.ts +0 -20
- package/dist/utils/timestamps.d.ts.map +0 -1
- package/dist/vite.svg +0 -1
- package/dist/writers/BufferWriter.d.ts +0 -96
- package/dist/writers/BufferWriter.d.ts.map +0 -1
- package/dist/writers/index.d.ts +0 -6
- package/dist/writers/index.d.ts.map +0 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI table — various column counts, auto-width, explicit columns.
|
|
3
|
+
* Run: bun playground/demo-table.ts
|
|
4
|
+
*/
|
|
5
|
+
import { Logger } from '../src/Logger.js';
|
|
6
|
+
|
|
7
|
+
const logger = new Logger();
|
|
8
|
+
|
|
9
|
+
function main() {
|
|
10
|
+
logger.header('Table Demos');
|
|
11
|
+
logger.divider();
|
|
12
|
+
logger.blank();
|
|
13
|
+
|
|
14
|
+
// ── Auto-detect columns from data ─────────────────
|
|
15
|
+
logger.info('1. Auto-detected columns');
|
|
16
|
+
logger.blank();
|
|
17
|
+
|
|
18
|
+
logger.cliTable([
|
|
19
|
+
{ name: 'Gemini SDK', version: '1.0.0', status: 'active' },
|
|
20
|
+
{ name: 'Groq SDK', version: '0.8.2', status: 'active' },
|
|
21
|
+
{ name: 'OpenRouter', version: '2.1.0', status: 'fallback' },
|
|
22
|
+
]);
|
|
23
|
+
logger.blank();
|
|
24
|
+
|
|
25
|
+
// ── Explicit columns via options ──────────────────
|
|
26
|
+
logger.info('2. Explicit columns (subset)');
|
|
27
|
+
logger.blank();
|
|
28
|
+
|
|
29
|
+
logger.cliTable(
|
|
30
|
+
[
|
|
31
|
+
{ id: 1, name: 'Alice', email: 'alice@example.com', role: 'admin' },
|
|
32
|
+
{ id: 2, name: 'Bob', email: 'bob@example.com', role: 'user' },
|
|
33
|
+
{ id: 3, name: 'Charlie', email: 'charlie@example.com', role: 'user' },
|
|
34
|
+
],
|
|
35
|
+
{ columns: ['name', 'role'] },
|
|
36
|
+
);
|
|
37
|
+
logger.blank();
|
|
38
|
+
|
|
39
|
+
// ── Various value lengths ─────────────────────────
|
|
40
|
+
logger.info('3. Mixed value lengths');
|
|
41
|
+
logger.blank();
|
|
42
|
+
|
|
43
|
+
logger.cliTable([
|
|
44
|
+
{ key: 'a', value: 'short' },
|
|
45
|
+
{ key: 'longer-key-name', value: 'This is a much longer value to test column sizing' },
|
|
46
|
+
{ key: 'b', value: '42' },
|
|
47
|
+
]);
|
|
48
|
+
logger.blank();
|
|
49
|
+
|
|
50
|
+
// ── Numeric and boolean values ────────────────────
|
|
51
|
+
logger.info('4. Numeric and boolean values');
|
|
52
|
+
logger.blank();
|
|
53
|
+
|
|
54
|
+
logger.cliTable([
|
|
55
|
+
{ metric: 'Requests/sec', value: 12500, healthy: true },
|
|
56
|
+
{ metric: 'Avg latency', value: 23.4, healthy: true },
|
|
57
|
+
{ metric: 'Error rate', value: 0.02, healthy: true },
|
|
58
|
+
{ metric: 'Memory (MB)', value: 512, healthy: false },
|
|
59
|
+
]);
|
|
60
|
+
logger.blank();
|
|
61
|
+
|
|
62
|
+
// ── Many columns ──────────────────────────────────
|
|
63
|
+
logger.info('5. Many columns');
|
|
64
|
+
logger.blank();
|
|
65
|
+
|
|
66
|
+
logger.cliTable([
|
|
67
|
+
{ col1: 'A', col2: 'B', col3: 'C', col4: 'D', col5: 'E', col6: 'F' },
|
|
68
|
+
{ col1: '1', col2: '2', col3: '3', col4: '4', col5: '5', col6: '6' },
|
|
69
|
+
]);
|
|
70
|
+
logger.blank();
|
|
71
|
+
|
|
72
|
+
// ── Single row ────────────────────────────────────
|
|
73
|
+
logger.info('6. Single row');
|
|
74
|
+
logger.blank();
|
|
75
|
+
|
|
76
|
+
logger.cliTable([{ provider: 'Gemini SDK', model: 'gemini-2.5-flash', latency: '120ms' }]);
|
|
77
|
+
logger.blank();
|
|
78
|
+
|
|
79
|
+
logger.divider();
|
|
80
|
+
logger.info('Table demos complete.');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
main();
|
package/src/Logger.ts
CHANGED
|
@@ -36,7 +36,7 @@ import { TransportManager } from './transports/index.js';
|
|
|
36
36
|
import { parseStackTrace } from './utils/stackTrace.js';
|
|
37
37
|
import { formatTimestamp } from './utils/timestamps.js';
|
|
38
38
|
import { createStyledOutput, setupThemeChangeListener } from './utils/output.js';
|
|
39
|
-
import { getEnvironment, getColorCapability } from './utils/environment-detector.js';
|
|
39
|
+
import { getEnvironment, getColorCapability, isRunningInTerminal } from './utils/environment-detector.js';
|
|
40
40
|
import { formatBadge } from './terminal/formatter.js';
|
|
41
41
|
|
|
42
42
|
// Styling imports
|
|
@@ -62,7 +62,17 @@ import { ExportLogHandler } from './handlers/index.js';
|
|
|
62
62
|
import { createDefaultCLI, type CommandProcessor } from './cli/index.js';
|
|
63
63
|
|
|
64
64
|
// Constants
|
|
65
|
-
import { DEFAULT_CONFIG } from './constants.js';
|
|
65
|
+
import { DEFAULT_CONFIG, CLI_LEVEL_MAP } from './constants.js';
|
|
66
|
+
|
|
67
|
+
// CLI Primitives
|
|
68
|
+
import type { CLILogLevel, ISpinnerHandle, IBoxOptions, ITableOptions } from './types/index.js';
|
|
69
|
+
import { renderStep } from './cli-primitives/step.js';
|
|
70
|
+
import { renderHeader } from './cli-primitives/header.js';
|
|
71
|
+
import { renderDivider } from './cli-primitives/divider.js';
|
|
72
|
+
import { renderBox } from './cli-primitives/box.js';
|
|
73
|
+
import { renderTable } from './cli-primitives/cli-table.js';
|
|
74
|
+
import { SpinnerManager, NoopSpinner } from './cli-primitives/spinner.js';
|
|
75
|
+
import { ServerFallback } from './cli-primitives/server-fallback.js';
|
|
66
76
|
|
|
67
77
|
/**
|
|
68
78
|
* Estilos del tema activo actual
|
|
@@ -116,6 +126,11 @@ export class Logger {
|
|
|
116
126
|
private hookManager: HookManager;
|
|
117
127
|
private transportManager?: TransportManager;
|
|
118
128
|
|
|
129
|
+
/** Whether CLI primitives (step, box, header, etc.) should be shown @since 5.0.0 */
|
|
130
|
+
private _showPrimitives = true;
|
|
131
|
+
/** Server-mode fallback for non-TTY environments @since 5.0.0 */
|
|
132
|
+
private _serverFallback?: ServerFallback;
|
|
133
|
+
|
|
119
134
|
/**
|
|
120
135
|
* Crea una nueva instancia del Logger
|
|
121
136
|
*
|
|
@@ -1292,21 +1307,22 @@ export class Logger {
|
|
|
1292
1307
|
|
|
1293
1308
|
/**
|
|
1294
1309
|
* Finaliza un temporizador y muestra el tiempo transcurrido
|
|
1295
|
-
*
|
|
1310
|
+
*
|
|
1296
1311
|
* @param {string} label - Etiqueta del temporizador a finalizar
|
|
1297
|
-
*
|
|
1312
|
+
* @returns {number} Elapsed milliseconds, or -1 if timer not found
|
|
1313
|
+
*
|
|
1298
1314
|
* @example
|
|
1299
1315
|
* logger.time('consulta-db');
|
|
1300
1316
|
* await consultarBaseDatos();
|
|
1301
|
-
* logger.timeEnd('consulta-db'); // ⏱️ Timer ended: consulta-db - 234.56ms
|
|
1302
|
-
*
|
|
1317
|
+
* const elapsed = logger.timeEnd('consulta-db'); // ⏱️ Timer ended: consulta-db - 234.56ms
|
|
1318
|
+
*
|
|
1303
1319
|
* @since 0.3.0
|
|
1304
1320
|
*/
|
|
1305
|
-
timeEnd(label: string):
|
|
1321
|
+
timeEnd(label: string): number {
|
|
1306
1322
|
const timer = this.timers.get(label);
|
|
1307
1323
|
if (!timer) {
|
|
1308
1324
|
this.warn(`Timer '${label}' does not exist`);
|
|
1309
|
-
return;
|
|
1325
|
+
return -1;
|
|
1310
1326
|
}
|
|
1311
1327
|
|
|
1312
1328
|
const elapsed = performance.now() - timer.startTime;
|
|
@@ -1314,6 +1330,8 @@ export class Logger {
|
|
|
1314
1330
|
|
|
1315
1331
|
const timerStyle = StylePresets.success().build();
|
|
1316
1332
|
console.log(`%c⏱️ Timer ended: ${label} - ${elapsed.toFixed(2)}ms`, timerStyle);
|
|
1333
|
+
|
|
1334
|
+
return elapsed;
|
|
1317
1335
|
}
|
|
1318
1336
|
|
|
1319
1337
|
// ===== ADVANCED VISUAL FEATURES =====
|
|
@@ -1462,6 +1480,193 @@ export class Logger {
|
|
|
1462
1480
|
}
|
|
1463
1481
|
}
|
|
1464
1482
|
|
|
1483
|
+
// ===== CLI PRIMITIVES (v5.0) =====
|
|
1484
|
+
|
|
1485
|
+
/**
|
|
1486
|
+
* Displays a step progress indicator in the terminal
|
|
1487
|
+
*
|
|
1488
|
+
* @param {number} current - Current step number
|
|
1489
|
+
* @param {number} total - Total number of steps
|
|
1490
|
+
* @param {string} message - Step description
|
|
1491
|
+
*
|
|
1492
|
+
* @example
|
|
1493
|
+
* logger.step(1, 5, 'Analyzing repository...');
|
|
1494
|
+
* logger.step(2, 5, 'Generating commit message...');
|
|
1495
|
+
*
|
|
1496
|
+
* @since 5.0.0
|
|
1497
|
+
*/
|
|
1498
|
+
step(current: number, total: number, message: string): void {
|
|
1499
|
+
if (!this._showPrimitives) return;
|
|
1500
|
+
if (!isRunningInTerminal()) {
|
|
1501
|
+
this.getServerFallback().step(current, total, message);
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
const colorCap = getColorCapability();
|
|
1505
|
+
const output = renderStep(current, total, message, colorCap);
|
|
1506
|
+
process.stderr.write(output + '\n');
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
/**
|
|
1510
|
+
* Displays a styled header with optional subtitle
|
|
1511
|
+
*
|
|
1512
|
+
* @param {string} title - Main title text
|
|
1513
|
+
* @param {string} subtitle - Optional subtitle (rendered dimmed)
|
|
1514
|
+
*
|
|
1515
|
+
* @example
|
|
1516
|
+
* logger.header('Commit Wizard', 'v2.0.0');
|
|
1517
|
+
*
|
|
1518
|
+
* @since 5.0.0
|
|
1519
|
+
*/
|
|
1520
|
+
header(title: string, subtitle?: string): void {
|
|
1521
|
+
if (!this._showPrimitives) return;
|
|
1522
|
+
if (!isRunningInTerminal()) {
|
|
1523
|
+
this.getServerFallback().header(title, subtitle);
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
const output = renderHeader(title, subtitle);
|
|
1527
|
+
process.stderr.write(output + '\n');
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
/**
|
|
1531
|
+
* Displays a horizontal divider line
|
|
1532
|
+
*
|
|
1533
|
+
* @example
|
|
1534
|
+
* logger.divider();
|
|
1535
|
+
*
|
|
1536
|
+
* @since 5.0.0
|
|
1537
|
+
*/
|
|
1538
|
+
divider(): void {
|
|
1539
|
+
if (!this._showPrimitives) return;
|
|
1540
|
+
if (!isRunningInTerminal()) {
|
|
1541
|
+
this.getServerFallback().divider();
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
const output = renderDivider();
|
|
1545
|
+
process.stderr.write(output + '\n');
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
/**
|
|
1549
|
+
* Outputs a blank line
|
|
1550
|
+
*
|
|
1551
|
+
* @example
|
|
1552
|
+
* logger.blank();
|
|
1553
|
+
*
|
|
1554
|
+
* @since 5.0.0
|
|
1555
|
+
*/
|
|
1556
|
+
blank(): void {
|
|
1557
|
+
if (!this._showPrimitives) return;
|
|
1558
|
+
if (!isRunningInTerminal()) {
|
|
1559
|
+
this.getServerFallback().blank();
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
process.stderr.write('\n');
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
/**
|
|
1566
|
+
* Renders content inside a bordered box
|
|
1567
|
+
*
|
|
1568
|
+
* @param {string} content - Content string (may contain newlines)
|
|
1569
|
+
* @param {IBoxOptions} options - Box rendering options
|
|
1570
|
+
*
|
|
1571
|
+
* @example
|
|
1572
|
+
* logger.box('3 commits generated\nProvider: Groq', { title: 'Done', borderColor: '#00ff00' });
|
|
1573
|
+
*
|
|
1574
|
+
* @since 5.0.0
|
|
1575
|
+
*/
|
|
1576
|
+
box(content: string, options?: IBoxOptions): void {
|
|
1577
|
+
if (!this._showPrimitives) return;
|
|
1578
|
+
if (!isRunningInTerminal()) {
|
|
1579
|
+
this.getServerFallback().box(content, options);
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1582
|
+
const colorCap = getColorCapability();
|
|
1583
|
+
const output = renderBox(content, options, colorCap);
|
|
1584
|
+
process.stderr.write(output + '\n');
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
/**
|
|
1588
|
+
* Renders an array of objects as a formatted ASCII table.
|
|
1589
|
+
* Note: This is distinct from the existing table() method which uses console.table.
|
|
1590
|
+
*
|
|
1591
|
+
* @param {Record<string, unknown>[]} rows - Array of row objects
|
|
1592
|
+
* @param {ITableOptions} options - Table rendering options
|
|
1593
|
+
*
|
|
1594
|
+
* @example
|
|
1595
|
+
* logger.cliTable([
|
|
1596
|
+
* { provider: 'Groq', status: 'Available', model: 'llama-3.3-70b' },
|
|
1597
|
+
* { provider: 'Gemini', status: 'Configured', model: 'gemini-2.5-flash' },
|
|
1598
|
+
* ]);
|
|
1599
|
+
*
|
|
1600
|
+
* @since 5.0.0
|
|
1601
|
+
*/
|
|
1602
|
+
cliTable(rows: Record<string, unknown>[], options?: ITableOptions): void {
|
|
1603
|
+
if (!this._showPrimitives) return;
|
|
1604
|
+
if (!isRunningInTerminal()) {
|
|
1605
|
+
this.getServerFallback().cliTable(rows, options);
|
|
1606
|
+
return;
|
|
1607
|
+
}
|
|
1608
|
+
const colorCap = getColorCapability();
|
|
1609
|
+
const output = renderTable(rows, options, colorCap);
|
|
1610
|
+
process.stderr.write(output + '\n');
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
/**
|
|
1614
|
+
* Creates a spinner handle for showing progress during async operations.
|
|
1615
|
+
* Returns a NoopSpinner in non-TTY environments.
|
|
1616
|
+
*
|
|
1617
|
+
* @param {string} message - Initial spinner text
|
|
1618
|
+
* @returns {ISpinnerHandle} Spinner controller
|
|
1619
|
+
*
|
|
1620
|
+
* @example
|
|
1621
|
+
* const s = logger.spinner('Analyzing repository...');
|
|
1622
|
+
* s.start();
|
|
1623
|
+
* await analyzeRepo();
|
|
1624
|
+
* s.succeed('Analysis complete (1.2s)');
|
|
1625
|
+
*
|
|
1626
|
+
* @since 5.0.0
|
|
1627
|
+
*/
|
|
1628
|
+
spinner(message: string): ISpinnerHandle {
|
|
1629
|
+
if (!isRunningInTerminal() || this.config.outputMode === 'silent') {
|
|
1630
|
+
return new NoopSpinner(message, this);
|
|
1631
|
+
}
|
|
1632
|
+
return new SpinnerManager(message, this.config, this);
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
/**
|
|
1636
|
+
* Sets the CLI verbosity level, controlling both log verbosity and primitive visibility
|
|
1637
|
+
*
|
|
1638
|
+
* @param {CLILogLevel} level - CLI log level
|
|
1639
|
+
*
|
|
1640
|
+
* @example
|
|
1641
|
+
* logger.setCLILevel('quiet'); // Only errors, no CLI primitives
|
|
1642
|
+
* logger.setCLILevel('verbose'); // Debug logs + all CLI primitives
|
|
1643
|
+
*
|
|
1644
|
+
* @since 5.0.0
|
|
1645
|
+
*/
|
|
1646
|
+
setCLILevel(level: CLILogLevel): void {
|
|
1647
|
+
const mapping = CLI_LEVEL_MAP[level];
|
|
1648
|
+
this.setVerbosity(mapping.verbosity);
|
|
1649
|
+
this._showPrimitives = mapping.showPrimitives;
|
|
1650
|
+
this.config.cliLevel = level;
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
/**
|
|
1654
|
+
* Returns the current CLI log level
|
|
1655
|
+
* @returns {CLILogLevel} Current CLI log level
|
|
1656
|
+
* @since 5.0.0
|
|
1657
|
+
*/
|
|
1658
|
+
get cliLevel(): CLILogLevel {
|
|
1659
|
+
return this.config.cliLevel ?? 'normal';
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
/** Lazily creates the server fallback instance @private */
|
|
1663
|
+
private getServerFallback(): ServerFallback {
|
|
1664
|
+
if (!this._serverFallback) {
|
|
1665
|
+
this._serverFallback = new ServerFallback(this);
|
|
1666
|
+
}
|
|
1667
|
+
return this._serverFallback;
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1465
1670
|
// ===== OUTPUT WRITER SYSTEM =====
|
|
1466
1671
|
|
|
1467
1672
|
/**
|
package/src/ScopedLogger.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Logger } from './Logger.js';
|
|
2
|
-
import type { TimerEntry, Bindings } from './types/index.js';
|
|
2
|
+
import type { TimerEntry, Bindings, ISpinnerHandle, IBoxOptions, ITableOptions, CLILogLevel } from './types/index.js';
|
|
3
3
|
|
|
4
4
|
export class ScopedLogger {
|
|
5
5
|
private readonly parent: Logger;
|
|
@@ -105,6 +105,48 @@ export class ScopedLogger {
|
|
|
105
105
|
console.trace(`[${this.scopeName}]`);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
// ===== CLI PRIMITIVES (v5.0 delegation) =====
|
|
109
|
+
|
|
110
|
+
/** @see Logger.step */
|
|
111
|
+
step(current: number, total: number, message: string): void {
|
|
112
|
+
this.parent.step(current, total, message);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** @see Logger.header */
|
|
116
|
+
header(title: string, subtitle?: string): void {
|
|
117
|
+
this.parent.header(title, subtitle);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** @see Logger.divider */
|
|
121
|
+
divider(): void {
|
|
122
|
+
this.parent.divider();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** @see Logger.blank */
|
|
126
|
+
blank(): void {
|
|
127
|
+
this.parent.blank();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** @see Logger.box */
|
|
131
|
+
box(content: string, options?: IBoxOptions): void {
|
|
132
|
+
this.parent.box(content, options);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** @see Logger.cliTable */
|
|
136
|
+
cliTable(rows: Record<string, unknown>[], options?: ITableOptions): void {
|
|
137
|
+
this.parent.cliTable(rows, options);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** @see Logger.spinner */
|
|
141
|
+
spinner(message: string): ISpinnerHandle {
|
|
142
|
+
return this.parent.spinner(message);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** @see Logger.setCLILevel */
|
|
146
|
+
setCLILevel(level: CLILogLevel): void {
|
|
147
|
+
this.parent.setCLILevel(level);
|
|
148
|
+
}
|
|
149
|
+
|
|
108
150
|
_pushContext(context: string): void {
|
|
109
151
|
this.contextStack.push(context);
|
|
110
152
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview CLI-only entry point for @mks2508/better-logger/cli
|
|
3
|
+
* Exports only CLI primitives for minimal bundle size in CLI tools.
|
|
4
|
+
* @version 5.0.0
|
|
5
|
+
* @since 5.0.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { renderStep } from './cli-primitives/step.js';
|
|
9
|
+
export { renderHeader } from './cli-primitives/header.js';
|
|
10
|
+
export { renderDivider } from './cli-primitives/divider.js';
|
|
11
|
+
export { renderBox } from './cli-primitives/box.js';
|
|
12
|
+
export { renderTable } from './cli-primitives/cli-table.js';
|
|
13
|
+
export { SpinnerManager, NoopSpinner } from './cli-primitives/spinner.js';
|
|
14
|
+
export { ServerFallback } from './cli-primitives/server-fallback.js';
|
|
15
|
+
|
|
16
|
+
export type {
|
|
17
|
+
CLILogLevel,
|
|
18
|
+
ISpinnerHandle,
|
|
19
|
+
IBoxOptions,
|
|
20
|
+
ITableOptions,
|
|
21
|
+
} from './types/index.js';
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Box renderer for CLI primitives
|
|
3
|
+
* @since 5.0.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { IBoxOptions } from '../types/core.js';
|
|
7
|
+
import type { ColorCapability } from '../terminal/color-converter.js';
|
|
8
|
+
import { getANSIForeground, ANSI } from '../terminal/color-converter.js';
|
|
9
|
+
import { getTerminalWidth } from '../utils/environment-detector.js';
|
|
10
|
+
import { stripAnsi, getVisibleLength } from '../terminal/formatter.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Border character sets for different styles
|
|
14
|
+
*/
|
|
15
|
+
const BORDER_CHARS = {
|
|
16
|
+
single: { tl: '\u250c', tr: '\u2510', bl: '\u2514', br: '\u2518', h: '\u2500', v: '\u2502' },
|
|
17
|
+
rounded: { tl: '\u256d', tr: '\u256e', bl: '\u2570', br: '\u256f', h: '\u2500', v: '\u2502' },
|
|
18
|
+
double: { tl: '\u2554', tr: '\u2557', bl: '\u255a', br: '\u255d', h: '\u2550', v: '\u2551' },
|
|
19
|
+
bold: { tl: '\u250f', tr: '\u2513', bl: '\u2517', br: '\u251b', h: '\u2501', v: '\u2503' },
|
|
20
|
+
} as const;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Renders content inside a bordered box
|
|
24
|
+
* @param content - Content string (may contain newlines)
|
|
25
|
+
* @param options - Box rendering options
|
|
26
|
+
* @param colorCap - Terminal color capability
|
|
27
|
+
* @returns Formatted box string with border
|
|
28
|
+
*/
|
|
29
|
+
export function renderBox(content: string, options: IBoxOptions = {}, colorCap: ColorCapability = 'full'): string {
|
|
30
|
+
const {
|
|
31
|
+
title,
|
|
32
|
+
borderColor,
|
|
33
|
+
borderStyle = 'rounded',
|
|
34
|
+
padding = 0,
|
|
35
|
+
} = options;
|
|
36
|
+
|
|
37
|
+
const chars = BORDER_CHARS[borderStyle] ?? BORDER_CHARS.rounded;
|
|
38
|
+
const lines = content.split('\n');
|
|
39
|
+
const maxTermWidth = Math.min(getTerminalWidth() - 4, 80);
|
|
40
|
+
|
|
41
|
+
// Calculate content width from visible text
|
|
42
|
+
const contentWidths = lines.map(l => getVisibleLength(l));
|
|
43
|
+
const titleWidth = title ? stripAnsi(title).length + 2 : 0; // +2 for spaces around title
|
|
44
|
+
const maxContentWidth = Math.max(...contentWidths, titleWidth);
|
|
45
|
+
const innerWidth = Math.min(maxContentWidth + 2, maxTermWidth); // +2 for horizontal padding
|
|
46
|
+
|
|
47
|
+
// Color wrapper for border chars
|
|
48
|
+
const bc = borderColor && colorCap !== 'none'
|
|
49
|
+
? getANSIForeground(borderColor, colorCap)
|
|
50
|
+
: '';
|
|
51
|
+
const reset = bc ? ANSI.reset : '';
|
|
52
|
+
|
|
53
|
+
const wrap = (char: string) => `${bc}${char}${reset}`;
|
|
54
|
+
|
|
55
|
+
// Build top border (with optional title)
|
|
56
|
+
let topBorder: string;
|
|
57
|
+
if (title) {
|
|
58
|
+
const titleStr = ` ${title} `;
|
|
59
|
+
const afterTitle = innerWidth - stripAnsi(titleStr).length;
|
|
60
|
+
topBorder = ` ${wrap(chars.tl)}${wrap(chars.h)}${wrap(titleStr)}${wrap(chars.h.repeat(Math.max(0, afterTitle - 1)))}${wrap(chars.tr)}`;
|
|
61
|
+
} else {
|
|
62
|
+
topBorder = ` ${wrap(chars.tl)}${wrap(chars.h.repeat(innerWidth))}${wrap(chars.tr)}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Build bottom border
|
|
66
|
+
const bottomBorder = ` ${wrap(chars.bl)}${wrap(chars.h.repeat(innerWidth))}${wrap(chars.br)}`;
|
|
67
|
+
|
|
68
|
+
// Build padding lines
|
|
69
|
+
const emptyLine = ` ${wrap(chars.v)}${' '.repeat(innerWidth)}${wrap(chars.v)}`;
|
|
70
|
+
const paddingLines = padding > 0 ? Array(padding).fill(emptyLine) : [];
|
|
71
|
+
|
|
72
|
+
// Build content lines
|
|
73
|
+
const contentLines = lines.map(line => {
|
|
74
|
+
const visible = getVisibleLength(line);
|
|
75
|
+
const pad = innerWidth - visible - 1; // -1 for left space
|
|
76
|
+
return ` ${wrap(chars.v)} ${line}${' '.repeat(Math.max(0, pad))}${wrap(chars.v)}`;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return [
|
|
80
|
+
topBorder,
|
|
81
|
+
...paddingLines,
|
|
82
|
+
...contentLines,
|
|
83
|
+
...paddingLines,
|
|
84
|
+
bottomBorder,
|
|
85
|
+
].join('\n');
|
|
86
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview CLI table renderer for CLI primitives
|
|
3
|
+
* @since 5.0.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ITableOptions } from '../types/core.js';
|
|
7
|
+
import type { ColorCapability } from '../terminal/color-converter.js';
|
|
8
|
+
import { getANSIForeground, ANSI } from '../terminal/color-converter.js';
|
|
9
|
+
import { getVisibleLength, padToWidth } from '../terminal/formatter.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Renders an array of objects as a formatted ASCII table
|
|
13
|
+
* @param rows - Array of row objects
|
|
14
|
+
* @param options - Table rendering options
|
|
15
|
+
* @param colorCap - Terminal color capability
|
|
16
|
+
* @returns Formatted table string
|
|
17
|
+
*/
|
|
18
|
+
export function renderTable(
|
|
19
|
+
rows: Record<string, unknown>[],
|
|
20
|
+
options: ITableOptions = {},
|
|
21
|
+
colorCap: ColorCapability = 'full'
|
|
22
|
+
): string {
|
|
23
|
+
if (rows.length === 0) return '';
|
|
24
|
+
|
|
25
|
+
// Determine columns from options or first row's keys
|
|
26
|
+
const columns = options.columns ?? Object.keys(rows[0]!);
|
|
27
|
+
const headers = options.head ?? columns;
|
|
28
|
+
|
|
29
|
+
// Calculate column widths (max of header and all values)
|
|
30
|
+
const colWidths = columns.map((col, i) => {
|
|
31
|
+
const headerLen = getVisibleLength(headers[i] ?? col);
|
|
32
|
+
const maxValueLen = rows.reduce((max, row) => {
|
|
33
|
+
const val = String(row[col] ?? '');
|
|
34
|
+
return Math.max(max, getVisibleLength(val));
|
|
35
|
+
}, 0);
|
|
36
|
+
return Math.max(headerLen, maxValueLen) + 2; // +2 for padding
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Color helpers
|
|
40
|
+
const cyan = colorCap !== 'none' ? getANSIForeground('#00bcd4', colorCap) : '';
|
|
41
|
+
const dim = colorCap !== 'none' ? ANSI.dim : '';
|
|
42
|
+
const reset = colorCap !== 'none' ? ANSI.reset : '';
|
|
43
|
+
|
|
44
|
+
// Build header row
|
|
45
|
+
const headerRow = ' ' + columns.map((col, i) => {
|
|
46
|
+
const label = headers[i] ?? col;
|
|
47
|
+
return cyan + ANSI.bold + padToWidth(` ${label}`, colWidths[i]!) + reset;
|
|
48
|
+
}).join('');
|
|
49
|
+
|
|
50
|
+
// Build separator
|
|
51
|
+
const separator = ' ' + dim + colWidths.map(w => '\u2500'.repeat(w)).join('\u2500') + reset;
|
|
52
|
+
|
|
53
|
+
// Build data rows
|
|
54
|
+
const dataRows = rows.map(row => {
|
|
55
|
+
return ' ' + columns.map((col, i) => {
|
|
56
|
+
const val = String(row[col] ?? '');
|
|
57
|
+
return padToWidth(` ${val}`, colWidths[i]!);
|
|
58
|
+
}).join('');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return [headerRow, separator, ...dataRows].join('\n');
|
|
62
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Divider renderer for CLI primitives
|
|
3
|
+
* @since 5.0.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ANSI } from '../terminal/color-converter.js';
|
|
7
|
+
import { getTerminalWidth } from '../utils/environment-detector.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Renders a horizontal divider line
|
|
11
|
+
* @param width - Optional explicit width (defaults to terminal width capped at 60)
|
|
12
|
+
* @returns Formatted divider string
|
|
13
|
+
*/
|
|
14
|
+
export function renderDivider(width?: number): string {
|
|
15
|
+
const w = width ?? Math.min(getTerminalWidth() - 4, 60);
|
|
16
|
+
return ANSI.dim + ' ' + '\u2500'.repeat(w) + ANSI.reset;
|
|
17
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Header renderer for CLI primitives
|
|
3
|
+
* @since 5.0.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ANSI } from '../terminal/color-converter.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Renders a styled header with optional subtitle
|
|
10
|
+
* @param title - Main title text
|
|
11
|
+
* @param subtitle - Optional subtitle (rendered dimmed)
|
|
12
|
+
* @returns Formatted header string
|
|
13
|
+
*/
|
|
14
|
+
export function renderHeader(title: string, subtitle?: string): string {
|
|
15
|
+
const t = ANSI.bold + title + ANSI.reset;
|
|
16
|
+
const s = subtitle ? ANSI.dim + ` ${subtitle}` + ANSI.reset : '';
|
|
17
|
+
return ` ${t}${s}`;
|
|
18
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview CLI Primitives - Built-in terminal UI components for better-logger
|
|
3
|
+
* @version 5.0.0
|
|
4
|
+
* @since 5.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { renderStep } from './step.js';
|
|
8
|
+
export { renderHeader } from './header.js';
|
|
9
|
+
export { renderDivider } from './divider.js';
|
|
10
|
+
export { renderBox } from './box.js';
|
|
11
|
+
export { renderTable } from './cli-table.js';
|
|
12
|
+
export { SpinnerManager, NoopSpinner } from './spinner.js';
|
|
13
|
+
export { ServerFallback } from './server-fallback.js';
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Server/JSON fallback for CLI primitives in non-TTY environments
|
|
3
|
+
* @since 5.0.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { IBoxOptions, ITableOptions } from '../types/core.js';
|
|
7
|
+
import type { Logger } from '../Logger.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Server-mode fallback: outputs CLI primitives as plain logger calls
|
|
11
|
+
* when not running in an interactive terminal.
|
|
12
|
+
*
|
|
13
|
+
* @since 5.0.0
|
|
14
|
+
*/
|
|
15
|
+
export class ServerFallback {
|
|
16
|
+
private logger: Logger;
|
|
17
|
+
|
|
18
|
+
constructor(logger: Logger) {
|
|
19
|
+
this.logger = logger;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Render step as plain info log */
|
|
23
|
+
step(current: number, total: number, msg: string): void {
|
|
24
|
+
this.logger.info(`[${current}/${total}] ${msg}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Render header as plain info log */
|
|
28
|
+
header(title: string, subtitle?: string): void {
|
|
29
|
+
const text = subtitle ? `${title} ${subtitle}` : title;
|
|
30
|
+
this.logger.info(text);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Divider is a no-op in server mode */
|
|
34
|
+
divider(): void {
|
|
35
|
+
// No-op in server/JSON mode
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Blank line is a no-op in server mode */
|
|
39
|
+
blank(): void {
|
|
40
|
+
// No-op in server/JSON mode
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Render box content as plain info log */
|
|
44
|
+
box(content: string, _options?: IBoxOptions): void {
|
|
45
|
+
this.logger.info(content);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Render table rows as plain info logs */
|
|
49
|
+
cliTable(rows: Record<string, unknown>[], _options?: ITableOptions): void {
|
|
50
|
+
for (const row of rows) {
|
|
51
|
+
this.logger.info(JSON.stringify(row));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|