Something went wrong. Try again.
Monorepo for the nibling.run platform
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586// Derived from Node.js v24.16.0 (MIT; Copyright Node.js contributors, and Joyent, Inc. and// other Node contributors). See LICENSE-node.// Port of Node v24's lib/internal/readline/emitKeypressEvents.js: makes a readable emit// 'keypress' for each key in its 'data', for as long as someone listens for keypresses.// A lone ESC waits escapeCodeTimeout (GNU readline's 500 ms keyseq-timeout) for the rest// of a sequence, on a guest timer, so it holds the loop as Node's does.import type { EventEmitter } from "../events";import { StringDecoder } from "../string_decoder";import type { Timers } from "../timers";import { kEscape } from "./callbacks";import { charLengthAt, emitKeys } from "./utils";
const KEYPRESS_DECODER = Symbol("keypress-decoder");const ESCAPE_DECODER = Symbol("escape-decoder");const ESCAPE_CODE_TIMEOUT = 500;
interface KeypressStream extends EventEmitter { [KEYPRESS_DECODER]?: StringDecoder; [ESCAPE_DECODER]?: Generator<void, never, string>;}
/** What emitKeypressEvents reads from and writes to an Interface. */export interface KeypressInterface { escapeCodeTimeout?: number; _sawKeyPress?: boolean; isCompletionEnabled?: boolean;}
export function makeEmitKeypressEvents(timers: () => Timers) { return function emitKeypressEvents(stream: KeypressStream, iface: KeypressInterface = {}): void { if (stream[KEYPRESS_DECODER]) return; stream[KEYPRESS_DECODER] = new StringDecoder("utf8"); stream[ESCAPE_DECODER] = emitKeys(stream as never); stream[ESCAPE_DECODER].next(undefined as never);
const triggerEscape = (): void => { stream[ESCAPE_DECODER]!.next(""); }; const { escapeCodeTimeout = ESCAPE_CODE_TIMEOUT } = iface; let timeoutId: unknown;
function onData(input: Uint8Array | string): void { if (stream.listenerCount("keypress") > 0) { const string = stream[KEYPRESS_DECODER]!.write(input); if (string) { timers().clearTimeout(timeoutId); // This supports characters of length 2. iface._sawKeyPress = charLengthAt(string, 0) === string.length; iface.isCompletionEnabled = false; let length = 0; for (const character of string) { length += character.length; if (length === string.length) iface.isCompletionEnabled = true; try { stream[ESCAPE_DECODER]!.next(character); // An escape at the tail may begin a sequence still on its way. if (length === string.length && character === kEscape) { timeoutId = timers().setTimeout(triggerEscape, escapeCodeTimeout); } } catch (err) { // A throwing 'keypress' listener kills the generator: restart it. stream[ESCAPE_DECODER] = emitKeys(stream as never); stream[ESCAPE_DECODER].next(undefined as never); throw err; } } } } else { // Nobody is watching. stream.removeListener("data", onData); stream.on("newListener", onNewListener); } }
function onNewListener(event: string | symbol): void { if (event === "keypress") { stream.on("data", onData); stream.removeListener("newListener", onNewListener); } }
if (stream.listenerCount("keypress") > 0) stream.on("data", onData); else stream.on("newListener", onNewListener); };}