@holz/ansi-terminal-backend 0.8.0-rc.3 → 0.8.2-rc.0

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Jesse Gibson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holz/ansi-terminal-backend",
3
- "version": "0.8.0-rc.3",
3
+ "version": "0.8.2-rc.0",
4
4
  "description": "An ANSI terminal backend for Holz",
5
5
  "type": "module",
6
6
  "repository": {
@@ -22,8 +22,7 @@
22
22
  "license": "MIT",
23
23
  "sideEffects": false,
24
24
  "files": [
25
- "dist",
26
- "src"
25
+ "dist"
27
26
  ],
28
27
  "keywords": [
29
28
  "holz-backend",
@@ -36,10 +35,10 @@
36
35
  "test:types": "tsc"
37
36
  },
38
37
  "peerDependencies": {
39
- "@holz/core": "^0.8.0-rc.3"
38
+ "@holz/core": "^0.8.0"
40
39
  },
41
40
  "devDependencies": {
42
- "@holz/core": "^0.8.0-rc.3",
41
+ "@holz/core": "^0.8.2-rc.0",
43
42
  "@types/node": "^22.0.0",
44
43
  "@vitest/coverage-v8": "^3.0.8",
45
44
  "typescript": "^5.8.2",
@@ -48,5 +47,5 @@
48
47
  "vite-tsconfig-paths": "^5.1.4",
49
48
  "vitest": "^3.0.8"
50
49
  },
51
- "stableVersion": "0.7.0"
52
- }
50
+ "gitHead": "59a13334498d9c3f0952235ed6cb59e493558824"
51
+ }
@@ -1,126 +0,0 @@
1
- import { Writable } from 'node:stream';
2
- import { Console } from 'node:console';
3
- import { createLogger } from '@holz/core';
4
- import { createAnsiTerminalBackend } from '../ansi-terminal-backend';
5
-
6
- const CURRENT_TIME = new Date('2020-06-15T03:05:07.010Z');
7
-
8
- describe('ANSI terminal backend', () => {
9
- const createStream = () => {
10
- let output = '';
11
- const stream = new Writable({
12
- write(chunk, _encoding, callback) {
13
- output += String(chunk);
14
- callback();
15
- },
16
- });
17
-
18
- return {
19
- getOutput: () => output,
20
- stream,
21
- };
22
- };
23
-
24
- // Named "terminal" to avoid conflict with `global.console`.
25
- const createTerminal = () => {
26
- const stdout = createStream();
27
- const stderr = createStream();
28
- const terminal = new Console({
29
- stdout: stdout.stream,
30
- stderr: stderr.stream,
31
- });
32
-
33
- return {
34
- stdout,
35
- stderr,
36
- terminal,
37
- };
38
- };
39
-
40
- beforeEach(() => {
41
- vi.useFakeTimers({
42
- now: CURRENT_TIME,
43
- });
44
- });
45
-
46
- afterEach(() => {
47
- vi.useRealTimers();
48
- });
49
-
50
- it('prints the message to the terminal', () => {
51
- const { terminal, stderr } = createTerminal();
52
- const backend = createAnsiTerminalBackend({ console: terminal });
53
-
54
- const logger = createLogger(backend);
55
- logger.info('hello world');
56
-
57
- expect(stderr.getOutput()).toContain('hello world');
58
- });
59
-
60
- it('includes the log level', () => {
61
- const { terminal, stderr } = createTerminal();
62
- const backend = createAnsiTerminalBackend({ console: terminal });
63
-
64
- const logger = createLogger(backend);
65
- logger.trace('scream');
66
- logger.debug('shout');
67
- logger.info('normal');
68
- logger.warn('hmmmm');
69
- logger.error('oh no');
70
- logger.fatal('goodbye');
71
-
72
- expect(stderr.getOutput()).toContain('TRACE');
73
- expect(stderr.getOutput()).toContain('DEBUG');
74
- expect(stderr.getOutput()).toContain('INFO');
75
- expect(stderr.getOutput()).toContain('WARN');
76
- expect(stderr.getOutput()).toContain('ERROR');
77
- expect(stderr.getOutput()).toContain('FATAL');
78
- });
79
-
80
- it('includes the log namespace', () => {
81
- const { terminal, stderr } = createTerminal();
82
- const backend = createAnsiTerminalBackend({ console: terminal });
83
- const logger = createLogger(backend)
84
- .namespace('my-lib')
85
- .namespace('MyClass');
86
-
87
- logger.debug('initialized');
88
-
89
- expect(stderr.getOutput()).toContain('my-lib:MyClass');
90
- });
91
-
92
- it('includes the log context', () => {
93
- const { terminal, stderr } = createTerminal();
94
- const backend = createAnsiTerminalBackend({ console: terminal });
95
- const logger = createLogger(backend);
96
-
97
- logger.info('creating session', { sessionId: 3109 });
98
-
99
- // Hard to test without replicating the implementation.
100
- expect(stderr.getOutput()).toContain('sessionId');
101
- expect(stderr.getOutput()).toContain('3109');
102
- });
103
-
104
- it('does not include the log context if it is empty', () => {
105
- const { terminal, stderr } = createTerminal();
106
- const backend = createAnsiTerminalBackend({ console: terminal });
107
- const logger = createLogger(backend);
108
-
109
- logger.warn('activating death ray', {});
110
-
111
- // Hard to test without replicating the implementation.
112
- expect(stderr.getOutput()).not.toContain('{');
113
- expect(stderr.getOutput()).not.toContain('}');
114
- });
115
-
116
- it('indents multiline strings', () => {
117
- const { terminal, stderr } = createTerminal();
118
- const backend = createAnsiTerminalBackend({ console: terminal });
119
- const logger = createLogger(backend);
120
-
121
- logger.info('This is a multiline string.\nIt has two lines.');
122
-
123
- expect(stderr.getOutput()).toContain('This is a multiline string.');
124
- expect(stderr.getOutput()).toContain(' It has two lines.');
125
- });
126
- });
package/src/ansi-codes.ts DELETED
@@ -1,20 +0,0 @@
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 black = ansiCode('30m');
13
- export const red = ansiCode('31m');
14
- export const green = ansiCode('32m');
15
- export const yellow = ansiCode('33m');
16
- export const blue = ansiCode('34m');
17
- export const magenta = ansiCode('35m');
18
- export const cyan = ansiCode('36m');
19
- export const white = ansiCode('37m');
20
- export const brightWhite = ansiCode('97m');
@@ -1,90 +0,0 @@
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 DELETED
@@ -1 +0,0 @@
1
- export { createAnsiTerminalBackend } from './ansi-terminal-backend';