@forge/cli-shared
9.5.1-next.59.6.0-next.6
out/ui/command-line-ui.js~
out/ui/command-line-ui.jsModified+100−6
Index: package/out/ui/command-line-ui.js
===================================================================
--- package/out/ui/command-line-ui.js
+++ package/out/ui/command-line-ui.js
@@ -1,8 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CommandLineUI = void 0;
const tslib_1 = require("tslib");
+const fs = tslib_1.__importStar(require("fs"));
+const tty = tslib_1.__importStar(require("tty"));
const cli_table3_1 = tslib_1.__importDefault(require("cli-table3"));
const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
const inquirer_autocomplete_prompt_1 = tslib_1.__importDefault(require("inquirer-autocomplete-prompt"));
const ora_1 = tslib_1.__importDefault(require("ora"));
@@ -41,14 +43,17 @@
addedProgressPadding = false;
static ANSI_MOVE_CURSOR_UP_ONE = '\x1B[1A';
static ANSI_CLEAR_CURSOR_LINE = '\x1B[2K';
static NON_TTY_ENV_DEFAULT_COLUMNS = 100;
+ static STDIN_CHUNK_BYTES = 256;
+ static STDIN_RETRY_MS = 10;
+ static STDIN_READ_TIMEOUT_MS = 30_000;
constructor(verbose, statsigService, spinner, logger, customEffectsEnabled) {
this.verbose = verbose;
this.customEffectsEnabled = customEffectsEnabled;
this.spinner = spinner || (0, ora_1.default)({ discardStdin: false });
this.logger = logger || console;
- this.promptInternal = inquirer_1.default.createPromptModule({ skipTTYChecks: false });
+ this.promptInternal = this.createPromptModule();
this.statsigService = statsigService || null;
this.registerCustomUIElements();
}
setStatsigService(statsigService) {
@@ -237,15 +242,15 @@
}
emptyLine() {
this.log('');
}
- async confirm(message) {
+ async confirm(message, defaultChoice = false) {
const { choice } = await this.prompt([
{
type: 'confirm',
name: 'choice',
message,
- default: false
+ default: defaultChoice
}
]);
return choice;
}
@@ -254,8 +259,46 @@
}
promptForSecret(message) {
return this.promptForString(message, true);
}
+ readFromStdin() {
+ const chunk = Buffer.alloc(CommandLineUI.STDIN_CHUNK_BYTES);
+ const collected = [];
+ const deadline = Date.now() + CommandLineUI.STDIN_READ_TIMEOUT_MS;
+ for (;;) {
+ let bytesRead;
+ try {
+ bytesRead = fs.readSync(0, chunk, 0, chunk.length, null);
+ }
+ catch (err) {
+ if (err.code === 'EAGAIN') {
+ if (Date.now() >= deadline) {
+ throw new Error('Failed to read from stdin: timed out waiting for input');
+ }
+ CommandLineUI.sleepSync(CommandLineUI.STDIN_RETRY_MS);
+ continue;
+ }
+ if (err.code === 'EOF') {
+ break;
+ }
+ throw new Error(`Failed to read from stdin: ${err.message}`);
+ }
+ if (bytesRead === 0) {
+ break;
+ }
+ const read = chunk.subarray(0, bytesRead);
+ const newlineIndex = read.indexOf(0x0a);
+ if (newlineIndex !== -1) {
+ collected.push(Buffer.from(read.subarray(0, newlineIndex)));
+ break;
+ }
+ collected.push(Buffer.from(read));
+ }
+ return Buffer.concat(collected).toString('utf8').trim();
+ }
+ static sleepSync(milliseconds) {
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
+ }
async promptForList(message, choices, config, pageSize) {
const formattedChoices = config?.format ? choices.map((choice) => config?.format?.(choice)) : choices;
const { choice } = await this.prompt([
{
@@ -515,11 +558,62 @@
}
]);
return text;
}
- registerCustomUIElements() {
- this.promptInternal.registerPrompt('multiple-choice-table', multiple_table_prompt_1.MultipleChoiceTablePrompt);
- this.promptInternal.registerPrompt('single-choice-table', single_table_prompt_1.SingleChoiceTablePrompt);
+ createPromptModule() {
+ if (process.stdin.isTTY) {
+ return inquirer_1.default.createPromptModule({ skipTTYChecks: false });
+ }
+ return this.createTtyBackedPromptModule();
+ }
+ createTtyBackedPromptModule() {
+ let resolvedPromptModule;
+ const getOrCreatePromptModule = () => {
+ if (resolvedPromptModule)
+ return resolvedPromptModule;
+ resolvedPromptModule = this.openTtyPromptModule();
+ this.registerCustomUIElements(resolvedPromptModule);
+ return resolvedPromptModule;
+ };
+ return Object.assign((questions, initialAnswers) => getOrCreatePromptModule()(questions, initialAnswers), inquirer_1.default.createPromptModule({ skipTTYChecks: true }));
+ }
+ openTtyPromptModule() {
+ let ttyFd;
+ try {
+ ttyFd = fs.openSync('/dev/tty', 'r+');
+ }
+ catch {
+ }
+ if (ttyFd === undefined) {
+ return inquirer_1.default.createPromptModule({ skipTTYChecks: false });
+ }
+ let ttyInput;
+ try {
+ ttyInput = new tty.ReadStream(ttyFd);
+ }
+ catch (err) {
+ try {
+ fs.closeSync(ttyFd);
+ }
+ catch {
+ }
+ throw err;
+ }
+ const ttyPromptModule = inquirer_1.default.createPromptModule({ input: ttyInput, skipTTYChecks: true });
+ return Object.assign((questions, initialAnswers) => {
+ ttyInput.ref();
+ return ttyPromptModule(questions, initialAnswers).then((answers) => {
+ ttyInput.unref();
+ return answers;
+ }, (err) => {
+ ttyInput.unref();
+ throw err;
+ });
+ }, ttyPromptModule);
+ }
+ registerCustomUIElements(target = this.promptInternal) {
+ target.registerPrompt('multiple-choice-table', multiple_table_prompt_1.MultipleChoiceTablePrompt);
+ target.registerPrompt('single-choice-table', single_table_prompt_1.SingleChoiceTablePrompt);
this.promptInternal.registerPrompt('autocomplete', inquirer_autocomplete_prompt_1.default);
}
formatKeyValueList(items, indent, addNewLine) {
const formattedItems = items.map(({ key, value }) => {