@reliverse/relinka 1.4.0 → 1.4.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/LICENSE +1 -1
- package/README.md +21 -11
- package/bin/deprecated/utils/format-new.d.ts +15 -0
- package/bin/deprecated/utils/format-new.js +13 -0
- package/bin/deprecated/utils/format.d.ts +2 -2
- package/bin/deprecated/utils/format.js +4 -4
- package/bin/impl.d.ts +1 -3
- package/bin/impl.js +44 -50
- package/bin/main.d.ts +1 -1
- package/bin/main.js +2 -2
- package/package.json +10 -9
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2025
|
|
3
|
+
Copyright (c) 2025 Nazar Kornienko (blefnk), Reliverse
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -2,11 +2,9 @@
|
|
|
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)
|
|
4
4
|
|
|
5
|
-
**@reliverse/relinka** is a modern, minimal logging library that actually *feels* right. It's not just pretty output — it's a system: smart formatting, file-safe logging, runtime config support, and a `fatal` mode built for developers who care about correctness.
|
|
5
|
+
**@reliverse/relinka** is a modern, minimal logging library that actually *feels* right. It's not just pretty output — it's a system: smart formatting, file-safe logging, runtime config support, and a `fatal` mode built for developers who care about correctness. Whether you're building CLI tools, SDKs, or full-stack apps — Relinka helps you log with intention.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## ✨ Why Relinka
|
|
7
|
+
## Why Relinka
|
|
10
8
|
|
|
11
9
|
- 🧙 Drop-in replacement for `node:console`, `consola`, or your internal logger
|
|
12
10
|
- 💬 Supports: `info`, `warn`, `success`, `error`, `verbose`, `fatal`, `clear`
|
|
@@ -19,13 +17,13 @@ Whether you're building CLI tools, SDKs, or full-stack apps — Relinka helps yo
|
|
|
19
17
|
|
|
20
18
|
## Getting Started
|
|
21
19
|
|
|
22
|
-
###
|
|
20
|
+
### 1. Install
|
|
23
21
|
|
|
24
22
|
```bash
|
|
25
23
|
bun add @reliverse/relinka
|
|
26
24
|
```
|
|
27
25
|
|
|
28
|
-
### 2
|
|
26
|
+
### 2. Use It
|
|
29
27
|
|
|
30
28
|
#### Direct Method (Recommended)
|
|
31
29
|
|
|
@@ -50,6 +48,7 @@ import {
|
|
|
50
48
|
relinkaConfig,
|
|
51
49
|
relinkaShutdown,
|
|
52
50
|
} from "@reliverse/relinka";
|
|
51
|
+
|
|
53
52
|
export async function main() {
|
|
54
53
|
await relinkaAsync(
|
|
55
54
|
// this automatically loads the config
|
|
@@ -74,17 +73,24 @@ export async function main() {
|
|
|
74
73
|
// "We should never reach this code! This should never happen! (see <anonymous> line)",
|
|
75
74
|
// ); // fatal level throws error and halts execution
|
|
76
75
|
relinka("success", "Thanks for using Relinka!");
|
|
76
|
+
|
|
77
77
|
// Make sure to shut down the logger at the end of your program
|
|
78
78
|
// This is important to flush all buffers and close file handles
|
|
79
79
|
await relinkaShutdown();
|
|
80
|
+
|
|
81
|
+
// Make sure to exit the program after your CLI is done
|
|
82
|
+
// It's not required for Bun-only apps, but recommended
|
|
83
|
+
// for other runtimes, esp. for Node.js (incl. `tsx`)
|
|
84
|
+
process.exit(0);
|
|
80
85
|
}
|
|
86
|
+
|
|
81
87
|
await main();
|
|
82
88
|
```
|
|
83
89
|
|
|
84
90
|
#### [🔜 Soon] Singleton Method
|
|
85
91
|
|
|
86
92
|
```ts
|
|
87
|
-
const logger = initRelinkaInstance();
|
|
93
|
+
const logger = initRelinkaInstance({/* per-project config */});
|
|
88
94
|
logger("info", "Looks great!");
|
|
89
95
|
```
|
|
90
96
|
|
|
@@ -267,14 +273,18 @@ defineConfig({ ... }) // helper for relinka.config.ts
|
|
|
267
273
|
- [x] Log rotation
|
|
268
274
|
- [x] `fatal` type
|
|
269
275
|
- [x] Runtime config
|
|
270
|
-
- [ ]
|
|
276
|
+
- [ ] Implement per-project config redefinition
|
|
271
277
|
- [ ] Plugin support (custom formatters, hooks)
|
|
278
|
+
- [ ] CLI interface (to manage logs, config, etc)
|
|
272
279
|
|
|
273
|
-
##
|
|
280
|
+
## Shoutouts
|
|
274
281
|
|
|
275
|
-
Relinka'
|
|
282
|
+
Relinka wouldn't exist without these gems:
|
|
276
283
|
|
|
277
|
-
- [unjs/consola](https://github.com/unjs/consola)
|
|
284
|
+
- [unjs/consola](https://github.com/unjs/consola#readme)
|
|
285
|
+
- [winston](https://github.com/winstonjs/winston#readme)
|
|
286
|
+
- [pino](https://github.com/pinojs/pino#readme)
|
|
287
|
+
- [node-bunyan](https://github.com/trentm/node-bunyan#readme)
|
|
278
288
|
|
|
279
289
|
## License
|
|
280
290
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compiles a format string with named or positional arguments to a standard format string.
|
|
3
|
+
* Uses @reliverse/repris's built-in compileFormat.
|
|
4
|
+
* @param {string} format - The format string containing named placeholders.
|
|
5
|
+
* @returns {string} The compiled format string with positional indices.
|
|
6
|
+
*/
|
|
7
|
+
export declare function compile(format: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Formats a string using either an array or an object of arguments.
|
|
10
|
+
* Uses @reliverse/repris's built-in formatString.
|
|
11
|
+
* @param {string} format - The format string (named or positional).
|
|
12
|
+
* @param {any[] | Record<string, unknown>} args - Arguments to format into the string.
|
|
13
|
+
* @returns {string} The formatted string.
|
|
14
|
+
*/
|
|
15
|
+
export declare function formatStr(format: string, args: any[] | Record<string, unknown>): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { compileFormat, formatString } from "@reliverse/repris";
|
|
2
|
+
const _compileCache = {};
|
|
3
|
+
export function compile(format) {
|
|
4
|
+
if (_compileCache[format]) {
|
|
5
|
+
return _compileCache[format];
|
|
6
|
+
}
|
|
7
|
+
const compiled = compileFormat(format);
|
|
8
|
+
_compileCache[format] = compiled;
|
|
9
|
+
return compiled;
|
|
10
|
+
}
|
|
11
|
+
export function formatStr(format, args) {
|
|
12
|
+
return formatString(format, args);
|
|
13
|
+
}
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
* @param {string} format - The format string containing the placeholders to replace.
|
|
5
5
|
* @returns {string} The compiled format string with placeholders replaced by positional indices.
|
|
6
6
|
*/
|
|
7
|
-
export declare function
|
|
7
|
+
export declare function compileFormatCompat(format: string): string;
|
|
8
8
|
/**
|
|
9
9
|
* Formats a string according to a custom format, using vsprintf for string formatting.
|
|
10
10
|
* @param {string} format - The custom format string.
|
|
11
11
|
* @param {any[]} argv - The arguments to format into the string.
|
|
12
12
|
* @returns {string} The formatted string.
|
|
13
13
|
*/
|
|
14
|
-
export declare function
|
|
14
|
+
export declare function formatStringCompat(format: string, argv: any): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { vsprintf } from "
|
|
1
|
+
import { vsprintf } from "@reliverse/repris";
|
|
2
2
|
const FORMAT_ARGS = [
|
|
3
3
|
["additional", 5],
|
|
4
4
|
["message", 4],
|
|
@@ -7,7 +7,7 @@ const FORMAT_ARGS = [
|
|
|
7
7
|
["tag", 3]
|
|
8
8
|
];
|
|
9
9
|
const _compileCache = {};
|
|
10
|
-
export function
|
|
10
|
+
export function compileFormatCompat(format) {
|
|
11
11
|
if (_compileCache[format]) {
|
|
12
12
|
return _compileCache[format];
|
|
13
13
|
}
|
|
@@ -21,6 +21,6 @@ export function compileFormat(format) {
|
|
|
21
21
|
_compileCache[format] = _format;
|
|
22
22
|
return _format;
|
|
23
23
|
}
|
|
24
|
-
export function
|
|
25
|
-
return vsprintf(
|
|
24
|
+
export function formatStringCompat(format, argv) {
|
|
25
|
+
return vsprintf(compileFormatCompat(format), argv);
|
|
26
26
|
}
|
package/bin/impl.d.ts
CHANGED
|
@@ -108,9 +108,7 @@ export type LogFileInfo = {
|
|
|
108
108
|
path: string;
|
|
109
109
|
mtime: number;
|
|
110
110
|
};
|
|
111
|
-
/**
|
|
112
|
-
* Promise that resolves once `reconf` loads and merges the config.
|
|
113
|
-
*/
|
|
111
|
+
/** Promise resolved once the user's config (if any) is merged. */
|
|
114
112
|
export declare const relinkaConfig: Promise<RelinkaConfig>;
|
|
115
113
|
/**
|
|
116
114
|
* Shuts down the logger, flushing all buffers and clearing timers.
|
package/bin/impl.js
CHANGED
|
@@ -4,7 +4,7 @@ import fs from "fs-extra";
|
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "pathe";
|
|
6
6
|
const ENABLE_DEV_DEBUG = false;
|
|
7
|
-
const
|
|
7
|
+
const EXIT_GUARD = Symbol.for("relinka.exitHandlersRegistered");
|
|
8
8
|
const DEFAULT_RELINKA_CONFIG = {
|
|
9
9
|
verbose: false,
|
|
10
10
|
dirs: {
|
|
@@ -90,13 +90,14 @@ function isUnicodeSupported() {
|
|
|
90
90
|
let currentConfig = { ...DEFAULT_RELINKA_CONFIG };
|
|
91
91
|
let isConfigInitialized = false;
|
|
92
92
|
let resolveRelinkaConfig;
|
|
93
|
-
export const relinkaConfig = new Promise((
|
|
94
|
-
resolveRelinkaConfig =
|
|
93
|
+
export const relinkaConfig = new Promise((res) => {
|
|
94
|
+
resolveRelinkaConfig = res;
|
|
95
95
|
});
|
|
96
96
|
const logBuffers = /* @__PURE__ */ new Map();
|
|
97
|
+
const activeTimers = [];
|
|
98
|
+
let bufferFlushTimer = null;
|
|
97
99
|
let lastCleanupTime = 0;
|
|
98
100
|
let cleanupScheduled = false;
|
|
99
|
-
let bufferFlushTimer = null;
|
|
100
101
|
async function initializeConfig() {
|
|
101
102
|
try {
|
|
102
103
|
const result = await loadConfig({
|
|
@@ -110,55 +111,42 @@ async function initializeConfig() {
|
|
|
110
111
|
});
|
|
111
112
|
currentConfig = result.config;
|
|
112
113
|
isConfigInitialized = true;
|
|
114
|
+
resolveRelinkaConfig?.(currentConfig);
|
|
115
|
+
resolveRelinkaConfig = void 0;
|
|
113
116
|
if (ENABLE_DEV_DEBUG) {
|
|
114
117
|
console.log("[Dev Debug] Config file used:", result.configFile);
|
|
115
118
|
console.log("[Dev Debug] All merged layers:", result.layers);
|
|
116
119
|
console.log("[Dev Debug] Final configuration:", currentConfig);
|
|
117
120
|
}
|
|
118
|
-
if (resolveRelinkaConfig) {
|
|
119
|
-
resolveRelinkaConfig(currentConfig);
|
|
120
|
-
resolveRelinkaConfig = void 0;
|
|
121
|
-
}
|
|
122
|
-
setupBufferFlushTimer();
|
|
123
121
|
} catch (err) {
|
|
124
122
|
console.error(
|
|
125
123
|
`[Relinka Config Error] Failed to load config: ${err instanceof Error ? err.message : String(err)}`
|
|
126
124
|
);
|
|
127
125
|
currentConfig = { ...DEFAULT_RELINKA_CONFIG };
|
|
128
126
|
isConfigInitialized = true;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
127
|
+
resolveRelinkaConfig?.(currentConfig);
|
|
128
|
+
resolveRelinkaConfig = void 0;
|
|
129
|
+
} finally {
|
|
133
130
|
setupBufferFlushTimer();
|
|
134
131
|
}
|
|
135
132
|
}
|
|
136
133
|
function setupBufferFlushTimer() {
|
|
137
134
|
if (bufferFlushTimer) {
|
|
138
135
|
clearInterval(bufferFlushTimer);
|
|
139
|
-
|
|
140
|
-
if (index !== -1) {
|
|
141
|
-
activeTimers.splice(index, 1);
|
|
142
|
-
}
|
|
136
|
+
activeTimers.splice(activeTimers.indexOf(bufferFlushTimer), 1);
|
|
143
137
|
}
|
|
144
|
-
const
|
|
145
|
-
bufferFlushTimer = setInterval(
|
|
146
|
-
|
|
147
|
-
const now = Date.now();
|
|
148
|
-
for (const [filePath, buffer] of logBuffers.entries()) {
|
|
149
|
-
if (buffer.entries.length > 0 && now - buffer.lastFlush >= maxBufferAge) {
|
|
150
|
-
flushLogBuffer(currentConfig, filePath).catch((err) => {
|
|
151
|
-
console.error(
|
|
152
|
-
`[Relinka Buffer Flush Error] Failed to flush aged buffer: ${err instanceof Error ? err.message : String(err)}`
|
|
153
|
-
);
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
},
|
|
158
|
-
Math.min(maxBufferAge / 2, 2500)
|
|
159
|
-
// Check at least every 2.5 seconds or half the max age
|
|
160
|
-
);
|
|
138
|
+
const maxAge = getMaxBufferAge(currentConfig);
|
|
139
|
+
bufferFlushTimer = setInterval(flushDueBuffers, Math.min(maxAge / 2, 2500));
|
|
140
|
+
bufferFlushTimer.unref();
|
|
161
141
|
activeTimers.push(bufferFlushTimer);
|
|
142
|
+
function flushDueBuffers() {
|
|
143
|
+
const now = Date.now();
|
|
144
|
+
for (const [fp, buf] of logBuffers) {
|
|
145
|
+
if (buf.entries.length && now - buf.lastFlush >= maxAge) {
|
|
146
|
+
flushLogBuffer(currentConfig, fp).catch(console.error);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
162
150
|
}
|
|
163
151
|
initializeConfig().catch((err) => {
|
|
164
152
|
console.error(
|
|
@@ -352,10 +340,14 @@ async function deleteFiles(filePaths, config) {
|
|
|
352
340
|
);
|
|
353
341
|
}
|
|
354
342
|
}
|
|
343
|
+
let sigintHandler;
|
|
344
|
+
let sigtermHandler;
|
|
355
345
|
export async function relinkaShutdown() {
|
|
356
346
|
activeTimers.forEach((timer) => clearTimeout(timer));
|
|
357
347
|
activeTimers.length = 0;
|
|
358
348
|
cleanupScheduled = false;
|
|
349
|
+
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
350
|
+
if (sigtermHandler) process.off("SIGTERM", sigtermHandler);
|
|
359
351
|
await flushAllLogBuffers();
|
|
360
352
|
}
|
|
361
353
|
async function cleanupOldLogFiles(config) {
|
|
@@ -380,6 +372,7 @@ async function cleanupOldLogFiles(config) {
|
|
|
380
372
|
}
|
|
381
373
|
});
|
|
382
374
|
}, delay);
|
|
375
|
+
timer.unref();
|
|
383
376
|
activeTimers.push(timer);
|
|
384
377
|
}
|
|
385
378
|
return;
|
|
@@ -502,6 +495,23 @@ export function casesHandled(unexpectedCase) {
|
|
|
502
495
|
`A case was not handled for value: ${truncateString(String(unexpectedCase ?? "unknown"))}`
|
|
503
496
|
);
|
|
504
497
|
}
|
|
498
|
+
function registerExitHandlers() {
|
|
499
|
+
if (globalThis[EXIT_GUARD]) return;
|
|
500
|
+
globalThis[EXIT_GUARD] = true;
|
|
501
|
+
process.once("beforeExit", () => {
|
|
502
|
+
flushAllLogBuffers().catch(() => {
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
sigintHandler = () => {
|
|
506
|
+
flushAllLogBuffers().finally(() => process.exit(0));
|
|
507
|
+
};
|
|
508
|
+
sigtermHandler = () => {
|
|
509
|
+
flushAllLogBuffers().finally(() => process.exit(0));
|
|
510
|
+
};
|
|
511
|
+
process.once("SIGINT", sigintHandler);
|
|
512
|
+
process.once("SIGTERM", sigtermHandler);
|
|
513
|
+
}
|
|
514
|
+
registerExitHandlers();
|
|
505
515
|
export function relinka(type, message, ...args) {
|
|
506
516
|
if (type === "clear") {
|
|
507
517
|
console.clear();
|
|
@@ -588,19 +598,3 @@ export async function relinkaAsync(type, message, ...args) {
|
|
|
588
598
|
export function defineConfig(config) {
|
|
589
599
|
return config;
|
|
590
600
|
}
|
|
591
|
-
process.on("beforeExit", () => {
|
|
592
|
-
flushAllLogBuffers().catch(() => {
|
|
593
|
-
});
|
|
594
|
-
});
|
|
595
|
-
process.on("SIGINT", () => {
|
|
596
|
-
flushAllLogBuffers().catch(() => {
|
|
597
|
-
}).finally(() => {
|
|
598
|
-
process.exit(0);
|
|
599
|
-
});
|
|
600
|
-
});
|
|
601
|
-
process.on("SIGTERM", () => {
|
|
602
|
-
flushAllLogBuffers().catch(() => {
|
|
603
|
-
}).finally(() => {
|
|
604
|
-
process.exit(0);
|
|
605
|
-
});
|
|
606
|
-
});
|
package/bin/main.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export { box } from "./deprecated/utils/box.js";
|
|
|
14
14
|
export type { ColorName, ColorFunction, } from "./deprecated/utils/deprecatedColors.js";
|
|
15
15
|
export { colors, getColor, colorize, } from "./deprecated/utils/deprecatedColors.js";
|
|
16
16
|
export { parseStack } from "./deprecated/utils/error.js";
|
|
17
|
-
export {
|
|
17
|
+
export { compileFormatCompat, formatStringCompat, } from "./deprecated/utils/format.js";
|
|
18
18
|
export { isPlainObject, isLogObj } from "./deprecated/utils/log.js";
|
|
19
19
|
export { writeStream } from "./deprecated/utils/stream.js";
|
|
20
20
|
export { stripAnsi, centerAlign, rightAlign, leftAlign, align, } from "./deprecated/utils/string.js";
|
package/bin/main.js
CHANGED
|
@@ -35,8 +35,8 @@ export {
|
|
|
35
35
|
} from "./deprecated/utils/deprecatedColors.js";
|
|
36
36
|
export { parseStack } from "./deprecated/utils/error.js";
|
|
37
37
|
export {
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
compileFormatCompat,
|
|
39
|
+
formatStringCompat
|
|
40
40
|
} from "./deprecated/utils/format.js";
|
|
41
41
|
export { isPlainObject, isLogObj } from "./deprecated/utils/log.js";
|
|
42
42
|
export { writeStream } from "./deprecated/utils/stream.js";
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"dependencies": {
|
|
3
3
|
"@reliverse/relico": "^1.1.0",
|
|
4
|
+
"@reliverse/repris": "^1.0.0",
|
|
4
5
|
"@reliverse/runtime": "^1.0.3",
|
|
5
6
|
"c12": "^3.0.3",
|
|
6
7
|
"defu": "^6.1.4",
|
|
7
8
|
"fs-extra": "^11.3.0",
|
|
8
9
|
"pathe": "^2.0.3",
|
|
9
|
-
"printj": "^1.3.1",
|
|
10
10
|
"std-env": "^3.9.0",
|
|
11
11
|
"string-width": "^7.2.0"
|
|
12
12
|
},
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"license": "MIT",
|
|
16
16
|
"name": "@reliverse/relinka",
|
|
17
17
|
"type": "module",
|
|
18
|
-
"version": "1.4.
|
|
18
|
+
"version": "1.4.1",
|
|
19
19
|
"keywords": [
|
|
20
20
|
"logger",
|
|
21
21
|
"consola",
|
|
@@ -34,22 +34,23 @@
|
|
|
34
34
|
],
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@biomejs/biome": "1.9.4",
|
|
37
|
-
"@eslint/js": "^9.
|
|
37
|
+
"@eslint/js": "^9.25.1",
|
|
38
38
|
"@reliverse/relidler-cfg": "^1.1.3",
|
|
39
39
|
"@stylistic/eslint-plugin": "^4.2.0",
|
|
40
40
|
"@total-typescript/ts-reset": "^0.6.1",
|
|
41
|
-
"@types/bun": "^1.2.
|
|
41
|
+
"@types/bun": "^1.2.10",
|
|
42
42
|
"@types/fs-extra": "^11.0.4",
|
|
43
|
-
"@types/node": "^22.
|
|
43
|
+
"@types/node": "^22.15.2",
|
|
44
44
|
"@types/sentencer": "^0.2.3",
|
|
45
|
-
"eslint": "^9.
|
|
45
|
+
"eslint": "^9.25.1",
|
|
46
46
|
"eslint-plugin-no-relative-import-paths": "^1.6.1",
|
|
47
|
-
"eslint-plugin-perfectionist": "^4.
|
|
47
|
+
"eslint-plugin-perfectionist": "^4.12.3",
|
|
48
48
|
"jiti": "^2.4.2",
|
|
49
|
-
"knip": "^5.50.
|
|
49
|
+
"knip": "^5.50.5",
|
|
50
50
|
"sentencer": "^0.2.1",
|
|
51
|
+
"tsx": "^4.19.3",
|
|
51
52
|
"typescript": "^5.8.3",
|
|
52
|
-
"typescript-eslint": "^8.
|
|
53
|
+
"typescript-eslint": "^8.31.0"
|
|
53
54
|
},
|
|
54
55
|
"exports": {
|
|
55
56
|
".": "./bin/main.js"
|