@topcli/prompts 3.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -4
- package/dist/index.d.mts +140 -0
- package/dist/index.mjs +1171 -0
- package/package.json +10 -10
- package/dist/index.cjs +0 -1312
- package/dist/index.d.cts +0 -121
- package/dist/index.d.ts +0 -121
- package/dist/index.js +0 -1271
package/dist/index.cjs
DELETED
|
@@ -1,1312 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
|
|
30
|
-
// src/index.ts
|
|
31
|
-
var index_exports = {};
|
|
32
|
-
__export(index_exports, {
|
|
33
|
-
PromptAgent: () => PromptAgent,
|
|
34
|
-
confirm: () => confirm,
|
|
35
|
-
multiselect: () => multiselect,
|
|
36
|
-
question: () => question,
|
|
37
|
-
select: () => select,
|
|
38
|
-
transformers: () => transformers,
|
|
39
|
-
validators: () => validators
|
|
40
|
-
});
|
|
41
|
-
module.exports = __toCommonJS(index_exports);
|
|
42
|
-
var import_node_events2 = require("events");
|
|
43
|
-
|
|
44
|
-
// src/validators.ts
|
|
45
|
-
function required() {
|
|
46
|
-
return {
|
|
47
|
-
validate: (input) => {
|
|
48
|
-
const isValid2 = Array.isArray(input) ? input.length > 0 : Boolean(input);
|
|
49
|
-
return isValid2 ? null : { isValid: isValid2, error: "required" };
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
function isValid(result) {
|
|
54
|
-
if (typeof result === "object") {
|
|
55
|
-
return result?.isValid !== false;
|
|
56
|
-
}
|
|
57
|
-
if (typeof result === "string") {
|
|
58
|
-
return result.length === 0;
|
|
59
|
-
}
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
function isValidTransformation(result) {
|
|
63
|
-
return typeof result === "object" && result.isValid === true;
|
|
64
|
-
}
|
|
65
|
-
function resultError(result) {
|
|
66
|
-
if (typeof result === "object") {
|
|
67
|
-
return result.error;
|
|
68
|
-
}
|
|
69
|
-
return result;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// src/prompt-agent.ts
|
|
73
|
-
var kPrivateInstancier = /* @__PURE__ */ Symbol("instancier");
|
|
74
|
-
var PromptAgent = class _PromptAgent {
|
|
75
|
-
/**
|
|
76
|
-
* The prompts answers queue.
|
|
77
|
-
* When not empty, any prompt will be answered by the first answer in this list.
|
|
78
|
-
*/
|
|
79
|
-
nextAnswers = [];
|
|
80
|
-
/**
|
|
81
|
-
* The shared PromptAgent.
|
|
82
|
-
*/
|
|
83
|
-
static #this;
|
|
84
|
-
static agent() {
|
|
85
|
-
return this.#this ??= new _PromptAgent(kPrivateInstancier);
|
|
86
|
-
}
|
|
87
|
-
constructor(instancier) {
|
|
88
|
-
if (instancier !== kPrivateInstancier) {
|
|
89
|
-
throw new Error("Cannot instanciate PromptAgent, use PromptAgent.agent() instead");
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Programmatically set the next answer for any prompt (`question()`, `confirm()`, `select()`)
|
|
94
|
-
*
|
|
95
|
-
* This is useful for testing.
|
|
96
|
-
*
|
|
97
|
-
* @example
|
|
98
|
-
* ```js
|
|
99
|
-
* const promptAgent = PromptAgent.agent();
|
|
100
|
-
* promptAgent.nextAnswer("toto");
|
|
101
|
-
*
|
|
102
|
-
* const input = await question("what is your name?");
|
|
103
|
-
* assert.equal(input, "toto");
|
|
104
|
-
* ```
|
|
105
|
-
*/
|
|
106
|
-
nextAnswer(value) {
|
|
107
|
-
if (Array.isArray(value)) {
|
|
108
|
-
this.nextAnswers.push(...value);
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
this.nextAnswers.push(value);
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
// src/transformers.ts
|
|
116
|
-
function number() {
|
|
117
|
-
return {
|
|
118
|
-
transform(input) {
|
|
119
|
-
if (input.trim() === "") {
|
|
120
|
-
return { isValid: false, error: "not a number" };
|
|
121
|
-
}
|
|
122
|
-
const parsed = Number(input.replace(",", "."));
|
|
123
|
-
if (Number.isNaN(parsed)) {
|
|
124
|
-
return { isValid: false, error: "not a number" };
|
|
125
|
-
}
|
|
126
|
-
return { isValid: true, transformed: parsed };
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
function integer() {
|
|
131
|
-
return {
|
|
132
|
-
transform(input) {
|
|
133
|
-
if (input.trim() === "") {
|
|
134
|
-
return { isValid: false, error: "not an integer" };
|
|
135
|
-
}
|
|
136
|
-
const parsed = Number(input);
|
|
137
|
-
if (Number.isNaN(parsed) || !Number.isInteger(parsed)) {
|
|
138
|
-
return { isValid: false, error: "not an integer" };
|
|
139
|
-
}
|
|
140
|
-
return { isValid: true, transformed: parsed };
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
function url() {
|
|
145
|
-
return {
|
|
146
|
-
transform(input) {
|
|
147
|
-
try {
|
|
148
|
-
return { isValid: true, transformed: new URL(input) };
|
|
149
|
-
} catch {
|
|
150
|
-
try {
|
|
151
|
-
const parsed = new URL(`https://${input}`);
|
|
152
|
-
if (!parsed.hostname.includes(".") && parsed.hostname !== "localhost") {
|
|
153
|
-
return { isValid: false, error: "invalid URL" };
|
|
154
|
-
}
|
|
155
|
-
return { isValid: true, transformed: parsed };
|
|
156
|
-
} catch {
|
|
157
|
-
return { isValid: false, error: "invalid URL" };
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// src/prompts/abstract.ts
|
|
165
|
-
var import_node_os = require("os");
|
|
166
|
-
var import_node_readline = __toESM(require("readline"), 1);
|
|
167
|
-
var import_node_stream = require("stream");
|
|
168
|
-
var import_node_events = __toESM(require("events"), 1);
|
|
169
|
-
var import_node_util = require("util");
|
|
170
|
-
|
|
171
|
-
// src/errors/abort.ts
|
|
172
|
-
var AbortError = class extends Error {
|
|
173
|
-
constructor(message) {
|
|
174
|
-
super(message);
|
|
175
|
-
this.name = "AbortError";
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
// src/prompts/abstract.ts
|
|
180
|
-
function kNoopTransformer(input) {
|
|
181
|
-
return input;
|
|
182
|
-
}
|
|
183
|
-
var AbstractPrompt = class _AbstractPrompt extends import_node_events.default {
|
|
184
|
-
stdin;
|
|
185
|
-
stdout;
|
|
186
|
-
message;
|
|
187
|
-
signal;
|
|
188
|
-
skip;
|
|
189
|
-
history;
|
|
190
|
-
agent;
|
|
191
|
-
transformer = kNoopTransformer;
|
|
192
|
-
rl;
|
|
193
|
-
#signalHandler;
|
|
194
|
-
constructor(options) {
|
|
195
|
-
super();
|
|
196
|
-
if (this.constructor === _AbstractPrompt) {
|
|
197
|
-
throw new Error("AbstractPrompt can't be instantiated.");
|
|
198
|
-
}
|
|
199
|
-
const {
|
|
200
|
-
stdin: input = process.stdin,
|
|
201
|
-
stdout: output = process.stdout,
|
|
202
|
-
message,
|
|
203
|
-
signal,
|
|
204
|
-
skip = false
|
|
205
|
-
} = options;
|
|
206
|
-
if (typeof message !== "string") {
|
|
207
|
-
throw new TypeError(`message must be string, ${typeof message} given.`);
|
|
208
|
-
}
|
|
209
|
-
if (!output.isTTY) {
|
|
210
|
-
Object.assign(output, {
|
|
211
|
-
moveCursor: () => void 0,
|
|
212
|
-
clearScreenDown: () => void 0
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
this.stdin = input;
|
|
216
|
-
this.stdout = output;
|
|
217
|
-
this.message = message;
|
|
218
|
-
this.signal = signal;
|
|
219
|
-
this.skip = skip;
|
|
220
|
-
this.history = [];
|
|
221
|
-
this.agent = PromptAgent.agent();
|
|
222
|
-
if (this.stdout.isTTY) {
|
|
223
|
-
this.stdin.setRawMode(true);
|
|
224
|
-
}
|
|
225
|
-
this.rl = import_node_readline.default.createInterface({
|
|
226
|
-
input,
|
|
227
|
-
output: new import_node_stream.Writable({
|
|
228
|
-
write: (chunk, encoding, callback) => {
|
|
229
|
-
if (chunk) {
|
|
230
|
-
const transformed = this.transformer(
|
|
231
|
-
Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
|
|
232
|
-
);
|
|
233
|
-
if (transformed !== null) {
|
|
234
|
-
this.stdout.write(transformed, encoding);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
callback();
|
|
238
|
-
}
|
|
239
|
-
}),
|
|
240
|
-
terminal: true
|
|
241
|
-
});
|
|
242
|
-
if (this.signal) {
|
|
243
|
-
this.#signalHandler = () => {
|
|
244
|
-
this.rl.close();
|
|
245
|
-
for (let i = 0; i < this.history.length; i++) {
|
|
246
|
-
this.clearLastLine();
|
|
247
|
-
}
|
|
248
|
-
this.emit("error", new AbortError("Prompt aborted"));
|
|
249
|
-
};
|
|
250
|
-
if (this.signal.aborted) {
|
|
251
|
-
this.#signalHandler();
|
|
252
|
-
}
|
|
253
|
-
this.signal.addEventListener("abort", this.#signalHandler, { once: true });
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
reset() {
|
|
257
|
-
this.transformer = kNoopTransformer;
|
|
258
|
-
}
|
|
259
|
-
write(data) {
|
|
260
|
-
const formattedData = (0, import_node_util.stripVTControlCharacters)(data).replace(import_node_os.EOL, "");
|
|
261
|
-
if (formattedData) {
|
|
262
|
-
this.history.push(formattedData);
|
|
263
|
-
}
|
|
264
|
-
return this.stdout.write(data);
|
|
265
|
-
}
|
|
266
|
-
clearLastLine() {
|
|
267
|
-
const lastLine = this.history.pop();
|
|
268
|
-
if (!lastLine) {
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
const lastLineRows = Math.ceil((0, import_node_util.stripVTControlCharacters)(lastLine).length / this.stdout.columns);
|
|
272
|
-
this.stdout.moveCursor(-this.stdout.columns, -lastLineRows);
|
|
273
|
-
this.stdout.clearScreenDown();
|
|
274
|
-
}
|
|
275
|
-
destroy() {
|
|
276
|
-
this.rl.close();
|
|
277
|
-
if (this.signal) {
|
|
278
|
-
this.signal.removeEventListener("abort", this.#signalHandler);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
};
|
|
282
|
-
|
|
283
|
-
// src/prompts/question.ts
|
|
284
|
-
var import_node_os2 = require("os");
|
|
285
|
-
var import_node_util4 = require("util");
|
|
286
|
-
|
|
287
|
-
// src/utils.ts
|
|
288
|
-
var import_node_process = __toESM(require("process"), 1);
|
|
289
|
-
var import_node_util2 = require("util");
|
|
290
|
-
var kLenSegmenter = new Intl.Segmenter();
|
|
291
|
-
function isUnicodeSupported() {
|
|
292
|
-
if (import_node_process.default.platform !== "win32") {
|
|
293
|
-
return import_node_process.default.env.TERM !== "linux";
|
|
294
|
-
}
|
|
295
|
-
return Boolean(import_node_process.default.env.WT_SESSION) || Boolean(import_node_process.default.env.TERMINUS_SUBLIME) || import_node_process.default.env.ConEmuTask === "{cmd::Cmder}" || import_node_process.default.env.TERM_PROGRAM === "Terminus-Sublime" || import_node_process.default.env.TERM_PROGRAM === "vscode" || import_node_process.default.env.TERM === "xterm-256color" || import_node_process.default.env.TERM === "alacritty" || import_node_process.default.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
|
|
296
|
-
}
|
|
297
|
-
function stringLength(string) {
|
|
298
|
-
if (string === "") {
|
|
299
|
-
return 0;
|
|
300
|
-
}
|
|
301
|
-
let length = 0;
|
|
302
|
-
for (const _ of kLenSegmenter.segment(
|
|
303
|
-
(0, import_node_util2.stripVTControlCharacters)(string)
|
|
304
|
-
)) {
|
|
305
|
-
length++;
|
|
306
|
-
}
|
|
307
|
-
return length;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// src/constants.ts
|
|
311
|
-
var import_node_util3 = require("util");
|
|
312
|
-
var kMainSymbols = {
|
|
313
|
-
tick: "\u2714",
|
|
314
|
-
cross: "\u2716",
|
|
315
|
-
pointer: "\u203A",
|
|
316
|
-
previous: "\u2B61",
|
|
317
|
-
next: "\u2B63",
|
|
318
|
-
active: "\u25CF",
|
|
319
|
-
inactive: "\u25CB"
|
|
320
|
-
};
|
|
321
|
-
var kFallbackSymbols = {
|
|
322
|
-
tick: "\u221A",
|
|
323
|
-
cross: "\xD7",
|
|
324
|
-
pointer: ">",
|
|
325
|
-
previous: "\u2191",
|
|
326
|
-
next: "\u2193",
|
|
327
|
-
active: "(+)",
|
|
328
|
-
inactive: "(-)"
|
|
329
|
-
};
|
|
330
|
-
var kSymbols = isUnicodeSupported() || process.env.CI ? kMainSymbols : kFallbackSymbols;
|
|
331
|
-
var kPointer = (0, import_node_util3.styleText)("gray", kSymbols.pointer);
|
|
332
|
-
var SYMBOLS = {
|
|
333
|
-
QuestionMark: (0, import_node_util3.styleText)(["blue", "bold"], "?"),
|
|
334
|
-
Tick: (0, import_node_util3.styleText)(["green", "bold"], kSymbols.tick),
|
|
335
|
-
Cross: (0, import_node_util3.styleText)(["red", "bold"], kSymbols.cross),
|
|
336
|
-
Pointer: kPointer,
|
|
337
|
-
Previous: kSymbols.previous,
|
|
338
|
-
Next: kSymbols.next,
|
|
339
|
-
ShowCursor: "\x1B[?25h",
|
|
340
|
-
HideCursor: "\x1B[?25l",
|
|
341
|
-
Active: (0, import_node_util3.styleText)("cyan", kSymbols.active),
|
|
342
|
-
Inactive: (0, import_node_util3.styleText)("gray", kSymbols.inactive)
|
|
343
|
-
};
|
|
344
|
-
var VALIDATION_SPINNER_INTERVAL = 300;
|
|
345
|
-
|
|
346
|
-
// src/prompts/question.ts
|
|
347
|
-
var QuestionPrompt = class extends AbstractPrompt {
|
|
348
|
-
defaultValue;
|
|
349
|
-
tip;
|
|
350
|
-
questionSuffixError;
|
|
351
|
-
answer;
|
|
352
|
-
answerBuffer;
|
|
353
|
-
#validators;
|
|
354
|
-
#transformer;
|
|
355
|
-
#transformedAnswer;
|
|
356
|
-
#secure;
|
|
357
|
-
#securePlaceholder = null;
|
|
358
|
-
constructor(options) {
|
|
359
|
-
const {
|
|
360
|
-
defaultValue,
|
|
361
|
-
validators: validators2 = [],
|
|
362
|
-
transformer,
|
|
363
|
-
secure = false,
|
|
364
|
-
...baseOptions
|
|
365
|
-
} = options;
|
|
366
|
-
super({ ...baseOptions });
|
|
367
|
-
if (validators2.length > 0 && transformer !== void 0) {
|
|
368
|
-
throw new Error("validators and transformer are mutually exclusive");
|
|
369
|
-
}
|
|
370
|
-
if (defaultValue && typeof defaultValue !== "string") {
|
|
371
|
-
throw new TypeError("defaultValue must be a string");
|
|
372
|
-
}
|
|
373
|
-
this.defaultValue = defaultValue;
|
|
374
|
-
this.tip = this.defaultValue ? ` (${this.defaultValue})` : "";
|
|
375
|
-
this.#validators = validators2;
|
|
376
|
-
this.#transformer = transformer;
|
|
377
|
-
if (typeof secure === "object") {
|
|
378
|
-
this.#secure = true;
|
|
379
|
-
this.#securePlaceholder = secure.placeholder;
|
|
380
|
-
} else {
|
|
381
|
-
this.#secure = Boolean(secure);
|
|
382
|
-
}
|
|
383
|
-
this.questionSuffixError = "";
|
|
384
|
-
}
|
|
385
|
-
#question() {
|
|
386
|
-
const { resolve, promise } = Promise.withResolvers();
|
|
387
|
-
const questionQuery = this.#getQuestionQuery();
|
|
388
|
-
this.history.push(questionQuery);
|
|
389
|
-
this.rl.question(questionQuery, (answer) => {
|
|
390
|
-
this.history.push(questionQuery + answer);
|
|
391
|
-
this.reset();
|
|
392
|
-
resolve(answer);
|
|
393
|
-
});
|
|
394
|
-
if (this.#securePlaceholder !== null) {
|
|
395
|
-
this.transformer = (input) => Buffer.from(this.#securePlaceholder.repeat(input.length), "utf-8");
|
|
396
|
-
}
|
|
397
|
-
return promise;
|
|
398
|
-
}
|
|
399
|
-
#getQuestionQuery() {
|
|
400
|
-
return `${(0, import_node_util4.styleText)("bold", `${SYMBOLS.QuestionMark} ${this.message}${this.tip}`)} ${this.questionSuffixError}`;
|
|
401
|
-
}
|
|
402
|
-
#setQuestionSuffixError(error) {
|
|
403
|
-
const suffix = (0, import_node_util4.styleText)("red", `[${error}] `);
|
|
404
|
-
this.questionSuffixError = suffix;
|
|
405
|
-
}
|
|
406
|
-
#writeAnswer() {
|
|
407
|
-
const prefix = this.answer ? SYMBOLS.Tick : SYMBOLS.Cross;
|
|
408
|
-
const answer = this.answer ?? "";
|
|
409
|
-
const maskedAnswer = this.#securePlaceholder ? this.#securePlaceholder.repeat(answer.length) : answer;
|
|
410
|
-
const stylizedAnswer = (0, import_node_util4.styleText)(
|
|
411
|
-
"yellow",
|
|
412
|
-
this.#secure && this.#securePlaceholder === null ? "CONFIDENTIAL" : maskedAnswer
|
|
413
|
-
);
|
|
414
|
-
this.write(`${prefix} ${(0, import_node_util4.styleText)("bold", this.message)} ${SYMBOLS.Pointer} ${stylizedAnswer}${import_node_os2.EOL}`);
|
|
415
|
-
}
|
|
416
|
-
#getValidatingQuery(dotCount) {
|
|
417
|
-
const question2 = (0, import_node_util4.styleText)("bold", `${SYMBOLS.QuestionMark} ${this.message}${this.tip}`);
|
|
418
|
-
const hint = (0, import_node_util4.styleText)("yellow", `[validating${".".repeat(dotCount)}]`);
|
|
419
|
-
return `${question2} ${hint}${import_node_os2.EOL}`;
|
|
420
|
-
}
|
|
421
|
-
async #runTransformer() {
|
|
422
|
-
const result = this.#transformer.transform(this.answer);
|
|
423
|
-
if (result instanceof Promise) {
|
|
424
|
-
let dotCount = 1;
|
|
425
|
-
this.write(this.#getValidatingQuery(dotCount));
|
|
426
|
-
const spinnerInterval = setInterval(() => {
|
|
427
|
-
dotCount = dotCount % 3 + 1;
|
|
428
|
-
this.clearLastLine();
|
|
429
|
-
this.write(this.#getValidatingQuery(dotCount));
|
|
430
|
-
}, VALIDATION_SPINNER_INTERVAL);
|
|
431
|
-
try {
|
|
432
|
-
return await result;
|
|
433
|
-
} finally {
|
|
434
|
-
clearInterval(spinnerInterval);
|
|
435
|
-
this.clearLastLine();
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
return result;
|
|
439
|
-
}
|
|
440
|
-
async #onQuestionAnswer() {
|
|
441
|
-
const questionLineCount = Math.ceil(
|
|
442
|
-
stringLength(this.#getQuestionQuery() + this.answer) / this.stdout.columns
|
|
443
|
-
);
|
|
444
|
-
this.stdout.moveCursor(-this.stdout.columns, -questionLineCount);
|
|
445
|
-
this.stdout.clearScreenDown();
|
|
446
|
-
if (this.#transformer) {
|
|
447
|
-
const result = await this.#runTransformer();
|
|
448
|
-
if (isValidTransformation(result) === false) {
|
|
449
|
-
this.#setQuestionSuffixError(resultError(result));
|
|
450
|
-
this.answerBuffer = this.#question();
|
|
451
|
-
return;
|
|
452
|
-
}
|
|
453
|
-
this.#transformedAnswer = result.transformed;
|
|
454
|
-
this.answerBuffer = void 0;
|
|
455
|
-
this.#writeAnswer();
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
for (const validator of this.#validators) {
|
|
459
|
-
let validationResult;
|
|
460
|
-
const result = validator.validate(this.answer);
|
|
461
|
-
if (result instanceof Promise) {
|
|
462
|
-
let dotCount = 1;
|
|
463
|
-
this.write(this.#getValidatingQuery(dotCount));
|
|
464
|
-
const spinnerInterval = setInterval(() => {
|
|
465
|
-
dotCount = dotCount % 3 + 1;
|
|
466
|
-
this.clearLastLine();
|
|
467
|
-
this.write(this.#getValidatingQuery(dotCount));
|
|
468
|
-
}, VALIDATION_SPINNER_INTERVAL);
|
|
469
|
-
try {
|
|
470
|
-
validationResult = await result;
|
|
471
|
-
} finally {
|
|
472
|
-
clearInterval(spinnerInterval);
|
|
473
|
-
this.clearLastLine();
|
|
474
|
-
}
|
|
475
|
-
} else {
|
|
476
|
-
validationResult = result;
|
|
477
|
-
}
|
|
478
|
-
if (isValid(validationResult) === false) {
|
|
479
|
-
this.#setQuestionSuffixError(resultError(validationResult));
|
|
480
|
-
this.answerBuffer = this.#question();
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
this.answerBuffer = void 0;
|
|
485
|
-
this.#writeAnswer();
|
|
486
|
-
}
|
|
487
|
-
async listen() {
|
|
488
|
-
if (this.skip) {
|
|
489
|
-
this.destroy();
|
|
490
|
-
if (this.#transformer) {
|
|
491
|
-
const rawValue = this.defaultValue ?? "";
|
|
492
|
-
const result = await this.#transformer.transform(rawValue);
|
|
493
|
-
if (isValidTransformation(result) === false) {
|
|
494
|
-
throw new Error(`Transformer failed for default value "${rawValue}": ${resultError(result)}`);
|
|
495
|
-
}
|
|
496
|
-
return result.transformed;
|
|
497
|
-
}
|
|
498
|
-
return this.defaultValue ?? "";
|
|
499
|
-
}
|
|
500
|
-
const agentAnswer = this.agent.nextAnswers.shift();
|
|
501
|
-
if (agentAnswer !== void 0) {
|
|
502
|
-
this.answer = agentAnswer;
|
|
503
|
-
this.#writeAnswer();
|
|
504
|
-
this.destroy();
|
|
505
|
-
if (this.#transformer) {
|
|
506
|
-
const result = await this.#transformer.transform(agentAnswer);
|
|
507
|
-
if (isValidTransformation(result) === false) {
|
|
508
|
-
throw new Error(`(PromptAgent) transformer failed for answer "${agentAnswer}": ${resultError(result)}`);
|
|
509
|
-
}
|
|
510
|
-
return result.transformed;
|
|
511
|
-
}
|
|
512
|
-
return this.answer;
|
|
513
|
-
}
|
|
514
|
-
this.answer = await this.#question();
|
|
515
|
-
if (this.answer === "" && this.defaultValue) {
|
|
516
|
-
this.answer = this.defaultValue;
|
|
517
|
-
}
|
|
518
|
-
await this.#onQuestionAnswer();
|
|
519
|
-
while (this.answerBuffer !== void 0) {
|
|
520
|
-
this.answer = await this.answerBuffer;
|
|
521
|
-
await this.#onQuestionAnswer();
|
|
522
|
-
}
|
|
523
|
-
this.destroy();
|
|
524
|
-
return this.#transformedAnswer ?? this.answer;
|
|
525
|
-
}
|
|
526
|
-
};
|
|
527
|
-
|
|
528
|
-
// src/prompts/confirm.ts
|
|
529
|
-
var import_node_os3 = require("os");
|
|
530
|
-
var import_node_util5 = require("util");
|
|
531
|
-
var kToggleKeys = /* @__PURE__ */ new Set([
|
|
532
|
-
"left",
|
|
533
|
-
"right",
|
|
534
|
-
"tab",
|
|
535
|
-
"q",
|
|
536
|
-
"a",
|
|
537
|
-
"d",
|
|
538
|
-
"h",
|
|
539
|
-
"j",
|
|
540
|
-
"k",
|
|
541
|
-
"l",
|
|
542
|
-
"space"
|
|
543
|
-
]);
|
|
544
|
-
var ConfirmPrompt = class extends AbstractPrompt {
|
|
545
|
-
initial;
|
|
546
|
-
selectedValue;
|
|
547
|
-
fastAnswer;
|
|
548
|
-
#boundKeyPressEvent;
|
|
549
|
-
#boundExitEvent;
|
|
550
|
-
constructor(options) {
|
|
551
|
-
const {
|
|
552
|
-
initial = false,
|
|
553
|
-
...baseOptions
|
|
554
|
-
} = options;
|
|
555
|
-
super({ ...baseOptions });
|
|
556
|
-
this.initial = initial;
|
|
557
|
-
this.selectedValue = initial;
|
|
558
|
-
}
|
|
559
|
-
#getHint() {
|
|
560
|
-
const Yes = (0, import_node_util5.styleText)(["cyan", "bold", "underline"], "Yes");
|
|
561
|
-
const No = (0, import_node_util5.styleText)(["cyan", "bold", "underline"], "No");
|
|
562
|
-
return this.selectedValue ? `${Yes}/No` : `Yes/${No}`;
|
|
563
|
-
}
|
|
564
|
-
#render() {
|
|
565
|
-
this.write(this.#getQuestionQuery());
|
|
566
|
-
}
|
|
567
|
-
#onKeypress(resolve, _value, key) {
|
|
568
|
-
this.stdout.moveCursor(
|
|
569
|
-
-this.stdout.columns,
|
|
570
|
-
-Math.floor(stringLength(this.#getQuestionQuery()) / this.stdout.columns)
|
|
571
|
-
);
|
|
572
|
-
this.stdout.clearScreenDown();
|
|
573
|
-
if (key.name === "return") {
|
|
574
|
-
resolve(this.selectedValue);
|
|
575
|
-
return;
|
|
576
|
-
}
|
|
577
|
-
if (kToggleKeys.has(key.name ?? "")) {
|
|
578
|
-
this.selectedValue = !this.selectedValue;
|
|
579
|
-
}
|
|
580
|
-
if (key.name === "y") {
|
|
581
|
-
this.selectedValue = true;
|
|
582
|
-
resolve(true);
|
|
583
|
-
this.fastAnswer = true;
|
|
584
|
-
} else if (key.name === "n") {
|
|
585
|
-
this.selectedValue = false;
|
|
586
|
-
resolve(false);
|
|
587
|
-
this.fastAnswer = true;
|
|
588
|
-
}
|
|
589
|
-
if (!this.fastAnswer) {
|
|
590
|
-
this.#render();
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
#onProcessExit() {
|
|
594
|
-
this.stdin.off("keypress", this.#boundKeyPressEvent);
|
|
595
|
-
}
|
|
596
|
-
#getQuestionQuery() {
|
|
597
|
-
const query = (0, import_node_util5.styleText)("bold", `${SYMBOLS.QuestionMark} ${this.message}`);
|
|
598
|
-
return `${query} ${this.#getHint()}`;
|
|
599
|
-
}
|
|
600
|
-
#onQuestionAnswer() {
|
|
601
|
-
this.clearLastLine();
|
|
602
|
-
this.stdout.moveCursor(
|
|
603
|
-
-this.stdout.columns,
|
|
604
|
-
-Math.floor(stringLength(this.#getQuestionQuery()) / this.stdout.columns)
|
|
605
|
-
);
|
|
606
|
-
this.stdout.clearScreenDown();
|
|
607
|
-
this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${(0, import_node_util5.styleText)("bold", this.message)}${import_node_os3.EOL}`);
|
|
608
|
-
}
|
|
609
|
-
async listen() {
|
|
610
|
-
if (this.skip) {
|
|
611
|
-
this.destroy();
|
|
612
|
-
return this.initial;
|
|
613
|
-
}
|
|
614
|
-
const answer = this.agent.nextAnswers.shift();
|
|
615
|
-
if (answer !== void 0) {
|
|
616
|
-
this.selectedValue = answer;
|
|
617
|
-
this.#onQuestionAnswer();
|
|
618
|
-
this.destroy();
|
|
619
|
-
return answer;
|
|
620
|
-
}
|
|
621
|
-
this.write(SYMBOLS.HideCursor);
|
|
622
|
-
try {
|
|
623
|
-
const { resolve, promise } = Promise.withResolvers();
|
|
624
|
-
const questionQuery = this.#getQuestionQuery();
|
|
625
|
-
this.write(questionQuery);
|
|
626
|
-
this.#boundKeyPressEvent = this.#onKeypress.bind(this, resolve);
|
|
627
|
-
this.stdin.on("keypress", this.#boundKeyPressEvent);
|
|
628
|
-
this.#boundExitEvent = this.#onProcessExit.bind(this);
|
|
629
|
-
process.once("exit", this.#boundExitEvent);
|
|
630
|
-
await promise;
|
|
631
|
-
this.#onQuestionAnswer();
|
|
632
|
-
} finally {
|
|
633
|
-
this.write(SYMBOLS.ShowCursor);
|
|
634
|
-
this.#onProcessExit();
|
|
635
|
-
process.off("exit", this.#boundExitEvent);
|
|
636
|
-
this.destroy();
|
|
637
|
-
}
|
|
638
|
-
return this.selectedValue;
|
|
639
|
-
}
|
|
640
|
-
};
|
|
641
|
-
|
|
642
|
-
// src/prompts/select.ts
|
|
643
|
-
var import_node_os4 = require("os");
|
|
644
|
-
var import_node_util6 = require("util");
|
|
645
|
-
var kRequiredChoiceProperties = ["label", "value"];
|
|
646
|
-
var SelectPrompt = class extends AbstractPrompt {
|
|
647
|
-
#boundExitEvent = () => void 0;
|
|
648
|
-
#boundKeyPressEvent = () => void 0;
|
|
649
|
-
#validators;
|
|
650
|
-
#isValidating = false;
|
|
651
|
-
activeIndex = 0;
|
|
652
|
-
questionMessage;
|
|
653
|
-
autocompleteValue = "";
|
|
654
|
-
options;
|
|
655
|
-
lastRender;
|
|
656
|
-
get choices() {
|
|
657
|
-
return this.options.choices;
|
|
658
|
-
}
|
|
659
|
-
get filteredChoices() {
|
|
660
|
-
if (!(this.options.autocomplete && this.autocompleteValue.length > 0)) {
|
|
661
|
-
return this.choices;
|
|
662
|
-
}
|
|
663
|
-
const isCaseSensitive = this.options.caseSensitive;
|
|
664
|
-
const autocompleteValue = isCaseSensitive ? this.autocompleteValue : this.autocompleteValue.toLowerCase();
|
|
665
|
-
return this.choices.filter((choice) => this.#filterChoice(choice, autocompleteValue, isCaseSensitive));
|
|
666
|
-
}
|
|
667
|
-
#filterChoice(choice, autocompleteValue, isCaseSensitive = false) {
|
|
668
|
-
const choiceValue = typeof choice === "string" ? isCaseSensitive ? choice : choice.toLowerCase() : isCaseSensitive ? choice.label : choice.label.toLowerCase();
|
|
669
|
-
if (autocompleteValue.includes(" ")) {
|
|
670
|
-
return this.#filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive);
|
|
671
|
-
}
|
|
672
|
-
return choiceValue.includes(autocompleteValue);
|
|
673
|
-
}
|
|
674
|
-
#filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive) {
|
|
675
|
-
return autocompleteValue.split(" ").every((word) => {
|
|
676
|
-
const wordValue = isCaseSensitive ? word : word.toLowerCase();
|
|
677
|
-
return choiceValue.includes(wordValue) || choiceValue.includes(autocompleteValue);
|
|
678
|
-
});
|
|
679
|
-
}
|
|
680
|
-
get longestChoice() {
|
|
681
|
-
return Math.max(...this.filteredChoices.map((choice) => {
|
|
682
|
-
if (typeof choice === "string") {
|
|
683
|
-
return choice.length;
|
|
684
|
-
}
|
|
685
|
-
return choice.label.length;
|
|
686
|
-
}));
|
|
687
|
-
}
|
|
688
|
-
constructor(options) {
|
|
689
|
-
const {
|
|
690
|
-
choices,
|
|
691
|
-
validators: validators2 = [],
|
|
692
|
-
...baseOptions
|
|
693
|
-
} = options;
|
|
694
|
-
super({ ...baseOptions });
|
|
695
|
-
this.options = options;
|
|
696
|
-
if (!choices?.length) {
|
|
697
|
-
this.destroy();
|
|
698
|
-
throw new TypeError("Missing required param: choices");
|
|
699
|
-
}
|
|
700
|
-
this.#validators = validators2;
|
|
701
|
-
for (const choice of choices) {
|
|
702
|
-
if (typeof choice === "string") {
|
|
703
|
-
continue;
|
|
704
|
-
}
|
|
705
|
-
for (const prop of kRequiredChoiceProperties) {
|
|
706
|
-
if (!choice[prop]) {
|
|
707
|
-
this.destroy();
|
|
708
|
-
throw new TypeError(`Missing ${prop} for choice ${JSON.stringify(choice)}`);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
#getFormattedChoice(choiceIndex) {
|
|
714
|
-
const choice = this.filteredChoices[choiceIndex];
|
|
715
|
-
if (typeof choice === "string") {
|
|
716
|
-
return { value: choice, label: choice };
|
|
717
|
-
}
|
|
718
|
-
return choice;
|
|
719
|
-
}
|
|
720
|
-
#getVisibleChoices() {
|
|
721
|
-
const maxVisible = this.options.maxVisible || 8;
|
|
722
|
-
let startIndex = Math.min(this.filteredChoices.length - maxVisible, this.activeIndex - Math.floor(maxVisible / 2));
|
|
723
|
-
if (startIndex < 0) {
|
|
724
|
-
startIndex = 0;
|
|
725
|
-
}
|
|
726
|
-
const endIndex = Math.min(startIndex + maxVisible, this.filteredChoices.length);
|
|
727
|
-
return { startIndex, endIndex };
|
|
728
|
-
}
|
|
729
|
-
#showChoices() {
|
|
730
|
-
const { startIndex, endIndex } = this.#getVisibleChoices();
|
|
731
|
-
this.lastRender = { startIndex, endIndex };
|
|
732
|
-
if (this.options.autocomplete) {
|
|
733
|
-
this.write(`${SYMBOLS.Pointer} ${this.autocompleteValue}${import_node_os4.EOL}`);
|
|
734
|
-
}
|
|
735
|
-
for (let choiceIndex = startIndex; choiceIndex < endIndex; choiceIndex++) {
|
|
736
|
-
const choice = this.#getFormattedChoice(choiceIndex);
|
|
737
|
-
const isChoiceSelected = choiceIndex === this.activeIndex;
|
|
738
|
-
const showPreviousChoicesArrow = startIndex > 0 && choiceIndex === startIndex;
|
|
739
|
-
const showNextChoicesArrow = endIndex < this.filteredChoices.length && choiceIndex === endIndex - 1;
|
|
740
|
-
let prefixArrow = " ";
|
|
741
|
-
if (showPreviousChoicesArrow) {
|
|
742
|
-
prefixArrow = SYMBOLS.Previous;
|
|
743
|
-
} else if (showNextChoicesArrow) {
|
|
744
|
-
prefixArrow = SYMBOLS.Next;
|
|
745
|
-
}
|
|
746
|
-
const prefix = `${prefixArrow}${isChoiceSelected ? `${SYMBOLS.Pointer} ` : " "}`;
|
|
747
|
-
const formattedLabel = choice.label.padEnd(
|
|
748
|
-
this.longestChoice < 10 ? this.longestChoice : 0
|
|
749
|
-
);
|
|
750
|
-
const formattedDescription = choice.description ? ` - ${choice.description}` : "";
|
|
751
|
-
const styles = isChoiceSelected ? ["white", "bold"] : ["gray"];
|
|
752
|
-
const str = `${prefix}${(0, import_node_util6.styleText)(styles, `${formattedLabel}${formattedDescription}`)}${import_node_os4.EOL}`;
|
|
753
|
-
this.write(str);
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
async #handleReturn(resolve, render) {
|
|
757
|
-
this.#isValidating = true;
|
|
758
|
-
try {
|
|
759
|
-
const choice = this.filteredChoices[this.activeIndex] || "";
|
|
760
|
-
const label = typeof choice === "string" ? choice : choice.label;
|
|
761
|
-
const value = typeof choice === "string" ? choice : choice.value;
|
|
762
|
-
for (const validator of this.#validators) {
|
|
763
|
-
let validationResult;
|
|
764
|
-
const result = validator.validate(value);
|
|
765
|
-
if (result instanceof Promise) {
|
|
766
|
-
let dotCount = 1;
|
|
767
|
-
render({ validating: `validating${".".repeat(dotCount)}` });
|
|
768
|
-
const spinnerInterval = setInterval(() => {
|
|
769
|
-
dotCount = dotCount % 3 + 1;
|
|
770
|
-
render({ validating: `validating${".".repeat(dotCount)}` });
|
|
771
|
-
}, VALIDATION_SPINNER_INTERVAL);
|
|
772
|
-
try {
|
|
773
|
-
validationResult = await result;
|
|
774
|
-
} finally {
|
|
775
|
-
clearInterval(spinnerInterval);
|
|
776
|
-
}
|
|
777
|
-
} else {
|
|
778
|
-
validationResult = result;
|
|
779
|
-
}
|
|
780
|
-
if (isValid(validationResult) === false) {
|
|
781
|
-
render({ error: resultError(validationResult) });
|
|
782
|
-
return;
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
render({ clearRender: true });
|
|
786
|
-
if (!this.options.ignoreValues?.includes(value)) {
|
|
787
|
-
this.#showAnsweredQuestion(label);
|
|
788
|
-
}
|
|
789
|
-
this.write(SYMBOLS.ShowCursor);
|
|
790
|
-
this.destroy();
|
|
791
|
-
this.#onProcessExit();
|
|
792
|
-
process.off("exit", this.#boundExitEvent);
|
|
793
|
-
resolve(value);
|
|
794
|
-
} finally {
|
|
795
|
-
this.#isValidating = false;
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
#showAnsweredQuestion(label) {
|
|
799
|
-
const symbolPrefix = label === "" ? SYMBOLS.Cross : SYMBOLS.Tick;
|
|
800
|
-
const prefix = `${symbolPrefix} ${(0, import_node_util6.styleText)("bold", this.message)} ${SYMBOLS.Pointer}`;
|
|
801
|
-
const formattedChoice = (0, import_node_util6.styleText)("yellow", label);
|
|
802
|
-
this.write(`${prefix} ${formattedChoice}${import_node_os4.EOL}`);
|
|
803
|
-
}
|
|
804
|
-
#onProcessExit() {
|
|
805
|
-
this.stdin.off("keypress", this.#boundKeyPressEvent);
|
|
806
|
-
this.stdout.moveCursor(-this.stdout.columns, 0);
|
|
807
|
-
this.stdout.clearScreenDown();
|
|
808
|
-
this.write(SYMBOLS.ShowCursor);
|
|
809
|
-
}
|
|
810
|
-
#onKeypress(...args) {
|
|
811
|
-
const [resolve, render, , key] = args;
|
|
812
|
-
if (this.#isValidating) {
|
|
813
|
-
return;
|
|
814
|
-
}
|
|
815
|
-
if (key.name === "up") {
|
|
816
|
-
this.activeIndex = this.activeIndex === 0 ? this.filteredChoices.length - 1 : this.activeIndex - 1;
|
|
817
|
-
render();
|
|
818
|
-
} else if (key.name === "down") {
|
|
819
|
-
this.activeIndex = this.activeIndex === this.filteredChoices.length - 1 ? 0 : this.activeIndex + 1;
|
|
820
|
-
render();
|
|
821
|
-
} else if (key.name === "return") {
|
|
822
|
-
void this.#handleReturn(resolve, render);
|
|
823
|
-
} else {
|
|
824
|
-
if (!key.ctrl && this.options.autocomplete) {
|
|
825
|
-
this.activeIndex = 0;
|
|
826
|
-
if (key.name === "backspace" && this.autocompleteValue.length > 0) {
|
|
827
|
-
this.autocompleteValue = this.autocompleteValue.slice(0, -1);
|
|
828
|
-
} else if (key.name !== "backspace") {
|
|
829
|
-
this.autocompleteValue += key.sequence;
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
render();
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
async listen() {
|
|
836
|
-
if (this.skip) {
|
|
837
|
-
this.destroy();
|
|
838
|
-
const answer2 = this.options.choices[0];
|
|
839
|
-
return typeof answer2 === "string" ? answer2 : answer2.value;
|
|
840
|
-
}
|
|
841
|
-
const answer = this.agent.nextAnswers.shift();
|
|
842
|
-
if (answer !== void 0) {
|
|
843
|
-
this.#showAnsweredQuestion(answer);
|
|
844
|
-
this.destroy();
|
|
845
|
-
return answer;
|
|
846
|
-
}
|
|
847
|
-
this.transformer = () => null;
|
|
848
|
-
this.write(SYMBOLS.HideCursor);
|
|
849
|
-
this.#showQuestion();
|
|
850
|
-
const render = (options = {}) => {
|
|
851
|
-
const {
|
|
852
|
-
initialRender = false,
|
|
853
|
-
clearRender = false,
|
|
854
|
-
error = null,
|
|
855
|
-
validating = null
|
|
856
|
-
} = options;
|
|
857
|
-
if (!initialRender) {
|
|
858
|
-
let linesToClear = this.lastRender.endIndex - this.lastRender.startIndex;
|
|
859
|
-
while (linesToClear > 0) {
|
|
860
|
-
this.clearLastLine();
|
|
861
|
-
linesToClear--;
|
|
862
|
-
}
|
|
863
|
-
if (this.options.autocomplete) {
|
|
864
|
-
let linesToClear2 = Math.ceil(
|
|
865
|
-
stringLength(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
|
|
866
|
-
);
|
|
867
|
-
while (linesToClear2 > 0) {
|
|
868
|
-
this.clearLastLine();
|
|
869
|
-
linesToClear2--;
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
if (clearRender) {
|
|
874
|
-
this.clearLastLine();
|
|
875
|
-
return;
|
|
876
|
-
}
|
|
877
|
-
if (error || validating) {
|
|
878
|
-
this.clearLastLine();
|
|
879
|
-
this.#showQuestion(error, validating);
|
|
880
|
-
}
|
|
881
|
-
this.#showChoices();
|
|
882
|
-
};
|
|
883
|
-
render({ initialRender: true });
|
|
884
|
-
this.#boundExitEvent = this.#onProcessExit.bind(this);
|
|
885
|
-
process.once("exit", this.#boundExitEvent);
|
|
886
|
-
const { resolve, promise } = Promise.withResolvers();
|
|
887
|
-
this.#boundKeyPressEvent = this.#onKeypress.bind(this, resolve, render);
|
|
888
|
-
this.stdin.on("keypress", this.#boundKeyPressEvent);
|
|
889
|
-
return promise;
|
|
890
|
-
}
|
|
891
|
-
#showQuestion(error = null, validating = null) {
|
|
892
|
-
let hint = "";
|
|
893
|
-
if (validating) {
|
|
894
|
-
hint = (0, import_node_util6.styleText)("yellow", `[${validating}]`);
|
|
895
|
-
} else if (error) {
|
|
896
|
-
hint += `${hint.length > 0 ? " " : ""}${(0, import_node_util6.styleText)(["red", "bold"], `[${error}]`)}`;
|
|
897
|
-
}
|
|
898
|
-
this.questionMessage = `${SYMBOLS.QuestionMark} ${(0, import_node_util6.styleText)("bold", this.message)}${hint.length > 0 ? ` ${hint}` : ""}`;
|
|
899
|
-
this.write(`${this.questionMessage}${import_node_os4.EOL}`);
|
|
900
|
-
}
|
|
901
|
-
};
|
|
902
|
-
|
|
903
|
-
// src/prompts/multiselect.ts
|
|
904
|
-
var import_node_os5 = require("os");
|
|
905
|
-
var import_node_util7 = require("util");
|
|
906
|
-
var kRequiredChoiceProperties2 = ["label", "value"];
|
|
907
|
-
var MultiselectPrompt = class extends AbstractPrompt {
|
|
908
|
-
#boundExitEvent = () => void 0;
|
|
909
|
-
#boundKeyPressEvent = () => void 0;
|
|
910
|
-
#validators;
|
|
911
|
-
#showHint;
|
|
912
|
-
#isValidating = false;
|
|
913
|
-
activeIndex = 0;
|
|
914
|
-
selectedIndexes = /* @__PURE__ */ new Set();
|
|
915
|
-
questionMessage;
|
|
916
|
-
autocompleteValue = "";
|
|
917
|
-
options;
|
|
918
|
-
lastRender;
|
|
919
|
-
get choices() {
|
|
920
|
-
return this.options.choices;
|
|
921
|
-
}
|
|
922
|
-
get filteredChoices() {
|
|
923
|
-
if (!(this.options.autocomplete && this.autocompleteValue.length > 0)) {
|
|
924
|
-
return this.choices;
|
|
925
|
-
}
|
|
926
|
-
const isCaseSensitive = this.options.caseSensitive;
|
|
927
|
-
const autocompleteValue = isCaseSensitive ? this.autocompleteValue : this.autocompleteValue.toLowerCase();
|
|
928
|
-
return this.choices.filter((choice) => this.#filterChoice(choice, autocompleteValue, isCaseSensitive));
|
|
929
|
-
}
|
|
930
|
-
#filterChoice(choice, autocompleteValue, isCaseSensitive = false) {
|
|
931
|
-
const choiceValue = typeof choice === "string" ? isCaseSensitive ? choice : choice.toLowerCase() : isCaseSensitive ? choice.label : choice.label.toLowerCase();
|
|
932
|
-
if (autocompleteValue.includes(" ")) {
|
|
933
|
-
return this.#filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive);
|
|
934
|
-
}
|
|
935
|
-
return choiceValue.includes(autocompleteValue);
|
|
936
|
-
}
|
|
937
|
-
#filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive) {
|
|
938
|
-
return autocompleteValue.split(" ").every((word) => {
|
|
939
|
-
const wordValue = isCaseSensitive ? word : word.toLowerCase();
|
|
940
|
-
return choiceValue.includes(wordValue) || choiceValue.includes(autocompleteValue);
|
|
941
|
-
});
|
|
942
|
-
}
|
|
943
|
-
get longestChoice() {
|
|
944
|
-
return Math.max(...this.filteredChoices.map((choice) => {
|
|
945
|
-
if (typeof choice === "string") {
|
|
946
|
-
return choice.length;
|
|
947
|
-
}
|
|
948
|
-
return choice.label.length;
|
|
949
|
-
}));
|
|
950
|
-
}
|
|
951
|
-
constructor(options) {
|
|
952
|
-
const {
|
|
953
|
-
choices,
|
|
954
|
-
preSelectedChoices = [],
|
|
955
|
-
validators: validators2 = [],
|
|
956
|
-
showHint = true,
|
|
957
|
-
...baseOptions
|
|
958
|
-
} = options;
|
|
959
|
-
super({ ...baseOptions });
|
|
960
|
-
this.options = options;
|
|
961
|
-
if (!choices?.length) {
|
|
962
|
-
this.destroy();
|
|
963
|
-
throw new TypeError("Missing required param: choices");
|
|
964
|
-
}
|
|
965
|
-
this.#validators = validators2;
|
|
966
|
-
this.#showHint = showHint;
|
|
967
|
-
for (const choice of choices) {
|
|
968
|
-
if (typeof choice === "string") {
|
|
969
|
-
continue;
|
|
970
|
-
}
|
|
971
|
-
for (const prop of kRequiredChoiceProperties2) {
|
|
972
|
-
if (!choice[prop]) {
|
|
973
|
-
this.destroy();
|
|
974
|
-
throw new TypeError(`Missing ${prop} for choice ${JSON.stringify(choice)}`);
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
for (const choice of preSelectedChoices) {
|
|
979
|
-
const choiceIndex = this.filteredChoices.findIndex((item) => {
|
|
980
|
-
if (typeof item === "string") {
|
|
981
|
-
return item === choice;
|
|
982
|
-
}
|
|
983
|
-
return item.value === choice;
|
|
984
|
-
});
|
|
985
|
-
if (choiceIndex === -1) {
|
|
986
|
-
this.destroy();
|
|
987
|
-
throw new Error(`Invalid pre-selected choice: ${typeof choice === "string" ? choice : choice.value}`);
|
|
988
|
-
}
|
|
989
|
-
this.selectedIndexes.add(choiceIndex);
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
#getFormattedChoice(choiceIndex) {
|
|
993
|
-
const choice = this.filteredChoices[choiceIndex];
|
|
994
|
-
if (typeof choice === "string") {
|
|
995
|
-
return { value: choice, label: choice };
|
|
996
|
-
}
|
|
997
|
-
return choice;
|
|
998
|
-
}
|
|
999
|
-
#getVisibleChoices() {
|
|
1000
|
-
const maxVisible = this.options.maxVisible || 8;
|
|
1001
|
-
let startIndex = Math.min(this.filteredChoices.length - maxVisible, this.activeIndex - Math.floor(maxVisible / 2));
|
|
1002
|
-
if (startIndex < 0) {
|
|
1003
|
-
startIndex = 0;
|
|
1004
|
-
}
|
|
1005
|
-
const endIndex = Math.min(startIndex + maxVisible, this.filteredChoices.length);
|
|
1006
|
-
return { startIndex, endIndex };
|
|
1007
|
-
}
|
|
1008
|
-
#showChoices() {
|
|
1009
|
-
const { startIndex, endIndex } = this.#getVisibleChoices();
|
|
1010
|
-
this.lastRender = { startIndex, endIndex };
|
|
1011
|
-
if (this.options.autocomplete) {
|
|
1012
|
-
this.write(`${SYMBOLS.Pointer} ${this.autocompleteValue}${import_node_os5.EOL}`);
|
|
1013
|
-
}
|
|
1014
|
-
for (let choiceIndex = startIndex; choiceIndex < endIndex; choiceIndex++) {
|
|
1015
|
-
const choice = this.#getFormattedChoice(choiceIndex);
|
|
1016
|
-
const isChoiceActive = choiceIndex === this.activeIndex;
|
|
1017
|
-
const isChoiceSelected = this.selectedIndexes.has(choiceIndex);
|
|
1018
|
-
const showPreviousChoicesArrow = startIndex > 0 && choiceIndex === startIndex;
|
|
1019
|
-
const showNextChoicesArrow = endIndex < this.filteredChoices.length && choiceIndex === endIndex - 1;
|
|
1020
|
-
let prefixArrow = " ";
|
|
1021
|
-
if (showPreviousChoicesArrow) {
|
|
1022
|
-
prefixArrow = SYMBOLS.Previous + " ";
|
|
1023
|
-
} else if (showNextChoicesArrow) {
|
|
1024
|
-
prefixArrow = SYMBOLS.Next + " ";
|
|
1025
|
-
}
|
|
1026
|
-
const prefix = `${prefixArrow}${isChoiceSelected ? SYMBOLS.Active : SYMBOLS.Inactive}`;
|
|
1027
|
-
const formattedLabel = choice.label.padEnd(
|
|
1028
|
-
this.longestChoice < 10 ? this.longestChoice : 0
|
|
1029
|
-
);
|
|
1030
|
-
const formattedDescription = choice.description ? ` - ${choice.description}` : "";
|
|
1031
|
-
const styles = isChoiceActive ? ["white", "bold"] : ["gray"];
|
|
1032
|
-
const str = `${prefix} ${(0, import_node_util7.styleText)(styles, `${formattedLabel}${formattedDescription}`)}${import_node_os5.EOL}`;
|
|
1033
|
-
this.write(str);
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
async #handleReturn(resolve, render) {
|
|
1037
|
-
this.#isValidating = true;
|
|
1038
|
-
try {
|
|
1039
|
-
const { values, labels } = this.#selectedChoices();
|
|
1040
|
-
for (const validator of this.#validators) {
|
|
1041
|
-
let validationResult;
|
|
1042
|
-
const result = validator.validate(values);
|
|
1043
|
-
if (result instanceof Promise) {
|
|
1044
|
-
let dotCount = 1;
|
|
1045
|
-
render({ validating: `validating${".".repeat(dotCount)}` });
|
|
1046
|
-
const spinnerInterval = setInterval(() => {
|
|
1047
|
-
dotCount = dotCount % 3 + 1;
|
|
1048
|
-
render({ validating: `validating${".".repeat(dotCount)}` });
|
|
1049
|
-
}, VALIDATION_SPINNER_INTERVAL);
|
|
1050
|
-
try {
|
|
1051
|
-
validationResult = await result;
|
|
1052
|
-
} finally {
|
|
1053
|
-
clearInterval(spinnerInterval);
|
|
1054
|
-
}
|
|
1055
|
-
} else {
|
|
1056
|
-
validationResult = result;
|
|
1057
|
-
}
|
|
1058
|
-
if (isValid(validationResult) === false) {
|
|
1059
|
-
render({ error: resultError(validationResult) });
|
|
1060
|
-
return;
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
render({ clearRender: true });
|
|
1064
|
-
this.#showAnsweredQuestion(labels.join(", "));
|
|
1065
|
-
this.write(SYMBOLS.ShowCursor);
|
|
1066
|
-
this.destroy();
|
|
1067
|
-
this.#onProcessExit();
|
|
1068
|
-
process.off("exit", this.#boundExitEvent);
|
|
1069
|
-
resolve(values);
|
|
1070
|
-
} finally {
|
|
1071
|
-
this.#isValidating = false;
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
#showAnsweredQuestion(choices, isAgentAnswer = false) {
|
|
1075
|
-
const prefixSymbol = this.selectedIndexes.size === 0 && !isAgentAnswer ? SYMBOLS.Cross : SYMBOLS.Tick;
|
|
1076
|
-
const prefix = `${prefixSymbol} ${(0, import_node_util7.styleText)("bold", this.message)} ${SYMBOLS.Pointer}`;
|
|
1077
|
-
const formattedChoice = (0, import_node_util7.styleText)("yellow", choices);
|
|
1078
|
-
this.write(`${prefix}${choices ? ` ${formattedChoice}` : ""}${import_node_os5.EOL}`);
|
|
1079
|
-
}
|
|
1080
|
-
#selectedChoices() {
|
|
1081
|
-
return [...this.selectedIndexes].reduce(
|
|
1082
|
-
(acc, index) => {
|
|
1083
|
-
const choice = this.filteredChoices[index];
|
|
1084
|
-
if (typeof choice === "string") {
|
|
1085
|
-
acc.values.push(choice);
|
|
1086
|
-
acc.labels.push(choice);
|
|
1087
|
-
} else {
|
|
1088
|
-
acc.values.push(choice.value);
|
|
1089
|
-
acc.labels.push(choice.label);
|
|
1090
|
-
}
|
|
1091
|
-
return acc;
|
|
1092
|
-
},
|
|
1093
|
-
{
|
|
1094
|
-
values: [],
|
|
1095
|
-
labels: []
|
|
1096
|
-
}
|
|
1097
|
-
);
|
|
1098
|
-
}
|
|
1099
|
-
#onProcessExit() {
|
|
1100
|
-
this.stdin.off("keypress", this.#boundKeyPressEvent);
|
|
1101
|
-
this.stdout.moveCursor(-this.stdout.columns, 0);
|
|
1102
|
-
this.stdout.clearScreenDown();
|
|
1103
|
-
this.write(SYMBOLS.ShowCursor);
|
|
1104
|
-
}
|
|
1105
|
-
#onKeypress(...args) {
|
|
1106
|
-
const [resolve, render, , key] = args;
|
|
1107
|
-
if (this.#isValidating) {
|
|
1108
|
-
return;
|
|
1109
|
-
}
|
|
1110
|
-
if (key.name === "up") {
|
|
1111
|
-
this.activeIndex = this.activeIndex === 0 ? this.filteredChoices.length - 1 : this.activeIndex - 1;
|
|
1112
|
-
render();
|
|
1113
|
-
} else if (key.name === "down") {
|
|
1114
|
-
this.activeIndex = this.activeIndex === this.filteredChoices.length - 1 ? 0 : this.activeIndex + 1;
|
|
1115
|
-
render();
|
|
1116
|
-
} else if (key.ctrl && key.name === "a") {
|
|
1117
|
-
this.selectedIndexes = this.selectedIndexes.size === this.filteredChoices.length ? /* @__PURE__ */ new Set() : new Set(this.filteredChoices.map((_, index) => index));
|
|
1118
|
-
render();
|
|
1119
|
-
} else if (key.name === "right") {
|
|
1120
|
-
this.selectedIndexes.add(this.activeIndex);
|
|
1121
|
-
render();
|
|
1122
|
-
} else if (key.name === "left") {
|
|
1123
|
-
this.selectedIndexes = new Set([...this.selectedIndexes].filter((index) => index !== this.activeIndex));
|
|
1124
|
-
render();
|
|
1125
|
-
} else if (key.name === "return") {
|
|
1126
|
-
void this.#handleReturn(resolve, render);
|
|
1127
|
-
} else {
|
|
1128
|
-
if (!key.ctrl && this.options.autocomplete) {
|
|
1129
|
-
this.selectedIndexes.clear();
|
|
1130
|
-
this.activeIndex = 0;
|
|
1131
|
-
if (key.name === "backspace" && this.autocompleteValue.length > 0) {
|
|
1132
|
-
this.autocompleteValue = this.autocompleteValue.slice(0, -1);
|
|
1133
|
-
} else if (key.name !== "backspace") {
|
|
1134
|
-
this.autocompleteValue += key.sequence;
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
render();
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
async listen() {
|
|
1141
|
-
if (this.skip) {
|
|
1142
|
-
this.destroy();
|
|
1143
|
-
const { values } = this.#selectedChoices();
|
|
1144
|
-
return values;
|
|
1145
|
-
}
|
|
1146
|
-
const answer = this.agent.nextAnswers.shift();
|
|
1147
|
-
if (answer !== void 0) {
|
|
1148
|
-
const formatedAnser = Array.isArray(answer) ? answer.join(", ") : answer;
|
|
1149
|
-
this.#showAnsweredQuestion(formatedAnser, true);
|
|
1150
|
-
this.destroy();
|
|
1151
|
-
return Array.isArray(answer) ? answer : [answer];
|
|
1152
|
-
}
|
|
1153
|
-
this.transformer = () => null;
|
|
1154
|
-
this.write(SYMBOLS.HideCursor);
|
|
1155
|
-
this.#showQuestion();
|
|
1156
|
-
const render = (options = {}) => {
|
|
1157
|
-
const {
|
|
1158
|
-
initialRender = false,
|
|
1159
|
-
clearRender = false,
|
|
1160
|
-
error = null,
|
|
1161
|
-
validating = null
|
|
1162
|
-
} = options;
|
|
1163
|
-
if (!initialRender) {
|
|
1164
|
-
let linesToClear = this.lastRender.endIndex - this.lastRender.startIndex;
|
|
1165
|
-
while (linesToClear > 0) {
|
|
1166
|
-
this.clearLastLine();
|
|
1167
|
-
linesToClear--;
|
|
1168
|
-
}
|
|
1169
|
-
if (this.options.autocomplete) {
|
|
1170
|
-
let linesToClear2 = Math.ceil(
|
|
1171
|
-
stringLength(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
|
|
1172
|
-
);
|
|
1173
|
-
while (linesToClear2 > 0) {
|
|
1174
|
-
this.clearLastLine();
|
|
1175
|
-
linesToClear2--;
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
if (clearRender) {
|
|
1180
|
-
this.clearLastLine();
|
|
1181
|
-
return;
|
|
1182
|
-
}
|
|
1183
|
-
if (error || validating) {
|
|
1184
|
-
this.clearLastLine();
|
|
1185
|
-
this.#showQuestion(error, validating);
|
|
1186
|
-
}
|
|
1187
|
-
this.#showChoices();
|
|
1188
|
-
};
|
|
1189
|
-
render({ initialRender: true });
|
|
1190
|
-
this.#boundExitEvent = this.#onProcessExit.bind(this);
|
|
1191
|
-
process.once("exit", this.#boundExitEvent);
|
|
1192
|
-
const { promise, resolve } = Promise.withResolvers();
|
|
1193
|
-
this.#boundKeyPressEvent = this.#onKeypress.bind(this, resolve, render);
|
|
1194
|
-
this.stdin.on("keypress", this.#boundKeyPressEvent);
|
|
1195
|
-
return promise;
|
|
1196
|
-
}
|
|
1197
|
-
#showQuestion(error = null, validating = null) {
|
|
1198
|
-
let hint = this.#showHint ? (0, import_node_util7.styleText)(
|
|
1199
|
-
"gray",
|
|
1200
|
-
// eslint-disable-next-line @stylistic/max-len
|
|
1201
|
-
`(Press ${(0, import_node_util7.styleText)("bold", "<Ctrl+A>")} to toggle all, ${(0, import_node_util7.styleText)("bold", "<Left/Right>")} to toggle, ${(0, import_node_util7.styleText)("bold", "<Return>")} to submit)`
|
|
1202
|
-
) : "";
|
|
1203
|
-
if (validating) {
|
|
1204
|
-
hint += `${hint.length > 0 ? " " : ""}${(0, import_node_util7.styleText)("yellow", `[${validating}]`)}`;
|
|
1205
|
-
} else if (error) {
|
|
1206
|
-
hint += `${hint.length > 0 ? " " : ""}${(0, import_node_util7.styleText)(["red", "bold"], `[${error}]`)}`;
|
|
1207
|
-
}
|
|
1208
|
-
this.questionMessage = `${SYMBOLS.QuestionMark} ${(0, import_node_util7.styleText)("bold", this.message)}${hint.length > 0 ? ` ${hint}` : ""}`;
|
|
1209
|
-
this.write(`${this.questionMessage}${import_node_os5.EOL}`);
|
|
1210
|
-
}
|
|
1211
|
-
};
|
|
1212
|
-
|
|
1213
|
-
// src/index.ts
|
|
1214
|
-
async function question(message, options = {}) {
|
|
1215
|
-
const prompt = new QuestionPrompt(
|
|
1216
|
-
{ ...options, message }
|
|
1217
|
-
);
|
|
1218
|
-
const onErrorSignal = new AbortController();
|
|
1219
|
-
const onError = (0, import_node_events2.once)(
|
|
1220
|
-
prompt,
|
|
1221
|
-
"error",
|
|
1222
|
-
{ signal: onErrorSignal.signal }
|
|
1223
|
-
);
|
|
1224
|
-
const result = await Promise.race([
|
|
1225
|
-
prompt.listen(),
|
|
1226
|
-
onError
|
|
1227
|
-
]);
|
|
1228
|
-
if (isAbortError(result)) {
|
|
1229
|
-
prompt.destroy();
|
|
1230
|
-
throw result[0];
|
|
1231
|
-
}
|
|
1232
|
-
onErrorSignal.abort();
|
|
1233
|
-
return result;
|
|
1234
|
-
}
|
|
1235
|
-
async function select(message, options) {
|
|
1236
|
-
const prompt = new SelectPrompt(
|
|
1237
|
-
{ ...options, message }
|
|
1238
|
-
);
|
|
1239
|
-
const onErrorSignal = new AbortController();
|
|
1240
|
-
const onError = (0, import_node_events2.once)(
|
|
1241
|
-
prompt,
|
|
1242
|
-
"error",
|
|
1243
|
-
{ signal: onErrorSignal.signal }
|
|
1244
|
-
);
|
|
1245
|
-
const result = await Promise.race([
|
|
1246
|
-
prompt.listen(),
|
|
1247
|
-
onError
|
|
1248
|
-
]);
|
|
1249
|
-
if (isAbortError(result)) {
|
|
1250
|
-
prompt.destroy();
|
|
1251
|
-
throw result[0];
|
|
1252
|
-
}
|
|
1253
|
-
onErrorSignal.abort();
|
|
1254
|
-
return result;
|
|
1255
|
-
}
|
|
1256
|
-
async function confirm(message, options = {}) {
|
|
1257
|
-
const prompt = new ConfirmPrompt(
|
|
1258
|
-
{ ...options, message }
|
|
1259
|
-
);
|
|
1260
|
-
const onErrorSignal = new AbortController();
|
|
1261
|
-
const onError = (0, import_node_events2.once)(
|
|
1262
|
-
prompt,
|
|
1263
|
-
"error",
|
|
1264
|
-
{ signal: onErrorSignal.signal }
|
|
1265
|
-
);
|
|
1266
|
-
const result = await Promise.race([
|
|
1267
|
-
prompt.listen(),
|
|
1268
|
-
onError
|
|
1269
|
-
]);
|
|
1270
|
-
if (isAbortError(result)) {
|
|
1271
|
-
prompt.destroy();
|
|
1272
|
-
throw result[0];
|
|
1273
|
-
}
|
|
1274
|
-
onErrorSignal.abort();
|
|
1275
|
-
return result;
|
|
1276
|
-
}
|
|
1277
|
-
async function multiselect(message, options) {
|
|
1278
|
-
const prompt = new MultiselectPrompt(
|
|
1279
|
-
{ ...options, message }
|
|
1280
|
-
);
|
|
1281
|
-
const onErrorSignal = new AbortController();
|
|
1282
|
-
const onError = (0, import_node_events2.once)(
|
|
1283
|
-
prompt,
|
|
1284
|
-
"error",
|
|
1285
|
-
{ signal: onErrorSignal.signal }
|
|
1286
|
-
);
|
|
1287
|
-
const result = await Promise.race([
|
|
1288
|
-
prompt.listen(),
|
|
1289
|
-
onError
|
|
1290
|
-
]);
|
|
1291
|
-
if (isAbortError(result)) {
|
|
1292
|
-
prompt.destroy();
|
|
1293
|
-
throw result[0];
|
|
1294
|
-
}
|
|
1295
|
-
onErrorSignal.abort();
|
|
1296
|
-
return result;
|
|
1297
|
-
}
|
|
1298
|
-
function isAbortError(error) {
|
|
1299
|
-
return Array.isArray(error) && error.length > 0 && error[0] instanceof Error;
|
|
1300
|
-
}
|
|
1301
|
-
var validators = { required };
|
|
1302
|
-
var transformers = { number, integer, url };
|
|
1303
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
1304
|
-
0 && (module.exports = {
|
|
1305
|
-
PromptAgent,
|
|
1306
|
-
confirm,
|
|
1307
|
-
multiselect,
|
|
1308
|
-
question,
|
|
1309
|
-
select,
|
|
1310
|
-
transformers,
|
|
1311
|
-
validators
|
|
1312
|
-
});
|