@likec4/log 1.49.0 → 1.51.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.
@@ -0,0 +1,11 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __exportAll = (all, no_symbols) => {
3
+ let target = {};
4
+ for (var name in all) __defProp(target, name, {
5
+ get: all[name],
6
+ enumerable: true
7
+ });
8
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
9
+ return target;
10
+ };
11
+ export { __exportAll as t };
package/dist/index.d.mts CHANGED
@@ -1,29 +1,61 @@
1
- import * as _logtape_logtape0 from "@logtape/logtape";
2
- import { AnsiColorFormatterOptions, Config, ConsoleFormatter, ConsoleSinkOptions, Filter, LogLevel, LogRecord, LogRecord as LogRecord$1, Logger, Sink, Sink as Sink$1, TextFormatter, TextFormatter as TextFormatter$1, TextFormatterOptions, withFilter } from "@logtape/logtape";
1
+ import { a as withFilter, c as TextFormatter, d as LogRecord, f as LogLevel, i as Sink, l as TextFormatterOptions, n as Config, o as AnsiColorFormatterOptions, r as ConsoleSinkOptions, s as ConsoleFormatter, t as Logger, u as Filter } from "./_chunks/libs/@logtape/logtape.mjs";
3
2
 
4
3
  //#region src/formatters.d.ts
5
- declare function errorFromLogRecord(record: LogRecord$1): Error | null;
6
- declare function getMessageOnlyFormatter(): TextFormatter$1;
7
- declare function getTextFormatter(options?: TextFormatterOptions): TextFormatter$1;
8
- declare function getAnsiColorFormatter(options?: AnsiColorFormatterOptions): TextFormatter$1;
4
+ /**
5
+ * Extract a single Error from a log record (from properties or rawMessage).
6
+ * @param record - Log record that may contain error in properties or rawMessage
7
+ * @returns Merged/wrapped Error or null if none found
8
+ */
9
+ declare function errorFromLogRecord(record: LogRecord): Error | null;
10
+ /**
11
+ * Formatter that outputs only the log message (no timestamp/level/category).
12
+ * @returns TextFormatter that returns just the message string
13
+ */
14
+ declare function getMessageOnlyFormatter(): TextFormatter;
15
+ /**
16
+ * Build a text formatter with optional custom format; appends error from record.
17
+ * @param options - Optional format and logtape options
18
+ * @returns TextFormatter
19
+ */
20
+ declare function getTextFormatter(options?: TextFormatterOptions): TextFormatter;
21
+ /**
22
+ * Build an ANSI-colored text formatter (level/category colors); appends error in red.
23
+ * @param options - Optional format and logtape ANSI options
24
+ * @returns TextFormatter with ANSI colors
25
+ */
26
+ declare function getAnsiColorFormatter(options?: AnsiColorFormatterOptions): TextFormatter;
9
27
  /**
10
28
  * The formatter returns an array where:
11
29
  * - First element is the formatted message string
12
30
  * - Second element is the record properties object
31
+ * @param options - Optional messageFormatter (TextFormatter)
32
+ * @returns ConsoleFormatter for console.log/error
13
33
  */
14
34
  declare function getConsoleFormatter(options?: {
15
- messageFormatter?: TextFormatter$1;
35
+ messageFormatter?: TextFormatter;
16
36
  }): ConsoleFormatter;
17
37
  //#endregion
18
38
  //#region src/sink.d.ts
19
- declare function getConsoleSink(options?: ConsoleSinkOptions): Sink$1;
39
+ /**
40
+ * Create a sink that writes formatted log records to stdout (default formatter).
41
+ * @param options - Optional console sink options (e.g. custom formatter)
42
+ * @returns Sink that writes to stdout
43
+ */
44
+ declare function getConsoleSink(options?: ConsoleSinkOptions): Sink;
20
45
  /**
21
46
  * Creates a console sink that writes to stderr.
22
47
  * (MCP protocol requires stderr to be used for logging)
48
+ * @param options - Optional console sink options (e.g. custom formatter)
49
+ * @returns Sink that writes to stderr
23
50
  */
