node-ansi-logger 3.3.2-dev-20260921-3edd1cb → 3.3.2-dev-20261015-6bea90d

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/CHANGELOG.md CHANGED
@@ -25,18 +25,29 @@ If you like this project and find it useful, please consider giving it a star on
25
25
 
26
26
  ### Added
27
27
 
28
+ - [logger]: Add an opt-in global circular message cache with configurable capacity, snapshots, callback replay, clearing and static setter/getter.
28
29
  - [devcontainer]: Add [`Dev Container`](.devcontainer/README.md) v.2.2.0 with dual Node and Bun runtime support.
29
30
  - [agents]: Add a [`shared setup`](.agents/README.md) for all agents: OpenAI Codex, Claude Code, GitHub Copilot and Google Gemini / Antigravity.
31
+ - [agents]: Add [`commit message instructions`](.github/commit-message-instructions.md) v.1.0.0 for the VS Code Copilot "Generate Commit Message" button (Conventional Commits).
30
32
 
31
33
  ### Changed
32
34
 
35
+ - [vscode]: Update `.vscode/settings.json` to v.1.0.15.
36
+ - [config]: Update Oxlint and Oxfmt configurations to v.1.1.0 and Antigravity settings to v.1.0.5.
37
+ - [config]: Update TypeScript build configurations and rename `vite.config.ts` to `vitest.config.ts`, including CI workflow detection.
38
+ - [styleguide]: Update [`STYLEGUIDE.md`](STYLEGUIDE.md) to v.1.1.0: align it with the lint and format config and add the Commit Messages and Changelog sections.
39
+ - [scripts]: Update scripts to v.2.0.0.
33
40
  - [package]: Upgrade package.
34
- - [package]: Bump `oxfmt` to v.0.68.0.
35
- - [package]: Bump `oxlint` to v.1.83.0.
36
- - [package]: Bump `oxlint-tsgolint` to v.7.0.2002.
37
- - [package]: Bump `vitest` to v.5.0.1.
38
- - [package]: Bump `@vitest/coverage-v8` to v.5.0.1.
39
- - [package]: Bump `@types/node` to v.26.6.2.
41
+ - [package]: Bump `oxfmt` to v.0.73.0.
42
+ - [package]: Bump `oxlint` to v.1.88.0.
43
+ - [package]: Bump `oxlint-tsgolint` to v.7.0.2004.
44
+ - [package]: Bump `vitest` to v.5.0.5.
45
+ - [package]: Bump `@vitest/coverage-v8` to v.5.0.5.
46
+ - [package]: Bump `@types/node` to v.26.6.5.
47
+
48
+ ### Fixed
49
+
50
+ - [ansi]: Move the unsafe type assertion suppression to match the current lint diagnostic.
40
51
 
41
52
  <a href="https://www.buymeacoffee.com/luligugithub"><img src="https://matterbridge.io/assets/bmc-button.svg" alt="Buy me a coffee" width="80"></a>
42
53
 
package/README.md CHANGED
@@ -29,12 +29,13 @@ If you like this project and find it useful, please consider giving it a star on
29
29
  for output instead of console.
30
30
  - Includes also a fully customizable stringify funtion with colors (it is bigint aware and manage circular reference).
31
31
  - Includes a chainable ANSI tagged template API (`ansi`) for styling terminal strings directly.
32
+ - Optional shared circular message cache with configurable capacity (default 200), snapshots, callback replay, and clearing.
32
33
 
33
34
  ## Getting Started
34
35
 
35
36
  ### Prerequisites
36
37
 
37
- - Node.js 20-22-24 installed on your machine.
38
+ - Node.js 20.19.0 or later within 20.x, 22.13.0 or later within 22.x, 24.x, or 26.x.
38
39
 
39
40
  ### Installation
40
41
 
