@reliverse/relinka 1.3.6 β†’ 1.3.7

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 CHANGED
@@ -1,116 +1,154 @@
1
- # Relidler: Reliverse Bundler
1
+ # Relinka: Stylish Logging Made Simple
2
2
 
3
3
  [πŸ’– GitHub Sponsors](https://github.com/sponsors/blefnk) β€’ [πŸ’¬ Discord](https://discord.gg/Pb8uKbwpsJ) β€’ [✨ Repo](https://github.com/reliverse/relinka-logger) β€’ [πŸ“¦ NPM](https://npmjs.com/@reliverse/relinka) β€’ [πŸ“š Docs](https://docs.reliverse.org)
4
4
 
5
- **@reliverse/relinka** is your next powerful logger, which allows you to style your terminal or browser console like never before.
5
+ **@reliverse/relinka** is your next favorite logging library β€” built to make your terminal (and browser console β€” soon) output look good, stay clean, and be actually helpful. It’s styled, structured, and smart. Oh, and it works with configs, files, and colors out of the box.
6
6
 
7
- ## Features
7
+ ## 🌟 Features
8
8
 
9
- - 😘 Drop-in replacement for `consola`
10
- - ⚑ `relinka` works via CLI and SDK
11
- - πŸ“¦ Automated NPM/JSR publishing
12
- - βœ… Ensures reliable JS/TS builds
13
- - πŸ”„ Handles automatic version bumps
14
- - πŸ”§ Eliminates `package.json` headaches
15
- - 🎯 Optimized for speed and modern workflows
16
- - πŸ› οΈ Converts TypeScript aliases to relative paths
17
- - ✨ Packed with powerful features under the hood
18
- - πŸ“ Highly configurable flow via a configuration file
19
- - πŸ”Œ Plugin system with one built-in plugin included
9
+ - πŸ§™ Drop-in replacement for `node:console` and `consola`
10
+ - πŸ’¬ `relinka` supports: `info`, `warn`, `success`, `error`, `verbose`
11
+ - 🎨 Beautiful, color-coded logs in the terminal
12
+ - 🧠 Auto-formats messages, objects, and errors
13
+ - πŸ“ Save logs to file (with daily logs, cleanup, and rotation)
14
+ - πŸ“¦ Use it programmatically or through CLI-compatible tools
15
+ - βš™οΈ Smart customization via config
16
+ - ✨ Extensible and future-proof
20
17
 
21
- ## Getting Started
18
+ ## πŸš€ Getting Started
22
19
 
23
- Ensure [Git](https://git-scm.com/downloads), [Node.js](https://nodejs.org), and a package manager ([bun](https://bun.sh)/[pnpm](https://pnpm.io)/[yarn](https://yarnpkg.com)/[npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)) are installed. Then follow these steps:
20
+ ### 1. Install
24
21
 
25
- ### Example Playground
22
+ ```bash
23
+ bun add @reliverse/relinka
24
+ ```
25
+
26
+ And, optionally, install the CLI globally to manage your config:
26
27
 
27
- Want to test Relinka before integrating it into your project? Clone the repo and launch the example:
28
+ ```bash
29
+ bun i -g @reliverse/relinka-cli
30
+ ```
28
31
 
29
- ```sh
30
- git clone https://github.com/reliverse/relinka-logger.git
31
- cd relinka-logger
32
- bun i
33
- bun dev # bun examples/e-main.ts
32
+ ### 2. Basic Usage
33
+
34
+ ```ts
35
+ import { relinkaConfig, relinka } from "@reliverse/relinka";
36
+ export async function main() {
37
+ await relinkaConfig;
38
+ relinka(
39
+ "verbose",
40
+ "This message can be seen only if config was loaded AND debug is enabled",
41
+ );
42
+ relinka("info", "Everything is running smoothly");
43
+ relinka("warn", "This might be a problem");
44
+ relinka("error", "Uh oh, something broke");
45
+ relinka("success", "Thanks for using Relinka! πŸ‘‹");
46
+ }
47
+ await main();
34
48
  ```
35
49
 
36
- ### Relidler Usage
50
+ ## πŸ§ͺ Advanced Usage
51
+
52
+ Want a clean blank line?
37
53
 
38
- 1. **Install globally**:
54
+ ```ts
55
+ relinka("info", ""); // Just prints a newline
56
+ ```
39
57
 
40
- ```sh
41
- bun i -g @reliverse/relidler
42
- ```
58
+ πŸ”œ Use the async logger if you want some advanced features (like typing text animation - soon):
43
59
 
44
- **Or update as needed**:
60
+ ```ts
61
+ import { relinkaAsync } from "@reliverse/relinka";
45
62
 
46
- ```sh
47
- bun -g update --latest
48
- ```
63
+ await relinkaAsync("info", "Something happened", { animate: true });
64
+ ```
49
65
 
50
- 2. **Prepare your project**:
66
+ ## βš™οΈ Configuration
67
+
68
+ Create a `relinka.config.ts` file with a content like:
69
+
70
+ ```ts
71
+ import { defineConfig } from "@reliverse/relinka";
72
+ export default defineConfig({
73
+ // Enable debug to see verbose logs
74
+ debug: true,
75
+ // Show timestamp in each log message
76
+ withTimestamp: false,
77
+ // Control whether logs are saved to a file
78
+ saveLogsToFile: true,
79
+ // Disable colors in the console
80
+ disableColors: false,
81
+ // Log file path
82
+ logFilePath: "relinka.log",
83
+ // Directory settings
84
+ dirs: {
85
+ dailyLogs: true,
86
+ logDir: ".reliverse/logs", // store logs in a custom folder
87
+ maxLogFiles: 5, // keep only the 5 most recent log files
88
+ specialDirs: {
89
+ distDirNames: [],
90
+ useParentConfigInDist: true,
91
+ },
92
+ },
93
+ });
94
+ ```
51
95
 
52
- a. **Configure `.gitignore`**:
96
+ Supported config file names:
53
97
 
54
- ```sh
55
- echo "*.log" >> .gitignore
56
- echo "dist-npm" >> .gitignore
57
- echo "dist-jsr" >> .gitignore
58
- echo "dist-libs" >> .gitignore
59
- ```
98
+ - `relinka.config.ts`
99
+ - πŸ”œ `.relinka.config.js`
100
+ - πŸ”œ `.relinkarc`
101
+ - πŸ”œ or any other supported by c12
60
102
 
61
- b. **Install config intellisense**:
103
+ ## πŸ“ Log Files
62
104
 
63
- ```sh
64
- bun add -D @reliverse/relidler-cfg
65
- ```
105
+ - Stored in `.reliverse/logs/` by default
106
+ - Filename: `relinka.log` or `YYYY-MM-DD-relinka.log` if daily logs are enabled
107
+ - Auto-rotates based on `maxLogFiles`
66
108
 
67
- c. **Initialize `relidler.cfg.ts`**:
109
+ ## πŸ“š API Summary
68
110
 
69
- ```sh
70
- relidler
71
- ```
111
+ ### relinka(level, message, ...args)
72
112
 
73
- - The `relidler.cfg.ts` file is created automatically on the first run.
74
- - **It's recommended to customize this file according to your needs.**
75
- - Supported names: `relidler.cfg.ts` β€’ `relidler.config.ts` β€’ `build.pub.ts` β€’ `build.cfg.ts`.
113
+ Logs synchronously. Skips debug logs if `debug: false`.
76
114
 
77
- 3. **Run and enjoy**:
115
+ ### relinkaAsync(level, message, ...args)
78
116
 
79
- ```sh
80
- relidler
81
- ```
117
+ Async logger that waits for config automatically, and provides some additional advanced features.
82
118
 
83
- ## Plugins & SDK
119
+ ### defineConfig(config)
84
120
 
85
- Relidler includes a plugin system, with the following official built-in plugin already available:
121
+ Helper to define typed config in `relinka.config.ts`
86
122
 
87
- - **`libraries-relidler-plugin`**: Builds and publishes specified subdirectories of your main project as separate packages.
123
+ ## 🧰 Utilities
88
124
 
89
- ### API (for advanced users)
125
+ βœ… Timestamping
126
+ βœ… Log file rotation
127
+ βœ… File-safe formatting
128
+ βœ… ANSI color support
129
+ βœ… Error object handling
90
130
 
91
- The SDK allows you to create new Relidler plugins and even extend your own CLI functionality.
131
+ ## πŸ’‘ Tips
92
132
 
93
- ```sh
94
- bun add -D @reliverse/relidler-sdk
95
- ```
133
+ - Want `@ts-expect-error` auto-injection? Check out [`@reliverse/reinject`](https://npmjs.com/@reliverse/reinject).
134
+ - Using this in a CLI tool? Combine with [`@reliverse/prompts`](https://npmjs.com/@reliverse/prompts).
96
135
 
97
- ## TODO
136
+ ## βœ… TODO
98
137
 
99
- - [x] ~~Implement stable `regular` build and publish~~
100
- - [ ] Implement stable `library` build and publish
101
- - [ ] Allow to minify dist with comments preserved
102
- - [ ] Achieve full drop-in replacement for `unbuild`
103
- - [ ] Support auto migration from `build.config.ts`
104
- - [ ] Allow plugins to extend Relidler's `defineConfig`
105
- - [ ] Support configuration via `reliverse.{ts,jsonc}`
106
- - [ ] Make config file optional with sensible defaults
138
+ - [x] File-based logging
139
+ - [x] Timestamp support
140
+ - [x] Daily logs
141
+ - [x] Smart config
142
+ - [x] Log rotation
143
+ - [ ] CLI interface (optional)
144
+ - [ ] Plugin support (custom formatters, log levels, etc)
107
145
 
108
- ## Related
146
+ ## πŸ™Œ Shoutout
109
147
 
110
- Kudos to the following project that made Relinka possible:
148
+ Relinka was inspired by this gem:
111
149
 
112
150
  - [unjs/consola](https://github.com/unjs/consola#readme)
113
151
 
114
- ## License
152
+ ## πŸ“„ License
115
153
 
116
- 🩷 [MIT](./LICENSE) © [blefnk Nazar Kornienko](https://github.com/blefnk)
154
+ πŸ’– MIT Β© [blefnk (Nazar Kornienko)](https://github.com/blefnk)
package/bin/main.d.ts CHANGED
@@ -19,4 +19,4 @@ export { writeStream } from "./relinka-impl/deprecated/utils/stream.js";
19
19
  export { stripAnsi, centerAlign, rightAlign, leftAlign, align, } from "./relinka-impl/deprecated/utils/string.js";
20
20
  export type { TreeItemObject, TreeItem, TreeOptions, } from "./relinka-impl/deprecated/utils/tree.js";
21
21
  export { formatTree } from "./relinka-impl/deprecated/utils/tree.js";
22
- export { configPromise, relinka, relinkaAsync, defineConfig, } from "./relinka-impl/impl-mod.js";
22
+ export { relinkaConfig, relinka, relinkaAsync, defineConfig, } from "./relinka-impl/impl-mod.js";
package/bin/main.js CHANGED
@@ -51,7 +51,7 @@ export {
51
51
  } from "./relinka-impl/deprecated/utils/string.js";
52
52
  export { formatTree } from "./relinka-impl/deprecated/utils/tree.js";
53
53
  export {
54
- configPromise,
54
+ relinkaConfig,
55
55
  relinka,
56
56
  relinkaAsync,
57
57
  defineConfig
@@ -1,20 +1,19 @@
1
+ import type { RelinkaConfig, LogLevel } from "../relinka-types.js";
1
2
  /**
2
- * Promise that resolves when the Relinka config has been loaded and merged
3
- * from defaults, environment variables, and optionally config files.
3
+ * Promise that resolves once c12 loads and merges the config.
4
4
  */
5
- export declare const configPromise: Promise<RelinkaConfig>;
5
+ export declare const relinkaConfig: Promise<RelinkaConfig>;
6
6
  /**
7
7
  * Logs a message synchronously using the current config.
8
- * Skips debug-level logs unless `debug` is set to true in the config.
8
+ * Skips debug-level logs unless `debug` is true in the config.
9
9
  */
10
- export declare const relinka: (type: LogLevel, message: string, ...args: unknown[]) => void;
10
+ export declare function relinka(type: LogLevel, message: string, ...args: unknown[]): void;
11
11
  /**
12
12
  * Logs a message asynchronously, waiting for the config to be fully loaded.
13
- * Also handles file writing and log cleanup if enabled in the config.
13
+ * Also handles file writing and log cleanup if enabled.
14
14
  */
15
- export declare const relinkaAsync: (type: LogLevel, message: string, ...args: unknown[]) => Promise<void>;
15
+ export declare function relinkaAsync(type: LogLevel, message: string, ...args: unknown[]): Promise<void>;
16
16
  /**
17
- * Type helper for defining configuration in `relinka.config.ts` (or similar).
18
- * Provides autocompletion and type checking for the configuration object.
17
+ * Type helper for user config files.
19
18
  */
20
- export declare const defineConfig: (config: RelinkaConfig) => RelinkaConfig;
19
+ export declare function defineConfig(config: Partial<RelinkaConfig>): Partial<RelinkaConfig>;
@@ -1,13 +1,13 @@
1
1
  import { re } from "@reliverse/relico";
2
2
  import { loadConfig } from "c12";
3
3
  import fs from "fs-extra";
4
- import { createJiti } from "jiti";
5
4
  import path from "pathe";
5
+ const DEV_VERBOSE = false;
6
6
  const DEFAULT_RELINKA_CONFIG = {
7
7
  debug: false,
8
8
  dirs: {
9
9
  dailyLogs: false,
10
- logDir: ".reliverse",
10
+ logDir: ".reliverse/logs",
11
11
  maxLogFiles: 0,
12
12
  specialDirs: {
13
13
  distDirNames: ["dist", "dist-jsr", "dist-npm", "dist-libs"],
@@ -19,223 +19,92 @@ const DEFAULT_RELINKA_CONFIG = {
19
19
  saveLogsToFile: false,
20
20
  withTimestamp: false
21
21
  };
22
- const SUPPORTED_EXTENSIONS = [".ts", ".js", ".mjs", ".cjs", ".json"];
23
- function isDebugEnabled(config) {
24
- return config?.debug ?? DEFAULT_RELINKA_CONFIG.debug;
25
- }
26
- function isColorEnabled(config) {
27
- return !(config?.disableColors ?? DEFAULT_RELINKA_CONFIG.disableColors);
28
- }
29
- function getLogDir(config) {
30
- return config?.dirs?.logDir ?? DEFAULT_RELINKA_CONFIG.dirs.logDir;
31
- }
32
- function isDailyLogsEnabled(config) {
33
- return config?.dirs?.dailyLogs ?? DEFAULT_RELINKA_CONFIG.dirs.dailyLogs;
34
- }
35
- function shouldSaveLogs(config) {
36
- return config?.saveLogsToFile ?? DEFAULT_RELINKA_CONFIG.saveLogsToFile;
37
- }
38
- function getMaxLogFiles(config) {
39
- return config?.dirs?.maxLogFiles ?? DEFAULT_RELINKA_CONFIG.dirs.maxLogFiles;
40
- }
41
- function getBaseLogName(config) {
42
- return config?.logFilePath ?? DEFAULT_RELINKA_CONFIG.logFilePath;
43
- }
44
- const getEnvBoolean = (envVarName) => {
45
- const value = process.env[envVarName];
46
- if (value === void 0 || value === "") return void 0;
47
- const lowerValue = value.toLowerCase().trim();
48
- return !["0", "false"].includes(lowerValue);
49
- };
50
- const getEnvString = (envVarName) => {
51
- return process.env[envVarName] || void 0;
52
- };
53
- const getEnvNumber = (envVarName) => {
54
- const value = process.env[envVarName];
55
- if (value === void 0) return void 0;
56
- const parsed = Number.parseInt(value, 10);
57
- return Number.isNaN(parsed) ? void 0 : parsed;
58
- };
59
- const getEnvOverrides = () => {
60
- const overrides = {};
61
- const dirsOverride = {};
62
- const specialDirsOverride = {};
63
- const debug = getEnvBoolean("RELINKA_DEBUG");
64
- if (debug !== void 0) overrides.debug = debug;
65
- const withTimestamp = getEnvBoolean("RELINKA_TIMESTAMP");
66
- if (withTimestamp !== void 0) overrides.withTimestamp = withTimestamp;
67
- const disableColors = getEnvBoolean("RELINKA_DISABLE_COLORS");
68
- if (disableColors !== void 0) overrides.disableColors = disableColors;
69
- const saveLogsToFile = getEnvBoolean("RELINKA_SAVE_LOGS");
70
- if (saveLogsToFile !== void 0) overrides.saveLogsToFile = saveLogsToFile;
71
- const logFilePath = getEnvString("RELINKA_LOG_FILE");
72
- if (logFilePath !== void 0) overrides.logFilePath = logFilePath;
73
- const logDir = getEnvString("RELINKA_LOG_DIR");
74
- if (logDir !== void 0) dirsOverride.logDir = logDir;
75
- const dailyLogs = getEnvBoolean("RELINKA_DAILY_LOGS");
76
- if (dailyLogs !== void 0) dirsOverride.dailyLogs = dailyLogs;
77
- const maxLogFiles = getEnvNumber("RELINKA_MAX_LOG_FILES");
78
- if (maxLogFiles !== void 0) dirsOverride.maxLogFiles = maxLogFiles;
79
- const useParentConfig = getEnvBoolean("RELINKA_USE_PARENT_CONFIG");
80
- if (useParentConfig !== void 0) {
81
- specialDirsOverride.useParentConfigInDist = useParentConfig;
82
- }
83
- if (Object.keys(specialDirsOverride).length > 0) {
84
- dirsOverride.specialDirs = specialDirsOverride;
85
- }
86
- if (Object.keys(dirsOverride).length > 0) {
87
- overrides.dirs = dirsOverride;
88
- }
89
- return overrides;
90
- };
91
22
  let currentConfig = { ...DEFAULT_RELINKA_CONFIG };
92
23
  let isConfigInitialized = false;
93
- let resolveConfigPromise;
94
- export const configPromise = new Promise((resolve) => {
95
- resolveConfigPromise = resolve;
24
+ let resolveRelinkaConfig;
25
+ export const relinkaConfig = new Promise((resolve) => {
26
+ resolveRelinkaConfig = resolve;
96
27
  });
97
- const findConfigFile = (basePath) => {
98
- for (const ext of SUPPORTED_EXTENSIONS) {
99
- const filePath = basePath + ext;
100
- if (fs.existsSync(filePath)) {
101
- return filePath;
102
- }
103
- }
104
- return void 0;
105
- };
106
- const initializeConfig = async () => {
28
+ async function initializeConfig() {
107
29
  try {
108
- const envOverrides = getEnvOverrides();
109
- const projectRoot = process.cwd();
110
- const reliverseDir = path.resolve(projectRoot, ".reliverse");
111
- const configName = "relinka";
112
- const resolveConfig = async (id, options) => {
113
- if (id !== configName) {
114
- return null;
115
- }
116
- const effectiveCwd = options.cwd || projectRoot;
117
- const _jitiRequire = createJiti(effectiveCwd, options.jitiOptions);
118
- const loadAndExtract = async (filePath) => {
119
- try {
120
- const loadedModule = await _jitiRequire.import(filePath);
121
- const configData = loadedModule.default || loadedModule;
122
- return configData;
123
- } catch (error) {
124
- console.error(
125
- `[Relinka Config Error] Failed to load or parse config from ${filePath}: ${error instanceof Error ? error.message : String(error)}`
126
- );
127
- return null;
128
- }
129
- };
130
- const reliverseBasePath = path.resolve(reliverseDir, configName);
131
- const reliverseConfigFile = findConfigFile(reliverseBasePath);
132
- if (reliverseConfigFile) {
133
- const config = await loadAndExtract(reliverseConfigFile);
134
- if (config !== null) {
135
- return { config, source: reliverseConfigFile };
136
- }
137
- }
138
- const rootBasePath = path.resolve(effectiveCwd, configName);
139
- const rootConfigFile = findConfigFile(rootBasePath);
140
- if (rootConfigFile) {
141
- const config = await loadAndExtract(rootConfigFile);
142
- if (config !== null) {
143
- return { config, source: rootConfigFile };
144
- }
145
- }
146
- return null;
147
- };
148
- const loadedConfigResult = await loadConfig({
149
- name: configName,
150
- cwd: projectRoot,
151
- resolve: resolveConfig,
152
- packageJson: "relinka",
153
- dotenv: true,
154
- defaults: DEFAULT_RELINKA_CONFIG,
155
- overrides: envOverrides
30
+ const result = await loadConfig({
31
+ name: "relinka",
32
+ // base name => tries relinka.config.*, .relinkarc, etc.
33
+ cwd: process.cwd(),
34
+ // working directory
35
+ dotenv: false,
36
+ packageJson: false,
37
+ rcFile: false,
38
+ globalRc: false,
39
+ defaults: DEFAULT_RELINKA_CONFIG
40
+ // lowest priority
41
+ // overrides: {}, // highest priority if we would need to forcibly override
156
42
  });
157
- currentConfig = loadedConfigResult.config ?? { ...DEFAULT_RELINKA_CONFIG };
43
+ currentConfig = result.config;
158
44
  isConfigInitialized = true;
159
- const customResolvedLayer = loadedConfigResult.layers?.find(
160
- (layer) => layer.source?.includes(configName)
161
- );
162
- if (customResolvedLayer?.source) {
45
+ if (DEV_VERBOSE) {
163
46
  console.log(
164
- `[Relinka Config] Loaded main configuration from: ${path.relative(
165
- projectRoot,
166
- customResolvedLayer.source
167
- )}`
47
+ "[Relinka Config Debug] Config file used:",
48
+ result.configFile
168
49
  );
169
- } else {
170
- const pkgJsonLayer = loadedConfigResult.layers?.find(
171
- (layer) => layer.source?.endsWith("package.json")
172
- );
173
- if (pkgJsonLayer?.config) {
174
- console.log("[Relinka Config] Loaded configuration from package.json.");
175
- } else {
176
- console.log(
177
- "[Relinka Config] No config file or package.json entry found. Using defaults and environment variables."
178
- );
179
- }
50
+ console.log("[Relinka Config Debug] All merged layers:", result.layers);
51
+ console.log("[Relinka Config Debug] Final configuration:", currentConfig);
180
52
  }
181
- if (isDebugEnabled(currentConfig)) {
182
- console.log(
183
- "[Relinka Config Debug] Final configuration object:",
184
- JSON.stringify(currentConfig, null, 2)
185
- );
186
- console.log(
187
- "[Relinka Config Debug] Resolved layers:",
188
- loadedConfigResult.layers?.map((l) => ({
189
- config: l.config ? "[Object]" : null,
190
- source: l.source ? path.relative(projectRoot, l.source) : void 0,
191
- meta: l.meta
192
- }))
193
- );
194
- }
195
- if (resolveConfigPromise) {
196
- resolveConfigPromise(currentConfig);
53
+ if (resolveRelinkaConfig) {
54
+ resolveRelinkaConfig(currentConfig);
197
55
  }
198
- return currentConfig;
199
- } catch (error) {
56
+ } catch (err) {
200
57
  console.error(
201
- `[Relinka Config Error] Failed during configuration loading process: ${error instanceof Error ? error.message : String(error)}`
58
+ `[Relinka Config Error] Failed to load config: ${err instanceof Error ? err.message : String(err)}`
202
59
  );
203
60
  currentConfig = { ...DEFAULT_RELINKA_CONFIG };
204
61
  isConfigInitialized = true;
205
- if (resolveConfigPromise) {
206
- resolveConfigPromise(currentConfig);
62
+ if (resolveRelinkaConfig) {
63
+ resolveRelinkaConfig(currentConfig);
207
64
  }
208
- return currentConfig;
209
65
  }
210
- };
66
+ }
211
67
  initializeConfig().catch((err) => {
212
68
  console.error(
213
- `[Relinka Config Error] Unhandled error during initial configuration load: ${err instanceof Error ? err.message : String(err)}`
69
+ `[Relinka Config Error] Unhandled error: ${err instanceof Error ? err.message : String(err)}`
214
70
  );
215
71
  if (!isConfigInitialized) {
216
72
  currentConfig = { ...DEFAULT_RELINKA_CONFIG };
217
73
  isConfigInitialized = true;
218
- if (resolveConfigPromise) {
219
- resolveConfigPromise(currentConfig);
74
+ if (resolveRelinkaConfig) {
75
+ resolveRelinkaConfig(currentConfig);
220
76
  }
221
77
  }
222
78
  });
223
- const getTimestamp = (config) => {
224
- if (!config?.withTimestamp) return "";
79
+ function isDebugEnabled(config) {
80
+ return config.debug ?? DEFAULT_RELINKA_CONFIG.debug;
81
+ }
82
+ function isColorEnabled(config) {
83
+ return !(config.disableColors ?? DEFAULT_RELINKA_CONFIG.disableColors);
84
+ }
85
+ function getLogDir(config) {
86
+ return config.dirs?.logDir ?? DEFAULT_RELINKA_CONFIG.dirs.logDir;
87
+ }
88
+ function isDailyLogsEnabled(config) {
89
+ return config.dirs?.dailyLogs ?? DEFAULT_RELINKA_CONFIG.dirs.dailyLogs;
90
+ }
91
+ function shouldSaveLogs(config) {
92
+ return config.saveLogsToFile ?? DEFAULT_RELINKA_CONFIG.saveLogsToFile;
93
+ }
94
+ function getMaxLogFiles(config) {
95
+ return config.dirs?.maxLogFiles ?? DEFAULT_RELINKA_CONFIG.dirs.maxLogFiles;
96
+ }
97
+ function getBaseLogName(config) {
98
+ return config.logFilePath ?? DEFAULT_RELINKA_CONFIG.logFilePath;
99
+ }
100
+ function getTimestamp(config) {
101
+ if (!config.withTimestamp) return "";
225
102
  const now = /* @__PURE__ */ new Date();
226
- return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(
227
- 2,
228
- "0"
229
- )}-${String(now.getDate()).padStart(2, "0")} ${String(
230
- now.getHours()
231
- ).padStart(2, "0")}:${String(now.getMinutes()).padStart(
232
- 2,
233
- "0"
234
- )}:${String(now.getSeconds()).padStart(2, "0")}.${String(
235
- now.getMilliseconds()
236
- ).padStart(3, "0")}`;
237
- };
238
- const getLogFilePath = (config) => {
103
+ return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")} ${String(now.getHours()).padStart(2, "0")}:${String(
104
+ now.getMinutes()
105
+ ).padStart(2, "0")}:${String(now.getSeconds()).padStart(2, "0")}.${String(now.getMilliseconds()).padStart(3, "0")}`;
106
+ }
107
+ function getLogFilePath(config) {
239
108
  const logDir = getLogDir(config);
240
109
  const daily = isDailyLogsEnabled(config);
241
110
  let finalLogName = getBaseLogName(config);
@@ -251,8 +120,8 @@ const getLogFilePath = (config) => {
251
120
  }
252
121
  const effectiveLogName = finalLogName || "relinka.log";
253
122
  return path.resolve(process.cwd(), logDir, effectiveLogName);
254
- };
255
- const formatLogMessage = (config, level, msg, details) => {
123
+ }
124
+ function formatLogMessage(config, level, msg, details) {
256
125
  const timestamp = getTimestamp(config);
257
126
  let detailsStr = "";
258
127
  if (details !== void 0) {
@@ -271,101 +140,101 @@ Stack Trace: ${details.stack || details.message}`;
271
140
  }
