Crust logoCrust

Types Reference

All exported types and interfaces from @crustjs/core.

Complete reference of all types exported from @crustjs/core.

Command Types

CommandNode

Internal representation of a single node in the command tree. Built by the Crust builder class; exported for downstream packages.

interface CommandNode {
  meta: CommandMeta;
  localFlags: FlagsDef;
  effectiveFlags: FlagsDef;
  args: ArgsDef | undefined;
  subCommands: Record<string, CommandNode>;
  plugins: CrustPlugin[];
  preRun?: (ctx: unknown) => void | Promise<void>;
  run?: (ctx: unknown) => void | Promise<void>;
  postRun?: (ctx: unknown) => void | Promise<void>;
}
  • localFlags — Flags defined directly on this command via .flags()
  • effectiveFlags — Inherited flags merged with local flags (used by the parser and help plugin)
  • plugins — Plugins registered via .use()

CrustCommandContext<A, F>

Runtime context passed to preRun, run, and postRun hooks on the Crust builder:

interface CrustCommandContext<A extends ArgsDef, F extends FlagsDef> {
  args: InferArgs<A>; // Resolved positional arguments
  flags: InferFlags<F>; // Resolved flags
  rawArgs: string[]; // Arguments after `--`
  command: CommandNode; // The resolved command node
}

CommandMeta

Metadata describing a command:

interface CommandMeta {
  name: string; // Command name (required)
  description?: string; // Help text description
  usage?: string; // Custom usage string
  aliases?: readonly string[]; // Alternative names
  hidden?: boolean; // Omit from default --help output
}

aliases

Alternative names that resolve to the same command. Type: readonly string[]. Default: undefined.

meta: { name: "issue", aliases: ["issues", "i"] }

With this metadata, cli issue, cli issues, and cli i all route to the same command node.

Conflict policy. Alias strings must not collide with the command's own canonical name, with any sibling's name, or with any sibling's alias. Each alias must be a non-empty string with no whitespace and must not start with -. Collisions throw a CrustError("DEFINITION", …) at registration time. Plugin-installed subcommands (via the addCommand setup action) are validated when registered too: a colliding alias is skipped with a warning, mirroring how a colliding canonical name is handled today. prepareCommandTree() re-runs the same check against the final tree.

Display contract. The canonical name is always what appears in commandPath, error messages, and didYouMeanPlugin suggestions — it does not depend on which alias the user typed. Help text renders the inline form name (alias1, alias2).

hidden

When true, the command is omitted from any tooling that enumerates the command tree for users — help output, generated man pages, skill descriptors, completion candidate lists, and similar surfaces. Type: boolean. Default: false.

meta: { name: "__complete", hidden: true, description: "Internal completion entrypoint" }

The command is only hidden from listings: it stays fully invocable by name (or alias), routing is unchanged, and it can still surface through tooling that looks up specific commands rather than enumerating the tree (e.g. didYouMeanPlugin suggestions).

Tooling contract. Any renderer or generator that walks subCommands to produce a user-facing listing should skip nodes where meta.hidden === true. The default helpPlugin follows this contract today; first-party generators (man pages, skill descriptors, completion) and any custom renderers should too.

Intended for internal/runtime commands such as a __complete entrypoint used by shell-completion tooling. Marking a user-facing subcommand as hidden is supported but unusual.

Argument Types

ArgDef

Defines a single positional argument. Discriminated union by type for type-safe defaults, plus a type-less variant for raw schema-backed parsing:

type ArgDef = StringArgDef | NumberArgDef | BooleanArgDef | RawArgDef;

type is optional. Omitting it activates the raw parser mode (see parseArgs) and is the default produced by arg() in @crustjs/validate.

Each variant constrains default to match its type. Boolean toggle fields (required, variadic) only accept true:

// String variant (Number and Boolean follow the same shape, minus `choices`)
interface StringArgDef {
  name: string; // Argument name
  type: "string"; // Type discriminant
  description?: string; // Help text
  default?: string; // Default value (type-safe)
  required?: true; // Error if missing
  variadic?: true; // Collect remaining into array
  choices?: readonly string[]; // Static enum of valid values (string only)
}

choices (string args only)

A static enum of valid values for the argument. Type: readonly string[]. Default: undefined. Only available on string-typed args (StringArgDef); not supported on number or boolean variants.

args: [{ name: "target", type: "string", choices: ["browser", "bun", "node"] }] as const;

Validated at parse time. Passing a value outside choices throws CrustError("PARSE", …) before any parse transform runs. Shell-completion plugins also consume choices to emit value candidates.

ArgsDef

Ordered tuple of argument definitions:

type ArgsDef = readonly ArgDef[];

Flag Types

FlagDef

Defines a single named flag. Discriminated union by type and multiple:

type FlagDef =
  | StringFlagDef
  | NumberFlagDef
  | BooleanFlagDef
  | UrlFlagDef
  | PathFlagDef
  | JsonFlagDef // single-value
  | StringMultiFlagDef
  | NumberMultiFlagDef
  | BooleanMultiFlagDef
  | UrlMultiFlagDef
  | PathMultiFlagDef
  | JsonMultiFlagDef; // multi-value