24
- declare function getConsoleStderrSink(options?: ConsoleSinkOptions): Sink$1;
51
+ declare function getConsoleStderrSink(options?: ConsoleSinkOptions): Sink;
25
52
  //#endregion
26
53
  //#region src/utils.d.ts
54
+ /**
55
+ * Serialize unknown to a string (Error → message + stack; else safe-stringify).
56
+ * @param error - Caught value (Error, string, or arbitrary object)
57
+ * @returns Human-readable string for logging
58
+ */
27
59
  declare function loggable(error: unknown): string;
28
60
  type NormalizeError<ErrorArg> = ErrorArg extends Error ? ErrorArg : Error;
29
61
  /**
@@ -62,14 +94,18 @@ type NormalizeError<ErrorArg> = ErrorArg extends Error ? ErrorArg : Error;
62
94
  declare function wrapError<ErrorArg>(error: ErrorArg, newMessage: string): NormalizeError<ErrorArg>;
63
95
  //#endregion
64
96
  //#region src/index.d.ts
65
- declare const logger: _logtape_logtape0.Logger;
97
+ declare const logger: Logger;
66
98
  /**
67
99
  * Get a child logger with the given subcategory.
68
100
  *
69
101
  * @param subcategory The subcategory.
70
102
  * @returns The child logger.
71
103
  */
72
- declare function createLogger(subcategory: string | readonly [string] | readonly [string, ...string[]]): _logtape_logtape0.Logger;
104
+ declare function createLogger(subcategory: string | readonly [string] | readonly [string, ...string[]]): Logger;
105
+ /**
106
+ * Configure the global logger: sinks, loggers, and lowest level per category.
107
+ * @param config - Optional partial config (sinks, loggers). Merged with defaults.
108
+ */
73
109
  declare function configureLogger<TSinkId extends string, TFilterId extends string>(config?: Partial<Config<TSinkId, TFilterId>>): void;
74
110
  //#endregion
75
111
  export { type Filter, type LogLevel, type LogRecord, type Logger, type Sink, type TextFormatter, configureLogger, logger as consola, logger, logger as rootLogger, createLogger, errorFromLogRecord, getAnsiColorFormatter, getConsoleFormatter, getConsoleSink, getConsoleStderrSink, getMessageOnlyFormatter, getTextFormatter, loggable, withFilter, wrapError };
package/dist/index.mjs CHANGED
@@ -1,16 +1,34 @@
1
+ import { a as getTextFormatter$1, i as getAnsiColorFormatter$1, n as getConsoleSink$1, o as getLogger, r as withFilter, t as configureSync } from "./_chunks/libs/@logtape/logtape.mjs";
1
2
  import { n as wrapErrorMessage, t as mergeErrorCause } from "./_chunks/libs/merge-error-cause.mjs";
3
+ import "./_chunks/libs/is-error-instance.mjs";
4
+ import "./_chunks/libs/is-plain-obj.mjs";
2
5
  import { t as safeStringify } from "./_chunks/libs/safe-stringify.mjs";
3
- import { configureSync, getAnsiColorFormatter as getAnsiColorFormatter$1, getConsoleSink as getConsoleSink$1, getLogger, getTextFormatter as getTextFormatter$1, withFilter } from "@logtape/logtape";
6
+ /**
7
+ * Split stack string into lines and normalize (e.g. strip file://).
8
+ * @param stack - Error stack string
9
+ * @returns Array of normalized lines
10
+ */
4
11
  const parseStack = (stack) => {
5
12
  return stack.split("\n").map((l) => {
6
13
  return l.trim().replace("file://", "");
7
14
  });
8
15
  };
16
+ /**
17
+ * Indent each line of value by the given number of spaces.
18
+ * @param value - String or array of lines to indent
19
+ * @param indentation - Number of spaces (default 2)
20
+ * @returns Indented string
21
+ */
9
22
  function indent(value, indentation = 2) {
10
23
  value = Array.isArray(value) ? value : value.split("\n");
11
24
  const prefix = " ".repeat(indentation);
12
25
  return value.map((l) => `${prefix}${l}`).join("\n");
13
26
  }
