Optique 0.8.0: Conditional parsing, pass-through options, and LogTape integration
-
We're excited to announce Optique 0.8.0! This release introduces powerful new features for building sophisticated CLI applications: the
conditional()combinator for discriminated union patterns, thepassThrough()parser for wrapper tools, and the new @optique/logtape package for seamless logging configuration.Optique is a type-safe combinatorial CLI parser for TypeScript, providing a functional approach to building command-line interfaces with composable parsers and full type inference.
New conditional parsing with
conditional()Ever needed to enable different sets of options based on a discriminator value? The new
conditional()combinator makes this pattern first-class. It creates discriminated unions where certain options only become valid when a specific discriminator value is selected.import { conditional, object } from "@optique/core/constructs"; import { option } from "@optique/core/primitives"; import { choice, string } from "@optique/core/valueparser"; const parser = conditional( option("--reporter", choice(["console", "junit", "html"])), { console: object({}), junit: object({ outputFile: option("--output-file", string()) }), html: object({ outputFile: option("--output-file", string()) }), } ); // Result type: ["console", {}] | ["junit", { outputFile: string }] | ...Key features:
- Explicit discriminator option determines which branch is selected
- Tuple result
[discriminator, branchValue]for clear type narrowing - Optional default branch for when discriminator is not provided
- Clear error messages indicating which options are required for each discriminator value
The
conditional()parser provides a more structured alternative toor()for discriminated union patterns. Use it when you have an explicit discriminator option that determines which set of options is valid.See the
conditional()documentation for more details and examples.Pass-through options with
passThrough()Building wrapper CLI tools that need to forward unrecognized options to an underlying tool? The new
passThrough()parser enables legitimate wrapper/proxy patterns by capturing unknown options without validation errors.import { object } from "@optique/core/constructs"; import { option, passThrough } from "@optique/core/primitives"; const parser = object({ debug: option("--debug"), extra: passThrough(), }); // mycli --debug --foo=bar --baz=qux // → { debug: true, extra: ["--foo=bar", "--baz=qux"] }Key features:
- Three capture formats:
"equalsOnly"(default, safest),"nextToken"(captures--opt valpairs), and"greedy"(captures all remaining tokens) - Lowest priority (−10) ensures explicit parsers always match first
- Respects
--options terminator in"equalsOnly"and"nextToken"modes - Works seamlessly with
object(), subcommands, and other combinators
This feature is designed for building Docker-like CLIs, build tool wrappers, or any tool that proxies commands to another process.
See the
passThrough()documentation for usage patterns and best practices.LogTape logging integration
The new @optique/logtape package provides seamless integration with LogTape, enabling you to configure logging through command-line arguments with various parsing strategies.
# Deno deno add --jsr @optique/logtape @logtape/logtape # npm npm add @optique/logtape @logtape/logtapeQuick start with the
loggingOptions()preset:import { loggingOptions, createLoggingConfig } from "@optique/logtape"; import { object } from "@optique/core/constructs"; import { parse } from "@optique/core/parser"; import { configure } from "@logtape/logtape"; const parser = object({ logging: loggingOptions({ level: "verbosity" }), }); const args = ["-vv", "--log-output=-"]; const result = parse(parser, args); if (result.success) { const config = await createLoggingConfig(result.value.logging); await configure(config); }The package offers multiple approaches to control log verbosity:
verbosity()parser: The classic-v/-vv/-vvvpattern where each flag increases verbosity (no flags →"warning",-v→"info",-vv→"debug",-vvv→"trace")debug()parser: Simple--debug/-dflag that toggles between normal and debug levelslogLevel()value parser: Explicit--log-level=debugoption for direct level selectionlogOutput()parser: Log output destination with-for console or file path for file output
See the LogTape integration documentation for complete examples and configuration options.
Bug fix: negative integers now accepted
Fixed an issue where the
integer()value parser rejected negative integers when usingtype: "number". The regex pattern has been updated from/^\d+$/to/^-?\d+$/to correctly handle values like-42. Note thattype: "bigint"already accepted negative integers, so this change brings consistency between the two types.Installation
# Deno deno add jsr:@optique/core # npm npm add @optique/core # pnpm pnpm add @optique/core # Yarn yarn add @optique/core # Bun bun add @optique/coreFor the LogTape integration:
# Deno deno add --jsr @optique/logtape @logtape/logtape # npm npm add @optique/logtape @logtape/logtape # pnpm pnpm add @optique/logtape @logtape/logtape # Yarn yarn add @optique/logtape @logtape/logtape # Bun bun add @optique/logtape @logtape/logtapeLooking forward
Optique 0.8.0 continues our focus on making CLI development more expressive and type-safe. The
conditional()combinator brings discriminated union patterns to the forefront,passThrough()enables new wrapper tool use cases, and the LogTape integration makes logging configuration a breeze.As always, all new features maintain full backward compatibility—your existing parsers continue to work unchanged.
We're grateful to the community for feedback and suggestions. If you have ideas for future improvements or encounter any issues, please let us know through GitHub Issues. For more information about Optique and its features, visit the documentation or check out the full changelog.
-
undefined hongminhee@hollo.social shared this topic