@topcli/prompts 2.3.0 → 2.4.1

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 CHANGED
@@ -55,7 +55,7 @@ Simple prompt, similar to `rl.question()` with an improved UI.
55
55
 
56
56
  Use `options.defaultValue` to set a default value.
57
57
 
58
- Use `options.secure` if you need to hide both input and answer.
58
+ Use `options.secure` if you need to hide both input and answer. You can provide either a **boolean** or an **object** which allows to configure a `placeholder` such as `*`.
59
59
 
60
60
  Use `options.signal` to set an `AbortSignal` (throws a [AbortError](#aborterror)).
61
61
 
@@ -84,9 +84,9 @@ const packageName = await question('Package name', {
84
84
  - required
85
85
 
86
86
  ```js
87
- import { prompt, required } from "@topcli/prompts";
87
+ import { question, required } from "@topcli/prompts";
88
88
 
89
- const name = await prompt("What's your name ?", {
89
+ const name = await question("What's your name ?", {
90
90
  validators: [required()]
91
91
  });
92
92
  ```
@@ -94,7 +94,7 @@ const name = await prompt("What's your name ?", {
94
94
  ### `select()`
95
95
 
96
96
  ```ts
97
- select(message: string, options: SelectOptions): Promise<string>
97
+ select<T extends string>(message: string, options: SelectOptions<T>): Promise<T>
98
98
  ```
99
99
 
100
100
  Scrollable select depending `maxVisible` (default `8`).
@@ -114,7 +114,7 @@ Use `options.skip` to skip prompt. It will return the first choice.
114
114
  ### `multiselect()`
115
115
 
116
116
  ```ts
117
- multiselect(message: string, options: MultiselectOptions): Promise<[string]>
117
+ multiselect<T extends string>(message: string, options: MultiselectOptions<T>): Promise<T[]>
118
118
  ```
119
119
 
120
120
  Scrollable multiselect depending `options.maxVisible` (default `8`).<br>
@@ -196,46 +196,46 @@ export interface AbstractPromptOptions {
196
196
  stdin?: Stdin;
197
197
  stdout?: Stdout;
198
198
  message: string;
199
- sginal?: AbortSignal;
199
+ skip?: boolean;
200
+ signal?: AbortSignal;
200
201
  }
201
202
 
202
- export interface PromptValidator<T = string | string[] | boolean> {
203
- validate: (input: T) => boolean;
204
- error: (input: T) => string;
203
+ export interface PromptValidator<T extends string | string[]> {
204
+ validate: (input: T) => boolean;
205
205
  }
206
206
 
207
207
  export interface QuestionOptions extends SharedOptions {
208
208
  defaultValue?: string;
209
- validators?: Validator[];
209
+ validators?: PromptValidator<string>[];
210
210
  secure?: boolean;
211
211
  }
212
212
 
213
- export interface Choice {
214
- value: any;
213
+ export interface Choice<T = any> {
214
+ value: T;
215
215
  label: string;
216
216
  description?: string;
217
217
  }
218
218
 
219
- export interface SelectOptions extends SharedOptions {
220
- choices: (Choice | string)[];
219
+ export interface SelectOptions<T extends string> extends AbstractPromptOptions {
220
+ choices: (Choice<T> | T)[];
221
221
  maxVisible?: number;
222
- ignoreValues?: (string | number | boolean)[];
223
- validators?: Validator[];
222
+ ignoreValues?: (T | number | boolean)[];
223
+ validators?: PromptValidator<string>[];
224
224
  autocomplete?: boolean;
225
225
  caseSensitive?: boolean;
226
226
  }
227
227
 
228
- export interface MultiselectOptions extends SharedOptions {
229
- choices: (Choice | string)[];
228
+ export interface MultiselectOptions<T extends string> extends AbstractPromptOptions {
229
+ choices: (Choice<T> | T)[];
230
230
  maxVisible?: number;
231
- preSelectedChoices?: (Choice | string)[];
232
- validators?: Validator[];
231
+ preSelectedChoices?: (Choice<T> | T)[];
232
+ validators?: PromptValidator<string[]>[];
233
233
  autocomplete?: boolean;
234
234
  caseSensitive?: boolean;
235
235
  showHint?: boolean;
236
236
  }
237
237
 
238
- export interface ConfirmOptions extends SharedOptions {
238
+ export interface ConfirmOptions extends AbstractPromptOptions {
239
239
  initial?: boolean;
240
240
  }
241
241
  ```
@@ -264,6 +264,7 @@ Open an issue if you want to provide feedback such as bug reports or enchancemen
264
264
  </tr>
265
265
  <tr>
266
266
  <td align="center" valign="top" width="14.28%"><a href="https://github.com/noxify"><img src="https://avatars.githubusercontent.com/u/521777?v=4?s=100" width="100px;" alt="Marcus Reinhardt"/><br /><sub><b>Marcus Reinhardt</b></sub></a><br /><a href="https://github.com/TopCli/prompts/commits?author=noxify" title="Code">💻</a> <a href="https://github.com/TopCli/prompts/commits?author=noxify" title="Tests">⚠️</a> <a href="https://github.com/TopCli/prompts/commits?author=noxify" title="Documentation">📖</a></td>
267
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/ItsHarper"><img src="https://avatars.githubusercontent.com/u/10224994?v=4?s=100" width="100px;" alt="Harper Andrews"/><br /><sub><b>Harper Andrews</b></sub></a><br /><a href="https://github.com/TopCli/prompts/commits?author=ItsHarper" title="Documentation">📖</a></td>
267
268
  </tr>
268
269
  </tbody>
269
270
  </table>
package/dist/index.cjs CHANGED
@@ -27,7 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  ));
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
 
30
- // index.ts
30
+ // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  PromptAgent: () => PromptAgent,
@@ -39,16 +39,30 @@ __export(index_exports, {
39
39
  });
40
40
  module.exports = __toCommonJS(index_exports);
41
41
 
42
- // src/prompts/question.ts
43
- var import_node_os2 = require("os");
44
- var import_node_util4 = require("util");
45
-
46
- // src/prompts/abstract.ts
47
- var import_node_os = require("os");
48
- var import_node_readline = __toESM(require("readline"), 1);
49
- var import_node_stream = require("stream");
50
- var import_node_events = __toESM(require("events"), 1);
51
- var import_node_util = require("util");
42
+ // src/validators.ts
43
+ function required() {
44
+ return {
45
+ validate: (input) => {
46
+ const isValid2 = Array.isArray(input) ? input.length > 0 : Boolean(input);
47
+ return isValid2 ? null : { isValid: isValid2, error: "required" };
48
+ }
49
+ };
50
+ }
51
+ function isValid(result) {
52
+ if (typeof result === "object") {
53
+ return result?.isValid !== false;
54
+ }
55
+ if (typeof result === "string") {
56
+ return result.length > 0;
57
+ }
58
+ return true;
59
+ }
60
+ function resultError(result) {
61
+ if (typeof result === "object") {
62
+ return result.error;
63
+ }
64
+ return result;
65
+ }
52
66
 
53
67
  // src/prompt-agent.ts
54
68
  var kPrivateInstancier = Symbol("instancier");
@@ -93,6 +107,13 @@ var PromptAgent = class _PromptAgent {
93
107
  }
94
108
  };
95
109
 
110
+ // src/prompts/abstract.ts
111
+ var import_node_os = require("os");
112
+ var import_node_readline = __toESM(require("readline"), 1);
113
+ var import_node_stream = require("stream");
114
+ var import_node_events = __toESM(require("events"), 1);
115
+ var import_node_util = require("util");
116
+
96
117
  // src/errors/abort.ts
97
118
  var AbortError = class extends Error {
98
119
  constructor(message) {
@@ -102,6 +123,9 @@ var AbortError = class extends Error {
102
123
  };
103
124
 
104
125
  // src/prompts/abstract.ts
126
+ function kNoopTransformer(input) {
127
+ return input;
128
+ }
105
129
  var AbstractPrompt = class _AbstractPrompt extends import_node_events.default {
106
130
  stdin;
107
131
  stdout;
@@ -110,7 +134,7 @@ var AbstractPrompt = class _AbstractPrompt extends import_node_events.default {
110
134
  skip;
111
135
  history;
112
136
  agent;
113
- mute;
137
+ transformer = kNoopTransformer;
114
138
  rl;
115
139
  #signalHandler;
116
140
  constructor(options) {
@@ -141,7 +165,6 @@ var AbstractPrompt = class _AbstractPrompt extends import_node_events.default {
141
165
  this.skip = skip;
142
166
  this.history = [];
143
167
  this.agent = PromptAgent.agent();
144
- this.mute = false;
145
168
  if (this.stdout.isTTY) {
146
169
  this.stdin.setRawMode(true);
147
170
  }
@@ -149,8 +172,13 @@ var AbstractPrompt = class _AbstractPrompt extends import_node_events.default {
149
172
  input,
150
173
  output: new import_node_stream.Writable({
151
174
  write: (chunk, encoding, callback) => {
152
- if (!this.mute && chunk) {
153
- this.stdout.write(chunk, encoding);
175
+ if (chunk) {
176
+ const transformed = this.transformer(
177
+ Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
178
+ );
179
+ if (transformed !== null) {
180
+ this.stdout.write(transformed, encoding);
181
+ }
154
182
  }
155
183
  callback();
156
184
  }
@@ -171,6 +199,9 @@ var AbstractPrompt = class _AbstractPrompt extends import_node_events.default {
171
199
  this.signal.addEventListener("abort", this.#signalHandler, { once: true });
172
200
  }
173
201
  }
202
+ reset() {
203
+ this.transformer = kNoopTransformer;
204
+ }
174
205
  write(data) {
175
206
  const formattedData = (0, import_node_util.stripVTControlCharacters)(data).replace(import_node_os.EOL, "");
176
207
  if (formattedData) {
@@ -195,6 +226,10 @@ var AbstractPrompt = class _AbstractPrompt extends import_node_events.default {
195
226
  }
196
227
  };
197
228
 
229
+ // src/prompts/question.ts
230
+ var import_node_os2 = require("os");
231
+ var import_node_util4 = require("util");
232
+
198
233
  // src/utils.ts
199
234
  var import_node_process = __toESM(require("process"), 1);
200
235
  var import_node_util2 = require("util");
@@ -253,31 +288,6 @@ var SYMBOLS = {
253
288
  Inactive: (0, import_node_util3.styleText)("gray", kSymbols.inactive)
254
289
  };
255
290
 
256
- // src/validators.ts
257
- function required() {
258
- return {
259
- validate: (input) => {
260
- const isValid2 = Array.isArray(input) ? input.length > 0 : Boolean(input);
261
- return isValid2 ? null : { isValid: isValid2, error: "required" };
262
- }
263
- };
264
- }
265
- function isValid(result) {
266
- if (typeof result === "object") {
267
- return result?.isValid !== false;
268
- }
269
- if (typeof result === "string") {
270
- return result.length > 0;
271
- }
272
- return true;
273
- }
274
- function resultError(result) {
275
- if (typeof result === "object") {
276
- return result.error;
277
- }
278
- return result;
279
- }
280
-
281
291
  // src/prompts/question.ts
282
292
  var QuestionPrompt = class extends AbstractPrompt {
283
293
  defaultValue;
@@ -287,6 +297,7 @@ var QuestionPrompt = class extends AbstractPrompt {
287
297
  answerBuffer;
288
298
  #validators;
289
299
  #secure;
300
+ #securePlaceholder = null;
290
301
  constructor(options) {
291
302
  const {
292
303
  defaultValue,
@@ -301,7 +312,12 @@ var QuestionPrompt = class extends AbstractPrompt {
301
312
  this.defaultValue = defaultValue;
302
313
  this.tip = this.defaultValue ? ` (${this.defaultValue})` : "";
303
314
  this.#validators = validators;
304
- this.#secure = Boolean(secure);
315
+ if (typeof secure === "object") {
316
+ this.#secure = true;
317
+ this.#securePlaceholder = secure.placeholder;
318
+ } else {
319
+ this.#secure = Boolean(secure);
320
+ }
305
321
  this.questionSuffixError = "";
306
322
  }
307
323
  #question() {
@@ -310,10 +326,12 @@ var QuestionPrompt = class extends AbstractPrompt {
310
326
  this.history.push(questionQuery);
311
327
  this.rl.question(questionQuery, (answer) => {
312
328
  this.history.push(questionQuery + answer);
313
- this.mute = false;
329
+ this.reset();
314
330
  resolve(answer);
315
331
  });
316
- this.mute = this.#secure;
332
+ if (this.#securePlaceholder !== null) {
333
+ this.transformer = (input) => Buffer.from(this.#securePlaceholder.repeat(input.length), "utf-8");
334
+ }
317
335
  });
318
336
  }
319
337
  #getQuestionQuery() {
@@ -325,8 +343,13 @@ var QuestionPrompt = class extends AbstractPrompt {
325
343
  }
326
344
  #writeAnswer() {
327
345
  const prefix = this.answer ? SYMBOLS.Tick : SYMBOLS.Cross;
328
- const answer = (0, import_node_util4.styleText)("yellow", this.#secure ? "CONFIDENTIAL" : this.answer ?? "");
329
- this.write(`${prefix} ${(0, import_node_util4.styleText)("bold", this.message)} ${SYMBOLS.Pointer} ${answer}${import_node_os2.EOL}`);
346
+ const answer = this.answer ?? "";
347
+ const maskedAnswer = this.#securePlaceholder ? this.#securePlaceholder.repeat(answer.length) : answer;
348
+ const stylizedAnswer = (0, import_node_util4.styleText)(
349
+ "yellow",
350
+ this.#secure && this.#securePlaceholder === null ? "CONFIDENTIAL" : maskedAnswer
351
+ );
352
+ this.write(`${prefix} ${(0, import_node_util4.styleText)("bold", this.message)} ${SYMBOLS.Pointer} ${stylizedAnswer}${import_node_os2.EOL}`);
330
353
  }
331
354
  #onQuestionAnswer() {
332
355
  const questionLineCount = Math.ceil(
@@ -1026,21 +1049,26 @@ var MultiselectPrompt = class extends AbstractPrompt {
1026
1049
  }
1027
1050
  };
1028
1051
 
1029
- // index.ts
1052
+ // src/index.ts
1030
1053
  function question(message, options = {}) {
1031
- return new QuestionPrompt({ ...options, message }).question();
1054
+ return new QuestionPrompt(
1055
+ { ...options, message }
1056
+ ).question();
1032
1057
  }
1033
1058
  function select(message, options) {
1034
- const selectPrompt = new SelectPrompt({ ...options, message });
1035
- return selectPrompt.select();
1059
+ return new SelectPrompt(
1060
+ { ...options, message }
1061
+ ).select();
1036
1062
  }
1037
1063
  function confirm(message, options = {}) {
1038
- const confirmPrompt = new ConfirmPrompt({ ...options, message });
1039
- return confirmPrompt.confirm();
1064
+ return new ConfirmPrompt(
1065
+ { ...options, message }
1066
+ ).confirm();
1040
1067
  }
1041
1068
  function multiselect(message, options) {
1042
- const multiselectPrompt = new MultiselectPrompt({ ...options, message });
1043
- return multiselectPrompt.multiselect();
1069
+ return new MultiselectPrompt(
1070
+ { ...options, message }
1071
+ ).multiselect();
1044
1072
  }
1045
1073
  // Annotate the CommonJS export names for ESM import in node:
1046
1074
  0 && (module.exports = {
package/dist/index.d.cts CHANGED
@@ -5,13 +5,14 @@ type InvalidResponseObject = {
5
5
  isValid: false;
6
6
  error: string;
7
7
  };
8
+ type ValidationResponseObject = ValidResponseObject | InvalidResponseObject;
8
9
  type ValidationResponse = InvalidResponse | ValidResponse;
9
10
  type InvalidResponse = string | InvalidResponseObject;
10
11
  type ValidResponse = null | undefined | true | ValidResponseObject;
11
- interface PromptValidator<T = string | string[] | boolean> {
12
+ interface PromptValidator<T extends string | string[]> {
12
13
  validate: (input: T) => ValidationResponse;
13
14
  }
14
- declare function required<T = string | string[] | boolean>(): PromptValidator<T>;
15
+ declare function required(): PromptValidator<any>;
15
16
 
16
17
  declare class PromptAgent<T = string> {
17
18
  #private;
@@ -39,6 +40,12 @@ declare class PromptAgent<T = string> {
39
40
  nextAnswer(value: T): void;
40
41
  }
41
42
 
43
+ interface Choice<T extends string> {
44
+ value: T;
45
+ label: string;
46
+ description?: string;
47
+ }
48
+
42
49
  type Stdin = NodeJS.ReadStream & {
43
50
  fd: 0;
44
51
  };
@@ -53,39 +60,35 @@ interface AbstractPromptOptions {
53
60
  signal?: AbortSignal;
54
61
  }
55
62
 
56
- interface Choice<T = any> {
57
- value: T;
58
- label: string;
59
- description?: string;
60
- }
61
-
62
63
  interface QuestionOptions extends AbstractPromptOptions {
63
64
  defaultValue?: string;
64
- validators?: PromptValidator[];
65
- secure?: boolean;
65
+ validators?: PromptValidator<string>[];
66
+ secure?: boolean | {
67
+ placeholder: string;
68
+ };
66
69
  }
67
70
 
68
71
  interface ConfirmOptions extends AbstractPromptOptions {
69
72
  initial?: boolean;
70
73
  }
71
74
 
72
- interface MultiselectOptions<T extends string> extends AbstractPromptOptions {
75
+ interface SelectOptions<T extends string> extends AbstractPromptOptions {
73
76
  choices: (Choice<T> | T)[];
74
77
  maxVisible?: number;
75
- preSelectedChoices?: (Choice | T)[];
76
- validators?: PromptValidator<T[]>[];
78
+ ignoreValues?: (T | number | boolean)[];
79
+ validators?: PromptValidator<string>[];
77
80
  autocomplete?: boolean;
78
81
  caseSensitive?: boolean;
79
- showHint?: boolean;
80
82
  }
81
83
 
82
- interface SelectOptions<T extends string> extends AbstractPromptOptions {
84
+ interface MultiselectOptions<T extends string> extends AbstractPromptOptions {
83
85
  choices: (Choice<T> | T)[];
84
86
  maxVisible?: number;
85
- ignoreValues?: (T | number | boolean)[];
86
- validators?: PromptValidator<T>[];
87
+ preSelectedChoices?: (Choice<T> | T)[];
88
+ validators?: PromptValidator<string[]>[];
87
89
  autocomplete?: boolean;
88
90
  caseSensitive?: boolean;
91
+ showHint?: boolean;
89
92
  }
90
93
 
91
94
  declare function question(message: string, options?: Omit<QuestionOptions, "message">): Promise<string>;
@@ -93,4 +96,4 @@ declare function select<T extends string>(message: string, options: Omit<SelectO
93
96
  declare function confirm(message: string, options?: Omit<ConfirmOptions, "message">): Promise<boolean>;
94
97
  declare function multiselect<T extends string>(message: string, options: Omit<MultiselectOptions<T>, "message">): Promise<T[]>;
95
98
 
96
- export { type AbstractPromptOptions, type Choice, type ConfirmOptions, type MultiselectOptions, PromptAgent, type PromptValidator, type QuestionOptions, type SelectOptions, confirm, multiselect, question, required, select };
99
+ export { type AbstractPromptOptions, type Choice, type ConfirmOptions, type InvalidResponse, type InvalidResponseObject, type MultiselectOptions, PromptAgent, type PromptValidator, type QuestionOptions, type SelectOptions, type ValidResponse, type ValidResponseObject, type ValidationResponse, type ValidationResponseObject, confirm, multiselect, question, required, select };
package/dist/index.d.ts CHANGED
@@ -5,13 +5,14 @@ type InvalidResponseObject = {
5
5
  isValid: false;
6
6
  error: string;
7
7
  };
8
+ type ValidationResponseObject = ValidResponseObject | InvalidResponseObject;
8
9
  type ValidationResponse = InvalidResponse | ValidResponse;
9
10
  type InvalidResponse = string | InvalidResponseObject;
10
11
  type ValidResponse = null | undefined | true | ValidResponseObject;
11
- interface PromptValidator<T = string | string[] | boolean> {
12
+ interface PromptValidator<T extends string | string[]> {
12
13
  validate: (input: T) => ValidationResponse;
13
14
  }
14
- declare function required<T = string | string[] | boolean>(): PromptValidator<T>;
15
+ declare function required(): PromptValidator<any>;
15
16
 
16
17
  declare class PromptAgent<T = string> {
17
18
  #private;
@@ -39,6 +40,12 @@ declare class PromptAgent<T = string> {
39
40
  nextAnswer(value: T): void;
40
41
  }
41
42
 
43
+ interface Choice<T extends string> {
44
+ value: T;
45
+ label: string;
46
+ description?: string;
47
+ }
48
+
42
49
  type Stdin = NodeJS.ReadStream & {
43
50
  fd: 0;
44
51
  };
@@ -53,39 +60,35 @@ interface AbstractPromptOptions {
53
60
  signal?: AbortSignal;
54
61
  }
55
62
 
56
- interface Choice<T = any> {
57
- value: T;
58
- label: string;
59
- description?: string;
60
- }
61
-
62
63
  interface QuestionOptions extends AbstractPromptOptions {
63
64
  defaultValue?: string;
64
- validators?: PromptValidator[];
65
- secure?: boolean;
65
+ validators?: PromptValidator<string>[];
66
+ secure?: boolean | {
67
+ placeholder: string;
68
+ };
66
69
  }
67
70
 
68
71
  interface ConfirmOptions extends AbstractPromptOptions {
69
72
  initial?: boolean;
70
73
  }
71
74
 
72
- interface MultiselectOptions<T extends string> extends AbstractPromptOptions {
75
+ interface SelectOptions<T extends string> extends AbstractPromptOptions {
73
76
  choices: (Choice<T> | T)[];
74
77
  maxVisible?: number;
75
- preSelectedChoices?: (Choice | T)[];
76
- validators?: PromptValidator<T[]>[];
78
+ ignoreValues?: (T | number | boolean)[];
79
+ validators?: PromptValidator<string>[];
77
80
  autocomplete?: boolean;
78
81
  caseSensitive?: boolean;
79
- showHint?: boolean;
80
82
  }
81
83
 
82
- interface SelectOptions<T extends string> extends AbstractPromptOptions {
84
+ interface MultiselectOptions<T extends string> extends AbstractPromptOptions {
83
85
  choices: (Choice<T> | T)[];
84
86
  maxVisible?: number;
85
- ignoreValues?: (T | number | boolean)[];
86
- validators?: PromptValidator<T>[];
87
+ preSelectedChoices?: (Choice<T> | T)[];
88
+ validators?: PromptValidator<string[]>[];
87
89
  autocomplete?: boolean;
88
90
  caseSensitive?: boolean;
91
+ showHint?: boolean;
89
92
  }
90
93
 
91
94
  declare function question(message: string, options?: Omit<QuestionOptions, "message">): Promise<string>;
@@ -93,4 +96,4 @@ declare function select<T extends string>(message: string, options: Omit<SelectO
93
96
  declare function confirm(message: string, options?: Omit<ConfirmOptions, "message">): Promise<boolean>;
94
97
  declare function multiselect<T extends string>(message: string, options: Omit<MultiselectOptions<T>, "message">): Promise<T[]>;
95
98
 
96
- export { type AbstractPromptOptions, type Choice, type ConfirmOptions, type MultiselectOptions, PromptAgent, type PromptValidator, type QuestionOptions, type SelectOptions, confirm, multiselect, question, required, select };
99
+ export { type AbstractPromptOptions, type Choice, type ConfirmOptions, type InvalidResponse, type InvalidResponseObject, type MultiselectOptions, PromptAgent, type PromptValidator, type QuestionOptions, type SelectOptions, type ValidResponse, type ValidResponseObject, type ValidationResponse, type ValidationResponseObject, confirm, multiselect, question, required, select };
package/dist/index.js CHANGED
@@ -1,13 +1,27 @@
1
- // src/prompts/question.ts
2
- import { EOL as EOL2 } from "node:os";
3
- import { styleText as styleText2 } from "node:util";
4
-
5
- // src/prompts/abstract.ts
6
- import { EOL } from "node:os";
7
- import readline from "node:readline";
8
- import { Writable } from "node:stream";
9
- import EventEmitter from "node:events";
10
- import { stripVTControlCharacters } from "node:util";
1
+ // src/validators.ts
2
+ function required() {
3
+ return {
4
+ validate: (input) => {
5
+ const isValid2 = Array.isArray(input) ? input.length > 0 : Boolean(input);
6
+ return isValid2 ? null : { isValid: isValid2, error: "required" };
7
+ }
8
+ };
9
+ }
10
+ function isValid(result) {
11
+ if (typeof result === "object") {
12
+ return result?.isValid !== false;
13
+ }
14
+ if (typeof result === "string") {
15
+ return result.length > 0;
16
+ }
17
+ return true;
18
+ }
19
+ function resultError(result) {
20
+ if (typeof result === "object") {
21
+ return result.error;
22
+ }
23
+ return result;
24
+ }
11
25
 
12
26
  // src/prompt-agent.ts
13
27
  var kPrivateInstancier = Symbol("instancier");
@@ -52,6 +66,13 @@ var PromptAgent = class _PromptAgent {
52
66
  }
53
67
  };
54
68
 
69
+ // src/prompts/abstract.ts
70
+ import { EOL } from "node:os";
71
+ import readline from "node:readline";
72
+ import { Writable } from "node:stream";
73
+ import EventEmitter from "node:events";
74
+ import { stripVTControlCharacters } from "node:util";
75
+
55
76
  // src/errors/abort.ts
56
77
  var AbortError = class extends Error {
57
78
  constructor(message) {
@@ -61,6 +82,9 @@ var AbortError = class extends Error {
61
82
  };
62
83
 
63
84
  // src/prompts/abstract.ts
85
+ function kNoopTransformer(input) {
86
+ return input;
87
+ }
64
88
  var AbstractPrompt = class _AbstractPrompt extends EventEmitter {
65
89
  stdin;
66
90
  stdout;
@@ -69,7 +93,7 @@ var AbstractPrompt = class _AbstractPrompt extends EventEmitter {
69
93
  skip;
70
94
  history;
71
95
  agent;
72
- mute;
96
+ transformer = kNoopTransformer;
73
97
  rl;
74
98
  #signalHandler;
75
99
  constructor(options) {
@@ -100,7 +124,6 @@ var AbstractPrompt = class _AbstractPrompt extends EventEmitter {
100
124
  this.skip = skip;
101
125
  this.history = [];
102
126
  this.agent = PromptAgent.agent();
103
- this.mute = false;
104
127
  if (this.stdout.isTTY) {
105
128
  this.stdin.setRawMode(true);
106
129
  }
@@ -108,8 +131,13 @@ var AbstractPrompt = class _AbstractPrompt extends EventEmitter {
108
131
  input,
109
132
  output: new Writable({
110
133
  write: (chunk, encoding, callback) => {
111
- if (!this.mute && chunk) {
112
- this.stdout.write(chunk, encoding);
134
+ if (chunk) {
135
+ const transformed = this.transformer(
136
+ Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
137
+ );
138
+ if (transformed !== null) {
139
+ this.stdout.write(transformed, encoding);
140
+ }
113
141
  }
114
142
  callback();
115
143
  }
@@ -130,6 +158,9 @@ var AbstractPrompt = class _AbstractPrompt extends EventEmitter {
130
158
  this.signal.addEventListener("abort", this.#signalHandler, { once: true });
131
159
  }
132
160
  }
161
+ reset() {
162
+ this.transformer = kNoopTransformer;
163
+ }
133
164
  write(data) {
134
165
  const formattedData = stripVTControlCharacters(data).replace(EOL, "");
135
166
  if (formattedData) {
@@ -154,6 +185,10 @@ var AbstractPrompt = class _AbstractPrompt extends EventEmitter {
154
185
  }
155
186
  };
156
187
 
188
+ // src/prompts/question.ts
189
+ import { EOL as EOL2 } from "node:os";
190
+ import { styleText as styleText2 } from "node:util";
191
+
157
192
  // src/utils.ts
158
193
  import process2 from "node:process";
159
194
  import { stripVTControlCharacters as stripVTControlCharacters2 } from "node:util";
@@ -212,31 +247,6 @@ var SYMBOLS = {
212
247
  Inactive: styleText("gray", kSymbols.inactive)
213
248
  };
214
249
 
215
- // src/validators.ts
216
- function required() {
217
- return {
218
- validate: (input) => {
219
- const isValid2 = Array.isArray(input) ? input.length > 0 : Boolean(input);
220
- return isValid2 ? null : { isValid: isValid2, error: "required" };
221
- }
222
- };
223
- }
224
- function isValid(result) {
225
- if (typeof result === "object") {
226
- return result?.isValid !== false;
227
- }
228
- if (typeof result === "string") {
229
- return result.length > 0;
230
- }
231
- return true;
232
- }
233
- function resultError(result) {
234
- if (typeof result === "object") {
235
- return result.error;
236
- }
237
- return result;
238
- }
239
-
240
250
  // src/prompts/question.ts
241
251
  var QuestionPrompt = class extends AbstractPrompt {
242
252
  defaultValue;
@@ -246,6 +256,7 @@ var QuestionPrompt = class extends AbstractPrompt {
246
256
  answerBuffer;
247
257
  #validators;
248
258
  #secure;
259
+ #securePlaceholder = null;
249
260
  constructor(options) {
250
261
  const {
251
262
  defaultValue,
@@ -260,7 +271,12 @@ var QuestionPrompt = class extends AbstractPrompt {
260
271
  this.defaultValue = defaultValue;
261
272
  this.tip = this.defaultValue ? ` (${this.defaultValue})` : "";
262
273
  this.#validators = validators;
263
- this.#secure = Boolean(secure);
274
+ if (typeof secure === "object") {
275
+ this.#secure = true;
276
+ this.#securePlaceholder = secure.placeholder;
277
+ } else {
278
+ this.#secure = Boolean(secure);
279
+ }
264
280
  this.questionSuffixError = "";
265
281
  }
266
282
  #question() {
@@ -269,10 +285,12 @@ var QuestionPrompt = class extends AbstractPrompt {
269
285
  this.history.push(questionQuery);
270
286
  this.rl.question(questionQuery, (answer) => {
271
287
  this.history.push(questionQuery + answer);
272
- this.mute = false;
288
+ this.reset();
273
289
  resolve(answer);
274
290
  });
275
- this.mute = this.#secure;
291
+ if (this.#securePlaceholder !== null) {
292
+ this.transformer = (input) => Buffer.from(this.#securePlaceholder.repeat(input.length), "utf-8");
293
+ }
276
294
  });
277
295
  }
278
296
  #getQuestionQuery() {
@@ -284,8 +302,13 @@ var QuestionPrompt = class extends AbstractPrompt {
284
302
  }
285
303
  #writeAnswer() {
286
304
  const prefix = this.answer ? SYMBOLS.Tick : SYMBOLS.Cross;
287
- const answer = styleText2("yellow", this.#secure ? "CONFIDENTIAL" : this.answer ?? "");
288
- this.write(`${prefix} ${styleText2("bold", this.message)} ${SYMBOLS.Pointer} ${answer}${EOL2}`);
305
+ const answer = this.answer ?? "";
306
+ const maskedAnswer = this.#securePlaceholder ? this.#securePlaceholder.repeat(answer.length) : answer;
307
+ const stylizedAnswer = styleText2(
308
+ "yellow",
309
+ this.#secure && this.#securePlaceholder === null ? "CONFIDENTIAL" : maskedAnswer
310
+ );
311
+ this.write(`${prefix} ${styleText2("bold", this.message)} ${SYMBOLS.Pointer} ${stylizedAnswer}${EOL2}`);
289
312
  }
290
313
  #onQuestionAnswer() {
291
314
  const questionLineCount = Math.ceil(
@@ -985,21 +1008,26 @@ var MultiselectPrompt = class extends AbstractPrompt {
985
1008
  }
986
1009
  };
987
1010
 
988
- // index.ts
1011
+ // src/index.ts
989
1012
  function question(message, options = {}) {
990
- return new QuestionPrompt({ ...options, message }).question();
1013
+ return new QuestionPrompt(
1014
+ { ...options, message }
1015
+ ).question();
991
1016
  }
992
1017
  function select(message, options) {
993
- const selectPrompt = new SelectPrompt({ ...options, message });
994
- return selectPrompt.select();
1018
+ return new SelectPrompt(
1019
+ { ...options, message }
1020
+ ).select();
995
1021
  }
996
1022
  function confirm(message, options = {}) {
997
- const confirmPrompt = new ConfirmPrompt({ ...options, message });
998
- return confirmPrompt.confirm();
1023
+ return new ConfirmPrompt(
1024
+ { ...options, message }
1025
+ ).confirm();
999
1026
  }
1000
1027
  function multiselect(message, options) {
1001
- const multiselectPrompt = new MultiselectPrompt({ ...options, message });
1002
- return multiselectPrompt.multiselect();
1028
+ return new MultiselectPrompt(
1029
+ { ...options, message }
1030
+ ).multiselect();
1003
1031
  }
1004
1032
  export {
1005
1033
  PromptAgent,
package/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "@topcli/prompts",
3
- "version": "2.3.0",
3
+ "version": "2.4.1",
4
4
  "description": "Node.js user input library for command-line interfaces.",
5
+ "type": "module",
5
6
  "scripts": {
6
- "build": "tsup index.ts --format cjs,esm --dts --clean",
7
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
7
8
  "prepublishOnly": "npm run build",
8
- "test": "glob -c \"tsx --test\" \"./test/**/*.test.ts\"",
9
- "coverage": "c8 -r html npm run test",
9
+ "test-only": "glob -c \"tsx --test\" \"./test/**/*.test.ts\"",
10
+ "test-types": "npm run build && tsd && attw --pack .",
11
+ "test": "c8 -r html npm run test-only && npm run test-types",
10
12
  "lint": "eslint src test",
11
13
  "lint:fix": "eslint . --fix"
12
14
  },
@@ -14,9 +16,8 @@
14
16
  "types": "./dist/index.d.ts",
15
17
  "exports": {
16
18
  ".": {
17
- "types": "./dist/index.d.ts",
18
- "require": "./dist/index.cjs",
19
- "import": "./dist/index.js"
19
+ "import": "./dist/index.js",
20
+ "require": "./dist/index.cjs"
20
21
  }
21
22
  },
22
23
  "keywords": [
@@ -29,13 +30,14 @@
29
30
  ],
30
31
  "author": "PierreDemailly <pierredemailly.pro@gmail.com>",
31
32
  "license": "ISC",
32
- "type": "module",
33
33
  "devDependencies": {
34
+ "@arethetypeswrong/cli": "^0.18.2",
34
35
  "@openally/config.eslint": "^2.0.0",
35
36
  "@openally/config.typescript": "^1.0.3",
36
37
  "@types/node": "^24.0.3",
37
38
  "c8": "^10.1.3",
38
39
  "glob": "^11.0.0",
40
+ "tsd": "^0.33.0",
39
41
  "tsup": "^8.3.5",
40
42
  "tsx": "^4.19.2",
41
43
  "typescript": "^5.7.2"
@@ -46,5 +48,8 @@
46
48
  "bugs": {
47
49
  "url": "https://github.com/TopCli/prompts/issues"
48
50
  },
49
- "homepage": "https://github.com/TopCli/prompts#readme"
51
+ "homepage": "https://github.com/TopCli/prompts#readme",
52
+ "tsd": {
53
+ "directory": "test/types"
54
+ }
50
55
  }