@@ -82,6 +83,46 @@ log(LogLevel.WARN, 'Warning message', ...parameters);
82
83
  log.debug(`Debug message ${YELLOW}with yellow part${db}`, ...);
83
84
  ```
84
85
 
86
+ ## Caching and replaying messages
87
+
88
+ Set a shared cache to enable caching for all logger instances:
89
+
90
+ ```typescript
91
+ import { AnsiLogger, AnsiLoggerCache } from 'node-ansi-logger';
92
+
93
+ const cache = new AnsiLoggerCache(); // Retains the latest 200 messages by default.
94
+ AnsiLogger.setGlobalCache(cache);
95
+
96
+ const log = new AnsiLogger({ logName: 'MyApp' });
97
+ log.info('Connected');
98
+
99
+ const messages = cache.get(); // [{ level: 'info', time: '...', name: 'MyApp', message: 'Connected' }]
100
+ const activeCache = AnsiLogger.getGlobalCache(); // Returns cache, or undefined when disabled.
101
+ cache.replay((entry) => {
102
+ console.log(`[${entry.time}] [${entry.name}] [${entry.level}] ${entry.message}`);
103
+ });
104
+
105
+ cache.clear(); // Removes retained messages; caching remains enabled.
106
+ AnsiLogger.setGlobalCache(undefined); // Disables caching without clearing retained messages.
107
+ ```
108
+
109
+ `setGlobalCache(cache)` returns the configured cache. Both static methods use
110
+ `globalThis.__AnsiLoggerCache__`, which can also be assigned directly.
111
+
112
+ Pass a positive safe integer to customize capacity, for example `new AnsiLoggerCache(500)`.
113
+ The circular buffer overwrites the oldest entry when full. Only messages allowed by each
114
+ logger's configured log level are cached, including messages routed to an external logger.
115
+ Additional parameters are joined with spaces, matching logger callbacks; message text and
116
+ ANSI codes are preserved. Existing console, file and callback output is unchanged.
117
+
118
+ `get()` returns independent copies in oldest-to-newest order. `replay(callback)` sends
119
+ the same structured entries to the callback from a snapshot without consuming the cache.
120
+ Messages added during replay are not included in that replay. Callback errors propagate
121
+ to the caller.
122
+
123
+ `clear()` empties the cache and resets the circular buffer without changing its capacity
124
+ or disabling caching. Existing snapshots returned by `get()` are unaffected.
125
+
85
126
  ## Using the logger internal timer:
86
127
 
87
128
  ```typescript
