@optique/core 1.4.0-dev.2976 → 1.4.0-dev.2979

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.
@@ -39,7 +39,8 @@ function withChildContext(context, segment, state, usage) {
39
39
  const exec = withChildExecPath(context.exec, segment);
40
40
  const dependencyRegistry = context.dependencyRegistry ?? exec?.dependencyRegistry;
41
41
  return {
42
- ...require_short_option.withOptionScopeChild(context, segment),
42
+ ...context,
43
+ ...require_short_option.getChildOptionScope(context, segment, exec?.path),
43
44
  state,
44
45
  ...usage != null ? { usage } : {},
45
46
  ...exec != null ? {
@@ -1,5 +1,5 @@
1
1
  import { mergeCommandDocPolicy } from "./internal/command-doc-policy.js";
2
- import { withOptionScopeChild } from "./short-option.js";
2
+ import { getChildOptionScope } from "./short-option.js";
3
3
 
4
4
  //#region src/execution-context.ts
5
5
  /**
@@ -39,7 +39,8 @@ function withChildContext(context, segment, state, usage) {
39
39
  const exec = withChildExecPath(context.exec, segment);
40
40
  const dependencyRegistry = context.dependencyRegistry ?? exec?.dependencyRegistry;
41
41
  return {
42
- ...withOptionScopeChild(context, segment),
42
+ ...context,
43
+ ...getChildOptionScope(context, segment, exec?.path),
43
44
  state,
44
45
  ...usage != null ? { usage } : {},
45
46
  ...exec != null ? {
package/dist/index.cjs CHANGED
@@ -139,4 +139,5 @@ exports.value = require_message.value;
139
139
  exports.valueSet = require_message.valueSet;
140
140
  exports.values = require_message.values;
141
141
  exports.withDefault = require_modifiers.withDefault;
142
+ exports.withSuggestions = require_valueparser.withSuggestions;
142
143
  exports.zsh = require_completion.zsh;
package/dist/index.d.cts CHANGED
@@ -4,7 +4,7 @@ import { Message, MessageFormatOptions, MessageFormatter, MessageFormatterOption
4
4
  import { ErrorLabelTerm, LabelTerm, ProgramNameTerm, SyntaxPunctuationTerm, TerminalColor, TerminalFormatContext, TerminalFormatter, TerminalFragment, TerminalStyle, TerminalTerm, TerminalTheme, defaultTerminalTheme } from "./terminal.cjs";
5
5
  import { HiddenVisibility, OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractLiteralValues, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage } from "./usage.cjs";
6
6
  import { DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, EnvironmentBindingDoc, ShowAliasesOptions, ShowChoicesOptions, ShowDefaultOptions, ShowEnvironmentOptions, cloneDocEntry, deduplicateDocEntries, deduplicateDocFragments, deriveEnvironmentSection, formatDocPage, isDocEntryHidden } from "./doc.cjs";
7
- import { ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, CidrOptions, CidrValue, Color, ColorFormat, ColorOptions, CronExpression, CronExpressionForOptions, CronOptions, DeferredMap, DomainOptions, EmailOptions, FileSizeOptions, FileSizeOptionsBigInt, FileSizeOptionsNumber, FileSizeUnit, FirstOfOptions, FloatOptions, HostnameOptions, IntegerOptionsBigInt, IntegerOptionsNumber, IpOptions, Ipv4Options, Ipv6Options, Json, JsonOptions, KeyValueOptions, LocaleOptions, MacAddressOptions, OriginOptions, PortOptionsBigInt, PortOptionsNumber, PortRangeOptionsBigInt, PortRangeOptionsNumber, PortRangeValueBigInt, PortRangeValueNumber, RegExpOptions, SemVer, SemVerOptionsObject, SemVerOptionsString, SemVerString, SocketAddressOptions, SocketAddressValue, StringOptions, TransformMapping, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, biject, checkBooleanOption, checkEnumOption, choice, cidr, color, cron, domain, email, fileSize, firstOf, float, hostname, integer, ip, ipv4, ipv6, isValueParser, json, keyValue, locale, macAddress, normalizeInput, origin, port, portRange, regExp, semVer, socketAddress, string, transform, url, uuid } from "./valueparser.cjs";
7
+ import { ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, CidrOptions, CidrValue, Color, ColorFormat, ColorOptions, CronExpression, CronExpressionForOptions, CronOptions, DeferredMap, DomainOptions, EmailOptions, FileSizeOptions, FileSizeOptionsBigInt, FileSizeOptionsNumber, FileSizeUnit, FirstOfOptions, FloatOptions, HostnameOptions, IntegerOptionsBigInt, IntegerOptionsNumber, IpOptions, Ipv4Options, Ipv6Options, Json, JsonOptions, KeyValueOptions, LocaleOptions, MacAddressOptions, OriginOptions, PortOptionsBigInt, PortOptionsNumber, PortRangeOptionsBigInt, PortRangeOptionsNumber, PortRangeValueBigInt, PortRangeValueNumber, RegExpOptions, SemVer, SemVerOptionsObject, SemVerOptionsString, SemVerString, SocketAddressOptions, SocketAddressValue, StringOptions, SuggestionInput, TransformMapping, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithSuggestionsAsyncOptions, WithSuggestionsOptions, WithSuggestionsOptionsBase, WithSuggestionsSyncOptions, biject, checkBooleanOption, checkEnumOption, choice, cidr, color, cron, domain, email, fileSize, firstOf, float, hostname, integer, ip, ipv4, ipv6, isValueParser, json, keyValue, locale, macAddress, normalizeInput, origin, port, portRange, regExp, semVer, socketAddress, string, transform, url, uuid, withSuggestions } from "./valueparser.cjs";
8
8
  import { CombineModes, DetailedParseResult, DocState, EffectfulCompletionSession, ExecutionContext, ExecutionPhase, InferMode, InferValue, Mode, ModeIterable, ModeValue, ParseFrame, Parser, ParserContext, ParserResult, Result, Suggestion, createParserContext, getDocPage, getDocPageAsync, getDocPageSync, parse, parseAsync, parseDetailed, parseSync, suggest, suggestAsync, suggestSync } from "./internal/parser.cjs";
9
9
  import { ShellCompletion, bash, fish, nu, pwsh, zsh } from "./completion.cjs";
10
10
  import { DeferredValue, DeferredValueOptions, DeferredValueSource, FluentParser, MultipleErrorOptions, MultipleOptions, ParserModifiers, WithDefaultError, WithDefaultOptions, deferredValue, fluent, isDeferredValue, map, multiple, nonEmpty, optional, withDefault } from "./modifiers.cjs";
@@ -13,4 +13,4 @@ import { ParserValuePlaceholder, SourceContext, SourceContextRequest } from "./c
13
13
  import { AnyDependencySource, CombineMode, CombinedDependencyMode, DependencyMode, DependencySource, DependencySourceInfo, DependencyValue, DependencyValues, DeriveAsyncOptions, DeriveFromAsyncOptions, DeriveFromOptions, DeriveFromSyncOptions, DeriveOptions, DeriveSyncOptions, DerivedValueParser, dependency, deriveFrom, deriveFromAsync, deriveFromSync, getDependencySourceInfo, isDependencySource, isDerivedValueParser } from "./internal/dependency.cjs";
14
14
  import { CommandListMode, CommandSubConfig, ContextOptionsParam, ExtractRequiredOptions, HelpSectionsOptions, OptionSubConfig, RunOptions, RunParserError, RunWithOptions, SubstituteParserValue, runParser, runParserAsync, runParserSync, runWith, runWithAsync, runWithSync } from "./facade.cjs";
15
15
  import { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, FlagErrorOptions, FlagOptions, NegatableFlagErrorOptions, NegatableFlagNameList, NegatableFlagNames, NegatableFlagOptions, NegatableFlagState, OptionErrorOptions, OptionOptions, OptionState, PassThroughFormat, PassThroughOptions, argument, command, constant, fail, flag, negatableFlag, option, passThrough } from "./primitives.cjs";
16
- export { type Annotations, AnyDependencySource, ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, CidrOptions, CidrValue, Color, ColorFormat, ColorOptions, CombineMode, CombineModes, CombinedDependencyMode, CommandErrorOptions, CommandListMode, CommandOptions, CommandSubConfig, ConditionalErrorOptions, ConditionalOptions, ContextOptionsParam, CronExpression, CronExpressionForOptions, CronOptions, DeferredMap, DeferredValue, DeferredValueOptions, DeferredValueSource, DependencyMode, DependencySource, DependencySourceInfo, DependencyValue, DependencyValues, DeriveAsyncOptions, DeriveFromAsyncOptions, DeriveFromOptions, DeriveFromSyncOptions, DeriveOptions, DeriveSyncOptions, DerivedValueParser, DetailedParseResult, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, DomainOptions, DuplicateOptionError, EffectfulCompletionSession, EmailOptions, EnvironmentBindingDoc, ErrorLabelTerm, ExecutionContext, ExecutionPhase, ExtractRequiredOptions, FileSizeOptions, FileSizeOptionsBigInt, FileSizeOptionsNumber, FileSizeUnit, FirstOfOptions, FlagErrorOptions, FlagOptions, FloatOptions, FluentParser, GroupOptions, HelpSectionsOptions, HiddenVisibility, HostnameOptions, InferMode, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, IpOptions, Ipv4Options, Ipv6Options, Json, JsonOptions, KeyValueOptions, LabelTerm, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, MacAddressOptions, MergeOptions, type Message, type MessageFormatOptions, type MessageFormatter, type MessageFormatterOptions, type MessageTerm, Mode, ModeIterable, ModeValue, MultipleErrorOptions, MultipleOptions, NegatableFlagErrorOptions, NegatableFlagNameList, NegatableFlagNames, NegatableFlagOptions, NegatableFlagState, NoMatchContext, NonEmptyString, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OptionState, OptionSubConfig, OrErrorOptions, OrOptions, OriginOptions, ParseFrame, type ParseOptions, Parser, ParserContext, ParserModifiers, ParserResult, ParserValuePlaceholder, PassThroughFormat, PassThroughOptions, PortOptionsBigInt, PortOptionsNumber, PortRangeOptionsBigInt, PortRangeOptionsNumber, PortRangeValueBigInt, PortRangeValueNumber, ProgramNameTerm, RegExpOptions, Result, RunOptions, RunParserError, RunWithOptions, SemVer, SemVerOptionsObject, SemVerOptionsString, SemVerString, SeqOptions, ShellCompletion, ShowAliasesOptions, ShowChoicesOptions, ShowDefaultOptions, ShowEnvironmentOptions, SocketAddressOptions, SocketAddressValue, SourceContext, SourceContextRequest, StringOptions, SubstituteParserValue, Suggestion, SyntaxPunctuationTerm, TerminalColor, TerminalFormatContext, TerminalFormatter, TerminalFragment, TerminalStyle, TerminalTerm, TerminalTheme, TransformMapping, TupleOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, type ValueSetOptions, WithDefaultError, WithDefaultOptions, argument, bash, biject, checkBooleanOption, checkEnumOption, choice, cidr, cloneDocEntry, cloneUsage, cloneUsageTerm, color, command, commandLine, concat, conditional, constant, createMessageFormatter, createParserContext, cron, deduplicateDocEntries, deduplicateDocFragments, defaultTerminalTheme, deferredValue, dependency, deriveEnvironmentSection, deriveFrom, deriveFromAsync, deriveFromSync, domain, email, ensureNonEmptyString, envVar, extractArgumentMetavars, extractCommandNames, extractLiteralValues, extractOptionNames, fail, fileSize, firstOf, fish, flag, float, fluent, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getAnnotations, getDependencySourceInfo, getDocPage, getDocPageAsync, getDocPageSync, group, hostname, integer, ip, ipv4, ipv6, isDeferredValue, isDependencySource, isDerivedValueParser, isDocEntryHidden, isDocHidden, isNonEmptyString, isSuggestionHidden, isUsageHidden, isValueParser, json, keyValue, lineBreak, link, locale, longestMatch, macAddress, map, merge, mergeHidden, message, metavar, multiple, negatableFlag, nonEmpty, normalizeInput, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, origin, parse, parseAsync, parseDetailed, parseSync, passThrough, port, portRange, pwsh, regExp, runParser, runParserAsync, runParserSync, runWith, runWithAsync, runWithSync, semVer, seq, socketAddress, string, suggest, suggestAsync, suggestSync, text, transform, tuple, url, uuid, value, valueSet, values, withDefault, zsh };
16
+ export { type Annotations, AnyDependencySource, ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, CidrOptions, CidrValue, Color, ColorFormat, ColorOptions, CombineMode, CombineModes, CombinedDependencyMode, CommandErrorOptions, CommandListMode, CommandOptions, CommandSubConfig, ConditionalErrorOptions, ConditionalOptions, ContextOptionsParam, CronExpression, CronExpressionForOptions, CronOptions, DeferredMap, DeferredValue, DeferredValueOptions, DeferredValueSource, DependencyMode, DependencySource, DependencySourceInfo, DependencyValue, DependencyValues, DeriveAsyncOptions, DeriveFromAsyncOptions, DeriveFromOptions, DeriveFromSyncOptions, DeriveOptions, DeriveSyncOptions, DerivedValueParser, DetailedParseResult, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, DomainOptions, DuplicateOptionError, EffectfulCompletionSession, EmailOptions, EnvironmentBindingDoc, ErrorLabelTerm, ExecutionContext, ExecutionPhase, ExtractRequiredOptions, FileSizeOptions, FileSizeOptionsBigInt, FileSizeOptionsNumber, FileSizeUnit, FirstOfOptions, FlagErrorOptions, FlagOptions, FloatOptions, FluentParser, GroupOptions, HelpSectionsOptions, HiddenVisibility, HostnameOptions, InferMode, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, IpOptions, Ipv4Options, Ipv6Options, Json, JsonOptions, KeyValueOptions, LabelTerm, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, MacAddressOptions, MergeOptions, type Message, type MessageFormatOptions, type MessageFormatter, type MessageFormatterOptions, type MessageTerm, Mode, ModeIterable, ModeValue, MultipleErrorOptions, MultipleOptions, NegatableFlagErrorOptions, NegatableFlagNameList, NegatableFlagNames, NegatableFlagOptions, NegatableFlagState, NoMatchContext, NonEmptyString, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OptionState, OptionSubConfig, OrErrorOptions, OrOptions, OriginOptions, ParseFrame, type ParseOptions, Parser, ParserContext, ParserModifiers, ParserResult, ParserValuePlaceholder, PassThroughFormat, PassThroughOptions, PortOptionsBigInt, PortOptionsNumber, PortRangeOptionsBigInt, PortRangeOptionsNumber, PortRangeValueBigInt, PortRangeValueNumber, ProgramNameTerm, RegExpOptions, Result, RunOptions, RunParserError, RunWithOptions, SemVer, SemVerOptionsObject, SemVerOptionsString, SemVerString, SeqOptions, ShellCompletion, ShowAliasesOptions, ShowChoicesOptions, ShowDefaultOptions, ShowEnvironmentOptions, SocketAddressOptions, SocketAddressValue, SourceContext, SourceContextRequest, StringOptions, SubstituteParserValue, Suggestion, SuggestionInput, SyntaxPunctuationTerm, TerminalColor, TerminalFormatContext, TerminalFormatter, TerminalFragment, TerminalStyle, TerminalTerm, TerminalTheme, TransformMapping, TupleOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, type ValueSetOptions, WithDefaultError, WithDefaultOptions, WithSuggestionsAsyncOptions, WithSuggestionsOptions, WithSuggestionsOptionsBase, WithSuggestionsSyncOptions, argument, bash, biject, checkBooleanOption, checkEnumOption, choice, cidr, cloneDocEntry, cloneUsage, cloneUsageTerm, color, command, commandLine, concat, conditional, constant, createMessageFormatter, createParserContext, cron, deduplicateDocEntries, deduplicateDocFragments, defaultTerminalTheme, deferredValue, dependency, deriveEnvironmentSection, deriveFrom, deriveFromAsync, deriveFromSync, domain, email, ensureNonEmptyString, envVar, extractArgumentMetavars, extractCommandNames, extractLiteralValues, extractOptionNames, fail, fileSize, firstOf, fish, flag, float, fluent, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getAnnotations, getDependencySourceInfo, getDocPage, getDocPageAsync, getDocPageSync, group, hostname, integer, ip, ipv4, ipv6, isDeferredValue, isDependencySource, isDerivedValueParser, isDocEntryHidden, isDocHidden, isNonEmptyString, isSuggestionHidden, isUsageHidden, isValueParser, json, keyValue, lineBreak, link, locale, longestMatch, macAddress, map, merge, mergeHidden, message, metavar, multiple, negatableFlag, nonEmpty, normalizeInput, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, origin, parse, parseAsync, parseDetailed, parseSync, passThrough, port, portRange, pwsh, regExp, runParser, runParserAsync, runParserSync, runWith, runWithAsync, runWithSync, semVer, seq, socketAddress, string, suggest, suggestAsync, suggestSync, text, transform, tuple, url, uuid, value, valueSet, values, withDefault, withSuggestions, zsh };
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ import { Message, MessageFormatOptions, MessageFormatter, MessageFormatterOption
4
4
  import { ErrorLabelTerm, LabelTerm, ProgramNameTerm, SyntaxPunctuationTerm, TerminalColor, TerminalFormatContext, TerminalFormatter, TerminalFragment, TerminalStyle, TerminalTerm, TerminalTheme, defaultTerminalTheme } from "./terminal.js";
5
5
  import { HiddenVisibility, OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractLiteralValues, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage } from "./usage.js";
6
6
  import { DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, EnvironmentBindingDoc, ShowAliasesOptions, ShowChoicesOptions, ShowDefaultOptions, ShowEnvironmentOptions, cloneDocEntry, deduplicateDocEntries, deduplicateDocFragments, deriveEnvironmentSection, formatDocPage, isDocEntryHidden } from "./doc.js";
7
- import { ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, CidrOptions, CidrValue, Color, ColorFormat, ColorOptions, CronExpression, CronExpressionForOptions, CronOptions, DeferredMap, DomainOptions, EmailOptions, FileSizeOptions, FileSizeOptionsBigInt, FileSizeOptionsNumber, FileSizeUnit, FirstOfOptions, FloatOptions, HostnameOptions, IntegerOptionsBigInt, IntegerOptionsNumber, IpOptions, Ipv4Options, Ipv6Options, Json, JsonOptions, KeyValueOptions, LocaleOptions, MacAddressOptions, OriginOptions, PortOptionsBigInt, PortOptionsNumber, PortRangeOptionsBigInt, PortRangeOptionsNumber, PortRangeValueBigInt, PortRangeValueNumber, RegExpOptions, SemVer, SemVerOptionsObject, SemVerOptionsString, SemVerString, SocketAddressOptions, SocketAddressValue, StringOptions, TransformMapping, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, biject, checkBooleanOption, checkEnumOption, choice, cidr, color, cron, domain, email, fileSize, firstOf, float, hostname, integer, ip, ipv4, ipv6, isValueParser, json, keyValue, locale, macAddress, normalizeInput, origin, port, portRange, regExp, semVer, socketAddress, string, transform, url, uuid } from "./valueparser.js";
7
+ import { ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, CidrOptions, CidrValue, Color, ColorFormat, ColorOptions, CronExpression, CronExpressionForOptions, CronOptions, DeferredMap, DomainOptions, EmailOptions, FileSizeOptions, FileSizeOptionsBigInt, FileSizeOptionsNumber, FileSizeUnit, FirstOfOptions, FloatOptions, HostnameOptions, IntegerOptionsBigInt, IntegerOptionsNumber, IpOptions, Ipv4Options, Ipv6Options, Json, JsonOptions, KeyValueOptions, LocaleOptions, MacAddressOptions, OriginOptions, PortOptionsBigInt, PortOptionsNumber, PortRangeOptionsBigInt, PortRangeOptionsNumber, PortRangeValueBigInt, PortRangeValueNumber, RegExpOptions, SemVer, SemVerOptionsObject, SemVerOptionsString, SemVerString, SocketAddressOptions, SocketAddressValue, StringOptions, SuggestionInput, TransformMapping, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithSuggestionsAsyncOptions, WithSuggestionsOptions, WithSuggestionsOptionsBase, WithSuggestionsSyncOptions, biject, checkBooleanOption, checkEnumOption, choice, cidr, color, cron, domain, email, fileSize, firstOf, float, hostname, integer, ip, ipv4, ipv6, isValueParser, json, keyValue, locale, macAddress, normalizeInput, origin, port, portRange, regExp, semVer, socketAddress, string, transform, url, uuid, withSuggestions } from "./valueparser.js";
8
8
  import { CombineModes, DetailedParseResult, DocState, EffectfulCompletionSession, ExecutionContext, ExecutionPhase, InferMode, InferValue, Mode, ModeIterable, ModeValue, ParseFrame, Parser, ParserContext, ParserResult, Result, Suggestion, createParserContext, getDocPage, getDocPageAsync, getDocPageSync, parse, parseAsync, parseDetailed, parseSync, suggest, suggestAsync, suggestSync } from "./internal/parser.js";
9
9
  import { ShellCompletion, bash, fish, nu, pwsh, zsh } from "./completion.js";
10
10
  import { DeferredValue, DeferredValueOptions, DeferredValueSource, FluentParser, MultipleErrorOptions, MultipleOptions, ParserModifiers, WithDefaultError, WithDefaultOptions, deferredValue, fluent, isDeferredValue, map, multiple, nonEmpty, optional, withDefault } from "./modifiers.js";
@@ -13,4 +13,4 @@ import { ParserValuePlaceholder, SourceContext, SourceContextRequest } from "./c
13
13
  import { AnyDependencySource, CombineMode, CombinedDependencyMode, DependencyMode, DependencySource, DependencySourceInfo, DependencyValue, DependencyValues, DeriveAsyncOptions, DeriveFromAsyncOptions, DeriveFromOptions, DeriveFromSyncOptions, DeriveOptions, DeriveSyncOptions, DerivedValueParser, dependency, deriveFrom, deriveFromAsync, deriveFromSync, getDependencySourceInfo, isDependencySource, isDerivedValueParser } from "./internal/dependency.js";
14
14
  import { CommandListMode, CommandSubConfig, ContextOptionsParam, ExtractRequiredOptions, HelpSectionsOptions, OptionSubConfig, RunOptions, RunParserError, RunWithOptions, SubstituteParserValue, runParser, runParserAsync, runParserSync, runWith, runWithAsync, runWithSync } from "./facade.js";
15
15
  import { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, FlagErrorOptions, FlagOptions, NegatableFlagErrorOptions, NegatableFlagNameList, NegatableFlagNames, NegatableFlagOptions, NegatableFlagState, OptionErrorOptions, OptionOptions, OptionState, PassThroughFormat, PassThroughOptions, argument, command, constant, fail, flag, negatableFlag, option, passThrough } from "./primitives.js";
16
- export { type Annotations, AnyDependencySource, ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, CidrOptions, CidrValue, Color, ColorFormat, ColorOptions, CombineMode, CombineModes, CombinedDependencyMode, CommandErrorOptions, CommandListMode, CommandOptions, CommandSubConfig, ConditionalErrorOptions, ConditionalOptions, ContextOptionsParam, CronExpression, CronExpressionForOptions, CronOptions, DeferredMap, DeferredValue, DeferredValueOptions, DeferredValueSource, DependencyMode, DependencySource, DependencySourceInfo, DependencyValue, DependencyValues, DeriveAsyncOptions, DeriveFromAsyncOptions, DeriveFromOptions, DeriveFromSyncOptions, DeriveOptions, DeriveSyncOptions, DerivedValueParser, DetailedParseResult, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, DomainOptions, DuplicateOptionError, EffectfulCompletionSession, EmailOptions, EnvironmentBindingDoc, ErrorLabelTerm, ExecutionContext, ExecutionPhase, ExtractRequiredOptions, FileSizeOptions, FileSizeOptionsBigInt, FileSizeOptionsNumber, FileSizeUnit, FirstOfOptions, FlagErrorOptions, FlagOptions, FloatOptions, FluentParser, GroupOptions, HelpSectionsOptions, HiddenVisibility, HostnameOptions, InferMode, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, IpOptions, Ipv4Options, Ipv6Options, Json, JsonOptions, KeyValueOptions, LabelTerm, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, MacAddressOptions, MergeOptions, type Message, type MessageFormatOptions, type MessageFormatter, type MessageFormatterOptions, type MessageTerm, Mode, ModeIterable, ModeValue, MultipleErrorOptions, MultipleOptions, NegatableFlagErrorOptions, NegatableFlagNameList, NegatableFlagNames, NegatableFlagOptions, NegatableFlagState, NoMatchContext, NonEmptyString, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OptionState, OptionSubConfig, OrErrorOptions, OrOptions, OriginOptions, ParseFrame, type ParseOptions, Parser, ParserContext, ParserModifiers, ParserResult, ParserValuePlaceholder, PassThroughFormat, PassThroughOptions, PortOptionsBigInt, PortOptionsNumber, PortRangeOptionsBigInt, PortRangeOptionsNumber, PortRangeValueBigInt, PortRangeValueNumber, ProgramNameTerm, RegExpOptions, Result, RunOptions, RunParserError, RunWithOptions, SemVer, SemVerOptionsObject, SemVerOptionsString, SemVerString, SeqOptions, ShellCompletion, ShowAliasesOptions, ShowChoicesOptions, ShowDefaultOptions, ShowEnvironmentOptions, SocketAddressOptions, SocketAddressValue, SourceContext, SourceContextRequest, StringOptions, SubstituteParserValue, Suggestion, SyntaxPunctuationTerm, TerminalColor, TerminalFormatContext, TerminalFormatter, TerminalFragment, TerminalStyle, TerminalTerm, TerminalTheme, TransformMapping, TupleOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, type ValueSetOptions, WithDefaultError, WithDefaultOptions, argument, bash, biject, checkBooleanOption, checkEnumOption, choice, cidr, cloneDocEntry, cloneUsage, cloneUsageTerm, color, command, commandLine, concat, conditional, constant, createMessageFormatter, createParserContext, cron, deduplicateDocEntries, deduplicateDocFragments, defaultTerminalTheme, deferredValue, dependency, deriveEnvironmentSection, deriveFrom, deriveFromAsync, deriveFromSync, domain, email, ensureNonEmptyString, envVar, extractArgumentMetavars, extractCommandNames, extractLiteralValues, extractOptionNames, fail, fileSize, firstOf, fish, flag, float, fluent, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getAnnotations, getDependencySourceInfo, getDocPage, getDocPageAsync, getDocPageSync, group, hostname, integer, ip, ipv4, ipv6, isDeferredValue, isDependencySource, isDerivedValueParser, isDocEntryHidden, isDocHidden, isNonEmptyString, isSuggestionHidden, isUsageHidden, isValueParser, json, keyValue, lineBreak, link, locale, longestMatch, macAddress, map, merge, mergeHidden, message, metavar, multiple, negatableFlag, nonEmpty, normalizeInput, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, origin, parse, parseAsync, parseDetailed, parseSync, passThrough, port, portRange, pwsh, regExp, runParser, runParserAsync, runParserSync, runWith, runWithAsync, runWithSync, semVer, seq, socketAddress, string, suggest, suggestAsync, suggestSync, text, transform, tuple, url, uuid, value, valueSet, values, withDefault, zsh };
16
+ export { type Annotations, AnyDependencySource, ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, CidrOptions, CidrValue, Color, ColorFormat, ColorOptions, CombineMode, CombineModes, CombinedDependencyMode, CommandErrorOptions, CommandListMode, CommandOptions, CommandSubConfig, ConditionalErrorOptions, ConditionalOptions, ContextOptionsParam, CronExpression, CronExpressionForOptions, CronOptions, DeferredMap, DeferredValue, DeferredValueOptions, DeferredValueSource, DependencyMode, DependencySource, DependencySourceInfo, DependencyValue, DependencyValues, DeriveAsyncOptions, DeriveFromAsyncOptions, DeriveFromOptions, DeriveFromSyncOptions, DeriveOptions, DeriveSyncOptions, DerivedValueParser, DetailedParseResult, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, DomainOptions, DuplicateOptionError, EffectfulCompletionSession, EmailOptions, EnvironmentBindingDoc, ErrorLabelTerm, ExecutionContext, ExecutionPhase, ExtractRequiredOptions, FileSizeOptions, FileSizeOptionsBigInt, FileSizeOptionsNumber, FileSizeUnit, FirstOfOptions, FlagErrorOptions, FlagOptions, FloatOptions, FluentParser, GroupOptions, HelpSectionsOptions, HiddenVisibility, HostnameOptions, InferMode, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, IpOptions, Ipv4Options, Ipv6Options, Json, JsonOptions, KeyValueOptions, LabelTerm, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, MacAddressOptions, MergeOptions, type Message, type MessageFormatOptions, type MessageFormatter, type MessageFormatterOptions, type MessageTerm, Mode, ModeIterable, ModeValue, MultipleErrorOptions, MultipleOptions, NegatableFlagErrorOptions, NegatableFlagNameList, NegatableFlagNames, NegatableFlagOptions, NegatableFlagState, NoMatchContext, NonEmptyString, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OptionState, OptionSubConfig, OrErrorOptions, OrOptions, OriginOptions, ParseFrame, type ParseOptions, Parser, ParserContext, ParserModifiers, ParserResult, ParserValuePlaceholder, PassThroughFormat, PassThroughOptions, PortOptionsBigInt, PortOptionsNumber, PortRangeOptionsBigInt, PortRangeOptionsNumber, PortRangeValueBigInt, PortRangeValueNumber, ProgramNameTerm, RegExpOptions, Result, RunOptions, RunParserError, RunWithOptions, SemVer, SemVerOptionsObject, SemVerOptionsString, SemVerString, SeqOptions, ShellCompletion, ShowAliasesOptions, ShowChoicesOptions, ShowDefaultOptions, ShowEnvironmentOptions, SocketAddressOptions, SocketAddressValue, SourceContext, SourceContextRequest, StringOptions, SubstituteParserValue, Suggestion, SuggestionInput, SyntaxPunctuationTerm, TerminalColor, TerminalFormatContext, TerminalFormatter, TerminalFragment, TerminalStyle, TerminalTerm, TerminalTheme, TransformMapping, TupleOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, type ValueSetOptions, WithDefaultError, WithDefaultOptions, WithSuggestionsAsyncOptions, WithSuggestionsOptions, WithSuggestionsOptionsBase, WithSuggestionsSyncOptions, argument, bash, biject, checkBooleanOption, checkEnumOption, choice, cidr, cloneDocEntry, cloneUsage, cloneUsageTerm, color, command, commandLine, concat, conditional, constant, createMessageFormatter, createParserContext, cron, deduplicateDocEntries, deduplicateDocFragments, defaultTerminalTheme, deferredValue, dependency, deriveEnvironmentSection, deriveFrom, deriveFromAsync, deriveFromSync, domain, email, ensureNonEmptyString, envVar, extractArgumentMetavars, extractCommandNames, extractLiteralValues, extractOptionNames, fail, fileSize, firstOf, fish, flag, float, fluent, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getAnnotations, getDependencySourceInfo, getDocPage, getDocPageAsync, getDocPageSync, group, hostname, integer, ip, ipv4, ipv6, isDeferredValue, isDependencySource, isDerivedValueParser, isDocEntryHidden, isDocHidden, isNonEmptyString, isSuggestionHidden, isUsageHidden, isValueParser, json, keyValue, lineBreak, link, locale, longestMatch, macAddress, map, merge, mergeHidden, message, metavar, multiple, negatableFlag, nonEmpty, normalizeInput, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, origin, parse, parseAsync, parseDetailed, parseSync, passThrough, port, portRange, pwsh, regExp, runParser, runParserAsync, runParserSync, runWith, runWithAsync, runWithSync, semVer, seq, socketAddress, string, suggest, suggestAsync, suggestSync, text, transform, tuple, url, uuid, value, valueSet, values, withDefault, withSuggestions, zsh };
package/dist/index.js CHANGED
@@ -9,8 +9,8 @@ import { bash, fish, nu, pwsh, zsh } from "./completion.js";
9
9
  import { WithDefaultError, deferredValue, fluent, isDeferredValue, map, multiple, nonEmpty, optional, withDefault } from "./modifiers.js";
10
10
  import { DuplicateOptionError, concat, conditional, group, longestMatch, merge, object, or, seq, tuple } from "./constructs.js";
11
11
  import { ensureNonEmptyString, isNonEmptyString } from "./nonempty.js";
12
- import { biject, checkBooleanOption, checkEnumOption, choice, cidr, color, cron, domain, email, fileSize, firstOf, float, hostname, integer, ip, ipv4, ipv6, isValueParser, json, keyValue, locale, macAddress, normalizeInput, origin, port, portRange, regExp, semVer, socketAddress, string, transform, url, uuid } from "./valueparser.js";
12
+ import { biject, checkBooleanOption, checkEnumOption, choice, cidr, color, cron, domain, email, fileSize, firstOf, float, hostname, integer, ip, ipv4, ipv6, isValueParser, json, keyValue, locale, macAddress, normalizeInput, origin, port, portRange, regExp, semVer, socketAddress, string, transform, url, uuid, withSuggestions } from "./valueparser.js";
13
13
  import { argument, command, constant, fail, flag, negatableFlag, option, passThrough } from "./primitives.js";
14
14
  import { RunParserError, runParser, runParserAsync, runParserSync, runWith, runWithAsync, runWithSync } from "./facade.js";
15
15
 
16
- export { DuplicateOptionError, RunParserError, WithDefaultError, argument, bash, biject, checkBooleanOption, checkEnumOption, choice, cidr, cloneDocEntry, cloneUsage, cloneUsageTerm, color, command, commandLine, concat, conditional, constant, createMessageFormatter, createParserContext, cron, deduplicateDocEntries, deduplicateDocFragments, defaultTerminalTheme, deferredValue, dependency, deriveEnvironmentSection, deriveFrom, deriveFromAsync, deriveFromSync, domain, email, ensureNonEmptyString, envVar, extractArgumentMetavars, extractCommandNames, extractLiteralValues, extractOptionNames, fail, fileSize, firstOf, fish, flag, float, fluent, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getAnnotations, getDependencySourceInfo, getDocPage, getDocPageAsync, getDocPageSync, group, hostname, integer, ip, ipv4, ipv6, isDeferredValue, isDependencySource, isDerivedValueParser, isDocEntryHidden, isDocHidden, isNonEmptyString, isSuggestionHidden, isUsageHidden, isValueParser, json, keyValue, lineBreak, link, locale, longestMatch, macAddress, map, merge, mergeHidden, message, metavar, multiple, negatableFlag, nonEmpty, normalizeInput, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, origin, parse, parseAsync, parseDetailed, parseSync, passThrough, port, portRange, pwsh, regExp, runParser, runParserAsync, runParserSync, runWith, runWithAsync, runWithSync, semVer, seq, socketAddress, string, suggest, suggestAsync, suggestSync, text, transform, tuple, url, uuid, value, valueSet, values, withDefault, zsh };
16
+ export { DuplicateOptionError, RunParserError, WithDefaultError, argument, bash, biject, checkBooleanOption, checkEnumOption, choice, cidr, cloneDocEntry, cloneUsage, cloneUsageTerm, color, command, commandLine, concat, conditional, constant, createMessageFormatter, createParserContext, cron, deduplicateDocEntries, deduplicateDocFragments, defaultTerminalTheme, deferredValue, dependency, deriveEnvironmentSection, deriveFrom, deriveFromAsync, deriveFromSync, domain, email, ensureNonEmptyString, envVar, extractArgumentMetavars, extractCommandNames, extractLiteralValues, extractOptionNames, fail, fileSize, firstOf, fish, flag, float, fluent, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getAnnotations, getDependencySourceInfo, getDocPage, getDocPageAsync, getDocPageSync, group, hostname, integer, ip, ipv4, ipv6, isDeferredValue, isDependencySource, isDerivedValueParser, isDocEntryHidden, isDocHidden, isNonEmptyString, isSuggestionHidden, isUsageHidden, isValueParser, json, keyValue, lineBreak, link, locale, longestMatch, macAddress, map, merge, mergeHidden, message, metavar, multiple, negatableFlag, nonEmpty, normalizeInput, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, origin, parse, parseAsync, parseDetailed, parseSync, passThrough, port, portRange, pwsh, regExp, runParser, runParserAsync, runParserSync, runWith, runWithAsync, runWithSync, semVer, seq, socketAddress, string, suggest, suggestAsync, suggestSync, text, transform, tuple, url, uuid, value, valueSet, values, withDefault, withSuggestions, zsh };
@@ -18,6 +18,16 @@ function getScopeBindings(selections) {
18
18
  }
19
19
  return bindings;
20
20
  }
21
+ function setSelection(selections, source, selected) {
22
+ if (selections.map.get(source) === selected) return;
23
+ if (!selections.owned) {
24
+ const previous = selections.map;
25
+ selections.map = new Map(previous);
26
+ scopeBindings.set(selections.map, getScopeBindings(previous));
27
+ selections.owned = true;
28
+ }
29
+ selections.map.set(source, selected);
30
+ }
21
31
  function bindScope(source, path, selections) {
22
32
  const boundScopes = getScopeBindings(selections);
23
33
  let node = boundScopes.get(source);
@@ -47,12 +57,9 @@ function bindScope(source, path, selections) {
47
57
  function scopePath(context) {
48
58
  return context.exec?.path ?? context[optionPathKey] ?? [];
49
59
  }
50
- /** Tracks child occurrences even for contexts without execution metadata. @internal */
51
- function withOptionScopeChild(context, segment) {
52
- return {
53
- ...context,
54
- [optionPathKey]: [...scopePath(context), segment]
55
- };
60
+ /** Supplies child occurrence metadata without copying the parent. @internal */
61
+ function getChildOptionScope(context, segment, path) {
62
+ return { [optionPathKey]: path ?? [...scopePath(context), segment] };
56
63
  }
57
64
  const primitiveScopes = /* @__PURE__ */ new WeakMap();
58
65
  function parserScope(parser) {
@@ -104,7 +111,7 @@ function selectableOptionScope(initial) {
104
111
  select(context, selected) {
105
112
  const selections = context[optionScopeKey]?.selections;
106
113
  if (selections == null) return;
107
- selections.set(bindScope(source, scopePath(context), selections), selected);
114
+ setSelection(selections, bindScope(source, scopePath(context), selections.map), selected);
108
115
  }
109
116
  };
110
117
  }
@@ -135,7 +142,7 @@ function conditionalOptionScope(discriminator, fallback, candidates = []) {
135
142
  /** Returns the names reachable in this parse scope. @internal */
136
143
  function getOptionScope(context) {
137
144
  const scoped = context[optionScopeKey];
138
- const names = scoped == null ? require_usage.extractOptionNames(context.usage, true) : new Set([...scoped.names, ...[...scoped.sources].flatMap((source) => [...source(scoped.selections, void 0, context)])]);
145
+ const names = scoped == null ? require_usage.extractOptionNames(context.usage, true) : new Set([...scoped.names, ...[...scoped.sources].flatMap((source) => [...source(scoped.selections.map, void 0, context)])]);
139
146
  return names;
140
147
  }
141
148
  /** Adds reachable names without consulting the flattened help tree. @internal */
@@ -146,19 +153,42 @@ function withOptionScope(context, names) {
146
153
  [optionScopeKey]: {
147
154
  names: new Set([...inherited?.names ?? [], ...names]),
148
155
  sources: inherited?.sources ?? /* @__PURE__ */ new Set(),
149
- selections: inherited?.selections ?? /* @__PURE__ */ new Map()
156
+ selections: inherited?.selections ?? {
157
+ map: /* @__PURE__ */ new Map(),
158
+ owned: true
159
+ }
150
160
  }
151
161
  };
152
162
  }
163
+ const scopeUnions = /* @__PURE__ */ new WeakMap();
164
+ function withScopeSource(sources, source) {
165
+ let unions = scopeUnions.get(sources);
166
+ if (unions == null) {
167
+ unions = /* @__PURE__ */ new WeakMap();
168
+ scopeUnions.set(sources, unions);
169
+ }
170
+ let combined = unions.get(source);
171
+ if (combined == null) {
172
+ combined = new Set([...sources, source]);
173
+ unions.set(source, combined);
174
+ }
175
+ return combined;
176
+ }
153
177
  /** Adds a parser's owned declarations, retaining modifier metadata. @internal */
154
178
  function withParserOptionScope(context, parser, segment) {
155
179
  const inherited = context[optionScopeKey];
156
- const selections = inherited?.selections ?? /* @__PURE__ */ new Map();
180
+ const selections = inherited?.selections ?? {
181
+ map: /* @__PURE__ */ new Map(),
182
+ owned: true
183
+ };
184
+ const source = bindScope(parserScope(parser), segment == null ? scopePath(context) : [...scopePath(context), segment], selections.map);
185
+ if (inherited?.sources.has(source)) return context;
186
+ const sources = inherited == null ? new Set([source]) : withScopeSource(inherited.sources, source);
157
187
  return {
158
188
  ...context,
159
189
  [optionScopeKey]: {
160
190
  names: inherited?.names ?? /* @__PURE__ */ new Set(),
161
- sources: new Set([...inherited?.sources ?? [], bindScope(parserScope(parser), segment == null ? scopePath(context) : [...scopePath(context), segment], selections)]),
191
+ sources,
162
192
  selections
163
193
  }
164
194
  };
@@ -167,8 +197,11 @@ function withParserOptionScope(context, parser, segment) {
167
197
  function forkOptionScope(context) {
168
198
  const scoped = context[optionScopeKey];
169
199
  if (scoped == null) return context;
170
- const selections = new Map(scoped.selections);
171
- scopeBindings.set(selections, getScopeBindings(scoped.selections));
200
+ scoped.selections.owned = false;
201
+ const selections = {
202
+ map: scoped.selections.map,
203
+ owned: false
204
+ };
172
205
  return {
173
206
  ...context,
174
207
  [optionScopeKey]: {
@@ -181,7 +214,7 @@ function forkOptionScope(context) {
181
214
  function adoptOptionScope(context, next) {
182
215
  const own = context[optionScopeKey];
183
216
  const child = next[optionScopeKey];
184
- if (own != null && child != null) for (const [source, selected] of child.selections) own.selections.set(source, selected);
217
+ if (own != null && child != null && own.selections.map !== child.selections.map) for (const [source, selected] of child.selections.map) setSelection(own.selections, source, selected);
185
218
  return context;
186
219
  }
187
220
  /** Removes names reserved only for a speculative probe. @internal */
@@ -268,7 +301,7 @@ function attachedValuePrefix(context, prefix, optionNames) {
268
301
  };
269
302
  const scoped = context[optionScopeKey];
270
303
  if (scoped == null) require_usage_internals.collectOptionNamesAtCurrentCommandDepth(context.usage, names, false);
271
- else for (const source of scoped.sources) source(scoped.selections, names, context);
304
+ else for (const source of scoped.sources) source(scoped.selections.map, names, context);
272
305
  for (let index = 1; index < prefix.length;) {
273
306
  const character = String.fromCodePoint(prefix.codePointAt(index));
274
307
  const nextIndex = index + character.length;
@@ -305,6 +338,7 @@ exports.combinedOptionScope = combinedOptionScope;
305
338
  exports.conditionalOptionScope = conditionalOptionScope;
306
339
  exports.forkOptionScope = forkOptionScope;
307
340
  exports.fullNameOwnsToken = fullNameOwnsToken;
341
+ exports.getChildOptionScope = getChildOptionScope;
308
342
  exports.inheritOptionScope = inheritOptionScope;
309
343
  exports.prefixSuggestion = prefixSuggestion;
310
344
  exports.repeatedOptionScope = repeatedOptionScope;
@@ -312,5 +346,4 @@ exports.restoreOptionScope = restoreOptionScope;
312
346
  exports.scopeParser = scopeParser;
313
347
  exports.selectableOptionScope = selectableOptionScope;
314
348
  exports.withOptionScope = withOptionScope;
315
- exports.withOptionScopeChild = withOptionScopeChild;
316
349
  exports.withParserOptionScope = withParserOptionScope;
@@ -18,6 +18,16 @@ function getScopeBindings(selections) {
18
18
  }
19
19
  return bindings;
20
20
  }
21
+ function setSelection(selections, source, selected) {
22
+ if (selections.map.get(source) === selected) return;
23
+ if (!selections.owned) {
24
+ const previous = selections.map;
25
+ selections.map = new Map(previous);
26
+ scopeBindings.set(selections.map, getScopeBindings(previous));
27
+ selections.owned = true;
28
+ }
29
+ selections.map.set(source, selected);
30
+ }
21
31
  function bindScope(source, path, selections) {
22
32
  const boundScopes = getScopeBindings(selections);
23
33
  let node = boundScopes.get(source);
@@ -47,12 +57,9 @@ function bindScope(source, path, selections) {
47
57
  function scopePath(context) {
48
58
  return context.exec?.path ?? context[optionPathKey] ?? [];
49
59
  }
50
- /** Tracks child occurrences even for contexts without execution metadata. @internal */
51
- function withOptionScopeChild(context, segment) {
52
- return {
53
- ...context,
54
- [optionPathKey]: [...scopePath(context), segment]
55
- };
60
+ /** Supplies child occurrence metadata without copying the parent. @internal */
61
+ function getChildOptionScope(context, segment, path) {
62
+ return { [optionPathKey]: path ?? [...scopePath(context), segment] };
56
63
  }
57
64
  const primitiveScopes = /* @__PURE__ */ new WeakMap();
58
65
  function parserScope(parser) {
@@ -104,7 +111,7 @@ function selectableOptionScope(initial) {
104
111
  select(context, selected) {
105
112
  const selections = context[optionScopeKey]?.selections;
106
113
  if (selections == null) return;
107
- selections.set(bindScope(source, scopePath(context), selections), selected);
114
+ setSelection(selections, bindScope(source, scopePath(context), selections.map), selected);
108
115
  }
109
116
  };
110
117
  }
@@ -135,7 +142,7 @@ function conditionalOptionScope(discriminator, fallback, candidates = []) {
135
142
  /** Returns the names reachable in this parse scope. @internal */
136
143
  function getOptionScope(context) {
137
144
  const scoped = context[optionScopeKey];
138
- const names = scoped == null ? extractOptionNames(context.usage, true) : new Set([...scoped.names, ...[...scoped.sources].flatMap((source) => [...source(scoped.selections, void 0, context)])]);
145
+ const names = scoped == null ? extractOptionNames(context.usage, true) : new Set([...scoped.names, ...[...scoped.sources].flatMap((source) => [...source(scoped.selections.map, void 0, context)])]);
139
146
  return names;
140
147
  }
141
148
  /** Adds reachable names without consulting the flattened help tree. @internal */
@@ -146,19 +153,42 @@ function withOptionScope(context, names) {
146
153
  [optionScopeKey]: {
147
154
  names: new Set([...inherited?.names ?? [], ...names]),
148
155
  sources: inherited?.sources ?? /* @__PURE__ */ new Set(),
149
- selections: inherited?.selections ?? /* @__PURE__ */ new Map()
156
+ selections: inherited?.selections ?? {
157
+ map: /* @__PURE__ */ new Map(),
158
+ owned: true
159
+ }
150
160
  }
151
161
  };
152
162
  }
163
+ const scopeUnions = /* @__PURE__ */ new WeakMap();
164
+ function withScopeSource(sources, source) {
165
+ let unions = scopeUnions.get(sources);
166
+ if (unions == null) {
167
+ unions = /* @__PURE__ */ new WeakMap();
168
+ scopeUnions.set(sources, unions);
169
+ }
170
+ let combined = unions.get(source);
171
+ if (combined == null) {
172
+ combined = new Set([...sources, source]);
173
+ unions.set(source, combined);
174
+ }
175
+ return combined;
176
+ }
153
177
  /** Adds a parser's owned declarations, retaining modifier metadata. @internal */
154
178
  function withParserOptionScope(context, parser, segment) {
155
179
  const inherited = context[optionScopeKey];
156
- const selections = inherited?.selections ?? /* @__PURE__ */ new Map();
180
+ const selections = inherited?.selections ?? {
181
+ map: /* @__PURE__ */ new Map(),
182
+ owned: true
183
+ };
184
+ const source = bindScope(parserScope(parser), segment == null ? scopePath(context) : [...scopePath(context), segment], selections.map);
185
+ if (inherited?.sources.has(source)) return context;
186
+ const sources = inherited == null ? new Set([source]) : withScopeSource(inherited.sources, source);
157
187
  return {
158
188
  ...context,
159
189
  [optionScopeKey]: {
160
190
  names: inherited?.names ?? /* @__PURE__ */ new Set(),
161
- sources: new Set([...inherited?.sources ?? [], bindScope(parserScope(parser), segment == null ? scopePath(context) : [...scopePath(context), segment], selections)]),
191
+ sources,
162
192
  selections
163
193
  }
164
194
  };
@@ -167,8 +197,11 @@ function withParserOptionScope(context, parser, segment) {
167
197
  function forkOptionScope(context) {
168
198
  const scoped = context[optionScopeKey];
169
199
  if (scoped == null) return context;
170
- const selections = new Map(scoped.selections);
171
- scopeBindings.set(selections, getScopeBindings(scoped.selections));
200
+ scoped.selections.owned = false;
201
+ const selections = {
202
+ map: scoped.selections.map,
203
+ owned: false
204
+ };
172
205
  return {
173
206
  ...context,
174
207
  [optionScopeKey]: {
@@ -181,7 +214,7 @@ function forkOptionScope(context) {
181
214
  function adoptOptionScope(context, next) {
182
215
  const own = context[optionScopeKey];
183
216
  const child = next[optionScopeKey];
184
- if (own != null && child != null) for (const [source, selected] of child.selections) own.selections.set(source, selected);
217
+ if (own != null && child != null && own.selections.map !== child.selections.map) for (const [source, selected] of child.selections.map) setSelection(own.selections, source, selected);
185
218
  return context;
186
219
  }
187
220
  /** Removes names reserved only for a speculative probe. @internal */
@@ -268,7 +301,7 @@ function attachedValuePrefix(context, prefix, optionNames) {
268
301
  };
269
302
  const scoped = context[optionScopeKey];
270
303
  if (scoped == null) collectOptionNamesAtCurrentCommandDepth(context.usage, names, false);
271
- else for (const source of scoped.sources) source(scoped.selections, names, context);
304
+ else for (const source of scoped.sources) source(scoped.selections.map, names, context);
272
305
  for (let index = 1; index < prefix.length;) {
273
306
  const character = String.fromCodePoint(prefix.codePointAt(index));
274
307
  const nextIndex = index + character.length;
@@ -299,4 +332,4 @@ function prefixSuggestion(head, suggestion) {
299
332
  }
300
333
 
301
334
  //#endregion
302
- export { adoptOptionScope, attachedValuePrefix, combinedOptionScope, conditionalOptionScope, forkOptionScope, fullNameOwnsToken, inheritOptionScope, prefixSuggestion, repeatedOptionScope, restoreOptionScope, scopeParser, selectableOptionScope, withOptionScope, withOptionScopeChild, withParserOptionScope };
335
+ export { adoptOptionScope, attachedValuePrefix, combinedOptionScope, conditionalOptionScope, forkOptionScope, fullNameOwnsToken, getChildOptionScope, inheritOptionScope, prefixSuggestion, repeatedOptionScope, restoreOptionScope, scopeParser, selectableOptionScope, withOptionScope, withParserOptionScope };
@@ -415,6 +415,275 @@ function preserveNormalizedDerivedMetadata(wrapped, parser, normalize) {
415
415
  });
416
416
  }
417
417
  }
418
+ function withSuggestions(parser, options) {
419
+ if (!isValueParser(parser)) throw new TypeError("Expected parser to be a value parser.");
420
+ if (options == null || typeof options !== "object") throw new TypeError("Expected options to be an object.");
421
+ if (typeof options.suggest !== "function") throw new TypeError("Expected suggest to be a function.");
422
+ checkEnumOption(options, "mode", ["sync", "async"]);
423
+ checkBooleanOption(options, "includeExisting");
424
+ checkBooleanOption(options, "filterByPrefix");
425
+ if (require_internal_dependency.isDependencySource(parser)) throw new TypeError("Cannot attach suggestions to a dependency source directly.");
426
+ const inner = parser;
427
+ const providerMode = options.mode ?? "sync";
428
+ const resultMode = providerMode === "async" ? "async" : inner.mode;
429
+ const lift = resultMode !== inner.mode;
430
+ const provider = options.suggest;
431
+ const includeExisting = options.includeExisting ?? false;
432
+ const filterByPrefix = options.filterByPrefix ?? true;
433
+ const existingSuggest = includeExisting ? inner.suggest?.bind(inner) : void 0;
434
+ const normalizeValue = inner.normalize?.bind(inner);
435
+ const validate = inner.validate?.bind(inner);
436
+ const getProviderSource = (prefix) => () => {
437
+ const source = provider(prefix);
438
+ return providerMode === "sync" ? checkSyncProviderResult(source) : source;
439
+ };
440
+ const suggestFrom = (prefix, existing) => require_mode_dispatch.dispatchIterableByMode(resultMode, () => suggestFromSources(createSuggestionProcessors(prefix, filterByPrefix), existing, getProviderSource(prefix)), () => suggestFromSourcesAsync(createSuggestionProcessors(prefix, filterByPrefix), existing, getProviderSource(prefix)));
441
+ const wrapped = {
442
+ mode: resultMode,
443
+ metavar: inner.metavar,
444
+ get placeholder() {
445
+ return inner.placeholder;
446
+ },
447
+ ...inner.choices == null ? {} : { choices: inner.choices },
448
+ parse(input) {
449
+ return lift ? liftToAsync(() => inner.parse(input)) : inner.parse(input);
450
+ },
451
+ format(value) {
452
+ return inner.format(value);
453
+ },
454
+ ...normalizeValue == null ? {} : { normalize(value) {
455
+ return normalizeValue(value);
456
+ } },
457
+ ...validate == null ? {} : { validate(value) {
458
+ return validate(value);
459
+ } },
460
+ suggest(prefix) {
461
+ return suggestFrom(prefix, existingSuggest == null ? void 0 : () => existingSuggest(prefix));
462
+ }
463
+ };
464
+ const docValue = require_internal_doc_metadata.getDocValue(inner);
465
+ if (docValue != null) require_internal_doc_metadata.captureDocValue(wrapped, docValue);
466
+ if (require_internal_dependency.isDerivedValueParser(inner)) preserveSuggestedDerivedMetadata(wrapped, inner, lift, includeExisting, suggestFrom);
467
+ return wrapped;
468
+ }
469
+ function preserveSuggestedDerivedMetadata(wrapped, parser, lift, includeExisting, suggestFrom) {
470
+ Object.defineProperties(wrapped, {
471
+ [require_internal_dependency.derivedValueParserMarker]: {
472
+ value: true,
473
+ enumerable: true
474
+ },
475
+ [require_internal_dependency.dependencyId]: {
476
+ value: parser[require_internal_dependency.dependencyId],
477
+ enumerable: true
478
+ },
479
+ [require_internal_dependency.parseWithDependency]: {
480
+ value(input, dependencyValue) {
481
+ return lift ? liftToAsync(() => parser[require_internal_dependency.parseWithDependency](input, dependencyValue)) : parser[require_internal_dependency.parseWithDependency](input, dependencyValue);
482
+ },
483
+ enumerable: true
484
+ }
485
+ });
486
+ if (require_internal_dependency.dependencyIds in parser && parser[require_internal_dependency.dependencyIds] != null) Object.defineProperty(wrapped, require_internal_dependency.dependencyIds, {
487
+ value: parser[require_internal_dependency.dependencyIds],
488
+ enumerable: true
489
+ });
490
+ if (require_internal_dependency.defaultValues in parser && parser[require_internal_dependency.defaultValues] != null) Object.defineProperty(wrapped, require_internal_dependency.defaultValues, {
491
+ value: parser[require_internal_dependency.defaultValues],
492
+ enumerable: true
493
+ });
494
+ if (require_internal_dependency.singleDefaultValue in parser && parser[require_internal_dependency.singleDefaultValue] != null) Object.defineProperty(wrapped, require_internal_dependency.singleDefaultValue, {
495
+ value: parser[require_internal_dependency.singleDefaultValue],
496
+ enumerable: true
497
+ });
498
+ if (includeExisting && require_internal_dependency.suggestWithDependency in parser && parser[require_internal_dependency.suggestWithDependency] != null) {
499
+ const existing = parser[require_internal_dependency.suggestWithDependency].bind(parser);
500
+ Object.defineProperty(wrapped, require_internal_dependency.suggestWithDependency, {
501
+ value(prefix, dependencyValue) {
502
+ return suggestFrom(prefix, () => existing(prefix, dependencyValue));
503
+ },
504
+ enumerable: true
505
+ });
506
+ }
507
+ }
508
+ /**
509
+ * Lifts a synchronous computation to async mode. It runs inside an async
510
+ * function, so a synchronous throw becomes a rejected promise, and the
511
+ * resolved result object is passed through unchanged.
512
+ */
513
+ function liftToAsync(compute) {
514
+ return require_mode_dispatch.dispatchByMode("async", () => require_mode_dispatch.wrapForMode("sync", compute()), async () => await compute());
515
+ }
516
+ function isThenable(value) {
517
+ return isObjectLike(value) && "then" in value && typeof value.then === "function";
518
+ }
519
+ /**
520
+ * Rejects results a synchronous provider must not return, even when the
521
+ * resulting parser is async. A thenable gets a rejection handler first so
522
+ * that an invalid async callback cannot cause an unhandled rejection.
523
+ * @throws {TypeError} If the result is not a synchronous iterable.
524
+ */
525
+ function checkSyncProviderResult(source) {
526
+ if (isThenable(source)) {
527
+ Promise.resolve(source).then(void 0, () => {});
528
+ throw new TypeError("A sync suggestion provider returned a promise.");
529
+ }
530
+ return { [Symbol.iterator]: getSyncIteratorMethod(source) };
531
+ }
532
+ function isObjectLike(value) {
533
+ return value != null && (typeof value === "object" || typeof value === "function");
534
+ }
535
+ /**
536
+ * @throws {TypeError} If the source is not a non-string iterable.
537
+ */
538
+ function getSyncIteratorMethod(source) {
539
+ if (!isObjectLike(source) || !(Symbol.iterator in source) || typeof source[Symbol.iterator] !== "function") throw new TypeError("Expected an iterable of suggestions.");
540
+ const iterable = source;
541
+ return () => iterable[Symbol.iterator]();
542
+ }
543
+ /**
544
+ * @throws {TypeError} If the source is not a non-string iterable.
545
+ */
546
+ function getSyncIterator(source) {
547
+ return getSyncIteratorMethod(source)();
548
+ }
549
+ /**
550
+ * @throws {TypeError} If the source is neither an async iterable nor a
551
+ * non-string iterable.
552
+ */
553
+ function getAnyIterator(source) {
554
+ if (isObjectLike(source) && Symbol.asyncIterator in source && typeof source[Symbol.asyncIterator] === "function") {
555
+ const iterable = source;
556
+ return iterable[Symbol.asyncIterator]();
557
+ }
558
+ return getSyncIterator(source);
559
+ }
560
+ const fileSuggestionTypes = new Set([
561
+ "file",
562
+ "directory",
563
+ "any"
564
+ ]);
565
+ function isOptionalOfType(item, key, check) {
566
+ const value = Reflect.get(item, key);
567
+ return value === void 0 || check(value);
568
+ }
569
+ function isString(value) {
570
+ return typeof value === "string";
571
+ }
572
+ function isStringArray(value) {
573
+ return Array.isArray(value) && value.every(isString);
574
+ }
575
+ /**
576
+ * Screens a provider item for the `Suggestion` fields that completion encoding
577
+ * relies on: required fields and the types of optional ones. This is shape
578
+ * checking, not full validation; message terms inside a description are
579
+ * trusted.
580
+ */
581
+ function screenSuggestionInput(item) {
582
+ if (typeof item === "string") return {
583
+ kind: "literal",
584
+ text: item
585
+ };
586
+ if (isThenable(item)) {
587
+ Promise.resolve(item).then(void 0, () => {});
588
+ return void 0;
589
+ }
590
+ if (item == null || typeof item !== "object" || !("kind" in item)) return void 0;
591
+ if (!isOptionalOfType(item, "description", Array.isArray)) return void 0;
592
+ if (item.kind === "literal") return "text" in item && typeof item.text === "string" ? item : void 0;
593
+ if (item.kind === "file") return "type" in item && fileSuggestionTypes.has(item.type) && isOptionalOfType(item, "pattern", isString) && isOptionalOfType(item, "prefix", isString) && isOptionalOfType(item, "extensions", isStringArray) && isOptionalOfType(item, "includeHidden", (value) => typeof value === "boolean") ? item : void 0;
594
+ return void 0;
595
+ }
596
+ function createSuggestionProcessors(prefix, filterByPrefix) {
597
+ const seen = /* @__PURE__ */ new Set();
598
+ const dedupe = (suggestion) => {
599
+ if (suggestion.kind !== "literal") return suggestion;
600
+ if (seen.has(suggestion.text)) return void 0;
601
+ seen.add(suggestion.text);
602
+ return suggestion;
603
+ };
604
+ return {
605
+ existing: (item) => dedupe(item),
606
+ provider(item) {
607
+ const suggestion = screenSuggestionInput(item);
608
+ if (suggestion == null || filterByPrefix && suggestion.kind === "literal" && !suggestion.text.startsWith(prefix)) return void 0;
609
+ return dedupe(suggestion);
610
+ }
611
+ };
612
+ }
613
+ function* suggestFromSources(processors, existing, provider) {
614
+ if (existing != null) yield* guardedSuggestions(existing, processors.existing);
615
+ yield* guardedSuggestions(provider, processors.provider);
616
+ }
617
+ async function* suggestFromSourcesAsync(processors, existing, provider) {
618
+ if (existing != null) yield* guardedSuggestionsAsync(existing, processors.existing);
619
+ yield* guardedSuggestionsAsync(provider, processors.provider);
620
+ }
621
+ /**
622
+ * Yields processed items from one source, ending quietly on any failure.
623
+ * Yields happen outside the guarded regions, so consumer cancellation is never
624
+ * mistaken for a source failure, and the source iterator is closed when the
625
+ * consumer stops early.
626
+ */
627
+ function* guardedSuggestions(getSource, process) {
628
+ let iterator;
629
+ try {
630
+ iterator = getSyncIterator(getSource());
631
+ } catch {
632
+ return;
633
+ }
634
+ let finished = false;
635
+ try {
636
+ while (true) {
637
+ let suggestion;
638
+ try {
639
+ const step = iterator.next();
640
+ if (!isObjectLike(step)) return;
641
+ if (step.done) {
642
+ finished = true;
643
+ return;
644
+ }
645
+ suggestion = process(step.value);
646
+ } catch {
647
+ return;
648
+ }
649
+ if (suggestion != null) yield suggestion;
650
+ }
651
+ } finally {
652
+ if (!finished) try {
653
+ iterator.return?.();
654
+ } catch {}
655
+ }
656
+ }
657
+ async function* guardedSuggestionsAsync(getSource, process) {
658
+ let iterator;
659
+ try {
660
+ iterator = getAnyIterator(await getSource());
661
+ } catch {
662
+ return;
663
+ }
664
+ let finished = false;
665
+ try {
666
+ while (true) {
667
+ let suggestion;
668
+ try {
669
+ const step = await iterator.next();
670
+ if (!isObjectLike(step)) return;
671
+ if (step.done) {
672
+ finished = true;
673
+ return;
674
+ }
675
+ suggestion = process(step.value);
676
+ } catch {
677
+ return;
678
+ }
679
+ if (suggestion != null) yield suggestion;
680
+ }
681
+ } finally {
682
+ if (!finished) try {
683
+ await iterator.return?.();
684
+ } catch {}
685
+ }
686
+ }
418
687
  /**
419
688
  * Creates a value parser that transforms the result of another value parser.
420
689
  *
@@ -7026,4 +7295,5 @@ exports.socketAddress = socketAddress;
7026
7295
  exports.string = string;
7027
7296
  exports.transform = transform;
7028
7297
  exports.url = url;
7029
- exports.uuid = uuid;
7298
+ exports.uuid = uuid;
7299
+ exports.withSuggestions = withSuggestions;
@@ -459,6 +459,139 @@ declare function biject<const T extends object>(mapping: T): ValueParser<"sync",
459
459
  * @since 1.4.0
460
460
  */
461
461
  declare function normalizeInput<M extends Mode, T>(parser: ValueParser<M, T>, normalize: (input: string) => string): ValueParser<M, T>;
462
+ /**
463
+ * A completion candidate produced by a {@link withSuggestions} provider.
464
+ *
465
+ * A string is shorthand for a literal suggestion with that text.
466
+ *
467
+ * @since 1.4.0
468
+ */
469
+ type SuggestionInput = string | Suggestion;
470
+ /**
471
+ * Options shared by every {@link withSuggestions} provider mode.
472
+ *
473
+ * @since 1.4.0
474
+ */
475
+ interface WithSuggestionsOptionsBase {
476
+ /**
477
+ * Whether to keep the wrapped parser's own suggestions. When `true`, they
478
+ * are produced first, followed by the provider's candidates. Literal
479
+ * candidates whose text was already produced are skipped.
480
+ *
481
+ * @default `false`
482
+ */
483
+ readonly includeExisting?: boolean;
484
+ /**
485
+ * Whether to drop provider literals whose text does not start with the
486
+ * completion prefix. Turn this off when the provider already matches in
487
+ * its own way, such as case-insensitively or by completing `42` to `#42`,
488
+ * and returns spellings that differ from the typed prefix. Suggestions
489
+ * kept by `includeExisting` are never filtered by this wrapper.
490
+ *
491
+ * @default `true`
492
+ */
493
+ readonly filterByPrefix?: boolean;
494
+ }
495
+ /**
496
+ * Options for {@link withSuggestions} with a synchronous provider.
497
+ *
498
+ * @since 1.4.0
499
+ */
500
+ interface WithSuggestionsSyncOptions extends WithSuggestionsOptionsBase {
501
+ /**
502
+ * The provider mode. Synchronous providers keep the wrapped parser's mode.
503
+ *
504
+ * @default `"sync"`
505
+ */
506
+ readonly mode?: "sync";
507
+ /**
508
+ * Returns completion candidates for the given prefix. This is a shell
509
+ * completion provider, unrelated to the typo hints configured by
510
+ * `choice()`'s `suggest` option. It is called only during completion,
511
+ * never while parsing. Failures are swallowed without logging, so log
512
+ * inside the provider if you need diagnostics.
513
+ *
514
+ * Return a collection of candidates, not a bare string: a string result
515
+ * yields no suggestions rather than one suggestion per character.
516
+ *
517
+ * @param prefix The current input prefix to complete.
518
+ * @returns The completion candidates.
519
+ */
520
+ readonly suggest: (prefix: string) => Iterable<SuggestionInput>;
521
+ }
522
+ /**
523
+ * Options for {@link withSuggestions} with an asynchronous provider.
524
+ *
525
+ * @since 1.4.0
526
+ */
527
+ interface WithSuggestionsAsyncOptions extends WithSuggestionsOptionsBase {
528
+ /**
529
+ * The provider mode. Asynchronous providers make the resulting value
530
+ * parser asynchronous.
531
+ */
532
+ readonly mode: "async";
533
+ /**
534
+ * Returns completion candidates for the given prefix. It may be an async
535
+ * generator, or return an iterable, an async iterable, or a promise of
536
+ * either. This is a shell completion provider, unrelated to the typo hints
537
+ * configured by `choice()`'s `suggest` option. It is called only during
538
+ * completion, never while parsing. Failures are swallowed without logging,
539
+ * so log inside the provider if you need diagnostics. A bare string result
540
+ * yields no suggestions.
541
+ *
542
+ * @param prefix The current input prefix to complete.
543
+ * @returns The completion candidates.
544
+ */
545
+ readonly suggest: (prefix: string) => Iterable<SuggestionInput> | AsyncIterable<SuggestionInput> | Promise<Iterable<SuggestionInput> | AsyncIterable<SuggestionInput>>;
546
+ }
547
+ /**
548
+ * Options for {@link withSuggestions}.
549
+ *
550
+ * @since 1.4.0
551
+ */
552
+ type WithSuggestionsOptions = WithSuggestionsSyncOptions | WithSuggestionsAsyncOptions;
553
+ /**
554
+ * Attaches completion candidates to an existing value parser.
555
+ *
556
+ * The returned parser accepts the same input and produces the same values as
557
+ * `parser`: parsing, formatting, `normalize()`, `validate()`, `choices`,
558
+ * the placeholder, and dependency-derived behavior are all delegated to it.
559
+ * Only `suggest()` changes. By default the provider's candidates replace the
560
+ * wrapped parser's own suggestions; set `includeExisting` to keep them.
561
+ *
562
+ * The provider receives the raw completion prefix, so it can filter on the
563
+ * server side, and literal candidates that do not start with the prefix are
564
+ * dropped afterwards unless `filterByPrefix` is `false`. Completion is best-effort: if the provider throws,
565
+ * rejects, or fails during iteration, candidates produced so far are kept and
566
+ * the rest are skipped. Malformed candidates are skipped.
567
+ *
568
+ * A synchronous provider keeps the wrapped parser's mode. An asynchronous
569
+ * provider (`mode: "async"`) makes the result asynchronous, so `parse()`
570
+ * returns a promise even when the wrapped parser is synchronous.
571
+ *
572
+ * @example
573
+ * ```typescript
574
+ * const issue = withSuggestions(integer({ min: 1 }), {
575
+ * mode: "async",
576
+ * async *suggest(prefix) {
577
+ * yield* await listIssueNumbers(prefix);
578
+ * },
579
+ * });
580
+ * ```
581
+ *
582
+ * @template M The mode of the wrapped parser.
583
+ * @template T The value type produced by the wrapped parser.
584
+ * @param parser The value parser to attach suggestions to.
585
+ * @param options The completion provider and its options.
586
+ * @returns A value parser that accepts the same input as `parser` and uses the
587
+ * provider for completion.
588
+ * @throws {TypeError} If `parser` is not a value parser or is a dependency
589
+ * source, or if `options` is invalid.
590
+ * @since 1.4.0
591
+ */
592
+ declare function withSuggestions<M extends Mode, T>(parser: ValueParser<M, T>, options: WithSuggestionsSyncOptions): ValueParser<M, T>;
593
+ declare function withSuggestions<M extends Mode, T>(parser: ValueParser<M, T>, options: WithSuggestionsAsyncOptions): ValueParser<"async", T>;
594
+ declare function withSuggestions<M extends Mode, T>(parser: ValueParser<M, T>, options: WithSuggestionsOptions): ValueParser<M | "async", T>;
462
595
  /**
463
596
  * Creates a value parser that transforms the result of another value parser.
464
597
  *
@@ -3457,4 +3590,4 @@ declare function firstOf<const TParsers extends readonly [ValueParser<"sync", un
3457
3590
  */
3458
3591
  declare function firstOf<const TParsers extends readonly ValueParser<"sync", unknown>[]>(parsers: TParsers, options?: FirstOfOptions): ValueParser<"sync", ValueParserValue<TParsers[number]>>;
3459
3592
  //#endregion
3460
- export { ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, CidrOptions, CidrValue, Color, ColorFormat, ColorOptions, CronExpression, CronExpressionForOptions, CronOptions, DeferredMap, DomainOptions, EmailOptions, FileSizeOptions, FileSizeOptionsBigInt, FileSizeOptionsNumber, FileSizeUnit, FirstOfOptions, FloatOptions, HostnameOptions, IntegerOptionsBigInt, IntegerOptionsNumber, IpOptions, Ipv4Options, Ipv6Options, Json, JsonOptions, KeyValueOptions, LocaleOptions, MacAddressOptions, type Mode, type ModeIterable, type ModeValue, type NonEmptyString, OriginOptions, PortOptionsBigInt, PortOptionsNumber, PortRangeOptionsBigInt, PortRangeOptionsNumber, PortRangeValueBigInt, PortRangeValueNumber, RegExpOptions, SemVer, SemVerOptionsObject, SemVerOptionsString, SemVerString, SocketAddressOptions, SocketAddressValue, StringOptions, TransformMapping, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, biject, checkBooleanOption, checkEnumOption, choice, cidr, color, cron, domain, email, ensureNonEmptyString, fileSize, firstOf, float, hostname, integer, ip, ipv4, ipv6, isNonEmptyString, isValueParser, json, keyValue, locale, macAddress, normalizeInput, origin, port, portRange, regExp, semVer, socketAddress, string, transform, url, uuid };
3593
+ export { ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, CidrOptions, CidrValue, Color, ColorFormat, ColorOptions, CronExpression, CronExpressionForOptions, CronOptions, DeferredMap, DomainOptions, EmailOptions, FileSizeOptions, FileSizeOptionsBigInt, FileSizeOptionsNumber, FileSizeUnit, FirstOfOptions, FloatOptions, HostnameOptions, IntegerOptionsBigInt, IntegerOptionsNumber, IpOptions, Ipv4Options, Ipv6Options, Json, JsonOptions, KeyValueOptions, LocaleOptions, MacAddressOptions, type Mode, type ModeIterable, type ModeValue, type NonEmptyString, OriginOptions, PortOptionsBigInt, PortOptionsNumber, PortRangeOptionsBigInt, PortRangeOptionsNumber, PortRangeValueBigInt, PortRangeValueNumber, RegExpOptions, SemVer, SemVerOptionsObject, SemVerOptionsString, SemVerString, SocketAddressOptions, SocketAddressValue, StringOptions, SuggestionInput, TransformMapping, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithSuggestionsAsyncOptions, WithSuggestionsOptions, WithSuggestionsOptionsBase, WithSuggestionsSyncOptions, biject, checkBooleanOption, checkEnumOption, choice, cidr, color, cron, domain, email, ensureNonEmptyString, fileSize, firstOf, float, hostname, integer, ip, ipv4, ipv6, isNonEmptyString, isValueParser, json, keyValue, locale, macAddress, normalizeInput, origin, port, portRange, regExp, semVer, socketAddress, string, transform, url, uuid, withSuggestions };
@@ -459,6 +459,139 @@ declare function biject<const T extends object>(mapping: T): ValueParser<"sync",
459
459
  * @since 1.4.0
460
460
  */
461
461
  declare function normalizeInput<M extends Mode, T>(parser: ValueParser<M, T>, normalize: (input: string) => string): ValueParser<M, T>;
462
+ /**
463
+ * A completion candidate produced by a {@link withSuggestions} provider.
464
+ *
465
+ * A string is shorthand for a literal suggestion with that text.
466
+ *
467
+ * @since 1.4.0
468
+ */
469
+ type SuggestionInput = string | Suggestion;
470
+ /**
471
+ * Options shared by every {@link withSuggestions} provider mode.
472
+ *
473
+ * @since 1.4.0
474
+ */
475
+ interface WithSuggestionsOptionsBase {
476
+ /**
477
+ * Whether to keep the wrapped parser's own suggestions. When `true`, they
478
+ * are produced first, followed by the provider's candidates. Literal
479
+ * candidates whose text was already produced are skipped.
480
+ *
481
+ * @default `false`
482
+ */
483
+ readonly includeExisting?: boolean;
484
+ /**
485
+ * Whether to drop provider literals whose text does not start with the
486
+ * completion prefix. Turn this off when the provider already matches in
487
+ * its own way, such as case-insensitively or by completing `42` to `#42`,
488
+ * and returns spellings that differ from the typed prefix. Suggestions
489
+ * kept by `includeExisting` are never filtered by this wrapper.
490
+ *
491
+ * @default `true`
492
+ */
493
+ readonly filterByPrefix?: boolean;
494
+ }
495
+ /**
496
+ * Options for {@link withSuggestions} with a synchronous provider.
497
+ *
498
+ * @since 1.4.0
499
+ */
500
+ interface WithSuggestionsSyncOptions extends WithSuggestionsOptionsBase {
501
+ /**
502
+ * The provider mode. Synchronous providers keep the wrapped parser's mode.
503
+ *
504
+ * @default `"sync"`
505
+ */
506
+ readonly mode?: "sync";
507
+ /**
508
+ * Returns completion candidates for the given prefix. This is a shell
509
+ * completion provider, unrelated to the typo hints configured by
510
+ * `choice()`'s `suggest` option. It is called only during completion,
511
+ * never while parsing. Failures are swallowed without logging, so log
512
+ * inside the provider if you need diagnostics.
513
+ *
514
+ * Return a collection of candidates, not a bare string: a string result
515
+ * yields no suggestions rather than one suggestion per character.
516
+ *
517
+ * @param prefix The current input prefix to complete.
518
+ * @returns The completion candidates.
519
+ */
520
+ readonly suggest: (prefix: string) => Iterable<SuggestionInput>;
521
+ }
522
+ /**
523
+ * Options for {@link withSuggestions} with an asynchronous provider.
524
+ *
525
+ * @since 1.4.0
526
+ */
527
+ interface WithSuggestionsAsyncOptions extends WithSuggestionsOptionsBase {
528
+ /**
529
+ * The provider mode. Asynchronous providers make the resulting value
530
+ * parser asynchronous.
531
+ */
532
+ readonly mode: "async";
533
+ /**
534
+ * Returns completion candidates for the given prefix. It may be an async
535
+ * generator, or return an iterable, an async iterable, or a promise of
536
+ * either. This is a shell completion provider, unrelated to the typo hints
537
+ * configured by `choice()`'s `suggest` option. It is called only during
538
+ * completion, never while parsing. Failures are swallowed without logging,
539
+ * so log inside the provider if you need diagnostics. A bare string result
540
+ * yields no suggestions.
541
+ *
542
+ * @param prefix The current input prefix to complete.
543
+ * @returns The completion candidates.
544
+ */
545
+ readonly suggest: (prefix: string) => Iterable<SuggestionInput> | AsyncIterable<SuggestionInput> | Promise<Iterable<SuggestionInput> | AsyncIterable<SuggestionInput>>;
546
+ }
547
+ /**
548
+ * Options for {@link withSuggestions}.
549
+ *
550
+ * @since 1.4.0
551
+ */
552
+ type WithSuggestionsOptions = WithSuggestionsSyncOptions | WithSuggestionsAsyncOptions;
553
+ /**
554
+ * Attaches completion candidates to an existing value parser.
555
+ *
556
+ * The returned parser accepts the same input and produces the same values as
557
+ * `parser`: parsing, formatting, `normalize()`, `validate()`, `choices`,
558
+ * the placeholder, and dependency-derived behavior are all delegated to it.
559
+ * Only `suggest()` changes. By default the provider's candidates replace the
560
+ * wrapped parser's own suggestions; set `includeExisting` to keep them.
561
+ *
562
+ * The provider receives the raw completion prefix, so it can filter on the
563
+ * server side, and literal candidates that do not start with the prefix are
564
+ * dropped afterwards unless `filterByPrefix` is `false`. Completion is best-effort: if the provider throws,
565
+ * rejects, or fails during iteration, candidates produced so far are kept and
566
+ * the rest are skipped. Malformed candidates are skipped.
567
+ *
568
+ * A synchronous provider keeps the wrapped parser's mode. An asynchronous
569
+ * provider (`mode: "async"`) makes the result asynchronous, so `parse()`
570
+ * returns a promise even when the wrapped parser is synchronous.
571
+ *
572
+ * @example
573
+ * ```typescript
574
+ * const issue = withSuggestions(integer({ min: 1 }), {
575
+ * mode: "async",
576
+ * async *suggest(prefix) {
577
+ * yield* await listIssueNumbers(prefix);
578
+ * },
579
+ * });
580
+ * ```
581
+ *
582
+ * @template M The mode of the wrapped parser.
583
+ * @template T The value type produced by the wrapped parser.
584
+ * @param parser The value parser to attach suggestions to.
585
+ * @param options The completion provider and its options.
586
+ * @returns A value parser that accepts the same input as `parser` and uses the
587
+ * provider for completion.
588
+ * @throws {TypeError} If `parser` is not a value parser or is a dependency
589
+ * source, or if `options` is invalid.
590
+ * @since 1.4.0
591
+ */
592
+ declare function withSuggestions<M extends Mode, T>(parser: ValueParser<M, T>, options: WithSuggestionsSyncOptions): ValueParser<M, T>;
593
+ declare function withSuggestions<M extends Mode, T>(parser: ValueParser<M, T>, options: WithSuggestionsAsyncOptions): ValueParser<"async", T>;
594
+ declare function withSuggestions<M extends Mode, T>(parser: ValueParser<M, T>, options: WithSuggestionsOptions): ValueParser<M | "async", T>;
462
595
  /**
463
596
  * Creates a value parser that transforms the result of another value parser.
464
597
  *
@@ -3457,4 +3590,4 @@ declare function firstOf<const TParsers extends readonly [ValueParser<"sync", un
3457
3590
  */
3458
3591
  declare function firstOf<const TParsers extends readonly ValueParser<"sync", unknown>[]>(parsers: TParsers, options?: FirstOfOptions): ValueParser<"sync", ValueParserValue<TParsers[number]>>;
3459
3592
  //#endregion
3460
- export { ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, CidrOptions, CidrValue, Color, ColorFormat, ColorOptions, CronExpression, CronExpressionForOptions, CronOptions, DeferredMap, DomainOptions, EmailOptions, FileSizeOptions, FileSizeOptionsBigInt, FileSizeOptionsNumber, FileSizeUnit, FirstOfOptions, FloatOptions, HostnameOptions, IntegerOptionsBigInt, IntegerOptionsNumber, IpOptions, Ipv4Options, Ipv6Options, Json, JsonOptions, KeyValueOptions, LocaleOptions, MacAddressOptions, type Mode, type ModeIterable, type ModeValue, type NonEmptyString, OriginOptions, PortOptionsBigInt, PortOptionsNumber, PortRangeOptionsBigInt, PortRangeOptionsNumber, PortRangeValueBigInt, PortRangeValueNumber, RegExpOptions, SemVer, SemVerOptionsObject, SemVerOptionsString, SemVerString, SocketAddressOptions, SocketAddressValue, StringOptions, TransformMapping, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, biject, checkBooleanOption, checkEnumOption, choice, cidr, color, cron, domain, email, ensureNonEmptyString, fileSize, firstOf, float, hostname, integer, ip, ipv4, ipv6, isNonEmptyString, isValueParser, json, keyValue, locale, macAddress, normalizeInput, origin, port, portRange, regExp, semVer, socketAddress, string, transform, url, uuid };
3593
+ export { ChoiceOptions, ChoiceOptionsBase, ChoiceOptionsNumber, ChoiceOptionsString, CidrOptions, CidrValue, Color, ColorFormat, ColorOptions, CronExpression, CronExpressionForOptions, CronOptions, DeferredMap, DomainOptions, EmailOptions, FileSizeOptions, FileSizeOptionsBigInt, FileSizeOptionsNumber, FileSizeUnit, FirstOfOptions, FloatOptions, HostnameOptions, IntegerOptionsBigInt, IntegerOptionsNumber, IpOptions, Ipv4Options, Ipv6Options, Json, JsonOptions, KeyValueOptions, LocaleOptions, MacAddressOptions, type Mode, type ModeIterable, type ModeValue, type NonEmptyString, OriginOptions, PortOptionsBigInt, PortOptionsNumber, PortRangeOptionsBigInt, PortRangeOptionsNumber, PortRangeValueBigInt, PortRangeValueNumber, RegExpOptions, SemVer, SemVerOptionsObject, SemVerOptionsString, SemVerString, SocketAddressOptions, SocketAddressValue, StringOptions, SuggestionInput, TransformMapping, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithSuggestionsAsyncOptions, WithSuggestionsOptions, WithSuggestionsOptionsBase, WithSuggestionsSyncOptions, biject, checkBooleanOption, checkEnumOption, choice, cidr, color, cron, domain, email, ensureNonEmptyString, fileSize, firstOf, float, hostname, integer, ip, ipv4, ipv6, isNonEmptyString, isValueParser, json, keyValue, locale, macAddress, normalizeInput, origin, port, portRange, regExp, semVer, socketAddress, string, transform, url, uuid, withSuggestions };
@@ -1,7 +1,7 @@
1
1
  import { cloneMessage, lineBreak, message, metavar, text, valueSet } from "./message.js";
2
- import { mapMaybePromiseByMode, wrapForMode, wrapIterableForMode } from "./internal/mode-dispatch.js";
2
+ import { dispatchByMode, dispatchIterableByMode, mapMaybePromiseByMode, wrapForMode, wrapIterableForMode } from "./internal/mode-dispatch.js";
3
3
  import { defaultValues, dependencyId, dependencyIds, derivedValueParserMarker, getSnapshottedDefaultDependencyValues, isDependencySource, isDerivedValueParser, parseWithDependency, singleDefaultValue, snapshotDefaultDependencyValues, suggestWithDependency } from "./internal/dependency.js";
4
- import { captureDocValue } from "./internal/doc-metadata.js";
4
+ import { captureDocValue, getDocValue } from "./internal/doc-metadata.js";
5
5
  import { appendValueHint, appendValueSuggestions, deduplicateSuggestions } from "./suggestion.js";
6
6
  import { ensureNonEmptyString, isNonEmptyString } from "./nonempty.js";
7
7
 
@@ -415,6 +415,275 @@ function preserveNormalizedDerivedMetadata(wrapped, parser, normalize) {
415
415
  });
416
416
  }
417
417
  }
418
+ function withSuggestions(parser, options) {
419
+ if (!isValueParser(parser)) throw new TypeError("Expected parser to be a value parser.");
420
+ if (options == null || typeof options !== "object") throw new TypeError("Expected options to be an object.");
421
+ if (typeof options.suggest !== "function") throw new TypeError("Expected suggest to be a function.");
422
+ checkEnumOption(options, "mode", ["sync", "async"]);
423
+ checkBooleanOption(options, "includeExisting");
424
+ checkBooleanOption(options, "filterByPrefix");
425
+ if (isDependencySource(parser)) throw new TypeError("Cannot attach suggestions to a dependency source directly.");
426
+ const inner = parser;
427
+ const providerMode = options.mode ?? "sync";
428
+ const resultMode = providerMode === "async" ? "async" : inner.mode;
429
+ const lift = resultMode !== inner.mode;
430
+ const provider = options.suggest;
431
+ const includeExisting = options.includeExisting ?? false;
432
+ const filterByPrefix = options.filterByPrefix ?? true;
433
+ const existingSuggest = includeExisting ? inner.suggest?.bind(inner) : void 0;
434
+ const normalizeValue = inner.normalize?.bind(inner);
435
+ const validate = inner.validate?.bind(inner);
436
+ const getProviderSource = (prefix) => () => {
437
+ const source = provider(prefix);
438
+ return providerMode === "sync" ? checkSyncProviderResult(source) : source;
439
+ };
440
+ const suggestFrom = (prefix, existing) => dispatchIterableByMode(resultMode, () => suggestFromSources(createSuggestionProcessors(prefix, filterByPrefix), existing, getProviderSource(prefix)), () => suggestFromSourcesAsync(createSuggestionProcessors(prefix, filterByPrefix), existing, getProviderSource(prefix)));
441
+ const wrapped = {
442
+ mode: resultMode,
443
+ metavar: inner.metavar,
444
+ get placeholder() {
445
+ return inner.placeholder;
446
+ },
447
+ ...inner.choices == null ? {} : { choices: inner.choices },
448
+ parse(input) {
449
+ return lift ? liftToAsync(() => inner.parse(input)) : inner.parse(input);
450
+ },
451
+ format(value) {
452
+ return inner.format(value);
453
+ },
454
+ ...normalizeValue == null ? {} : { normalize(value) {
455
+ return normalizeValue(value);
456
+ } },
457
+ ...validate == null ? {} : { validate(value) {
458
+ return validate(value);
459
+ } },
460
+ suggest(prefix) {
461
+ return suggestFrom(prefix, existingSuggest == null ? void 0 : () => existingSuggest(prefix));
462
+ }
463
+ };
464
+ const docValue = getDocValue(inner);
465
+ if (docValue != null) captureDocValue(wrapped, docValue);
466
+ if (isDerivedValueParser(inner)) preserveSuggestedDerivedMetadata(wrapped, inner, lift, includeExisting, suggestFrom);
467
+ return wrapped;
468
+ }
469
+ function preserveSuggestedDerivedMetadata(wrapped, parser, lift, includeExisting, suggestFrom) {
470
+ Object.defineProperties(wrapped, {
471
+ [derivedValueParserMarker]: {
472
+ value: true,
473
+ enumerable: true
474
+ },
475
+ [dependencyId]: {
476
+ value: parser[dependencyId],
477
+ enumerable: true
478
+ },
479
+ [parseWithDependency]: {
480
+ value(input, dependencyValue) {
481
+ return lift ? liftToAsync(() => parser[parseWithDependency](input, dependencyValue)) : parser[parseWithDependency](input, dependencyValue);
482
+ },
483
+ enumerable: true
484
+ }
485
+ });
486
+ if (dependencyIds in parser && parser[dependencyIds] != null) Object.defineProperty(wrapped, dependencyIds, {
487
+ value: parser[dependencyIds],
488
+ enumerable: true
489
+ });
490
+ if (defaultValues in parser && parser[defaultValues] != null) Object.defineProperty(wrapped, defaultValues, {
491
+ value: parser[defaultValues],
492
+ enumerable: true
493
+ });
494
+ if (singleDefaultValue in parser && parser[singleDefaultValue] != null) Object.defineProperty(wrapped, singleDefaultValue, {
495
+ value: parser[singleDefaultValue],
496
+ enumerable: true
497
+ });
498
+ if (includeExisting && suggestWithDependency in parser && parser[suggestWithDependency] != null) {
499
+ const existing = parser[suggestWithDependency].bind(parser);
500
+ Object.defineProperty(wrapped, suggestWithDependency, {
501
+ value(prefix, dependencyValue) {
502
+ return suggestFrom(prefix, () => existing(prefix, dependencyValue));
503
+ },
504
+ enumerable: true
505
+ });
506
+ }
507
+ }
508
+ /**
509
+ * Lifts a synchronous computation to async mode. It runs inside an async
510
+ * function, so a synchronous throw becomes a rejected promise, and the
511
+ * resolved result object is passed through unchanged.
512
+ */
513
+ function liftToAsync(compute) {
514
+ return dispatchByMode("async", () => wrapForMode("sync", compute()), async () => await compute());
515
+ }
516
+ function isThenable(value) {
517
+ return isObjectLike(value) && "then" in value && typeof value.then === "function";
518
+ }
519
+ /**
520
+ * Rejects results a synchronous provider must not return, even when the
521
+ * resulting parser is async. A thenable gets a rejection handler first so
522
+ * that an invalid async callback cannot cause an unhandled rejection.
523
+ * @throws {TypeError} If the result is not a synchronous iterable.
524
+ */
525
+ function checkSyncProviderResult(source) {
526
+ if (isThenable(source)) {
527
+ Promise.resolve(source).then(void 0, () => {});
528
+ throw new TypeError("A sync suggestion provider returned a promise.");
529
+ }
530
+ return { [Symbol.iterator]: getSyncIteratorMethod(source) };
531
+ }
532
+ function isObjectLike(value) {
533
+ return value != null && (typeof value === "object" || typeof value === "function");
534
+ }
535
+ /**
536
+ * @throws {TypeError} If the source is not a non-string iterable.
537
+ */
538
+ function getSyncIteratorMethod(source) {
539
+ if (!isObjectLike(source) || !(Symbol.iterator in source) || typeof source[Symbol.iterator] !== "function") throw new TypeError("Expected an iterable of suggestions.");
540
+ const iterable = source;
541
+ return () => iterable[Symbol.iterator]();
542
+ }
543
+ /**
544
+ * @throws {TypeError} If the source is not a non-string iterable.
545
+ */
546
+ function getSyncIterator(source) {
547
+ return getSyncIteratorMethod(source)();
548
+ }
549
+ /**
550
+ * @throws {TypeError} If the source is neither an async iterable nor a
551
+ * non-string iterable.
552
+ */
553
+ function getAnyIterator(source) {
554
+ if (isObjectLike(source) && Symbol.asyncIterator in source && typeof source[Symbol.asyncIterator] === "function") {
555
+ const iterable = source;
556
+ return iterable[Symbol.asyncIterator]();
557
+ }
558
+ return getSyncIterator(source);
559
+ }
560
+ const fileSuggestionTypes = new Set([
561
+ "file",
562
+ "directory",
563
+ "any"
564
+ ]);
565
+ function isOptionalOfType(item, key, check) {
566
+ const value = Reflect.get(item, key);
567
+ return value === void 0 || check(value);
568
+ }
569
+ function isString(value) {
570
+ return typeof value === "string";
571
+ }
572
+ function isStringArray(value) {
573
+ return Array.isArray(value) && value.every(isString);
574
+ }
575
+ /**
576
+ * Screens a provider item for the `Suggestion` fields that completion encoding
577
+ * relies on: required fields and the types of optional ones. This is shape
578
+ * checking, not full validation; message terms inside a description are
579
+ * trusted.
580
+ */
581
+ function screenSuggestionInput(item) {
582
+ if (typeof item === "string") return {
583
+ kind: "literal",
584
+ text: item
585
+ };
586
+ if (isThenable(item)) {
587
+ Promise.resolve(item).then(void 0, () => {});
588
+ return void 0;
589
+ }
590
+ if (item == null || typeof item !== "object" || !("kind" in item)) return void 0;
591
+ if (!isOptionalOfType(item, "description", Array.isArray)) return void 0;
592
+ if (item.kind === "literal") return "text" in item && typeof item.text === "string" ? item : void 0;
593
+ if (item.kind === "file") return "type" in item && fileSuggestionTypes.has(item.type) && isOptionalOfType(item, "pattern", isString) && isOptionalOfType(item, "prefix", isString) && isOptionalOfType(item, "extensions", isStringArray) && isOptionalOfType(item, "includeHidden", (value) => typeof value === "boolean") ? item : void 0;
594
+ return void 0;
595
+ }
596
+ function createSuggestionProcessors(prefix, filterByPrefix) {
597
+ const seen = /* @__PURE__ */ new Set();
598
+ const dedupe = (suggestion) => {
599
+ if (suggestion.kind !== "literal") return suggestion;
600
+ if (seen.has(suggestion.text)) return void 0;
601
+ seen.add(suggestion.text);
602
+ return suggestion;
603
+ };
604
+ return {
605
+ existing: (item) => dedupe(item),
606
+ provider(item) {
607
+ const suggestion = screenSuggestionInput(item);
608
+ if (suggestion == null || filterByPrefix && suggestion.kind === "literal" && !suggestion.text.startsWith(prefix)) return void 0;
609
+ return dedupe(suggestion);
610
+ }
611
+ };
612
+ }
613
+ function* suggestFromSources(processors, existing, provider) {
614
+ if (existing != null) yield* guardedSuggestions(existing, processors.existing);
615
+ yield* guardedSuggestions(provider, processors.provider);
616
+ }
617
+ async function* suggestFromSourcesAsync(processors, existing, provider) {
618
+ if (existing != null) yield* guardedSuggestionsAsync(existing, processors.existing);
619
+ yield* guardedSuggestionsAsync(provider, processors.provider);
620
+ }
621
+ /**
622
+ * Yields processed items from one source, ending quietly on any failure.
623
+ * Yields happen outside the guarded regions, so consumer cancellation is never
624
+ * mistaken for a source failure, and the source iterator is closed when the
625
+ * consumer stops early.
626
+ */
627
+ function* guardedSuggestions(getSource, process) {
628
+ let iterator;
629
+ try {
630
+ iterator = getSyncIterator(getSource());
631
+ } catch {
632
+ return;
633
+ }
634
+ let finished = false;
635
+ try {
636
+ while (true) {
637
+ let suggestion;
638
+ try {
639
+ const step = iterator.next();
640
+ if (!isObjectLike(step)) return;
641
+ if (step.done) {
642
+ finished = true;
643
+ return;
644
+ }
645
+ suggestion = process(step.value);
646
+ } catch {
647
+ return;
648
+ }
649
+ if (suggestion != null) yield suggestion;
650
+ }
651
+ } finally {
652
+ if (!finished) try {
653
+ iterator.return?.();
654
+ } catch {}
655
+ }
656
+ }
657
+ async function* guardedSuggestionsAsync(getSource, process) {
658
+ let iterator;
659
+ try {
660
+ iterator = getAnyIterator(await getSource());
661
+ } catch {
662
+ return;
663
+ }
664
+ let finished = false;
665
+ try {
666
+ while (true) {
667
+ let suggestion;
668
+ try {
669
+ const step = await iterator.next();
670
+ if (!isObjectLike(step)) return;
671
+ if (step.done) {
672
+ finished = true;
673
+ return;
674
+ }
675
+ suggestion = process(step.value);
676
+ } catch {
677
+ return;
678
+ }
679
+ if (suggestion != null) yield suggestion;
680
+ }
681
+ } finally {
682
+ if (!finished) try {
683
+ await iterator.return?.();
684
+ } catch {}
685
+ }
686
+ }
418
687
  /**
419
688
  * Creates a value parser that transforms the result of another value parser.
420
689
  *
@@ -6992,4 +7261,4 @@ function plainObjectsEqual(a, b) {
6992
7261
  }
6993
7262
 
6994
7263
  //#endregion
6995
- export { biject, checkBooleanOption, checkEnumOption, choice, cidr, color, cron, domain, email, ensureNonEmptyString, fileSize, firstOf, float, hostname, integer, ip, ipv4, ipv6, isNonEmptyString, isValueParser, json, keyValue, locale, macAddress, normalizeInput, origin, port, portRange, regExp, semVer, socketAddress, string, transform, url, uuid };
7264
+ export { biject, checkBooleanOption, checkEnumOption, choice, cidr, color, cron, domain, email, ensureNonEmptyString, fileSize, firstOf, float, hostname, integer, ip, ipv4, ipv6, isNonEmptyString, isValueParser, json, keyValue, locale, macAddress, normalizeInput, origin, port, portRange, regExp, semVer, socketAddress, string, transform, url, uuid, withSuggestions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.4.0-dev.2976",
3
+ "version": "1.4.0-dev.2979",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",
@@ -269,7 +269,7 @@
269
269
  },
270
270
  "sideEffects": false,
271
271
  "devDependencies": {
272
- "@optique/env": "1.4.0-dev.2976+6b99c6c9",
272
+ "@optique/env": "1.4.0-dev.2979+a175cdb7",
273
273
  "@types/node": "^24.0.0",
274
274
  "fast-check": "^4.7.0",
275
275
  "tsdown": "^0.13.0",
@@ -45,9 +45,9 @@ Core rules
45
45
  parsing. Use `regExp({ flags })` for user-supplied sources, `biject()` for
46
46
  one-to-one mappings, `transform()` for mapped results, and
47
47
  `choice(values, { key })` for custom string matching that returns the
48
- declared spelling. Use `normalizeInput()` for raw-string cleanup and
49
- `path()` from `@optique/run/valueparser` for file-system paths. Write a
50
- custom value parser only when these tools do not cover the domain.
48
+ declared spelling. Use `normalizeInput()` to clean raw strings,
49
+ `withSuggestions()` to replace completions, and `path()` (*@optique/run*)
50
+ for paths. Write a custom value parser only when these do not fit.
51
51
  - Since 1.4.0, `-p8080`/`-vp8080` accept attached values. Values consume
52
52
  the literal suffix (`-p=5` gives `"=5"`); full single-dash names win.
53
53
  - Async value parsers like *@optique/git* make containing parsers async.