@inquirer/core
Advanced tools
@@ -9,2 +9,5 @@ import * as readline from 'node:readline'; | ||
| import { AbortPromptError, CancelPromptError, ExitPromptError } from "./errors.js"; | ||
| // Capture the real setImmediate at module load time so it works even when test | ||
| // frameworks mock timers with vi.useFakeTimers() or similar. | ||
| const nativeSetImmediate = globalThis.setImmediate; | ||
| function getCallSites() { | ||
@@ -40,2 +43,6 @@ // eslint-disable-next-line @typescript-eslint/unbound-method | ||
| output.pipe(context.output ?? process.stdout); | ||
| // Pre-mute the output so that readline doesn't echo stale keystrokes | ||
| // to the terminal before the first render. ScreenManager will unmute/mute | ||
| // the output around each render call as needed. | ||
| output.mute(); | ||
| const rl = readline.createInterface({ | ||
@@ -67,9 +74,2 @@ terminal: true, | ||
| cleanups.add(() => rl.removeListener('SIGINT', sigint)); | ||
| // Re-renders only happen when the state change; but the readline cursor could change position | ||
| // and that also requires a re-render (and a manual one because we mute the streams). | ||
| // We set the listener after the initial workLoop to avoid a double render if render triggered | ||
| // by a state change sets the cursor to the right position. | ||
| const checkCursorPos = () => screen.checkCursorPos(); | ||
| rl.input.on('keypress', checkCursorPos); | ||
| cleanups.add(() => rl.input.removeListener('keypress', checkCursorPos)); | ||
| return withHooks(rl, (cycle) => { | ||
@@ -82,21 +82,47 @@ // The close event triggers immediately when the user press ctrl+c. SignalExit on the other hand | ||
| cleanups.add(() => rl.removeListener('close', hooksCleanup)); | ||
| cycle(() => { | ||
| try { | ||
| const nextView = view(config, (value) => { | ||
| setImmediate(() => resolve(value)); | ||
| }); | ||
| // Typescript won't allow this, but not all users rely on typescript. | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| if (nextView === undefined) { | ||
| const callerFilename = callSites[1]?.getFileName(); | ||
| throw new Error(`Prompt functions must return a string.\n at ${callerFilename}`); | ||
| const startCycle = () => { | ||
| // Re-renders only happen when the state change; but the readline cursor could | ||
| // change position and that also requires a re-render (and a manual one because | ||
| // we mute the streams). We set the listener after the initial workLoop to avoid | ||
| // a double render if render triggered by a state change sets the cursor to the | ||
| // right position. | ||
| const checkCursorPos = () => screen.checkCursorPos(); | ||
| rl.input.on('keypress', checkCursorPos); | ||
| cleanups.add(() => rl.input.removeListener('keypress', checkCursorPos)); | ||
| cycle(() => { | ||
| try { | ||
| const nextView = view(config, (value) => { | ||
| setImmediate(() => resolve(value)); | ||
| }); | ||
| // Typescript won't allow this, but not all users rely on typescript. | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| if (nextView === undefined) { | ||
| const callerFilename = callSites[1]?.getFileName(); | ||
| throw new Error(`Prompt functions must return a string.\n at ${callerFilename}`); | ||
| } | ||
| const [content, bottomContent] = typeof nextView === 'string' ? [nextView] : nextView; | ||
| screen.render(content, bottomContent); | ||
| effectScheduler.run(); | ||
| } | ||
| const [content, bottomContent] = typeof nextView === 'string' ? [nextView] : nextView; | ||
| screen.render(content, bottomContent); | ||
| effectScheduler.run(); | ||
| } | ||
| catch (error) { | ||
| reject(error); | ||
| } | ||
| }); | ||
| catch (error) { | ||
| reject(error); | ||
| } | ||
| }); | ||
| }; | ||
| // Proper Readable streams (like process.stdin) may have OS-level buffered | ||
| // data that arrives in the poll phase when readline resumes the stream. | ||
| // Deferring the first render by one setImmediate tick (check phase, after | ||
| // poll) lets that stale data flow through readline harmlessly—no keypress | ||
| // handlers are registered yet and the output is muted, so the stale | ||
| // keystrokes are silently discarded. | ||
| // Old-style streams (like MuteStream) have no such buffering, so the | ||
| // render cycle starts immediately. | ||
| // | ||
| // @see https://github.com/SBoudrias/Inquirer.js/issues/1303 | ||
| if ('readableFlowing' in input) { | ||
| nativeSetImmediate(startCycle); | ||
| } | ||
| else { | ||
| startCycle(); | ||
| } | ||
| return Object.assign(promise | ||
@@ -103,0 +129,0 @@ .then((answer) => { |
+3
-3
| { | ||
| "name": "@inquirer/core", | ||
| "version": "11.1.4", | ||
| "version": "11.1.5", | ||
| "description": "Core Inquirer prompt API", | ||
@@ -79,3 +79,3 @@ "keywords": [ | ||
| "devDependencies": { | ||
| "@inquirer/testing": "^3.2.0", | ||
| "@inquirer/testing": "^3.3.0", | ||
| "@types/mute-stream": "^0.0.4", | ||
@@ -98,3 +98,3 @@ "@types/node": "^25.0.2", | ||
| "types": "./dist/index.d.ts", | ||
| "gitHead": "fd001c191cfb287b430d5e90cb159cf8f6500dc5" | ||
| "gitHead": "526eca2e64853510821ffd457561840ec0cbfb93" | ||
| } |
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
55908
2.77%1034
2.58%