@optique/logtape 1.0.0-dev.921 → 1.0.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/dist/index.cjs +64 -9
- package/dist/index.d.cts +27 -2
- package/dist/index.d.ts +27 -2
- package/dist/index.js +64 -9
- package/package.json +6 -4
package/dist/index.cjs
CHANGED
|
@@ -25,6 +25,8 @@ const __optique_core_valueparser = __toESM(require("@optique/core/valueparser"))
|
|
|
25
25
|
const __optique_core_primitives = __toESM(require("@optique/core/primitives"));
|
|
26
26
|
const __optique_core_modifiers = __toESM(require("@optique/core/modifiers"));
|
|
27
27
|
const __optique_core_message = __toESM(require("@optique/core/message"));
|
|
28
|
+
const node_path = __toESM(require("node:path"));
|
|
29
|
+
const __optique_core_nonempty = __toESM(require("@optique/core/nonempty"));
|
|
28
30
|
const __optique_core_constructs = __toESM(require("@optique/core/constructs"));
|
|
29
31
|
|
|
30
32
|
//#region src/loglevel.ts
|
|
@@ -40,6 +42,17 @@ const LOG_LEVELS = [
|
|
|
40
42
|
"fatal"
|
|
41
43
|
];
|
|
42
44
|
/**
|
|
45
|
+
* Validates that the given value is a valid {@link LogLevel}.
|
|
46
|
+
*
|
|
47
|
+
* @param value The value to validate.
|
|
48
|
+
* @param paramName The parameter name for the error message.
|
|
49
|
+
* @throws {TypeError} If the value is not a valid log level.
|
|
50
|
+
* @since 1.0.0
|
|
51
|
+
*/
|
|
52
|
+
function validateLogLevel(value, paramName) {
|
|
53
|
+
if (!LOG_LEVELS.includes(value)) throw new TypeError(`Invalid log level for ${paramName}: ${String(value)}. Expected ${LOG_LEVELS.map((l) => `"${l}"`).join(", ")}.`);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
43
56
|
* Creates a {@link ValueParser} for LogTape log levels.
|
|
44
57
|
*
|
|
45
58
|
* This parser validates that the input is one of the valid LogTape severity
|
|
@@ -112,6 +125,7 @@ const WARNING_INDEX = 2;
|
|
|
112
125
|
*
|
|
113
126
|
* @param options Configuration options for the verbosity parser.
|
|
114
127
|
* @returns A {@link Parser} that produces a {@link LogLevel}.
|
|
128
|
+
* @throws {TypeError} If `baseLevel` is not a valid log level.
|
|
115
129
|
*
|
|
116
130
|
* @example Basic usage
|
|
117
131
|
* ```typescript
|
|
@@ -146,6 +160,7 @@ function verbosity(options = {}) {
|
|
|
146
160
|
const short = options.short ?? "-v";
|
|
147
161
|
const long = options.long ?? "--verbose";
|
|
148
162
|
const baseLevel = options.baseLevel ?? "warning";
|
|
163
|
+
validateLogLevel(baseLevel, "baseLevel");
|
|
149
164
|
const baseIndex = VERBOSITY_LEVELS.indexOf(baseLevel);
|
|
150
165
|
const effectiveBaseIndex = baseIndex >= 0 ? baseIndex : WARNING_INDEX;
|
|
151
166
|
const flagParser = (0, __optique_core_primitives.flag)(short, long, { description: options.description ?? __optique_core_message.message`Be more verbose. Can be repeated.` });
|
|
@@ -168,6 +183,8 @@ function verbosity(options = {}) {
|
|
|
168
183
|
*
|
|
169
184
|
* @param options Configuration options for the debug flag parser.
|
|
170
185
|
* @returns A {@link Parser} that produces a {@link LogLevel}.
|
|
186
|
+
* @throws {TypeError} If `debugLevel` or `normalLevel` is not a valid log
|
|
187
|
+
* level.
|
|
171
188
|
*
|
|
172
189
|
* @example Basic usage
|
|
173
190
|
* ```typescript
|
|
@@ -201,6 +218,8 @@ function debug(options = {}) {
|
|
|
201
218
|
const long = options.long ?? "--debug";
|
|
202
219
|
const debugLevel = options.debugLevel ?? "debug";
|
|
203
220
|
const normalLevel = options.normalLevel ?? "info";
|
|
221
|
+
validateLogLevel(debugLevel, "debugLevel");
|
|
222
|
+
validateLogLevel(normalLevel, "normalLevel");
|
|
204
223
|
const flagParser = (0, __optique_core_primitives.flag)(short, long, { description: options.description ?? __optique_core_message.message`Enable debug logging.` });
|
|
205
224
|
return (0, __optique_core_modifiers.map)((0, __optique_core_modifiers.optional)(flagParser), (value) => {
|
|
206
225
|
return value === true ? debugLevel : normalLevel;
|
|
@@ -218,11 +237,15 @@ function debug(options = {}) {
|
|
|
218
237
|
*
|
|
219
238
|
* @param options Configuration options for the parser.
|
|
220
239
|
* @returns A {@link ValueParser} that produces a {@link LogOutput}.
|
|
240
|
+
* @throws {TypeError} If `options.metavar` is an empty string.
|
|
221
241
|
*/
|
|
222
242
|
function logOutputValueParser(options = {}) {
|
|
243
|
+
const metavar = options.metavar ?? "FILE";
|
|
244
|
+
(0, __optique_core_nonempty.ensureNonEmptyString)(metavar);
|
|
223
245
|
return {
|
|
224
|
-
|
|
225
|
-
metavar
|
|
246
|
+
mode: "sync",
|
|
247
|
+
metavar,
|
|
248
|
+
placeholder: { type: "console" },
|
|
226
249
|
parse(input) {
|
|
227
250
|
if (input === "-") return {
|
|
228
251
|
success: true,
|
|
@@ -251,7 +274,8 @@ function logOutputValueParser(options = {}) {
|
|
|
251
274
|
yield {
|
|
252
275
|
kind: "file",
|
|
253
276
|
type: "file",
|
|
254
|
-
pattern: prefix
|
|
277
|
+
pattern: prefix,
|
|
278
|
+
includeHidden: (0, node_path.basename)(prefix).startsWith(".") && (0, node_path.basename)(prefix) !== ".."
|
|
255
279
|
};
|
|
256
280
|
}
|
|
257
281
|
};
|
|
@@ -264,6 +288,7 @@ function logOutputValueParser(options = {}) {
|
|
|
264
288
|
*
|
|
265
289
|
* @param options Configuration options for the log output parser.
|
|
266
290
|
* @returns A {@link Parser} that produces a {@link LogOutput} or `undefined`.
|
|
291
|
+
* @throws {TypeError} If `options.metavar` is an empty string.
|
|
267
292
|
*
|
|
268
293
|
* @example Basic usage
|
|
269
294
|
* ```typescript
|
|
@@ -299,6 +324,10 @@ function logOutput(options = {}) {
|
|
|
299
324
|
*
|
|
300
325
|
* @param options Configuration options for the console sink.
|
|
301
326
|
* @returns A {@link Sink} function.
|
|
327
|
+
* @throws {TypeError} If `options.stream` is not `"stdout"` or `"stderr"`
|
|
328
|
+
* when `streamResolver` is not provided.
|
|
329
|
+
* @throws {TypeError} If `streamResolver` returns a value other than
|
|
330
|
+
* `"stdout"` or `"stderr"`.
|
|
302
331
|
*
|
|
303
332
|
* @example Static stream selection
|
|
304
333
|
* ```typescript
|
|
@@ -320,10 +349,23 @@ function logOutput(options = {}) {
|
|
|
320
349
|
* @since 0.8.0
|
|
321
350
|
*/
|
|
322
351
|
function createConsoleSink(options = {}) {
|
|
323
|
-
const defaultStream = options.stream ?? "stderr";
|
|
324
352
|
const streamResolver = options.streamResolver;
|
|
353
|
+
const defaultStream = options.stream ?? "stderr";
|
|
354
|
+
const invalidStreamError = (value) => {
|
|
355
|
+
let repr;
|
|
356
|
+
if (typeof value === "string") repr = JSON.stringify(value);
|
|
357
|
+
else if (value === null || typeof value !== "object") repr = String(value);
|
|
358
|
+
else try {
|
|
359
|
+
repr = JSON.stringify(value) ?? String(value);
|
|
360
|
+
} catch {
|
|
361
|
+
repr = String(value);
|
|
362
|
+
}
|
|
363
|
+
return /* @__PURE__ */ new TypeError(`Invalid stream: expected "stdout" or "stderr", got ${repr}.`);
|
|
364
|
+
};
|
|
365
|
+
if (!streamResolver && defaultStream !== "stdout" && defaultStream !== "stderr") throw invalidStreamError(defaultStream);
|
|
325
366
|
return (record) => {
|
|
326
367
|
const stream = streamResolver ? streamResolver(record.level) : defaultStream;
|
|
368
|
+
if (stream !== "stdout" && stream !== "stderr") throw invalidStreamError(stream);
|
|
327
369
|
const messageParts = [];
|
|
328
370
|
for (let i = 0; i < record.message.length; i++) {
|
|
329
371
|
const part = record.message[i];
|
|
@@ -331,7 +373,8 @@ function createConsoleSink(options = {}) {
|
|
|
331
373
|
else messageParts.push(String(part));
|
|
332
374
|
}
|
|
333
375
|
const formattedMessage = messageParts.join("");
|
|
334
|
-
const
|
|
376
|
+
const ts = record.timestamp;
|
|
377
|
+
const timestamp = new Date(ts != null && !Number.isNaN(ts) ? ts : Date.now()).toISOString();
|
|
335
378
|
const category = record.category.join(".");
|
|
336
379
|
const level = record.level.toUpperCase().padEnd(7);
|
|
337
380
|
const line = `${timestamp} [${level}] ${category}: ${formattedMessage}`;
|
|
@@ -348,7 +391,11 @@ function createConsoleSink(options = {}) {
|
|
|
348
391
|
* @param output The log output destination.
|
|
349
392
|
* @param consoleSinkOptions Options for console sink (only used when output is console).
|
|
350
393
|
* @returns A promise that resolves to a {@link Sink}.
|
|
351
|
-
* @throws {Error} If file output is requested but `@logtape/file` is not
|
|
394
|
+
* @throws {Error} If file output is requested but `@logtape/file` is not
|
|
395
|
+
* installed.
|
|
396
|
+
* @throws If `@logtape/file` is installed but `getFileSink(output.path)` fails
|
|
397
|
+
* at runtime (e.g., the target directory does not exist), the original error
|
|
398
|
+
* propagates as-is.
|
|
352
399
|
*
|
|
353
400
|
* @example Console output
|
|
354
401
|
* ```typescript
|
|
@@ -368,9 +415,9 @@ function createConsoleSink(options = {}) {
|
|
|
368
415
|
*/
|
|
369
416
|
async function createSink(output, consoleSinkOptions = {}) {
|
|
370
417
|
if (output.type === "console") return createConsoleSink(consoleSinkOptions);
|
|
418
|
+
let getFileSink;
|
|
371
419
|
try {
|
|
372
|
-
|
|
373
|
-
return getFileSink(output.path);
|
|
420
|
+
({getFileSink} = await import("@logtape/file"));
|
|
374
421
|
} catch (e) {
|
|
375
422
|
throw new Error(`File sink requires @logtape/file package. Install it with:
|
|
376
423
|
npm install @logtape/file
|
|
@@ -379,6 +426,7 @@ async function createSink(output, consoleSinkOptions = {}) {
|
|
|
379
426
|
|
|
380
427
|
Original error: ${e}`);
|
|
381
428
|
}
|
|
429
|
+
return getFileSink(output.path);
|
|
382
430
|
}
|
|
383
431
|
|
|
384
432
|
//#endregion
|
|
@@ -430,6 +478,9 @@ Original error: ${e}`);
|
|
|
430
478
|
* // --debug
|
|
431
479
|
* ```
|
|
432
480
|
*
|
|
481
|
+
* @throws {TypeError} If the `level` discriminant is not one of `"option"`,
|
|
482
|
+
* `"verbosity"`, or `"debug"`, or if a log level option (`default`,
|
|
483
|
+
* `debugLevel`, `normalLevel`, or `baseLevel`) is not a valid log level.
|
|
433
484
|
* @since 0.8.0
|
|
434
485
|
*/
|
|
435
486
|
function loggingOptions(config) {
|
|
@@ -442,6 +493,7 @@ function loggingOptions(config) {
|
|
|
442
493
|
const long = config.long ?? "--log-level";
|
|
443
494
|
const short = config.short ?? "-l";
|
|
444
495
|
const defaultLevel = config.default ?? "info";
|
|
496
|
+
validateLogLevel(defaultLevel, "default");
|
|
445
497
|
levelParser = (0, __optique_core_modifiers.withDefault)((0, __optique_core_primitives.option)(short, long, logLevel()), defaultLevel);
|
|
446
498
|
break;
|
|
447
499
|
}
|
|
@@ -464,16 +516,19 @@ function loggingOptions(config) {
|
|
|
464
516
|
levelParser = debug(debugOptions);
|
|
465
517
|
break;
|
|
466
518
|
}
|
|
519
|
+
default: throw new TypeError(`Unsupported level configuration: ${String(config.level)}. Expected "option", "verbosity", or "debug".`);
|
|
467
520
|
}
|
|
468
521
|
const defaultOutput = { type: "console" };
|
|
469
522
|
const outputParser = (0, __optique_core_modifiers.withDefault)(logOutput({ long: outputLong }), defaultOutput);
|
|
470
523
|
if (!outputEnabled) {
|
|
471
524
|
const constantOutputParser = {
|
|
472
|
-
|
|
525
|
+
mode: "sync",
|
|
473
526
|
$valueType: [],
|
|
474
527
|
$stateType: [],
|
|
475
528
|
priority: 0,
|
|
476
529
|
usage: [],
|
|
530
|
+
leadingNames: /* @__PURE__ */ new Set(),
|
|
531
|
+
acceptingAnyToken: false,
|
|
477
532
|
initialState: void 0,
|
|
478
533
|
parse: (context) => ({
|
|
479
534
|
success: true,
|
package/dist/index.d.cts
CHANGED
|
@@ -8,6 +8,15 @@ import { Parser } from "@optique/core/parser";
|
|
|
8
8
|
* All valid log levels in order from lowest to highest severity.
|
|
9
9
|
*/
|
|
10
10
|
declare const LOG_LEVELS: readonly LogLevel$1[];
|
|
11
|
+
/**
|
|
12
|
+
* Validates that the given value is a valid {@link LogLevel}.
|
|
13
|
+
*
|
|
14
|
+
* @param value The value to validate.
|
|
15
|
+
* @param paramName The parameter name for the error message.
|
|
16
|
+
* @throws {TypeError} If the value is not a valid log level.
|
|
17
|
+
* @since 1.0.0
|
|
18
|
+
*/
|
|
19
|
+
|
|
11
20
|
/**
|
|
12
21
|
* Options for creating a log level value parser.
|
|
13
22
|
* @since 0.8.0
|
|
@@ -105,6 +114,7 @@ interface VerbosityOptions {
|
|
|
105
114
|
*
|
|
106
115
|
* @param options Configuration options for the verbosity parser.
|
|
107
116
|
* @returns A {@link Parser} that produces a {@link LogLevel}.
|
|
117
|
+
* @throws {TypeError} If `baseLevel` is not a valid log level.
|
|
108
118
|
*
|
|
109
119
|
* @example Basic usage
|
|
110
120
|
* ```typescript
|
|
@@ -177,6 +187,8 @@ interface DebugOptions {
|
|
|
177
187
|
*
|
|
178
188
|
* @param options Configuration options for the debug flag parser.
|
|
179
189
|
* @returns A {@link Parser} that produces a {@link LogLevel}.
|
|
190
|
+
* @throws {TypeError} If `debugLevel` or `normalLevel` is not a valid log
|
|
191
|
+
* level.
|
|
180
192
|
*
|
|
181
193
|
* @example Basic usage
|
|
182
194
|
* ```typescript
|
|
@@ -228,9 +240,10 @@ type LogOutput = {
|
|
|
228
240
|
interface ConsoleSinkOptions {
|
|
229
241
|
/**
|
|
230
242
|
* The stream to write to. Either `"stdout"` or `"stderr"`.
|
|
243
|
+
* If `null` or `undefined`, defaults to `"stderr"`.
|
|
231
244
|
* @default `"stderr"`
|
|
232
245
|
*/
|
|
233
|
-
readonly stream?: "stdout" | "stderr";
|
|
246
|
+
readonly stream?: "stdout" | "stderr" | null;
|
|
234
247
|
/**
|
|
235
248
|
* A function that determines which stream to use based on the log level.
|
|
236
249
|
* If provided, this takes precedence over the `stream` option.
|
|
@@ -287,6 +300,7 @@ interface LogOutputOptions {
|
|
|
287
300
|
*
|
|
288
301
|
* @param options Configuration options for the log output parser.
|
|
289
302
|
* @returns A {@link Parser} that produces a {@link LogOutput} or `undefined`.
|
|
303
|
+
* @throws {TypeError} If `options.metavar` is an empty string.
|
|
290
304
|
*
|
|
291
305
|
* @example Basic usage
|
|
292
306
|
* ```typescript
|
|
@@ -313,6 +327,10 @@ declare function logOutput(options?: LogOutputOptions): Parser<"sync", LogOutput
|
|
|
313
327
|
*
|
|
314
328
|
* @param options Configuration options for the console sink.
|
|
315
329
|
* @returns A {@link Sink} function.
|
|
330
|
+
* @throws {TypeError} If `options.stream` is not `"stdout"` or `"stderr"`
|
|
331
|
+
* when `streamResolver` is not provided.
|
|
332
|
+
* @throws {TypeError} If `streamResolver` returns a value other than
|
|
333
|
+
* `"stdout"` or `"stderr"`.
|
|
316
334
|
*
|
|
317
335
|
* @example Static stream selection
|
|
318
336
|
* ```typescript
|
|
@@ -343,7 +361,11 @@ declare function createConsoleSink(options?: ConsoleSinkOptions): Sink$1;
|
|
|
343
361
|
* @param output The log output destination.
|
|
344
362
|
* @param consoleSinkOptions Options for console sink (only used when output is console).
|
|
345
363
|
* @returns A promise that resolves to a {@link Sink}.
|
|
346
|
-
* @throws {Error} If file output is requested but `@logtape/file` is not
|
|
364
|
+
* @throws {Error} If file output is requested but `@logtape/file` is not
|
|
365
|
+
* installed.
|
|
366
|
+
* @throws If `@logtape/file` is installed but `getFileSink(output.path)` fails
|
|
367
|
+
* at runtime (e.g., the target directory does not exist), the original error
|
|
368
|
+
* propagates as-is.
|
|
347
369
|
*
|
|
348
370
|
* @example Console output
|
|
349
371
|
* ```typescript
|
|
@@ -561,6 +583,9 @@ type LoggingOptionsConfig = LoggingOptionsWithLevel | LoggingOptionsWithVerbosit
|
|
|
561
583
|
* // --debug
|
|
562
584
|
* ```
|
|
563
585
|
*
|
|
586
|
+
* @throws {TypeError} If the `level` discriminant is not one of `"option"`,
|
|
587
|
+
* `"verbosity"`, or `"debug"`, or if a log level option (`default`,
|
|
588
|
+
* `debugLevel`, `normalLevel`, or `baseLevel`) is not a valid log level.
|
|
564
589
|
* @since 0.8.0
|
|
565
590
|
*/
|
|
566
591
|
declare function loggingOptions(config: LoggingOptionsConfig): Parser<"sync", LoggingOptionsResult, unknown>;
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,15 @@ import { Parser } from "@optique/core/parser";
|
|
|
8
8
|
* All valid log levels in order from lowest to highest severity.
|
|
9
9
|
*/
|
|
10
10
|
declare const LOG_LEVELS: readonly LogLevel$1[];
|
|
11
|
+
/**
|
|
12
|
+
* Validates that the given value is a valid {@link LogLevel}.
|
|
13
|
+
*
|
|
14
|
+
* @param value The value to validate.
|
|
15
|
+
* @param paramName The parameter name for the error message.
|
|
16
|
+
* @throws {TypeError} If the value is not a valid log level.
|
|
17
|
+
* @since 1.0.0
|
|
18
|
+
*/
|
|
19
|
+
|
|
11
20
|
/**
|
|
12
21
|
* Options for creating a log level value parser.
|
|
13
22
|
* @since 0.8.0
|
|
@@ -105,6 +114,7 @@ interface VerbosityOptions {
|
|
|
105
114
|
*
|
|
106
115
|
* @param options Configuration options for the verbosity parser.
|
|
107
116
|
* @returns A {@link Parser} that produces a {@link LogLevel}.
|
|
117
|
+
* @throws {TypeError} If `baseLevel` is not a valid log level.
|
|
108
118
|
*
|
|
109
119
|
* @example Basic usage
|
|
110
120
|
* ```typescript
|
|
@@ -177,6 +187,8 @@ interface DebugOptions {
|
|
|
177
187
|
*
|
|
178
188
|
* @param options Configuration options for the debug flag parser.
|
|
179
189
|
* @returns A {@link Parser} that produces a {@link LogLevel}.
|
|
190
|
+
* @throws {TypeError} If `debugLevel` or `normalLevel` is not a valid log
|
|
191
|
+
* level.
|
|
180
192
|
*
|
|
181
193
|
* @example Basic usage
|
|
182
194
|
* ```typescript
|
|
@@ -228,9 +240,10 @@ type LogOutput = {
|
|
|
228
240
|
interface ConsoleSinkOptions {
|
|
229
241
|
/**
|
|
230
242
|
* The stream to write to. Either `"stdout"` or `"stderr"`.
|
|
243
|
+
* If `null` or `undefined`, defaults to `"stderr"`.
|
|
231
244
|
* @default `"stderr"`
|
|
232
245
|
*/
|
|
233
|
-
readonly stream?: "stdout" | "stderr";
|
|
246
|
+
readonly stream?: "stdout" | "stderr" | null;
|
|
234
247
|
/**
|
|
235
248
|
* A function that determines which stream to use based on the log level.
|
|
236
249
|
* If provided, this takes precedence over the `stream` option.
|
|
@@ -287,6 +300,7 @@ interface LogOutputOptions {
|
|
|
287
300
|
*
|
|
288
301
|
* @param options Configuration options for the log output parser.
|
|
289
302
|
* @returns A {@link Parser} that produces a {@link LogOutput} or `undefined`.
|
|
303
|
+
* @throws {TypeError} If `options.metavar` is an empty string.
|
|
290
304
|
*
|
|
291
305
|
* @example Basic usage
|
|
292
306
|
* ```typescript
|
|
@@ -313,6 +327,10 @@ declare function logOutput(options?: LogOutputOptions): Parser<"sync", LogOutput
|
|
|
313
327
|
*
|
|
314
328
|
* @param options Configuration options for the console sink.
|
|
315
329
|
* @returns A {@link Sink} function.
|
|
330
|
+
* @throws {TypeError} If `options.stream` is not `"stdout"` or `"stderr"`
|
|
331
|
+
* when `streamResolver` is not provided.
|
|
332
|
+
* @throws {TypeError} If `streamResolver` returns a value other than
|
|
333
|
+
* `"stdout"` or `"stderr"`.
|
|
316
334
|
*
|
|
317
335
|
* @example Static stream selection
|
|
318
336
|
* ```typescript
|
|
@@ -343,7 +361,11 @@ declare function createConsoleSink(options?: ConsoleSinkOptions): Sink$1;
|
|
|
343
361
|
* @param output The log output destination.
|
|
344
362
|
* @param consoleSinkOptions Options for console sink (only used when output is console).
|
|
345
363
|
* @returns A promise that resolves to a {@link Sink}.
|
|
346
|
-
* @throws {Error} If file output is requested but `@logtape/file` is not
|
|
364
|
+
* @throws {Error} If file output is requested but `@logtape/file` is not
|
|
365
|
+
* installed.
|
|
366
|
+
* @throws If `@logtape/file` is installed but `getFileSink(output.path)` fails
|
|
367
|
+
* at runtime (e.g., the target directory does not exist), the original error
|
|
368
|
+
* propagates as-is.
|
|
347
369
|
*
|
|
348
370
|
* @example Console output
|
|
349
371
|
* ```typescript
|
|
@@ -561,6 +583,9 @@ type LoggingOptionsConfig = LoggingOptionsWithLevel | LoggingOptionsWithVerbosit
|
|
|
561
583
|
* // --debug
|
|
562
584
|
* ```
|
|
563
585
|
*
|
|
586
|
+
* @throws {TypeError} If the `level` discriminant is not one of `"option"`,
|
|
587
|
+
* `"verbosity"`, or `"debug"`, or if a log level option (`default`,
|
|
588
|
+
* `debugLevel`, `normalLevel`, or `baseLevel`) is not a valid log level.
|
|
564
589
|
* @since 0.8.0
|
|
565
590
|
*/
|
|
566
591
|
declare function loggingOptions(config: LoggingOptionsConfig): Parser<"sync", LoggingOptionsResult, unknown>;
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,8 @@ import { choice } from "@optique/core/valueparser";
|
|
|
2
2
|
import { flag, option } from "@optique/core/primitives";
|
|
3
3
|
import { map, multiple, optional, withDefault } from "@optique/core/modifiers";
|
|
4
4
|
import { message } from "@optique/core/message";
|
|
5
|
+
import { basename } from "node:path";
|
|
6
|
+
import { ensureNonEmptyString } from "@optique/core/nonempty";
|
|
5
7
|
import { group, object } from "@optique/core/constructs";
|
|
6
8
|
|
|
7
9
|
//#region src/loglevel.ts
|
|
@@ -17,6 +19,17 @@ const LOG_LEVELS = [
|
|
|
17
19
|
"fatal"
|
|
18
20
|
];
|
|
19
21
|
/**
|
|
22
|
+
* Validates that the given value is a valid {@link LogLevel}.
|
|
23
|
+
*
|
|
24
|
+
* @param value The value to validate.
|
|
25
|
+
* @param paramName The parameter name for the error message.
|
|
26
|
+
* @throws {TypeError} If the value is not a valid log level.
|
|
27
|
+
* @since 1.0.0
|
|
28
|
+
*/
|
|
29
|
+
function validateLogLevel(value, paramName) {
|
|
30
|
+
if (!LOG_LEVELS.includes(value)) throw new TypeError(`Invalid log level for ${paramName}: ${String(value)}. Expected ${LOG_LEVELS.map((l) => `"${l}"`).join(", ")}.`);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
20
33
|
* Creates a {@link ValueParser} for LogTape log levels.
|
|
21
34
|
*
|
|
22
35
|
* This parser validates that the input is one of the valid LogTape severity
|
|
@@ -89,6 +102,7 @@ const WARNING_INDEX = 2;
|
|
|
89
102
|
*
|
|
90
103
|
* @param options Configuration options for the verbosity parser.
|
|
91
104
|
* @returns A {@link Parser} that produces a {@link LogLevel}.
|
|
105
|
+
* @throws {TypeError} If `baseLevel` is not a valid log level.
|
|
92
106
|
*
|
|
93
107
|
* @example Basic usage
|
|
94
108
|
* ```typescript
|
|
@@ -123,6 +137,7 @@ function verbosity(options = {}) {
|
|
|
123
137
|
const short = options.short ?? "-v";
|
|
124
138
|
const long = options.long ?? "--verbose";
|
|
125
139
|
const baseLevel = options.baseLevel ?? "warning";
|
|
140
|
+
validateLogLevel(baseLevel, "baseLevel");
|
|
126
141
|
const baseIndex = VERBOSITY_LEVELS.indexOf(baseLevel);
|
|
127
142
|
const effectiveBaseIndex = baseIndex >= 0 ? baseIndex : WARNING_INDEX;
|
|
128
143
|
const flagParser = flag(short, long, { description: options.description ?? message`Be more verbose. Can be repeated.` });
|
|
@@ -145,6 +160,8 @@ function verbosity(options = {}) {
|
|
|
145
160
|
*
|
|
146
161
|
* @param options Configuration options for the debug flag parser.
|
|
147
162
|
* @returns A {@link Parser} that produces a {@link LogLevel}.
|
|
163
|
+
* @throws {TypeError} If `debugLevel` or `normalLevel` is not a valid log
|
|
164
|
+
* level.
|
|
148
165
|
*
|
|
149
166
|
* @example Basic usage
|
|
150
167
|
* ```typescript
|
|
@@ -178,6 +195,8 @@ function debug(options = {}) {
|
|
|
178
195
|
const long = options.long ?? "--debug";
|
|
179
196
|
const debugLevel = options.debugLevel ?? "debug";
|
|
180
197
|
const normalLevel = options.normalLevel ?? "info";
|
|
198
|
+
validateLogLevel(debugLevel, "debugLevel");
|
|
199
|
+
validateLogLevel(normalLevel, "normalLevel");
|
|
181
200
|
const flagParser = flag(short, long, { description: options.description ?? message`Enable debug logging.` });
|
|
182
201
|
return map(optional(flagParser), (value) => {
|
|
183
202
|
return value === true ? debugLevel : normalLevel;
|
|
@@ -195,11 +214,15 @@ function debug(options = {}) {
|
|
|
195
214
|
*
|
|
196
215
|
* @param options Configuration options for the parser.
|
|
197
216
|
* @returns A {@link ValueParser} that produces a {@link LogOutput}.
|
|
217
|
+
* @throws {TypeError} If `options.metavar` is an empty string.
|
|
198
218
|
*/
|
|
199
219
|
function logOutputValueParser(options = {}) {
|
|
220
|
+
const metavar = options.metavar ?? "FILE";
|
|
221
|
+
ensureNonEmptyString(metavar);
|
|
200
222
|
return {
|
|
201
|
-
|
|
202
|
-
metavar
|
|
223
|
+
mode: "sync",
|
|
224
|
+
metavar,
|
|
225
|
+
placeholder: { type: "console" },
|
|
203
226
|
parse(input) {
|
|
204
227
|
if (input === "-") return {
|
|
205
228
|
success: true,
|
|
@@ -228,7 +251,8 @@ function logOutputValueParser(options = {}) {
|
|
|
228
251
|
yield {
|
|
229
252
|
kind: "file",
|
|
230
253
|
type: "file",
|
|
231
|
-
pattern: prefix
|
|
254
|
+
pattern: prefix,
|
|
255
|
+
includeHidden: basename(prefix).startsWith(".") && basename(prefix) !== ".."
|
|
232
256
|
};
|
|
233
257
|
}
|
|
234
258
|
};
|
|
@@ -241,6 +265,7 @@ function logOutputValueParser(options = {}) {
|
|
|
241
265
|
*
|
|
242
266
|
* @param options Configuration options for the log output parser.
|
|
243
267
|
* @returns A {@link Parser} that produces a {@link LogOutput} or `undefined`.
|
|
268
|
+
* @throws {TypeError} If `options.metavar` is an empty string.
|
|
244
269
|
*
|
|
245
270
|
* @example Basic usage
|
|
246
271
|
* ```typescript
|
|
@@ -276,6 +301,10 @@ function logOutput(options = {}) {
|
|
|
276
301
|
*
|
|
277
302
|
* @param options Configuration options for the console sink.
|
|
278
303
|
* @returns A {@link Sink} function.
|
|
304
|
+
* @throws {TypeError} If `options.stream` is not `"stdout"` or `"stderr"`
|
|
305
|
+
* when `streamResolver` is not provided.
|
|
306
|
+
* @throws {TypeError} If `streamResolver` returns a value other than
|
|
307
|
+
* `"stdout"` or `"stderr"`.
|
|
279
308
|
*
|
|
280
309
|
* @example Static stream selection
|
|
281
310
|
* ```typescript
|
|
@@ -297,10 +326,23 @@ function logOutput(options = {}) {
|
|
|
297
326
|
* @since 0.8.0
|
|
298
327
|
*/
|
|
299
328
|
function createConsoleSink(options = {}) {
|
|
300
|
-
const defaultStream = options.stream ?? "stderr";
|
|
301
329
|
const streamResolver = options.streamResolver;
|
|
330
|
+
const defaultStream = options.stream ?? "stderr";
|
|
331
|
+
const invalidStreamError = (value) => {
|
|
332
|
+
let repr;
|
|
333
|
+
if (typeof value === "string") repr = JSON.stringify(value);
|
|
334
|
+
else if (value === null || typeof value !== "object") repr = String(value);
|
|
335
|
+
else try {
|
|
336
|
+
repr = JSON.stringify(value) ?? String(value);
|
|
337
|
+
} catch {
|
|
338
|
+
repr = String(value);
|
|
339
|
+
}
|
|
340
|
+
return /* @__PURE__ */ new TypeError(`Invalid stream: expected "stdout" or "stderr", got ${repr}.`);
|
|
341
|
+
};
|
|
342
|
+
if (!streamResolver && defaultStream !== "stdout" && defaultStream !== "stderr") throw invalidStreamError(defaultStream);
|
|
302
343
|
return (record) => {
|
|
303
344
|
const stream = streamResolver ? streamResolver(record.level) : defaultStream;
|
|
345
|
+
if (stream !== "stdout" && stream !== "stderr") throw invalidStreamError(stream);
|
|
304
346
|
const messageParts = [];
|
|
305
347
|
for (let i = 0; i < record.message.length; i++) {
|
|
306
348
|
const part = record.message[i];
|
|
@@ -308,7 +350,8 @@ function createConsoleSink(options = {}) {
|
|
|
308
350
|
else messageParts.push(String(part));
|
|
309
351
|
}
|
|
310
352
|
const formattedMessage = messageParts.join("");
|
|
311
|
-
const
|
|
353
|
+
const ts = record.timestamp;
|
|
354
|
+
const timestamp = new Date(ts != null && !Number.isNaN(ts) ? ts : Date.now()).toISOString();
|
|
312
355
|
const category = record.category.join(".");
|
|
313
356
|
const level = record.level.toUpperCase().padEnd(7);
|
|
314
357
|
const line = `${timestamp} [${level}] ${category}: ${formattedMessage}`;
|
|
@@ -325,7 +368,11 @@ function createConsoleSink(options = {}) {
|
|
|
325
368
|
* @param output The log output destination.
|
|
326
369
|
* @param consoleSinkOptions Options for console sink (only used when output is console).
|
|
327
370
|
* @returns A promise that resolves to a {@link Sink}.
|
|
328
|
-
* @throws {Error} If file output is requested but `@logtape/file` is not
|
|
371
|
+
* @throws {Error} If file output is requested but `@logtape/file` is not
|
|
372
|
+
* installed.
|
|
373
|
+
* @throws If `@logtape/file` is installed but `getFileSink(output.path)` fails
|
|
374
|
+
* at runtime (e.g., the target directory does not exist), the original error
|
|
375
|
+
* propagates as-is.
|
|
329
376
|
*
|
|
330
377
|
* @example Console output
|
|
331
378
|
* ```typescript
|
|
@@ -345,9 +392,9 @@ function createConsoleSink(options = {}) {
|
|
|
345
392
|
*/
|
|
346
393
|
async function createSink(output, consoleSinkOptions = {}) {
|
|
347
394
|
if (output.type === "console") return createConsoleSink(consoleSinkOptions);
|
|
395
|
+
let getFileSink;
|
|
348
396
|
try {
|
|
349
|
-
|
|
350
|
-
return getFileSink(output.path);
|
|
397
|
+
({getFileSink} = await import("@logtape/file"));
|
|
351
398
|
} catch (e) {
|
|
352
399
|
throw new Error(`File sink requires @logtape/file package. Install it with:
|
|
353
400
|
npm install @logtape/file
|
|
@@ -356,6 +403,7 @@ async function createSink(output, consoleSinkOptions = {}) {
|
|
|
356
403
|
|
|
357
404
|
Original error: ${e}`);
|
|
358
405
|
}
|
|
406
|
+
return getFileSink(output.path);
|
|
359
407
|
}
|
|
360
408
|
|
|
361
409
|
//#endregion
|
|
@@ -407,6 +455,9 @@ Original error: ${e}`);
|
|
|
407
455
|
* // --debug
|
|
408
456
|
* ```
|
|
409
457
|
*
|
|
458
|
+
* @throws {TypeError} If the `level` discriminant is not one of `"option"`,
|
|
459
|
+
* `"verbosity"`, or `"debug"`, or if a log level option (`default`,
|
|
460
|
+
* `debugLevel`, `normalLevel`, or `baseLevel`) is not a valid log level.
|
|
410
461
|
* @since 0.8.0
|
|
411
462
|
*/
|
|
412
463
|
function loggingOptions(config) {
|
|
@@ -419,6 +470,7 @@ function loggingOptions(config) {
|
|
|
419
470
|
const long = config.long ?? "--log-level";
|
|
420
471
|
const short = config.short ?? "-l";
|
|
421
472
|
const defaultLevel = config.default ?? "info";
|
|
473
|
+
validateLogLevel(defaultLevel, "default");
|
|
422
474
|
levelParser = withDefault(option(short, long, logLevel()), defaultLevel);
|
|
423
475
|
break;
|
|
424
476
|
}
|
|
@@ -441,16 +493,19 @@ function loggingOptions(config) {
|
|
|
441
493
|
levelParser = debug(debugOptions);
|
|
442
494
|
break;
|
|
443
495
|
}
|
|
496
|
+
default: throw new TypeError(`Unsupported level configuration: ${String(config.level)}. Expected "option", "verbosity", or "debug".`);
|
|
444
497
|
}
|
|
445
498
|
const defaultOutput = { type: "console" };
|
|
446
499
|
const outputParser = withDefault(logOutput({ long: outputLong }), defaultOutput);
|
|
447
500
|
if (!outputEnabled) {
|
|
448
501
|
const constantOutputParser = {
|
|
449
|
-
|
|
502
|
+
mode: "sync",
|
|
450
503
|
$valueType: [],
|
|
451
504
|
$stateType: [],
|
|
452
505
|
priority: 0,
|
|
453
506
|
usage: [],
|
|
507
|
+
leadingNames: /* @__PURE__ */ new Set(),
|
|
508
|
+
acceptingAnyToken: false,
|
|
454
509
|
initialState: void 0,
|
|
455
510
|
parse: (context) => ({
|
|
456
511
|
success: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@optique/logtape",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "LogTape logging integration for Optique CLI parser",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"CLI",
|
|
@@ -54,7 +54,8 @@
|
|
|
54
54
|
},
|
|
55
55
|
"sideEffects": false,
|
|
56
56
|
"peerDependencies": {
|
|
57
|
-
"@logtape/
|
|
57
|
+
"@logtape/file": "^2.0.4",
|
|
58
|
+
"@logtape/logtape": "^2.0.4"
|
|
58
59
|
},
|
|
59
60
|
"peerDependenciesMeta": {
|
|
60
61
|
"@logtape/file": {
|
|
@@ -62,10 +63,11 @@
|
|
|
62
63
|
}
|
|
63
64
|
},
|
|
64
65
|
"dependencies": {
|
|
65
|
-
"@optique/core": "1.0.
|
|
66
|
+
"@optique/core": "1.0.1"
|
|
66
67
|
},
|
|
67
68
|
"devDependencies": {
|
|
68
|
-
"@logtape/
|
|
69
|
+
"@logtape/file": "^2.0.4",
|
|
70
|
+
"@logtape/logtape": "^2.0.4",
|
|
69
71
|
"@types/node": "^20.19.9",
|
|
70
72
|
"tsdown": "^0.13.0",
|
|
71
73
|
"typescript": "^5.8.3"
|