@catladder/cli
Version:
Panter cli tool for cloud CI/CD and DevOps
69 lines (62 loc) • 1.98 kB
text/typescript
import type {
CommandContext,
CommandDef,
InputDef,
InputResultType,
InputsSchema,
OptionalInput,
} from "../core/types";
/**
* Base class for CommandContext implementations.
* Handles caching, the `get()` method, and `promptDirect` delegation.
* Subclasses only implement the adapter-specific resolution logic.
*/
export abstract class BaseContext<TInputs extends InputsSchema>
implements CommandContext<TInputs>
{
private cache = new Map<string, Promise<any>>();
constructor(protected readonly command: CommandDef<TInputs>) {}
abstract log(message: string): void;
abstract confirm(message: string): Promise<boolean>;
/**
* Resolve a single input value. Called once per input (result is cached).
* Implementations check pre-supplied values, prompt interactively, use defaults, or throw.
*/
protected abstract resolveInput<P extends InputDef>(
name: string,
spec: P,
): Promise<InputResultType<P>>;
get<K extends keyof TInputs & string>(
name: K,
): Promise<OptionalInput<TInputs[K]>> {
if (!this.cache.has(name)) {
const spec = this.command.inputs[name];
if (!spec) {
return Promise.reject(
new Error(
`Input "${name}" is not declared in command "${this.command.name}"`,
),
);
}
// For optional inputs (required: false), catch MissingInputError
// and return undefined instead of prompting
if (spec.required === false) {
this.cache.set(
name,
this.resolveInput(name, spec).catch((err) => {
if (err?.name === "MissingInputError") return undefined;
throw err;
}),
);
} else {
this.cache.set(name, this.resolveInput(name, spec));
}
}
return this.cache.get(name)!;
}
async promptDirect<P extends InputDef>(
spec: P & { name: string },
): Promise<InputResultType<P>> {
return this.resolveInput(spec.name, spec);
}
}