UNPKG

@stricli/core

Version:

Build complex CLIs with type safety and no dependencies

1,294 lines (1,274 loc) 56 kB
/** * Minimal expected interface for an output stream; used to narrow the types of NodeJS's stdout/stderr. */ interface Writable { /** * Write the provided string to this stream. */ readonly write: (str: string) => void; /** * Determine the available color depth of the underlying stream. * Environment variables are also provided to control or suppress color output. */ readonly getColorDepth?: (env?: Readonly<Partial<Record<string, string>>>) => number; } interface WritableStreams { /** * Contains a writable stream connected to stdout (fd 1). */ readonly stdout: Writable; /** * Contains a writable stream connected to stderr (fd 2). */ readonly stderr: Writable; } /** * Command-level context that provides necessary process information and is available to all command runs. * This type should be extended to include context specific to your command implementations. */ interface CommandContext { readonly process: WritableStreams; } /** * Simple interface that mirrors NodeJS.Process but only requires the minimum API required by Stricli. */ interface StricliProcess extends WritableStreams { /** * Object that stores all available environment variables. * * @see {@link EnvironmentVariableName} for variable names used by Stricli. */ readonly env?: Readonly<Partial<Record<string, string>>>; /** * A number which will be the process exit code. */ exitCode?: number | string; } /** * Environment variable names used by Stricli. * * - `STRICLI_SKIP_VERSION_CHECK` - If specified and non-0, skips the latest version check. * - `STRICLI_NO_COLOR` - If specified and non-0, disables ANSI terminal coloring. */ type EnvironmentVariableName = "STRICLI_SKIP_VERSION_CHECK" | "STRICLI_NO_COLOR"; /** * Top-level context that provides necessary process information to Stricli internals. */ interface ApplicationContext extends CommandContext { readonly process: StricliProcess; /** * A string that represents the current user's locale. * It is passed to {@link LocalizationConfiguration.loadText} which provides the text for Stricli to use * when formatting built-in output. */ readonly locale?: string; } /** * Contextual information about the current command. */ interface CommandInfo { /** * Prefix of command line inputs used to navigate to the current command. */ readonly prefix: readonly string[]; } /** * Function to build a generic CommandContext given the current command information. */ type StricliCommandContextBuilder<CONTEXT extends CommandContext> = (info: CommandInfo) => CONTEXT | Promise<CONTEXT>; /** * Dynamic context for command that contains either the generic CommandContext or simply the more limited * ApplicationContext and a method that builds a specific instance of the generic CommandContext. */ type StricliDynamicCommandContext<CONTEXT extends CommandContext> = ApplicationContext & (CONTEXT | { /** * Method to build specific CommandContext instance for the current command. */ readonly forCommand: StricliCommandContextBuilder<CONTEXT>; }); /** * Keyword strings used to build help text. */ interface DocumentationKeywords { /** * Keyword to be included when flags or arguments have a default value. * * Defaults to `"default"`. */ readonly default: string; } /** * Section header strings used to build help text. */ interface DocumentationHeaders { /** * Header for help text section that lists all usage lines. * * Defaults to `"USAGE"`. */ readonly usage: string; /** * Header for help text section that lists all aliases for the route. * * Defaults to `"ALIASES"`. */ readonly aliases: string; /** * Header for help text section that lists all commands in a route map. * * Defaults to `"COMMANDS"`. */ readonly commands: string; /** * Header for help text section that lists all flags accepted by the route. * * Defaults to `"FLAGS"`. */ readonly flags: string; /** * Header for help text section that lists all arguments accepted by the command. * * Defaults to `"ARGUMENTS"`. */ readonly arguments: string; } /** * Short documentation brief strings used to build help text. */ interface DocumentationBriefs { /** * Documentation brief to be included alongside `--help` flag in help text. * * Defaults to `"Print help information and exit"`. */ readonly help: string; /** * Documentation brief to be included alongside `--helpAll` flag in help text. * * Defaults to `"Print help information (including hidden commands/flags) and exit"`. */ readonly helpAll: string; /** * Documentation brief to be included alongside `--version` flag in help text. * * Defaults to `"Print version information and exit"`. */ readonly version: string; /** * Documentation brief to be included alongside `--` escape sequence in help text. * Only present when `scanner.allowArgumentEscapeSequence` is `true`. * * Defaults to `"All subsequent inputs should be interpreted as arguments"`. */ readonly argumentEscapeSequence: string; } /** * Strings used to build help text. */ interface DocumentationText { /** * Keyword strings used to build help text. */ readonly keywords: DocumentationKeywords; /** * Section header strings used to build help text. */ readonly headers: DocumentationHeaders; /** * Short documentation brief strings used to build help text. */ readonly briefs: DocumentationBriefs; } /** * Methods to customize the formatting of stderr messages handled by command execution. */ interface CommandErrorFormatting { /** * Formatted error message for the case where some exception was thrown while parsing the arguments. * * Exceptions intentionally thrown by this library while parsing arguments will extend from ArgumentScannerError. * These subclasses provide additional context about the specific error. * Use the {@link formatMessageForArgumentScannerError} helper to handle the different error types. * * If `ansiColor` is true, this string can use ANSI terminal codes. * Codes may have already been applied so be aware you may have to reset to achieve the desired output. */ readonly exceptionWhileParsingArguments: (exc: unknown, ansiColor: boolean) => string; /** * Formatted error message for the case where some exception was thrown while loading the command function. * This likely indicates an issue with the application itself or possibly the user's installation of the application. * * If `ansiColor` is true, this string can use ANSI terminal codes. * Codes may have already been applied so be aware you may have to reset to achieve the desired output. */ readonly exceptionWhileLoadingCommandFunction: (exc: unknown, ansiColor: boolean) => string; /** * Formatted error message for the case where some exception was thrown while loading the context for the command run. * This likely indicates an issue with the application itself or possibly the user's installation of the application. * * If `ansiColor` is true, this string can use ANSI terminal codes. * Codes may have already been applied so be aware you may have to reset to achieve the desired output. */ readonly exceptionWhileLoadingCommandContext: (exc: unknown, ansiColor: boolean) => string; /** * Formatted error message for the case where some exception was thrown while running the command. * Users are most likely to hit this case, so make sure that the error text provides practical, usable feedback. * * If `ansiColor` is true, this string can use ANSI terminal codes. * Codes may have already been applied so be aware you may have to reset to achieve the desired output. */ readonly exceptionWhileRunningCommand: (exc: unknown, ansiColor: boolean) => string; /** * Formatted error message for the case where an Error was safely returned from the command. * Users are most likely to hit this case, so make sure that the error text provides practical, usable feedback. * * If `ansiColor` is true, this string can use ANSI terminal codes. * Codes may have already been applied so be aware you may have to reset to achieve the desired output. */ readonly commandErrorResult: (err: Error, ansiColor: boolean) => string; } /** * Methods to customize the formatting of stderr messages handled by application execution. */ interface ApplicationErrorFormatting extends CommandErrorFormatting { /** * Formatted error message for the case where the supplied command line inputs do not resolve to a registered command. * Supplied with arguments for the argument in question, and several possible corrections based on registered commands. * * If `ansiColor` is true, this string can use ANSI terminal codes. * Codes may have already been applied so be aware you may have to reset to achieve the desired output. */ readonly noCommandRegisteredForInput: (args: { readonly input: string; readonly corrections: readonly string[]; readonly ansiColor: boolean; }) => string; /** * Formatted error message for the case where the application does not provide text for the current requested locale. * Should indicate that the default locale will be used instead. * * If `ansiColor` is true, this string can use ANSI terminal codes. * Codes may have already been applied so be aware you may have to reset to achieve the desired output. */ readonly noTextAvailableForLocale: (args: { readonly requestedLocale: string; readonly defaultLocale: string; readonly ansiColor: boolean; }) => string; } /** * The full set of static text and text-returning callbacks that are necessary for Stricli to write the necessary output. */ interface ApplicationText extends ApplicationErrorFormatting, DocumentationText { /** * Generate warning text to be written to stdout when the latest version is not installed. * Should include brief instructions for how to update to that version. * * If `ansiColor` is true, this string can use ANSI terminal codes. * Codes may have already been applied so be aware you may have to reset to achieve the desired output. */ readonly currentVersionIsNotLatest: (args: { readonly currentVersion: string; readonly latestVersion: string; readonly upgradeCommand?: string; readonly ansiColor: boolean; }) => string; } /** * Default English text implementation of {@link ApplicationText}. */ declare const text_en: ApplicationText; /** * The weights of various edit operations used when calculating the Damerau-Levenshtein distance. */ interface DamerauLevenshteinWeights { /** * The edit cost of inserting a character. * * Example: `"ab" -> "abc"` */ readonly insertion: number; /** * The edit cost of deleting a character. * * Example: `"abc" -> "ab"` */ readonly deletion: number; /** * The edit cost of replacing one character with another. * * Example: `"abc" -> "arc"` */ readonly substitution: number; /** * The edit cost of swapping two adjacent characters. * * Example: `"acb" -> "abc"` */ readonly transposition: number; } /** * Customizable options for edit cost weights and threshold when calculating the Damerau-Levenshtein distance. */ interface DamerauLevenshteinOptions { /** * The upper threshold for edit distance when considering potential alternatives. */ readonly threshold: number; /** * The weights of various edit operations used when calculating the Damerau-Levenshtein distance. */ readonly weights: DamerauLevenshteinWeights; } /** * Methods to determine application version information for `--version` flag or latest version check. */ type VersionInfo = ({ /** * Statically known current version. */ readonly currentVersion: string; } | { /** * Asynchonously determine the current version of this application. */ readonly getCurrentVersion: (this: ApplicationContext) => Promise<string>; }) & { /** * Asynchonously determine the latest version of this application. * If value is retrieved from cache, a change to the current version should invalidate that cache. */ readonly getLatestVersion?: (this: ApplicationContext, currentVersion: string) => Promise<string | undefined>; /** * Command to display to the end user that will upgrade this application. * Passed to {@link ApplicationText.currentVersionIsNotLatest} to format/localize. */ readonly upgradeCommand?: string; }; /** * Case style configuration for parsing route and flag names from the command line. * Each value has the following behavior: * * `original` - Only accepts exact matches. * * `allow-kebab-for-camel` - In addition to exact matches, allows kebab-case input for camelCase. */ type ScannerCaseStyle = "original" | "allow-kebab-for-camel"; /** * Configuration for controlling the behavior of the command and argument scanners. */ interface ScannerConfiguration { /** * Case style configuration for scanning route and flag names. * * Default value is `original` */ readonly caseStyle: ScannerCaseStyle; /** * If true, when scanning inputs for a command will treat `--` as an escape sequence. * This will force the scanner to treat all remaining inputs as arguments. * * Example for `false` * ```shell * $ cli --foo -- --bar * # { foo: true, bar: true }, ["--"] * ``` * * Example for `true` * ```shell * $ cli --foo -- --bar * # { foo: true }, ["--bar"] * ``` * * Default value is `false` */ readonly allowArgumentEscapeSequence: boolean; /** * Options used when calculating distance for alternative inputs ("did you mean?"). * * Default value is equivalent to the empirically determined values used by git: * ```json * { * "threshold": 7, * "weights": { * "insertion": 1, * "deletion": 3, * "substitution": 2, * "transposition": 0 * } * } * ``` */ readonly distanceOptions: DamerauLevenshteinOptions; } /** * Case style configuration for displaying route and flag names in documentation text. * Each value has the following behavior: * * `original` - Displays the original names unchanged. * * `convert-camel-to-kebab` - Converts all camelCase names to kebab-case in output. Only allowed if `scannerCaseStyle` is set to `allow-kebab-for-camel`. */ type DisplayCaseStyle = "original" | "convert-camel-to-kebab"; /** * Configuration for controlling the content of the printed documentation. */ interface DocumentationConfiguration { /** * In addition to the `--help` flag, there is a `--helpAll`/`--help-all` flag that shows all documentation * including entries for hidden commands/arguments. * The `--helpAll` flag cannot be functionally disabled, but it is hidden when listing the built-in flags by default. * Setting this option to `true` forces the output to always include this flag in the list of built-in flags. * * Defaults to `false`. */ readonly alwaysShowHelpAllFlag: boolean; /** * Controls whether or not to include alias of flags in the usage line. * Only replaces name with alias when a single alias exists. * * Defaults to `false`. */ readonly useAliasInUsageLine: boolean; /** * Controls whether or not to include optional flags and positional parameters in the usage line. * If enabled, all parameters that are optional at runtime (including parameters with defaults) will be hidden. * * Defaults to `false`. */ readonly onlyRequiredInUsageLine: boolean; /** * Case style configuration for displaying route and flag names. * Cannot be `convert-camel-to-kebab` if {@link ScannerConfiguration.caseStyle} is `original`. * * Default value is derived from value for {@link ScannerConfiguration.caseStyle}: * * Defaults to `original` for `original`. * * Defaults to `convert-camel-to-kebab` for `allow-kebab-for-camel`. */ readonly caseStyle: DisplayCaseStyle; /** * By default, if the color depth of the stdout stream is greater than 4, ANSI terminal colors will be used. * If this value is `true`, disables all ANSI terminal color output. * * Defaults to `false`. */ readonly disableAnsiColor: boolean; } /** * Configuration for controlling the behavior of completion proposals. */ interface CompletionConfiguration { /** * This flag controls whether or not to include aliases of routes and flags. * * Defaults to match value of {@link DocumentationConfiguration.useAliasInUsageLine}. */ readonly includeAliases: boolean; /** * This flag controls whether or not to include hidden routes. * * Defaults to `false`. */ readonly includeHiddenRoutes: boolean; } /** * Configuration for controlling the localization behavior. */ interface LocalizationConfiguration { /** * The default locale that should be used if the context does not have a locale. * * If unspecified, will default to `en`. */ readonly defaultLocale: string; /** * Mapping of locale to application text. * Locale is optionally provided at runtime by the context. * * If unspecified, will return the default English implementation {@link text_en} for all "en" locales. */ readonly loadText: (locale: string) => ApplicationText | undefined; } /** * Configuration for controlling the runtime behavior of the application. */ interface ApplicationConfiguration { /** * Unique name for this application. * It should match the command that is used to run the application. */ readonly name: string; /** * If supplied, application will be aware of version info at runtime. * * Before every run, the application will fetch the latest version and warn if it differs from the current version. * As well, a new flag `--version` (with alias `-v`) will be available on the base route, which will print the current * version to stdout. */ readonly versionInfo?: VersionInfo; /** * If supplied, customizes the command/argument scanning behavior of the application. * * See documentation of inner types for default values. */ readonly scanner: ScannerConfiguration; /** * If supplied, customizes the formatting of documentation lines in help text. * * See documentation of inner types for default values. */ readonly documentation: DocumentationConfiguration; /** * If supplied, customizes command completion proposal behavior. * * See documentation of inner types for default values. */ readonly completion: CompletionConfiguration; /** * If supplied, customizes text localization. * * See documentation of inner types for default values. */ readonly localization: LocalizationConfiguration; /** * In the case where a command function throws some value unexpectedly or safely returns an Error, * this function will translate that into an exit code. * * If unspecified, the exit code will default to 1 when a command function throws some value. */ readonly determineExitCode?: (exc: unknown) => number; } /** * Partial configuration for application, see individual description for behavior when each value is unspecified. */ type PartialApplicationConfiguration = Pick<ApplicationConfiguration, "name" | "versionInfo" | "determineExitCode"> & { [K in "scanner" | "documentation" | "completion" | "localization"]?: Partial<ApplicationConfiguration[K]>; }; /** * Contextual information used to format the usage text for a route/command. */ interface UsageFormattingArguments { readonly prefix: readonly string[]; readonly config: DocumentationConfiguration; readonly ansiColor: boolean; } interface BaseFlagParameter { /** * In-line documentation for this flag. */ readonly brief: string; /** * String that serves as placeholder for the value in the generated usage line. Defaults to "value". */ readonly placeholder?: string; } interface BaseBooleanFlagParameter extends BaseFlagParameter { /** * Indicates flag should be treated as a boolean. */ readonly kind: "boolean"; readonly optional?: boolean; /** * Default flag value if input is not provided at runtime. * * If no value is provided, boolean flags default to `false`. */ readonly default?: boolean; } type RequiredBooleanFlagParameter = BaseBooleanFlagParameter & { /** * Parameter is required and cannot be set as optional. */ readonly optional?: false; } & ({ /** * Parameter is required and cannot be set as hidden without a default value. */ readonly hidden?: false; } | { /** * Default input value if one is not provided at runtime. */ readonly default: boolean; /** * Parameter should be hidden from all help text and proposed completions. * Only available for runtime-optional parameters. */ readonly hidden: true; }); interface OptionalBooleanFlagParameter extends BaseBooleanFlagParameter { /** * Parameter is optional and must be specified as such. */ readonly optional: true; /** * Parameter should be hidden from all help text and proposed completions. * Only available for optional parameters. */ readonly hidden?: boolean; /** * Optional parameters should not have default values. * This flag should be required if a value will always be provided. */ readonly default?: undefined; } type BooleanFlagParameter = RequiredBooleanFlagParameter | OptionalBooleanFlagParameter; interface BaseCounterFlagParameter extends BaseFlagParameter { /** * Indicates flag should be treated as a counter. */ readonly kind: "counter"; readonly optional?: boolean; } interface RequiredCounterFlagParameter extends BaseCounterFlagParameter { /** * Parameter is required and cannot be set as optional. */ readonly optional?: false; /** * Parameter is required and cannot be set as hidden. */ readonly hidden?: false; } interface OptionalCounterFlagParameter extends BaseCounterFlagParameter { /** * Parameter is optional and must be specified as such. */ readonly optional: true; /** * Parameter should be hidden from all help text and proposed completions. * Only available for optional parameters. */ readonly hidden?: boolean; } type CounterFlagParameter = RequiredCounterFlagParameter | OptionalCounterFlagParameter; interface BaseEnumFlagParameter<T extends string> extends BaseFlagParameter { /** * Indicates flag should be treated as an enumeration of strings. */ readonly kind: "enum"; /** * Array of all possible enumerations supported by this flag. */ readonly values: readonly T[]; /** * Default input value if one is not provided at runtime. */ readonly default?: T; readonly optional?: boolean; readonly hidden?: boolean; readonly variadic?: boolean; } interface RequiredEnumFlagParameter<T extends string> extends BaseEnumFlagParameter<T> { /** * Parameter is required and cannot be set as optional. */ readonly optional?: false; /** * Parameter is required and cannot be set as hidden. */ readonly hidden?: false; /** * Parameter does not extend array and cannot be set as variadic. */ readonly variadic?: false; } interface OptionalEnumFlagParameter<T extends string> extends BaseEnumFlagParameter<T> { /** * Parameter is optional and must be specified as such. */ readonly optional: true; /** * Parameter should be hidden from all help text and proposed completions. * Only available for optional parameters. */ readonly hidden?: boolean; /** * Parameter does not extend array and cannot be set as variadic. */ readonly variadic?: false; } interface OptionalVariadicEnumFlagParameter<T extends string> extends BaseEnumFlagParameter<T> { /** * Default values are not supported for variadic parameters. */ readonly default?: undefined; /** * Optional variadic parameter will parse to an empty array if no arguments are found. */ readonly optional: true; /** * Parameter is required and cannot be set as hidden. */ readonly hidden?: false; /** * Parameter extends array and must be set as variadic. */ readonly variadic: true; } interface RequiredVariadicEnumFlagParameter<T extends string> extends BaseEnumFlagParameter<T> { /** * Default values are not supported for variadic parameters. */ readonly default?: undefined; /** * Parameter is required and cannot be set as optional. * Expects at least one value to be satisfied. */ readonly optional?: false; /** * Parameter is required and cannot be set as hidden. */ readonly hidden?: false; /** * Parameter extends array and must be set as variadic. */ readonly variadic: true; } interface BaseParsedFlagParameter<T, CONTEXT extends CommandContext> extends ParsedParameter<T, CONTEXT>, BaseFlagParameter { /** * Indicates flag should be parsed with a specified input parser. */ readonly kind: "parsed"; /** * Default input value if one is not provided at runtime. */ readonly default?: string; /** * If flag is specified with no corresponding input, infer an empty string `""` as the input. */ readonly inferEmpty?: boolean; readonly optional?: boolean; readonly variadic?: boolean; readonly hidden?: boolean; } type RequiredParsedFlagParameter<T, CONTEXT extends CommandContext> = BaseParsedFlagParameter<T, CONTEXT> & { /** * Parameter is required and cannot be set as optional. */ readonly optional?: false; /** * Parameter does not extend array and cannot be set as variadic. */ readonly variadic?: false; } & ({ /** * Parameter is required and cannot be set as hidden without a default value. */ readonly hidden?: false; } | { /** * Default input value if one is not provided at runtime. */ readonly default: string; /** * Parameter should be hidden from all help text and proposed completions. * Only available for runtime-optional parameters. */ readonly hidden: true; }); interface OptionalParsedFlagParameter<T, CONTEXT extends CommandContext> extends BaseParsedFlagParameter<T, CONTEXT> { /** * Parameter is optional and must be specified as such. */ readonly optional: true; /** * Optional parameters should not have default values. * This flag should be required if a value will always be provided. */ readonly default?: undefined; /** * Parameter does not extend array and cannot be set as variadic. */ readonly variadic?: false; /** * Parameter should be hidden from all help text and proposed completions. * Only available for runtime-optional parameters. */ readonly hidden?: boolean; } interface OptionalVariadicParsedFlagParameter<T, CONTEXT extends CommandContext> extends BaseParsedFlagParameter<T, CONTEXT> { /** * Variadic flags are always optional in that they will parse to an empty array if no arguments are found. */ readonly optional: true; /** * Parameter extends array and must be set as variadic. */ readonly variadic: true; /** * Default values are not supported for variadic parameters. */ readonly default?: undefined; } interface RequiredVariadicParsedFlagParameter<T, CONTEXT extends CommandContext> extends BaseParsedFlagParameter<T, CONTEXT> { /** * Parameter is required and cannot be set as optional. * Expects at least one value to be satisfied. */ readonly optional?: false; /** * Parameter extends array and must be set as variadic. */ readonly variadic: true; /** * Default values are not supported for variadic parameters. */ readonly default?: undefined; } type TypedFlagParameter_Optional<T, CONTEXT extends CommandContext> = [T] extends [readonly (infer A)[]] ? [A] extends [string] ? OptionalVariadicParsedFlagParameter<A, CONTEXT> | OptionalParsedFlagParameter<T, CONTEXT> | OptionalVariadicEnumFlagParameter<A> : OptionalVariadicParsedFlagParameter<A, CONTEXT> | OptionalParsedFlagParameter<T, CONTEXT> : [T] extends [boolean] ? OptionalBooleanFlagParameter | OptionalParsedFlagParameter<boolean, CONTEXT> : [T] extends [number] ? OptionalCounterFlagParameter | OptionalParsedFlagParameter<number, CONTEXT> : string extends T ? OptionalParsedFlagParameter<string, CONTEXT> : [T] extends [string] ? OptionalEnumFlagParameter<T> | OptionalParsedFlagParameter<T, CONTEXT> : OptionalParsedFlagParameter<T, CONTEXT>; type TypedFlagParameter_Required<T, CONTEXT extends CommandContext> = [T] extends [readonly (infer A)[]] ? [A] extends [string] ? RequiredVariadicParsedFlagParameter<A, CONTEXT> | RequiredParsedFlagParameter<readonly A[], CONTEXT> | RequiredVariadicEnumFlagParameter<A> : RequiredVariadicParsedFlagParameter<A, CONTEXT> | RequiredParsedFlagParameter<readonly A[], CONTEXT> : [T] extends [boolean] ? RequiredBooleanFlagParameter | RequiredParsedFlagParameter<boolean, CONTEXT> : [T] extends [number] ? RequiredCounterFlagParameter | RequiredParsedFlagParameter<number, CONTEXT> : string extends T ? RequiredParsedFlagParameter<string, CONTEXT> : [T] extends [string] ? RequiredEnumFlagParameter<T> | RequiredParsedFlagParameter<T, CONTEXT> : RequiredParsedFlagParameter<T, CONTEXT>; /** * Definition of a flag parameter that will eventually be parsed as a flag. * Required properties may vary depending on the type argument `T`. */ type TypedFlagParameter<T, CONTEXT extends CommandContext = CommandContext> = undefined extends T ? TypedFlagParameter_Optional<NonNullable<T>, CONTEXT> : TypedFlagParameter_Required<T, CONTEXT>; type FlagParameter<CONTEXT extends CommandContext> = BooleanFlagParameter | CounterFlagParameter | BaseEnumFlagParameter<string> | BaseParsedFlagParameter<unknown, CONTEXT>; /** * Definition of flags for each named parameter. * Required properties may vary depending on the type argument `T`. */ type FlagParametersForType<T, CONTEXT extends CommandContext = CommandContext> = { readonly [K in keyof T]: TypedFlagParameter<T[K], CONTEXT>; }; /** * Definition of flags for each named parameter. * This is a separate version of {@link FlagParametersForType} without a type parameter and is primarily used internally * and should only be used after the types are checked. */ type FlagParameters<CONTEXT extends CommandContext = CommandContext> = Record<string, FlagParameter<CONTEXT>>; /** * Generic function that synchronously or asynchronously parses a string to an arbitrary type. */ type InputParser<T, CONTEXT extends CommandContext = CommandContext> = (this: CONTEXT, input: string) => T | Promise<T>; interface ParsedParameter<T, CONTEXT extends CommandContext> { /** * Function to parse an input string to the type of this parameter. */ readonly parse: InputParser<T, CONTEXT>; /** * Propose possible completions for a partial input string. */ readonly proposeCompletions?: (this: CONTEXT, partial: string) => readonly string[] | Promise<readonly string[]>; } type LowercaseLetter = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"; type UppercaseLetter = Capitalize<LowercaseLetter>; type ReservedAlias = "h"; type AvailableAlias = Exclude<LowercaseLetter | UppercaseLetter, ReservedAlias>; type Aliases<T> = Readonly<Partial<Record<AvailableAlias, T>>>; type BaseFlags = Readonly<Record<string, unknown>>; interface TypedCommandFlagParameters_<FLAGS extends BaseFlags, CONTEXT extends CommandContext> { /** * Typed definitions for all flag parameters. */ readonly flags: FlagParametersForType<FLAGS, CONTEXT>; /** * Object that aliases single characters to flag names. */ readonly aliases?: Aliases<keyof FLAGS & string>; } type TypedCommandFlagParameters<FLAGS extends BaseFlags, CONTEXT extends CommandContext> = Record<string, never> extends FLAGS ? Partial<TypedCommandFlagParameters_<FLAGS, CONTEXT>> : TypedCommandFlagParameters_<FLAGS, CONTEXT>; interface TypedCommandPositionalParameters_<ARGS extends BaseArgs, CONTEXT extends CommandContext> { /** * Typed definitions for all positional parameters. */ readonly positional: TypedPositionalParameters<ARGS, CONTEXT>; } type TypedCommandPositionalParameters<ARGS extends BaseArgs, CONTEXT extends CommandContext> = [] extends ARGS ? Partial<TypedCommandPositionalParameters_<ARGS, CONTEXT>> : TypedCommandPositionalParameters_<ARGS, CONTEXT>; /** * Definitions for all parameters requested by the corresponding command. */ type TypedCommandParameters<FLAGS extends BaseFlags, ARGS extends BaseArgs, CONTEXT extends CommandContext> = TypedCommandFlagParameters<FLAGS, CONTEXT> & TypedCommandPositionalParameters<ARGS, CONTEXT>; /** * Definitions for all parameters requested by the corresponding command. * This is a separate version of {@link TypedCommandParameters} without a type parameter and is primarily used * internally and should only be used after the types are checked. */ interface CommandParameters { readonly flags?: FlagParameters; readonly aliases?: Aliases<string>; readonly positional?: PositionalParameters; } interface BasePositionalParameter<T, CONTEXT extends CommandContext> extends ParsedParameter<T, CONTEXT> { /** * In-line documentation for this parameter. */ readonly brief: string; /** * String that serves as placeholder for the value in the generated usage line. * Defaults to "argN" where N is the index of this parameter. */ readonly placeholder?: string; /** * Default input value if one is not provided at runtime. */ readonly default?: string; readonly optional?: boolean; } interface RequiredPositionalParameter<T, CONTEXT extends CommandContext> extends BasePositionalParameter<T, CONTEXT> { /** * Parameter is required and cannot be set as optional. */ readonly optional?: false; } interface OptionalPositionalParameter<T, CONTEXT extends CommandContext> extends BasePositionalParameter<T, CONTEXT> { /** * Parameter is optional and must be specified as such. */ readonly optional: true; } /** * Definition of a positional parameter that will eventually be parsed to an argument. * Required properties may vary depending on the type argument `T`. */ type TypedPositionalParameter<T, CONTEXT extends CommandContext = CommandContext> = undefined extends T ? OptionalPositionalParameter<NonNullable<T>, CONTEXT> : RequiredPositionalParameter<T, CONTEXT>; type PositionalParameter = BasePositionalParameter<unknown, CommandContext>; interface PositionalParameterArray<T, CONTEXT extends CommandContext> { readonly kind: "array"; readonly parameter: TypedPositionalParameter<T, CONTEXT>; readonly minimum?: number; readonly maximum?: number; } type PositionalParametersForTuple<T, CONTEXT extends CommandContext> = { readonly [K in keyof T]: TypedPositionalParameter<T[K], CONTEXT>; }; interface PositionalParameterTuple<T> { readonly kind: "tuple"; readonly parameters: T; } type BaseArgs = readonly unknown[]; /** * Definition of all positional parameters. * Required properties may vary depending on the type argument `T`. */ type TypedPositionalParameters<T, CONTEXT extends CommandContext> = [T] extends [readonly (infer E)[]] ? number extends T["length"] ? PositionalParameterArray<E, CONTEXT> : PositionalParameterTuple<PositionalParametersForTuple<T, CONTEXT>> : PositionalParameterTuple<PositionalParametersForTuple<T, CONTEXT>>; /** * Definition of all positional parameters. * This is a separate version of {@link TypedPositionalParameters} without a type parameter and is primarily used * internally and should only be used after the types are checked. */ type PositionalParameters = PositionalParameterArray<unknown, CommandContext> | PositionalParameterTuple<readonly PositionalParameter[]>; /** * All command functions are required to have a general signature: * ```ts * (flags: {...}, ...args: [...]) => void | Promise<void> * ``` * - `args` should be an array/tuple of any length or type. * - `flags` should be an object with any key-value pairs. * * The specific types of `args` and `flags` are customizable to the individual use case * and will be used to determine the structure of the positional arguments and flags. */ type CommandFunction<FLAGS extends BaseFlags, ARGS extends BaseArgs, CONTEXT extends CommandContext> = (this: CONTEXT, flags: FLAGS, ...args: ARGS) => void | Error | Promise<void | Error>; /** * A command module exposes the target function as the default export. */ interface CommandModule<FLAGS extends BaseFlags, ARGS extends BaseArgs, CONTEXT extends CommandContext> { readonly default: CommandFunction<FLAGS, ARGS, CONTEXT>; } /** * Asynchronously loads a function or a module containing a function to be executed by a command. */ type CommandFunctionLoader<FLAGS extends BaseFlags, ARGS extends BaseArgs, CONTEXT extends CommandContext> = () => Promise<CommandModule<FLAGS, ARGS, CONTEXT> | CommandFunction<FLAGS, ARGS, CONTEXT>>; declare const CommandSymbol: unique symbol; /** * Parsed and validated command instance. */ interface Command<CONTEXT extends CommandContext> extends DocumentedTarget { readonly kind: typeof CommandSymbol; readonly loader: CommandFunctionLoader<BaseFlags, BaseArgs, CONTEXT>; readonly parameters: CommandParameters; readonly usesFlag: (flagName: string) => boolean; } declare const RouteMapSymbol: unique symbol; interface RouteMapEntry<CONTEXT extends CommandContext> { readonly name: Readonly<Record<DisplayCaseStyle, string>>; readonly target: RoutingTarget<CONTEXT>; readonly aliases: readonly string[]; readonly hidden: boolean; } /** * Route map that stores multiple routes. */ interface RouteMap<CONTEXT extends CommandContext> extends DocumentedTarget { readonly kind: typeof RouteMapSymbol; readonly getRoutingTargetForInput: (input: string) => RoutingTarget<CONTEXT> | undefined; readonly getDefaultCommand: () => Command<CONTEXT> | undefined; readonly getOtherAliasesForInput: (input: string, caseStyle: ScannerCaseStyle) => Readonly<Record<DisplayCaseStyle, readonly string[]>>; readonly getAllEntries: () => readonly RouteMapEntry<CONTEXT>[]; } interface HelpFormattingArguments extends UsageFormattingArguments { readonly text: DocumentationText; readonly includeHelpAllFlag: boolean; readonly includeVersionFlag: boolean; readonly includeArgumentEscapeSequenceFlag: boolean; readonly includeHidden: boolean; readonly aliases?: readonly string[]; } interface DocumentedTarget { readonly brief: string; readonly formatUsageLine: (args: UsageFormattingArguments) => string; readonly formatHelp: (args: HelpFormattingArguments) => string; } type RoutingTarget<CONTEXT extends CommandContext> = Command<CONTEXT> | RouteMap<CONTEXT>; interface RoutingTargetCompletion { readonly kind: "routing-target:command" | "routing-target:route-map"; readonly completion: string; readonly brief: string; } /** * Interface for top-level command line application. */ interface Application<CONTEXT extends CommandContext> { readonly root: RoutingTarget<CONTEXT>; readonly config: ApplicationConfiguration; readonly defaultText: ApplicationText; } /** * Builds an application from a top-level route map and configuration. */ declare function buildApplication<CONTEXT extends CommandContext>(root: RouteMap<CONTEXT>, config: PartialApplicationConfiguration): Application<CONTEXT>; /** * Builds an application with a single, top-level command and configuration. */ declare function buildApplication<CONTEXT extends CommandContext>(command: Command<CONTEXT>, appConfig: PartialApplicationConfiguration): Application<CONTEXT>; type DocumentedCommand = readonly [route: string, helpText: string]; /** * Generate help text in the given locale for each command in this application. * Return value is an array of tuples containing the route to that command and the help text. * * If no locale specified, uses the defaultLocale from the application configuration. */ declare function generateHelpTextForAllCommands({ root, defaultText, config }: Application<CommandContext>, locale?: string): readonly DocumentedCommand[]; declare class InternalError extends Error { } /** * Abstract class that all internal argument scanner errors extend from. */ declare abstract class ArgumentScannerError extends InternalError { private readonly _brand; } interface ArgumentScannerErrorType { readonly AliasNotFoundError: AliasNotFoundError; readonly ArgumentParseError: ArgumentParseError; readonly EnumValidationError: EnumValidationError; readonly FlagNotFoundError: FlagNotFoundError; readonly InvalidNegatedFlagSyntaxError: InvalidNegatedFlagSyntaxError; readonly UnexpectedFlagError: UnexpectedFlagError; readonly UnexpectedPositionalError: UnexpectedPositionalError; readonly UnsatisfiedFlagError: UnsatisfiedFlagError; readonly UnsatisfiedPositionalError: UnsatisfiedPositionalError; } type ArgumentScannerErrorFormatter = { readonly [T in keyof ArgumentScannerErrorType]: (error: ArgumentScannerErrorType[T]) => string; }; /** * Utility method for customizing message of argument scanner error * @param error Error thrown by argument scanner * @param formatter For all keys specified, controls message formatting for that specific subtype of error */ declare function formatMessageForArgumentScannerError(error: ArgumentScannerError, formatter: Partial<ArgumentScannerErrorFormatter>): string; /** * Thrown when input suggests an flag, but no flag with that name was found. */ declare class FlagNotFoundError extends ArgumentScannerError { /** * Command line input that triggered this error. */ readonly input: string; /** * Set of proposed suggestions that are similar to the input. */ readonly corrections: readonly string[]; /** * Set if error was caused indirectly by an alias. * This indicates that something is wrong with the command configuration itself. */ readonly aliasName?: string; constructor(input: string, corrections: readonly string[], aliasName?: string); } /** * Thrown when input suggests an alias, but no alias with that letter was found. */ declare class AliasNotFoundError extends ArgumentScannerError { /** * Command line input that triggered this error. */ readonly input: string; constructor(input: string); } type Placeholder = string & { readonly __Placeholder: unique symbol; }; type ExternalFlagName = string & { readonly __ExternalFlagName: unique symbol; }; /** * Thrown when underlying parameter parser throws an exception parsing some input. */ declare class ArgumentParseError extends ArgumentScannerError { /** * External name of flag or placeholder for positional argument that was parsing this input. */ readonly externalFlagNameOrPlaceholder: string; /** * Command line input that triggered this error. */ readonly input: string; /** * Raw exception thrown from parse function. */ readonly exception: unknown; constructor(externalFlagNameOrPlaceholder: ExternalFlagName | Placeholder, input: string, exception: unknown); } /** * Thrown when input fails to match the given values for an enum flag. */ declare class EnumValidationError extends ArgumentScannerError { /** * External name of flag that was parsing this input. */ readonly externalFlagName: string; /** * Command line input that triggered this error. */ readonly input: string; /** * All possible enum values. */ readonly values: readonly string[]; constructor(externalFlagName: ExternalFlagName, input: string, values: readonly string[], corrections: readonly string[]); } /** * Thrown when flag was expecting input that was not provided. */ declare class UnsatisfiedFlagError extends ArgumentScannerError { /** * External name of flag that was active when this error was thrown. */ readonly externalFlagName: string; /** * External name of flag that interrupted the original flag. */ readonly nextFlagName?: string; constructor(externalFlagName: ExternalFlagName, nextFlagName?: ExternalFlagName); } /** * Thrown when too many positional arguments were encountered. */ declare class UnexpectedPositionalError extends ArgumentScannerError { /** * Expected (maximum) count of positional arguments. */ readonly expectedCount: number; /** * Command line input that triggered this error. */ readonly input: string; constructor(expectedCount: number, input: string); } /** * Thrown when positional parameter was expecting input that was not provided. */ declare class UnsatisfiedPositionalError extends ArgumentScannerError { /** * Placeholder for positional argument that was active when this error was thrown. */ readonly placeholder: string; /** * If specified, indicates the minimum number of arguments that are expected and the last argument count. */ readonly limit?: [minimum: number, count: number]; constructor(placeholder: Placeholder, limit?: [minimum: number, count: number]); } /** * Thrown when a value is provided for a negated flag. */ declare class InvalidNegatedFlagSyntaxError extends ArgumentScannerError { /** * External name of flag that was active when this error was thrown. */ readonly externalFlagName: string; /** * Input text equivalent to right hand side of input */ readonly valueText: string; constructor(externalFlagName: ExternalFlagName, valueText: string); } interface ArgumentCompletion { readonly kind: "argument:flag" | "argument:value"; readonly completion: string; readonly brief: string; } /** * Thrown when single-valued flag encounters more than one value. */ declare class UnexpectedFlagError extends ArgumentScannerError { /** * External name of flag that was parsing this input. */ readonly externalFlagName: string; /** * Command line input that was previously encountered by this flag. */ readonly previousInput: string; /** * Command line input that triggered this error. */ readonly input: string; constructor(externalFlagName: ExternalFlagName, previousInput: string, input: string); } type InputCompletion = ArgumentCompletion | RoutingTargetCompletion; /** * Propose possible completions for a partial input string. */ declare function proposeCompletionsForApplication<CONTEXT extends CommandContext>({ root, config, defaultText }: Application<CONTEXT>, rawInputs: readonly string[], context: StricliDynamicCommandContext<CONTEXT>): Promise<readonly InputCompletion[]>; /** * Enumeration of all possible exit codes returned by an application. */ declare const ExitCode: { /** * Unable to find a command in the application with the given command line arguments. */ readonly UnknownCommand: -5; /** * Unable to parse the specified arguments. */ readonly InvalidArgument: -4; /** * An error was thrown while loading the context for a command run. */ readonly ContextLoadError: -3; /** * Failed to load command module. */ readonly CommandLoadError: -2; /** * An unexpected error was thrown by or not caught by this library. */ readonly InternalError: -1; /** * Command executed successfully. */ readonly Success: 0; /** * Command module unexpectedly threw an error. */ readonly CommandRunError: 1; }; /** * Parses input strings as booleans. * Transforms to lowercase and then checks against "true" and "false". */ declare const booleanParser: (input: string) => boolean; /** * Parses input strings as booleans (loosely). * Transforms to lowercase and then checks against the following values: * - `true`: `"true"`, `"t"`, `"yes"`, `"y"`, `"on"`, `"1"` * - `false`: `"false"`, `"f"`, `"no"`, `"n"`, `"off"`, `"0"` */ declare const looseBooleanParser: (input: string) => boolean; /** * Creates an input parser that checks if the input string is found in a list of choices. */ declare function buildChoiceParser<T extends string>(choices: readonly T[]): InputParser<T>; /** * Parses input strings as numbers. */ declare const numberParser: (input: