telstore 0.1.2 → 0.1.3
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/package.json +1 -1
- package/src/prompt.js +45 -7
package/package.json
CHANGED
package/src/prompt.js
CHANGED
|
@@ -3,29 +3,47 @@ import { stdin, stdout, stderr } from 'node:process'
|
|
|
3
3
|
import { Writable } from 'node:stream'
|
|
4
4
|
|
|
5
5
|
// readline echoes what it reads through its own output, so putting a curtain in front of that
|
|
6
|
-
// output is what makes typing invisible.
|
|
6
|
+
// output is what makes typing invisible. The curtain is also where the mask is drawn from:
|
|
7
|
+
// every echo readline makes is a write arriving here, and a write arriving here is the one
|
|
8
|
+
// signal that something was typed which does not involve reading readline's internals.
|
|
7
9
|
function veiledOutput(output) {
|
|
8
|
-
let
|
|
10
|
+
let onEcho = null
|
|
9
11
|
|
|
10
12
|
return {
|
|
11
|
-
hide: () => {
|
|
12
|
-
|
|
13
|
+
hide: (draw) => {
|
|
14
|
+
onEcho = draw
|
|
13
15
|
},
|
|
14
16
|
show: () => {
|
|
15
|
-
|
|
17
|
+
onEcho = null
|
|
16
18
|
},
|
|
17
19
|
// The question goes straight to the real output, past the curtain, so it stays on screen
|
|
18
20
|
// while the answer to it does not.
|
|
19
21
|
say: (text) => output.write(text),
|
|
20
22
|
stream: new Writable({
|
|
21
23
|
write(chunk, encoding, callback) {
|
|
22
|
-
if (
|
|
24
|
+
if (onEcho) onEcho(String(chunk))
|
|
25
|
+
else output.write(chunk, encoding)
|
|
23
26
|
callback()
|
|
24
27
|
},
|
|
25
28
|
}),
|
|
26
29
|
}
|
|
27
30
|
}
|
|
28
31
|
|
|
32
|
+
// What the line reads while a secret is being typed. The mask is capped to the line the question
|
|
33
|
+
// sits on: askSecret redraws the whole line on every keystroke, and that redraw clears one line
|
|
34
|
+
// only, so a mask allowed to wrap would leave stale asterisks on the rows above it. The trailing
|
|
35
|
+
// ellipsis says the counting stopped, not the typing.
|
|
36
|
+
export function maskLine(question, length, columns) {
|
|
37
|
+
// One column is kept back for the ellipsis, and one for the mask itself however narrow the
|
|
38
|
+
// terminal is: a question that fills the line would otherwise mask to nothing, which is the
|
|
39
|
+
// silence this whole thing exists to end.
|
|
40
|
+
const budget = Math.max(1, (columns || 80) - question.length - 1)
|
|
41
|
+
|
|
42
|
+
if (length <= budget) return question + '*'.repeat(length)
|
|
43
|
+
|
|
44
|
+
return `${question}${'*'.repeat(budget)}\u2026`
|
|
45
|
+
}
|
|
46
|
+
|
|
29
47
|
const NO_TERMINAL =
|
|
30
48
|
'There is no terminal here to type a secret into. Run this where you can type it.'
|
|
31
49
|
|
|
@@ -51,7 +69,27 @@ export function createPrompts({ input = stdin, output = stdout } = {}) {
|
|
|
51
69
|
if (!input.isTTY) throw new Error(NO_TERMINAL)
|
|
52
70
|
|
|
53
71
|
veil.say(question)
|
|
54
|
-
|
|
72
|
+
// The question is already on screen and carries no asterisks yet, so the first echo has
|
|
73
|
+
// nothing to add. rl.line is what the mask counts — readline's own idea of the line,
|
|
74
|
+
// after it has applied the keystroke, whether that was a character, a paste, a backspace
|
|
75
|
+
// or a Ctrl-U.
|
|
76
|
+
let drawn = question
|
|
77
|
+
|
|
78
|
+
veil.hide((echo) => {
|
|
79
|
+
// Readline announces the finished line by writing a newline, and by then it has already
|
|
80
|
+
// emptied rl.line: redrawing on that would wipe the mask off the screen at the very
|
|
81
|
+
// moment the answer was accepted.
|
|
82
|
+
if (echo.includes('\n')) return
|
|
83
|
+
|
|
84
|
+
const line = maskLine(question, rl.line.length, output.columns)
|
|
85
|
+
|
|
86
|
+
// One keystroke can cost readline four writes — a redraw per write would be the same
|
|
87
|
+
// line four times and a cursor that flickers for nothing.
|
|
88
|
+
if (line === drawn) return
|
|
89
|
+
|
|
90
|
+
drawn = line
|
|
91
|
+
veil.say(`\x1b[2K\r${line}`)
|
|
92
|
+
})
|
|
55
93
|
|
|
56
94
|
try {
|
|
57
95
|
return await rl.question('')
|