@optique/logtape 0.8.0-dev.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 ADDED
@@ -0,0 +1,570 @@
1
+ //#region rolldown:runtime
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+
23
+ //#endregion
24
+ const __optique_core_valueparser = __toESM(require("@optique/core/valueparser"));
25
+ const __optique_core_primitives = __toESM(require("@optique/core/primitives"));
26
+ const __optique_core_modifiers = __toESM(require("@optique/core/modifiers"));
27
+ const __optique_core_message = __toESM(require("@optique/core/message"));
28
+ const __optique_core_constructs = __toESM(require("@optique/core/constructs"));
29
+
30
+ //#region src/loglevel.ts
31
+ /**
32
+ * All valid log levels in order from lowest to highest severity.
33
+ */
34
+ const LOG_LEVELS = [
35
+ "trace",
36
+ "debug",
37
+ "info",
38
+ "warning",
39
+ "error",
40
+ "fatal"
41
+ ];
42
+ /**
43
+ * Creates a {@link ValueParser} for LogTape log levels.
44
+ *
45
+ * This parser validates that the input is one of the valid LogTape severity
46
+ * levels: `"trace"`, `"debug"`, `"info"`, `"warning"`, `"error"`, or `"fatal"`.
47
+ * The parsing is case-insensitive.
48
+ *
49
+ * @param options Configuration options for the log level parser.
50
+ * @returns A {@link ValueParser} that converts string input to {@link LogLevel}.
51
+ *
52
+ * @example Basic usage
53
+ * ```typescript
54
+ * import { logLevel } from "@optique/logtape";
55
+ * import { option, withDefault } from "@optique/core";
56
+ *
57
+ * const parser = object({
58
+ * level: withDefault(
59
+ * option("--log-level", "-l", logLevel()),
60
+ * "info"
61
+ * ),
62
+ * });
63
+ * ```
64
+ *
65
+ * @example Custom metavar
66
+ * ```typescript
67
+ * import { logLevel } from "@optique/logtape";
68
+ *
69
+ * const parser = logLevel({ metavar: "LOG_LEVEL" });
70
+ * ```
71
+ *
72
+ * @since 0.8.0
73
+ */
74
+ function logLevel(options = {}) {
75
+ return (0, __optique_core_valueparser.choice)(LOG_LEVELS, {
76
+ metavar: options.metavar ?? "LEVEL",
77
+ caseInsensitive: true,
78
+ errors: options.errors?.invalidLevel != null ? { invalidChoice: typeof options.errors.invalidLevel === "function" ? (input, _choices) => options.errors.invalidLevel(input) : options.errors.invalidLevel } : void 0
79
+ });
80
+ }
81
+
82
+ //#endregion
83
+ //#region src/verbosity.ts
84
+ /**
85
+ * Mapping from verbosity count (offset from base) to log level.
86
+ * Index 0 is base, 1 is base+1 flag, 2 is base+2 flags, etc.
87
+ */
88
+ const VERBOSITY_LEVELS = [
89
+ "fatal",
90
+ "error",
91
+ "warning",
92
+ "info",
93
+ "debug",
94
+ "trace"
95
+ ];
96
+ /**
97
+ * Index of "warning" in VERBOSITY_LEVELS (the default base level).
98
+ */
99
+ const WARNING_INDEX = 2;
100
+ /**
101
+ * Creates a parser for verbosity flags (`-v`, `-vv`, `-vvv`, etc.).
102
+ *
103
+ * This parser accumulates `-v` flags to determine the log level.
104
+ * Each additional `-v` flag increases the verbosity (decreases the log level
105
+ * severity).
106
+ *
107
+ * Default level mapping with `baseLevel: "warning"`:
108
+ * - No flags: `"warning"`
109
+ * - `-v`: `"info"`
110
+ * - `-vv`: `"debug"`
111
+ * - `-vvv` or more: `"trace"`
112
+ *
113
+ * @param options Configuration options for the verbosity parser.
114
+ * @returns A {@link Parser} that produces a {@link LogLevel}.
115
+ *
116
+ * @example Basic usage
117
+ * ```typescript
118
+ * import { verbosity } from "@optique/logtape";
119
+ * import { object } from "@optique/core/constructs";
120
+ *
121
+ * const parser = object({
122
+ * logLevel: verbosity(),
123
+ * });
124
+ *
125
+ * // No flags -> "warning"
126
+ * // -v -> "info"
127
+ * // -vv -> "debug"
128
+ * // -vvv -> "trace"
129
+ * ```
130
+ *
131
+ * @example Custom base level
132
+ * ```typescript
133
+ * import { verbosity } from "@optique/logtape";
134
+ *
135
+ * const parser = verbosity({ baseLevel: "error" });
136
+ * // No flags -> "error"
137
+ * // -v -> "warning"
138
+ * // -vv -> "info"
139
+ * // -vvv -> "debug"
140
+ * // -vvvv -> "trace"
141
+ * ```
142
+ *
143
+ * @since 0.8.0
144
+ */
145
+ function verbosity(options = {}) {
146
+ const short = options.short ?? "-v";
147
+ const long = options.long ?? "--verbose";
148
+ const baseLevel = options.baseLevel ?? "warning";
149
+ const baseIndex = VERBOSITY_LEVELS.indexOf(baseLevel);
150
+ const effectiveBaseIndex = baseIndex >= 0 ? baseIndex : WARNING_INDEX;
151
+ const flagParser = (0, __optique_core_primitives.flag)(short, long, { description: options.description });
152
+ const multipleFlags = (0, __optique_core_modifiers.multiple)(flagParser);
153
+ return (0, __optique_core_modifiers.map)(multipleFlags, (flags) => {
154
+ const count = flags.length;
155
+ const targetIndex = Math.min(effectiveBaseIndex + count, VERBOSITY_LEVELS.length - 1);
156
+ return VERBOSITY_LEVELS[targetIndex];
157
+ });
158
+ }
159
+
160
+ //#endregion
161
+ //#region src/debug.ts
162
+ /**
163
+ * Creates a parser for a debug flag (`-d`, `--debug`).
164
+ *
165
+ * This parser provides a simple boolean toggle for enabling debug-level
166
+ * logging. When the flag is present, it returns the debug level; otherwise,
167
+ * it returns the normal level.
168
+ *
169
+ * @param options Configuration options for the debug flag parser.
170
+ * @returns A {@link Parser} that produces a {@link LogLevel}.
171
+ *
172
+ * @example Basic usage
173
+ * ```typescript
174
+ * import { debug } from "@optique/logtape";
175
+ * import { object } from "@optique/core/constructs";
176
+ *
177
+ * const parser = object({
178
+ * logLevel: debug(),
179
+ * });
180
+ *
181
+ * // No flag -> "info"
182
+ * // --debug or -d -> "debug"
183
+ * ```
184
+ *
185
+ * @example Custom levels
186
+ * ```typescript
187
+ * import { debug } from "@optique/logtape";
188
+ *
189
+ * const parser = debug({
190
+ * debugLevel: "trace",
191
+ * normalLevel: "warning",
192
+ * });
193
+ * // No flag -> "warning"
194
+ * // --debug -> "trace"
195
+ * ```
196
+ *
197
+ * @since 0.8.0
198
+ */
199
+ function debug(options = {}) {
200
+ const short = options.short ?? "-d";
201
+ const long = options.long ?? "--debug";
202
+ const debugLevel = options.debugLevel ?? "debug";
203
+ const normalLevel = options.normalLevel ?? "info";
204
+ const flagParser = (0, __optique_core_primitives.flag)(short, long, { description: options.description });
205
+ return (0, __optique_core_modifiers.map)((0, __optique_core_modifiers.optional)(flagParser), (value) => {
206
+ return value === true ? debugLevel : normalLevel;
207
+ });
208
+ }
209
+
210
+ //#endregion
211
+ //#region src/output.ts
212
+ /**
213
+ * Creates a value parser for log output destinations.
214
+ *
215
+ * This parser accepts either `-` for console output or a file path for file
216
+ * output. The `-` value follows the common CLI convention for representing
217
+ * standard output/error.
218
+ *
219
+ * @param options Configuration options for the parser.
220
+ * @returns A {@link ValueParser} that produces a {@link LogOutput}.
221
+ */
222
+ function logOutputValueParser(options = {}) {
223
+ return {
224
+ metavar: options.metavar ?? "FILE",
225
+ parse(input) {
226
+ if (input === "-") return {
227
+ success: true,
228
+ value: { type: "console" }
229
+ };
230
+ if (input.trim() === "") return {
231
+ success: false,
232
+ error: options.errors?.emptyPath ? typeof options.errors.emptyPath === "function" ? options.errors.emptyPath(input) : options.errors.emptyPath : __optique_core_message.message`Log output path cannot be empty.`
233
+ };
234
+ return {
235
+ success: true,
236
+ value: {
237
+ type: "file",
238
+ path: input
239
+ }
240
+ };
241
+ },
242
+ format(value) {
243
+ return value.type === "console" ? "-" : value.path;
244
+ },
245
+ *suggest(prefix) {
246
+ if ("-".startsWith(prefix)) yield {
247
+ kind: "literal",
248
+ text: "-"
249
+ };
250
+ yield {
251
+ kind: "file",
252
+ type: "file",
253
+ pattern: prefix
254
+ };
255
+ }
256
+ };
257
+ }
258
+ /**
259
+ * Creates a parser for log output destination (`--log-output`).
260
+ *
261
+ * This parser accepts either `-` for console output (following CLI convention)
262
+ * or a file path for file output.
263
+ *
264
+ * @param options Configuration options for the log output parser.
265
+ * @returns A {@link Parser} that produces a {@link LogOutput} or `undefined`.
266
+ *
267
+ * @example Basic usage
268
+ * ```typescript
269
+ * import { logOutput } from "@optique/logtape";
270
+ * import { object } from "@optique/core/constructs";
271
+ *
272
+ * const parser = object({
273
+ * output: logOutput(),
274
+ * });
275
+ *
276
+ * // --log-output=- -> console output
277
+ * // --log-output=/var/log/app.log -> file output
278
+ * ```
279
+ *
280
+ * @since 0.8.0
281
+ */
282
+ function logOutput(options = {}) {
283
+ const long = options.long ?? "--log-output";
284
+ const valueParser = logOutputValueParser(options);
285
+ if (options.short) {
286
+ const short = options.short;
287
+ return (0, __optique_core_modifiers.optional)((0, __optique_core_primitives.option)(short, long, valueParser, { description: options.description }));
288
+ }
289
+ return (0, __optique_core_modifiers.optional)((0, __optique_core_primitives.option)(long, valueParser, { description: options.description }));
290
+ }
291
+ /**
292
+ * Creates a console sink with configurable stream selection.
293
+ *
294
+ * This function creates a LogTape sink that writes to the console. The target
295
+ * stream (stdout or stderr) can be configured statically or dynamically per
296
+ * log record.
297
+ *
298
+ * @param options Configuration options for the console sink.
299
+ * @returns A {@link Sink} function.
300
+ *
301
+ * @example Static stream selection
302
+ * ```typescript
303
+ * import { createConsoleSink } from "@optique/logtape";
304
+ *
305
+ * const sink = createConsoleSink({ stream: "stderr" });
306
+ * ```
307
+ *
308
+ * @example Dynamic stream selection based on level
309
+ * ```typescript
310
+ * import { createConsoleSink } from "@optique/logtape";
311
+ *
312
+ * const sink = createConsoleSink({
313
+ * streamResolver: (level) =>
314
+ * level === "error" || level === "fatal" ? "stderr" : "stdout"
315
+ * });
316
+ * ```
317
+ *
318
+ * @since 0.8.0
319
+ */
320
+ function createConsoleSink(options = {}) {
321
+ const defaultStream = options.stream ?? "stderr";
322
+ const streamResolver = options.streamResolver;
323
+ return (record) => {
324
+ const stream = streamResolver ? streamResolver(record.level) : defaultStream;
325
+ const messageParts = [];
326
+ for (let i = 0; i < record.message.length; i++) {
327
+ const part = record.message[i];
328
+ if (typeof part === "string") messageParts.push(part);
329
+ else messageParts.push(String(part));
330
+ }
331
+ const formattedMessage = messageParts.join("");
332
+ const timestamp = record.timestamp ? new Date(record.timestamp).toISOString() : (/* @__PURE__ */ new Date()).toISOString();
333
+ const category = record.category.join(".");
334
+ const level = record.level.toUpperCase().padEnd(7);
335
+ const line = `${timestamp} [${level}] ${category}: ${formattedMessage}`;
336
+ if (stream === "stderr") console.error(line);
337
+ else console.log(line);
338
+ };
339
+ }
340
+ /**
341
+ * Creates a sink from a {@link LogOutput} destination.
342
+ *
343
+ * For console output, this creates a console sink. For file output, this
344
+ * dynamically imports `@logtape/file` and creates a file sink.
345
+ *
346
+ * @param output The log output destination.
347
+ * @param consoleSinkOptions Options for console sink (only used when output is console).
348
+ * @returns A promise that resolves to a {@link Sink}.
349
+ * @throws {Error} If file output is requested but `@logtape/file` is not installed.
350
+ *
351
+ * @example Console output
352
+ * ```typescript
353
+ * import { createSink } from "@optique/logtape";
354
+ *
355
+ * const sink = await createSink({ type: "console" }, { stream: "stderr" });
356
+ * ```
357
+ *
358
+ * @example File output
359
+ * ```typescript
360
+ * import { createSink } from "@optique/logtape";
361
+ *
362
+ * const sink = await createSink({ type: "file", path: "/var/log/app.log" });
363
+ * ```
364
+ *
365
+ * @since 0.8.0
366
+ */
367
+ async function createSink(output, consoleSinkOptions = {}) {
368
+ if (output.type === "console") return createConsoleSink(consoleSinkOptions);
369
+ try {
370
+ const { getFileSink } = await import("@logtape/file");
371
+ return getFileSink(output.path);
372
+ } catch (e) {
373
+ throw new Error(`File sink requires @logtape/file package. Install it with:
374
+ npm install @logtape/file
375
+ # or
376
+ deno add jsr:@logtape/file
377
+
378
+ Original error: ${e}`);
379
+ }
380
+ }
381
+
382
+ //#endregion
383
+ //#region src/preset.ts
384
+ /**
385
+ * Creates a logging options parser preset.
386
+ *
387
+ * This function creates a parser that combines log level and log output
388
+ * options into a single group. The log level can be configured using one of
389
+ * three methods (mutually exclusive):
390
+ *
391
+ * - `"option"`: Explicit `--log-level=LEVEL` option
392
+ * - `"verbosity"`: `-v`/`-vv`/`-vvv` flags for increasing verbosity
393
+ * - `"debug"`: Simple `--debug` flag toggle
394
+ *
395
+ * @param config Configuration for the logging options.
396
+ * @returns A {@link Parser} that produces a {@link LoggingOptionsResult}.
397
+ *
398
+ * @example Using log level option
399
+ * ```typescript
400
+ * import { loggingOptions } from "@optique/logtape";
401
+ * import { object } from "@optique/core/constructs";
402
+ *
403
+ * const parser = object({
404
+ * logging: loggingOptions({ level: "option" }),
405
+ * });
406
+ * // --log-level=debug --log-output=/var/log/app.log
407
+ * ```
408
+ *
409
+ * @example Using verbosity flags
410
+ * ```typescript
411
+ * import { loggingOptions } from "@optique/logtape";
412
+ * import { object } from "@optique/core/constructs";
413
+ *
414
+ * const parser = object({
415
+ * logging: loggingOptions({ level: "verbosity" }),
416
+ * });
417
+ * // -vv --log-output=-
418
+ * ```
419
+ *
420
+ * @example Using debug flag
421
+ * ```typescript
422
+ * import { loggingOptions } from "@optique/logtape";
423
+ * import { object } from "@optique/core/constructs";
424
+ *
425
+ * const parser = object({
426
+ * logging: loggingOptions({ level: "debug" }),
427
+ * });
428
+ * // --debug
429
+ * ```
430
+ *
431
+ * @since 0.8.0
432
+ */
433
+ function loggingOptions(config) {
434
+ const groupLabel = config.groupLabel ?? "Logging options";
435
+ const outputEnabled = config.output?.enabled !== false;
436
+ const outputLong = config.output?.long ?? "--log-output";
437
+ let levelParser;
438
+ switch (config.level) {
439
+ case "option": {
440
+ const long = config.long ?? "--log-level";
441
+ const short = config.short ?? "-l";
442
+ const defaultLevel = config.default ?? "info";
443
+ levelParser = (0, __optique_core_modifiers.withDefault)((0, __optique_core_primitives.option)(short, long, logLevel()), defaultLevel);
444
+ break;
445
+ }
446
+ case "verbosity": {
447
+ const verbosityOptions = {
448
+ short: config.short,
449
+ long: config.long,
450
+ baseLevel: config.baseLevel
451
+ };
452
+ levelParser = verbosity(verbosityOptions);
453
+ break;
454
+ }
455
+ case "debug": {
456
+ const debugOptions = {
457
+ short: config.short,
458
+ long: config.long,
459
+ debugLevel: config.debugLevel,
460
+ normalLevel: config.normalLevel
461
+ };
462
+ levelParser = debug(debugOptions);
463
+ break;
464
+ }
465
+ }
466
+ const defaultOutput = { type: "console" };
467
+ const outputParser = (0, __optique_core_modifiers.withDefault)(logOutput({ long: outputLong }), defaultOutput);
468
+ if (!outputEnabled) {
469
+ const constantOutputParser = {
470
+ $valueType: [],
471
+ $stateType: [],
472
+ priority: 0,
473
+ usage: [],
474
+ initialState: void 0,
475
+ parse: (context) => ({
476
+ success: true,
477
+ next: context,
478
+ consumed: []
479
+ }),
480
+ complete: () => ({
481
+ success: true,
482
+ value: defaultOutput
483
+ }),
484
+ suggest: () => [],
485
+ getDocFragments: () => ({ fragments: [] })
486
+ };
487
+ return (0, __optique_core_constructs.group)(groupLabel, (0, __optique_core_constructs.object)({
488
+ logLevel: levelParser,
489
+ logOutput: constantOutputParser
490
+ }));
491
+ }
492
+ const innerParser = (0, __optique_core_constructs.object)({
493
+ logLevel: levelParser,
494
+ logOutput: outputParser
495
+ });
496
+ return (0, __optique_core_constructs.group)(groupLabel, innerParser);
497
+ }
498
+ /**
499
+ * Creates a LogTape configuration from parsed logging options.
500
+ *
501
+ * This helper function converts the result of {@link loggingOptions} parser
502
+ * into a configuration object that can be passed to LogTape's `configure()`
503
+ * function.
504
+ *
505
+ * @param options The parsed logging options.
506
+ * @param consoleSinkOptions Options for console sink (only used when output is console).
507
+ * @param additionalConfig Additional LogTape configuration to merge.
508
+ * @returns A promise that resolves to a LogTape {@link Config}.
509
+ *
510
+ * @example Basic usage
511
+ * ```typescript
512
+ * import { loggingOptions, createLoggingConfig } from "@optique/logtape";
513
+ * import { configure } from "@logtape/logtape";
514
+ * import { object, parse } from "@optique/core";
515
+ *
516
+ * const parser = object({
517
+ * logging: loggingOptions({ level: "option" }),
518
+ * });
519
+ *
520
+ * const result = parse(parser, ["--log-level=debug"]);
521
+ * if (result.success) {
522
+ * const config = await createLoggingConfig(result.value.logging);
523
+ * await configure(config);
524
+ * }
525
+ * ```
526
+ *
527
+ * @example With additional configuration
528
+ * ```typescript
529
+ * import { loggingOptions, createLoggingConfig } from "@optique/logtape";
530
+ * import { configure } from "@logtape/logtape";
531
+ *
532
+ * const config = await createLoggingConfig(result.value.logging, {
533
+ * stream: "stderr",
534
+ * }, {
535
+ * loggers: [
536
+ * { category: ["my-app", "database"], lowestLevel: "debug", sinks: ["default"] },
537
+ * ],
538
+ * });
539
+ * await configure(config);
540
+ * ```
541
+ *
542
+ * @since 0.8.0
543
+ */
544
+ async function createLoggingConfig(options, consoleSinkOptions = {}, additionalConfig = {}) {
545
+ const sink = await createSink(options.logOutput, consoleSinkOptions);
546
+ return {
547
+ sinks: {
548
+ default: sink,
549
+ ...additionalConfig.sinks
550
+ },
551
+ loggers: [{
552
+ category: [],
553
+ lowestLevel: options.logLevel,
554
+ sinks: ["default"]
555
+ }, ...additionalConfig.loggers ?? []],
556
+ filters: additionalConfig.filters,
557
+ reset: additionalConfig.reset
558
+ };
559
+ }
560
+
561
+ //#endregion
562
+ exports.LOG_LEVELS = LOG_LEVELS;
563
+ exports.createConsoleSink = createConsoleSink;
564
+ exports.createLoggingConfig = createLoggingConfig;
565
+ exports.createSink = createSink;
566
+ exports.debug = debug;
567
+ exports.logLevel = logLevel;
568
+ exports.logOutput = logOutput;
569
+ exports.loggingOptions = loggingOptions;
570
+ exports.verbosity = verbosity;