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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/prompt.js +45 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "telstore",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Split large files into chunks and store them on Telegram",
5
5
  "keywords": [
6
6
  "telegram",
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 hidden = false
10
+ let onEcho = null
9
11
 
10
12
  return {
11
- hide: () => {
12
- hidden = true
13
+ hide: (draw) => {
14
+ onEcho = draw
13
15
  },
14
16
  show: () => {
15
- hidden = false
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 (!hidden) output.write(chunk, encoding)
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
- veil.hide()
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('')