package/dist/ansi.js CHANGED
@@ -196,9 +196,9 @@ function isStyleName(prop) {
196
196
  * @returns {AnsiTag} Chainable ANSI tag.
197
197
  */
198
198
  function createTag(styles = []) {
199
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion
199
200
  const tag = ((strings, ...values) => {
200
201
  return applyStyles(joinTemplate(strings, values), styles);
201
- // oxlint-disable-next-line typescript/no-unsafe-type-assertion
202
202
  });
203
203
  return new Proxy(tag, {
204
204
  get(target, prop) {
@@ -0,0 +1,71 @@
1
+ /**
2
+ * @file src/cache.ts
3
+ * @description Circular message cache for AnsiLogger.
4
+ * @author Luca Liguori
5
+ * @created 2026-10-15
6
+ * @version 3.3.2
7
+ * @license Apache-2.0
8
+ *
9
+ * Copyright 2026 Luca Liguori.
10
+ *
11
+ * Licensed under the Apache License, Version 2.0 (the "License");
12
+ * you may not use this file except in compliance with the License.
13
+ * You may obtain a copy of the License at
14
+ *
15
+ * http://www.apache.org/licenses/LICENSE-2.0
16
+ *
17
+ * Unless required by applicable law or agreed to in writing, software
18
+ * distributed under the License is distributed on an "AS IS" BASIS,
19
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
+ * See the License for the specific language governing permissions and
21
+ * limitations under the License.
22
+ */
23
+ import type { LogLevel } from './logger.js';
24
+ /**
25
+ * A cached log message with its original level, timestamp and logger name.
26
+ */
27
+ export interface AnsiLoggerCacheMessage {
28
+ level: LogLevel;
29
+ time: string;
30
+ name: string;
31
+ message: string;
32
+ }
33
+ /**
34
+ * Stores the most recent log messages in a fixed-capacity circular buffer.
35
+ */
36
+ export declare class AnsiLoggerCache {
37
+ #private;
38
+ /**
39
+ * Creates an empty message cache.
40
+ *
41
+ * @param {number} capacity - Maximum number of messages to retain; defaults to 200.
42
+ * @throws {RangeError} If capacity is not a positive safe integer.
43
+ */
44
+ constructor(capacity?: number);
45
+ /**
46
+ * Adds a message, overwriting the oldest message when the cache is full.
47
+ *
48
+ * @param {AnsiLoggerCacheMessage} entry - Message to copy into the cache.
49
+ * @returns {void}
50
+ */
51
+ add(entry: AnsiLoggerCacheMessage): void;
52
+ /**
53
+ * Removes all retained messages without changing capacity or disabling caching.
54
+ *
55
+ * @returns {void}
56
+ */
57
+ clear(): void;
58
+ /**
59
+ * Returns a snapshot of cached messages from oldest to newest.
60
+ *
61
+ * @returns {AnsiLoggerCacheMessage[]} Copies of all retained messages.
62
+ */
63
+ get(): AnsiLoggerCacheMessage[];
64
+ /**
65
+ * Replays a snapshot from oldest to newest without consuming the cache.
66
+ *
67
+ * @param {(entry: AnsiLoggerCacheMessage) => void} callback - Receives each cached message; thrown errors propagate to the caller.
68
+ * @returns {void}
69
+ */
70
+ replay(callback: (entry: AnsiLoggerCacheMessage) => void): void;
71
+ }
package/dist/cache.js ADDED
@@ -0,0 +1,80 @@
1
+ /**
2
+ * @file src/cache.ts
3
+ * @description Circular message cache for AnsiLogger.
4
+ * @author Luca Liguori
5
+ * @created 2026-10-15
6
+ * @version 3.3.2
7
+ * @license Apache-2.0
8
+ *
9
+ * Copyright 2026 Luca Liguori.
10
+ *
11
+ * Licensed under the Apache License, Version 2.0 (the "License");
12
+ * you may not use this file except in compliance with the License.
13
+ * You may obtain a copy of the License at
14
+ *
15
+ * http://www.apache.org/licenses/LICENSE-2.0
16
+ *
17
+ * Unless required by applicable law or agreed to in writing, software
18
+ * distributed under the License is distributed on an "AS IS" BASIS,
19
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
+ * See the License for the specific language governing permissions and
21
+ * limitations under the License.
22
+ */
23
+ /**
24
+ * Stores the most recent log messages in a fixed-capacity circular buffer.
25
+ */
26
+ export class AnsiLoggerCache {
27
+ #capacity;
28
+ #messages = [];
29
+ #next = 0;
30
+ /**
31
+ * Creates an empty message cache.
32
+ *
33
+ * @param {number} capacity - Maximum number of messages to retain; defaults to 200.
34
+ * @throws {RangeError} If capacity is not a positive safe integer.
35
+ */
36
+ constructor(capacity = 200) {
37
+ if (!Number.isSafeInteger(capacity) || capacity <= 0) {
38
+ throw new RangeError('Cache capacity must be a positive safe integer.');
39
+ }
40
+ this.#capacity = capacity;
41
+ }
42
+ /**
43
+ * Adds a message, overwriting the oldest message when the cache is full.
44
+ *
45
+ * @param {AnsiLoggerCacheMessage} entry - Message to copy into the cache.
46
+ * @returns {void}
47
+ */
48
+ add(entry) {
49
+ this.#messages[this.#next] = { ...entry };
50
+ this.#next = (this.#next + 1) % this.#capacity;
51
+ }
52
+ /**
53
+ * Removes all retained messages without changing capacity or disabling caching.
54
+ *
55
+ * @returns {void}
56
+ */
57
+ clear() {
58
+ this.#messages.length = 0;
59
+ this.#next = 0;
60
+ }
61
+ /**
62
+ * Returns a snapshot of cached messages from oldest to newest.
63
+ *
64
+ * @returns {AnsiLoggerCacheMessage[]} Copies of all retained messages.
65
+ */
66
+ get() {
67
+ return [...this.#messages.slice(this.#next), ...this.#messages.slice(0, this.#next)].map((entry) => ({ ...entry }));
68
+ }
69
+ /**
70
+ * Replays a snapshot from oldest to newest without consuming the cache.
71
+ *
72
+ * @param {(entry: AnsiLoggerCacheMessage) => void} callback - Receives each cached message; thrown errors propagate to the caller.
73
+ * @returns {void}
74
+ */
75
+ replay(callback) {
76
+ for (const entry of this.get()) {
77
+ callback(entry);
78
+ }
79
+ }
80
+ }
package/dist/logger.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * @description This file contains the AnsiLogger class.
4
4
  * @author Luca Liguori
5
5
  * @created 2023-06-01
6
- * @version 3.0.3
6
+ * @version 3.3.2
7
7
  * @license Apache-2.0
8
8
  *
9
9
  * Copyright 2024, 2025, 2026 Luca Liguori.
@@ -20,6 +20,7 @@
20
20
  * See the License for the specific language governing permissions and
21
21
  * limitations under the License.
22
22
  */
23
+ import type { AnsiLoggerCache } from './cache.js';
23
24
  export declare const RESET = "\u001B[0m";
24
25
  export declare const BRIGHT = "\u001B[1m";
25
26
  export declare const DIM = "\u001B[2m";
@@ -118,8 +119,18 @@ export interface AnsiLoggerParams {
118
119
  }
119
120
  export type AnsiLoggerCallback = (level: string, time: string, name: string, message: string) => void;
120
121
  declare global {
122
+ /**
123
+ * Global cache for AnsiLogger messages.
124
+ */
125
+ var __AnsiLoggerCache__: AnsiLoggerCache | undefined;
126
+ /**
127
+ * Global callback for AnsiLogger messages.
128
+ */
121
129
  var __AnsiLoggerCallback__: AnsiLoggerCallback | undefined;
122
130
  var __AnsiLoggerCallbackLoglevel__: LogLevel | undefined;
131
+ /**
132
+ * Global file path for AnsiLogger file output.
133
+ */
123
134
  var __AnsiLoggerFilePath__: string | undefined;
124
135
  var __AnsiLoggerFileLoglevel__: LogLevel | undefined;
125
136
  var __AnsiLoggerFileLogSize__: number | undefined;
@@ -298,6 +309,19 @@ export declare class AnsiLogger {
298
309
  * @returns {AnsiLoggerCallback | undefined} The callback function.
299
310
  */
300
311
  getCallback(): AnsiLoggerCallback | undefined;
312
+ /**
313
+ * Sets the shared cache used by all logger instances.
314
+ *
315
+ * @param {AnsiLoggerCache | undefined} cache - Cache to enable, or undefined to disable caching without clearing retained messages.
316
+ * @returns {AnsiLoggerCache | undefined} The configured cache.
317
+ */
318
+ static setGlobalCache(cache: AnsiLoggerCache | undefined): AnsiLoggerCache | undefined;
319
+ /**
320
+ * Gets the shared cache currently used by all logger instances.
321
+ *
322
+ * @returns {AnsiLoggerCache | undefined} The configured cache, or undefined when caching is disabled.
323
+ */
324
+ static getGlobalCache(): AnsiLoggerCache | undefined;
301
325
  /**
302
326
  * Sets the global callback function to be used by the logger.
303
327
  *
package/dist/logger.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * @description This file contains the AnsiLogger class.
4
4
  * @author Luca Liguori
5
5
  * @created 2023-06-01
6
- * @version 3.0.3
6
+ * @version 3.3.2
7
7
  * @license Apache-2.0
8
8
  *
9
9
  * Copyright 2024, 2025, 2026 Luca Liguori.
@@ -102,6 +102,8 @@ export var TimestampFormat;
102
102
  TimestampFormat[TimestampFormat["CUSTOM"] = 6] = "CUSTOM";
103
103
  })(TimestampFormat || (TimestampFormat = {}));
104
104
  // Initialize the global variables
105
+ if (typeof globalThis.__AnsiLoggerCache__ === 'undefined')
106
+ globalThis.__AnsiLoggerCache__ = undefined;
105
107
  if (typeof globalThis.__AnsiLoggerCallback__ === 'undefined')
106
108
  globalThis.__AnsiLoggerCallback__ = undefined;
107
109
  if (typeof globalThis.__AnsiLoggerCallbackLoglevel__ === 'undefined')
@@ -405,6 +407,24 @@ export class AnsiLogger {
405
407
  getCallback() {
406
408
  return this.#callback;
407
409
  }
410
+ /**
411
+ * Sets the shared cache used by all logger instances.
412
+ *
413
+ * @param {AnsiLoggerCache | undefined} cache - Cache to enable, or undefined to disable caching without clearing retained messages.
414
+ * @returns {AnsiLoggerCache | undefined} The configured cache.
415
+ */
416
+ static setGlobalCache(cache) {
417
+ globalThis.__AnsiLoggerCache__ = cache;
418
+ return globalThis.__AnsiLoggerCache__;
419
+ }
420
+ /**
421
+ * Gets the shared cache currently used by all logger instances.
422
+ *
423
+ * @returns {AnsiLoggerCache | undefined} The configured cache, or undefined when caching is disabled.
424
+ */
425
+ static getGlobalCache() {
426
+ return globalThis.__AnsiLoggerCache__;
427
+ }
408
428
  /**
409
429
  * Sets the global callback function to be used by the logger.
410
430
  *
@@ -713,6 +733,21 @@ export class AnsiLogger {
713
733
  if (typeof level !== 'string' || level.startsWith === undefined || typeof message !== 'string' || message.startsWith === undefined) {
714
734
  return;
715
735
  }
736
+ // Cache the log message in the global cache if available
737
+ try {
738
+ if (globalThis.__AnsiLoggerCache__ !== undefined && this.shouldLog(level, this.#logLevel)) {
739
+ const parametersString = parameters.length > 0 ? ' ' + parameters.join(' ') : '';
740
+ globalThis.__AnsiLoggerCache__.add({
741
+ level,
742
+ time: this.getTimestamp(),
743
+ name: this.#logName,
744
+ message: message + parametersString,
745
+ });
746
+ }
747
+ }
748
+ catch (error) {
749
+ console.error('Error caching log message:', error);
750
+ }
716
751
  // Local callback
717
752
  try {
718
753
  if (this.#callback !== undefined && this.shouldLog(level, this.#logLevel)) {
@@ -743,7 +778,7 @@ export class AnsiLogger {
743
778
  const size = this.logToFile(this.logFilePath, level, message, ...parameters);
744
779
  this.#logFileSize += size;
745
780
  if (this.#logFileSize >= this.#maxFileSize) {
746
- // istanbul ignore next
781
+ /* v8 ignore next */
747
782
  appendFileSync(this.logFilePath, `Logging on file has been stoppped because the file size is greater then ${this.#maxFileSize}B.\n`);
748
783
  }
749
784
  }
@@ -761,7 +796,7 @@ export class AnsiLogger {
761
796
  const size = this.logToFile(__AnsiLoggerFilePath__, level, message, ...parameters);
762
797
  __AnsiLoggerFileLogSize__ += size;
763
798
  if (__AnsiLoggerFileLogSize__ >= this.#maxFileSize) {
764
- // istanbul ignore next
799
+ /* v8 ignore next */
765
800
  appendFileSync(__AnsiLoggerFilePath__, `Logging on file has been stoppped because the file size is greater then ${this.#maxFileSize}B.\n`);
766
801
  }
767
802
  }
package/dist/module.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * @description This file contains the public API entry point for the node-ansi-logger package.
4
4
  * @author Luca Liguori
5
5
  * @created 2024-02-20
6
- * @version 3.3.1
6
+ * @version 3.3.2
7
7
  * @license Apache-2.0
8
8
  *
9
9
  * Copyright 2024, 2025, 2026 Luca Liguori.
@@ -21,5 +21,6 @@
21
21
  * limitations under the License.
22
22
  */
23
23
  export * from './ansi.js';
24
+ export * from './cache.js';
24
25
  export * from './logger.js';
25
26
  export * from './stringify.js';
package/dist/module.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * @description This file contains the public API entry point for the node-ansi-logger package.
4
4
  * @author Luca Liguori
5
5
  * @created 2024-02-20
6
- * @version 3.3.1
6
+ * @version 3.3.2
7
7
  * @license Apache-2.0
8
8
  *
9
9
  * Copyright 2024, 2025, 2026 Luca Liguori.
@@ -22,5 +22,6 @@
22
22
  */
23
23
  /* v8 ignore file -- barrel file with only re-exports; nothing to cover (honored by both jest/istanbul and vitest/v8) */
24
24
  export * from './ansi.js';
25
+ export * from './cache.js';
25
26
  export * from './logger.js';
26
27
  export * from './stringify.js';
package/dist/stringify.js CHANGED
@@ -123,7 +123,7 @@ export function stringify(payload, enableColors = false, colorPayload = 252, col
123
123
  return `${clr(colorUndefined)}(function)${reset()}`;
124
124
  // No Default
125
125
  }
126
- /* istanbul ignore next */
126
+ /* v8 ignore next */
127
127
  return `${clr(colorUndefined)}${String(value)}${reset()}`;
128
128
  };
129
129
  const isTrackable = typeof payload === 'object' && payload !== null;
@@ -156,7 +156,7 @@ export function stringify(payload, enableColors = false, colorPayload = 252, col
156
156
  // @ts-expect-error -- The type of value is unknown, but we will handle it in the code below
157
157
  newValue = value;
158
158
  // console.log(typeof newValue, key, value);
159
- // istanbul ignore next cause is unreachable code for typeof, but included for completeness
159
+ /* v8 ignore next cause is unreachable code for typeof, but included for completeness */
160
160
  if (value === null) {
161
161
  newValue = `${clr(colorUndefined)}null${reset()}`;
162
162
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "node-ansi-logger",
3
- "version": "3.3.2-dev-20260921-3edd1cb",
3
+ "version": "3.3.2-dev-20261015-6bea90d",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "node-ansi-logger",
9
- "version": "3.3.2-dev-20260921-3edd1cb",
9
+ "version": "3.3.2-dev-20261015-6bea90d",
10
10
  "license": "Apache-2.0",
11
11
  "engines": {
12
12
  "node": ">=20.19.0 <21.0.0 || >=22.13.0 <23.0.0 || >=24.0.0 <25.0.0 || >=26.0.0 <27.0.0"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-ansi-logger",
3
- "version": "3.3.2-dev-20260921-3edd1cb",
3
+ "version": "3.3.2-dev-20261015-6bea90d",
4
4
  "description": "Enhanced Ansi Color Logging and Stringify for Node.js in type script",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",