Types
Public command definitions, snapshots, Command Action contexts, Contexts, and Extensions.
Every type on this page is a type export of @crustjs/core. Tables are generated from the owning declarations and omit some generic constraints and overloads; follow each section's source link for the exact contract. Most applications never name these types: .args(), .flags(), .provide(), and .action() infer them.
Definition types
Declared in types.ts.
ValueType
| Value | Runtime value |
|---|---|
string | string |
number | number |
boolean | boolean |
url | URL |
path | Absolute string, with ~ expanded |
json | unknown from JSON.parse() |
ArgDef
ArgsDef is readonly ArgDef[]. Core-value argument definitions share these fields:
Prop
Type
const args = [
{ name: "target", type: "string", choices: ["browser", "node"] },
{ name: "files", type: "path", variadic: true },
] as const satisfies ArgsDef;A core variadic value is always an array. A required core variadic without a default resolves to a nonempty tuple; an omitted variadic with a scalar default resolves to [default]. parse runs once per element.
A schema-backed argument has name, optional description and variadic, and a Standard Schema that receives string | undefined (or string[] when variadic). The schema owns coercion, defaults, requiredness, choices, and validation, so type, required, default, choices, and parse are unavailable. Its output type reaches the Command Action.
const args = [{ name: "port", schema: z.coerce.number().int().min(1) }] as const satisfies ArgsDef;FlagDef
FlagsDef is Record<string, FlagDef>. Core-value flag definitions support:
Prop
Type
const flags = {
verbose: { type: "boolean", short: "v" },
port: { type: "number", default: 3000 },
token: { type: "string", env: { name: "MYAPP_TOKEN" }, required: true },
tags: {
type: "string",
multiple: true,
delimiter: ",",
env: { name: "MYAPP_TAGS", delimiter: ":" },
},
} satisfies FlagsDef;env is consulted by terminal parsing only when the flag has no argv occurrence: argv wins over env, and env wins over default. The env value goes through the same conversion as an argv token and satisfies required. Help, man pages, and generated skills show [env: NAME], never the value. Structured run() input ignores env.
delimiter splits string argv occurrences; env.delimiter independently splits environment text. Both require multiple: true and drop empty segments. Boolean argv switches are never split.
Schema-backed flags set type: "string" | "boolean" only to declare whether a value token follows, plus schema and the syntax fields description, short, aliases, multiple, env, delimiter, and (boolean only) noNegate. The schema receives the raw string | undefined or boolean | undefined; with multiple: true it receives an array when the flag appears and undefined when omitted.
const flags = {
port: { type: "string", schema: z.coerce.number().int().min(1) },
enabled: { type: "boolean", schema: z.boolean().default(false) },
} satisfies FlagsDef;NamedFlagDef
A FlagDef carrying its name: the shape accepted by command and Context .flags(...defs), and defineFlag().
defineFlag and defineArg
Declare a definition separately from .flags() or .args() while keeping its literal types, without as const:
import { defineArg, defineFlag } from "@crustjs/core";
const verbose = defineFlag("verbose", { type: "boolean", short: "v" });
const target = defineArg("target", { type: "string", required: true });Invalid literal names, defaults, and parsers fail typecheck. Dynamic definitions are checked where they are consumed. UnnamedArgDef is the defineArg() definition without its name.
MergeFlags
MergeFlags<Base, Override> is Base & Override for two flag definition maps.
CommandMeta
Prop
Type
version comes from new Crust(name, { version }) and appears on the root snapshot. hidden removes a command from help, completion, man-page, typo-suggestion, and skill listings without disabling routing. sections are rendered by help and man after their built-in sections.
CommandSectionInput
Prop
Type
A plain-text section, optionally scoped with only or except (never both) to a nonempty list of SectionConsumers. A SectionConsumer is an ExtensionId or any value exposing one at .id, such as the help, man, and skill Extension factories. SectionAudience is the only / except part alone.
import { help } from "@crustjs/extensions";
const sections = [
{ title: "Examples", body: "my-cli deploy prod" },
{ title: "Agent notes", body: "Prefer --json output.", except: [help] },
] satisfies CommandSectionInput[];Renderers filter sections for their audience first, then merge entries with the same exact, case-sensitive title, joining bodies with a newline. CommandSection is the validated form stored on snapshot meta.sections, with consumers resolved to ExtensionIds.
Command Snapshots
Command Snapshots are frozen, serializable descriptions of the command tree. They omit Command Actions, parsers, schemas, and Context functions. Command Actions, Extension hooks, and COMMAND_NOT_FOUND details receive snapshots. Declared in snapshot.ts.
CommandSnapshot
Prop
Type
flags contains effective local and Context-owned flags. meta.sections holds each command-authored, Context-owned, and Extension-contributed section separately; same-title merging happens only when a renderer consumes them.
ArgSnapshot
Prop
Type
FlagSnapshot
Prop
Type
URL defaults become strings. env records the variable name and delimiter, never the value. Object-valued JSON defaults are shared references, not copies; treat them as immutable.
Typed invocation
Declared in command/typed-run.ts. These types back run(), at(), and the @crustjs/testing helpers.
| Type | Role |
|---|---|
CommandTree | Compile-time command tree accumulated by .add() and .command() |
CommandPath<Tree> | Every valid path through the tree, including the root path [] |
CommandShape / CommandShapeAt | Argument, flag, subtree, and result types of the command a path selects |
RunInput<Shape> | Structured args / flags / raw input; omits sections the shape lacks |
InputArgs / InputFlags | Pre-parse input value types used by RunInput |
RunInputArguments / RunArguments | Argument tuples that make input optional when nothing is required |
CommandHandle<Shape> | Path-bound invoker returned by at(): a readonly path and run() |
AnyCrust | Any completed application; broad run() resolves to RunOutcome<unknown> |
import type { CommandPath, CommandShapeAt, RunInput } from "@crustjs/core";
type Path = CommandPath<(typeof app)["_types"]["tree"]>; // e.g. readonly [] | readonly ["greet"]
type GreetInput = RunInput<CommandShapeAt<(typeof app)["_types"]["shape"], readonly ["greet"]>>;Crust._types is a type-only view of an application's inferred flags, arguments, Contexts, tree, shape, and root metadata (rootMeta); it has no runtime value. AnyCrust has no authoring methods; use it for inspection, execute(), and genuinely dynamic run() calls.
JSON inputs accept JSON primitives, structurally JSON-compatible objects, and arrays; Date, bigint, and functions are excluded. url inputs accept URL.
RunOutcome<Result>
The inferred return value of run(). Every branch carries readonly captured stdout and stderr. Narrow status before reading its branch-specific field:
completed: the action's awaitedresult.handled: the handling Extension id,by.failed: the original thrownerror.
See the source declaration for the union and run() for a narrowing example and capture behavior. Extension postRun hooks receive InvocationOutcome instead, which carries no output or result.
Parser boundary types
For parser integrations and tooling. ParseResult<A, F> is the syntax-parsed input before required-value and schema validation: excessArgs holds unconsumed positionals and rawArgs holds tokens after --. ParsedArgValue and ParsedFlagValue are its erased value types. ValidatedInput<A, F> is the validated input passed to Command Actions.
Invocation I/O
InvocationIO
Prop
Type
The same callbacks are passed to the Command Action and every Context setup in the invocation.
InvocationOptions
Prop
Type
The optional last argument of run(path, input?, options?) and handle.run(input?, options?).
ExecuteOptions
Prop
Type
The optional argument of execute(options?).
Command Action context
CrustCommandContext<A, F, Ctx>
Prop
Type
ctx is a lazy ContextBag of the Contexts available on the command path. rootCommand is the root snapshot, including Extension contributions.
Context types
Declared in api/context.ts. See Contexts for worked examples and Context lifecycle for cleanup rules.
ContextBuilder
Prop
Type
defineContext(name) starts an immutable definition. .use(), .flags(), and .sections() append and return new frozen builders; the original is unchanged. The terminal .setup(callback) returns a ContextFactory. An unfinished builder cannot be provided or used as a factory.
.use(...factories)declares the dependencies setup reads fromctx. Literal lists retain exact types; widened arrays declare open dependencies checked at runtime during composition. Empty calls are allowed..flags(...defs)declares owned flags. Definitions are copied, and spellings are checked against earlier calls both by TypeScript and when the method runs..sections(...sections)declaresCommandSectionInputdocumentation, validated and frozen at declaration. It documents only the providing command (the root for Extension-provided Contexts); duplicate titles from other Contexts or command sections fail preparation. See Context-owned documentation.- Setup receives types from the preceding declarations. Annotate its second parameter for factory options without restating
ctx, flag, or return types:
const api = defineContext("api")
.use(config)
.setup(async ({ ctx }, options: { timeout: number } = { timeout: 1000 }) => ({
config: await ctx.config,
timeout: options.timeout,
}));
api(); // default options
api({ timeout: 5000 });Context factories accept at most one options value. It may be omitted whenever its type accepts undefined, including optional or defaulted setup parameters and explicit T | undefined unions. Unlike an Extension's .factory() callback, setup runs lazily during invocation, not when the factory is called.
ContextFactory
.setup() returns a ContextFactory. Call it to create a ContextInstance, even when it takes no options. factory.contextName is the ctx property name.
const config = defineContext("config").setup(() => ({ region: "eu" }));
app.provide(config()); // runs setup lazily in each invocation
app.provide(config.of({ region: "us" })); // precomputed value, e.g. a test double.of(value) creates an instance with no dependencies that keeps the factory's owned flags and sections. Every invocation that pulls it disposes value if it is disposable (see replacing a Context in tests).
ContextSetup and ContextBag
Prop
Type
ContextSetup<Flags, Deps> describes setup's first parameter; factory options are its second parameter. flags contains validated values from .flags(), and .use() determines the setup's ctx bag. A ContextBag<Deps> maps each name to a Promise of its value: reading a property starts memoized construction, so destructuring the bag starts every construction immediately.
Setup also receives the invocation's stdout, stderr, signal, and defer(cleanup).
ContextInstance
Prop
Type
Attach instances with .provide(). Prefer inferred instance types over explicitly annotated ContextInstance holders, which cannot carry owned-flag and dependency state.
FactoryValueOf<F>
The awaited value a factory produces: FactoryValueOf<typeof config> is what await ctx.config resolves to. Use it to type helpers that take a Context value.
ContextMap and MergeContext
ContextMap is the upper bound for Context registries inferred by .provide(); consumer-defined shapes need no index signature. MergeContext<A, B> is A & B. AnyContextFactory accepts any Context factory.
Extension types
Declared in api/extension.ts. See Extensions for authoring.
ExtensionBuilder
Prop
Type
defineExtension(id) starts an immutable fluent definition; defineExtension<MetaKeys>(id) also requires root metadata. Every method except .factory() returns a new frozen handle, and every handle is an Extension accepted by .extend(). There is no final build step.
- Collection methods append on repeated calls.
.use()declares Contexts the hooks consume..provide()installs Context instances application-wide regardless of chain position; hooks see them only if also declared with.use()..flags()owns flags..add()contributesdefineCommand()definitions at the root. - Lifecycle setters replace the previous callback:
.preRun(),.postRun(), and.onError()set the hooks;.sections(snapshot)returns section contributions;.build(ctx)returnsBuildArtifactsfor build tooling to write. - Each callback is typed by the declarations made before it in the chain.
- Flag spellings and contributed command names are checked for collisions by TypeScript and when the method runs.
.factory(define) returns an ExtensionFactory.
ExtensionFactory
A callable (...args) => Extension with the handle's id. .factory((extension, ...args) => ...) keeps the callback's parameters after the handle, including optional and defaulted ones, and infers flags, dependencies, providers, and commands. define must return an Extension with the same id, typically a chain on the received handle; otherwise the call throws a DEFINITION error (factory-id-mismatch).
When annotating a published factory, preserve literal tuples for precise typed invocation, and preserve its metadata keys if it requires root metadata. If only some calls require metadata, annotate the factory with overloads; see required root metadata. See authoring an Extension.
Extension
Prop
Type
A frozen handle created only by defineExtension(). Spreading or copying it does not redefine the Extension. Mint the branded ExtensionId with defineExtensionId(), which requires a non-empty, trimmed string.
RootMetaKey
keyof RootCommandMeta: the keys defineExtension<MetaKeys>(id) accepts as its only type argument. Required keys become non-optional in the root snapshots passed to hooks, build, and sections, and .extend() checks at compile time that the root supplies them. See required root metadata.
ExtensionFlagDef and NamedExtensionFlagDef
A FlagDef (or NamedFlagDef) plus recursive?: boolean. recursive defaults to true; false contributes the flag only to the root.
InferExtensionFlags<Defs>
The flag values an Extension's hooks see. They are pre-validation values, not schema outputs: a required flag can still be absent, and a non-recursive flag can be absent on descendants.
ExtensionContext
Prop
Type
args and flags are syntax-parsed values. Hooks do not change Command Action types.
ExtensionHooks
Prop
Type
preRunruns in.extend()order after routing and input binding, before validation. Returnctx.handled()to end the invocation successfully. Pulling a flag-owning Context here rejects, because its flags are not validated yet.onErrorruns forexecute()only. Returntrueafter rendering to stop the chain, or a falsy value to fall through to Core's renderer.postRunruns in reverse.extend()order after every completed, handled, or failed invocation that reached hooks, likefinally. Contexts remain available until it completes. Preparation, routing, and parse failures skip it.
See propagation boundaries for hook timing on failure.
Handled
Prop
Type
The opaque token returned by ctx.handled(). Return that exact value from preRun.
InvocationOutcome
Prop
Type
postRun receives completed, handled with the short-circuiting Extension's by, or failed with the thrown value. On failed, by names the Extension whose onError claimed the failure.
ExtensionSectionContribution
Prop
Type
One entry returned by .sections(snapshot): a section plus the canonical command path it targets, relative to the root ([] is the root). Contributions append to the target's meta.sections in Extension registration order. The callback receives a snapshot without Extension-contributed sections. Blank or multi-line titles, blank bodies, empty audiences, both only and except, and unknown command paths fail preparation with a DEFINITION error.
ExtensionBuildContext
Prop
Type
Build hooks run in registration order, and each receives a snapshot refreshed after earlier hooks' files were written; a hook does not see its own outputs. There is no output directory: return files instead. During this phase, resolveArtifactDir(name) points at the entry's build output directory.
BuildFile
Prop
Type
path is relative to the build output directory. Build tooling rejects absolute or escaping paths and paths that collide with an earlier file, compared case-insensitively, including a file and a directory with the same name.
BuildArtifacts
Prop
Type
The files one build hook returns; [] emits nothing. Put files under an Extension-specific top-level directory such as acme-manifest/manifest.json: only top-level directories are copied into packages, and a loose top-level file is not shipped.
BuildReport
Prop
Type
One entry per executed build hook, in order, listing the files build tooling wrote for it. crust build records it per bin entry in .crust/manifest.json. Files a hook writes elsewhere itself are not reported.
Error types
See Errors for CrustError, CrustErrorCode, CrustErrorDetails, CrustErrorDetailsMap, CrustErrorJson (the .toJSON() shape), and the detail interfaces.