From 24d054b01220f90b54bb46b1807900aeae7696d6 Mon Sep 17 00:00:00 2001 From: Shazron Abdullah <36107+shazron@users.noreply.github.com> Date: Mon, 13 Apr 2026 21:20:38 +0800 Subject: [PATCH 1/2] fix: resolve MaxListenersExceededWarning on prompt calls Bump stdout/stderr max listeners to prevent the Node.js memory-leak warning triggered when @inquirer/core pipes a MuteStream to process.stdout in an oclif environment that already saturates the default 10-listener budget. Also switches config:clear from a raw text input to a proper confirm prompt. Co-Authored-By: Claude Sonnet 4.6 --- src/commands/config/clear.js | 6 +++--- src/prompt.js | 24 +++++++++++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/commands/config/clear.js b/src/commands/config/clear.js index 93f308c..9d7f296 100755 --- a/src/commands/config/clear.js +++ b/src/commands/config/clear.js @@ -12,15 +12,15 @@ governing permissions and limitations under the License. const { Flags } = require('@oclif/core') const BaseCommand = require('../../base-command') -const { prompt } = require('../../prompt') +const { promptConfirm } = require('../../prompt') class ClearCommand extends BaseCommand { async run () { const { flags } = await this.parse(ClearCommand) if (!flags.force) { - const confirm = await prompt('are you sure? [yN]') - if (!confirm[0] || confirm[0].toLowerCase() !== 'y') { + const confirmed = await promptConfirm('are you sure?') + if (!confirmed) { return } } diff --git a/src/prompt.js b/src/prompt.js index 3ecbe7f..0779068 100644 --- a/src/prompt.js +++ b/src/prompt.js @@ -10,10 +10,17 @@ OF ANY KIND, either express or implied. See the License for the specific languag governing permissions and limitations under the License. */ -const { input } = require('@inquirer/prompts') +const { input, confirm } = require('@inquirer/prompts') + +// @inquirer/core pipes a MuteStream to process.stdout on every prompt call, +// adding an 'error' listener each time. oclif's plugin loading fills most of +// the default 10-slot budget before we even get here, so bump the limit to +// avoid the MaxListenersExceededWarning. +process.stdout.setMaxListeners(Math.max(process.stdout.getMaxListeners(), 20)) +process.stderr.setMaxListeners(Math.max(process.stderr.getMaxListeners(), 20)) /** - * Prompts the user for input. + * Prompts the user for text input. * * @param {string} message - the prompt message to display * @returns {Promise} the user's input @@ -22,4 +29,15 @@ async function prompt (message) { return input({ message }) } -module.exports = { prompt } +/** + * Prompts the user for a yes/no confirmation. + * + * @param {string} message - the prompt message to display + * @param {boolean} [defaultValue=false] - the default value if the user just presses Enter + * @returns {Promise} true if the user confirmed + */ +async function promptConfirm (message, defaultValue = false) { + return confirm({ message, default: defaultValue }) +} + +module.exports = { prompt, promptConfirm } From dfa541ffa4897324412afc17a8be7184ef4da20f Mon Sep 17 00:00:00 2001 From: Shazron Abdullah <36107+shazron@users.noreply.github.com> Date: Mon, 13 Apr 2026 21:30:35 +0800 Subject: [PATCH 2/2] test: fix clear tests and add promptConfirm coverage Update clear.js test to mock promptConfirm (boolean) instead of the old text prompt, add promptConfirm tests to prompt.js test suite, and fix the jsdoc/no-defaults lint warning on the defaultValue param. Co-Authored-By: Claude Sonnet 4.6 --- src/prompt.js | 2 +- test/commands/config/clear.js | 9 +++++---- test/commands/prompt.js | 35 ++++++++++++++++++++++++++++++++--- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/prompt.js b/src/prompt.js index 0779068..fc38897 100644 --- a/src/prompt.js +++ b/src/prompt.js @@ -33,7 +33,7 @@ async function prompt (message) { * Prompts the user for a yes/no confirmation. * * @param {string} message - the prompt message to display - * @param {boolean} [defaultValue=false] - the default value if the user just presses Enter + * @param {boolean} defaultValue - the default value if the user just presses Enter (default: false) * @returns {Promise} true if the user confirmed */ async function promptConfirm (message, defaultValue = false) { diff --git a/test/commands/config/clear.js b/test/commands/config/clear.js index 9d2a655..249df46 100755 --- a/test/commands/config/clear.js +++ b/test/commands/config/clear.js @@ -14,9 +14,10 @@ const TheCommand = require('../../../src/commands/config/clear.js') const { mockSet } = require('@adobe/aio-lib-core-config/src/Config') jest.mock('../../../src/prompt', () => ({ - prompt: jest.fn() + prompt: jest.fn(), + promptConfirm: jest.fn() })) -const { prompt } = require('../../../src/prompt') +const { promptConfirm } = require('../../../src/prompt') describe('clear', () => { afterEach(() => { @@ -46,14 +47,14 @@ describe('clear', () => { }) test('prompt with yes', () => { - prompt.mockResolvedValue('y') + promptConfirm.mockResolvedValue(true) return TheCommand.run([]).then(() => { expect(mockSet).toHaveBeenCalledWith(null, null, false) }) }) test('prompt with no', () => { - prompt.mockResolvedValue('n') + promptConfirm.mockResolvedValue(false) return TheCommand.run([]).then(() => { expect(mockSet).not.toHaveBeenCalled() }) diff --git a/test/commands/prompt.js b/test/commands/prompt.js index 62056de..f7ef531 100644 --- a/test/commands/prompt.js +++ b/test/commands/prompt.js @@ -10,12 +10,13 @@ OF ANY KIND, either express or implied. See the License for the specific languag governing permissions and limitations under the License. */ -const { prompt } = require('../../src/prompt') +const { prompt, promptConfirm } = require('../../src/prompt') jest.mock('@inquirer/prompts', () => ({ - input: jest.fn() + input: jest.fn(), + confirm: jest.fn() })) -const { input } = require('@inquirer/prompts') +const { input, confirm } = require('@inquirer/prompts') describe('prompt', () => { test('returns user input', async () => { @@ -27,3 +28,31 @@ describe('prompt', () => { expect(result).toEqual('user answer') }) }) + +describe('promptConfirm', () => { + test('returns true when confirmed', async () => { + confirm.mockResolvedValue(true) + + const result = await promptConfirm('are you sure?') + + expect(confirm).toHaveBeenCalledWith({ message: 'are you sure?', default: false }) + expect(result).toBe(true) + }) + + test('returns false when denied', async () => { + confirm.mockResolvedValue(false) + + const result = await promptConfirm('are you sure?') + + expect(confirm).toHaveBeenCalledWith({ message: 'are you sure?', default: false }) + expect(result).toBe(false) + }) + + test('passes custom default value', async () => { + confirm.mockResolvedValue(true) + + await promptConfirm('proceed?', true) + + expect(confirm).toHaveBeenCalledWith({ message: 'proceed?', default: true }) + }) +})