@catladder/cli
Version:
Panter cli tool for cloud CI/CD and DevOps
205 lines (204 loc) • 6.66 kB
JavaScript
;
var __createBinding = this && this.__createBinding || (Object.create ? function (o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = {
enumerable: true,
get: function () {
return m[k];
}
};
}
Object.defineProperty(o, k2, desc);
} : function (o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
});
var __setModuleDefault = this && this.__setModuleDefault || (Object.create ? function (o, v) {
Object.defineProperty(o, "default", {
enumerable: true,
value: v
});
} : function (o, v) {
o["default"] = v;
});
var __importStar = this && this.__importStar || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createTerminalIO = exports.createTerminalContext = void 0;
const types_1 = require("../core/types");
const baseContext_1 = require("./baseContext");
/**
* Resolve choices from a choices function.
*/
async function resolveChoices(choices, ctx) {
const raw = await choices(ctx);
return raw.map(c => typeof c === "string" ? {
name: c,
value: c
} : c);
}
/**
* Prompt the user interactively using @inquirer/prompts.
* The UI is determined by the value type + presence of choices.
*/
async function promptInteractive(spec, ctx) {
const inquirer = await Promise.resolve().then(() => __importStar(require("@inquirer/prompts")));
switch (spec.type) {
case "string":
{
if (spec.choices) {
const choices = await resolveChoices(spec.choices, ctx);
return inquirer.select({
message: spec.message,
choices
});
}
return inquirer.input({
message: spec.message,
default: spec.default
});
}
case "string[]":
{
if (spec.choices) {
const choices = await resolveChoices(spec.choices, ctx);
return inquirer.checkbox({
message: spec.message,
choices
});
}
const raw = await inquirer.input({
message: spec.message
});
return raw ? raw.split(",").map(s => s.trim()) : [];
}
case "number":
return inquirer.number({
message: spec.message,
default: spec.default
});
case "boolean":
return inquirer.confirm({
message: spec.message,
default: spec.default
});
default:
throw new Error(`Unsupported input type: ${spec.type}`);
}
}
class TerminalContext extends baseContext_1.BaseContext {
constructor(command, options) {
super(command);
this.options = options;
}
log(message) {
console.log(message);
}
async confirm(message) {
var _a;
if (this.options.yes) return true;
const isInteractive = (_a = this.options.interactive) !== null && _a !== void 0 ? _a : process.stdin.isTTY === true;
if (!isInteractive) return true;
const inquirer = await Promise.resolve().then(() => __importStar(require("@inquirer/prompts")));
return inquirer.confirm({
message
});
}
async resolveInput(name, spec) {
const {
cliOptions = {},
jsonInputs = {},
interactive
} = this.options;
const isInteractive = interactive !== null && interactive !== void 0 ? interactive : process.stdin.isTTY === true;
// Helper: validate a pre-supplied value against choices if they exist
const validateAgainstChoices = async value => {
if (!("choices" in spec) || !spec.choices) return;
const choices = await resolveChoices(spec.choices, this);
const validValues = choices.map(c => c.value);
if (Array.isArray(value)) {
const invalid = value.filter(v => !validValues.includes(v));
if (invalid.length > 0) {
throw new Error(`Invalid value(s) for "${name}": ${invalid.join(", ")}. Valid choices: ${validValues.join(", ")}`);
}
} else if (!validValues.includes(value)) {
throw new Error(`Invalid value for "${name}": "${value}". Valid choices: ${validValues.join(", ")}`);
}
};
// 1. CLI flag or positional arg
if (name in cliOptions && cliOptions[name] !== undefined) {
await validateAgainstChoices(cliOptions[name]);
return cliOptions[name];
}
// 2. JSON inputs
if (name in jsonInputs && jsonInputs[name] !== undefined) {
await validateAgainstChoices(jsonInputs[name]);
return jsonInputs[name];
}
// 3. Default (check before prompting, so optional inputs with defaults don't prompt)
if ("default" in spec && spec.default !== undefined) {
return spec.default;
}
// 4. Optional inputs: don't prompt, throw so baseContext returns undefined
if (spec.required === false) {
throw new types_1.MissingInputError(name, spec.message);
}
// 5. Interactive prompt
if (isInteractive) {
return promptInteractive({
...spec,
name
}, this);
}
// 6. Throw
throw new types_1.MissingInputError(name, spec.message);
}
}
/**
* Create a CommandContext for terminal usage.
*/
function createTerminalContext(command, options = {}) {
return new TerminalContext(command, options);
}
exports.createTerminalContext = createTerminalContext;
/**
* Create a bare IO instance for terminal helper functions.
*/
function createTerminalIO(options = {}) {
return {
log(message) {
console.log(message);
},
async confirm(message) {
var _a;
if (options.yes) return true;
const isInteractive = (_a = options.interactive) !== null && _a !== void 0 ? _a : process.stdin.isTTY === true;
if (!isInteractive) return true;
const inquirer = await Promise.resolve().then(() => __importStar(require("@inquirer/prompts")));
return inquirer.confirm({
message
});
},
async promptDirect(spec) {
var _a;
const isInteractive = (_a = options.interactive) !== null && _a !== void 0 ? _a : process.stdin.isTTY === true;
if (isInteractive) {
return promptInteractive(spec, null);
}
if ("default" in spec && spec.default !== undefined) {
return spec.default;
}
throw new types_1.MissingInputError(spec.name, spec.message);
}
};
}
exports.createTerminalIO = createTerminalIO;