Flags always declare type because it defines CLI grammar/token ownership: boolean flags do not consume a value, while string and number flags consume --flag value / --flag=value. Schema-backed flags produced by flag() still include this parser metadata; the schema validates and transforms the parsed value afterward.

Single-value variants use multiple?: never (field absent); multi-value variants require multiple: true:

// Single-value string flag
interface StringFlagDef {
  type: "string"; // Type discriminant
  description?: string; // Help text
  default?: string; // Default value (type-safe)
  required?: true; // Error if missing
  short?: string; // Single-character short form (-x)
  aliases?: string[]; // Additional aliases (single-char or long)
  multiple?: never; // Absent for single-value flags
  inherit?: true; // Inherited by subcommands
  choices?: readonly string[]; // Static enum of valid values (string only)
}

// Multi-value string flag
interface StringMultiFlagDef {
  type: "string"; // Type discriminant
  description?: string; // Help text
  default?: string[]; // Default array value (type-safe)
  required?: true; // Error if missing
  short?: string; // Single-character short form (-x)
  aliases?: string[]; // Additional aliases (single-char or long)
  multiple: true; // Collect repeated values into array
  inherit?: true; // Inherited by subcommands
  choices?: readonly string[]; // Static enum of valid values (string only)
}
// Number variants follow the same pattern.
// Boolean variants additionally support `noNegate`:

interface BooleanFlagDef {
  type: "boolean";
  description?: string;
  default?: boolean;
  required?: true;
  short?: string;
  aliases?: string[];
  multiple?: never;
  inherit?: true;
  noNegate?: true; // Hide the --no-{name} negation label
}

Boolean toggle fields (required, multiple, inherit, noNegate) only accept true — passing false is a type error.

noNegate is exclusive to boolean flag types. Setting it on a string or number flag is a compile-time error.

choices (string flags only)

A static enum of valid values for the flag. Type: readonly string[]. Default: undefined. Only available on string-typed flags (StringFlagDef and StringMultiFlagDef); not supported on number or boolean variants.

flags: {
  target: { type: "string", choices: ["browser", "bun", "node"] },
  suites: { type: "string", multiple: true, choices: ["unit", "integration"] },
}

Validated at parse time. Passing a value outside choices throws CrustError("PARSE", …). When combined with parse, the raw argv token is validated against choices first; parse only runs on values that pass the choices gate. Shell-completion plugins also consume choices to emit value candidates after --flag= or --flag <TAB>.

parse (string flags and args only)

A synchronous parser for the raw argv token: parse?: (raw: string) => unknown. The return type becomes the inferred runtime type of the flag/argument. Only allowed on StringFlagDef, StringMultiFlagDef, and StringArgDef; every non-string variant declares parse?: never, so misuse is rejected at compile time.

flags: {
  port: { type: "string", parse: (s) => Number(s), default: "3000" },
}
// flags.port: number (3000 when argv is absent)

Async parse functions are rejected at command setup with CrustError("CONFIG", …). For multiple: true, parse runs per element. When default is set and argv is absent, Crust runs parse(String(default)) so the runtime value matches the inferred type. See Types — the parse escape hatch for the full contract.

FlagsDef

Record mapping flag names to definitions:

type FlagsDef = Record<string, FlagDef>;

Flag Inheritance Types

InheritableFlags<F>

Picks only the flags from F that have inherit: true:

type InheritableFlags<F extends FlagsDef> = {
  [K in keyof F as F[K] extends { inherit: true } ? K : never]: F[K];
};

MergeFlags<Parent, Local>

Merges parent flags with local flags, where local keys override parent keys:

type MergeFlags<Parent extends FlagsDef, Local extends FlagsDef> = Omit<Parent, keyof Local> &
  Local;

EffectiveFlags<Inherited, Local>

Computes the effective flags for a command by filtering inherited flags (only those with inherit: true) and merging with local flags:

type EffectiveFlags<Inherited extends FlagsDef, Local extends FlagsDef> = MergeFlags<
  InheritableFlags<Inherited>,
  Local
>;

This is the type-level counterpart of the runtime computeEffectiveFlags() function.

Inference Types

InferArgs<A>

Maps an ArgsDef tuple to resolved arg types:

type InferArgs<A> = A extends ArgsDef ? /* resolved types */ : Record<string, never>;

Resolution rules:

DefinitionInferred Type
{ type: "string" }string | undefined
{ type: "string", required: true }string
{ type: "string", default: "hi" }string
{ type: "number" }number | undefined
{ type: "number", default: 3000 }number
{ type: "string", variadic: true }string[]
{ type: "string", variadic: true, required: true }string[]
{ name: "x" } (no type)unknown
{ name: "x", variadic: true } (no type)unknown[]

Variadic args always resolve to T[]. required does not change the type — it only controls whether an empty array fails validation at runtime.

Type-less (raw) positional args — produced by arg() from @crustjs/validate — resolve to unknown (or unknown[] when variadic). The downstream schema validator narrows them to the concrete output type.

