kailogger 1.0.0-dark.red → 1.0.2
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 +195 -53
- package/dist/core/Config.d.ts +15 -0
- package/dist/core/Config.js +44 -0
- package/dist/core/Logger.d.ts +75 -4
- package/dist/core/Logger.js +375 -47
- package/dist/core/Scope.d.ts +13 -0
- package/dist/core/Scope.js +36 -0
- package/dist/features/Chart.d.ts +15 -0
- package/dist/features/Chart.js +64 -0
- package/dist/features/Diff.d.ts +3 -0
- package/dist/features/Diff.js +30 -0
- package/dist/features/Encrypt.d.ts +10 -0
- package/dist/features/Encrypt.js +47 -0
- package/dist/features/Notify.d.ts +14 -0
- package/dist/features/Notify.js +70 -0
- package/dist/features/Screenshot.d.ts +10 -0
- package/dist/features/Screenshot.js +106 -0
- package/dist/features/Sound.d.ts +12 -0
- package/dist/features/Sound.js +116 -0
- package/dist/features/Timer.d.ts +6 -0
- package/dist/features/Timer.js +38 -0
- package/dist/features/Tree.d.ts +7 -0
- package/dist/features/Tree.js +25 -0
- package/dist/features/index.d.ts +8 -0
- package/dist/features/index.js +24 -0
- package/dist/icon/logo.png +0 -0
- package/dist/index.d.ts +13 -1
- package/dist/index.js +21 -1
- package/dist/sounds/error.wav +0 -0
- package/dist/sounds/notification.wav +0 -0
- package/dist/sounds/success.wav +0 -0
- package/dist/sounds/warning.wav +0 -0
- package/dist/styles/KaiChroma.d.ts +85 -0
- package/dist/styles/KaiChroma.js +407 -0
- package/dist/styles/gradients.d.ts +28 -0
- package/dist/styles/palettes.d.ts +21 -26
- package/dist/styles/palettes.js +167 -13
- package/dist/transports/ConsoleTransport.d.ts +9 -0
- package/dist/transports/ConsoleTransport.js +18 -0
- package/dist/transports/FileTransport.d.ts +16 -0
- package/dist/transports/FileTransport.js +84 -0
- package/dist/transports/WebhookTransport.d.ts +15 -0
- package/dist/transports/WebhookTransport.js +31 -0
- package/dist/transports/index.d.ts +3 -0
- package/dist/transports/index.js +19 -0
- package/dist/types/index.d.ts +16 -0
- package/dist/types/index.js +11 -0
- package/dist/utils/json.d.ts +3 -0
- package/dist/utils/json.js +33 -0
- package/dist/utils/prettyError.d.ts +3 -0
- package/dist/utils/prettyError.js +94 -0
- package/dist/utils/progress.d.ts +11 -0
- package/dist/utils/progress.js +43 -0
- package/dist/utils/prompt.d.ts +4 -0
- package/dist/utils/prompt.js +59 -0
- package/dist/utils/selection.d.ts +4 -0
- package/dist/utils/selection.js +156 -0
- package/dist/utils/spinner.d.ts +1 -1
- package/dist/utils/spinner.js +9 -13
- package/dist/utils/stripAnsi.d.ts +1 -0
- package/dist/utils/stripAnsi.js +7 -0
- package/dist/utils/table.d.ts +3 -0
- package/dist/utils/table.js +35 -0
- package/examples/demo.js +134 -0
- package/examples/demo.ts +88 -25
- package/package.json +20 -6
- package/scripts/copy-assets.js +37 -0
- package/src/core/Config.ts +44 -0
- package/src/core/Logger.ts +427 -51
- package/src/core/Scope.ts +35 -0
- package/src/features/Chart.ts +81 -0
- package/src/features/Diff.ts +25 -0
- package/src/features/Encrypt.ts +47 -0
- package/src/features/Notify.ts +39 -0
- package/src/features/Screenshot.ts +70 -0
- package/src/features/Sound.ts +92 -0
- package/src/features/Timer.ts +35 -0
- package/src/features/Tree.ts +25 -0
- package/src/features/index.ts +8 -0
- package/src/icon/logo.png +0 -0
- package/src/index.ts +13 -1
- package/src/sounds/error.wav +0 -0
- package/src/sounds/notification.wav +0 -0
- package/src/sounds/success.wav +0 -0
- package/src/sounds/warning.wav +0 -0
- package/src/styles/KaiChroma.ts +370 -0
- package/src/styles/palettes.ts +197 -14
- package/src/transports/ConsoleTransport.ts +19 -0
- package/src/transports/FileTransport.ts +55 -0
- package/src/transports/WebhookTransport.ts +37 -0
- package/src/transports/index.ts +3 -0
- package/src/types/cli-highlight.d.ts +3 -0
- package/src/types/index.ts +23 -0
- package/src/utils/json.ts +33 -0
- package/src/utils/prettyError.ts +65 -0
- package/src/utils/progress.ts +56 -0
- package/src/utils/prompt.ts +27 -0
- package/src/utils/selection.ts +136 -0
- package/src/utils/spinner.ts +11 -7
- package/src/utils/stripAnsi.ts +6 -0
- package/src/utils/table.ts +38 -0
- package/src/styles/gradients.ts +0 -22
package/src/core/Logger.ts
CHANGED
|
@@ -1,80 +1,456 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
1
|
+
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import { paint, palettes, ThemeName } from '../styles/palettes';
|
|
4
|
+
import { KaiChroma } from '../styles/KaiChroma';
|
|
3
5
|
import { KaiSpinner } from '../utils/spinner';
|
|
4
|
-
import {
|
|
6
|
+
import { KaiProgress } from '../utils/progress';
|
|
7
|
+
import { KaiTable } from '../utils/table';
|
|
8
|
+
import { KaiPrompt } from '../utils/prompt';
|
|
9
|
+
import { KaiSelection } from '../utils/selection';
|
|
10
|
+
import { KaiJson } from '../utils/json';
|
|
11
|
+
import { PrettyError } from '../utils/prettyError';
|
|
12
|
+
import { stripAnsi } from '../utils/stripAnsi';
|
|
13
|
+
import { config } from './Config';
|
|
14
|
+
import { ScopedLogger } from './Scope';
|
|
15
|
+
import { KaiTimer } from '../features/Timer';
|
|
16
|
+
import { KaiTree } from '../features/Tree';
|
|
17
|
+
import { KaiDiff } from '../features/Diff';
|
|
18
|
+
import { KaiChart } from '../features/Chart';
|
|
19
|
+
import { KaiNotify } from '../features/Notify';
|
|
20
|
+
import { KaiScreenshot } from '../features/Screenshot';
|
|
21
|
+
import { KaiEncrypt } from '../features/Encrypt';
|
|
22
|
+
import { KaiSound } from '../features/Sound';
|
|
23
|
+
import { LogLevel, LOG_LEVEL_PRIORITY, Transport, KaiConfig } from '../types';
|
|
24
|
+
import { FileTransport, FileTransportOptions } from '../transports/FileTransport';
|
|
25
|
+
import { WebhookTransport, WebhookTransportOptions } from '../transports/WebhookTransport';
|
|
26
|
+
|
|
27
|
+
interface LoggerOptions {
|
|
28
|
+
theme?: ThemeName;
|
|
29
|
+
level?: LogLevel;
|
|
30
|
+
logFile?: string;
|
|
31
|
+
silent?: boolean;
|
|
32
|
+
timestamp?: 'ISO' | 'locale' | 'relative' | 'none';
|
|
33
|
+
}
|
|
5
34
|
|
|
6
|
-
type LogLevel = 'success' | 'error' | 'warning' | 'info' | 'debug';
|
|
7
35
|
export class KaiLogger {
|
|
8
36
|
private spinner = new KaiSpinner();
|
|
9
|
-
|
|
10
|
-
|
|
37
|
+
private timer = new KaiTimer();
|
|
38
|
+
private logFilePath?: string;
|
|
39
|
+
private buffer: string[] = [];
|
|
40
|
+
private startTime: number = Date.now();
|
|
41
|
+
|
|
42
|
+
public theme: ThemeName;
|
|
43
|
+
public level: LogLevel;
|
|
44
|
+
public silent: boolean;
|
|
45
|
+
public timestampFormat: 'ISO' | 'locale' | 'relative' | 'none';
|
|
46
|
+
|
|
47
|
+
constructor(options: LoggerOptions | ThemeName = 'zen') {
|
|
48
|
+
if (typeof options === 'string') {
|
|
49
|
+
this.theme = options;
|
|
50
|
+
this.level = 'debug';
|
|
51
|
+
this.silent = false;
|
|
52
|
+
this.timestampFormat = 'locale';
|
|
53
|
+
} else {
|
|
54
|
+
this.theme = options.theme || 'zen';
|
|
55
|
+
this.level = options.level || 'debug';
|
|
56
|
+
this.logFilePath = options.logFile;
|
|
57
|
+
this.silent = options.silent || false;
|
|
58
|
+
this.timestampFormat = options.timestamp || 'locale';
|
|
59
|
+
}
|
|
60
|
+
paint.setTheme(this.theme);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ========== CONFIGURATION ==========
|
|
64
|
+
|
|
65
|
+
public configure(options: Partial<LoggerOptions>): void {
|
|
66
|
+
if (options.theme) this.setTheme(options.theme);
|
|
67
|
+
if (options.level) this.level = options.level;
|
|
68
|
+
if (options.logFile) this.logFilePath = options.logFile;
|
|
69
|
+
if (options.silent !== undefined) this.silent = options.silent;
|
|
70
|
+
if (options.timestamp) this.timestampFormat = options.timestamp;
|
|
11
71
|
}
|
|
12
|
-
|
|
72
|
+
|
|
73
|
+
public setTheme(theme: ThemeName): void {
|
|
13
74
|
this.theme = theme;
|
|
14
75
|
paint.setTheme(theme);
|
|
15
76
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
77
|
+
|
|
78
|
+
public setLevel(level: LogLevel): void {
|
|
79
|
+
this.level = level;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
public setSilent(silent: boolean): void {
|
|
83
|
+
this.silent = silent;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ========== TRANSPORTS ==========
|
|
87
|
+
|
|
88
|
+
public addFileTransport(options: FileTransportOptions): void {
|
|
89
|
+
config.addTransport(new FileTransport(options));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public addWebhookTransport(options: WebhookTransportOptions): void {
|
|
93
|
+
config.addTransport(new WebhookTransport(options));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public removeTransport(name: string): void {
|
|
97
|
+
config.removeTransport(name);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ========== SCOPED LOGGERS ==========
|
|
101
|
+
|
|
102
|
+
public scope(name: string): ScopedLogger {
|
|
103
|
+
return new ScopedLogger(this, name);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ========== TIMESTAMPS ==========
|
|
107
|
+
|
|
108
|
+
private getTime(): string {
|
|
109
|
+
if (this.timestampFormat === 'none') return '';
|
|
110
|
+
|
|
111
|
+
const now = new Date();
|
|
112
|
+
|
|
113
|
+
switch (this.timestampFormat) {
|
|
114
|
+
case 'ISO':
|
|
115
|
+
return `[${now.toISOString()}]`;
|
|
116
|
+
case 'relative':
|
|
117
|
+
const elapsed = Date.now() - this.startTime;
|
|
118
|
+
const seconds = (elapsed / 1000).toFixed(1);
|
|
119
|
+
return `[+${seconds}s]`;
|
|
120
|
+
case 'locale':
|
|
121
|
+
default:
|
|
122
|
+
return `[${now.toLocaleTimeString()}]`;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ========== LEVEL CHECKING ==========
|
|
127
|
+
|
|
128
|
+
private shouldLog(level: LogLevel): boolean {
|
|
129
|
+
if (this.silent) return false;
|
|
130
|
+
return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[this.level];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ========== FILE & TRANSPORT LOGGING ==========
|
|
134
|
+
|
|
135
|
+
private logToFile(level: string, message: string): void {
|
|
136
|
+
if (this.logFilePath) {
|
|
137
|
+
const cleanMsg = stripAnsi(message);
|
|
138
|
+
const logLine = `${this.getTime()} [${level.toUpperCase()}] ${cleanMsg}\n`;
|
|
139
|
+
fs.appendFileSync(this.logFilePath, logLine);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const transports = config.get('transports') || [];
|
|
143
|
+
transports.forEach(t => t.log(level as LogLevel, message));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private addToBuffer(level: string, message: string): void {
|
|
147
|
+
this.buffer.push(`${this.getTime()} [${level.toUpperCase()}] ${message}`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
public getBuffer(): string[] {
|
|
151
|
+
return [...this.buffer];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
public clearBuffer(): void {
|
|
155
|
+
this.buffer = [];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ========== CORE LOGGING ==========
|
|
159
|
+
|
|
160
|
+
private print(level: LogLevel, label: string, message: string, ...args: any[]): void {
|
|
161
|
+
if (!this.shouldLog(level)) {
|
|
162
|
+
this.addToBuffer(level, message);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
this.stopSpinner();
|
|
167
|
+
// Accedemos safe a paint.colors[level]
|
|
168
|
+
const colors = (paint.colors as any)[level] as string[];
|
|
169
|
+
const time = this.timestampFormat !== 'none' ? KaiChroma.hex('#666666', this.getTime()) + ' ' : '';
|
|
170
|
+
const badge = paint.apply(` ${label.padEnd(7)} `, colors);
|
|
171
|
+
const output = `${time}${badge} ${message}`;
|
|
172
|
+
|
|
173
|
+
console.log(output, ...args);
|
|
174
|
+
|
|
175
|
+
const fileMsg = [message, ...args.map(a => typeof a === 'object' ? JSON.stringify(a) : a)].join(' ');
|
|
176
|
+
this.logToFile(level, fileMsg);
|
|
177
|
+
this.addToBuffer(level, fileMsg);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
public success(message: string, ...args: any[]): void {
|
|
181
|
+
this.print('success', '✔ SUCCESS', message, ...args);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
public error(message: string | Error, ...args: any[]): void {
|
|
185
|
+
if (!this.shouldLog('error')) return;
|
|
186
|
+
|
|
187
|
+
if (message instanceof Error) {
|
|
188
|
+
this.stopSpinner();
|
|
189
|
+
PrettyError.handle(message, paint.colors);
|
|
190
|
+
this.logToFile('error', message.toString());
|
|
191
|
+
this.addToBuffer('error', message.toString());
|
|
192
|
+
} else {
|
|
193
|
+
this.print('error', '✖ ERROR', message, ...args);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
public warning(message: string, ...args: any[]): void {
|
|
198
|
+
this.print('warning', '⚠ WARN', message, ...args);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
public info(message: string, ...args: any[]): void {
|
|
202
|
+
this.print('info', 'ℹ INFO', message, ...args);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
public debug(message: string, ...args: any[]): void {
|
|
206
|
+
this.print('debug', '⚙ DEBUG', message, ...args);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
public log(message: string): void {
|
|
210
|
+
if (this.silent) return;
|
|
211
|
+
this.stopSpinner();
|
|
212
|
+
const time = this.timestampFormat !== 'none' ? KaiChroma.hex('#666666', this.getTime()) + ' ' : '';
|
|
213
|
+
console.log(`${time}${message}`);
|
|
214
|
+
this.logToFile('log', message);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ========== CUSTOM BADGE ==========
|
|
218
|
+
|
|
219
|
+
public custom(badge: string, color: string, message: string, ...args: any[]): void {
|
|
220
|
+
if (this.silent) return;
|
|
221
|
+
this.stopSpinner();
|
|
222
|
+
const time = this.timestampFormat !== 'none' ? KaiChroma.hex('#666666', this.getTime()) + ' ' : '';
|
|
223
|
+
const styledBadge = KaiChroma.bgHex(color, ` ${badge.toUpperCase().padEnd(7)} `);
|
|
224
|
+
console.log(`${time}${styledBadge} ${message}`, ...args);
|
|
225
|
+
this.logToFile(badge, message);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ========== BOX ==========
|
|
229
|
+
|
|
230
|
+
public box(title: string, message: string): void {
|
|
231
|
+
if (this.silent) return;
|
|
232
|
+
this.stopSpinner();
|
|
233
|
+
const width = Math.max(message.length, title.length) + 4;
|
|
234
|
+
const border = paint.apply('─'.repeat(width), paint.colors.info);
|
|
235
|
+
const topLeft = paint.apply('╭', paint.colors.info);
|
|
236
|
+
const topRight = paint.apply('╮', paint.colors.info);
|
|
237
|
+
const bottomLeft = paint.apply('╰', paint.colors.info);
|
|
238
|
+
const bottomRight = paint.apply('╯', paint.colors.info);
|
|
239
|
+
const side = paint.apply('│', paint.colors.info);
|
|
240
|
+
|
|
241
|
+
console.log(`${topLeft}${border}${topRight}`);
|
|
242
|
+
console.log(`${side} ${paint.apply(title.padEnd(width - 2), paint.colors.success)} ${side}`);
|
|
243
|
+
console.log(`${side}${paint.apply('─'.repeat(width), paint.colors.debug)}${side}`);
|
|
244
|
+
console.log(`${side} ${message.padEnd(width - 2)} ${side}`);
|
|
245
|
+
console.log(`${bottomLeft}${border}${bottomRight}`);
|
|
246
|
+
|
|
247
|
+
this.logToFile('box', `${title}: ${message}`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ========== SPINNER ==========
|
|
251
|
+
|
|
252
|
+
public await(message: string): void {
|
|
253
|
+
if (this.silent) return;
|
|
254
|
+
this.spinner.start(message, paint.colors.info[0]);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
public stop(message?: string): void {
|
|
258
|
+
this.spinner.stop('✔', message, paint.colors.success[1]);
|
|
259
|
+
if (message) this.logToFile('stop', message);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private stopSpinner(): void {
|
|
263
|
+
this.spinner.stop();
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// ========== PROGRESS ==========
|
|
267
|
+
|
|
268
|
+
public createProgress(total: number, width?: number): KaiProgress {
|
|
269
|
+
return new KaiProgress(total, width, this.theme);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ========== TABLE ==========
|
|
273
|
+
|
|
274
|
+
public table(data: any[]): void {
|
|
275
|
+
if (this.silent) return;
|
|
276
|
+
this.stopSpinner();
|
|
277
|
+
KaiTable.print(data, paint.colors);
|
|
278
|
+
this.logToFile('table', JSON.stringify(data));
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ========== JSON ==========
|
|
282
|
+
|
|
283
|
+
public json(data: any): void {
|
|
284
|
+
if (this.silent) return;
|
|
285
|
+
this.stopSpinner();
|
|
286
|
+
KaiJson.print(data, paint.colors);
|
|
287
|
+
this.logToFile('json', JSON.stringify(data));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// ========== TREE ==========
|
|
291
|
+
|
|
292
|
+
public tree(data: any): void {
|
|
293
|
+
if (this.silent) return;
|
|
294
|
+
this.stopSpinner();
|
|
295
|
+
KaiTree.print(data, paint.colors);
|
|
296
|
+
this.logToFile('tree', JSON.stringify(data));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ========== DIFF ==========
|
|
300
|
+
|
|
301
|
+
public diff(before: any, after: any): void {
|
|
302
|
+
if (this.silent) return;
|
|
303
|
+
this.stopSpinner();
|
|
304
|
+
KaiDiff.print(before, after, paint.colors);
|
|
305
|
+
this.logToFile('diff', JSON.stringify({ before, after }));
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ========== TIMER ==========
|
|
309
|
+
|
|
310
|
+
public time(label: string): void {
|
|
311
|
+
this.timer.time(label);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
public timeEnd(label: string): number | null {
|
|
315
|
+
if (this.silent) return null;
|
|
316
|
+
return this.timer.timeEnd(label, paint.colors);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ========== PROMPTS ==========
|
|
320
|
+
|
|
321
|
+
public async ask(question: string): Promise<string> {
|
|
322
|
+
this.stopSpinner();
|
|
323
|
+
// Pasamos theme.info colors, asumimos que prompt maneja esto
|
|
324
|
+
const answer = await KaiPrompt.ask(question, paint.colors);
|
|
325
|
+
this.logToFile('ask', `${question} -> ${answer}`);
|
|
326
|
+
return answer;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
public async confirm(question: string): Promise<boolean> {
|
|
330
|
+
this.stopSpinner();
|
|
331
|
+
const answer = await KaiPrompt.confirm(question, paint.colors);
|
|
332
|
+
this.logToFile('confirm', `${question} -> ${answer}`);
|
|
333
|
+
return answer;
|
|
19
334
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
335
|
+
|
|
336
|
+
public async select(question: string, options: string[]): Promise<string> {
|
|
337
|
+
this.stopSpinner();
|
|
338
|
+
const answer = await KaiSelection.select(question, options, paint.colors);
|
|
339
|
+
this.logToFile('select', `${question} -> ${answer}`);
|
|
340
|
+
return answer;
|
|
25
341
|
}
|
|
26
|
-
|
|
342
|
+
|
|
343
|
+
public async multiselect(question: string, options: string[]): Promise<string[]> {
|
|
27
344
|
this.stopSpinner();
|
|
28
|
-
const
|
|
29
|
-
|
|
345
|
+
const answer = await KaiSelection.multiselect(question, options, paint.colors);
|
|
346
|
+
this.logToFile('multiselect', `${question} -> ${answer.join(', ')}`);
|
|
347
|
+
return answer;
|
|
30
348
|
}
|
|
31
|
-
|
|
349
|
+
|
|
350
|
+
// ========== CHARTS ==========
|
|
351
|
+
|
|
352
|
+
public chart(data: { label: string; value: number }[], options?: { width?: number; showValues?: boolean }): void {
|
|
353
|
+
if (this.silent) return;
|
|
32
354
|
this.stopSpinner();
|
|
33
|
-
|
|
34
|
-
console.log(`${this.getTime()} ${paint.apply(' ✖ ERROR ', colors)} ${message}`, ...args);
|
|
355
|
+
KaiChart.bar(data, paint.colors, options);
|
|
35
356
|
}
|
|
36
|
-
|
|
357
|
+
|
|
358
|
+
public sparkline(values: number[]): void {
|
|
359
|
+
if (this.silent) return;
|
|
37
360
|
this.stopSpinner();
|
|
38
|
-
|
|
39
|
-
console.log(`${this.getTime()} ${paint.apply(' ⚠ WARNING ', colors)} ${message}`, ...args);
|
|
361
|
+
KaiChart.sparkline(values, paint.colors);
|
|
40
362
|
}
|
|
41
|
-
|
|
363
|
+
|
|
364
|
+
public gauge(value: number, max: number, label?: string): void {
|
|
365
|
+
if (this.silent) return;
|
|
42
366
|
this.stopSpinner();
|
|
43
|
-
|
|
44
|
-
|
|
367
|
+
KaiChart.gauge(value, max, paint.colors, label);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// ========== NOTIFICATIONS ==========
|
|
371
|
+
|
|
372
|
+
public notify(message: string, title?: string): void {
|
|
373
|
+
KaiNotify.send({ message, title });
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
public notifySuccess(message: string): void {
|
|
377
|
+
KaiNotify.success(message);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
public notifyError(message: string): void {
|
|
381
|
+
KaiNotify.error(message);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// ========== SCREENSHOT ==========
|
|
385
|
+
|
|
386
|
+
public startCapture(): void {
|
|
387
|
+
KaiScreenshot.startCapture();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
public stopCapture(): string[] {
|
|
391
|
+
return KaiScreenshot.stopCapture();
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
public saveScreenshot(filename: string): void {
|
|
395
|
+
KaiScreenshot.save(filename);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
public saveScreenshotHtml(filename: string): void {
|
|
399
|
+
KaiScreenshot.saveHtml(filename);
|
|
45
400
|
}
|
|
46
|
-
|
|
401
|
+
|
|
402
|
+
// ========== ENCRYPTION ==========
|
|
403
|
+
|
|
404
|
+
public setEncryptionKey(key: string): void {
|
|
405
|
+
KaiEncrypt.setDefaultKey(key);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
public encrypted(message: string, key?: string): void {
|
|
409
|
+
if (this.silent) return;
|
|
47
410
|
this.stopSpinner();
|
|
48
|
-
|
|
49
|
-
console.log(`${this.getTime()} ${paint.apply(' ⚙ DEBUG ', colors)} ${message}`, ...args);
|
|
411
|
+
KaiEncrypt.printEncrypted(message, key);
|
|
50
412
|
}
|
|
51
|
-
|
|
413
|
+
|
|
414
|
+
public decrypted(encryptedMessage: string, key?: string): void {
|
|
415
|
+
if (this.silent) return;
|
|
52
416
|
this.stopSpinner();
|
|
53
|
-
|
|
417
|
+
KaiEncrypt.printDecrypted(encryptedMessage, key);
|
|
54
418
|
}
|
|
55
|
-
|
|
419
|
+
|
|
420
|
+
public masked(label: string, value: string, showLast?: number): void {
|
|
421
|
+
if (this.silent) return;
|
|
56
422
|
this.stopSpinner();
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
423
|
+
KaiEncrypt.printMasked(label, value, showLast);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// ========== SOUNDS ==========
|
|
427
|
+
|
|
428
|
+
public setSoundsDir(dir: string): void {
|
|
429
|
+
KaiSound.setSoundsDir(dir);
|
|
63
430
|
}
|
|
64
|
-
|
|
65
|
-
|
|
431
|
+
|
|
432
|
+
public beep(): void {
|
|
433
|
+
KaiSound.beep();
|
|
66
434
|
}
|
|
67
|
-
|
|
68
|
-
|
|
435
|
+
|
|
436
|
+
public async soundSuccess(): Promise<void> {
|
|
437
|
+
await KaiSound.success();
|
|
69
438
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
439
|
+
|
|
440
|
+
public async soundError(): Promise<void> {
|
|
441
|
+
await KaiSound.error();
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
public async soundWarning(): Promise<void> {
|
|
445
|
+
await KaiSound.warning();
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
public async soundNotification(): Promise<void> {
|
|
449
|
+
await KaiSound.notification();
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
public async playSound(filename: string): Promise<void> {
|
|
453
|
+
await KaiSound.play(filename);
|
|
78
454
|
}
|
|
79
455
|
}
|
|
80
456
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { KaiLogger } from './Logger';
|
|
2
|
+
|
|
3
|
+
export class ScopedLogger {
|
|
4
|
+
private parent: KaiLogger;
|
|
5
|
+
private scopeName: string;
|
|
6
|
+
constructor(parent: KaiLogger, scopeName: string) {
|
|
7
|
+
this.parent = parent;
|
|
8
|
+
this.scopeName = scopeName;
|
|
9
|
+
}
|
|
10
|
+
private prefix(message: string): string {
|
|
11
|
+
return `[${this.scopeName}] ${message}`;
|
|
12
|
+
}
|
|
13
|
+
success(message: string, ...args: any[]) {
|
|
14
|
+
this.parent.success(this.prefix(message), ...args);
|
|
15
|
+
}
|
|
16
|
+
error(message: string | Error, ...args: any[]) {
|
|
17
|
+
if (message instanceof Error) {
|
|
18
|
+
this.parent.error(message);
|
|
19
|
+
} else {
|
|
20
|
+
this.parent.error(this.prefix(message), ...args);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
warning(message: string, ...args: any[]) {
|
|
24
|
+
this.parent.warning(this.prefix(message), ...args);
|
|
25
|
+
}
|
|
26
|
+
info(message: string, ...args: any[]) {
|
|
27
|
+
this.parent.info(this.prefix(message), ...args);
|
|
28
|
+
}
|
|
29
|
+
debug(message: string, ...args: any[]) {
|
|
30
|
+
this.parent.debug(this.prefix(message), ...args);
|
|
31
|
+
}
|
|
32
|
+
log(message: string) {
|
|
33
|
+
this.parent.log(this.prefix(message));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
|
|
2
|
+
import { KaiChroma } from '../styles/KaiChroma';
|
|
3
|
+
import { paint } from '../styles/palettes';
|
|
4
|
+
|
|
5
|
+
interface ChartItem {
|
|
6
|
+
label: string;
|
|
7
|
+
value: number;
|
|
8
|
+
color?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class KaiChart {
|
|
12
|
+
static bar(data: ChartItem[], theme: any, options?: { width?: number; showValues?: boolean }) {
|
|
13
|
+
const width = options?.width || 30;
|
|
14
|
+
const showValues = options?.showValues !== false;
|
|
15
|
+
|
|
16
|
+
const maxValue = Math.max(...data.map(d => d.value));
|
|
17
|
+
const maxLabelLength = Math.max(...data.map(d => d.label.length));
|
|
18
|
+
|
|
19
|
+
console.log('');
|
|
20
|
+
data.forEach((item, index) => {
|
|
21
|
+
const percentage = item.value / maxValue;
|
|
22
|
+
const filledWidth = Math.round(width * percentage);
|
|
23
|
+
const emptyWidth = width - filledWidth;
|
|
24
|
+
|
|
25
|
+
const filled = '█'.repeat(filledWidth);
|
|
26
|
+
const empty = '░'.repeat(emptyWidth);
|
|
27
|
+
|
|
28
|
+
// Cycle through theme colors
|
|
29
|
+
const colorKeys = ['success', 'info', 'warning', 'error', 'debug'];
|
|
30
|
+
const colorKey = colorKeys[index % colorKeys.length];
|
|
31
|
+
// @ts-ignore
|
|
32
|
+
const colors = theme[colorKey];
|
|
33
|
+
|
|
34
|
+
const label = item.label.padEnd(maxLabelLength);
|
|
35
|
+
// Usamos PaletteEngine apply (que usa KaiChroma)
|
|
36
|
+
const bar = paint.apply(filled, colors) + KaiChroma.hex('#666666', empty);
|
|
37
|
+
const value = showValues ? ` ${item.value}` : '';
|
|
38
|
+
|
|
39
|
+
console.log(`${KaiChroma.bold(label)} ${bar}${KaiChroma.dim(value)}`);
|
|
40
|
+
});
|
|
41
|
+
console.log('');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static horizontal(data: ChartItem[], theme: any) {
|
|
45
|
+
this.bar(data, theme);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static sparkline(values: number[], theme: any) {
|
|
49
|
+
const chars = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
|
|
50
|
+
const min = Math.min(...values);
|
|
51
|
+
const max = Math.max(...values);
|
|
52
|
+
const range = max - min || 1;
|
|
53
|
+
|
|
54
|
+
const line = values.map(v => {
|
|
55
|
+
const index = Math.floor(((v - min) / range) * (chars.length - 1));
|
|
56
|
+
return chars[index];
|
|
57
|
+
}).join('');
|
|
58
|
+
|
|
59
|
+
console.log(paint.apply(line, theme.info));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static gauge(value: number, max: number, theme: any, label?: string) {
|
|
63
|
+
const percentage = Math.min(1, value / max);
|
|
64
|
+
const width = 20;
|
|
65
|
+
const filledWidth = Math.round(width * percentage);
|
|
66
|
+
const emptyWidth = width - filledWidth;
|
|
67
|
+
|
|
68
|
+
const filled = '█'.repeat(filledWidth);
|
|
69
|
+
const empty = '░'.repeat(emptyWidth);
|
|
70
|
+
|
|
71
|
+
let colors = theme.success;
|
|
72
|
+
if (percentage > 0.7) colors = theme.warning;
|
|
73
|
+
if (percentage > 0.9) colors = theme.error;
|
|
74
|
+
|
|
75
|
+
const bar = paint.apply(filled, colors) + KaiChroma.hex('#666666', empty);
|
|
76
|
+
const percentText = Math.round(percentage * 100) + '%';
|
|
77
|
+
const labelText = label ? `${label}: ` : '';
|
|
78
|
+
|
|
79
|
+
console.log(`${labelText}[${bar}] ${percentText}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
|
|
2
|
+
import { paint } from '../styles/palettes';
|
|
3
|
+
|
|
4
|
+
export class KaiDiff {
|
|
5
|
+
static print(before: any, after: any, theme: any, path: string = ''): void {
|
|
6
|
+
const allKeys = new Set([
|
|
7
|
+
...Object.keys(before || {}),
|
|
8
|
+
...Object.keys(after || {})
|
|
9
|
+
]);
|
|
10
|
+
allKeys.forEach(key => {
|
|
11
|
+
const fullPath = path ? `${path}.${key}` : key;
|
|
12
|
+
const oldVal = before?.[key];
|
|
13
|
+
const newVal = after?.[key];
|
|
14
|
+
if (oldVal === undefined && newVal !== undefined) {
|
|
15
|
+
console.log(paint.apply(`+ ${fullPath}: ${JSON.stringify(newVal)}`, theme.success));
|
|
16
|
+
} else if (oldVal !== undefined && newVal === undefined) {
|
|
17
|
+
console.log(paint.apply(`- ${fullPath}: ${JSON.stringify(oldVal)}`, theme.error));
|
|
18
|
+
} else if (typeof oldVal === 'object' && typeof newVal === 'object' && oldVal !== null && newVal !== null) {
|
|
19
|
+
KaiDiff.print(oldVal, newVal, theme, fullPath);
|
|
20
|
+
} else if (oldVal !== newVal) {
|
|
21
|
+
console.log(paint.apply(`~ ${fullPath}: ${JSON.stringify(oldVal)} → ${JSON.stringify(newVal)}`, theme.warning));
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|