27
+ /**
28
+ * Serialize unknown to a string (Error → message + stack; else safe-stringify).
29
+ * @param error - Caught value (Error, string, or arbitrary object)
30
+ * @returns Human-readable string for logging
31
+ */
14
32
  function loggable(error) {
15
33
  if (typeof error === "string") return error;
16
34
  if (error instanceof Error) {
@@ -23,36 +41,80 @@ function loggable(error) {
23
41
  }
24
42
  return safeStringify(error, { indentation: " " });
25
43
  }
44
+ /**
45
+ * Appends `message` to `error.message`. If `message` ends with `:` or `:\n`,
46
+ * prepends it instead.
47
+ *
48
+ * Returns `error`. If `error` is not an `Error` instance, it is converted to
49
+ * one.
50
+ *
51
+ * @example
52
+ * ```js
53
+ * wrapErrorMessage(new Error('Message.'), 'Additional message.')
54
+ * // Error: Message.
55
+ * // Additional message.
56
+ *
57
+ * wrapErrorMessage(new Error('Message.'), 'Additional message:')
58
+ * // Error: Additional message: Message.
59
+ *
60
+ * wrapErrorMessage(new Error('Message.'), 'Additional message:\n')
61
+ * // Error: Additional message:
62
+ * // Message.
63
+ *
64
+ * wrapErrorMessage(new Error('Message.'), '')
65
+ * // Error: Message.
66
+ *
67
+ * const invalidError = 'Message.'
68
+ * wrapErrorMessage(invalidError, 'Additional message.')
69
+ * // Error: Message.
70
+ * // Additional message.
71
+ *
72
+ * wrapErrorMessage(new Error(' Message with spaces '), ' Additional message ')
73
+ * // Error: Message with spaces
74
+ * // Additional message
75
+ * ```
76
+ */
26
77
  function wrapError(error, newMessage) {
27
78
  return wrapErrorMessage(error, newMessage);
28
79
  }
29
- function gerErrorFromLogRecord(record) {
80
+ function getErrorFromLogRecord(record) {
30
81
  const errors = Object.entries(record.properties).flatMap(([k, err]) => {
31
82
  if (err instanceof Error) {
32
83
  const mergedErr = mergeErrorCause(err);
33
84
  if (mergedErr.stack) mergedErr.stack = parseStack(mergedErr.stack).join("\n");
34
85
  return [mergedErr];
35
86
  }
36
- if (k === "error" || k === "err") return [/* @__PURE__ */ new Error(`${err}`)];
87
+ if (k === "error" || k === "err") return [new Error(loggable(err))];
37
88
  return [];
38
89
  });
39
90
  if (errors.length === 0) return null;
40
91
  return errors.length === 1 ? errors[0] : new AggregateError(errors);
41
92
  }
93
+ /**
94
+ * Extract a single Error from a log record (from properties or rawMessage).
95
+ * @param record - Log record that may contain error in properties or rawMessage
96
+ * @returns Merged/wrapped Error or null if none found
97
+ */
42
98
  function errorFromLogRecord(record) {
43
- const error = gerErrorFromLogRecord(record);
99
+ const error = getErrorFromLogRecord(record);
44
100
  if (error && typeof record.rawMessage === "string") return wrapErrorMessage(error, record.rawMessage + "\n");
45
101
  return error;
46
102
  }
103
+ /**
104
+ * Append error from record properties to the formatted message (optionally colored).
105
+ * @param values - Formatted values (record + message)
106
+ * @param color - When true, wrap error text in ANSI red
107
+ * @returns Updated FormattedValues with error appended to message
108
+ */
47
109
  function appendErrorToMessage(values, color = false) {
48
- const error = gerErrorFromLogRecord(values.record);
110
+ const error = getErrorFromLogRecord(values.record);
49
111
  if (error) {
50
- let errorMessge = error.message;
51
- if (error.stack) errorMessge = errorMessge + "\n" + indent(error.stack.split("\n").slice(1));
52
- if (color) errorMessge = `${ansiColors.red}${errorMessge}${RESET}`;
112
+ let errorMessage = error.message;
113
+ if (error.stack) errorMessage = errorMessage + "\n" + indent(error.stack.split("\n").slice(1));
114
+ if (color) errorMessage = `${ansiColors.red}${errorMessage}${RESET}`;
53
115
  return {
54
116
  ...values,
55
- message: values.message + "\n" + indent(errorMessge)
117
+ message: values.message + "\n" + indent(errorMessage)
56
118
  };
57
119
  }
58
120
  return values;
@@ -65,12 +127,21 @@ const levelAbbreviations = {
65
127
  "error": "ERROR",
66
128
  "fatal": "FATAL"
67
129
  };
130
+ /**
131
+ * Formatter that outputs only the log message (no timestamp/level/category).
132
+ * @returns TextFormatter that returns just the message string
133
+ */
68
134
  function getMessageOnlyFormatter() {
69
135
  return getTextFormatter({ format: ({ message }) => {
70
136
  return message;
71
137
  } });
72
138
  }
73
139
  const level = (l) => levelAbbreviations[l];
140
+ /**
141
+ * Build a text formatter with optional custom format; appends error from record.
142
+ * @param options - Optional format and logtape options
143
+ * @returns TextFormatter
144
+ */
74
145
  function getTextFormatter(options) {
75
146
  const _format = options?.format ?? (({ timestamp, level, category, message }) => {
76
147
  return `${timestamp} ${level} ${category} ${message}`;
@@ -87,6 +158,11 @@ function getTextFormatter(options) {
87
158
  }
88
159
  const RESET = "\x1B[0m";
89
160
  const ansiColors = { red: "\x1B[31m" };
161
+ /**
162
+ * Build an ANSI-colored text formatter (level/category colors); appends error in red.
163
+ * @param options - Optional format and logtape ANSI options
164
+ * @returns TextFormatter with ANSI colors
165
+ */
90
166
  function getAnsiColorFormatter(options) {
91
167
  const _format = options?.format ?? (({ timestamp, level, category, message }) => {
92
168
  return `${timestamp} ${level} ${category} ${message}`;
@@ -103,6 +179,13 @@ function getAnsiColorFormatter(options) {
103
179
  }
104
180
  });
105
181
  }
182
+ /**
183
+ * The formatter returns an array where:
184
+ * - First element is the formatted message string
185
+ * - Second element is the record properties object
186
+ * @param options - Optional messageFormatter (TextFormatter)
187
+ * @returns ConsoleFormatter for console.log/error
188
+ */
106
189
  function getConsoleFormatter(options) {
107
190
  const formatter = options?.messageFormatter;
108
191
  if (formatter) return (record) => {
@@ -116,12 +199,23 @@ function getConsoleFormatter(options) {
116
199
  return message;
117
200
  };
118
201
  }
202
+ /**
203
+ * Create a sink that writes formatted log records to stdout (default formatter).
204
+ * @param options - Optional console sink options (e.g. custom formatter)
205
+ * @returns Sink that writes to stdout
206
+ */
119
207
  function getConsoleSink(options) {
120
208
  return getConsoleSink$1({
121
209
  formatter: getConsoleFormatter(),
122
210
  ...options
123
211
  });
124
212
  }
213
+ /**
214
+ * Creates a console sink that writes to stderr.
215
+ * (MCP protocol requires stderr to be used for logging)
216
+ * @param options - Optional console sink options (e.g. custom formatter)
217
+ * @returns Sink that writes to stderr
218
+ */
125
219
  function getConsoleStderrSink(options) {
126
220
  const formatter = options?.formatter ?? getConsoleFormatter();
127
221
  return (record) => {
@@ -133,19 +227,30 @@ function getConsoleStderrSink(options) {
133
227
  };
134
228
  }
135
229
  const logger = getLogger("likec4");
230
+ /**
231
+ * Get a child logger with the given subcategory.
232
+ *
233
+ * @param subcategory The subcategory.
234
+ * @returns The child logger.
235
+ */
136
236
  function createLogger(subcategory) {
137
237
  return logger.getChild(subcategory);
138
238
  }
239
+ /**
240
+ * Configure the global logger: sinks, loggers, and lowest level per category.
241
+ * @param config - Optional partial config (sinks, loggers). Merged with defaults.
242
+ */
139
243
  function configureLogger(config) {
140
244
  try {
141
- const sinks = config?.sinks ?? {};
245
+ const { sinks = {}, loggers: _loggers, ...restConfig } = config ?? {};
246
+ const sinksWithConsole = {
247
+ console: getConsoleSink(),
248
+ ...sinks
249
+ };
142
250
  configureSync({
143
251
  reset: true,
144
- ...config,
145
- sinks: {
146
- console: getConsoleSink(),
147
- ...sinks
148
- },
252
+ ...restConfig,
253
+ sinks: sinksWithConsole,
149
254
  loggers: [{
150
255
  category: ["logtape", "meta"],
151
256
  sinks: ["console"],
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@likec4/log",
3
3
  "description": "Shared interface for logging",
4
4
  "license": "MIT",
5
- "version": "1.49.0",
5
+ "version": "1.51.0",
6
6
  "bugs": "https://github.com/likec4/likec4/issues",
7
7
  "homepage": "https://likec4.dev",
8
8
  "author": "Denis Davydkov <denis@davydkov.com>",
@@ -31,17 +31,15 @@
31
31
  "registry": "https://registry.npmjs.org",
32
32
  "access": "public"
33
33
  },
34
- "dependencies": {
35
- "@logtape/logtape": "^1.3.7"
36
- },
37
34
  "devDependencies": {
38
- "@types/node": "~22.19.10",
35
+ "@logtape/logtape": "^1.3.7",
36
+ "@types/node": "~22.19.11",
39
37
  "merge-error-cause": "^5.0.2",
40
38
  "safe-stringify": "^1.3.0",
41
39
  "typescript": "5.9.3",
42
- "obuild": "^0.4.27",
40
+ "obuild": "^0.4.31",
43
41
  "wrap-error-message": "^3.0.1",
44
- "@likec4/tsconfig": "1.49.0",
42
+ "@likec4/tsconfig": "1.51.0",
45
43
  "@likec4/devops": "1.42.0"
46
44
  },
47
45
  "scripts": {
package/src/formatters.ts CHANGED
@@ -11,9 +11,9 @@ import {
11
11
  } from '@logtape/logtape'
12
12
  import mergeErrorCause from 'merge-error-cause'
13
13
  import wrapErrorMessage from 'wrap-error-message'
14
- import { indent, parseStack } from './utils'
14
+ import { indent, loggable, parseStack } from './utils'
15
15
 
16
- function gerErrorFromLogRecord(record: LogRecord): Error | null {
16
+ function getErrorFromLogRecord(record: LogRecord): Error | null {
17
17
  const errors = Object
18
18
  .entries(record.properties)
19
19
  .flatMap(([k, err]) => {
@@ -25,7 +25,7 @@ function gerErrorFromLogRecord(record: LogRecord): Error | null {
25
25
  return [mergedErr]
26
26
  }
27
27
  if (k === 'error' || k === 'err') {
28
- return [new Error(`${err}`)]
28
+ return [new Error(loggable(err))]
29
29
  }
30
30
  return []
31
31
  })
@@ -35,27 +35,38 @@ function gerErrorFromLogRecord(record: LogRecord): Error | null {
35
35
  return errors.length === 1 ? errors[0]! : new AggregateError(errors)
36
36
  }
37
37
 
38
+ /**
39
+ * Extract a single Error from a log record (from properties or rawMessage).
40
+ * @param record - Log record that may contain error in properties or rawMessage
41
+ * @returns Merged/wrapped Error or null if none found
42
+ */
38
43
  export function errorFromLogRecord(record: LogRecord): Error | null {
39
- const error = gerErrorFromLogRecord(record)
44
+ const error = getErrorFromLogRecord(record)
40
45
  if (error && typeof record.rawMessage === 'string') {
41
46
  return wrapErrorMessage(error, record.rawMessage + '\n')
42
47
  }
43
48
  return error
44
49
  }
45
50
 
51
+ /**
52
+ * Append error from record properties to the formatted message (optionally colored).
53
+ * @param values - Formatted values (record + message)
54
+ * @param color - When true, wrap error text in ANSI red
55
+ * @returns Updated FormattedValues with error appended to message
56
+ */
46
57
  export function appendErrorToMessage(values: FormattedValues, color = false): FormattedValues {
47
- const error = gerErrorFromLogRecord(values.record)
58
+ const error = getErrorFromLogRecord(values.record)
48
59
  if (error) {
49
- let errorMessge = error.message
60
+ let errorMessage = error.message
50
61
  if (error.stack) {
51
- errorMessge = errorMessge + '\n' + indent(error.stack.split('\n').slice(1))
62
+ errorMessage = errorMessage + '\n' + indent(error.stack.split('\n').slice(1))
52
63
  }
53
64
  if (color) {
54
- errorMessge = `${ansiColors.red}${errorMessge}${RESET}`
65
+ errorMessage = `${ansiColors.red}${errorMessage}${RESET}`
55
66
  }
56
67
  return {
57
68
  ...values,
58
- message: values.message + '\n' + indent(errorMessge),
69
+ message: values.message + '\n' + indent(errorMessage),
59
70
  }
60
71
  }
61
72
  return values
@@ -70,6 +81,10 @@ const levelAbbreviations: Record<LogLevel, string> = {
70
81
  'fatal': 'FATAL',
71
82
  }
72
83
 
84
+ /**
85
+ * Formatter that outputs only the log message (no timestamp/level/category).
86
+ * @returns TextFormatter that returns just the message string
87
+ */
73
88
  export function getMessageOnlyFormatter(): TextFormatter {
74
89
  return getTextFormatter({
75
90
  format: ({ message }): string => {
@@ -80,11 +95,15 @@ export function getMessageOnlyFormatter(): TextFormatter {
80
95
 
81
96
  const level = (l: LogLevel): string => levelAbbreviations[l]
82
97
 
98
+ /**
99
+ * Build a text formatter with optional custom format; appends error from record.
100
+ * @param options - Optional format and logtape options
101
+ * @returns TextFormatter
102
+ */
83
103
  export function getTextFormatter(options?: TextFormatterOptions): TextFormatter {
84
104
  const _format = options?.format ?? (({ timestamp, level, category, message }: FormattedValues): string => {
85
105
  return `${timestamp} ${level} ${category} ${message}`
86
106
  })
87
- // const format = options?.format
88
107
  return getLogtapeTextFormatter({
89
108
  timestamp: 'time',
90
109
  level,
@@ -109,6 +128,11 @@ const ansiColors = {
109
128
  // white: "\x1b[37m",
110
129
  } as const
111
130
 
131
+ /**
132
+ * Build an ANSI-colored text formatter (level/category colors); appends error in red.
133
+ * @param options - Optional format and logtape ANSI options
134
+ * @returns TextFormatter with ANSI colors
135
+ */
112
136
  export function getAnsiColorFormatter(options?: AnsiColorFormatterOptions): TextFormatter {
113
137
  const _format = options?.format ?? (({ timestamp, level, category, message }: FormattedValues): string => {
114
138
  return `${timestamp} ${level} ${category} ${message}`
@@ -130,6 +154,8 @@ export function getAnsiColorFormatter(options?: AnsiColorFormatterOptions): Text
130
154
  * The formatter returns an array where:
131
155
  * - First element is the formatted message string
132
156
  * - Second element is the record properties object
157
+ * @param options - Optional messageFormatter (TextFormatter)
158
+ * @returns ConsoleFormatter for console.log/error
133
159
  */
134
160
  export function getConsoleFormatter(options?: {
135
161
  messageFormatter?: TextFormatter
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  type Config,
3
+ type Sink,
3
4
  configureSync as configureLogtape,
4
5
  getLogger,
5
6
  } from '@logtape/logtape'
@@ -53,18 +54,23 @@ export function createLogger(subcategory: string | readonly [string] | readonly
53
54
  return logger.getChild(subcategory)
54
55
  }
55
56
 
57
+ /**
58
+ * Configure the global logger: sinks, loggers, and lowest level per category.
59
+ * @param config - Optional partial config (sinks, loggers). Merged with defaults.
60
+ */
56
61
  export function configureLogger<TSinkId extends string, TFilterId extends string>(
57
62
  config?: Partial<Config<TSinkId, TFilterId>>,
58
63
  ) {
59
64
  try {
60
- const sinks = config?.sinks ?? {}
61
- configureLogtape<any, any>({
65
+ const { sinks = {}, loggers: _loggers, ...restConfig } = config ?? {}
66
+ const sinksWithConsole: Record<TSinkId | 'console', Sink> = {
67
+ console: getConsoleSink(),
68
+ ...sinks,
69
+ } as Record<TSinkId | 'console', Sink>
70
+ configureLogtape<TSinkId | 'console', TFilterId>({
62
71
  reset: true,
63
- ...config,
64
- sinks: {
65
- console: getConsoleSink(),
66
- ...sinks,
67
- },
72
+ ...restConfig,
73
+ sinks: sinksWithConsole,
68
74
  loggers: [
69
75
  { category: ['logtape', 'meta'], sinks: ['console'], lowestLevel: 'warning' },
70
76
  ...(config?.loggers ?? [
package/src/sink.ts CHANGED
@@ -6,6 +6,11 @@ import {
6
6
  } from '@logtape/logtape'
7
7
  import { getConsoleFormatter } from './formatters'
8
8
 
9
+ /**
10
+ * Create a sink that writes formatted log records to stdout (default formatter).
11
+ * @param options - Optional console sink options (e.g. custom formatter)
12
+ * @returns Sink that writes to stdout
13
+ */
9
14
  export function getConsoleSink(options?: ConsoleSinkOptions): Sink {
10
15
  return getLogtapeConsoleSink({
11
16
  formatter: getConsoleFormatter(),
@@ -16,6 +21,8 @@ export function getConsoleSink(options?: ConsoleSinkOptions): Sink {
16
21
  /**
17
22
  * Creates a console sink that writes to stderr.
18
23
  * (MCP protocol requires stderr to be used for logging)
24
+ * @param options - Optional console sink options (e.g. custom formatter)
25
+ * @returns Sink that writes to stderr
19
26
  */
20
27
  export function getConsoleStderrSink(options?: ConsoleSinkOptions): Sink {
21
28
  const formatter = options?.formatter ?? getConsoleFormatter()
package/src/utils.ts CHANGED
@@ -2,6 +2,11 @@ import mergeErrorCause from 'merge-error-cause'
2
2
  import safeStringify from 'safe-stringify'
3
3
  import wrapErrorMessage from 'wrap-error-message'
4
4
 
5
+ /**
6
+ * Split stack string into lines and normalize (e.g. strip file://).
7
+ * @param stack - Error stack string
8
+ * @returns Array of normalized lines
9
+ */
5
10
  export const parseStack = (stack: string): string[] => {
6
11
  const lines = stack
7
12
  .split('\n')
@@ -17,12 +22,23 @@ export const parseStack = (stack: string): string[] => {
17
22
  return lines
18
23
  }
19
24
 
25
+ /**
26
+ * Indent each line of value by the given number of spaces.
27
+ * @param value - String or array of lines to indent
28
+ * @param indentation - Number of spaces (default 2)
29
+ * @returns Indented string
30
+ */
20
31
  export function indent(value: string | string[], indentation = 2): string {
21
32
  value = Array.isArray(value) ? value : value.split('\n')
22
33
  const prefix = ' '.repeat(indentation)
23
34
  return value.map((l) => `${prefix}${l}`).join('\n')
24
35
  }
25
36
 
37
+ /**
38
+ * Serialize unknown to a string (Error → message + stack; else safe-stringify).
39
+ * @param error - Caught value (Error, string, or arbitrary object)
40
+ * @returns Human-readable string for logging
41
+ */
26
42
  export function loggable(error: unknown): string {
27
43
  if (typeof error === 'string') {
28
44
  return error