InferFlags<F>

Maps a FlagsDef record to resolved flag types:

type InferFlags<F> = F extends FlagsDef ? /* resolved types */ : Record<string, never>;

Resolution rules:

DefinitionInferred Type
{ type: "string" }string | undefined
{ type: "string", required: true }string
{ type: "string", default: "hi" }string
{ type: "boolean" }boolean | undefined
{ type: "string", multiple: true }string[] | undefined
{ type: "string", multiple: true, required: true }string[]

Parse Types

ParseResult<A, F>

Output of parseArgs:

interface ParseResult<A extends ArgsDef, F extends FlagsDef> {
  args: InferArgs<A>; // Resolved positional arguments
  flags: InferFlags<F>; // Resolved flags
  rawArgs: string[]; // Arguments after `--`
}

Routing Types

CommandRoute

Output of resolveCommand:

interface CommandRoute {
  command: CommandNode; // The resolved command
  argv: string[]; // Remaining argv
  commandPath: string[]; // Full command path
}

Plugin Types

CrustPlugin

The plugin interface:

interface CrustPlugin {
  name?: string;
  setup?: (context: SetupContext, actions: SetupActions) => void | Promise<void>;
  middleware?: PluginMiddleware;
}

SetupContext

Context available during plugin setup:

interface SetupContext {
  readonly argv: readonly string[];
  readonly rootCommand: CommandNode;
  readonly state: PluginState;
}

SetupActions

Actions available during plugin setup:

interface SetupActions {
  addFlag(command: CommandNode, name: string, def: FlagDef): void;
  addSubCommand(parent: CommandNode, name: string, command: CommandNode): void;
}
  • addFlag — inject a flag into a command's effectiveFlags object (localFlags is unchanged). If the command already has a flag with the same name, the plugin's flag overrides it. crust build emits a warning when this happens.
  • addSubCommand — inject a subcommand into a command's subCommands record. If the parent already has a subcommand with the same name (user-defined), the call is skipped — user definitions always take priority. crust build emits a warning when this happens.

MiddlewareContext

Context available during middleware execution:

interface MiddlewareContext {
  readonly argv: readonly string[];
  readonly rootCommand: CommandNode;
  readonly state: PluginState;
  route: Readonly<CommandRoute> | null;
  input: ParseResult | null;
}

PluginMiddleware

Middleware function type:

type PluginMiddleware = (
  context: MiddlewareContext,
  next: () => Promise<void>,
) => void | Promise<void>;

Other Types

ValueType

Supported type literals for args and flags:

type ValueType = "string" | "number" | "boolean" | "url" | "path" | "json";

The three formatted built-ins resolve to:

  • "url"URL instance via new URL(raw)
  • "path" — absolute string via path.resolve(process.cwd(), expand(raw))
  • "json"unknown via JSON.parse(raw)

See Types for semantics, error modes, and shell-completion behaviour.

Resolve<T>

Maps a ValueType literal to its runtime TypeScript type. Delegates to ResolvePrimitive<T> from @crustjs/utils for the three base types.

type Resolve<T extends ValueType> = T extends "string"
  ? string
  : T extends "number"
    ? number
    : T extends "boolean"
      ? boolean
      : T extends "url"
        ? URL
        : T extends "path"
          ? string
          : T extends "json"
            ? unknown
            : never;

ResolveBaseType<F>

Resolves the inferred runtime type for a flag/arg definition. When a parse escape hatch is declared, the inferred type is ReturnType<typeof parse>; otherwise it delegates to Resolve<F["type"]>.

type ResolveBaseType<F> = F extends { parse: (raw: string) => infer R }
  ? R
  : F extends { type: infer T extends ValueType }
    ? Resolve<T>
    : never;

Compile-Time Validation Types

ValidateVariadicArgs<A>

Per-arg validation used by .args(). Enforces that only the last positional argument can use variadic: true.

type ValidateVariadicArgs<A extends readonly object[]> = /* ... */;
// On error, the offending arg gets a branded property:
// { readonly FIX_VARIADIC_POSITION: "Only the last positional argument can be variadic" }

ValidateFlagAliases<F>

Per-flag validation used by .flags(). Enforces that aliases do not collide with other flag names or aliases.

type ValidateFlagAliases<F extends Record<string, unknown>> = /* ... */;
// On error, the offending flag gets a branded property:
// { readonly FIX_ALIAS_COLLISION: `Alias "${...}" collides with another flag name or alias` }

ValidateNoPrefixedFlags<F>

Per-flag validation used by .flags(). Reserves the no- prefix for boolean negation by rejecting flag names and aliases that start with no-.

type ValidateNoPrefixedFlags<F extends Record<string, unknown>> = /* ... */;
// On error, the offending flag gets a branded property:
// { readonly FIX_NO_PREFIX: `Flag name "${...}" must not start with "no-"; ...` }
// or
// { readonly FIX_NO_PREFIX: `Alias "${...}" must not start with "no-"; ...` }

On this page