import { NumberConstraints } from "./number-constraints.mjs"; import { InferStandardOutput, StandardSchemaV1 } from "./standard.mjs"; import { ConfirmPromptConfig, InputPromptConfig, MultiselectPromptConfig, PROMPT_KINDS, PromptConfig, PromptConfigBase, PromptKind, PromptResult, SelectChoice, SelectPromptConfig } from "./prompt.mjs"; import { StringConstraintViolation, StringConstraints } from "./string-constraints.mjs"; import { DateFlagOptions, UrlFlagOptions } from "./value-parsers.mjs"; //#region src/core/schema/flag.d.ts /** All flag presence states as a runtime array. */ declare const FLAG_PRESENCES: readonly ["optional", "required", "defaulted"]; /** * Presence describes whether a flag value is guaranteed to exist when the * action handler runs: * * - `'optional'` — not required; unresolved value follows the kind-specific * optional fallback (`undefined` for most flags, `[]` for arrays) * - `'required'` — must be supplied; error if missing * - `'defaulted'` — always present (falls back to default value) */ type FlagPresence = (typeof FLAG_PRESENCES)[number]; /** * Fallback behavior when an optional flag resolves no value from any source. * * Most optional flags resolve to `undefined`; array flags instead resolve to * an empty array `[]`, and key-value flags to an empty object `{}`. */ type OptionalFallback = 'undefined' | 'empty-array' | 'empty-object'; /** * Compile-time state carried through the builder chain. * * Adding new tracked properties only requires extending this interface — no * builder signature changes. */ interface FlagConfig { /** The resolved value type (e.g. `string`, `number`, `'us' | 'eu'`). */ readonly valueType: unknown; /** Whether the flag is optional, required, or has a default. */ readonly presence: FlagPresence; /** What an unresolved optional flag becomes at the action boundary. */ readonly optionalFallback: OptionalFallback; /** The runtime kind discriminator, mirroring {@link FlagKind}. */ readonly flagKind: FlagKind; /** * Whether this builder may still be passed to `flag.array()` as the * element schema. * * Factories producing element-meaningful kinds start `true`. Flag-level * modifiers (`.alias()`, `.env()`, `.prompt()`, `.default()`, …) flip it * to `false` — those settings describe the *flag*, are never read from an * element schema, and would otherwise be silently ignored. */ readonly elementEligible: boolean; } /** * Advanced type helper used by {@linkcode FlagBuilder} modifiers to replace presence. * Most consumers rely on inference and never reference this directly. */ type WithPresence = { readonly valueType: C['valueType']; readonly presence: P; readonly optionalFallback: C['optionalFallback']; readonly flagKind: C['flagKind']; readonly elementEligible: false; }; /** * Advanced type helper: marks a builder as no longer usable as an array * element (returned by flag-level modifiers whose settings elements ignore). */ type WithoutElementEligibility = { readonly valueType: C['valueType']; readonly presence: C['presence']; readonly optionalFallback: C['optionalFallback']; readonly flagKind: C['flagKind']; readonly elementEligible: false; }; /** * Compute the final value type from config — this is what handlers receive. * * Advanced type helper: this powers {@link InferFlag} and action-handler * inference. Most apps do not need to mention it explicitly. * * - `'optional'` + `'undefined'` fallback → `T | undefined` * - `'optional'` + `'empty-array'` / `'empty-object'` fallback → `T` * - `'required'` → `T` * - `'defaulted'` → `T` */ type ResolvedValue = C['presence'] extends 'optional' ? C['optionalFallback'] extends 'undefined' ? C['valueType'] | undefined : C['valueType'] : C['valueType']; /** Extract the resolved value type from a {@linkcode FlagBuilder}. */ type InferFlag = B extends FlagBuilder ? ResolvedValue : never; /** Extract resolved value types from a record of builders. */ type InferFlags>> = { [K in keyof T]: InferFlag; }; /** * Maps a {@linkcode FlagConfig} to the prompt config types that are compatible * with the flag's kind. Prevents compile-time mismatches such as * `flag.enum([…]).prompt({ kind: 'multiselect' })`. * * - `'boolean'` → {@link ConfirmPromptConfig} * - `'string'` → {@link InputPromptConfig} | {@link SelectPromptConfig} * - `'number'` → {@link InputPromptConfig} * - `'enum'` → {@link SelectPromptConfig} | {@link InputPromptConfig} * - `'array'` → {@link MultiselectPromptConfig} * - `'custom'` → all prompt kinds ({@link PromptConfig}) * - `'count'` / `'keyValue'` → `never` (not promptable) */ type PromptConfigByFlagKind = { readonly string: InputPromptConfig | SelectPromptConfig; readonly number: InputPromptConfig; readonly boolean: ConfirmPromptConfig; readonly enum: SelectPromptConfig | InputPromptConfig; readonly array: MultiselectPromptConfig; readonly custom: PromptConfig; readonly count: never; readonly keyValue: never; }; /** Prompt configuration compatible with the kind carried by a {@link FlagConfig}. */ type AllowedPromptConfig = PromptConfigByFlagKind[C['flagKind']]; /** All flag kind discriminators as a runtime array. */ declare const FLAG_KINDS: readonly ["string", "number", "boolean", "enum", "array", "custom", "count", "keyValue"]; /** Discriminator for the kind of value a flag accepts. */ type FlagKind = (typeof FLAG_KINDS)[number]; /** * Custom parse function for `flag.custom()`. * * Receives `string` from CLI argv and env vars, or any JSON-representable * value from config files. Narrow inside the function as needed. */ type FlagParseFn = (raw: unknown) => T; /** Options accepted by `flag.path()` for any-kind or file paths. */ interface FilePathFlagOptions { /** * Reject the value if nothing exists at the path. * @defaultValue `false` (`true` when `type` is set) */ readonly mustExist?: boolean; /** * Require the path to be a file or a directory. Implies existence * unless `mustExist` is explicitly `false`, in which case a missing * path passes and only an existing path is type-checked. * @defaultValue `undefined` (any kind) */ readonly type?: 'file'; /** Directory creation is only available with `type: 'directory'`. */ readonly create?: never; } /** Options accepted by `flag.path()` for directory paths. */ interface DirectoryPathFlagOptions { /** * Reject the value if nothing exists at the path. * @defaultValue `false` (`true` when `type` is set) */ readonly mustExist?: boolean; /** * Require the path to be a directory. Implies existence unless * `mustExist` is explicitly `false`, in which case a missing path * passes and only an existing path is type-checked. */ readonly type: 'directory'; /** * Create the directory (recursively) when nothing exists at the path. * An existing non-directory path still fails the type check. * @defaultValue `false` */ readonly create?: boolean; } /** Options accepted by `flag.path()`. */ type PathFlagOptions = FilePathFlagOptions | DirectoryPathFlagOptions; /** * Filesystem expectations attached by `flag.path()`. * * Checked after resolution (not during parse) via the runtime adapter, so * `src/core` stays free of platform I/O and all sources (CLI, env, config) * are validated identically. */ interface PathChecks { /** Reject the value if nothing exists at the path. */ readonly mustExist: boolean; /** * Require the existing path to be a file or a directory. Implies * existence when set, unless `mustExist` is `false`. */ readonly type: 'file' | 'directory' | undefined; /** Create the directory (recursively) when nothing exists at the path. */ readonly create: boolean; } /** Runtime descriptor for a flag alias. */ interface FlagAlias { /** Alias name without `-` / `--` prefix. */ readonly name: string; /** Whether the alias is parser-only and hidden from user-facing surfaces. */ readonly hidden: boolean; } /** * Negation settings for a boolean flag (set by `.negatable()`). * * The negated spelling and the positive form are two spellings of ONE * logical flag: they share duplicate policy, and the last CLI occurrence * wins across both. The negated spelling is presence-only — `--no-foo=x` * is rejected. */ interface FlagNegation { /** * Explicit negated spelling without the `--` prefix (e.g. `'no-sandbox'`). * `undefined` synthesizes `no-` wherever the flag name is known. */ readonly alias: string | undefined; /** Hide the negated spelling from help, completions, and suggestions. */ readonly hidden: boolean; } /** * How repeated CLI occurrences of a singleton flag combine. * * - `'last'` — last occurrence wins (matches historic behavior) * - `'first'` — first occurrence wins; later ones parse but are ignored * - `'error'` — a second occurrence is a `ParseError` (`DUPLICATE_FLAG`) * * Applies to CLI token occurrences only — env/config/prompt/default * resolution keeps its precedence semantics and never raises duplicates. * Occurrences are counted per *logical* flag: aliases and the negated * spelling all count toward the same flag. * * @defaultValue `'last'` */ type DuplicatePolicy = 'last' | 'first' | 'error'; /** * The runtime descriptor stored inside every {@linkcode FlagBuilder}. Consumers (parser, * help generator, resolution chain) read this to understand the flag's shape * without touching generics. */ interface FlagSchema { /** What kind of value this flag accepts. */ readonly kind: FlagKind; /** Current presence state. */ readonly presence: FlagPresence; /** Runtime default value (if any). */ readonly defaultValue: unknown; /** Short/long aliases (e.g. `[{ name: 'f', hidden: false }]` for `--force`). */ readonly aliases: readonly FlagAlias[]; /** Environment variable name for v0.2+ resolution. */ readonly envVar: string | undefined; /** Dotted config path for v0.2+ resolution (e.g. `'deploy.region'`). */ readonly configPath: string | undefined; /** Human-readable description for help text. */ readonly description: string | undefined; /** Allowed literal values when `kind === 'enum'`. */ readonly enumValues: readonly string[] | undefined; /** * Numeric constraints when `kind === 'number'` (`undefined` otherwise). * * Enforced at the parse and resolution boundaries. `finite` defaults to * `true`, so `Infinity` is rejected even when no constraints object is set. */ readonly numberConstraints: NumberConstraints | undefined; /** * String constraints when `kind === 'string'` (`undefined` otherwise). * * Enforced at the parse and resolution boundaries, in fixed order: * nonEmpty → minLength → maxLength → pattern. */ readonly stringConstraints: StringConstraints | undefined; /** Element schema when `kind === 'array'`. */ readonly elementSchema: FlagSchema | undefined; /** * Value separator when `kind === 'array'` (`undefined` otherwise). * * When set, each CLI occurrence is split on this separator before element * coercion, so `--tag a,b --tag c` yields `['a', 'b', 'c']`. Env and * config string values use this separator too (default `','`). */ readonly separator: string | undefined; /** * Deduplicate resolved array values when `kind === 'array'`. * * Applied after all sources resolve, preserving first-seen order. * Uses `SameValueZero` semantics (like `Set`). */ readonly unique: boolean; /** * Filesystem checks for path-valued flags (set by `flag.path()`). * * Validated after resolution through the runtime adapter. */ readonly pathChecks: PathChecks | undefined; /** * Help placeholder label (e.g. `'url'` renders as ``). * * Set by the sugar factories (`flag.url()`, `flag.date()`, …) so help * output names the expected value shape; `undefined` falls back to the * kind-derived hint. */ readonly valueHint: string | undefined; /** Interactive prompt configuration for v0.3+ resolution. */ readonly prompt: PromptConfig | undefined; /** Custom parse function (only when `kind === 'custom'`). */ readonly parseFn: FlagParseFn | undefined; /** * Standard Schema v1 validator applied to the resolved value. * * When set, the value from any source (CLI, env, config, prompt, default) * is validated after resolution via `~standard.validate`. Sync and async * validators are both awaited; issues surface as a `CONSTRAINT_VIOLATED` * {@link ValidationError}. Only meaningful when `kind === 'custom'`. */ readonly standard: StandardSchemaV1 | undefined; /** * Deprecation marker. * * - `undefined` — not deprecated (default) * - `true` — deprecated with no migration message * - `string` — deprecated with a reason/migration message * * When a deprecated flag is used, a warning is emitted to stderr. * Help text shows `[deprecated]` or `[deprecated: ]`. */ readonly deprecated: string | true | undefined; /** * Whether this flag propagates to subcommands in nested command trees. * * When `true`, the flag is automatically available to all descendant * commands. A child command that defines a flag with the same name * shadows the propagated parent flag. * * @defaultValue `false` */ readonly propagate: boolean; /** * Negation settings when `kind === 'boolean'` and `.negatable()` was * called (`undefined` otherwise). See {@link FlagNegation}. */ readonly negation: FlagNegation | undefined; /** * Duplicate policy for repeated CLI occurrences. See {@link DuplicatePolicy}. * * @defaultValue `'last'` */ readonly duplicates: DuplicatePolicy; } /** * Low-level overrides accepted by {@link createSchema}. * * `aliases` accepts both legacy string input and structured {@link FlagAlias} * objects so tests and internal fixtures can be migrated incrementally. */ type FlagSchemaOverrides = Omit, 'aliases'> & { readonly aliases?: readonly (string | FlagAlias)[]; }; /** * Normalise an alias input into a full {@link FlagAlias} object. * * @param alias - Raw alias name or structured alias object. * @returns Normalised alias record. */ declare function normalizeFlagAlias(alias: string | FlagAlias): FlagAlias; /** * Normalise alias input into immutable alias records. * * @param aliases - Alias input values. * @returns Normalised alias objects. */ declare function normalizeFlagAliases(aliases: readonly (string | FlagAlias)[]): readonly FlagAlias[]; /** * List alias names for a flag schema. * * @param schema - Flag schema whose aliases should be listed. * @param options - Visibility and length filtering. * @returns Alias names in registration order. */ declare function getFlagAliasNames(schema: FlagSchema, options?: { readonly includeHidden?: boolean; readonly kind?: 'all' | 'short' | 'long'; }): readonly string[]; /** * Effective negated spelling for a flag, or `undefined` when not negatable. * * The builder cannot know its flag name, so a default (`no-`) is * synthesized here — everywhere the canonical name is known (parser, help, * completions, collision validation). * * @param name - Canonical flag name. * @param schema - Flag schema (read for {@link FlagNegation}). * @returns The negated spelling without the `--` prefix. */ declare function getFlagNegatedName(name: string, schema: FlagSchema): string | undefined; /** * Create a raw {@link FlagSchema} object with sensible defaults. * * Most consumers should prefer the higher-level {@link flag} factory, * which returns an immutable {@link FlagBuilder} with type inference and * safe modifier chaining. `createSchema()` is the low-level escape hatch * for advanced schema composition, tests, or custom factories that need to * work directly with the runtime descriptor. * * `overrides` are shallow-merged on top of the default shape, so callers are * responsible for keeping the resulting schema internally consistent. * * @param kind - Discriminator for the value type this flag accepts. * @param overrides - Partial {@link FlagSchema} fields merged onto defaults. * @returns A fully populated {@link FlagSchema}. * * @example * ```ts * const schema = createSchema('enum', { * enumValues: ['us', 'eu', 'ap'], * description: 'Deployment region', * }); * ``` */ declare function createSchema(kind: FlagKind, overrides?: FlagSchemaOverrides): FlagSchema; /** * Immutable flag schema builder. * * The type parameter `C` is a phantom that tracks the value type and presence * through the fluent chain. Each modifier returns a **new** builder — the * original is never mutated. * * @example * ```ts * const port = flag.number().default(8080); * type Port = InferFlag; // number * * const region = flag.enum(['us', 'eu', 'ap']); * type Region = InferFlag; // 'us' | 'eu' | 'ap' | undefined * ``` */ declare class FlagBuilder { /** @internal Runtime schema descriptor. */ readonly schema: FlagSchema; /** * @internal Type brand — exists only in the type system (`declare` * produces no runtime property). Used by {@linkcode InferFlag} / {@linkcode InferFlags}. */ readonly _config: C; /** * Create a flag builder from a pre-built schema descriptor. * * @param schema - Runtime descriptor seeding this builder's state. */ constructor(schema: FlagSchema); /** * Provide a default value. The flag becomes "always present" — handlers * will never see `undefined`. * * The generic constraint `V extends C['valueType']` ensures the default * matches the flag's declared type. * * @param value - Fallback value used when no source provides one. * @returns The builder (for chaining). * * @example * ```ts * flag.number().default(8080).describe('Port to listen on') * * // $ mycli serve → port = 8080 * // $ mycli serve --port 443 → port = 443 * ``` */ default(value: V): FlagBuilder>; /** * Mark the flag as required. If not resolved from any source the framework * will emit a `ValidationError` before the action handler runs. * * @returns The builder (for chaining). * * @example * ```ts * flag.string().required().describe('Deploy target') * * // $ mycli deploy * // # → Error: Missing required flag --target * // $ mycli deploy --target staging * // # → target = 'staging' * ``` */ required(): FlagBuilder>; /** * Add a short or long alias (e.g. `'f'` for `--force`, `'verbose'` as an * alternative long name). * * @param name - Single-char short alias or alternative long name. * @param options - Optional alias metadata. Hidden aliases remain parseable * but are omitted from help, completions, and suggestions. * @returns The builder (for chaining). * * @example * ```ts * flag.boolean().alias('v').describe('Enable verbose output') * * // $ mycli build -v → verbose = true * // $ mycli build --verbose → verbose = true * ``` */ alias(name: string, options?: { hidden?: boolean; }): FlagBuilder>; /** * Bind to an environment variable (resolved in v0.2+). * * @param varName - Environment variable name (e.g. `'PORT'`). * @returns The builder (for chaining). * * @example * ```ts * flag.string().env('API_KEY').describe('Service API key') * * // $ API_KEY=sk-123 mycli request → apiKey = 'sk-123' * // $ mycli request --api-key sk-456 → apiKey = 'sk-456' (CLI wins) * ``` */ env(varName: string): FlagBuilder>; /** * Bind to a dotted config path (resolved in v0.2+). * * @param path - Dotted config key (e.g. `'deploy.region'`). * @returns The builder (for chaining). * * @example * ```ts * flag.string().config('deploy.region').default('us-east-1') * // Config file: { "deploy": { "region": "eu-west-1" } } * // $ mycli deploy * // # → region = 'eu-west-1' (from config) * // $ mycli deploy --region ap-south-1 * // # → CLI flag wins * ``` */ config(path: string): FlagBuilder>; /** * Human-readable description shown in help output. * * @param description - Text displayed next to the flag in `--help`. * @returns The builder (for chaining). */ describe(description: string): FlagBuilder>; /** * Attach interactive prompt configuration for v0.3+ resolution. * * When a flag value is not resolved from CLI, env, or config, the * prompt engine uses this config to interactively ask the user. * In non-interactive contexts (CI, piped stdin) prompts are skipped * and resolution falls through to default or required validation. * * @param config - {@link PromptConfig} describing the interactive prompt. * @returns The builder (for chaining). * * @example * ```ts * flag.string().prompt({ kind: 'input', message: 'Enter value:' }) * * // $ mycli init → prompts "Enter value:" interactively * // $ mycli init --name foo → skips prompt, uses CLI value * ``` */ prompt(config: AllowedPromptConfig): FlagBuilder>; /** * Mark this flag as deprecated. * * When used, a warning is emitted to stderr. Help text shows * `[deprecated]` or `[deprecated: ]`. * * Does not change the flag's type-level config — it's metadata only. * * @param message - Optional migration reason/guidance. * @returns The builder (for chaining). * * @example * ```ts * flag.string().deprecated('Use --target instead') * * // $ mycli deploy --dest staging * // ⚠ --dest is deprecated: Use --target instead * ``` */ deprecated(message?: string): FlagBuilder>; /** * Mark this flag as propagated to subcommands. * * Propagated flags are automatically available to all descendant * commands in a nested command tree. A child command that defines * a flag with the same name shadows the propagated parent flag. * * Does not change the flag's type-level config — it's metadata only. * * @returns The builder (for chaining). * * @example * ```ts * flag.boolean().alias('v').propagate().describe('Enable verbose output') * * // $ mycli --verbose deploy staging * // # → verbose = true in deploy handler * // $ mycli deploy --verbose staging * // # → same, inherited from parent * ``` */ propagate(): FlagBuilder>; /** * Require an integer value. Composes with other numeric constraints. * * @param value - Whether to require an integer. * @defaultValue `true` * @returns The builder (for chaining). * * @example * ```ts * flag.number().int() // rejects 3.7, accepts 3 * flag.number({ int: true }).int(false) // re-allows non-integers * ``` */ int(this: FlagBuilder, value?: boolean): FlagBuilder; /** * Set an inclusive lower bound. Composes with other numeric constraints; * a later call overrides an earlier `min` (including one from the options * object). * * @param value - Inclusive minimum. * @returns The builder (for chaining). * * @example * ```ts * flag.number().min(0) // rejects -1, accepts 0 * flag.number({ min: 0 }).min(5) // effective min is 5 * ``` */ min(this: FlagBuilder, value: number): FlagBuilder; /** * Set an inclusive upper bound. Composes with other numeric constraints; * a later call overrides an earlier `max`. * * @param value - Inclusive maximum. * @returns The builder (for chaining). * * @example * ```ts * flag.number().max(100) // rejects 101, accepts 100 * ``` */ max(this: FlagBuilder, value: number): FlagBuilder; /** * Require (or, with `false`, allow) a finite value. Finiteness is enforced * by default, so this is mainly used as `.finite(false)` to re-allow * `Infinity` / `-Infinity`. * * @param allow - Whether to require a finite value. * @defaultValue `true` * @returns The builder (for chaining). * * @example * ```ts * flag.number().finite(false) // accepts Infinity * ``` */ finite(this: FlagBuilder, allow?: boolean): FlagBuilder; /** * Reject empty strings. Composes with other string constraints. * * @param value - Whether to reject empty strings. * @defaultValue `true` * @returns The builder (for chaining). * * @example * ```ts * flag.string().nonEmpty() // rejects '', accepts 'x' * ``` */ nonEmpty(this: FlagBuilder, value?: boolean): FlagBuilder; /** * Set an inclusive minimum length (UTF-16 code units). Composes with other * string constraints; a later call overrides an earlier `minLength`. * * @param value - Inclusive minimum length. * @returns The builder (for chaining). * * @example * ```ts * flag.string().minLength(3) // rejects 'ab', accepts 'abc' * ``` */ minLength(this: FlagBuilder, value: number): FlagBuilder; /** * Set an inclusive maximum length (UTF-16 code units). Composes with other * string constraints; a later call overrides an earlier `maxLength`. * * @param value - Inclusive maximum length. * @returns The builder (for chaining). * * @example * ```ts * flag.string().maxLength(8) // rejects 9+ chars * ``` */ maxLength(this: FlagBuilder, value: number): FlagBuilder; /** * Require the value to match a regular expression. Anchor with `^`/`$` * for full-string matching. Composes with other string constraints. * * @param value - Pattern the value must match. * @returns The builder (for chaining). * * @example * ```ts * flag.string().pattern(/^ghp_/) // rejects 'abc', accepts 'ghp_x' * ``` */ pattern(this: FlagBuilder, value: RegExp): FlagBuilder; /** * Split each CLI occurrence on a separator before element coercion, so * `--tag a,b --tag c` resolves to `['a', 'b', 'c']`. Elements are coerced * (and rejected) individually with the element schema's own error format. * * Env and config string values are split on the same separator (their * default split is `','` even without this modifier). * * @param value - Separator string (e.g. `','`). * @returns The builder (for chaining). * * @example * ```ts * flag.array(flag.enum(['us', 'eu', 'ap'])).separator(',') * // --region us,eu --region ap → ['us', 'eu', 'ap'] * ``` */ separator(this: FlagBuilder, value: string): FlagBuilder; /** * Deduplicate the resolved array, preserving first-seen order. Applied * after all sources resolve, using `SameValueZero` semantics (like `Set`). * * @param value - Whether to deduplicate. * @defaultValue `true` * @returns The builder (for chaining). * * @example * ```ts * flag.array(flag.string()).separator(',').unique() * // --tag a,a --tag a → ['a'] * ``` */ unique(this: FlagBuilder, value?: boolean): FlagBuilder; /** * Accept a negated spelling (`--no-`) that sets the flag to `false`. * * Both spellings are ONE logical flag: the last CLI occurrence wins across * them, and they share the duplicate policy. The negated spelling is * presence-only — `--no-=true` is rejected. Help renders the flag as * `--[no-]` (or lists a custom alias); env/config/prompt/default * resolution is unaffected. * * @param options - Optional custom spelling (`alias`, without `--`) and * `hidden` to keep the negated spelling parseable but unadvertised. * @returns The builder (for chaining). * * @example * ```ts * flag.boolean().default(true).negatable() * // $ mycli build --no-sandbox → sandbox = false * // $ mycli build --sandbox → sandbox = true * ``` */ negatable(this: FlagBuilder, options?: { alias?: string; hidden?: boolean; }): FlagBuilder>; /** * Set how repeated CLI occurrences of this flag combine. * * Counted per logical flag — aliases and the negated spelling all count * toward the same flag. CLI tokens only: env/config/prompt/default * resolution keeps its precedence semantics. Not available on `array`, * `count`, or `keyValue` flags, which inherently accumulate. * * @param policy - `'last'` (default), `'first'`, or `'error'`. * @returns The builder (for chaining). * * @example * ```ts * flag.enum(['session', 'same-dir', 'worktree']).duplicates('error') * // $ mycli run --spawn session --spawn worktree * // # → Error: Flag --spawn may only be specified once * ``` */ duplicates(this: FlagBuilder, policy: DuplicatePolicy): FlagBuilder>; } /** * Factory that creates {@link FlagBuilder} instances seeded with the correct * {@link FlagKind} and initial type-level config. */ interface FlagFactory { /** * String-valued flag, with optional string constraints. * * Constraints are enforced at the parse and resolution boundaries, in * fixed order: nonEmpty → minLength → maxLength → pattern. They also * compose via chained methods (`.nonEmpty()`, `.minLength()`, * `.maxLength()`, `.pattern()`), which override values set here. * * @param constraints - Optional string constraints. * @defaultValue `undefined` (no constraints) * @returns A {@link FlagBuilder} for `string` values. * * @example * ```ts * flag.string() // any string * flag.string({ nonEmpty: true }) // rejects '' * flag.string({ pattern: /^ghp_/ }) // token shapes * ``` */ string(constraints?: StringConstraints): FlagBuilder<{ readonly valueType: string; readonly presence: 'optional'; readonly optionalFallback: 'undefined'; readonly flagKind: 'string'; readonly elementEligible: true; }>; /** * Number-valued flag, with optional numeric constraints. * * Constraints are enforced at the parse and resolution boundaries. The * resolved value type stays `number` — constraints are runtime + schema, not * type-level. Bounds are inclusive. * * Constraints also compose via chained methods (`.int()`, `.min()`, * `.max()`, `.finite()`), which override values set here. * * @param constraints - Optional numeric constraints. `finite` defaults to * `true`, so `Infinity` / `-Infinity` are rejected unless `finite: false`. * @defaultValue `undefined` (finite-only, no bounds, non-integer allowed) * @returns A {@link FlagBuilder} for `number` values. * * @example * ```ts * flag.number() // finite numbers only * flag.number({ int: true, min: 0 }) // non-negative integers * flag.number({ finite: false }) // also accepts Infinity * ``` */ number(constraints?: NumberConstraints): FlagBuilder<{ readonly valueType: number; readonly presence: 'optional'; readonly optionalFallback: 'undefined'; readonly flagKind: 'number'; readonly elementEligible: true; }>; /** * Boolean flag. Implicitly defaults to `false` — the only flag kind where * the absence of a value is still meaningful (not `undefined`). * * @returns A {@link FlagBuilder} for `boolean` values (defaulted to `false`). */ boolean(): FlagBuilder<{ readonly valueType: boolean; readonly presence: 'defaulted'; readonly optionalFallback: 'undefined'; readonly flagKind: 'boolean'; readonly elementEligible: true; }>; /** * Enum flag with literal type inference. * * Requires a **non-empty** readonly tuple so that `T[number]` produces a * union of string literals rather than just `string`. * * @example * ```ts * flag.enum(['us', 'eu', 'ap']) * // inferred type: 'us' | 'eu' | 'ap' * ``` * * @param values - Non-empty tuple of allowed string literals. * @returns A {@link FlagBuilder} whose value type is the union of `values`. */ enum(values: T): FlagBuilder<{ readonly valueType: T[number]; readonly presence: 'optional'; readonly optionalFallback: 'undefined'; readonly flagKind: 'enum'; readonly elementEligible: true; }>; /** * Array flag — collects multiple values of the same element type. * * @example * ```ts * flag.array(flag.string()) * // inferred type: string[] * ``` * * @param element - {@link FlagBuilder} describing the element type. * @returns A {@link FlagBuilder} for arrays of the element type. */ array(element: FlagBuilder): FlagBuilder<{ readonly valueType: E['valueType'][]; readonly presence: 'optional'; readonly optionalFallback: 'empty-array'; readonly flagKind: 'array'; readonly elementEligible: false; }>; /** * Custom flag validated by a Standard Schema v1 validator (zod, valibot, * arktype, …). The resolved value from any source is validated after * resolution; the flag's value type is the validator's output type. * * Sync and async validators are both supported. Validation issues surface * as a `CONSTRAINT_VIOLATED` error naming the flag. * * @example * ```ts * import { z } from 'zod'; * flag.custom(z.string().url()) * // inferred type: string | undefined * ``` * * @param schema - A Standard Schema v1 validator. * @returns A {@link FlagBuilder} whose value type is the validator's output. */ custom(schema: S): FlagBuilder<{ readonly valueType: InferStandardOutput; readonly presence: 'optional'; readonly optionalFallback: 'undefined'; readonly flagKind: 'custom'; readonly elementEligible: true; }>; /** * Custom-parsed flag. The parse function receives the raw value and must * return a value of type `T`. The return type is inferred from `parseFn`. * * The input is `string` from CLI argv and env vars, or any JSON value * from config files. Narrow inside the function as needed: * * ```ts * flag.custom((raw: unknown): string[] => { * if (Array.isArray(raw)) return raw.map(String); * if (typeof raw === 'string') return raw.split(','); * throw new Error(`Expected string or array, got ${typeof raw}`); * }) * ``` * * Throw an `Error` (or `ParseError`) to signal invalid input — it will * be wrapped with context and re-thrown as a `ParseError`. * * @see `coerceConfigValue` `'custom'` case in `core/resolve/index.ts` * * @example * ```ts * flag.custom((raw) => new URL(String(raw))) * // inferred type: URL | undefined * ``` * * @param parseFn - Converts the raw input into a value of type `T`. * @returns A {@link FlagBuilder} whose value type is inferred from `parseFn`. */ custom(parseFn: FlagParseFn): FlagBuilder<{ readonly valueType: T; readonly presence: 'optional'; readonly optionalFallback: 'undefined'; readonly flagKind: 'custom'; readonly elementEligible: true; }>; /** * URL-valued flag. Parses into a `URL`; invalid URLs are rejected with an * `INVALID_VALUE` error naming the flag. * * @param options - Optional protocol allowlist (without trailing colon). * @returns A {@link FlagBuilder} for `URL` values. * * @example * ```ts * flag.url() // any URL * flag.url({ protocols: ['https'] }) // https only * ``` */ url(options?: UrlFlagOptions): FlagBuilder<{ readonly valueType: URL; readonly presence: 'optional'; readonly optionalFallback: 'undefined'; readonly flagKind: 'custom'; readonly elementEligible: true; }>; /** * Path-valued flag. The value stays a `string`; optional filesystem * checks run **after resolution** through the runtime adapter, so CLI, * env, and config values are validated identically. * * @param options - Optional existence/type checks. `type` implies * existence unless `mustExist` is explicitly `false`. * @returns A {@link FlagBuilder} for path strings. * * @example * ```ts * flag.path() // any string, help shows * flag.path({ mustExist: true }) // rejects missing paths * flag.path({ type: 'directory' }) // must exist and be a directory * flag.path({ type: 'directory', mustExist: false }) * // missing passes; existing must be a directory * flag.path({ type: 'directory', create: true }) * // created recursively when missing * ``` */ path(options?: PathFlagOptions): FlagBuilder<{ readonly valueType: string; readonly presence: 'optional'; readonly optionalFallback: 'undefined'; readonly flagKind: 'string'; readonly elementEligible: false; }>; /** * Date-valued flag. Accepts strict ISO-8601 (`2026-07-10`, * `2026-07-10T14:30:00Z`) and parses into a `Date`. Lenient `Date.parse` * inputs (`'0'`, `'March 5'`) and calendar-invalid dates (`2026-02-31`) * are rejected. * * Returns `Date` (not `Temporal`) because the supported runtimes do not * all ship Temporal yet; use `flag.custom()` with `Temporal.PlainDate.from` * where the target runtime has it. * * @param options - Optional inclusive `min`/`max` date bounds. * @returns A {@link FlagBuilder} for `Date` values. * * @example * ```ts * flag.date() * flag.date({ min: new Date('2020-01-01') }) * ``` */ date(options?: DateFlagOptions): FlagBuilder<{ readonly valueType: Date; readonly presence: 'optional'; readonly optionalFallback: 'undefined'; readonly flagKind: 'custom'; readonly elementEligible: true; }>; /** * Duration flag. Accepts `'30s'`, `'5m'`, `'1.5h'`, `'250ms'`, `'2d'`, * compounds like `'1h30m'`, or a bare millisecond count, and resolves to * **milliseconds**. * * @returns A {@link FlagBuilder} for duration values in milliseconds. * * @example * ```ts * flag.duration().default(30_000) // --timeout 45s → 45000 * ``` */ duration(): FlagBuilder<{ readonly valueType: number; readonly presence: 'optional'; readonly optionalFallback: 'undefined'; readonly flagKind: 'custom'; readonly elementEligible: true; }>; /** * Byte-size flag. Accepts `'512mb'`, `'1.5gb'`, `'64kb'`, `'100b'` or a * bare byte count, and resolves to **bytes**. Units are binary * (`1kb` = 1024) and case-insensitive. * * @returns A {@link FlagBuilder} for sizes in bytes. * * @example * ```ts * flag.bytes().default(10 * 1024 ** 2) // --max-size 512kb → 524288 * ``` */ bytes(): FlagBuilder<{ readonly valueType: number; readonly presence: 'optional'; readonly optionalFallback: 'undefined'; readonly flagKind: 'custom'; readonly elementEligible: true; }>; /** * Count flag — resolves to how many times the flag appears. `-vvv`, * `-v -v -v`, and `--verbose --verbose --verbose` all yield `3`; absent * yields `0`. An explicit value (`--verbose=2`, env, config) sets the * count directly. * * Not promptable. * * @returns A {@link FlagBuilder} for occurrence counts (defaulted to `0`). * * @example * ```ts * flag.count().alias('v').describe('Increase verbosity') * // $ mycli build -vv → verbose = 2 * ``` */ count(): FlagBuilder<{ readonly valueType: number; readonly presence: 'defaulted'; readonly optionalFallback: 'undefined'; readonly flagKind: 'count'; readonly elementEligible: false; }>; /** * Key-value flag — repeated `KEY=VALUE` occurrences merge into a * `Record` (docker/kubectl `--env` style). The value is * split at the **first** `=`, so `--env A=b=c` yields `{ A: 'b=c' }`. * Later occurrences of the same key win. Absent resolves to `{}`. * * Env vars accept separator-joined pairs (`A=1,B=2`); config files accept * a plain object. Not promptable. * * @returns A {@link FlagBuilder} for `Record` values. * * @example * ```ts * flag.keyValue().alias('e').describe('Environment variables') * // $ mycli run -e A=1 -e B=2 → env = { A: '1', B: '2' } * ``` */ keyValue(): FlagBuilder<{ readonly valueType: Record; readonly presence: 'optional'; readonly optionalFallback: 'empty-object'; readonly flagKind: 'keyValue'; readonly elementEligible: false; }>; } /** * Flag schema factory. Call `flag.()` to create an immutable * {@link FlagBuilder} with full type inference and safe modifier chaining. */ declare const flag: FlagFactory; //#endregion export { type AllowedPromptConfig, type ConfirmPromptConfig, type DateFlagOptions, type DuplicatePolicy, FLAG_KINDS, FLAG_PRESENCES, type FlagAlias, FlagBuilder, type FlagConfig, type FlagFactory, type FlagKind, type FlagNegation, type FlagParseFn, type FlagPresence, type FlagSchema, type FlagSchemaOverrides, type InferFlag, type InferFlags, type InputPromptConfig, type MultiselectPromptConfig, type OptionalFallback, PROMPT_KINDS, type PathChecks, type PathFlagOptions, type PromptConfig, type PromptConfigBase, type PromptConfigByFlagKind, type PromptKind, type PromptResult, type ResolvedValue, type SelectChoice, type SelectPromptConfig, type StringConstraintViolation, type StringConstraints, type UrlFlagOptions, type WithPresence, type WithoutElementEligibility, createSchema, flag, getFlagAliasNames, getFlagNegatedName, normalizeFlagAlias, normalizeFlagAliases };