From d02c83f59e5d45587a98e2d18611e13533c5d858 Mon Sep 17 00:00:00 2001 From: mshanemc Date: Thu, 4 Jan 2024 12:10:37 -0600 Subject: [PATCH] fix: use new sf-plugins-core prompts --- LICENSE.txt | 2 +- package.json | 10 +- src/commands/org/list/auth.ts | 10 +- src/commands/org/login/access-token.ts | 30 +-- src/commands/org/login/web.ts | 18 +- src/commands/org/logout.ts | 236 ++++++++----------- src/common.ts | 8 - test/commands/org/login/access-token.test.ts | 144 +++++------ test/commands/org/login/login.device.test.ts | 5 +- test/commands/org/login/login.web.test.ts | 3 +- test/commands/org/logout.test.ts | 2 + yarn.lock | 151 +++++------- 12 files changed, 272 insertions(+), 347 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index f2cee7bb..6cfd8734 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2023, Salesforce.com, Inc. +Copyright (c) 2024, Salesforce.com, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/package.json b/package.json index d1f817e7..5859fda8 100644 --- a/package.json +++ b/package.json @@ -5,12 +5,14 @@ "author": "Salesforce", "bugs": "https://github.com/forcedotcom/cli/issues", "dependencies": { + "@inquirer/checkbox": "^1.5.0", "@inquirer/confirm": "^2.0.15", "@inquirer/password": "^1.1.14", - "@oclif/core": "^3.15.0", - "@salesforce/core": "^6.4.2", + "@inquirer/select": "^1.3.1", + "@oclif/core": "^3.16.0", + "@salesforce/core": "^6.4.3", "@salesforce/kit": "^3.0.15", - "@salesforce/sf-plugins-core": "^5.0.13", + "@salesforce/sf-plugins-core": "5.0.14-dev.0", "@salesforce/ts-types": "^2.0.9", "chalk": "^5.3.0", "open": "^9.1.0" @@ -18,7 +20,7 @@ "devDependencies": { "@oclif/plugin-command-snapshot": "^5.0.3", "@salesforce/cli-plugins-testkit": "^5.1.3", - "@salesforce/dev-scripts": "^8.1.3", + "@salesforce/dev-scripts": "^8.2.0", "@salesforce/plugin-command-reference": "^3.0.58", "@salesforce/ts-sinon": "^1.4.19", "eslint-plugin-sf-plugin": "^1.17.0", diff --git a/src/commands/org/list/auth.ts b/src/commands/org/list/auth.ts index dd3208fe..ba6ae3e2 100644 --- a/src/commands/org/list/auth.ts +++ b/src/commands/org/list/auth.ts @@ -5,13 +5,11 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ - - import { loglevel, SfCommand } from '@salesforce/sf-plugins-core'; import { AuthInfo, Messages, OrgAuthorization } from '@salesforce/core'; type AuthListResult = Omit & { alias: string }; export type AuthListResults = AuthListResult[]; -Messages.importMessagesDirectoryFromMetaUrl(import.meta.url) +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('@salesforce/plugin-auth', 'list'); export default class ListAuth extends SfCommand { @@ -40,16 +38,14 @@ export default class ListAuth extends SfCommand { }); const hasErrors = auths.filter((auth) => !!auth.error).length > 0; - let columns = { + const columns = { alias: { header: 'ALIAS' }, username: { header: 'USERNAME' }, orgId: { header: 'ORG ID' }, instanceUrl: { header: 'INSTANCE URL' }, oauthMethod: { header: 'AUTH METHOD' }, + ...(hasErrors ? { error: { header: 'ERROR' } } : {}), }; - if (hasErrors) { - columns = { ...columns, ...{ error: { header: 'ERROR' } } }; - } this.styledHeader('authenticated orgs'); this.table(mappedAuths, columns); return mappedAuths; diff --git a/src/commands/org/login/access-token.ts b/src/commands/org/login/access-token.ts index d773dd38..b291cb72 100644 --- a/src/commands/org/login/access-token.ts +++ b/src/commands/org/login/access-token.ts @@ -8,8 +8,7 @@ import { Flags, loglevel, SfCommand } from '@salesforce/sf-plugins-core'; import { AuthFields, AuthInfo, Messages, matchesAccessToken, SfError, StateAggregator } from '@salesforce/core'; import { env } from '@salesforce/kit'; -import { Interfaces } from '@oclif/core'; -import common from '../../../common.js'; +import { InferredFlags } from '@oclif/core/lib/interfaces'; Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('@salesforce/plugin-auth', 'accesstoken.store'); @@ -64,13 +63,13 @@ export default class LoginAccessToken extends SfCommand { loglevel, }; - private flags!: Interfaces.InferredFlags; + private flags!: InferredFlags; public async run(): Promise { const { flags } = await this.parse(LoginAccessToken); this.flags = flags; const instanceUrl = flags['instance-url'].href; - const accessToken = await getAccessToken(); + const accessToken = await this.getAccessToken(); const authInfo = await this.getUserInfo(accessToken, instanceUrl); return this.storeAuthFromAccessToken(authInfo); } @@ -107,19 +106,22 @@ export default class LoginAccessToken extends SfCommand { if (!this.flags['no-prompt']) { const stateAggregator = await StateAggregator.getInstance(); if (await stateAggregator.orgs.exists(username)) { - return this.confirm(messages.getMessage('overwriteAccessTokenAuthUserFile', [username])); + return this.confirm({ message: messages.getMessage('overwriteAccessTokenAuthUserFile', [username]) }); } } return true; } -} - -const getAccessToken = async (): Promise => { - const accessToken = - env.getString('SF_ACCESS_TOKEN') ?? env.getString('SFDX_ACCESS_TOKEN') ?? (await common.accessTokenPrompt()); - if (!matchesAccessToken(accessToken)) { - throw new SfError(messages.getMessage('invalidAccessTokenFormat', [ACCESS_TOKEN_FORMAT])); + private async getAccessToken(): Promise { + const accessToken = + env.getString('SF_ACCESS_TOKEN') ?? + env.getString('SFDX_ACCESS_TOKEN') ?? + (this.flags['no-prompt'] === true + ? '' // will throw when validating + : await this.secretPrompt({ message: commonMessages.getMessage('accessTokenStdin') })); + if (!matchesAccessToken(accessToken)) { + throw new SfError(messages.getMessage('invalidAccessTokenFormat', [ACCESS_TOKEN_FORMAT])); + } + return accessToken; } - return accessToken; -}; +} diff --git a/src/commands/org/login/web.ts b/src/commands/org/login/web.ts index ce59e106..0a0b203a 100644 --- a/src/commands/org/login/web.ts +++ b/src/commands/org/login/web.ts @@ -9,7 +9,6 @@ import open, { apps, AppName } from 'open'; import { Flags, SfCommand, loglevel } from '@salesforce/sf-plugins-core'; import { AuthFields, AuthInfo, Logger, Messages, OAuth2Config, SfError, WebOAuthServer } from '@salesforce/core'; import { Env } from '@salesforce/kit'; -import { Interfaces } from '@oclif/core'; import common from '../../../common.js'; Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); @@ -24,12 +23,12 @@ export default class LoginWeb extends SfCommand { public static readonly aliases = ['force:auth:web:login', 'auth:web:login']; public static readonly flags = { - browser: Flags.string({ + browser: Flags.option({ char: 'b', summary: messages.getMessage('flags.browser.summary'), description: messages.getMessage('flags.browser.description'), options: ['chrome', 'edge', 'firefox'], // These are ones supported by "open" package - }), + })(), 'client-id': Flags.string({ char: 'i', summary: commonMessages.getMessage('flags.client-id.summary'), @@ -72,11 +71,8 @@ export default class LoginWeb extends SfCommand { loglevel, }; - private flags!: Interfaces.InferredFlags; - public async run(): Promise { const { flags } = await this.parse(LoginWeb); - this.flags = flags; if (isSFDXContainerMode()) { throw new SfError(messages.getMessage('deviceWarning'), 'DEVICE_WARNING'); } @@ -86,11 +82,13 @@ export default class LoginWeb extends SfCommand { const oauthConfig: OAuth2Config = { loginUrl: await common.resolveLoginUrl(flags['instance-url']?.href), clientId: flags['client-id'], - ...(flags['client-id'] ? { clientSecret: await common.clientSecretPrompt() } : {}), + ...(flags['client-id'] + ? { clientSecret: await this.secretPrompt({ message: commonMessages.getMessage('clientSecretStdin') }) } + : {}), }; try { - const authInfo = await this.executeLoginFlow(oauthConfig); + const authInfo = await this.executeLoginFlow(oauthConfig, flags.browser); await authInfo.handleAliasAndDefaultSettings({ alias: flags.alias, setDefault: flags['set-default'], @@ -113,10 +111,10 @@ export default class LoginWeb extends SfCommand { // leave it because it's stubbed in the test // eslint-disable-next-line class-methods-use-this - private async executeLoginFlow(oauthConfig: OAuth2Config): Promise { + private async executeLoginFlow(oauthConfig: OAuth2Config, browser?: string): Promise { const oauthServer = await WebOAuthServer.create({ oauthConfig }); await oauthServer.start(); - const app = this.flags.browser && this.flags.browser in apps ? (this.flags.browser as AppName) : undefined; + const app = browser && browser in apps ? (browser as AppName) : undefined; const openOptions = app ? { app: { name: apps[app] }, wait: false } : { wait: false }; await open(oauthServer.getAuthorizationUrl(), openOptions); return oauthServer.authorizeAndSave(); diff --git a/src/commands/org/logout.ts b/src/commands/org/logout.ts index 8d162358..69353d82 100644 --- a/src/commands/org/logout.ts +++ b/src/commands/org/logout.ts @@ -16,8 +16,8 @@ import { OrgAuthorization, OrgConfigProperties, } from '@salesforce/core'; -import { Flags, loglevel, Separator, SfCommand } from '@salesforce/sf-plugins-core'; -import { Interfaces } from '@oclif/core'; +import checkbox, { Separator } from '@inquirer/checkbox'; +import { Flags, loglevel, SfCommand } from '@salesforce/sf-plugins-core'; import chalk from 'chalk'; Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); @@ -60,65 +60,11 @@ export default class Logout extends SfCommand { loglevel, }; - private flags!: Interfaces.InferredFlags; - - private static buildChoices(orgAuths: OrgAuthorization[], all: boolean): Array { - const maxUsernameLength = Math.max('Username'.length, ...orgAuths.map((orgAuth) => orgAuth.username.length)); - const maxAliasLength = Math.max( - 'Aliases'.length, - ...orgAuths.map((orgAuth) => (orgAuth.aliases ? orgAuth.aliases.join(',') : '').length) - ); - const maxConfigLength = Math.max( - 'Configs'.length, - ...orgAuths.map((orgAuth) => (orgAuth.configs ? orgAuth.configs.join(',') : '').length) - ); - const maxTypeLength = Math.max( - 'Type'.length, - ...orgAuths.map((orgAuth) => { - if (orgAuth.isScratchOrg) { - return 'Scratch'.length; - } - if (orgAuth.isDevHub) { - return 'DevHub'.length; - } - if (orgAuth.isSandbox) { - return 'Sandbox'.length; - } - return 0; - }) - ); - const choices = orgAuths - .map((orgAuth) => { - const aliasString = (orgAuth.aliases ? orgAuth.aliases.join(',') : '').padEnd(maxAliasLength, ' '); - const configString = (orgAuth.configs ? orgAuth.configs.join(',') : '').padEnd(maxConfigLength, ' '); - const typeString = chalk.dim( - (orgAuth.isScratchOrg ? 'Scratch' : orgAuth.isDevHub ? 'DevHub' : orgAuth.isSandbox ? 'Sandbox' : '').padEnd( - maxTypeLength, - ' ' - ) - ); - // username - aliases - configs - const key = `${chalk.bold( - orgAuth.username.padEnd(maxUsernameLength) - )} | ${typeString} | ${aliasString} | ${chalk.yellowBright(configString)}`; - return { name: key, value: orgAuth, checked: all, short: `${os.EOL}${orgAuth.username}` }; - }) - .sort((a, b) => a.value.username.localeCompare(b.value.username)); - const userHeader = `${'Username'.padEnd(maxUsernameLength, ' ')}`; - const aliasHeader = `${'Aliases'.padEnd(maxAliasLength, ' ')}`; - const configHeader = `${'Configs'.padEnd(maxConfigLength, ' ')}`; - const typeHeader = `${'Type'.padEnd(maxTypeLength, ' ')}`; - return [new Separator(` ${userHeader} | ${typeHeader} | ${aliasHeader} | ${configHeader}`), ...choices]; - } - public async run(): Promise { const { flags } = await this.parse(Logout); - this.flags = flags; - this.configAggregator = await ConfigAggregator.create(); - const remover = await AuthRemover.create(); - let orgAuths: OrgAuthorization[] = []; const targetUsername = - this.flags['target-org'] ?? (this.configAggregator.getInfo(OrgConfigProperties.TARGET_ORG).value as string); + flags['target-org'] ?? + ((await ConfigAggregator.create()).getInfo(OrgConfigProperties.TARGET_ORG).value as string); // if no-prompt, there must be a resolved target-org or --all if (flags['no-prompt'] && !targetUsername && !flags.all) { @@ -128,37 +74,38 @@ export default class Logout extends SfCommand { if (this.jsonEnabled() && !targetUsername && !flags.all) { throw messages.createError('noOrgSpecifiedWithJson'); } - - if (this.shouldFindAllAuths(targetUsername)) { - orgAuths = await AuthInfo.listAllAuthorizations(); - } else if (targetUsername) { - orgAuths = await AuthInfo.listAllAuthorizations( - (orgAuth) => orgAuth.username === targetUsername || !!orgAuth.aliases?.includes(targetUsername) - ); - } else { - // just for clarity - orgAuths = []; - } + const shouldFindAllAuths = + targetUsername && !flags.all + ? false + : flags.all || Global.getEnvironmentMode() === Mode.DEMO || !flags['no-prompt']; + + const orgAuths = shouldFindAllAuths + ? await AuthInfo.listAllAuthorizations() + : targetUsername + ? (await AuthInfo.listAllAuthorizations()).filter( + (orgAuth) => orgAuth.username === targetUsername || !!orgAuth.aliases?.includes(targetUsername) + ) + : []; if (orgAuths.length === 0) { - if (this.flags['target-org']) { + if (flags['target-org']) { // user specified a target org but it was not resolved, issue success message and return - this.logSuccess(messages.getMessage('logoutOrgCommandSuccess', [this.flags['target-org']])); - return [this.flags['target-org']]; + this.logSuccess(messages.getMessage('logoutOrgCommandSuccess', [flags['target-org']])); + return [flags['target-org']]; } this.info(messages.getMessage('noOrgsFound')); return []; } + const skipPrompt = flags['no-prompt'] || this.jsonEnabled(); - const { orgs, confirmed } = await this.promptForOrgsToRemove(orgAuths, flags.all); + const selectedOrgs = this.maybeWarnScratchOrgs( + skipPrompt ? orgAuths : await promptForOrgsToRemove(orgAuths, flags.all) + ); - if (confirmed) { - for (const org of orgs) { - // run sequentially to avoid configFile concurrency issues - // eslint-disable-next-line no-await-in-loop - await remover.removeAuth(org.username); - } - const loggedOutUsernames = orgs.map((org) => org.username); + if (skipPrompt || (await this.confirm({ message: getOrgConfirmationMessage(selectedOrgs, orgAuths.length) }))) { + const remover = await AuthRemover.create(); + const loggedOutUsernames = selectedOrgs.map((org) => org.username); + await Promise.all(loggedOutUsernames.map((username) => remover.removeAuth(username))); this.logSuccess(messages.getMessage('logoutOrgCommandSuccess', [loggedOutUsernames.join(os.EOL)])); return loggedOutUsernames; } else { @@ -167,68 +114,77 @@ export default class Logout extends SfCommand { } } - private shouldFindAllAuths(targetUsername: string | undefined): boolean { - if (targetUsername && !this.flags.all) { - return false; + /** Warning about logging out of a scratch org and losing access to it */ + private maybeWarnScratchOrgs(orgs: OrgAuthorization[]): OrgAuthorization[] { + if (orgs.some((org) => org.isScratchOrg)) { + this.warn(messages.getMessage('warning')); } - return this.flags.all || Global.getEnvironmentMode() === Mode.DEMO || !this.flags['no-prompt']; + return orgs; } +} - private async promptForOrgsToRemove( - orgAuths: OrgAuthorization[], - all: boolean - ): Promise<{ orgs: OrgAuthorization[]; confirmed: boolean }> { - if (this.flags['no-prompt'] || this.jsonEnabled()) { - return { orgs: orgAuths, confirmed: true }; - } - - if (orgAuths.length === 1) { - if (orgAuths[0].isScratchOrg) { - this.warn(messages.getMessage('warning')); +const promptForOrgsToRemove = async (orgAuths: OrgAuthorization[], all: boolean): Promise => + orgAuths.length === 1 + ? orgAuths + : checkbox({ + message: messages.getMessage('prompt.select-envs'), + // pick the orgs to delete - if flags.all - set each org to selected + // otherwise prompt the user to select the orgs to delete + choices: buildChoices(orgAuths, all), + loop: true, + }); + +const getOrgConfirmationMessage = (selectedOrgs: OrgAuthorization[], originalOrgCount: number): string => + selectedOrgs.length === originalOrgCount + ? messages.getMessage('prompt.confirm-all') + : messages.getMessage('prompt.confirm', [selectedOrgs.length, selectedOrgs.length > 1 ? 's' : '']); + +/** A whole bunch of custom formatting to make the list look nicer */ +const buildChoices = (orgAuths: OrgAuthorization[], all: boolean): Array => { + const maxUsernameLength = Math.max('Username'.length, ...orgAuths.map((orgAuth) => orgAuth.username.length)); + const maxAliasLength = Math.max( + 'Aliases'.length, + ...orgAuths.map((orgAuth) => (orgAuth.aliases ? orgAuth.aliases.join(',') : '').length) + ); + const maxConfigLength = Math.max( + 'Configs'.length, + ...orgAuths.map((orgAuth) => (orgAuth.configs ? orgAuth.configs.join(',') : '').length) + ); + const maxTypeLength = Math.max( + 'Type'.length, + ...orgAuths.map((orgAuth) => { + if (orgAuth.isScratchOrg) { + return 'Scratch'.length; } - if (await this.confirm(messages.getMessage('prompt.confirm.single', [orgAuths[0].username]), 30_000, false)) { - return { orgs: orgAuths, confirmed: true }; - } else { - return { orgs: [], confirmed: false }; + if (orgAuth.isDevHub) { + return 'DevHub'.length; } - } - - // pick the orgs to delete - if this.flags.all - set each org to selected - // otherwise prompt the user to select the orgs to delete - const choices = Logout.buildChoices(orgAuths, all); - const { orgs, confirmed } = await this.timedPrompt<{ orgs: OrgAuthorization[]; confirmed: boolean }>( - [ - { - name: 'orgs', - message: messages.getMessage('prompt.select-envs'), - type: 'checkbox', - choices, - loop: true, - }, - { - name: 'confirmed', - when: (answers): boolean => answers.orgs.length > 0, - message: (answers): string => { - const hasScratchOrgs = answers.orgs.some((org) => org.isScratchOrg); - if (hasScratchOrgs) { - this.warn(messages.getMessage('warning')); - } - const names = answers.orgs.map((org) => org.username); - if (names.length === orgAuths.length) { - return messages.getMessage('prompt.confirm-all'); - } else { - return messages.getMessage('prompt.confirm', [names.length, names.length > 1 ? 's' : '']); - } - }, - type: 'confirm', - default: false, - }, - ], - 30_000 - ); - return { - orgs: orgs.map((a) => a), - confirmed, - }; - } -} + if (orgAuth.isSandbox) { + return 'Sandbox'.length; + } + return 0; + }) + ); + const choices = orgAuths + .map((orgAuth) => { + const aliasString = (orgAuth.aliases ? orgAuth.aliases.join(',') : '').padEnd(maxAliasLength, ' '); + const configString = (orgAuth.configs ? orgAuth.configs.join(',') : '').padEnd(maxConfigLength, ' '); + const typeString = chalk.dim( + (orgAuth.isScratchOrg ? 'Scratch' : orgAuth.isDevHub ? 'DevHub' : orgAuth.isSandbox ? 'Sandbox' : '').padEnd( + maxTypeLength, + ' ' + ) + ); + // username - aliases - configs + const key = `${chalk.bold( + orgAuth.username.padEnd(maxUsernameLength) + )} | ${typeString} | ${aliasString} | ${chalk.yellowBright(configString)}`; + return { name: key, value: orgAuth, checked: all, short: `${os.EOL}${orgAuth.username}` }; + }) + .sort((a, b) => a.value.username.localeCompare(b.value.username)); + const userHeader = `${'Username'.padEnd(maxUsernameLength, ' ')}`; + const aliasHeader = `${'Aliases'.padEnd(maxAliasLength, ' ')}`; + const configHeader = `${'Configs'.padEnd(maxConfigLength, ' ')}`; + const typeHeader = `${'Type'.padEnd(maxTypeLength, ' ')}`; + return [new Separator(` ${userHeader} | ${typeHeader} | ${aliasHeader} | ${configHeader}`), ...choices]; +}; diff --git a/src/common.ts b/src/common.ts index 2ecb8b45..0b32a517 100644 --- a/src/common.ts +++ b/src/common.ts @@ -8,7 +8,6 @@ import { Logger, SfdcUrl, SfProject, Messages, SfError, Global, Mode } from '@salesforce/core'; import { getString, isObject } from '@salesforce/ts-types'; import chalk from 'chalk'; -import password from '@inquirer/password'; import confirm from '@inquirer/confirm'; Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); @@ -44,19 +43,12 @@ const throwIfLightning = (urlString?: string): void => { } }; -const clientSecretPrompt = async (): Promise => - password({ message: chalk.dim(messages.getMessage('clientSecretStdin')) }); -const accessTokenPrompt = async (): Promise => - password({ message: chalk.dim(messages.getMessage('accessTokenStdin')) }); - const shouldExitCommand = async (noPrompt?: boolean): Promise => Boolean(noPrompt) || Global.getEnvironmentMode() !== Mode.DEMO ? false : !(await confirm({ message: chalk.dim(messages.getMessage('warnAuth', ['sf'])) })); export default { - clientSecretPrompt, - accessTokenPrompt, shouldExitCommand, resolveLoginUrl, }; diff --git a/test/commands/org/login/access-token.test.ts b/test/commands/org/login/access-token.test.ts index e318ec56..34c5a277 100644 --- a/test/commands/org/login/access-token.test.ts +++ b/test/commands/org/login/access-token.test.ts @@ -5,21 +5,23 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -/* eslint-disable @typescript-eslint/ban-ts-comment */ - -import { AuthFields, AuthInfo, SfError, StateAggregator } from '@salesforce/core'; +import { AuthFields, AuthInfo, StateAggregator } from '@salesforce/core'; import { assert, expect } from 'chai'; import { TestContext } from '@salesforce/core/lib/testSetup.js'; -import { Env } from '@salesforce/kit'; -import { Config } from '@oclif/core'; +import { stubPrompter, stubSfCommandUx } from '@salesforce/sf-plugins-core'; +import { env } from '@salesforce/kit'; import Store from '../../../../src/commands/org/login/access-token.js'; -import common from '../../../../src/common.js'; describe('org:login:access-token', () => { const $$ = new TestContext(); - - let authFields: Required>; const accessToken = '00Dxx0000000000!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; + const authFields = { + accessToken, + instanceUrl: 'https://foo.bar.org.salesforce.com', + loginUrl: 'https://foo.bar.org.salesforce.com', + orgId: '00D000000000000', + username: 'foo@baz.org', + } as const satisfies AuthFields; /* eslint-disable camelcase */ const userInfo = { @@ -28,27 +30,10 @@ describe('org:login:access-token', () => { custom_domain: 'https://foo.bar.org.salesforce.com', }; /* eslint-enable camelcase */ + let stubSfCommandUxStubs: ReturnType; + let prompterStubs: ReturnType; - async function createNewStoreCommand( - flags: string[] = [], - promptAnswer = accessToken, - authFileExists = false, - useSfdxAccessTokenEnvVar = false - ): Promise { - authFields = { - accessToken, - instanceUrl: 'https://foo.bar.org.salesforce.com', - loginUrl: 'https://foo.bar.org.salesforce.com', - orgId: '00D000000000000', - username: 'foo@baz.org', - }; - - $$.SANDBOX.stub(StateAggregator, 'getInstance').resolves({ - // @ts-expect-error because incomplete interface - orgs: { - exists: () => Promise.resolve(authFileExists), - }, - }); + beforeEach(() => { // @ts-expect-error because private method $$.SANDBOX.stub(Store.prototype, 'saveAuthInfo').resolves(userInfo); $$.SANDBOX.stub(AuthInfo.prototype, 'getUsername').returns(authFields.username); @@ -61,68 +46,87 @@ describe('org:login:access-token', () => { }); // @ts-expect-error because private method $$.SANDBOX.stub(Store.prototype, 'getUserInfo').resolves(AuthInfo.prototype); - if (useSfdxAccessTokenEnvVar) { - $$.SANDBOX.stub(Env.prototype, 'getString').callsFake(() => accessToken); - } - const store = new Store( - [...new Set(['--instance-url', 'https://foo.bar.org.salesforce.com', '--no-prompt', ...flags])], - {} as Config - ); - $$.SANDBOX.stub(common, 'accessTokenPrompt').resolves(promptAnswer); - - return Promise.resolve(store); - } + stubSfCommandUxStubs = stubSfCommandUx($$.SANDBOX); + prompterStubs = stubPrompter($$.SANDBOX); + }); it('should return auth fields after successful auth', async () => { - const store = await createNewStoreCommand(['--instance-url', 'https://foo.bar.org.salesforce.com', '--no-prompt']); - const result = await store.run(); - expect(result).to.deep.equal(authFields); - }); + prompterStubs.secret.resolves(accessToken); - it('should prompt for access token when accesstokenfile is not present', async () => { - const store = await createNewStoreCommand(['--instance-url', 'https://foo.bar.org.salesforce.com', '--no-prompt']); - const result = await store.run(); + const result = await Store.run(['--instance-url', 'https://foo.bar.org.salesforce.com']); + expect(prompterStubs.secret.callCount).to.equal(1); + expect(stubSfCommandUxStubs.logSuccess.callCount).to.equal(1); expect(result).to.deep.equal(authFields); }); it('should show invalid access token provided as input', async () => { - const store = await createNewStoreCommand( - ['--instance-url', 'https://foo.bar.org.salesforce.com', '--no-prompt'], - 'invalidaccesstokenformat' - ); + prompterStubs.secret.resolves('invalidaccesstokenformat'); + try { - await store.run(); + await Store.run(['--instance-url', 'https://foo.bar.org.salesforce.com']); assert(false, 'should throw error'); } catch (e) { - const err = e as SfError; - expect(err.message).to.include("The access token isn't in the correct format"); + assert(e instanceof Error); + expect(e.message).to.include("The access token isn't in the correct format"); } + expect(prompterStubs.secret.callCount).to.equal(1); }); it('should show that auth file already exists', async () => { - const store = await createNewStoreCommand( - ['--instance-url', 'https://foo.bar.org.salesforce.com'], - accessToken, - true - ); - await store.run(); + prompterStubs.secret.resolves(accessToken); + prompterStubs.confirm.resolves(false); + $$.SANDBOX.stub(StateAggregator, 'getInstance').resolves({ + // @ts-expect-error because incomplete interface + orgs: { + exists: () => Promise.resolve(true), + }, + }); + const result = await Store.run(['--instance-url', 'https://foo.bar.org.salesforce.com']); + expect(result).to.deep.equal(authFields); + expect(prompterStubs.secret.callCount).to.equal(1); + expect(prompterStubs.confirm.callCount).to.equal(1); }); it('should show that auth file does not already exist', async () => { - const store = await createNewStoreCommand(['--instance-url', 'https://foo.bar.org.salesforce.com'], accessToken); - const result = await store.run(); - // prompt once; one for access token + prompterStubs.secret.resolves(accessToken); + $$.SANDBOX.stub(StateAggregator, 'getInstance').resolves({ + // @ts-expect-error because incomplete interface + orgs: { + exists: () => Promise.resolve(false), + }, + }); + const result = await Store.run(['--instance-url', 'https://foo.bar.org.salesforce.com']); expect(result).to.deep.equal(authFields); + expect(prompterStubs.confirm.callCount).to.equal(0); }); + it('should use env var SF_ACCESS_TOKEN as input to the store command', async () => { - const store = await createNewStoreCommand( - ['--instance-url', 'https://foo.bar.org.salesforce.com'], - accessToken, - false, - true - ); - const result = await store.run(); - // no prompts + $$.SANDBOX.stub(env, 'getString') + .withArgs('SF_ACCESS_TOKEN') + .returns(accessToken) + .withArgs('SFDX_ACCESS_TOKEN') + // @ts-expect-error not sure why TS thinks a string is required. getString can return undefined + .returns(undefined); + + const result = await Store.run(['--instance-url', 'https://foo.bar.org.salesforce.com']); + expect(result).to.deep.equal(authFields); + // no prompts needed when using Env + expect(prompterStubs.confirm.callCount).to.equal(0); + expect(prompterStubs.secret.callCount).to.equal(0); + }); + + it('should use env var SFDX_ACCESS_TOKEN as input to the store command', async () => { + $$.SANDBOX.stub(env, 'getString') + .withArgs('SFDX_ACCESS_TOKEN') + .returns(accessToken) + .withArgs('SF_ACCESS_TOKEN') + // @ts-expect-error not sure why TS thinks a string is required. getString can return undefined + .returns(undefined); + + const result = await Store.run(['--instance-url', 'https://foo.bar.org.salesforce.com']); expect(result).to.deep.equal(authFields); + // no prompts needed when using Env + expect(prompterStubs.confirm.callCount).to.equal(0); + expect(prompterStubs.secret.callCount).to.equal(0); }); }); diff --git a/test/commands/org/login/login.device.test.ts b/test/commands/org/login/login.device.test.ts index 0aa59710..aa73161e 100644 --- a/test/commands/org/login/login.device.test.ts +++ b/test/commands/org/login/login.device.test.ts @@ -15,7 +15,6 @@ import { expect } from 'chai'; import { Config } from '@oclif/core'; import { SfCommand } from '@salesforce/sf-plugins-core'; import Login from '../../../../src/commands/org/login/device.js'; -import common from '../../../../src/common.js'; interface Options { approvalTimesout?: boolean; approvalFails?: boolean; @@ -136,9 +135,7 @@ describe('org:login:device', () => { it('should prompt for client secret if client id is provided', async () => { await prepareStubs(); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - $$.SANDBOX.stub(common, 'clientSecretPrompt').resolves('1234'); + $$.SANDBOX.stub(SfCommand.prototype, 'secretPrompt').resolves('1234'); const login = new Login(['-i', 'CoffeeBeans', '--json'], {} as Config); const response = await login.run(); expect(response.username).to.equal(testData.username); diff --git a/test/commands/org/login/login.web.test.ts b/test/commands/org/login/login.web.test.ts index c7e035ad..8e23e556 100644 --- a/test/commands/org/login/login.web.test.ts +++ b/test/commands/org/login/login.web.test.ts @@ -15,7 +15,6 @@ import { assert, expect } from 'chai'; import { Env } from '@salesforce/kit'; import { SfCommand, Ux } from '@salesforce/sf-plugins-core'; import LoginWeb from '../../../../src/commands/org/login/web.js'; -import common from '../../../../src/common.js'; describe('org:login:web', () => { const $$ = new TestContext(); @@ -31,7 +30,7 @@ describe('org:login:web', () => { clientSecret = '' ): Promise { authFields = await testData.getConfig(); - $$.SANDBOX.stub(common, 'clientSecretPrompt').resolves(clientSecret); + $$.SANDBOX.stub(SfCommand.prototype, 'secretPrompt').resolves(clientSecret); $$.SANDBOX.stub(SfCommand.prototype, 'confirm').resolves(promptAnswer); authInfoStub = stubInterface($$.SANDBOX, { diff --git a/test/commands/org/logout.test.ts b/test/commands/org/logout.test.ts index 55d1d6c5..21fcefba 100644 --- a/test/commands/org/logout.test.ts +++ b/test/commands/org/logout.test.ts @@ -42,6 +42,8 @@ describe('org:logout', () => { if (options['target-org']) { $$.setConfigStubContents('Config', { contents: { 'target-org': options['target-org'] } }); + } else { + $$.setConfigStubContents('Config', { contents: {} }); } if (options.aliases) { diff --git a/yarn.lock b/yarn.lock index 13563c31..c8c47ad3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -445,6 +445,17 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz#e5211452df060fa8522b55c7b3c0c4d1981cb044" integrity sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw== +"@inquirer/checkbox@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@inquirer/checkbox/-/checkbox-1.5.0.tgz#05869b4ee81e2c8d523799ef350d57cabd556bfa" + integrity sha512-3cKJkW1vIZAs4NaS0reFsnpAjP0azffYII4I2R7PTI7ZTMg5Y1at4vzXccOH3762b2c2L4drBhpJpf9uiaGNxA== + dependencies: + "@inquirer/core" "^5.1.1" + "@inquirer/type" "^1.1.5" + ansi-escapes "^4.3.2" + chalk "^4.1.2" + figures "^3.2.0" + "@inquirer/confirm@^2.0.15": version "2.0.15" resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-2.0.15.tgz#b5512ed190efd8c5b96e0969115756b48546ab36" @@ -493,6 +504,17 @@ ansi-escapes "^4.3.2" chalk "^4.1.2" +"@inquirer/select@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@inquirer/select/-/select-1.3.1.tgz#b10bb8d4ba72f08eb887b3d948eb734d680897c6" + integrity sha512-EgOPHv7XOHEqiBwBJTyiMg9r57ySyW4oyYCumGp+pGyOaXQaLb2kTnccWI6NFd9HSi5kDJhF7YjA+3RfMQJ2JQ== + dependencies: + "@inquirer/core" "^5.1.1" + "@inquirer/type" "^1.1.5" + ansi-escapes "^4.3.2" + chalk "^4.1.2" + figures "^3.2.0" + "@inquirer/type@^1.1.5": version "1.1.5" resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-1.1.5.tgz#b8c171f755859c8159b10e41e1e3a88f0ca99d7f" @@ -791,10 +813,10 @@ wordwrap "^1.0.0" wrap-ansi "^7.0.0" -"@oclif/core@^3.0.4", "@oclif/core@^3.10.8", "@oclif/core@^3.15.0", "@oclif/core@^3.15.1", "@oclif/core@^3.3.1": - version "3.15.1" - resolved "https://registry.yarnpkg.com/@oclif/core/-/core-3.15.1.tgz#e03fa775d658e76056150ac0c7b8097b6f51ab9c" - integrity sha512-d4457zVo2agLoJG97CmdY6M3BeP5sogBP3BtP65hUvJH6wA6Us1hdY3UiPPtD/ZzZImq7cATVMABuCF9tM+rWA== +"@oclif/core@^3.0.4", "@oclif/core@^3.10.8", "@oclif/core@^3.15.1", "@oclif/core@^3.16.0", "@oclif/core@^3.3.1": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@oclif/core/-/core-3.16.0.tgz#682657cb5e4a3262a47e26e0c8a7bf0343acaf76" + integrity sha512-/PIz+udzb59XE8O/bQvqlCtXy6RByEHH0KsrAJNa/ZrqtdsLmeDNJcHdgygFHx+nz+PYMoUzsyzJMau++EDNoQ== dependencies: ansi-escapes "^4.3.2" ansi-styles "^4.3.0" @@ -987,10 +1009,10 @@ strip-ansi "6.0.1" ts-retry-promise "^0.7.1" -"@salesforce/core@^6.4.0", "@salesforce/core@^6.4.1", "@salesforce/core@^6.4.2": - version "6.4.2" - resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-6.4.2.tgz#e6e5e2d5e6ad17f1cbe0feb5a0e55e8b74c91e3c" - integrity sha512-ZtLwgI18f1th6SY0fKmuiyVGGwTXPy4nI+KfXzkww/vqdPxDhAfoHwqvFeo4tjFE2rldK7IPiIIYdtpsaI8d5g== +"@salesforce/core@^6.4.0", "@salesforce/core@^6.4.1", "@salesforce/core@^6.4.2", "@salesforce/core@^6.4.3": + version "6.4.3" + resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-6.4.3.tgz#77edcf00c4b6e0b43a0620eb7d1cd97ad0bf17e4" + integrity sha512-MEHd9W2PM+Ytosirry7vInBrtU4RZK+v7ubNozbR4kCWr4yMe+1vzAkGqB2NoL5vAVbOmF7wyy8+tMGYCyLyww== dependencies: "@salesforce/kit" "^3.0.15" "@salesforce/schemas" "^1.6.1" @@ -1006,7 +1028,7 @@ jszip "3.10.1" pino "^8.16.2" pino-abstract-transport "^1.1.0" - pino-pretty "^10.3.0" + pino-pretty "^10.3.1" proper-lockfile "^4.1.2" semver "^7.5.4" ts-retry-promise "^0.7.1" @@ -1016,10 +1038,10 @@ resolved "https://registry.yarnpkg.com/@salesforce/dev-config/-/dev-config-4.1.0.tgz#e529576466d074e7a5f1441236510fef123da01e" integrity sha512-2iDDepiIwjXHS5IVY7pwv8jMo4xWosJ7p/UTj+lllpB/gnJiYLhjJPE4Z3FCGFKyvfg5jGaimCd8Ca6bLGsCQA== -"@salesforce/dev-scripts@^8.1.3": - version "8.1.3" - resolved "https://registry.yarnpkg.com/@salesforce/dev-scripts/-/dev-scripts-8.1.3.tgz#b33c53b4b648b5090322d1144c6852ca587d6eb7" - integrity sha512-mSWCab+uO3O2QNBCRJpKsHw/UYpOGpNFWT12BC85e+VyWBp4LTIAsssBKDY8czRI02K7NwiYaC34fNdP+yqrzA== +"@salesforce/dev-scripts@^8.2.0": + version "8.2.0" + resolved "https://registry.yarnpkg.com/@salesforce/dev-scripts/-/dev-scripts-8.2.0.tgz#e8ca6fd377b0e85ecf88baea727493a68c2154ee" + integrity sha512-dzVBmetyBe8IM1XJCBw83U5rC0TSwvXa1MPRRI0pmE/VeBqlfxM7rneKtlORCirz9n8SAvCiQvI4oJ/yWW5Quw== dependencies: "@commitlint/cli" "^17.1.2" "@commitlint/config-conventional" "^17.8.1" @@ -1080,7 +1102,20 @@ resolved "https://registry.yarnpkg.com/@salesforce/schemas/-/schemas-1.6.1.tgz#7d1c071e1e509ca9d2d8a6e48ac7447dd67a534d" integrity sha512-eVy947ZMxCJReKJdgfddUIsBIbPTa/i8RwQGwxq4/ss38H5sLOAeSTaun9V7HpJ1hkpDznWKfgzYvjsst9K6ig== -"@salesforce/sf-plugins-core@^5.0.12", "@salesforce/sf-plugins-core@^5.0.13": +"@salesforce/sf-plugins-core@5.0.14-dev.0": + version "5.0.14-dev.0" + resolved "https://registry.yarnpkg.com/@salesforce/sf-plugins-core/-/sf-plugins-core-5.0.14-dev.0.tgz#9588807ae33e4b7bafeaab76c67ee2cbb4529dc8" + integrity sha512-gveYtjQkkcU3b38dkhLg1J9+DKbsezc1l8xyRBv087F5+6EE241CuGhGGuzOlDWdSg+YKZiUKG5F1BxqV8q55w== + dependencies: + "@inquirer/confirm" "^2.0.15" + "@inquirer/password" "^1.1.14" + "@oclif/core" "^3.16.0" + "@salesforce/core" "^6.4.2" + "@salesforce/kit" "^3.0.15" + "@salesforce/ts-types" "^2.0.9" + chalk "^5.3.0" + +"@salesforce/sf-plugins-core@^5.0.12": version "5.0.13" resolved "https://registry.yarnpkg.com/@salesforce/sf-plugins-core/-/sf-plugins-core-5.0.13.tgz#f2941527d66ded5750a6646e146af047ab72acc9" integrity sha512-b5R8krKeOIkW0hPxvfpm8T5tCSyWW7MDERnJwm/FXq4ueUJsC1/TCWSscyVKPSZ0VRcEFbzOWKJvpV/omB1D9w== @@ -1307,10 +1342,10 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@^18": - version "18.18.8" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.18.8.tgz#2b285361f2357c8c8578ec86b5d097c7f464cfd6" - integrity sha512-OLGBaaK5V3VRBS1bAkMVP2/W9B+H8meUfl866OrMNQqt7wDgdpWPp5o6gmIc9pB+lIQHSq4ZL8ypeH1vPxcPaQ== +"@types/node@*", "@types/node@^20.9.0": + version "20.10.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.6.tgz#a3ec84c22965802bf763da55b2394424f22bfbb5" + integrity sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw== dependencies: undici-types "~5.26.4" @@ -1324,10 +1359,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa" integrity sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A== -"@types/node@^20.9.0": - version "20.10.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.6.tgz#a3ec84c22965802bf763da55b2394424f22bfbb5" - integrity sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw== +"@types/node@^18": + version "18.18.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.18.8.tgz#2b285361f2357c8c8578ec86b5d097c7f464cfd6" + integrity sha512-OLGBaaK5V3VRBS1bAkMVP2/W9B+H8meUfl866OrMNQqt7wDgdpWPp5o6gmIc9pB+lIQHSq4ZL8ypeH1vPxcPaQ== dependencies: undici-types "~5.26.4" @@ -1622,11 +1657,6 @@ ansi-colors@4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== -ansi-escapes@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" - integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== - ansi-escapes@^4.2.1, ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" @@ -2309,12 +2339,7 @@ cli-progress@^3.12.0: dependencies: string-width "^4.2.3" -cli-spinners@^2.5.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" - integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== - -cli-spinners@^2.9.1: +cli-spinners@^2.5.0, cli-spinners@^2.9.1: version "2.9.2" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== @@ -2608,17 +2633,6 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -5523,11 +5537,6 @@ neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - nise@^1.3.3: version "1.5.3" resolved "https://registry.yarnpkg.com/nise/-/nise-1.5.3.tgz#9d2cfe37d44f57317766c6e9408a359c5d3ac1f7" @@ -6090,15 +6099,7 @@ pascal-case@^3.1.2: no-case "^3.0.4" tslib "^2.0.3" -password-prompt@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/password-prompt/-/password-prompt-1.1.2.tgz#85b2f93896c5bd9e9f2d6ff0627fa5af3dc00923" - integrity sha512-bpuBhROdrhuN3E7G/koAju0WjVw9/uQOG5Co5mokNj0MiOSBVZS1JTwM4zl55hu0WFmIEFvO9cU9sJQiBIYeIA== - dependencies: - ansi-escapes "^3.1.0" - cross-spawn "^6.0.5" - -password-prompt@^1.1.3: +password-prompt@^1.1.2, password-prompt@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/password-prompt/-/password-prompt-1.1.3.tgz#05e539f4e7ca4d6c865d479313f10eb9db63ee5f" integrity sha512-HkrjG2aJlvF0t2BMH0e2LB/EHf3Lcq3fNMzy4GYHcQblAvOl+QQji1Lx7WRBMqpVK8p+KR7bCg7oqAMXtdgqyw== @@ -6124,11 +6125,6 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== - path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" @@ -6197,10 +6193,10 @@ pino-abstract-transport@^1.0.0, pino-abstract-transport@^1.1.0, pino-abstract-tr readable-stream "^4.0.0" split2 "^4.0.0" -pino-pretty@^10.3.0: - version "10.3.0" - resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-10.3.0.tgz#483ff78b98d277c33d00e0419c00601d9152bc7e" - integrity sha512-JthvQW289q3454mhM3/38wFYGWPiBMR28T3CpDNABzoTQOje9UKS7XCJQSnjWF9LQGQkGd8D7h0oq+qwiM3jFA== +pino-pretty@^10.3.1: + version "10.3.1" + resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-10.3.1.tgz#e3285a5265211ac6c7cd5988f9e65bf3371a0ca9" + integrity sha512-az8JbIYeN/1iLj2t0jR9DV48/LQ3RC6hZPpapKPkb84Q+yTidMCpgWxIT3N0flnBDilyBQ1luWNpOeJptjdp/g== dependencies: colorette "^2.0.7" dateformat "^4.6.3" @@ -6754,7 +6750,7 @@ secure-json-parse@^2.4.0: resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== -"semver@2 || 3 || 4 || 5", semver@^5.5.0: +"semver@2 || 3 || 4 || 5": version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== @@ -6833,13 +6829,6 @@ setimmediate@^1.0.5: resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== - dependencies: - shebang-regex "^1.0.0" - shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -6847,11 +6836,6 @@ shebang-command@^2.0.0: dependencies: shebang-regex "^3.0.0" -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== - shebang-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" @@ -7812,13 +7796,6 @@ which-typed-array@^1.1.11, which-typed-array@^1.1.13, which-typed-array@^1.1.2: gopd "^1.0.1" has-tostringtag "^1.0.0" -which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"