@holz/ansi-terminal-backend 0.9.1-rc.8 → 0.9.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/package.json +10 -7
- package/src/ansi-codes.ts +16 -0
- package/src/ansi-terminal-backend.ts +90 -0
- package/src/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holz/ansi-terminal-backend",
|
|
3
|
-
"version": "0.9.1
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "An ANSI terminal backend for Holz",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|
|
13
|
+
"source": "./src/index.ts",
|
|
13
14
|
"types": "./dist/holz-ansi-terminal-backend.d.ts",
|
|
14
15
|
"require": "./dist/holz-ansi-terminal-backend.cjs",
|
|
15
16
|
"import": "./dist/holz-ansi-terminal-backend.js"
|
|
@@ -22,7 +23,9 @@
|
|
|
22
23
|
"license": "MIT",
|
|
23
24
|
"sideEffects": false,
|
|
24
25
|
"files": [
|
|
25
|
-
"dist"
|
|
26
|
+
"dist",
|
|
27
|
+
"src",
|
|
28
|
+
"!src/**/__tests__"
|
|
26
29
|
],
|
|
27
30
|
"keywords": [
|
|
28
31
|
"holz-backend",
|
|
@@ -36,18 +39,18 @@
|
|
|
36
39
|
"test:types": "tsc"
|
|
37
40
|
},
|
|
38
41
|
"peerDependencies": {
|
|
39
|
-
"@holz/core": "^0.8.0"
|
|
42
|
+
"@holz/core": "^0.8.0 || ^0.9.0"
|
|
40
43
|
},
|
|
41
44
|
"devDependencies": {
|
|
42
|
-
"@holz/core": "^0.9.1
|
|
45
|
+
"@holz/core": "^0.9.1",
|
|
43
46
|
"@microsoft/api-extractor": "^7.58.8",
|
|
44
47
|
"@types/node": "^24.0.0",
|
|
45
|
-
"@vitest/coverage-v8": "^
|
|
48
|
+
"@vitest/coverage-v8": "^5.0.0",
|
|
46
49
|
"typescript": "^6.0.0",
|
|
47
50
|
"vite": "^8.0.0",
|
|
48
51
|
"vite-plugin-dts": "^5.0.0",
|
|
49
52
|
"vite-tsconfig-paths": "^6.0.0",
|
|
50
|
-
"vitest": "^
|
|
53
|
+
"vitest": "^5.0.0"
|
|
51
54
|
},
|
|
52
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "87f40467fe9aa798fa894c04a208751403c45e8c"
|
|
53
56
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const ansiCode = (code: string) => `\x1b[${code}`;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Assumes 3-bit color support for an ANSI terminal. This should work on most
|
|
5
|
+
* modern terminals, including on Windows.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const reset = ansiCode('0m');
|
|
9
|
+
export const bold = ansiCode('1m');
|
|
10
|
+
export const dim = ansiCode('2m');
|
|
11
|
+
|
|
12
|
+
export const red = ansiCode('31m');
|
|
13
|
+
export const green = ansiCode('32m');
|
|
14
|
+
export const yellow = ansiCode('33m');
|
|
15
|
+
export const blue = ansiCode('34m');
|
|
16
|
+
export const brightWhite = ansiCode('97m');
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { level, type LogLevel, type Log, type LogProcessor } from '@holz/core';
|
|
2
|
+
import * as ansi from './ansi-codes';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A backend that prints logs to a 3-bit ansi terminal. This should work on
|
|
6
|
+
* most Unix systems and Windows >= 10.
|
|
7
|
+
*
|
|
8
|
+
* NOTE: This is not smart enough to detect if the output is a TTY or if it
|
|
9
|
+
* supports colors, nor is this the appropriate place to check. Without color
|
|
10
|
+
* ques, the printed text is much less understandable. It is better to check
|
|
11
|
+
* when constructing the logger instead.
|
|
12
|
+
*/
|
|
13
|
+
export const createAnsiTerminalBackend = (
|
|
14
|
+
options: Options = {},
|
|
15
|
+
): LogProcessor => {
|
|
16
|
+
const output = options.console ?? console;
|
|
17
|
+
const labelSizeInWhitespace = ' '.repeat(5); // char length of longest level type
|
|
18
|
+
|
|
19
|
+
return (log: Log) => {
|
|
20
|
+
const timestamp = formatAsTimestamp(new Date(log.timestamp));
|
|
21
|
+
const timestampPrefix = `${ansi.reset}${ansi.dim}${timestamp}${ansi.reset}`;
|
|
22
|
+
const segments = [
|
|
23
|
+
{
|
|
24
|
+
include: true,
|
|
25
|
+
command: '%s',
|
|
26
|
+
content: timestampPrefix,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
include: true,
|
|
30
|
+
command: '%s',
|
|
31
|
+
content: logLevelLabel[log.level],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
include: true,
|
|
35
|
+
command: '%s',
|
|
36
|
+
content: log.message.replace(
|
|
37
|
+
/(\r?\n)/g,
|
|
38
|
+
`$1${timestampPrefix} ${labelSizeInWhitespace} `,
|
|
39
|
+
),
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
include: Object.keys(log.context).length > 0,
|
|
43
|
+
command: '%O',
|
|
44
|
+
content: log.context,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
include: log.origin.length > 0,
|
|
48
|
+
command: '%s',
|
|
49
|
+
content: `${ansi.dim}${log.origin.join(':')}${ansi.reset}`,
|
|
50
|
+
},
|
|
51
|
+
].filter((segment) => segment.include);
|
|
52
|
+
|
|
53
|
+
const format = segments.map((segment) => segment.command).join(' ');
|
|
54
|
+
const values = segments.map((segment) => segment.content);
|
|
55
|
+
|
|
56
|
+
// CLIs typically print interactive messages to stdout and logs to stderr.
|
|
57
|
+
output.error(format, ...values);
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// This is an interactive TTY, so we can assume someone is watching. They
|
|
62
|
+
// probably know what day it is. Focus on the time.
|
|
63
|
+
const formatAsTimestamp = (date: Date) => {
|
|
64
|
+
const hours = date.getHours().toString().padStart(2, '0');
|
|
65
|
+
const minutes = date.getMinutes().toString().padStart(2, '0');
|
|
66
|
+
const seconds = date.getSeconds().toString().padStart(2, '0');
|
|
67
|
+
const milliseconds = date.getMilliseconds().toString().padStart(3, '0');
|
|
68
|
+
|
|
69
|
+
return `[${hours}:${minutes}:${seconds}.${milliseconds}]`;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// Trailing whitespace is important for alignment.
|
|
73
|
+
const logLevelLabel: Record<LogLevel, string> = {
|
|
74
|
+
[level.trace]: `${ansi.brightWhite}TRACE${ansi.reset}`,
|
|
75
|
+
[level.debug]: `${ansi.blue}DEBUG${ansi.reset}`,
|
|
76
|
+
[level.info]: `${ansi.green}INFO${ansi.reset} `,
|
|
77
|
+
[level.warn]: `${ansi.yellow}WARN${ansi.reset} `,
|
|
78
|
+
[level.error]: `${ansi.red}ERROR${ansi.reset}`,
|
|
79
|
+
[level.fatal]: `${ansi.bold}${ansi.red}FATAL${ansi.reset}`,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
interface Options {
|
|
83
|
+
/**
|
|
84
|
+
* Defaults the global `console`, but in NodeJS you can create a console
|
|
85
|
+
* over any writable stream. It could be a file or a network socket.
|
|
86
|
+
*
|
|
87
|
+
* @see https://nodejs.org/api/console.html#new-consoleoptions
|
|
88
|
+
*/
|
|
89
|
+
console?: Console;
|
|
90
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createAnsiTerminalBackend } from './ansi-terminal-backend';
|