func compiles application declarations into an executable structure, then runs one invocation for each argv array it receives. Decorators only describe relationships among Modules, Commands, fields, and Handlers. Instance creation and method calls happen at runtime.
createApp() targets a process application: after compilation, bootstrap() runs once and updates the process exit code. createInvoker() also compiles once, but its invoke() method may be called repeatedly and returns a separate result and exit code each time.
-
Compile application
Expand Modules, validate declarations, and build the command graph and option rules for each node.
-
Parse invocation
Parse argv, match a command path, and select one Handler or option action.
-
Prepare scope
Create the Context and dependency container, then activate the required Modules and Providers.
-
Execute pipeline
Bind and validate fields, then call the selected entry point.
-
Handle and dispose
Handle errors, settle the result and exit code, and dispose instances created for the invocation.
Compile the application
When createApp() or createInvoker() is called, func starts at the root Module, expands its imports, checks Provider declarations and visibility, and collects Commands and options. Command paths, Handler paths, and aliases are then compiled into a command graph.
Application-structure defects—duplicate tokens, invalid exports, circular imports, or conflicting paths—fail immediately in this phase. Compilation generates and validates structure only; it does not instantiate Modules, Commands, or Providers.
See Modules for the full dependency and visibility rules.
Parse an invocation
Each invocation receives the argv string array after the shell has already processed it. func parses argv against the options visible at each candidate command node, then matches the remaining positional input against the command graph. A visible option may therefore appear before or after the Command path. Everything after -- is preserved as positional input.
Entry selection follows this general order:
- Choose the unique named Command or Handler path with the deepest match.
- If there is no named entry, try a root-level option action.
- Then try
@CommandMajor,@CommandMissing, or the root command’s default behavior.
One invocation selects one entry. Multiple action options, equally specific candidates, and options invisible at the target node produce parse errors; registration order is never used as a tie-breaker.
Ordinary field options supply data. Actions declared by @Handler({ flag }) or @OptionCommand() replace the business entry point with their own. When an action is selected, unrelated business fields are not validated, so deploy --help does not fail merely because required deploy inputs are absent.
Create the invocation scope
func creates a fresh Context and runtime dependency container for every invocation. The root Module always belongs to the active scope. Once a Command matches, the Module that directly owns that Command is activated as well. Intermediate Modules used only to connect imports do not become execution scopes automatically.
Class and factory Providers are created on first request and reused within the current invocation; the next invocation receives fresh instances. Value Providers return the value supplied at registration time. Providers unused by the selected entry are never created.
Modules initialize before the execution entry. Other dependencies run onInit() when first instantiated. An option Provider receives bound field values before its onInit(), so its constructor must not assume argv has already been assigned to fields.
Execute the selected entry
After a successful parse, interceptors wrap the invocation from outermost to innermost: application → owner Module → Command. When the inner work completes, its result unwinds through those interceptors in reverse order.
The core sequence for an ordinary Command is:
- Construct the Command and its constructor dependencies.
- Apply field values and defaults, then run required checks, validators, and cross-field constraints.
- Create the frozen
Argssnapshot and run the Command’sonInit(). - Resolve Handler parameters and invoke the one selected Handler.
A Handler may return synchronously or through a Promise; both follow the same result pipeline. Args contains raw argv, actual and canonical command paths, remaining positional input, and a normalized option snapshot. Context provides the current command, IO, cwd, signal, result-output mode, and exit code.
See Handler parameters for parameter injection and Errors and lifecycle for initialization and disposal behavior.
Handle errors and dispose resources
Runtime errors go to the onError hooks that are active for the invocation. After a Command is selected, propagation runs Command → direct owner Module → root Module. Returning normally from onError marks the error handled; throwing passes it outward. A parse failure has no active Command or owner Module and therefore reaches the root Module only.
An ordinary runtime error left unhandled is written to stderr and ends the invocation with exit code 1. onError may adjust the exit code through Context and control what is written to the output streams. Error handling documents the complete propagation contract.
After business execution, func visits activated Commands, Modules, and Providers for disposal. Commands and Modules are disposed in reverse activation order, while Providers are disposed in reverse creation order.