@topcli/prompts 1.10.0 → 1.11.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/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2023-2024 Pierre Demailly
1
+ Copyright 2023-2024 TopCli
2
2
 
3
3
  Permission to use, copy, modify, and/or distribute this software for any purpose
4
4
  with or without fee is hereby granted, provided that the above copyright notice
package/README.md CHANGED
@@ -4,7 +4,10 @@
4
4
  ![version](https://img.shields.io/badge/dynamic/json.svg?style=for-the-badge&url=https://raw.githubusercontent.com/TopCli/prompts/main/package.json&query=$.version&label=Version)
5
5
  [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg?style=for-the-badge)](https://github.com/TopCli/prompts/commit-activity)
6
6
  [![isc](https://img.shields.io/badge/License-ISC-blue.svg?style=for-the-badge)](https://github.com/TopCli/prompts/blob/main/LICENSE)
7
+ [![scorecard](https://api.securityscorecards.dev/projects/github.com/TopCli/prompts/badge?style=for-the-badge)](https://ossf.github.io/scorecard-visualizer/#/projects/github.com/TopCli/prompts)
7
8
  ![build](https://img.shields.io/github/actions/workflow/status/TopCli/prompts/node.js.yml?style=for-the-badge)
9
+
10
+ <img src="./public/topcli.gif" alt="demo">
8
11
  </div>
9
12
 
10
13
  ## Requirements
@@ -52,18 +55,21 @@ Simple prompt, similar to `rl.question()` with an improved UI.
52
55
 
53
56
  Use `options.secure` if you need to hide both input and answer.
54
57
 
55
- Use `options.validators` to handle user input.
56
-
57
58
  Use `options.signal` to set an `AbortSignal` (throws a [AbortError](#aborterror)).
58
59
 
60
+ Use `options.validators` to handle user input.
61
+
59
62
  **Example**
60
63
 
61
64
  ```js
62
65
  const packageName = await question('Package name', {
63
66
  validators: [
64
67
  {
65
- validate: (value) => !existsSync(join(process.cwd(), value)),
66
- error: (value) => `Folder ${value} already exists`
68
+ validate: (value) => {
69
+ if (!existsSync(join(process.cwd(), value))) {
70
+ return `Folder ${value} already exists`
71
+ }
72
+ }
67
73
  }
68
74
  ]
69
75
  });
@@ -124,7 +130,10 @@ Use `options.signal` to set an `AbortSignal` (throws a [AbortError](#aborterror)
124
130
  confirm(message: string, options?: ConfirmOptions): Promise<boolean>
125
131
  ```
126
132
 
127
- Boolean prompt, return `options.initial` if user input is different from `y`/`yes`/`n`/`no` (case insensitive), (default `false`).
133
+ Boolean prompt, default to `options.initial` (`false`).
134
+
135
+ > [!TIP]
136
+ > You can answer pressing <kbd>Y</kbd> or <kbd>N</kbd>
128
137
 
129
138
  Use `options.signal` to set an `AbortSignal` (throws a [AbortError](#aborterror)).
130
139
 
@@ -221,6 +230,12 @@ export interface ConfirmOptions extends SharedOptions {
221
230
  }
222
231
  ```
223
232
 
233
+ ## Contributing
234
+
235
+ Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.
236
+
237
+ Open an issue if you want to provide feedback such as bug reports or enchancements.
238
+
224
239
  ## Contributors
225
240
 
226
241
  <!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
package/dist/index.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -40,7 +41,7 @@ module.exports = __toCommonJS(prompts_exports);
40
41
 
41
42
  // src/prompts/question.ts
42
43
  var import_node_os2 = require("os");
43
- var import_kleur2 = __toESM(require("kleur"), 1);
44
+ var import_node_util2 = require("util");
44
45
  var import_wcwidth = __toESM(require("@topcli/wcwidth"), 1);
45
46
 
46
47
  // src/prompts/abstract.ts
@@ -54,7 +55,7 @@ var import_node_process = __toESM(require("process"), 1);
54
55
  var kAnsiRegex = ansiRegex();
55
56
  function ansiRegex({ onlyFirst = false } = {}) {
56
57
  const pattern = [
57
- // eslint-disable-next-line max-len
58
+ // eslint-disable-next-line @stylistic/max-len
58
59
  "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
59
60
  "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"
60
61
  ].join("|");
@@ -146,6 +147,12 @@ var AbstractPrompt = class _AbstractPrompt extends import_node_events.default {
146
147
  if (typeof message !== "string") {
147
148
  throw new TypeError(`message must be string, ${typeof message} given.`);
148
149
  }
150
+ if (!output.isTTY) {
151
+ Object.assign(output, {
152
+ moveCursor: () => void 0,
153
+ clearScreenDown: () => void 0
154
+ });
155
+ }
149
156
  this.stdin = input;
150
157
  this.stdout = output;
151
158
  this.message = message;
@@ -176,6 +183,9 @@ var AbstractPrompt = class _AbstractPrompt extends import_node_events.default {
176
183
  }
177
184
  this.emit("error", new AbortError("Prompt aborted"));
178
185
  };
186
+ if (this.signal.aborted) {
187
+ this.#signalHandler();
188
+ }
179
189
  this.signal.addEventListener("abort", this.#signalHandler, { once: true });
180
190
  }
181
191
  }
@@ -204,7 +214,7 @@ var AbstractPrompt = class _AbstractPrompt extends import_node_events.default {
204
214
  };
205
215
 
206
216
  // src/constants.ts
207
- var import_kleur = __toESM(require("kleur"), 1);
217
+ var import_node_util = require("util");
208
218
  var kMainSymbols = {
209
219
  tick: "\u2714",
210
220
  cross: "\u2716",
@@ -224,20 +234,45 @@ var kFallbackSymbols = {
224
234
  inactive: "(-)"
225
235
  };
226
236
  var kSymbols = isUnicodeSupported() || process.env.CI ? kMainSymbols : kFallbackSymbols;
227
- var kPointer = import_kleur.default.gray(kSymbols.pointer);
237
+ var kPointer = (0, import_node_util.styleText)("gray", kSymbols.pointer);
228
238
  var SYMBOLS = {
229
- QuestionMark: import_kleur.default.blue().bold("?"),
230
- Tick: import_kleur.default.green().bold(kSymbols.tick),
231
- Cross: import_kleur.default.red().bold(kSymbols.cross),
239
+ QuestionMark: (0, import_node_util.styleText)(["blue", "bold"], "?"),
240
+ Tick: (0, import_node_util.styleText)(["green", "bold"], kSymbols.tick),
241
+ Cross: (0, import_node_util.styleText)(["red", "bold"], kSymbols.cross),
232
242
  Pointer: kPointer,
233
243
  Previous: kSymbols.previous,
234
244
  Next: kSymbols.next,
235
245
  ShowCursor: "\x1B[?25h",
236
246
  HideCursor: "\x1B[?25l",
237
- Active: import_kleur.default.cyan(kSymbols.active),
238
- Inactive: import_kleur.default.gray(kSymbols.inactive)
247
+ Active: (0, import_node_util.styleText)("cyan", kSymbols.active),
248
+ Inactive: (0, import_node_util.styleText)("gray", kSymbols.inactive)
239
249
  };
240
250
 
251
+ // src/validators.ts
252
+ function required() {
253
+ return {
254
+ validate: (input) => {
255
+ const isValid2 = Array.isArray(input) ? input.length > 0 : Boolean(input);
256
+ return isValid2 ? null : { isValid: isValid2, error: "required" };
257
+ }
258
+ };
259
+ }
260
+ function isValid(result) {
261
+ if (typeof result === "object") {
262
+ return result?.isValid !== false;
263
+ }
264
+ if (typeof result === "string") {
265
+ return result.length > 0;
266
+ }
267
+ return true;
268
+ }
269
+ function resultError(result) {
270
+ if (typeof result === "object") {
271
+ return result.error;
272
+ }
273
+ return result;
274
+ }
275
+
241
276
  // src/prompts/question.ts
242
277
  var QuestionPrompt = class extends AbstractPrompt {
243
278
  defaultValue;
@@ -277,16 +312,16 @@ var QuestionPrompt = class extends AbstractPrompt {
277
312
  });
278
313
  }
279
314
  #getQuestionQuery() {
280
- return `${import_kleur2.default.bold(`${SYMBOLS.QuestionMark} ${this.message}${this.tip}`)} ${this.questionSuffixError}`;
315
+ return `${(0, import_node_util2.styleText)("bold", `${SYMBOLS.QuestionMark} ${this.message}${this.tip}`)} ${this.questionSuffixError}`;
281
316
  }
282
317
  #setQuestionSuffixError(error) {
283
- const suffix = import_kleur2.default.red(`[${error}] `);
318
+ const suffix = (0, import_node_util2.styleText)("red", `[${error}] `);
284
319
  this.questionSuffixError = suffix;
285
320
  }
286
321
  #writeAnswer() {
287
322
  const prefix = this.answer ? SYMBOLS.Tick : SYMBOLS.Cross;
288
- const answer = import_kleur2.default.yellow(this.#secure ? "CONFIDENTIAL" : this.answer ?? "");
289
- this.write(`${prefix} ${import_kleur2.default.bold(this.message)} ${SYMBOLS.Pointer} ${answer}${import_node_os2.EOL}`);
323
+ const answer = (0, import_node_util2.styleText)("yellow", this.#secure ? "CONFIDENTIAL" : this.answer ?? "");
324
+ this.write(`${prefix} ${(0, import_node_util2.styleText)("bold", this.message)} ${SYMBOLS.Pointer} ${answer}${import_node_os2.EOL}`);
290
325
  }
291
326
  #onQuestionAnswer() {
292
327
  const questionLineCount = Math.ceil(
@@ -295,9 +330,9 @@ var QuestionPrompt = class extends AbstractPrompt {
295
330
  this.stdout.moveCursor(-this.stdout.columns, -questionLineCount);
296
331
  this.stdout.clearScreenDown();
297
332
  for (const validator of this.#validators) {
298
- if (!validator.validate(this.answer)) {
299
- const error = validator.error(this.answer);
300
- this.#setQuestionSuffixError(error);
333
+ const validationResult = validator.validate(this.answer);
334
+ if (isValid(validationResult) === false) {
335
+ this.#setQuestionSuffixError(resultError(validationResult));
301
336
  this.answerBuffer = this.#question();
302
337
  return;
303
338
  }
@@ -334,7 +369,7 @@ var QuestionPrompt = class extends AbstractPrompt {
334
369
 
335
370
  // src/prompts/confirm.ts
336
371
  var import_node_os3 = require("os");
337
- var import_kleur3 = __toESM(require("kleur"), 1);
372
+ var import_node_util3 = require("util");
338
373
  var import_wcwidth2 = __toESM(require("@topcli/wcwidth"), 1);
339
374
  var kToggleKeys = /* @__PURE__ */ new Set([
340
375
  "left",
@@ -365,8 +400,8 @@ var ConfirmPrompt = class extends AbstractPrompt {
365
400
  this.selectedValue = initial;
366
401
  }
367
402
  #getHint() {
368
- const Yes = import_kleur3.default.bold().underline().cyan("Yes");
369
- const No = import_kleur3.default.bold().underline().cyan("No");
403
+ const Yes = (0, import_node_util3.styleText)(["cyan", "bold", "underline"], "Yes");
404
+ const No = (0, import_node_util3.styleText)(["cyan", "bold", "underline"], "No");
370
405
  return this.selectedValue ? `${Yes}/No` : `Yes/${No}`;
371
406
  }
372
407
  #render() {
@@ -412,7 +447,7 @@ var ConfirmPrompt = class extends AbstractPrompt {
412
447
  this.stdin.off("keypress", this.#boundKeyPressEvent);
413
448
  }
414
449
  #getQuestionQuery() {
415
- const query = import_kleur3.default.bold(`${SYMBOLS.QuestionMark} ${this.message}`);
450
+ const query = (0, import_node_util3.styleText)("bold", `${SYMBOLS.QuestionMark} ${this.message}`);
416
451
  return `${query} ${this.#getHint()}`;
417
452
  }
418
453
  #onQuestionAnswer() {
@@ -422,7 +457,7 @@ var ConfirmPrompt = class extends AbstractPrompt {
422
457
  -Math.floor((0, import_wcwidth2.default)(stripAnsi(this.#getQuestionQuery())) / this.stdout.columns)
423
458
  );
424
459
  this.stdout.clearScreenDown();
425
- this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${import_kleur3.default.bold(this.message)}${import_node_os3.EOL}`);
460
+ this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${(0, import_node_util3.styleText)("bold", this.message)}${import_node_os3.EOL}`);
426
461
  }
427
462
  confirm() {
428
463
  return new Promise(async (resolve, reject) => {
@@ -454,7 +489,7 @@ var ConfirmPrompt = class extends AbstractPrompt {
454
489
 
455
490
  // src/prompts/select.ts
456
491
  var import_node_os4 = require("os");
457
- var import_kleur4 = __toESM(require("kleur"), 1);
492
+ var import_node_util4 = require("util");
458
493
  var import_wcwidth3 = __toESM(require("@topcli/wcwidth"), 1);
459
494
  var kRequiredChoiceProperties = ["label", "value"];
460
495
  var SelectPrompt = class extends AbstractPrompt {
@@ -561,15 +596,15 @@ var SelectPrompt = class extends AbstractPrompt {
561
596
  this.longestChoice < 10 ? this.longestChoice : 0
562
597
  );
563
598
  const formattedDescription = choice.description ? ` - ${choice.description}` : "";
564
- const color = isChoiceSelected ? import_kleur4.default.white().bold : import_kleur4.default.gray;
565
- const str = `${prefix}${color(`${formattedLabel}${formattedDescription}`)}${import_node_os4.EOL}`;
599
+ const styles = isChoiceSelected ? ["white", "bold"] : ["gray"];
600
+ const str = `${prefix}${(0, import_node_util4.styleText)(styles, `${formattedLabel}${formattedDescription}`)}${import_node_os4.EOL}`;
566
601
  this.write(str);
567
602
  }
568
603
  }
569
604
  #showAnsweredQuestion(choice) {
570
605
  const symbolPrefix = choice === "" ? SYMBOLS.Cross : SYMBOLS.Tick;
571
- const prefix = `${symbolPrefix} ${import_kleur4.default.bold(this.message)} ${SYMBOLS.Pointer}`;
572
- const formattedChoice = import_kleur4.default.yellow(typeof choice === "string" ? choice : choice.label);
606
+ const prefix = `${symbolPrefix} ${(0, import_node_util4.styleText)("bold", this.message)} ${SYMBOLS.Pointer}`;
607
+ const formattedChoice = (0, import_node_util4.styleText)("yellow", typeof choice === "string" ? choice : choice.label);
573
608
  this.write(`${prefix} ${formattedChoice}${import_node_os4.EOL}`);
574
609
  }
575
610
  #onProcessExit() {
@@ -591,9 +626,9 @@ var SelectPrompt = class extends AbstractPrompt {
591
626
  const label = typeof choice === "string" ? choice : choice.label;
592
627
  const value = typeof choice === "string" ? choice : choice.value;
593
628
  for (const validator of this.#validators) {
594
- if (!validator.validate(value)) {
595
- const error = validator.error(value);
596
- render({ error });
629
+ const validationResult = validator.validate(value);
630
+ if (isValid(validationResult) === false) {
631
+ render({ error: resultError(validationResult) });
597
632
  return;
598
633
  }
599
634
  }
@@ -680,16 +715,16 @@ var SelectPrompt = class extends AbstractPrompt {
680
715
  #showQuestion(error = null) {
681
716
  let hint = "";
682
717
  if (error) {
683
- hint = ` ${hint.length > 0 ? " " : ""}${import_kleur4.default.red().bold(`[${error}]`)}`;
718
+ hint = ` ${hint.length > 0 ? " " : ""}${(0, import_node_util4.styleText)(["red", "bold"], `[${error}]`)}`;
684
719
  }
685
- this.questionMessage = `${SYMBOLS.QuestionMark} ${import_kleur4.default.bold(this.message)}${hint}`;
720
+ this.questionMessage = `${SYMBOLS.QuestionMark} ${(0, import_node_util4.styleText)("bold", this.message)}${hint}`;
686
721
  this.write(`${this.questionMessage}${import_node_os4.EOL}`);
687
722
  }
688
723
  };
689
724
 
690
725
  // src/prompts/multiselect.ts
691
726
  var import_node_os5 = require("os");
692
- var import_kleur5 = __toESM(require("kleur"), 1);
727
+ var import_node_util5 = require("util");
693
728
  var import_wcwidth4 = __toESM(require("@topcli/wcwidth"), 1);
694
729
  var kRequiredChoiceProperties2 = ["label", "value"];
695
730
  var MultiselectPrompt = class extends AbstractPrompt {
@@ -818,15 +853,15 @@ var MultiselectPrompt = class extends AbstractPrompt {
818
853
  this.longestChoice < 10 ? this.longestChoice : 0
819
854
  );
820
855
  const formattedDescription = choice.description ? ` - ${choice.description}` : "";
821
- const color = isChoiceActive ? import_kleur5.default.white().bold : import_kleur5.default.gray;
822
- const str = `${prefix} ${color(`${formattedLabel}${formattedDescription}`)}${import_node_os5.EOL}`;
856
+ const styles = isChoiceActive ? ["white", "bold"] : ["gray"];
857
+ const str = `${prefix} ${(0, import_node_util5.styleText)(styles, `${formattedLabel}${formattedDescription}`)}${import_node_os5.EOL}`;
823
858
  this.write(str);
824
859
  }
825
860
  }
826
861
  #showAnsweredQuestion(choices, isAgentAnswer = false) {
827
862
  const prefixSymbol = this.selectedIndexes.size === 0 && !isAgentAnswer ? SYMBOLS.Cross : SYMBOLS.Tick;
828
- const prefix = `${prefixSymbol} ${import_kleur5.default.bold(this.message)} ${SYMBOLS.Pointer}`;
829
- const formattedChoice = import_kleur5.default.yellow(choices);
863
+ const prefix = `${prefixSymbol} ${(0, import_node_util5.styleText)("bold", this.message)} ${SYMBOLS.Pointer}`;
864
+ const formattedChoice = (0, import_node_util5.styleText)("yellow", choices);
830
865
  this.write(`${prefix}${choices ? ` ${formattedChoice}` : ""}${import_node_os5.EOL}`);
831
866
  }
832
867
  #onProcessExit() {
@@ -862,9 +897,9 @@ var MultiselectPrompt = class extends AbstractPrompt {
862
897
  return typeof choice === "string" ? choice : choice.value;
863
898
  });
864
899
  for (const validator of this.#validators) {
865
- if (!validator.validate(values)) {
866
- const error = validator.error(values);
867
- render({ error });
900
+ const validationResult = validator.validate(values);
901
+ if (isValid(validationResult) === false) {
902
+ render({ error: resultError(validationResult) });
868
903
  return;
869
904
  }
870
905
  }
@@ -949,26 +984,19 @@ var MultiselectPrompt = class extends AbstractPrompt {
949
984
  });
950
985
  }
951
986
  #showQuestion(error = null) {
952
- let hint = this.#showHint ? import_kleur5.default.gray(
953
- // eslint-disable-next-line max-len
954
- `(Press ${import_kleur5.default.bold("<Ctrl+A>")} to toggle all, ${import_kleur5.default.bold("<Left/Right>")} to toggle, ${import_kleur5.default.bold("<Return>")} to submit)`
987
+ let hint = this.#showHint ? (0, import_node_util5.styleText)(
988
+ "gray",
989
+ // eslint-disable-next-line @stylistic/max-len
990
+ `(Press ${(0, import_node_util5.styleText)("bold", "<Ctrl+A>")} to toggle all, ${(0, import_node_util5.styleText)("bold", "<Left/Right>")} to toggle, ${(0, import_node_util5.styleText)("bold", "<Return>")} to submit)`
955
991
  ) : "";
956
992
  if (error) {
957
- hint += `${hint.length > 0 ? " " : ""}${import_kleur5.default.red().bold(`[${error}]`)}`;
993
+ hint += `${hint.length > 0 ? " " : ""}${(0, import_node_util5.styleText)(["red", "bold"], `[${error}]`)}`;
958
994
  }
959
- this.questionMessage = `${SYMBOLS.QuestionMark} ${import_kleur5.default.bold(this.message)}${hint.length > 0 ? ` ${hint}` : ""}`;
995
+ this.questionMessage = `${SYMBOLS.QuestionMark} ${(0, import_node_util5.styleText)("bold", this.message)}${hint.length > 0 ? ` ${hint}` : ""}`;
960
996
  this.write(`${this.questionMessage}${import_node_os5.EOL}`);
961
997
  }
962
998
  };
963
999
 
964
- // src/validators.ts
965
- function required() {
966
- return {
967
- validate: (input) => Array.isArray(input) ? input.length > 0 : Boolean(input),
968
- error: () => "required"
969
- };
970
- }
971
-
972
1000
  // index.ts
973
1001
  async function question(message, options = {}) {
974
1002
  return new QuestionPrompt({ ...options, message }).question();
@@ -977,7 +1005,7 @@ async function select(message, options) {
977
1005
  const selectPrompt = new SelectPrompt({ ...options, message });
978
1006
  return selectPrompt.select();
979
1007
  }
980
- async function confirm(message, options) {
1008
+ async function confirm(message, options = {}) {
981
1009
  const confirmPrompt = new ConfirmPrompt({ ...options, message });
982
1010
  return confirmPrompt.confirm();
983
1011
  }
package/dist/index.d.cts CHANGED
@@ -1,6 +1,15 @@
1
+ type ValidResponseObject = {
2
+ isValid?: true;
3
+ };
4
+ type InvalidResponseObject = {
5
+ isValid: false;
6
+ error: string;
7
+ };
8
+ type ValidationResponse = InvalidResponse | ValidResponse;
9
+ type InvalidResponse = string | InvalidResponseObject;
10
+ type ValidResponse = null | undefined | true | ValidResponseObject;
1
11
  interface PromptValidator<T = string | string[] | boolean> {
2
- validate: (input: T) => boolean;
3
- error: (input: T) => string;
12
+ validate: (input: T) => ValidationResponse;
4
13
  }
5
14
  declare function required<T = string | string[] | boolean>(): PromptValidator<T>;
6
15
 
@@ -80,7 +89,7 @@ interface SelectOptions extends AbstractPromptOptions {
80
89
 
81
90
  declare function question(message: string, options?: Omit<QuestionOptions, "message">): Promise<string>;
82
91
  declare function select(message: string, options: Omit<SelectOptions, "message">): Promise<string>;
83
- declare function confirm(message: string, options: Omit<ConfirmOptions, "message">): Promise<boolean>;
92
+ declare function confirm(message: string, options?: Omit<ConfirmOptions, "message">): Promise<boolean>;
84
93
  declare function multiselect(message: string, options: Omit<MultiselectOptions, "message">): Promise<string[]>;
85
94
 
86
95
  export { type AbstractPromptOptions, type Choice, type ConfirmOptions, type MultiselectOptions, PromptAgent, type PromptValidator, type QuestionOptions, type SelectOptions, confirm, multiselect, question, required, select };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,15 @@
1
+ type ValidResponseObject = {
2
+ isValid?: true;
3
+ };
4
+ type InvalidResponseObject = {
5
+ isValid: false;
6
+ error: string;
7
+ };
8
+ type ValidationResponse = InvalidResponse | ValidResponse;
9
+ type InvalidResponse = string | InvalidResponseObject;
10
+ type ValidResponse = null | undefined | true | ValidResponseObject;
1
11
  interface PromptValidator<T = string | string[] | boolean> {
2
- validate: (input: T) => boolean;
3
- error: (input: T) => string;
12
+ validate: (input: T) => ValidationResponse;
4
13
  }
5
14
  declare function required<T = string | string[] | boolean>(): PromptValidator<T>;
6
15
 
@@ -80,7 +89,7 @@ interface SelectOptions extends AbstractPromptOptions {
80
89
 
81
90
  declare function question(message: string, options?: Omit<QuestionOptions, "message">): Promise<string>;
82
91
  declare function select(message: string, options: Omit<SelectOptions, "message">): Promise<string>;
83
- declare function confirm(message: string, options: Omit<ConfirmOptions, "message">): Promise<boolean>;
92
+ declare function confirm(message: string, options?: Omit<ConfirmOptions, "message">): Promise<boolean>;
84
93
  declare function multiselect(message: string, options: Omit<MultiselectOptions, "message">): Promise<string[]>;
85
94
 
86
95
  export { type AbstractPromptOptions, type Choice, type ConfirmOptions, type MultiselectOptions, PromptAgent, type PromptValidator, type QuestionOptions, type SelectOptions, confirm, multiselect, question, required, select };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/prompts/question.ts
2
2
  import { EOL as EOL2 } from "os";
3
- import kleur2 from "kleur";
3
+ import { styleText as styleText2 } from "util";
4
4
  import wcwidth from "@topcli/wcwidth";
5
5
 
6
6
  // src/prompts/abstract.ts
@@ -14,7 +14,7 @@ import process2 from "process";
14
14
  var kAnsiRegex = ansiRegex();
15
15
  function ansiRegex({ onlyFirst = false } = {}) {
16
16
  const pattern = [
17
- // eslint-disable-next-line max-len
17
+ // eslint-disable-next-line @stylistic/max-len
18
18
  "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
19
19
  "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"
20
20
  ].join("|");
@@ -106,6 +106,12 @@ var AbstractPrompt = class _AbstractPrompt extends EventEmitter {
106
106
  if (typeof message !== "string") {
107
107
  throw new TypeError(`message must be string, ${typeof message} given.`);
108
108
  }
109
+ if (!output.isTTY) {
110
+ Object.assign(output, {
111
+ moveCursor: () => void 0,
112
+ clearScreenDown: () => void 0
113
+ });
114
+ }
109
115
  this.stdin = input;
110
116
  this.stdout = output;
111
117
  this.message = message;
@@ -136,6 +142,9 @@ var AbstractPrompt = class _AbstractPrompt extends EventEmitter {
136
142
  }
137
143
  this.emit("error", new AbortError("Prompt aborted"));
138
144
  };
145
+ if (this.signal.aborted) {
146
+ this.#signalHandler();
147
+ }
139
148
  this.signal.addEventListener("abort", this.#signalHandler, { once: true });
140
149
  }
141
150
  }
@@ -164,7 +173,7 @@ var AbstractPrompt = class _AbstractPrompt extends EventEmitter {
164
173
  };
165
174
 
166
175
  // src/constants.ts
167
- import kleur from "kleur";
176
+ import { styleText } from "util";
168
177
  var kMainSymbols = {
169
178
  tick: "\u2714",
170
179
  cross: "\u2716",
@@ -184,20 +193,45 @@ var kFallbackSymbols = {
184
193
  inactive: "(-)"
185
194
  };
186
195
  var kSymbols = isUnicodeSupported() || process.env.CI ? kMainSymbols : kFallbackSymbols;
187
- var kPointer = kleur.gray(kSymbols.pointer);
196
+ var kPointer = styleText("gray", kSymbols.pointer);
188
197
  var SYMBOLS = {
189
- QuestionMark: kleur.blue().bold("?"),
190
- Tick: kleur.green().bold(kSymbols.tick),
191
- Cross: kleur.red().bold(kSymbols.cross),
198
+ QuestionMark: styleText(["blue", "bold"], "?"),
199
+ Tick: styleText(["green", "bold"], kSymbols.tick),
200
+ Cross: styleText(["red", "bold"], kSymbols.cross),
192
201
  Pointer: kPointer,
193
202
  Previous: kSymbols.previous,
194
203
  Next: kSymbols.next,
195
204
  ShowCursor: "\x1B[?25h",
196
205
  HideCursor: "\x1B[?25l",
197
- Active: kleur.cyan(kSymbols.active),
198
- Inactive: kleur.gray(kSymbols.inactive)
206
+ Active: styleText("cyan", kSymbols.active),
207
+ Inactive: styleText("gray", kSymbols.inactive)
199
208
  };
200
209
 
210
+ // src/validators.ts
211
+ function required() {
212
+ return {
213
+ validate: (input) => {
214
+ const isValid2 = Array.isArray(input) ? input.length > 0 : Boolean(input);
215
+ return isValid2 ? null : { isValid: isValid2, error: "required" };
216
+ }
217
+ };
218
+ }
219
+ function isValid(result) {
220
+ if (typeof result === "object") {
221
+ return result?.isValid !== false;
222
+ }
223
+ if (typeof result === "string") {
224
+ return result.length > 0;
225
+ }
226
+ return true;
227
+ }
228
+ function resultError(result) {
229
+ if (typeof result === "object") {
230
+ return result.error;
231
+ }
232
+ return result;
233
+ }
234
+
201
235
  // src/prompts/question.ts
202
236
  var QuestionPrompt = class extends AbstractPrompt {
203
237
  defaultValue;
@@ -237,16 +271,16 @@ var QuestionPrompt = class extends AbstractPrompt {
237
271
  });
238
272
  }
239
273
  #getQuestionQuery() {
240
- return `${kleur2.bold(`${SYMBOLS.QuestionMark} ${this.message}${this.tip}`)} ${this.questionSuffixError}`;
274
+ return `${styleText2("bold", `${SYMBOLS.QuestionMark} ${this.message}${this.tip}`)} ${this.questionSuffixError}`;
241
275
  }
242
276
  #setQuestionSuffixError(error) {
243
- const suffix = kleur2.red(`[${error}] `);
277
+ const suffix = styleText2("red", `[${error}] `);
244
278
  this.questionSuffixError = suffix;
245
279
  }
246
280
  #writeAnswer() {
247
281
  const prefix = this.answer ? SYMBOLS.Tick : SYMBOLS.Cross;
248
- const answer = kleur2.yellow(this.#secure ? "CONFIDENTIAL" : this.answer ?? "");
249
- this.write(`${prefix} ${kleur2.bold(this.message)} ${SYMBOLS.Pointer} ${answer}${EOL2}`);
282
+ const answer = styleText2("yellow", this.#secure ? "CONFIDENTIAL" : this.answer ?? "");
283
+ this.write(`${prefix} ${styleText2("bold", this.message)} ${SYMBOLS.Pointer} ${answer}${EOL2}`);
250
284
  }
251
285
  #onQuestionAnswer() {
252
286
  const questionLineCount = Math.ceil(
@@ -255,9 +289,9 @@ var QuestionPrompt = class extends AbstractPrompt {
255
289
  this.stdout.moveCursor(-this.stdout.columns, -questionLineCount);
256
290
  this.stdout.clearScreenDown();
257
291
  for (const validator of this.#validators) {
258
- if (!validator.validate(this.answer)) {
259
- const error = validator.error(this.answer);
260
- this.#setQuestionSuffixError(error);
292
+ const validationResult = validator.validate(this.answer);
293
+ if (isValid(validationResult) === false) {
294
+ this.#setQuestionSuffixError(resultError(validationResult));
261
295
  this.answerBuffer = this.#question();
262
296
  return;
263
297
  }
@@ -294,7 +328,7 @@ var QuestionPrompt = class extends AbstractPrompt {
294
328
 
295
329
  // src/prompts/confirm.ts
296
330
  import { EOL as EOL3 } from "os";
297
- import kleur3 from "kleur";
331
+ import { styleText as styleText3 } from "util";
298
332
  import wcwidth2 from "@topcli/wcwidth";
299
333
  var kToggleKeys = /* @__PURE__ */ new Set([
300
334
  "left",
@@ -325,8 +359,8 @@ var ConfirmPrompt = class extends AbstractPrompt {
325
359
  this.selectedValue = initial;
326
360
  }
327
361
  #getHint() {
328
- const Yes = kleur3.bold().underline().cyan("Yes");
329
- const No = kleur3.bold().underline().cyan("No");
362
+ const Yes = styleText3(["cyan", "bold", "underline"], "Yes");
363
+ const No = styleText3(["cyan", "bold", "underline"], "No");
330
364
  return this.selectedValue ? `${Yes}/No` : `Yes/${No}`;
331
365
  }
332
366
  #render() {
@@ -372,7 +406,7 @@ var ConfirmPrompt = class extends AbstractPrompt {
372
406
  this.stdin.off("keypress", this.#boundKeyPressEvent);
373
407
  }
374
408
  #getQuestionQuery() {
375
- const query = kleur3.bold(`${SYMBOLS.QuestionMark} ${this.message}`);
409
+ const query = styleText3("bold", `${SYMBOLS.QuestionMark} ${this.message}`);
376
410
  return `${query} ${this.#getHint()}`;
377
411
  }
378
412
  #onQuestionAnswer() {
@@ -382,7 +416,7 @@ var ConfirmPrompt = class extends AbstractPrompt {
382
416
  -Math.floor(wcwidth2(stripAnsi(this.#getQuestionQuery())) / this.stdout.columns)
383
417
  );
384
418
  this.stdout.clearScreenDown();
385
- this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${kleur3.bold(this.message)}${EOL3}`);
419
+ this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${styleText3("bold", this.message)}${EOL3}`);
386
420
  }
387
421
  confirm() {
388
422
  return new Promise(async (resolve, reject) => {
@@ -414,7 +448,7 @@ var ConfirmPrompt = class extends AbstractPrompt {
414
448
 
415
449
  // src/prompts/select.ts
416
450
  import { EOL as EOL4 } from "os";
417
- import kleur4 from "kleur";
451
+ import { styleText as styleText4 } from "util";
418
452
  import wcwidth3 from "@topcli/wcwidth";
419
453
  var kRequiredChoiceProperties = ["label", "value"];
420
454
  var SelectPrompt = class extends AbstractPrompt {
@@ -521,15 +555,15 @@ var SelectPrompt = class extends AbstractPrompt {
521
555
  this.longestChoice < 10 ? this.longestChoice : 0
522
556
  );
523
557
  const formattedDescription = choice.description ? ` - ${choice.description}` : "";
524
- const color = isChoiceSelected ? kleur4.white().bold : kleur4.gray;
525
- const str = `${prefix}${color(`${formattedLabel}${formattedDescription}`)}${EOL4}`;
558
+ const styles = isChoiceSelected ? ["white", "bold"] : ["gray"];
559
+ const str = `${prefix}${styleText4(styles, `${formattedLabel}${formattedDescription}`)}${EOL4}`;
526
560
  this.write(str);
527
561
  }
528
562
  }
529
563
  #showAnsweredQuestion(choice) {
530
564
  const symbolPrefix = choice === "" ? SYMBOLS.Cross : SYMBOLS.Tick;
531
- const prefix = `${symbolPrefix} ${kleur4.bold(this.message)} ${SYMBOLS.Pointer}`;
532
- const formattedChoice = kleur4.yellow(typeof choice === "string" ? choice : choice.label);
565
+ const prefix = `${symbolPrefix} ${styleText4("bold", this.message)} ${SYMBOLS.Pointer}`;
566
+ const formattedChoice = styleText4("yellow", typeof choice === "string" ? choice : choice.label);
533
567
  this.write(`${prefix} ${formattedChoice}${EOL4}`);
534
568
  }
535
569
  #onProcessExit() {
@@ -551,9 +585,9 @@ var SelectPrompt = class extends AbstractPrompt {
551
585
  const label = typeof choice === "string" ? choice : choice.label;
552
586
  const value = typeof choice === "string" ? choice : choice.value;
553
587
  for (const validator of this.#validators) {
554
- if (!validator.validate(value)) {
555
- const error = validator.error(value);
556
- render({ error });
588
+ const validationResult = validator.validate(value);
589
+ if (isValid(validationResult) === false) {
590
+ render({ error: resultError(validationResult) });
557
591
  return;
558
592
  }
559
593
  }
@@ -640,16 +674,16 @@ var SelectPrompt = class extends AbstractPrompt {
640
674
  #showQuestion(error = null) {
641
675
  let hint = "";
642
676
  if (error) {
643
- hint = ` ${hint.length > 0 ? " " : ""}${kleur4.red().bold(`[${error}]`)}`;
677
+ hint = ` ${hint.length > 0 ? " " : ""}${styleText4(["red", "bold"], `[${error}]`)}`;
644
678
  }
645
- this.questionMessage = `${SYMBOLS.QuestionMark} ${kleur4.bold(this.message)}${hint}`;
679
+ this.questionMessage = `${SYMBOLS.QuestionMark} ${styleText4("bold", this.message)}${hint}`;
646
680
  this.write(`${this.questionMessage}${EOL4}`);
647
681
  }
648
682
  };
649
683
 
650
684
  // src/prompts/multiselect.ts
651
685
  import { EOL as EOL5 } from "os";
652
- import kleur5 from "kleur";
686
+ import { styleText as styleText5 } from "util";
653
687
  import wcwidth4 from "@topcli/wcwidth";
654
688
  var kRequiredChoiceProperties2 = ["label", "value"];
655
689
  var MultiselectPrompt = class extends AbstractPrompt {
@@ -778,15 +812,15 @@ var MultiselectPrompt = class extends AbstractPrompt {
778
812
  this.longestChoice < 10 ? this.longestChoice : 0
779
813
  );
780
814
  const formattedDescription = choice.description ? ` - ${choice.description}` : "";
781
- const color = isChoiceActive ? kleur5.white().bold : kleur5.gray;
782
- const str = `${prefix} ${color(`${formattedLabel}${formattedDescription}`)}${EOL5}`;
815
+ const styles = isChoiceActive ? ["white", "bold"] : ["gray"];
816
+ const str = `${prefix} ${styleText5(styles, `${formattedLabel}${formattedDescription}`)}${EOL5}`;
783
817
  this.write(str);
784
818
  }
785
819
  }
786
820
  #showAnsweredQuestion(choices, isAgentAnswer = false) {
787
821
  const prefixSymbol = this.selectedIndexes.size === 0 && !isAgentAnswer ? SYMBOLS.Cross : SYMBOLS.Tick;
788
- const prefix = `${prefixSymbol} ${kleur5.bold(this.message)} ${SYMBOLS.Pointer}`;
789
- const formattedChoice = kleur5.yellow(choices);
822
+ const prefix = `${prefixSymbol} ${styleText5("bold", this.message)} ${SYMBOLS.Pointer}`;
823
+ const formattedChoice = styleText5("yellow", choices);
790
824
  this.write(`${prefix}${choices ? ` ${formattedChoice}` : ""}${EOL5}`);
791
825
  }
792
826
  #onProcessExit() {
@@ -822,9 +856,9 @@ var MultiselectPrompt = class extends AbstractPrompt {
822
856
  return typeof choice === "string" ? choice : choice.value;
823
857
  });
824
858
  for (const validator of this.#validators) {
825
- if (!validator.validate(values)) {
826
- const error = validator.error(values);
827
- render({ error });
859
+ const validationResult = validator.validate(values);
860
+ if (isValid(validationResult) === false) {
861
+ render({ error: resultError(validationResult) });
828
862
  return;
829
863
  }
830
864
  }
@@ -909,26 +943,19 @@ var MultiselectPrompt = class extends AbstractPrompt {
909
943
  });
910
944
  }
911
945
  #showQuestion(error = null) {
912
- let hint = this.#showHint ? kleur5.gray(
913
- // eslint-disable-next-line max-len
914
- `(Press ${kleur5.bold("<Ctrl+A>")} to toggle all, ${kleur5.bold("<Left/Right>")} to toggle, ${kleur5.bold("<Return>")} to submit)`
946
+ let hint = this.#showHint ? styleText5(
947
+ "gray",
948
+ // eslint-disable-next-line @stylistic/max-len
949
+ `(Press ${styleText5("bold", "<Ctrl+A>")} to toggle all, ${styleText5("bold", "<Left/Right>")} to toggle, ${styleText5("bold", "<Return>")} to submit)`
915
950
  ) : "";
916
951
  if (error) {
917
- hint += `${hint.length > 0 ? " " : ""}${kleur5.red().bold(`[${error}]`)}`;
952
+ hint += `${hint.length > 0 ? " " : ""}${styleText5(["red", "bold"], `[${error}]`)}`;
918
953
  }
919
- this.questionMessage = `${SYMBOLS.QuestionMark} ${kleur5.bold(this.message)}${hint.length > 0 ? ` ${hint}` : ""}`;
954
+ this.questionMessage = `${SYMBOLS.QuestionMark} ${styleText5("bold", this.message)}${hint.length > 0 ? ` ${hint}` : ""}`;
920
955
  this.write(`${this.questionMessage}${EOL5}`);
921
956
  }
922
957
  };
923
958
 
924
- // src/validators.ts
925
- function required() {
926
- return {
927
- validate: (input) => Array.isArray(input) ? input.length > 0 : Boolean(input),
928
- error: () => "required"
929
- };
930
- }
931
-
932
959
  // index.ts
933
960
  async function question(message, options = {}) {
934
961
  return new QuestionPrompt({ ...options, message }).question();
@@ -937,7 +964,7 @@ async function select(message, options) {
937
964
  const selectPrompt = new SelectPrompt({ ...options, message });
938
965
  return selectPrompt.select();
939
966
  }
940
- async function confirm(message, options) {
967
+ async function confirm(message, options = {}) {
941
968
  const confirmPrompt = new ConfirmPrompt({ ...options, message });
942
969
  return confirmPrompt.confirm();
943
970
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topcli/prompts",
3
- "version": "1.10.0",
3
+ "version": "1.11.0",
4
4
  "description": "Node.js user input library for command-line interfaces.",
5
5
  "scripts": {
6
6
  "build": "tsup index.ts --format cjs,esm --dts --clean",
@@ -31,22 +31,21 @@
31
31
  "license": "ISC",
32
32
  "type": "module",
33
33
  "devDependencies": {
34
- "@nodesecure/eslint-config": "^1.9.0",
35
- "@types/node": "^20.11.6",
36
- "c8": "^9.1.0",
37
- "eslint": "^8.56.0",
38
- "esmock": "^2.6.3",
39
- "glob": "^10.3.10",
40
- "tsup": "^8.0.1",
41
- "tsx": "^4.7.0",
42
- "typescript": "^5.3.3"
34
+ "@openally/config.eslint": "^1.0.0",
35
+ "@openally/config.typescript": "^1.0.3",
36
+ "@types/node": "^22.3.0",
37
+ "c8": "^10.1.2",
38
+ "esmock": "^2.6.7",
39
+ "glob": "^11.0.0",
40
+ "tsup": "^8.2.4",
41
+ "tsx": "^4.17.0",
42
+ "typescript": "^5.5.4"
43
43
  },
44
44
  "dependencies": {
45
- "@topcli/wcwidth": "^1.0.1",
46
- "kleur": "^4.1.5"
45
+ "@topcli/wcwidth": "^1.0.1"
47
46
  },
48
47
  "engines": {
49
- "node": ">=14"
48
+ "node": "^20.12.0 || >=21.7.0"
50
49
  },
51
50
  "bugs": {
52
51
  "url": "https://github.com/TopCli/prompts/issues"