272
141
  const paddedLevel = level.padEnd(7, " ");
273
142
  return timestamp ? `[${timestamp}] ${paddedLevel} ${msg}${detailsStr}` : `${paddedLevel} ${msg}${detailsStr}`;
274
- };
275
- const logToConsole = (config, level, formattedMessage) => {
143
+ }
144
+ function logToConsole(config, level, formattedMessage) {
145
+ const COLOR_RESET = "\x1B[0m";
276
146
  if (!isColorEnabled(config)) {
277
- switch (level) {
278
- case "ERROR":
279
- console.error(formattedMessage);
280
- break;
281
- default:
282
- console.log(formattedMessage);
147
+ if (level === "ERROR") {
148
+ console.error(formattedMessage);
149
+ } else {
150
+ console.log(formattedMessage);
283
151
  }
284
152
  } else {
285
153
  switch (level) {
286
154
  case "ERROR":
287
- console.error(re.redBright(formattedMessage));
155
+ console.error(re.redBright(formattedMessage) + COLOR_RESET);
288
156
  break;
289
157
  case "WARN":
290
- console.warn(re.yellowBright(formattedMessage));
158
+ console.warn(re.yellowBright(formattedMessage) + COLOR_RESET);
291
159
  break;
292
160
  case "SUCCESS":
293
- console.log(re.greenBright(formattedMessage));
161
+ console.log(re.greenBright(formattedMessage) + COLOR_RESET);
294
162
  break;
295
163
  case "INFO":
296
- console.log(re.cyanBright(formattedMessage));
164
+ console.log(re.cyanBright(formattedMessage) + COLOR_RESET);
297
165
  break;
298
166
  default:
299
- console.log(re.dim(formattedMessage));
167
+ console.log(re.dim(formattedMessage) + COLOR_RESET);
300
168
  }
301
169
  }
302
- };
303
- const getLogFilesSortedByDate = async (config) => {
170
+ }
171
+ async function getLogFilesSortedByDate(config) {
304
172
  const logDirectoryPath = path.resolve(process.cwd(), getLogDir(config));
305
- const debugEnabled = isDebugEnabled(config);
306
- try {
307
- if (!await fs.pathExists(logDirectoryPath)) {
308
- if (debugEnabled) {
309
- console.log(
310
- `[Relinka FS Debug] Log directory does not exist: ${logDirectoryPath}`
311
- );
312
- }
313
- return [];
173
+ if (!await fs.pathExists(logDirectoryPath)) {
174
+ if (isDebugEnabled(config)) {
175
+ console.log(
176
+ `[Relinka FS Debug] Log directory not found: ${logDirectoryPath}`
177
+ );
314
178
  }
179
+ return [];
180
+ }
181
+ try {
315
182
  const files = await fs.readdir(logDirectoryPath);
316
- const logFilesPromises = files.filter((file) => file.endsWith(".log")).map(async (file) => {
317
- const filePath = path.join(logDirectoryPath, file);
183
+ const logFilesPromises = files.filter((f) => f.endsWith(".log")).map(async (f) => {
184
+ const filePath = path.join(logDirectoryPath, f);
318
185
  try {
319
186
  const stats = await fs.stat(filePath);
320
- return stats.isFile() ? { path: filePath, mtime: stats.mtime.getTime() } : null;
321
- } catch (statError) {
322
- if (debugEnabled) {
187
+ if (stats.isFile()) {
188
+ return { path: filePath, mtime: stats.mtime.getTime() };
189
+ }
190
+ return null;
191
+ } catch (err) {
192
+ if (isDebugEnabled(config)) {
323
193
  console.error(
324
- `[Relinka FS Debug] Error stating file ${filePath}: ${statError instanceof Error ? statError.message : String(statError)}`
194
+ `[Relinka FS Debug] Error reading stats for ${filePath}: ${err instanceof Error ? err.message : String(err)}`
325
195
  );
326
196
  }
327
197
  return null;
328
198
  }
329
199
  });
330
200
  const logFiles = (await Promise.all(logFilesPromises)).filter(
331
- (fileInfo) => Boolean(fileInfo)
201
+ (f) => Boolean(f)
332
202
  );
333
203
  return logFiles.sort((a, b) => b.mtime - a.mtime);
334
- } catch (readDirError) {
335
- if (debugEnabled) {
204
+ } catch (readErr) {
205
+ if (isDebugEnabled(config)) {
336
206
  console.error(
337
- `[Relinka FS Error] Error reading log directory ${logDirectoryPath}: ${readDirError instanceof Error ? readDirError.message : String(readDirError)}`
207
+ `[Relinka FS Error] Failed reading log directory ${logDirectoryPath}: ${readErr instanceof Error ? readErr.message : String(readErr)}`
338
208
  );
339
209
  }
340
210
  return [];
341
211
  }
342
- };
343
- const deleteFiles = async (filePaths, config) => {
344
- const debugEnabled = isDebugEnabled(config);
345
- const deletePromises = filePaths.map(async (filePath) => {
346
- try {
347
- await fs.unlink(filePath);
348
- } catch (unlinkErr) {
349
- if (debugEnabled) {
350
- console.error(
351
- `[Relinka FS Error] Failed to delete log file ${filePath}: ${unlinkErr instanceof Error ? unlinkErr.message : String(unlinkErr)}`
352
- );
212
+ }
213
+ async function deleteFiles(filePaths, config) {
214
+ await Promise.all(
215
+ filePaths.map(async (filePath) => {
216
+ try {
217
+ await fs.unlink(filePath);
218
+ } catch (err) {
219
+ if (isDebugEnabled(config)) {
220
+ console.error(
221
+ `[Relinka FS Error] Failed to delete log file ${filePath}: ${err instanceof Error ? err.message : String(err)}`
222
+ );
223
+ }
353
224
  }
354
- }
355
- });
356
- await Promise.all(deletePromises);
357
- };
358
- const cleanupOldLogFiles = async (config) => {
359
- const maxFiles = getMaxLogFiles(config);
360
- const debugEnabled = isDebugEnabled(config);
361
- if (!shouldSaveLogs(config) || maxFiles <= 0) return;
225
+ })
226
+ );
227
+ }
228
+ async function cleanupOldLogFiles(config) {
229
+ if (!shouldSaveLogs(config) || getMaxLogFiles(config) <= 0) return;
362
230
  try {
363
231
  const sortedLogFiles = await getLogFilesSortedByDate(config);
232
+ const maxFiles = getMaxLogFiles(config);
364
233
  if (sortedLogFiles.length > maxFiles) {
365
234
  const filesToDelete = sortedLogFiles.slice(maxFiles).map((f) => f.path);
366
235
  if (filesToDelete.length > 0) {
367
236
  await deleteFiles(filesToDelete, config);
368
- if (debugEnabled) {
237
+ if (isDebugEnabled(config)) {
369
238
  console.log(
370
239
  `[Relinka Cleanup] Deleted ${filesToDelete.length} old log file(s). Kept ${maxFiles}.`
371
240
  );
@@ -373,82 +242,76 @@ const cleanupOldLogFiles = async (config) => {
373
242
  }
374
243
  }
375
244
  } catch (err) {
376
- if (debugEnabled) {
245
+ if (isDebugEnabled(config)) {
377
246
  console.error(
378
247
  `[Relinka Cleanup Error] Failed during log cleanup: ${err instanceof Error ? err.message : String(err)}`
379
248
  );
380
249
  }
381
250
  }
382
- };
383
- const appendToLogFile = async (config, absoluteLogFilePath, logMessage) => {
384
- const debugEnabled = isDebugEnabled(config);
251
+ }
252
+ async function appendToLogFile(config, absoluteLogFilePath, logMessage) {
385
253
  try {
386
254
  await fs.ensureDir(path.dirname(absoluteLogFilePath));
387
255
  await fs.appendFile(absoluteLogFilePath, `${logMessage}
388
256
  `);
389
257
  } catch (err) {
390
- if (debugEnabled) {
258
+ if (isDebugEnabled(config)) {
391
259
  console.error(
392
260
  `[Relinka File Error] Failed to write to log file ${absoluteLogFilePath}: ${err instanceof Error ? err.message : String(err)}`
393
261
  );
394
262
  }
395
263
  }
396
- };
397
- export const relinka = (type, message, ...args) => {
398
- const configToUse = currentConfig;
264
+ }
265
+ export function relinka(type, message, ...args) {
399
266
  if (message === "") {
400
267
  console.log();
401
268
  return;
402
269
  }
403
270
  const logLevelLabel = type === "verbose" ? "DEBUG" : type.toUpperCase();
404
- if (logLevelLabel === "DEBUG" && !isDebugEnabled(configToUse)) {
271
+ if (logLevelLabel === "DEBUG" && !isDebugEnabled(currentConfig)) {
405
272
  return;
406
273
  }
407
- const details = args.length > 0 ? args.length === 1 ? args[0] : args : void 0;
408
- const formattedMessage = formatLogMessage(
409
- configToUse,
274
+ const details = args.length > 1 ? args : args[0];
275
+ const formatted = formatLogMessage(
276
+ currentConfig,
410
277
  logLevelLabel,
411
278
  message,
412
279
  details
413
280
  );
414
- logToConsole(configToUse, logLevelLabel, formattedMessage);
415
- };
416
- export const relinkaAsync = async (type, message, ...args) => {
417
- const loadedConfig = await configPromise;
281
+ logToConsole(currentConfig, logLevelLabel, formatted);
282
+ }
283
+ export async function relinkaAsync(type, message, ...args) {
284
+ await relinkaConfig;
418
285
  if (message === "") {
419
286
  console.log();
420
287
  return;
421
288
  }
422
289
  const logLevelLabel = type === "verbose" ? "DEBUG" : type.toUpperCase();
423
- if (logLevelLabel === "DEBUG" && !isDebugEnabled(loadedConfig)) {
290
+ if (logLevelLabel === "DEBUG" && !isDebugEnabled(currentConfig)) {
424
291
  return;
425
292
  }
426
- const details = args.length > 0 ? args.length === 1 ? args[0] : args : void 0;
427
- const formattedMessage = formatLogMessage(
428
- loadedConfig,
293
+ const details = args.length > 1 ? args : args[0];
294
+ const formatted = formatLogMessage(
295
+ currentConfig,
429
296
  logLevelLabel,
430
297
  message,
431
298
  details
432
299
  );
433
- logToConsole(loadedConfig, logLevelLabel, formattedMessage);
434
- if (shouldSaveLogs(loadedConfig)) {
435
- const absoluteLogFilePath = getLogFilePath(loadedConfig);
300
+ logToConsole(currentConfig, logLevelLabel, formatted);
301
+ if (shouldSaveLogs(currentConfig)) {
302
+ const absoluteLogFilePath = getLogFilePath(currentConfig);
436
303
  try {
437
- await appendToLogFile(
438
- loadedConfig,
439
- absoluteLogFilePath,
440
- formattedMessage
441
- );
442
- await cleanupOldLogFiles(loadedConfig);
304
+ await appendToLogFile(currentConfig, absoluteLogFilePath, formatted);
305
+ await cleanupOldLogFiles(currentConfig);
443
306
  } catch (err) {
444
- if (isDebugEnabled(loadedConfig)) {
307
+ if (isDebugEnabled(currentConfig)) {
445
308
  console.error(
446
- `[Relinka File Async Error] Error during file logging/cleanup process: ${err instanceof Error ? err.message : String(err)}`
309
+ `[Relinka File Async Error] Error during file logging/cleanup: ${err instanceof Error ? err.message : String(err)}`
447
310
  );
448
311
  }
449
312
  }
450
313
  }
451
- };
452
- export const defineConfig = (config) => {
314
+ }
315
+ export function defineConfig(config) {
453
316
  return config;
454
- };
317
+ }
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
- "description": "@reliverse/relinka is a powerful logger for your terminal.",
2
+ "description": "@reliverse/relinka is your next favorite logging library β€” built to make your terminal (and browser console β€” soon) output look good, stay clean, and be actually helpful. It’s styled, structured, and smart. Oh, and it works with configs, files, and colors out of the box.",
3
3
  "license": "MIT",
4
4
  "name": "@reliverse/relinka",
5
5
  "type": "module",
6
- "version": "1.3.6",
6
+ "version": "1.3.7",
7
7
  "dependencies": {
8
- "@reliverse/relico": "^1.0.2",
8
+ "@reliverse/relico": "^1.1.0",
9
9
  "@reliverse/runtime": "^1.0.3",
10
10
  "c12": "^3.0.2",
11
11
  "defu": "^6.1.4",