colorino 0.12.7 → 0.13.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/README.md CHANGED
@@ -20,12 +20,13 @@ Colorino automatically adapts its palette to your terminal or browser DevTools t
20
20
  - [Examples](#5-3-2)
21
21
  - [Customization](#5-4)
22
22
  - [Supported Environment Variables](#5-5)
23
+ - [Colorize Helper (Manual Overrides)](#5-6)
23
24
  - [Colorino vs. Chalk](#6)
24
25
  - [API Reference](#7)
25
26
  - [1. `colorino` (default instance)](#7-1)
26
27
  - [2. `createColorino(palette?, options?)` (factory)](#7-2)
27
28
  - [Extending Colorino](#8)
28
- - [Use case: automatic file/context info](#8-1)
29
+ - [Use Case: Automatic File/Context Info](#8-1)
29
30
  - [Internals & Dependencies](#9)
30
31
  - [Why This Pattern?](#9-1)
31
32
  - [License](#10)
@@ -36,11 +37,11 @@ Colorino automatically adapts its palette to your terminal or browser DevTools t
36
37
 
37
38
  Plain `console.log` is colorless and inconsistent. Libraries like `chalk` let you style strings, but you have to decorate every message and manually manage color choices.
38
39
 
39
- Colorino is different: it’s a "batteries-included" logging facade with beautiful, theme-aware colors and a familiar API—no learning curve, no configuration. Instantly upgrade your logs everywhere.
40
+ Colorino is different: it’s a "batteries-included" logging facade with beautiful, theme-aware colors and a familiar API. Instantly upgrade your logs everywhere.
40
41
 
41
42
  ## <a id="2"></a>Features
42
43
 
43
- - 🎨 **Smart Theming:** Automatically detects *dark/light* mode and applies a high‑contrast base palette by default (Dracula for dark, GitHub Light for light); opt into a coordinated theme preset when you want richer colors.
44
+ - 🎨 **Smart Theming:** Automatically detects _dark/light_ mode and applies a high‑contrast base palette by default (Dracula for dark, GitHub Light for light); opt into a coordinated theme preset when you want richer colors.
44
45
  - 🤘 **Graceful Color Degradation**: Accepts rich colors (hex/RGB) and automatically down‑samples to the best ANSI‑16/ANSI‑256/Truecolor match for the current environment.​
45
46
  - 🤝 **Familiar API:** If you know `console.log`, you already know Colorino: all standard log levels are supported.
46
47
  - 🔀 **Environment-Aware:** Works in **Node.js** (ANSI color and truecolor) and all major **Browsers** (CSS styles).
@@ -65,10 +66,10 @@ You can use Colorino directly in the browser without any build step.
65
66
  <html>
66
67
  <head>
67
68
  <script type="module">
68
- import { colorino } from 'https://unpkg.com/colorino/dist/browser.bundle.mjs';
69
+ import { colorino } from 'https://unpkg.com/colorino/dist/browser.bundle.mjs'
69
70
 
70
- colorino.info('Hello from the browser!');
71
- colorino.error('Something went wrong');
71
+ colorino.info('Hello from the browser!')
72
+ colorino.error('Something went wrong')
72
73
  </script>
73
74
  </head>
74
75
  <body></body>
@@ -118,9 +119,10 @@ Use the factory to create as many loggers as you want (each with its own palette
118
119
  import { createColorino } from 'colorino'
119
120
 
120
121
  const myLogger = createColorino(
121
- { // Palette (partial)
122
+ {
123
+ // Palette (partial)
122
124
  error: '#ff007b',
123
- info: '#3498db'
125
+ info: '#3498db',
124
126
  },
125
127
  { disableWarnings: true } // Options (see below)
126
128
  )
@@ -132,17 +134,17 @@ myLogger.info('Rebranded info!')
132
134
 
133
135
  `createColorino(palette?, options?)` accepts:
134
136
 
135
- | Option | Type | Default | Description |
136
- |-------------------|--------------------------------------|---------|-----------------------------------------------------------------------------|
137
- | `disableWarnings` | `boolean` | `false` | Suppress warnings when color support can't be detected or is disabled. |
138
- | `theme` | `ThemeOption` (see below) | `'auto'`| Control the active color theme or force a specific mode. |
139
- | `disableOscProbe` | `boolean` | `false` | Disable OSC 11 terminal theme probing (use only env heuristics for theme). |
140
- | `maxDepth` | `number` | `5` | Maximum depth when pretty-printing objects in log output. |
137
+ | Option | Type | Default | Description |
138
+ | ----------------- | ------------------------- | -------- | -------------------------------------------------------------------------- |
139
+ | `disableWarnings` | `boolean` | `false` | Suppress warnings when color support can't be detected or is disabled. |
140
+ | `theme` | `ThemeOption` (see below) | `'auto'` | Control the active color theme or force a specific mode. |
141
+ | `disableOscProbe` | `boolean` | `false` | Disable OSC 11 terminal theme probing (use only env heuristics for theme). |
142
+ | `maxDepth` | `number` | `5` | Maximum depth when pretty-printing objects in log output. |
141
143
 
142
144
  **`theme` accepts three types of values:**
143
145
 
144
146
  1. **`'auto'`** (Default): Automatically detects your terminal or browser theme (dark/light) and applies the matching default preset.
145
- When combined with `disableOscProbe: true`, only environment variables are used for theme detection (no OSC 11 probe).
147
+ When combined with `disableOscProbe: true`, only environment variables are used for theme detection (no OSC 11 probe).
146
148
  2. **`'dark' | 'light'`**: Forces the logger into a specific mode using the default preset for that mode.
147
149
  3. **`ThemeName`**: Forces a specific built-in palette (e.g., `'dracula'`).
148
150
 
@@ -150,12 +152,12 @@ When combined with `disableOscProbe: true`, only environment variables are used
150
152
 
151
153
  Pass any of these names to the `theme` option to use a specific palette:
152
154
 
153
- | Theme Name | Type | Description |
154
- |----------------------|-----------------|--------------------------------------------------|
155
- | `'dracula'` | **Dark** (High Contrast) | Vibrant pinks, purples, and cyans. |
156
- | `'catppuccin-mocha'` | **Dark** (Low Contrast) | Soothing pastel colors. |
157
- | `'github-light'` | **Light** (High Contrast)| Clean, sharp, high-contrast. |
158
- | `'catppuccin-latte'` | **Light** (Low Contrast) | Warm, cozy light mode with soft colors. |
155
+ | Theme Name | Type | Description |
156
+ | -------------------- | ------------------------- | --------------------------------------- |
157
+ | `'dracula'` | **Dark** (High Contrast) | Vibrant pinks, purples, and cyans. |
158
+ | `'catppuccin-mocha'` | **Dark** (Low Contrast) | Soothing pastel colors. |
159
+ | `'github-light'` | **Light** (High Contrast) | Clean, sharp, high-contrast. |
160
+ | `'catppuccin-latte'` | **Light** (Low Contrast) | Warm, cozy light mode with soft colors. |
159
161
 
160
162
  In auto mode, Colorino uses dracula in dark environments and github-light in light environments.
161
163
 
@@ -166,14 +168,14 @@ Set only the colors you care about; everything else uses the detected base theme
166
168
 
167
169
  ```typescript
168
170
  // Only customize error and warn
169
- const myLogger = createColorino({
171
+ const myLogger = createColorino({
170
172
  error: '#ff007b',
171
- warn: '#ffa500'
173
+ warn: '#ffa500',
172
174
  })
173
175
 
174
176
  // Detected dark terminal (uses dracula as base):
175
177
  // - error: #ff007b (your custom red)
176
- // - warn: #ffa500 (your custom orange)
178
+ // - warn: #ffa500 (your custom orange)
177
179
  // - info: #8be9fd (dracula cyan)
178
180
  // - log: #f8f8f2 (dracula foreground)
179
181
  // - debug: #bd93f9 (dracula purple)
@@ -201,10 +203,7 @@ Overlay your own colors on top of a built-in theme.
201
203
 
202
204
  ```typescript
203
205
  // Use GitHub Light but with a custom error color
204
- const myLogger = createColorino(
205
- { error: '#ff007b' },
206
- { theme: 'github-light' }
207
- )
206
+ const myLogger = createColorino({ error: '#ff007b' }, { theme: 'github-light' })
208
207
  ```
209
208
 
210
209
  **4. Force a specific mode (uses defaults):**
@@ -220,11 +219,11 @@ const darkLogger = createColorino({}, { theme: 'dark' })
220
219
 
221
220
  ### <a id="5-4"></a>Customization
222
221
 
223
- Use your brand colors by passing a partial palette to the `createColorino` factory. Any log levels you don't specify will use the detected **minimal** defaults (`minimal-dark` / `minimal-light`) unless you explicitly select a theme preset.
222
+ Use your brand colors by passing a partial palette to the `createColorino` factory. Any log levels you don't specify will use the detected default colors unless you explicitly select a theme preset.
224
223
 
225
224
  Colorino always targets the highest color fidelity supported by the environment. If your palette uses hex colors but only ANSI‑16 is available, Colorino computes the nearest ANSI color so your branding stays recognizable, even on limited terminals.
226
225
 
227
- If you pass an invalid color value (e.g. malformed hex) in a custom palette, Colorino throws an InputValidationError at creation time so broken palettes fail fast.
226
+ If you pass an invalid color value (e.g. malformed hex) in a custom palette, Colorino throws an `InputValidationError` at creation time so broken palettes fail fast.
228
227
 
229
228
  ```typescript
230
229
  import { createColorino } from 'colorino'
@@ -240,26 +239,41 @@ myLogger.info('Still styled by theme.') // Uses the default theme color
240
239
 
241
240
  Colorino auto-detects your environment and color support, but you can override behavior using these standard environment variables (compatible with Chalk):
242
241
 
243
- | Variable | Effect | Example |
244
- |-----------------|------------------------------------------------------------------------------------------|--------------------------------------|
245
- | `NO_COLOR` | Forces no color output | `NO_COLOR=1 node app.js` |
246
- | `FORCE_COLOR` | Forces color level: `0`=off, `1`=ANSI‑16, `2`=ANSI‑256, `3`=Truecolor | `FORCE_COLOR=3 node app.js` |
247
- | `CLICOLOR` | `"0"` disables color | `CLICOLOR=0 node app.js` |
248
- | `CLICOLOR_FORCE`| Non‑`"0"` value enables color even if not a TTY | `CLICOLOR_FORCE=1 node app.js` |
249
- | `TERM` | Terminal type; may influence color support | `TERM=xterm-256color` |
250
- | `COLORTERM` | `'truecolor'` or `'24bit'` enables truecolor | `COLORTERM=truecolor` |
251
- | `WT_SESSION` | Enables color detection for Windows Terminal | |
252
- | `CI` | Many CI platforms default to no color | `CI=1 node app.js` |
242
+ | Variable | Effect | Example |
243
+ | ---------------- | --------------------------------------------------------------------- | ------------------------------ |
244
+ | `NO_COLOR` | Forces no color output | `NO_COLOR=1 node app.js` |
245
+ | `FORCE_COLOR` | Forces color level: `0`=off, `1`=ANSI‑16, `2`=ANSI‑256, `3`=Truecolor | `FORCE_COLOR=3 node app.js` |
246
+ | `CLICOLOR` | `"0"` disables color | `CLICOLOR=0 node app.js` |
247
+ | `CLICOLOR_FORCE` | Non‑`"0"` value enables color even if not a TTY | `CLICOLOR_FORCE=1 node app.js` |
248
+ | `TERM` | Terminal type; may influence color support | `TERM=xterm-256color` |
249
+ | `COLORTERM` | `'truecolor'` or `'24bit'` enables truecolor | `COLORTERM=truecolor` |
250
+ | `WT_SESSION` | Enables color detection for Windows Terminal | |
251
+ | `CI` | Many CI platforms default to no color | `CI=1 node app.js` |
252
+
253
+ ### <a id="5-6"></a>Colorize Helper (Manual Overrides)
254
+
255
+ Sometimes you want full control over a single piece of text without changing your global palette, e.g. when you use a mostly neutral theme but still want to highlight a keyword.
256
+
257
+ Colorino exposes a small `colorize(text, hex)` helper on every logger instance:
258
+
259
+ ```ts
260
+ import { colorino } from 'colorino'
261
+
262
+ const important = colorino.colorize('IMPORTANT', '#ff5733')
263
+ colorino.info(important, 'Something happened')
264
+ ```
265
+
266
+ When color is disabled (for example via `NO_COLOR=1` or lack of support), `colorize` returns the plain input string, so your logs stay readable.
253
267
 
254
268
  ## <a id="6"></a>Colorino vs. Chalk
255
269
 
256
- | Feature | 🎨 **Colorino** | 🖍️ **Chalk** |
257
- |--------------------------|----------------------------|-----------------|
258
- | Out-of-box logs | ✔ themed, all log levels | ✘ string styling|
259
- | Zero-config | ✔ | ✘ manual, per-use|
260
- | Node + browser | ✔ | ✘ (Node only) |
261
- | CSS console logs | ✔ | ✘ |
262
- | Extensible / Composable | ✔ (via factory) | ✘ |
270
+ | Feature | 🎨 **Colorino** | 🖍️ **Chalk** |
271
+ | ----------------------- | ------------------------ | ----------------- |
272
+ | Out-of-box logs | ✔ themed, all log levels | ✘ string styling |
273
+ | Zero-config | ✔ | ✘ manual, per-use |
274
+ | Node + browser | ✔ | ✘ (Node only) |
275
+ | CSS console logs | ✔ | ✘ |
276
+ | Extensible / Composable | ✔ (via factory) | ✘ |
263
277
 
264
278
  ## <a id="7"></a>API Reference
265
279
 
@@ -289,12 +303,17 @@ A factory function to create your own customized logger instances.
289
303
 
290
304
  Colorino is designed for composition: create a base logger via `createColorino()`, then extend it by inheriting from the base and overriding only the methods you need.
291
305
 
292
- ### <a id="8-1"></a>Use case: automatic file/context info
306
+ ### <a id="8-1"></a>Use Case: Automatic File/Context Info
293
307
 
294
308
  This example prefixes every `.info()` and `.error()` call with best‑effort caller context (file/line) derived from a synthetic `Error` stack.
295
309
 
296
310
  ```ts
297
- import { createColorino, type Colorino, type ColorinoOptions, type Palette } from 'colorino'
311
+ import {
312
+ createColorino,
313
+ type Colorino,
314
+ type ColorinoOptions,
315
+ type Palette,
316
+ } from 'colorino'
298
317
 
299
318
  function getCallerContext(): string {
300
319
  const err = new Error()
@@ -318,15 +337,16 @@ function getCallerContext(): string {
318
337
 
319
338
  export function createContextLogger(
320
339
  palette?: Partial<Palette>,
321
- options?: ColorinoOptions,
340
+ options?: ColorinoOptions
322
341
  ): Colorino {
323
342
  const base = createColorino(palette, options)
324
343
 
325
344
  // Inherit all default methods from the base logger...
326
- const logger = Object.create(base) as Colorino // Object.create uses `base` as the prototype.
345
+ const logger = Object.create(base) as Colorino // Object.create uses `base` as the prototype.
327
346
 
328
347
  // ...and override only what you need.
329
- Object.assign(logger, { // Object.assign copies these methods onto `logger`.
348
+ Object.assign(logger, {
349
+ // Object.assign copies these methods onto `logger`.
330
350
  info(...args: unknown[]) {
331
351
  base.info(`[${getCallerContext()}]`, ...args)
332
352
  },
@@ -359,4 +379,4 @@ logger.error('Failed to load user', { id: 456 })
359
379
 
360
380
  ## <a id="10"></a>License
361
381
 
362
- [MIT](LICENSE.md)
382
+ [MIT](LICENSE.md)
@@ -62,6 +62,8 @@ var ColorLevel = /* @__PURE__ */ ((ColorLevel2) => {
62
62
  function isConsoleMethod(level) {
63
63
  return ["log", "info", "warn", "error", "trace", "debug"].includes(level);
64
64
  }
65
+ const ColorinoBrowserColorized = Symbol("colorino.browserColorized");
66
+ const ColorinoBrowserObject = Symbol("colorino.browserObject");
65
67
 
66
68
  const catppuccinMochaPalette = {
67
69
  log: "#cdd6f4",
@@ -146,6 +148,33 @@ function determineBaseTheme(themeOpt, detectedBrowserTheme) {
146
148
  return baseThemeName;
147
149
  }
148
150
 
151
+ class TypeValidator {
152
+ static isNull(value) {
153
+ return value === null;
154
+ }
155
+ static isObject(value) {
156
+ return typeof value === "object" && value !== null;
157
+ }
158
+ static isString(value) {
159
+ return typeof value === "string";
160
+ }
161
+ static isError(value) {
162
+ return value instanceof Error;
163
+ }
164
+ static isBrowserColorizedArg(value) {
165
+ return typeof value === "object" && value !== null && ColorinoBrowserColorized in value;
166
+ }
167
+ static isBrowserObjectArg(value) {
168
+ return typeof value === "object" && value !== null && ColorinoBrowserObject in value;
169
+ }
170
+ static isAnsiColoredString(value) {
171
+ return TypeValidator.isString(value) && /\x1b\[[0-9;]*m/.test(value);
172
+ }
173
+ static isFormattableObject(value) {
174
+ return TypeValidator.isObject(value) && !TypeValidator.isError(value) && !TypeValidator.isBrowserColorizedArg(value);
175
+ }
176
+ }
177
+
149
178
  class MyColorino {
150
179
  constructor(initialPalette, _userPalette, _validator, _browserColorSupportDetector, _nodeColorSupportDetector, _options = {}) {
151
180
  this._userPalette = _userPalette;
@@ -164,7 +193,7 @@ class MyColorino {
164
193
  const themeOpt = this._options.theme ?? "auto";
165
194
  if (themeOpt === "auto" && this._nodeColorSupportDetector) {
166
195
  this._nodeColorSupportDetector.onTheme((resolvedTheme) => {
167
- this._appllyResolvedTheme(resolvedTheme);
196
+ this._applyResolvedTheme(resolvedTheme);
168
197
  });
169
198
  }
170
199
  }
@@ -172,12 +201,6 @@ class MyColorino {
172
201
  _colorLevel;
173
202
  isBrowser;
174
203
  _palette;
175
- _appllyResolvedTheme(resolvedTheme) {
176
- const themeOpt = this._options.theme ?? "auto";
177
- const baseThemeName = determineBaseTheme(themeOpt, resolvedTheme);
178
- const basePalette = themePalettes[baseThemeName];
179
- this._palette = { ...basePalette, ...this._userPalette };
180
- }
181
204
  log(...args) {
182
205
  this._out("log", args);
183
206
  }
@@ -196,6 +219,32 @@ class MyColorino {
196
219
  debug(...args) {
197
220
  this._out("debug", args);
198
221
  }
222
+ colorize(text, hex) {
223
+ if (this._colorLevel === ColorLevel.NO_COLOR || this._colorLevel === "UnknownEnv") {
224
+ return text;
225
+ }
226
+ if (this.isBrowser) {
227
+ return {
228
+ [ColorinoBrowserColorized]: true,
229
+ text,
230
+ hex
231
+ };
232
+ }
233
+ const ansiPrefix = this._toAnsiPrefix(hex);
234
+ if (!ansiPrefix) {
235
+ return text;
236
+ }
237
+ return `${ansiPrefix}${text}\x1B[0m`;
238
+ }
239
+ _isAnsiColoredString(value) {
240
+ return typeof value === "string" && /\x1b\[[0-9;]*m/.test(value);
241
+ }
242
+ _applyResolvedTheme(resolvedTheme) {
243
+ const themeOpt = this._options.theme ?? "auto";
244
+ const baseThemeName = determineBaseTheme(themeOpt, resolvedTheme);
245
+ const basePalette = themePalettes[baseThemeName];
246
+ this._palette = { ...basePalette, ...this._userPalette };
247
+ }
199
248
  _detectColorSupport() {
200
249
  if (this.isBrowser) {
201
250
  return this._browserColorSupportDetector?.getColorLevel() ?? "UnknownEnv";
@@ -215,7 +264,7 @@ class MyColorino {
215
264
  _formatValue(value, maxDepth = this._options.maxDepth ?? 5) {
216
265
  const seen = /* @__PURE__ */ new WeakSet();
217
266
  const sanitize = (val, currentDepth) => {
218
- if (val === null || typeof val !== "object") return val;
267
+ if (val == null || typeof val !== "object") return val;
219
268
  if (seen.has(val)) return "[Circular]";
220
269
  seen.add(val);
221
270
  if (currentDepth >= maxDepth) return "[Object]";
@@ -233,21 +282,36 @@ class MyColorino {
233
282
  };
234
283
  return JSON.stringify(sanitize(value, 0), null, 2);
235
284
  }
285
+ _normalizeString(value) {
286
+ if (value instanceof String) return value.valueOf();
287
+ return value;
288
+ }
236
289
  _processArgs(args) {
237
290
  const processedArgs = [];
238
291
  let previousWasObject = false;
239
- for (const arg of args) {
240
- const isFormattableObject = arg !== null && typeof arg === "object" && typeof arg !== "string" && !(arg instanceof Error);
241
- const isError = arg instanceof Error;
242
- if (isFormattableObject) {
243
- processedArgs.push(`
292
+ for (const rawArg of args) {
293
+ const arg = this._normalizeString(rawArg);
294
+ if (TypeValidator.isBrowserColorizedArg(arg)) {
295
+ processedArgs.push(arg);
296
+ previousWasObject = false;
297
+ continue;
298
+ }
299
+ if (TypeValidator.isFormattableObject(arg)) {
300
+ if (this.isBrowser) {
301
+ processedArgs.push({
302
+ [ColorinoBrowserObject]: true,
303
+ value: arg
304
+ });
305
+ } else {
306
+ processedArgs.push(`
244
307
  ${this._formatValue(arg)}`);
308
+ }
245
309
  previousWasObject = true;
246
- } else if (isError) {
310
+ } else if (TypeValidator.isError(arg)) {
247
311
  processedArgs.push("\n", this._cleanErrorStack(arg));
248
312
  previousWasObject = true;
249
313
  } else {
250
- if (typeof arg === "string" && previousWasObject) {
314
+ if (TypeValidator.isString(arg) && previousWasObject) {
251
315
  processedArgs.push(`
252
316
  ${arg}`);
253
317
  } else {
@@ -259,48 +323,78 @@ ${arg}`);
259
323
  return processedArgs;
260
324
  }
261
325
  _applyBrowserColors(consoleMethod, args) {
262
- const hex = this._palette[consoleMethod];
263
- if (typeof args[0] === "string") {
264
- return [`%c${args[0]}`, `color:${hex}`, ...args.slice(1)];
326
+ const formatParts = [];
327
+ const cssArgs = [];
328
+ const otherArgs = [];
329
+ const paletteHex = this._palette[consoleMethod];
330
+ for (const rawArg of args) {
331
+ const arg = this._normalizeString(rawArg);
332
+ if (TypeValidator.isBrowserColorizedArg(arg)) {
333
+ formatParts.push(`%c${arg.text}`);
334
+ cssArgs.push(`color:${arg.hex}`);
335
+ continue;
336
+ }
337
+ if (TypeValidator.isBrowserObjectArg(arg)) {
338
+ formatParts.push("%o");
339
+ otherArgs.push(arg.value);
340
+ continue;
341
+ }
342
+ if (TypeValidator.isString(arg)) {
343
+ formatParts.push(`%c${arg}`);
344
+ cssArgs.push(`color:${paletteHex}`);
345
+ continue;
346
+ }
347
+ formatParts.push("%o");
348
+ otherArgs.push(arg);
265
349
  }
266
- return args;
350
+ if (formatParts.length === 0) {
351
+ return args;
352
+ }
353
+ return [formatParts.join(""), ...cssArgs, ...otherArgs];
267
354
  }
268
- _applyNodeColors(consoleMethod, args) {
269
- const hex = this._palette[consoleMethod];
270
- let ansiCode;
355
+ _toAnsiPrefix(hex) {
356
+ if (this._colorLevel === ColorLevel.NO_COLOR || this._colorLevel === "UnknownEnv") {
357
+ return "";
358
+ }
271
359
  switch (this._colorLevel) {
272
360
  case ColorLevel.TRUECOLOR: {
273
361
  const [r, g, b] = colorConverter.hex.toRgb(hex);
274
- ansiCode = `\x1B[38;2;${r};${g};${b}m`;
275
- break;
362
+ return `\x1B[38;2;${r};${g};${b}m`;
276
363
  }
277
364
  case ColorLevel.ANSI256: {
278
365
  const code = colorConverter.hex.toAnsi256(hex);
279
- ansiCode = `\x1B[38;5;${code}m`;
280
- break;
366
+ return `\x1B[38;5;${code}m`;
281
367
  }
282
368
  case ColorLevel.ANSI:
283
369
  default: {
284
370
  const code = colorConverter.hex.toAnsi16(hex);
285
- ansiCode = `\x1B[${code}m`;
286
- break;
371
+ return `\x1B[${code}m`;
287
372
  }
288
373
  }
374
+ }
375
+ _applyNodeColors(consoleMethod, args) {
289
376
  const coloredArgs = [...args];
290
377
  const firstStringIndex = coloredArgs.findIndex(
291
378
  (arg) => typeof arg === "string"
292
379
  );
293
- if (firstStringIndex !== -1) {
294
- coloredArgs[firstStringIndex] = `${ansiCode}${coloredArgs[firstStringIndex]}\x1B[0m`;
380
+ if (firstStringIndex === -1) {
381
+ return coloredArgs;
382
+ }
383
+ const first = coloredArgs[firstStringIndex];
384
+ if (this._isAnsiColoredString(first)) {
385
+ return coloredArgs;
386
+ }
387
+ const hex = this._palette[consoleMethod];
388
+ const ansiPrefix = this._toAnsiPrefix(hex);
389
+ if (!ansiPrefix) {
390
+ return coloredArgs;
295
391
  }
392
+ coloredArgs[firstStringIndex] = `${ansiPrefix}${String(first)}\x1B[0m`;
296
393
  return coloredArgs;
297
394
  }
298
395
  _output(consoleMethod, args) {
299
- if (consoleMethod === "trace") {
300
- this._printCleanTrace(args);
301
- } else {
302
- console[consoleMethod](...args);
303
- }
396
+ if (consoleMethod === "trace") this._printCleanTrace(args);
397
+ else console[consoleMethod](...args);
304
398
  }
305
399
  _out(level, args) {
306
400
  const consoleMethod = isConsoleMethod(level) ? level : "log";
@@ -822,11 +916,11 @@ class Err {
822
916
  }
823
917
  Result.fromThrowable;
824
918
 
825
- class ColorinoError extends Error {
919
+ class InputValidationError extends Error {
826
920
  constructor(message) {
827
921
  super(message);
828
- this.name = "ColorinoError";
829
- Object.setPrototypeOf(this, ColorinoError.prototype);
922
+ this.name = "InputValidationError";
923
+ Object.setPrototypeOf(this, InputValidationError.prototype);
830
924
  }
831
925
  }
832
926
 
@@ -835,7 +929,7 @@ class InputValidator {
835
929
  const trimmedHex = hex.trim();
836
930
  const isHexValid = /^#[0-9A-F]{6}$/i.test(trimmedHex);
837
931
  if (!isHexValid) {
838
- return err(new ColorinoError(`Invalid hex color: '${hex}'`));
932
+ return err(new InputValidationError(`Invalid hex color: '${hex}'`));
839
933
  }
840
934
  return ok(true);
841
935
  }