@reliverse/relinka 1.5.2 → 1.5.4
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 +295 -210
- package/bin/impl.d.ts +5 -129
- package/bin/impl.js +132 -133
- package/bin/mod.d.ts +2 -2
- package/bin/mod.js +1 -0
- package/bin/setup.d.ts +144 -0
- package/bin/setup.js +90 -0
- package/package.json +1 -1
package/bin/impl.d.ts
CHANGED
|
@@ -1,115 +1,10 @@
|
|
|
1
|
-
import type
|
|
2
|
-
/**
|
|
3
|
-
export
|
|
4
|
-
distDirNames?: string[];
|
|
5
|
-
useParentConfigInDist?: boolean;
|
|
6
|
-
};
|
|
7
|
-
/** Configuration for directory-related settings. */
|
|
8
|
-
export type RelinkaDirsConfig = {
|
|
9
|
-
dailyLogs?: boolean;
|
|
10
|
-
logDir?: string;
|
|
11
|
-
maxLogFiles?: number;
|
|
12
|
-
specialDirs?: RelinkaSpecialDirsConfig;
|
|
13
|
-
};
|
|
14
|
-
/** Configuration for a single log level. */
|
|
15
|
-
export type LogLevelConfig = {
|
|
16
|
-
/**
|
|
17
|
-
* Symbol to display for this log level.
|
|
18
|
-
* @see https://symbl.cc
|
|
19
|
-
*/
|
|
20
|
-
symbol: string;
|
|
21
|
-
/**
|
|
22
|
-
* Fallback symbol to use if Unicode is not supported.
|
|
23
|
-
*/
|
|
24
|
-
fallbackSymbol: string;
|
|
25
|
-
/**
|
|
26
|
-
* Color to use for this log level.
|
|
27
|
-
*/
|
|
28
|
-
color: DefaultColorKeys;
|
|
29
|
-
/**
|
|
30
|
-
* Number of spaces after the symbol/fallback
|
|
31
|
-
*/
|
|
32
|
-
spacing?: number;
|
|
33
|
-
};
|
|
34
|
-
/** Configuration for all log levels. */
|
|
35
|
-
export type LogLevelsConfig = Partial<Record<LogLevel, LogLevelConfig>>;
|
|
36
|
-
/** Log level types used by the logger. */
|
|
37
|
-
export type LogLevel = "error" | "fatal" | "info" | "success" | "verbose" | "warn" | "log" | "internal" | "null" | "step" | "box" | "message";
|
|
1
|
+
import { type LogLevel, type RelinkaConfig, type RelinkaConfigOptions, type RelinkaFunction } from "./setup";
|
|
2
|
+
/** Promise resolved once the user's config (if any) is merged. */
|
|
3
|
+
export declare const loadRelinkaConfig: Promise<RelinkaConfig>;
|
|
38
4
|
/**
|
|
39
|
-
*
|
|
40
|
-
* All properties are optional to allow for partial configuration.
|
|
41
|
-
* Defaults will be applied during initialization.
|
|
5
|
+
* Enhanced relinkaConfig function that accepts options
|
|
42
6
|
*/
|
|
43
|
-
export
|
|
44
|
-
/**
|
|
45
|
-
* Enables verbose (aka debug) mode for detailed logging.
|
|
46
|
-
*
|
|
47
|
-
* `true` here works only for end-users of CLIs/libs when theirs developers
|
|
48
|
-
* has been awaited for user's config via `@reliverse/relinka`'s `await relinkaConfig;`
|
|
49
|
-
*/
|
|
50
|
-
verbose?: boolean;
|
|
51
|
-
/**
|
|
52
|
-
* Configuration for directory-related settings.
|
|
53
|
-
* - `dailyLogs`: If true, logs will be stored in a daily subdirectory.
|
|
54
|
-
* - `logDir`: The base directory for logs.
|
|
55
|
-
* - `maxLogFiles`: The maximum number of log files to keep before cleanup.
|
|
56
|
-
* - `specialDirs`: Configuration for special directory handling.
|
|
57
|
-
* - `distDirNames`: An array of directory names to check for special handling.
|
|
58
|
-
* - `useParentConfigInDist`: If true, use the parent config in dist directories.
|
|
59
|
-
*/
|
|
60
|
-
dirs?: RelinkaDirsConfig;
|
|
61
|
-
/**
|
|
62
|
-
* Disables color output in the console.
|
|
63
|
-
*/
|
|
64
|
-
disableColors?: boolean;
|
|
65
|
-
/**
|
|
66
|
-
* Path to the log file.
|
|
67
|
-
*/
|
|
68
|
-
logFilePath?: string;
|
|
69
|
-
/**
|
|
70
|
-
* If true, logs will be saved to a file.
|
|
71
|
-
*/
|
|
72
|
-
saveLogsToFile?: boolean;
|
|
73
|
-
/**
|
|
74
|
-
* Configuration for timestamp in log messages.
|
|
75
|
-
*/
|
|
76
|
-
timestamp?: {
|
|
77
|
-
/**
|
|
78
|
-
* If true, timestamps will be added to log messages.
|
|
79
|
-
*/
|
|
80
|
-
enabled: boolean;
|
|
81
|
-
/**
|
|
82
|
-
* The format for timestamps. Default is YYYY-MM-DD HH:mm:ss.SSS
|
|
83
|
-
*/
|
|
84
|
-
format?: string;
|
|
85
|
-
};
|
|
86
|
-
/**
|
|
87
|
-
* Allows to customize the log levels.
|
|
88
|
-
*/
|
|
89
|
-
levels?: LogLevelsConfig;
|
|
90
|
-
/**
|
|
91
|
-
* Controls how often the log cleanup runs (in milliseconds)
|
|
92
|
-
* Default: 10000 (10 seconds)
|
|
93
|
-
*/
|
|
94
|
-
cleanupInterval?: number;
|
|
95
|
-
/**
|
|
96
|
-
* Maximum size of the log write buffer before flushing to disk (in bytes)
|
|
97
|
-
* Default: 4096 (4KB)
|
|
98
|
-
*/
|
|
99
|
-
bufferSize?: number;
|
|
100
|
-
/**
|
|
101
|
-
* Maximum time to hold logs in buffer before flushing to disk (in milliseconds)
|
|
102
|
-
* Default: 5000 (5 seconds)
|
|
103
|
-
*/
|
|
104
|
-
maxBufferAge?: number;
|
|
105
|
-
};
|
|
106
|
-
/** Represents information about a log file for cleanup purposes. */
|
|
107
|
-
export type LogFileInfo = {
|
|
108
|
-
path: string;
|
|
109
|
-
mtime: number;
|
|
110
|
-
};
|
|
111
|
-
/** Promise resolved once the user's config (if any) is merged. */
|
|
112
|
-
export declare const relinkaConfig: Promise<RelinkaConfig>;
|
|
7
|
+
export declare function relinkaConfig(options?: RelinkaConfigOptions): Promise<RelinkaConfig>;
|
|
113
8
|
/**
|
|
114
9
|
* Shuts down the logger, flushing all buffers and clearing timers.
|
|
115
10
|
* As Relinka user - call this at the end of your program to prevent hanging.
|
|
@@ -131,25 +26,6 @@ export declare function truncateString(msg: string, maxLength?: number): string;
|
|
|
131
26
|
* Exhaustiveness check. If we land here, we missed a union case.
|
|
132
27
|
*/
|
|
133
28
|
export declare function casesHandled(unexpectedCase: never): never;
|
|
134
|
-
/**
|
|
135
|
-
* Enhanced relinka function type that supports both traditional and method syntax
|
|
136
|
-
*/
|
|
137
|
-
export type RelinkaFunction = {
|
|
138
|
-
(type: LogLevel | "clear", message: string, ...args: unknown[]): undefined | never;
|
|
139
|
-
error(message: string, ...args: unknown[]): void;
|
|
140
|
-
fatal(message: string, ...args: unknown[]): never;
|
|
141
|
-
info(message: string, ...args: unknown[]): void;
|
|
142
|
-
success(message: string, ...args: unknown[]): void;
|
|
143
|
-
verbose(message: string, ...args: unknown[]): void;
|
|
144
|
-
warn(message: string, ...args: unknown[]): void;
|
|
145
|
-
log(message: string, ...args: unknown[]): void;
|
|
146
|
-
internal(message: string, ...args: unknown[]): void;
|
|
147
|
-
null(message: string, ...args: unknown[]): void;
|
|
148
|
-
step(message: string, ...args: unknown[]): void;
|
|
149
|
-
box(message: string, ...args: unknown[]): void;
|
|
150
|
-
message(message: string, ...args: unknown[]): void;
|
|
151
|
-
clear(): void;
|
|
152
|
-
};
|
|
153
29
|
/**
|
|
154
30
|
* Logs a message synchronously using the current config.
|
|
155
31
|
* If type === "fatal", logs a fatal error and throws (never returns).
|
package/bin/impl.js
CHANGED
|
@@ -3,98 +3,11 @@ import { re } from "@reliverse/relico";
|
|
|
3
3
|
import fs from "@reliverse/relifso";
|
|
4
4
|
import { loadConfig } from "c12";
|
|
5
5
|
import os from "node:os";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
dailyLogs: false,
|
|
12
|
-
logDir: "logs",
|
|
13
|
-
maxLogFiles: 0,
|
|
14
|
-
specialDirs: {
|
|
15
|
-
distDirNames: ["dist", "dist-jsr", "dist-npm", "dist-libs"],
|
|
16
|
-
useParentConfigInDist: true
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
disableColors: false,
|
|
20
|
-
logFilePath: "relinka.log",
|
|
21
|
-
saveLogsToFile: false,
|
|
22
|
-
timestamp: {
|
|
23
|
-
enabled: false,
|
|
24
|
-
format: "YYYY-MM-DD HH:mm:ss.SSS"
|
|
25
|
-
},
|
|
26
|
-
cleanupInterval: 1e4,
|
|
27
|
-
// 10 seconds
|
|
28
|
-
bufferSize: 4096,
|
|
29
|
-
// 4KB
|
|
30
|
-
maxBufferAge: 5e3,
|
|
31
|
-
// 5 seconds
|
|
32
|
-
// A default set of levels, with fallback symbols for non-Unicode terminals:
|
|
33
|
-
levels: {
|
|
34
|
-
success: {
|
|
35
|
-
symbol: "\u2713",
|
|
36
|
-
fallbackSymbol: "[OK]",
|
|
37
|
-
color: "greenBright",
|
|
38
|
-
spacing: 3
|
|
39
|
-
},
|
|
40
|
-
info: {
|
|
41
|
-
symbol: "\u25C8",
|
|
42
|
-
fallbackSymbol: "[i]",
|
|
43
|
-
color: "cyanBright",
|
|
44
|
-
spacing: 3
|
|
45
|
-
},
|
|
46
|
-
error: {
|
|
47
|
-
symbol: "\u2716",
|
|
48
|
-
fallbackSymbol: "[ERR]",
|
|
49
|
-
color: "redBright",
|
|
50
|
-
spacing: 3
|
|
51
|
-
},
|
|
52
|
-
warn: {
|
|
53
|
-
symbol: "\u26A0",
|
|
54
|
-
fallbackSymbol: "[WARN]",
|
|
55
|
-
color: "yellowBright",
|
|
56
|
-
spacing: 3
|
|
57
|
-
},
|
|
58
|
-
fatal: {
|
|
59
|
-
symbol: "\u203C",
|
|
60
|
-
fallbackSymbol: "[FATAL]",
|
|
61
|
-
color: "redBright",
|
|
62
|
-
spacing: 3
|
|
63
|
-
},
|
|
64
|
-
verbose: {
|
|
65
|
-
symbol: "\u2731",
|
|
66
|
-
fallbackSymbol: "[VERBOSE]",
|
|
67
|
-
color: "gray",
|
|
68
|
-
spacing: 3
|
|
69
|
-
},
|
|
70
|
-
internal: {
|
|
71
|
-
symbol: "\u2699",
|
|
72
|
-
fallbackSymbol: "[INTERNAL]",
|
|
73
|
-
color: "magentaBright",
|
|
74
|
-
spacing: 3
|
|
75
|
-
},
|
|
76
|
-
log: { symbol: "\u2502", fallbackSymbol: "|", color: "dim", spacing: 3 },
|
|
77
|
-
step: {
|
|
78
|
-
symbol: "\u2192",
|
|
79
|
-
fallbackSymbol: "[STEP]",
|
|
80
|
-
color: "blueBright",
|
|
81
|
-
spacing: 3
|
|
82
|
-
},
|
|
83
|
-
box: {
|
|
84
|
-
symbol: "\u25A0",
|
|
85
|
-
fallbackSymbol: "[BOX]",
|
|
86
|
-
color: "whiteBright",
|
|
87
|
-
spacing: 1
|
|
88
|
-
},
|
|
89
|
-
message: {
|
|
90
|
-
symbol: "\u{1F7A0}",
|
|
91
|
-
fallbackSymbol: "[MSG]",
|
|
92
|
-
color: "cyan",
|
|
93
|
-
spacing: 3
|
|
94
|
-
},
|
|
95
|
-
null: { symbol: "", fallbackSymbol: "", color: "dim", spacing: 0 }
|
|
96
|
-
}
|
|
97
|
-
};
|
|
6
|
+
import {
|
|
7
|
+
DEFAULT_RELINKA_CONFIG,
|
|
8
|
+
ENABLE_DEV_DEBUG,
|
|
9
|
+
EXIT_GUARD
|
|
10
|
+
} from "./setup.js";
|
|
98
11
|
function isUnicodeSupported() {
|
|
99
12
|
if (process.env.TERM_PROGRAM === "vscode" || process.env.WT_SESSION || process.env.TERM_PROGRAM === "iTerm.app" || process.env.TERM_PROGRAM === "hyper" || process.env.TERMINAL_EMULATOR === "JetBrains-JediTerm" || process.env.ConEmuTask === "{cmd::Cmder}" || process.env.TERM === "xterm-256color") {
|
|
100
13
|
return true;
|
|
@@ -115,9 +28,33 @@ function isUnicodeSupported() {
|
|
|
115
28
|
let currentConfig = { ...DEFAULT_RELINKA_CONFIG };
|
|
116
29
|
let isConfigInitialized = false;
|
|
117
30
|
let resolveRelinkaConfig;
|
|
118
|
-
|
|
31
|
+
let userTerminalCwd;
|
|
32
|
+
export const loadRelinkaConfig = new Promise((res) => {
|
|
119
33
|
resolveRelinkaConfig = res;
|
|
120
34
|
});
|
|
35
|
+
export async function relinkaConfig(options = {}) {
|
|
36
|
+
if (!userTerminalCwd) {
|
|
37
|
+
userTerminalCwd = process.cwd();
|
|
38
|
+
}
|
|
39
|
+
const config = await loadRelinkaConfig;
|
|
40
|
+
if (options.supportFreshLogFile && isFreshLogFileEnabled(config)) {
|
|
41
|
+
try {
|
|
42
|
+
const logFilePath = getLogFilePath(config);
|
|
43
|
+
await fs.ensureDir(path.dirname(logFilePath));
|
|
44
|
+
await fs.writeFile(logFilePath, "");
|
|
45
|
+
if (isVerboseEnabled(config)) {
|
|
46
|
+
console.log(`[Relinka Fresh Log] Cleared log file: ${logFilePath}`);
|
|
47
|
+
}
|
|
48
|
+
} catch (err) {
|
|
49
|
+
if (isVerboseEnabled(config)) {
|
|
50
|
+
console.error(
|
|
51
|
+
`[Relinka Fresh Log Error] Failed to clear log file: ${err instanceof Error ? err.message : String(err)}`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return config;
|
|
57
|
+
}
|
|
121
58
|
const logBuffers = /* @__PURE__ */ new Map();
|
|
122
59
|
const activeTimers = [];
|
|
123
60
|
let bufferFlushTimer = null;
|
|
@@ -125,22 +62,52 @@ let lastCleanupTime = 0;
|
|
|
125
62
|
let cleanupScheduled = false;
|
|
126
63
|
async function initializeConfig() {
|
|
127
64
|
try {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
65
|
+
let result;
|
|
66
|
+
try {
|
|
67
|
+
result = await loadConfig({
|
|
68
|
+
name: "dler",
|
|
69
|
+
// name: "relinka",
|
|
70
|
+
cwd: process.cwd(),
|
|
71
|
+
dotenv: false,
|
|
72
|
+
packageJson: false,
|
|
73
|
+
rcFile: false,
|
|
74
|
+
globalRc: false,
|
|
75
|
+
defaults: {}
|
|
76
|
+
});
|
|
77
|
+
if (result.config?.relinka) {
|
|
78
|
+
currentConfig = { ...DEFAULT_RELINKA_CONFIG, ...result.config.relinka };
|
|
79
|
+
if (ENABLE_DEV_DEBUG) {
|
|
80
|
+
console.log("[Dev Debug] Using relinka config from dler config");
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
currentConfig = { ...DEFAULT_RELINKA_CONFIG };
|
|
84
|
+
if (ENABLE_DEV_DEBUG) {
|
|
85
|
+
console.log(
|
|
86
|
+
"[Dev Debug] No relinka config in dler config, using defaults"
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} catch {
|
|
91
|
+
const relinkaResult = await loadConfig({
|
|
92
|
+
name: "relinka",
|
|
93
|
+
cwd: process.cwd(),
|
|
94
|
+
dotenv: false,
|
|
95
|
+
packageJson: false,
|
|
96
|
+
rcFile: false,
|
|
97
|
+
globalRc: false,
|
|
98
|
+
defaults: DEFAULT_RELINKA_CONFIG
|
|
99
|
+
});
|
|
100
|
+
currentConfig = relinkaResult.config;
|
|
101
|
+
if (ENABLE_DEV_DEBUG) {
|
|
102
|
+
console.log(
|
|
103
|
+
"[Dev Debug] Dler config loading failed, using separate relinka config"
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
138
107
|
isConfigInitialized = true;
|
|
139
108
|
resolveRelinkaConfig?.(currentConfig);
|
|
140
109
|
resolveRelinkaConfig = void 0;
|
|
141
110
|
if (ENABLE_DEV_DEBUG) {
|
|
142
|
-
console.log("[Dev Debug] Config file used:", result.configFile);
|
|
143
|
-
console.log("[Dev Debug] All merged layers:", result.layers);
|
|
144
111
|
console.log("[Dev Debug] Final configuration:", currentConfig);
|
|
145
112
|
}
|
|
146
113
|
} catch (err) {
|
|
@@ -193,20 +160,15 @@ function isVerboseEnabled(config) {
|
|
|
193
160
|
function isColorEnabled(config) {
|
|
194
161
|
return !(config.disableColors ?? DEFAULT_RELINKA_CONFIG.disableColors);
|
|
195
162
|
}
|
|
196
|
-
function getLogDir(config) {
|
|
197
|
-
return config.dirs?.logDir ?? DEFAULT_RELINKA_CONFIG.dirs?.logDir ?? "logs";
|
|
198
|
-
}
|
|
199
|
-
function isDailyLogsEnabled(config) {
|
|
200
|
-
return config.dirs?.dailyLogs ?? DEFAULT_RELINKA_CONFIG.dirs?.dailyLogs ?? false;
|
|
201
|
-
}
|
|
202
163
|
function shouldSaveLogs(config) {
|
|
203
164
|
return config.saveLogsToFile ?? DEFAULT_RELINKA_CONFIG.saveLogsToFile ?? false;
|
|
204
165
|
}
|
|
205
166
|
function getMaxLogFiles(config) {
|
|
206
|
-
return config.dirs?.maxLogFiles ??
|
|
167
|
+
return config.dirs?.maxLogFiles ?? 0;
|
|
207
168
|
}
|
|
208
169
|
function getBaseLogName(config) {
|
|
209
|
-
|
|
170
|
+
const logFileConfig = config.logFile || DEFAULT_RELINKA_CONFIG.logFile || {};
|
|
171
|
+
return logFileConfig.outputPath ?? "logs.log";
|
|
210
172
|
}
|
|
211
173
|
function getBufferSize(config) {
|
|
212
174
|
return config.bufferSize ?? DEFAULT_RELINKA_CONFIG.bufferSize ?? 4096;
|
|
@@ -217,6 +179,9 @@ function getMaxBufferAge(config) {
|
|
|
217
179
|
function getCleanupInterval(config) {
|
|
218
180
|
return config.cleanupInterval ?? DEFAULT_RELINKA_CONFIG.cleanupInterval ?? 1e4;
|
|
219
181
|
}
|
|
182
|
+
function isFreshLogFileEnabled(config) {
|
|
183
|
+
return config.logFile?.freshLogFile ?? DEFAULT_RELINKA_CONFIG.logFile?.freshLogFile ?? false;
|
|
184
|
+
}
|
|
220
185
|
function isDevEnv() {
|
|
221
186
|
return process.env.NODE_ENV === "development";
|
|
222
187
|
}
|
|
@@ -232,18 +197,27 @@ function getTimestamp(config) {
|
|
|
232
197
|
return format.replace("YYYY", String(now.getFullYear())).replace("MM", String(now.getMonth() + 1).padStart(2, "0")).replace("DD", String(now.getDate()).padStart(2, "0")).replace("HH", String(now.getHours()).padStart(2, "0")).replace("mm", String(now.getMinutes()).padStart(2, "0")).replace("ss", String(now.getSeconds()).padStart(2, "0")).replace("SSS", String(now.getMilliseconds()).padStart(3, "0"));
|
|
233
198
|
}
|
|
234
199
|
function getLogFilePath(config) {
|
|
235
|
-
const
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
200
|
+
const logFileConfig = config.logFile || DEFAULT_RELINKA_CONFIG.logFile || {};
|
|
201
|
+
const nameWithDate = logFileConfig.nameWithDate || "disable";
|
|
202
|
+
const outputPath = getBaseLogName(config);
|
|
203
|
+
const dir = path.dirname(outputPath);
|
|
204
|
+
let filename = path.basename(outputPath);
|
|
205
|
+
if (nameWithDate !== "disable") {
|
|
206
|
+
const dateString = getDateString();
|
|
207
|
+
if (nameWithDate === "append-before") {
|
|
208
|
+
filename = `${dateString}-${filename}`;
|
|
209
|
+
} else if (nameWithDate === "append-after") {
|
|
210
|
+
const nameWithoutExt = filename.replace(/\.log$/, "");
|
|
211
|
+
filename = `${nameWithoutExt}-${dateString}.log`;
|
|
212
|
+
}
|
|
241
213
|
}
|
|
242
|
-
if (
|
|
243
|
-
|
|
214
|
+
if (filename && !filename.endsWith(".log")) {
|
|
215
|
+
filename += ".log";
|
|
244
216
|
}
|
|
245
|
-
const
|
|
246
|
-
|
|
217
|
+
const effectiveFilename = filename || "logs.log";
|
|
218
|
+
const finalPath = dir === "." ? effectiveFilename : path.join(dir, effectiveFilename);
|
|
219
|
+
const baseCwd = userTerminalCwd || process.cwd();
|
|
220
|
+
return path.resolve(baseCwd, finalPath);
|
|
247
221
|
}
|
|
248
222
|
function getLevelStyle(config, level) {
|
|
249
223
|
const allLevels = config.levels || DEFAULT_RELINKA_CONFIG.levels || {};
|
|
@@ -305,6 +279,7 @@ const consoleMethodMap = {
|
|
|
305
279
|
success: console.log,
|
|
306
280
|
verbose: console.log,
|
|
307
281
|
log: console.log,
|
|
282
|
+
internal: console.log,
|
|
308
283
|
step: console.log,
|
|
309
284
|
box: console.log,
|
|
310
285
|
message: console.log,
|
|
@@ -322,16 +297,40 @@ function logToConsole(config, level, formattedMessage) {
|
|
|
322
297
|
method(`${colorFn(formattedMessage)}\x1B[0m`);
|
|
323
298
|
}
|
|
324
299
|
async function getLogFilesSortedByDate(config) {
|
|
325
|
-
const logDirectoryPath =
|
|
300
|
+
const logDirectoryPath = userTerminalCwd || process.cwd();
|
|
326
301
|
try {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
302
|
+
const files = await fs.readdir(logDirectoryPath);
|
|
303
|
+
const logFiles = [];
|
|
304
|
+
for (const file of files) {
|
|
305
|
+
const filePath = path.join(logDirectoryPath, file);
|
|
306
|
+
try {
|
|
307
|
+
const stats = await fs.stat(filePath);
|
|
308
|
+
if (stats.isFile() && file.endsWith(".log")) {
|
|
309
|
+
logFiles.push(file);
|
|
310
|
+
} else if (stats.isDirectory()) {
|
|
311
|
+
try {
|
|
312
|
+
const subFiles = await fs.readdir(filePath);
|
|
313
|
+
for (const subFile of subFiles) {
|
|
314
|
+
if (subFile.endsWith(".log")) {
|
|
315
|
+
logFiles.push(path.join(file, subFile));
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
} catch (subDirErr) {
|
|
319
|
+
if (isVerboseEnabled(config)) {
|
|
320
|
+
console.error(
|
|
321
|
+
`[Relinka FS Debug] Error reading subdirectory ${filePath}: ${subDirErr instanceof Error ? subDirErr.message : String(subDirErr)}`
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
} catch (err) {
|
|
327
|
+
if (isVerboseEnabled(config)) {
|
|
328
|
+
console.error(
|
|
329
|
+
`[Relinka FS Debug] Error accessing ${filePath}: ${err instanceof Error ? err.message : String(err)}`
|
|
330
|
+
);
|
|
331
|
+
}
|
|
330
332
|
}
|
|
331
|
-
return [];
|
|
332
333
|
}
|
|
333
|
-
const files = await fs.readdir(logDirectoryPath);
|
|
334
|
-
const logFiles = files.filter((f) => f.endsWith(".log"));
|
|
335
334
|
if (logFiles.length === 0) return [];
|
|
336
335
|
const fileInfoPromises = logFiles.map(
|
|
337
336
|
async (fileName) => {
|
|
@@ -357,7 +356,7 @@ async function getLogFilesSortedByDate(config) {
|
|
|
357
356
|
} catch (readErr) {
|
|
358
357
|
if (isVerboseEnabled(config)) {
|
|
359
358
|
console.error(
|
|
360
|
-
`[Relinka FS Error] Failed reading
|
|
359
|
+
`[Relinka FS Error] Failed reading directory ${logDirectoryPath}: ${readErr instanceof Error ? readErr.message : String(readErr)}`
|
|
361
360
|
);
|
|
362
361
|
}
|
|
363
362
|
return [];
|
|
@@ -615,7 +614,7 @@ export async function relinkaAsync(type, message, ...args) {
|
|
|
615
614
|
console.log();
|
|
616
615
|
return;
|
|
617
616
|
}
|
|
618
|
-
await relinkaConfig;
|
|
617
|
+
await relinkaConfig({ supportFreshLogFile: false });
|
|
619
618
|
const levelName = type.toLowerCase();
|
|
620
619
|
if (levelName === "fatal") {
|
|
621
620
|
shouldNeverHappen(message, ...args);
|
|
@@ -658,7 +657,7 @@ export function formatBox(text) {
|
|
|
658
657
|
const bottom = `\u2514${"\u2500".repeat(width)}\u2518`;
|
|
659
658
|
const content = lines.map((line) => {
|
|
660
659
|
const padding = width - line.length;
|
|
661
|
-
return `\u2502 ${line}${" ".repeat(padding)}\u2502`;
|
|
660
|
+
return `\u2502 ${line}${" ".repeat(padding - 2)}\u2502`;
|
|
662
661
|
}).join("\n");
|
|
663
662
|
return `${top}
|
|
664
663
|
${content}
|
package/bin/mod.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { message, step, log, logger } from "./alias.js";
|
|
2
|
-
export type {
|
|
3
|
-
export { relinkaConfig, relinkaShutdown, flushAllLogBuffers, shouldNeverHappen, truncateString, casesHandled, relinka, relinkaAsync, defineConfig, formatBox, } from "./impl.js";
|
|
2
|
+
export type { RelinkaDirsConfig, LogLevelConfig, LogLevelsConfig, LogLevel, RelinkaConfig, LogFileInfo, RelinkaFunction, RelinkaConfigOptions, } from "./setup.js";
|
|
3
|
+
export { loadRelinkaConfig, relinkaConfig, relinkaShutdown, flushAllLogBuffers, shouldNeverHappen, truncateString, casesHandled, relinka, relinkaAsync, defineConfig, formatBox, } from "./impl.js";
|
package/bin/mod.js
CHANGED
package/bin/setup.d.ts
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type { DefaultColorKeys } from "@reliverse/relico";
|
|
2
|
+
export declare const ENABLE_DEV_DEBUG = false;
|
|
3
|
+
export declare const EXIT_GUARD: unique symbol;
|
|
4
|
+
/** Configuration for directory-related settings. */
|
|
5
|
+
export type RelinkaDirsConfig = {
|
|
6
|
+
maxLogFiles?: number;
|
|
7
|
+
};
|
|
8
|
+
/** Configuration for a single log level. */
|
|
9
|
+
export type LogLevelConfig = {
|
|
10
|
+
/**
|
|
11
|
+
* Symbol to display for this log level.
|
|
12
|
+
* @see https://symbl.cc
|
|
13
|
+
*/
|
|
14
|
+
symbol: string;
|
|
15
|
+
/**
|
|
16
|
+
* Fallback symbol to use if Unicode is not supported.
|
|
17
|
+
*/
|
|
18
|
+
fallbackSymbol: string;
|
|
19
|
+
/**
|
|
20
|
+
* Color to use for this log level.
|
|
21
|
+
*/
|
|
22
|
+
color: DefaultColorKeys;
|
|
23
|
+
/**
|
|
24
|
+
* Number of spaces after the symbol/fallback
|
|
25
|
+
*/
|
|
26
|
+
spacing?: number;
|
|
27
|
+
};
|
|
28
|
+
/** Configuration for all log levels. */
|
|
29
|
+
export type LogLevelsConfig = Partial<Record<LogLevel, LogLevelConfig>>;
|
|
30
|
+
/** Log level types used by the logger. */
|
|
31
|
+
export type LogLevel = "error" | "fatal" | "info" | "success" | "verbose" | "warn" | "log" | "internal" | "null" | "step" | "box" | "message";
|
|
32
|
+
/**
|
|
33
|
+
* Configuration options for the Relinka logger.
|
|
34
|
+
* All properties are optional to allow for partial configuration.
|
|
35
|
+
* Defaults will be applied during initialization.
|
|
36
|
+
*/
|
|
37
|
+
export type RelinkaConfig = {
|
|
38
|
+
/**
|
|
39
|
+
* Enables verbose (aka debug) mode for detailed logging.
|
|
40
|
+
*
|
|
41
|
+
* `true` here works only for end-users of CLIs/libs when theirs developers
|
|
42
|
+
* has been awaited for user's config via `@reliverse/relinka`'s `await relinkaConfig;`
|
|
43
|
+
*/
|
|
44
|
+
verbose?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Configuration for directory-related settings.
|
|
47
|
+
* - `maxLogFiles`: The maximum number of log files to keep before cleanup.
|
|
48
|
+
*/
|
|
49
|
+
dirs?: RelinkaDirsConfig;
|
|
50
|
+
/**
|
|
51
|
+
* Disables color output in the console.
|
|
52
|
+
*/
|
|
53
|
+
disableColors?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Configuration for log file output.
|
|
56
|
+
*/
|
|
57
|
+
logFile?: {
|
|
58
|
+
/**
|
|
59
|
+
* Path to the log file.
|
|
60
|
+
*/
|
|
61
|
+
outputPath?: string;
|
|
62
|
+
/**
|
|
63
|
+
* How to handle date in the filename.
|
|
64
|
+
* - `disable`: No date prefix/suffix
|
|
65
|
+
* - `append-before`: Add date before the filename (e.g., "2024-01-15-log.txt")
|
|
66
|
+
* - `append-after`: Add date after the filename (e.g., "log-2024-01-15.txt")
|
|
67
|
+
*/
|
|
68
|
+
nameWithDate?: "disable" | "append-before" | "append-after";
|
|
69
|
+
/**
|
|
70
|
+
* If true, clears the log file when relinkaConfig is executed with supportFreshLogFile: true.
|
|
71
|
+
* This is useful for starting with a clean log file on each run.
|
|
72
|
+
*/
|
|
73
|
+
freshLogFile?: boolean;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* If true, logs will be saved to a file.
|
|
77
|
+
*/
|
|
78
|
+
saveLogsToFile?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Configuration for timestamp in log messages.
|
|
81
|
+
*/
|
|
82
|
+
timestamp?: {
|
|
83
|
+
/**
|
|
84
|
+
* If true, timestamps will be added to log messages.
|
|
85
|
+
*/
|
|
86
|
+
enabled: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* The format for timestamps. Default is YYYY-MM-DD HH:mm:ss.SSS
|
|
89
|
+
*/
|
|
90
|
+
format?: string;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Allows to customize the log levels.
|
|
94
|
+
*/
|
|
95
|
+
levels?: LogLevelsConfig;
|
|
96
|
+
/**
|
|
97
|
+
* Controls how often the log cleanup runs (in milliseconds)
|
|
98
|
+
* Default: 10000 (10 seconds)
|
|
99
|
+
*/
|
|
100
|
+
cleanupInterval?: number;
|
|
101
|
+
/**
|
|
102
|
+
* Maximum size of the log write buffer before flushing to disk (in bytes)
|
|
103
|
+
* Default: 4096 (4KB)
|
|
104
|
+
*/
|
|
105
|
+
bufferSize?: number;
|
|
106
|
+
/**
|
|
107
|
+
* Maximum time to hold logs in buffer before flushing to disk (in milliseconds)
|
|
108
|
+
* Default: 5000 (5 seconds)
|
|
109
|
+
*/
|
|
110
|
+
maxBufferAge?: number;
|
|
111
|
+
};
|
|
112
|
+
/** Represents information about a log file for cleanup purposes. */
|
|
113
|
+
export type LogFileInfo = {
|
|
114
|
+
path: string;
|
|
115
|
+
mtime: number;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Default configuration object.
|
|
119
|
+
* `reconf` will merge this with a config file.
|
|
120
|
+
*/
|
|
121
|
+
export declare const DEFAULT_RELINKA_CONFIG: RelinkaConfig;
|
|
122
|
+
/**
|
|
123
|
+
* Enhanced relinka function type that supports both traditional and method syntax
|
|
124
|
+
*/
|
|
125
|
+
export type RelinkaFunction = {
|
|
126
|
+
(type: LogLevel | "clear", message: string, ...args: unknown[]): undefined | never;
|
|
127
|
+
error(message: string, ...args: unknown[]): void;
|
|
128
|
+
fatal(message: string, ...args: unknown[]): never;
|
|
129
|
+
info(message: string, ...args: unknown[]): void;
|
|
130
|
+
success(message: string, ...args: unknown[]): void;
|
|
131
|
+
verbose(message: string, ...args: unknown[]): void;
|
|
132
|
+
warn(message: string, ...args: unknown[]): void;
|
|
133
|
+
log(message: string, ...args: unknown[]): void;
|
|
134
|
+
internal(message: string, ...args: unknown[]): void;
|
|
135
|
+
null(message: string, ...args: unknown[]): void;
|
|
136
|
+
step(message: string, ...args: unknown[]): void;
|
|
137
|
+
box(message: string, ...args: unknown[]): void;
|
|
138
|
+
message(message: string, ...args: unknown[]): void;
|
|
139
|
+
clear(): void;
|
|
140
|
+
};
|
|
141
|
+
/** Options for relinkaConfig */
|
|
142
|
+
export type RelinkaConfigOptions = {
|
|
143
|
+
supportFreshLogFile?: boolean;
|
|
144
|
+
};
|