dsh-ssh-tui 0.5.9 → 0.6.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.en.md +28 -5
- package/README.md +76 -6
- package/lib/attach.js +156 -0
- package/lib/attach.js.map +1 -0
- package/lib/commands.js +73 -0
- package/lib/commands.js.map +1 -0
- package/lib/diag.js +292 -0
- package/lib/diag.js.map +1 -0
- package/lib/dialogs.js +82 -0
- package/lib/dialogs.js.map +1 -0
- package/lib/display-sock.js +304 -86
- package/lib/display-sock.js.map +1 -1
- package/lib/dsh-compat.js +25 -4
- package/lib/dsh-compat.js.map +1 -1
- package/lib/i18n/en.js +55 -0
- package/lib/i18n/en.js.map +1 -1
- package/lib/i18n/index.js +2 -0
- package/lib/i18n/index.js.map +1 -1
- package/lib/i18n/zh.js +55 -0
- package/lib/i18n/zh.js.map +1 -1
- package/lib/index.js +75 -73
- package/lib/index.js.map +1 -1
- package/lib/paint.js +20 -38
- package/lib/paint.js.map +1 -1
- package/lib/picker.js +152 -37
- package/lib/picker.js.map +1 -1
- package/lib/rows.js +120 -0
- package/lib/rows.js.map +1 -0
- package/lib/session-index.js +4 -1
- package/lib/session-index.js.map +1 -1
- package/lib/session-list.js +372 -167
- package/lib/session-list.js.map +1 -1
- package/lib/session-lock.js +20 -9
- package/lib/session-lock.js.map +1 -1
- package/lib/stats.js +136 -0
- package/lib/stats.js.map +1 -0
- package/lib/terminal-input.js +470 -0
- package/lib/terminal-input.js.map +1 -0
- package/lib/tui.js +257 -311
- package/lib/tui.js.map +1 -1
- package/lib/types/attach.d.ts +106 -0
- package/lib/types/commands.d.ts +103 -0
- package/lib/types/diag.d.ts +78 -0
- package/lib/types/dialogs.d.ts +79 -0
- package/lib/types/display-sock.d.ts +59 -3
- package/lib/types/dsh-compat.d.ts +7 -0
- package/lib/types/i18n/index.d.ts +4 -0
- package/lib/types/index.d.ts +8 -4
- package/lib/types/paint.d.ts +6 -3
- package/lib/types/picker.d.ts +27 -7
- package/lib/types/rows.d.ts +69 -0
- package/lib/types/session-list.d.ts +43 -3
- package/lib/types/stats.d.ts +98 -0
- package/lib/types/terminal-input.d.ts +164 -0
- package/lib/types/tui.d.ts +33 -8
- package/package.json +11 -5
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal input hygiene for the display relay.
|
|
3
|
+
*
|
|
4
|
+
* The relay measures the SSH round-trip with a Device Status Report (`CSI 6n`)
|
|
5
|
+
* and the terminal answers with a cursor position report (`CSI row;col R`).
|
|
6
|
+
* That answer arrives on stdin, where it is indistinguishable from typing: if
|
|
7
|
+
* it lands after the probe's window closed — the terminal was still busy with
|
|
8
|
+
* a repaint, the link queued it behind a burst, or an earlier launcher asked
|
|
9
|
+
* and died before reading it — it used to be forwarded to the Host and painted
|
|
10
|
+
* into the prompt as `[17;1R`. An ESC whose tail arrived in a later read
|
|
11
|
+
* cancelled whatever dialog was open, and while two launchers fought over one
|
|
12
|
+
* session the TTY spent half its time in cooked mode, so the replies in flight
|
|
13
|
+
* were *echoed* as `^[[17;1R` lines on top of the TUI.
|
|
14
|
+
*
|
|
15
|
+
* The same in-flight replies corrupt the measurement itself: the probe that
|
|
16
|
+
* reads a previous request's answer reports ~2 ms, and the answer to its own
|
|
17
|
+
* request is still on the wire (it becomes the next garbage the prompt shows).
|
|
18
|
+
* A DSR request can also be queued behind a full-screen repaint, which is how
|
|
19
|
+
* a 50 ms SSH link reported 1900 ms.
|
|
20
|
+
*
|
|
21
|
+
* Two things keep both under control:
|
|
22
|
+
* - `TerminalInputFilter` removes replies from the byte stream wherever they
|
|
23
|
+
* appear, including split across reads, and holds a half-arrived sequence
|
|
24
|
+
* briefly so it cannot leak as `17;1R` digits either;
|
|
25
|
+
* - `TerminalInputPump` measures RTT by asking several times, discarding
|
|
26
|
+
* answers that cannot be a real round-trip, and reporting the middle one —
|
|
27
|
+
* an estimate of the link rather than of the current queue.
|
|
28
|
+
*/
|
|
29
|
+
import { StringDecoder } from 'node:string_decoder';
|
|
30
|
+
/** True when this process is talking to a terminal through SSH. */
|
|
31
|
+
export function detectSshSession(env = process.env) {
|
|
32
|
+
return Boolean(env.SSH_CONNECTION || env.SSH_CLIENT || env.SSH_TTY);
|
|
33
|
+
}
|
|
34
|
+
/** Cursor position report: `CSI row ; col R` (`?` for DECXCPR). */
|
|
35
|
+
const CURSOR_REPLY = /\x1b\[\??\d+;\d+R/gu;
|
|
36
|
+
/** Longest run that can still grow into one: `\x1b[?12345;12345`. */
|
|
37
|
+
const CURSOR_PREFIX_MAX = 16;
|
|
38
|
+
/** A partial reply: `\x1b`, `\x1b[`, `\x1b[12;3`, but not `\x1b[1;2;3`. */
|
|
39
|
+
const CURSOR_PREFIX = /^\x1b(?:\[(?:\??(?:\d*(?:;\d*)?)?)?)?$/u;
|
|
40
|
+
/** What a probe writes to ask the terminal where its cursor is. */
|
|
41
|
+
export const CURSOR_POSITION_REQUEST = '\x1b[6n';
|
|
42
|
+
/**
|
|
43
|
+
* How long a half-arrived reply is held before it is handed over as typing.
|
|
44
|
+
* A real sequence arrives within one read or the next; anything slower is
|
|
45
|
+
* either a very unlucky split or the start of a user's own escape key.
|
|
46
|
+
*/
|
|
47
|
+
export const INPUT_HOLD_MS = 30;
|
|
48
|
+
/** A single probe answer that misses this window counts as "no reply". */
|
|
49
|
+
export const RTT_SAMPLE_TIMEOUT_MS = 350;
|
|
50
|
+
/** Wider window for the rest of the measurement once one has been missed. */
|
|
51
|
+
export const RTT_SLOW_SAMPLE_TIMEOUT_MS = 800;
|
|
52
|
+
/** Answers to collect before one is reported. */
|
|
53
|
+
export const RTT_SAMPLE_COUNT = 3;
|
|
54
|
+
/** Two answers this close already describe the link; the third is not needed. */
|
|
55
|
+
export const RTT_AGREEMENT_MS = 6;
|
|
56
|
+
/** Rejected or lost answers do not consume the sampling budget. */
|
|
57
|
+
const RTT_EXTRA_ATTEMPTS = 3;
|
|
58
|
+
/** Wall-clock ceiling for one measurement, however chatty the terminal is. */
|
|
59
|
+
export const RTT_MEASURE_BUDGET_MS = 2_500;
|
|
60
|
+
/** Quiet time before re-asking when nothing was measured yet. */
|
|
61
|
+
const RTT_QUIET_MIN_MS = 150;
|
|
62
|
+
/** On top of the last good round-trip: how far behind it an answer can be. */
|
|
63
|
+
const RTT_QUIET_MARGIN_MS = 25;
|
|
64
|
+
/**
|
|
65
|
+
* An SSH answer that came back this much faster than the slowest sample cannot
|
|
66
|
+
* have made the round trip — it was already on the wire when we asked. Relative
|
|
67
|
+
* on purpose: `ssh localhost` honestly answers in about 2 ms.
|
|
68
|
+
*/
|
|
69
|
+
export const RTT_OUTLIER_RATIO = 0.25;
|
|
70
|
+
/** Remove every complete cursor reply from `text`. */
|
|
71
|
+
export function stripCursorReplies(text) {
|
|
72
|
+
let replies = 0;
|
|
73
|
+
const cleaned = text.replace(CURSOR_REPLY, () => {
|
|
74
|
+
replies += 1;
|
|
75
|
+
return '';
|
|
76
|
+
});
|
|
77
|
+
return { text: cleaned, replies };
|
|
78
|
+
}
|
|
79
|
+
/** The trailing run of `text` that a reply could still grow out of. */
|
|
80
|
+
function cursorReplyTail(text) {
|
|
81
|
+
const start = text.lastIndexOf('\x1b');
|
|
82
|
+
if (start === -1)
|
|
83
|
+
return '';
|
|
84
|
+
const tail = text.slice(start);
|
|
85
|
+
if (tail.length > CURSOR_PREFIX_MAX || !CURSOR_PREFIX.test(tail))
|
|
86
|
+
return '';
|
|
87
|
+
return tail;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Drop cursor replies from a byte stream that is otherwise user input.
|
|
91
|
+
*
|
|
92
|
+
* Feed every read to `push()`; the bytes it returns are what the user actually
|
|
93
|
+
* typed. A read that ends in the middle of a possible reply is held back (see
|
|
94
|
+
* `flush()`) so `ESC [ 1` + `7;1R` cannot become visible digits either.
|
|
95
|
+
*/
|
|
96
|
+
export class TerminalInputFilter {
|
|
97
|
+
held = '';
|
|
98
|
+
push(text) {
|
|
99
|
+
const { text: cleaned, replies } = stripCursorReplies(this.held + text);
|
|
100
|
+
this.held = '';
|
|
101
|
+
const tail = cursorReplyTail(cleaned);
|
|
102
|
+
if (tail === '')
|
|
103
|
+
return { forward: cleaned, replies };
|
|
104
|
+
this.held = tail;
|
|
105
|
+
return { forward: cleaned.slice(0, cleaned.length - tail.length), replies };
|
|
106
|
+
}
|
|
107
|
+
/** Release a held partial (its window passed, so it was not a reply). */
|
|
108
|
+
flush() {
|
|
109
|
+
const held = this.held;
|
|
110
|
+
this.held = '';
|
|
111
|
+
return held;
|
|
112
|
+
}
|
|
113
|
+
/** Forget a held partial — used when the caller knows it must be stale. */
|
|
114
|
+
reset() {
|
|
115
|
+
this.held = '';
|
|
116
|
+
}
|
|
117
|
+
get pending() {
|
|
118
|
+
return this.held !== '';
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The filter with the hold timer attached, for a caller that turns bytes into
|
|
123
|
+
* keystrokes (the TUI and the session picker) instead of forwarding them.
|
|
124
|
+
*/
|
|
125
|
+
export class TerminalInputGuard {
|
|
126
|
+
emit;
|
|
127
|
+
holdMs;
|
|
128
|
+
filter = new TerminalInputFilter();
|
|
129
|
+
timer;
|
|
130
|
+
constructor(emit, holdMs = INPUT_HOLD_MS) {
|
|
131
|
+
this.emit = emit;
|
|
132
|
+
this.holdMs = holdMs;
|
|
133
|
+
}
|
|
134
|
+
/** Feed one decoded read; anything that is not a reply reaches `emit`. */
|
|
135
|
+
push(text) {
|
|
136
|
+
const { forward } = this.filter.push(text);
|
|
137
|
+
if (this.filter.pending) {
|
|
138
|
+
if (this.timer !== undefined)
|
|
139
|
+
clearTimeout(this.timer);
|
|
140
|
+
this.timer = setTimeout(() => {
|
|
141
|
+
this.timer = undefined;
|
|
142
|
+
this.release();
|
|
143
|
+
}, this.holdMs);
|
|
144
|
+
this.timer.unref?.();
|
|
145
|
+
}
|
|
146
|
+
if (forward !== '')
|
|
147
|
+
this.emit(forward);
|
|
148
|
+
}
|
|
149
|
+
/** Hand a half-arrived sequence over (its window passed; it is typing). */
|
|
150
|
+
release() {
|
|
151
|
+
if (this.timer !== undefined) {
|
|
152
|
+
clearTimeout(this.timer);
|
|
153
|
+
this.timer = undefined;
|
|
154
|
+
}
|
|
155
|
+
const held = this.filter.flush();
|
|
156
|
+
if (held !== '')
|
|
157
|
+
this.emit(held);
|
|
158
|
+
}
|
|
159
|
+
/** Drop the timer and any held bytes (display detached, picker settled). */
|
|
160
|
+
stop() {
|
|
161
|
+
if (this.timer !== undefined) {
|
|
162
|
+
clearTimeout(this.timer);
|
|
163
|
+
this.timer = undefined;
|
|
164
|
+
}
|
|
165
|
+
this.filter.reset();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* stdin → replies (for the probe) + real input (for the caller).
|
|
170
|
+
*
|
|
171
|
+
* Long-lived on purpose: the relay keeps it running for the whole attachment,
|
|
172
|
+
* so a reply that arrives seconds late is dropped instead of reaching the Host.
|
|
173
|
+
*/
|
|
174
|
+
export class TerminalInputPump {
|
|
175
|
+
options;
|
|
176
|
+
filter = new TerminalInputFilter();
|
|
177
|
+
decoder = new StringDecoder('utf8');
|
|
178
|
+
waiters = [];
|
|
179
|
+
/** When the last answer (accepted or not) arrived; drives the quiet wait. */
|
|
180
|
+
lastAnswerAt = 0;
|
|
181
|
+
/** When the last request went out; an answer may follow it for a window. */
|
|
182
|
+
lastRequestAt = 0;
|
|
183
|
+
/** Current answer window; widened once when the first one is missed. */
|
|
184
|
+
windowMs = RTT_SAMPLE_TIMEOUT_MS;
|
|
185
|
+
/** Answers seen, used to tell "slow terminal" from "terminal never answers". */
|
|
186
|
+
answersSeen = 0;
|
|
187
|
+
/** Slowest sample so far; the quiet wait has to clear the worst of them. */
|
|
188
|
+
slowestSampleMs;
|
|
189
|
+
holdTimer;
|
|
190
|
+
listening = false;
|
|
191
|
+
ssh;
|
|
192
|
+
constructor(options) {
|
|
193
|
+
this.options = options;
|
|
194
|
+
this.ssh = options.ssh ?? detectSshSession();
|
|
195
|
+
}
|
|
196
|
+
start() {
|
|
197
|
+
if (this.listening)
|
|
198
|
+
return;
|
|
199
|
+
this.listening = true;
|
|
200
|
+
this.options.stdin.on('data', this.onData);
|
|
201
|
+
}
|
|
202
|
+
stop() {
|
|
203
|
+
const wasListening = this.listening;
|
|
204
|
+
this.listening = false;
|
|
205
|
+
if (wasListening)
|
|
206
|
+
this.options.stdin.removeListener('data', this.onData);
|
|
207
|
+
if (this.holdTimer !== undefined) {
|
|
208
|
+
clearTimeout(this.holdTimer);
|
|
209
|
+
this.holdTimer = undefined;
|
|
210
|
+
}
|
|
211
|
+
for (const waiter of this.waiters.splice(0)) {
|
|
212
|
+
clearTimeout(waiter.timer);
|
|
213
|
+
waiter.settle(undefined);
|
|
214
|
+
}
|
|
215
|
+
const held = this.filter.flush();
|
|
216
|
+
if (held !== '')
|
|
217
|
+
this.options.onInput(held);
|
|
218
|
+
const tail = this.decoder.end();
|
|
219
|
+
if (tail !== '')
|
|
220
|
+
this.options.onInput(tail);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Ask the terminal for its cursor a few times and report the round-trip that
|
|
224
|
+
* best describes the link. `undefined` means the terminal never answered in
|
|
225
|
+
* time (a dumb pipe), and the caller must not treat that as "0 ms".
|
|
226
|
+
*/
|
|
227
|
+
async measure() {
|
|
228
|
+
const timeoutMs = this.options.sampleTimeoutMs ?? RTT_SAMPLE_TIMEOUT_MS;
|
|
229
|
+
this.windowMs = timeoutMs;
|
|
230
|
+
const wanted = Math.max(1, this.options.samples ?? RTT_SAMPLE_COUNT);
|
|
231
|
+
const samples = [];
|
|
232
|
+
const note = this.options.debug;
|
|
233
|
+
let misses = 0;
|
|
234
|
+
let attempts = 0;
|
|
235
|
+
// A rejected or lost answer must not eat the budget: on a reconnect the
|
|
236
|
+
// terminal can still be finishing a dead launcher's probe.
|
|
237
|
+
const maxAttempts = wanted + RTT_EXTRA_ATTEMPTS;
|
|
238
|
+
const budgetMs = this.options.budgetMs ?? RTT_MEASURE_BUDGET_MS;
|
|
239
|
+
const startedAt = Date.now();
|
|
240
|
+
const answersAtStart = this.answersSeen;
|
|
241
|
+
this.lastAnswerAt = startedAt;
|
|
242
|
+
while (samples.length < wanted && attempts < maxAttempts
|
|
243
|
+
&& Date.now() - startedAt < budgetMs) {
|
|
244
|
+
attempts += 1;
|
|
245
|
+
// Never ask while an earlier request may still be in flight: its answer
|
|
246
|
+
// would be read as this request's, and because it left earlier it is
|
|
247
|
+
// *faster* — the "2 ms on a 50 ms link" reading. Wait for the whole
|
|
248
|
+
// answer window to go by before asking again.
|
|
249
|
+
if (attempts > 1)
|
|
250
|
+
await this.quiet(startedAt + budgetMs);
|
|
251
|
+
this.drain();
|
|
252
|
+
const rtt = await this.ask(this.windowMs);
|
|
253
|
+
if (rtt === undefined) {
|
|
254
|
+
misses += 1;
|
|
255
|
+
note?.(`rtt probe ${attempts}: no answer`);
|
|
256
|
+
// A miss may just mean the link is slower than the first window, so
|
|
257
|
+
// widen it — but only for a terminal that has answered at all: a dumb
|
|
258
|
+
// pipe must not cost the widened window twice.
|
|
259
|
+
if (this.answersSeen > answersAtStart) {
|
|
260
|
+
this.windowMs = Math.min(RTT_SLOW_SAMPLE_TIMEOUT_MS, Math.max(this.windowMs, timeoutMs * 3));
|
|
261
|
+
}
|
|
262
|
+
// Two silent windows in a row and nothing collected: this terminal
|
|
263
|
+
// does not answer DSR at all, and a third only delays the attach.
|
|
264
|
+
if (misses >= 2 && samples.length === 0)
|
|
265
|
+
break;
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
samples.push(rtt);
|
|
269
|
+
note?.(`rtt probe ${attempts}: ${rtt}ms`);
|
|
270
|
+
if (samples.length >= 2
|
|
271
|
+
&& Math.max(...samples) - Math.min(...samples) <= RTT_AGREEMENT_MS)
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
// Whatever else was queued belongs to a request we have already given up
|
|
275
|
+
// on; it must not reach the Host, and it must not extend this measurement.
|
|
276
|
+
this.drain();
|
|
277
|
+
const kept = this.dropFastOutliers(samples);
|
|
278
|
+
// Median of three, not the minimum: one answer that was already in flight
|
|
279
|
+
// when we asked is *fast*, and taking the smallest sample let it decide the
|
|
280
|
+
// footer's chip. Two samples only get here when they already agree (the
|
|
281
|
+
// early stop above), and the larger one is the conservative pick.
|
|
282
|
+
const measured = kept.length === 0
|
|
283
|
+
? undefined
|
|
284
|
+
: kept.length >= 3
|
|
285
|
+
? [...kept].sort((a, b) => a - b)[Math.floor(kept.length / 2)]
|
|
286
|
+
: Math.max(...kept);
|
|
287
|
+
note?.(measured === undefined
|
|
288
|
+
? 'rtt probe: unknown'
|
|
289
|
+
: `rtt probe: ${measured}ms (of ${samples.map(value => `${value}ms`).join(', ')})`);
|
|
290
|
+
return measured;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Drop an answer that came back far too fast to have made the round trip —
|
|
294
|
+
* an answer to somebody else's request, still on the wire when we asked.
|
|
295
|
+
* Relative, not absolute (`ssh localhost` honestly answers in 2 ms), and
|
|
296
|
+
* measured against the *median*: comparing with the slowest sample would
|
|
297
|
+
* throw away three good answers because one request sat behind a repaint.
|
|
298
|
+
*/
|
|
299
|
+
dropFastOutliers(samples) {
|
|
300
|
+
if (!this.ssh || samples.length < 2)
|
|
301
|
+
return samples;
|
|
302
|
+
const sorted = [...samples].sort((a, b) => a - b);
|
|
303
|
+
const middle = Math.floor(sorted.length / 2);
|
|
304
|
+
const median = sorted.length % 2 === 1
|
|
305
|
+
? (sorted[middle] ?? 0)
|
|
306
|
+
: ((sorted[middle - 1] ?? 0) + (sorted[middle] ?? 0)) / 2;
|
|
307
|
+
return samples.filter(sample => sample >= median * RTT_OUTLIER_RATIO);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* How long the line must be quiet before the next request may go out.
|
|
311
|
+
*
|
|
312
|
+
* The only safe answer is the full sample window: a request we sent can be
|
|
313
|
+
* answered at any point inside it, so anything shorter can attribute that
|
|
314
|
+
* answer to the request that follows. Deriving it from the samples instead
|
|
315
|
+
* looks cheaper and is exactly what goes wrong — a leftover answer reports
|
|
316
|
+
* 1 ms, the window shrinks to the minimum, and the honest answer to the
|
|
317
|
+
* abandoned request lands in the *next* window (250 ms link reported as 1 ms
|
|
318
|
+
* and 0 ms, two "agreeing" samples, early stop, chip frozen on a wrong value).
|
|
319
|
+
*/
|
|
320
|
+
quietMs() {
|
|
321
|
+
if (this.slowestSampleMs === undefined)
|
|
322
|
+
return Math.max(RTT_QUIET_MIN_MS, this.windowMs);
|
|
323
|
+
return Math.max(RTT_QUIET_MIN_MS, this.windowMs, this.slowestSampleMs + RTT_QUIET_MARGIN_MS);
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Wait until the line has been quiet for a full window, measured from the
|
|
327
|
+
* later of the last answer and the last request. An answer to a request we
|
|
328
|
+
* already gave up on lands in here and is thrown away, so it cannot be
|
|
329
|
+
* attributed to the request that follows it.
|
|
330
|
+
*/
|
|
331
|
+
async quiet(deadline) {
|
|
332
|
+
const quietMs = this.quietMs();
|
|
333
|
+
for (;;) {
|
|
334
|
+
const last = Math.max(this.lastAnswerAt, this.lastRequestAt);
|
|
335
|
+
const idle = Date.now() - last;
|
|
336
|
+
if (idle >= quietMs)
|
|
337
|
+
return;
|
|
338
|
+
if (Date.now() >= deadline)
|
|
339
|
+
return;
|
|
340
|
+
await new Promise(resolve => setTimeout(resolve, Math.min(20, quietMs - idle)));
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Consume what the kernel already holds, so an answer that was on its way
|
|
345
|
+
* before this request cannot be read as its answer.
|
|
346
|
+
*
|
|
347
|
+
* `read()` emits `data` for the chunk it returns whenever a listener is
|
|
348
|
+
* attached, and the listener path would then deliver the same bytes twice
|
|
349
|
+
* (a typed `hi` arrived at the Host as `hihi`). Detach for the duration of
|
|
350
|
+
* the loop: it is synchronous, so nothing can be lost in between.
|
|
351
|
+
*/
|
|
352
|
+
drain() {
|
|
353
|
+
const read = this.options.stdin.read;
|
|
354
|
+
const listening = this.listening;
|
|
355
|
+
if (listening && typeof read === 'function')
|
|
356
|
+
this.options.stdin.removeListener('data', this.onData);
|
|
357
|
+
try {
|
|
358
|
+
if (typeof read === 'function') {
|
|
359
|
+
for (;;) {
|
|
360
|
+
let chunk;
|
|
361
|
+
try {
|
|
362
|
+
chunk = read.call(this.options.stdin);
|
|
363
|
+
}
|
|
364
|
+
catch {
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
if (chunk === null || chunk === undefined)
|
|
368
|
+
break;
|
|
369
|
+
if (chunk.length === 0)
|
|
370
|
+
break;
|
|
371
|
+
this.feed(chunk);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
finally {
|
|
376
|
+
if (listening)
|
|
377
|
+
this.options.stdin.on('data', this.onData);
|
|
378
|
+
}
|
|
379
|
+
if (this.holdTimer !== undefined) {
|
|
380
|
+
clearTimeout(this.holdTimer);
|
|
381
|
+
this.holdTimer = undefined;
|
|
382
|
+
}
|
|
383
|
+
// A half-arrived sequence sitting here now predates the request we are
|
|
384
|
+
// about to send, so it cannot be its answer.
|
|
385
|
+
this.filter.reset();
|
|
386
|
+
}
|
|
387
|
+
ask(timeoutMs) {
|
|
388
|
+
return new Promise(resolve => {
|
|
389
|
+
let settled = false;
|
|
390
|
+
const settle = (value) => {
|
|
391
|
+
if (settled)
|
|
392
|
+
return;
|
|
393
|
+
settled = true;
|
|
394
|
+
resolve(value);
|
|
395
|
+
};
|
|
396
|
+
const waiter = {
|
|
397
|
+
started: Date.now(),
|
|
398
|
+
settle,
|
|
399
|
+
timer: setTimeout(() => {
|
|
400
|
+
const index = this.waiters.indexOf(waiter);
|
|
401
|
+
if (index !== -1)
|
|
402
|
+
this.waiters.splice(index, 1);
|
|
403
|
+
settle(undefined);
|
|
404
|
+
}, timeoutMs),
|
|
405
|
+
};
|
|
406
|
+
waiter.timer.unref?.();
|
|
407
|
+
this.waiters.push(waiter);
|
|
408
|
+
this.lastRequestAt = Date.now();
|
|
409
|
+
try {
|
|
410
|
+
this.options.stdout.write(CURSOR_POSITION_REQUEST);
|
|
411
|
+
}
|
|
412
|
+
catch {
|
|
413
|
+
const index = this.waiters.indexOf(waiter);
|
|
414
|
+
if (index !== -1)
|
|
415
|
+
this.waiters.splice(index, 1);
|
|
416
|
+
clearTimeout(waiter.timer);
|
|
417
|
+
settle(undefined);
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
onData = (chunk) => {
|
|
422
|
+
this.feed(chunk);
|
|
423
|
+
};
|
|
424
|
+
feed(chunk) {
|
|
425
|
+
const text = typeof chunk === 'string' ? chunk : this.decoder.write(chunk);
|
|
426
|
+
if (text === '')
|
|
427
|
+
return;
|
|
428
|
+
const { forward, replies } = this.filter.push(text);
|
|
429
|
+
for (let index = 0; index < replies; index += 1)
|
|
430
|
+
this.answer();
|
|
431
|
+
if (forward !== '')
|
|
432
|
+
this.options.onInput(forward);
|
|
433
|
+
this.scheduleHold();
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* One reply: hand it to the oldest outstanding request. An answer that
|
|
437
|
+
* belongs to a request we already gave up on is simply dropped here — and
|
|
438
|
+
* its arrival time still restarts the quiet window, which is what keeps it
|
|
439
|
+
* from being attributed to the next request.
|
|
440
|
+
*/
|
|
441
|
+
answer() {
|
|
442
|
+
this.lastAnswerAt = Date.now();
|
|
443
|
+
this.answersSeen += 1;
|
|
444
|
+
const waiter = this.waiters.shift();
|
|
445
|
+
if (waiter === undefined)
|
|
446
|
+
return;
|
|
447
|
+
clearTimeout(waiter.timer);
|
|
448
|
+
const elapsed = Math.max(0, Date.now() - waiter.started);
|
|
449
|
+
if (this.slowestSampleMs === undefined || elapsed > this.slowestSampleMs) {
|
|
450
|
+
this.slowestSampleMs = elapsed;
|
|
451
|
+
}
|
|
452
|
+
waiter.settle(elapsed);
|
|
453
|
+
}
|
|
454
|
+
scheduleHold() {
|
|
455
|
+
if (this.holdTimer !== undefined) {
|
|
456
|
+
clearTimeout(this.holdTimer);
|
|
457
|
+
this.holdTimer = undefined;
|
|
458
|
+
}
|
|
459
|
+
if (!this.filter.pending)
|
|
460
|
+
return;
|
|
461
|
+
this.holdTimer = setTimeout(() => {
|
|
462
|
+
this.holdTimer = undefined;
|
|
463
|
+
const held = this.filter.flush();
|
|
464
|
+
if (held !== '')
|
|
465
|
+
this.options.onInput(held);
|
|
466
|
+
}, this.options.holdMs ?? INPUT_HOLD_MS);
|
|
467
|
+
this.holdTimer.unref?.();
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
//# sourceMappingURL=terminal-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-input.js","sourceRoot":"","sources":["../src/terminal-input.ts"],"names":[],"mappings":"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"}
|