From 3cd0fbfe209e2111e15a91b509f5e336ceedf771 Mon Sep 17 00:00:00 2001 From: mshanemc Date: Sat, 30 Dec 2023 14:06:41 -0600 Subject: [PATCH 1/4] feat: use new inquirer --- README.md | 2 +- command-snapshot.json | 12 +-- messages/jwt.grant.md | 2 +- messages/messages.md | 4 - package.json | 4 +- src/authBaseCommand.ts | 54 ----------- src/commands/org/login/access-token.ts | 30 +++--- src/commands/org/login/device.ts | 23 ++--- src/commands/org/login/jwt.ts | 18 ++-- src/commands/org/login/sfdx-url.ts | 14 ++- src/commands/org/login/web.ts | 37 +++---- src/commands/org/logout.ts | 11 +-- src/common.ts | 67 ++++++++----- test/commands/org/login/access-token.test.ts | 22 +++-- test/commands/org/login/login.device.test.ts | 24 +---- test/commands/org/login/login.jwt.test.ts | 38 +------- .../commands/org/login/login.sfdx-url.test.ts | 30 +----- test/commands/org/login/login.web.test.ts | 20 +--- test/common.test.ts | 40 +++++--- yarn.lock | 96 ++++++++++++++++++- 20 files changed, 239 insertions(+), 309 deletions(-) delete mode 100644 src/authBaseCommand.ts diff --git a/README.md b/README.md index c8c2f8de..f1a0ff28 100644 --- a/README.md +++ b/README.md @@ -275,7 +275,7 @@ ALIASES $ sf auth jwt grant EXAMPLES - Log into an org with username jdoe@example.org and on the default instance URL (https://login.salesforce.org). The + Log into an org with username jdoe@example.org and on the default instance URL (https://login.salesforce.com). The private key is stored in the file /Users/jdoe/JWT/server.key and the command uses the connected app with consumer key (client id) 04580y4051234051. diff --git a/command-snapshot.json b/command-snapshot.json index 6c698b13..5c2a190e 100644 --- a/command-snapshot.json +++ b/command-snapshot.json @@ -25,16 +25,7 @@ { "command": "org:login:device", "plugin": "@salesforce/plugin-auth", - "flags": [ - "alias", - "client-id", - "disable-masking", - "instance-url", - "json", - "loglevel", - "set-default", - "set-default-dev-hub" - ], + "flags": ["alias", "client-id", "instance-url", "json", "loglevel", "set-default", "set-default-dev-hub"], "alias": ["force:auth:device:login", "auth:device:login"], "flagChars": ["a", "d", "i", "r", "s"], "flagAliases": [ @@ -101,7 +92,6 @@ "alias", "browser", "client-id", - "disable-masking", "instance-url", "json", "loglevel", diff --git a/messages/jwt.grant.md b/messages/jwt.grant.md index d0bb8458..aaa05457 100644 --- a/messages/jwt.grant.md +++ b/messages/jwt.grant.md @@ -20,7 +20,7 @@ We recommend that you set an alias when you log into an org. Aliases make it eas # examples -- Log into an org with username jdoe@example.org and on the default instance URL (https://login.salesforce.org). The private key is stored in the file /Users/jdoe/JWT/server.key and the command uses the connected app with consumer key (client id) 04580y4051234051. +- Log into an org with username jdoe@example.org and on the default instance URL (https://login.salesforce.com). The private key is stored in the file /Users/jdoe/JWT/server.key and the command uses the connected app with consumer key (client id) 04580y4051234051. <%= config.bin %> <%= command.id %> --username jdoe@example.org --jwt-key-file /Users/jdoe/JWT/server.key --client-id 04580y4051234051 diff --git a/messages/messages.md b/messages/messages.md index 88d85fed..ea3222c8 100644 --- a/messages/messages.md +++ b/messages/messages.md @@ -40,10 +40,6 @@ Do you want to authorize this org for use with the Salesforce CLI? Don't prompt for confirmation. -# flags.disable-masking.summary - -Disable masking of user input; use with problematic terminals. - # clientSecretStdin OAuth client secret of personal connected app? Press Enter if it's not required. diff --git a/package.json b/package.json index f4aa7264..d1f817e7 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,8 @@ "author": "Salesforce", "bugs": "https://github.com/forcedotcom/cli/issues", "dependencies": { + "@inquirer/confirm": "^2.0.15", + "@inquirer/password": "^1.1.14", "@oclif/core": "^3.15.0", "@salesforce/core": "^6.4.2", "@salesforce/kit": "^3.0.15", @@ -219,7 +221,7 @@ "output": [] }, "link-check": { - "command": "node -e \"process.exit(process.env.CI ? 0 : 1)\" || linkinator \"**/*.md\" --skip \"CHANGELOG.md|node_modules|test/|confluence.internal.salesforce.com|%s\" --markdown --retry --directory-listing --verbosity error", + "command": "node -e \"process.exit(process.env.CI ? 0 : 1)\" || linkinator \"**/*.md\" --skip \"CHANGELOG.md|node_modules|test/|confluence.internal.salesforce.com|my.salesforce.com|%s\" --markdown --retry --directory-listing --verbosity error", "files": [ "./*.md", "./!(CHANGELOG).md", diff --git a/src/authBaseCommand.ts b/src/authBaseCommand.ts deleted file mode 100644 index e79d51b2..00000000 --- a/src/authBaseCommand.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2020, salesforce.com, inc. - * All rights reserved. - * Licensed under the BSD 3-Clause license. - * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause - */ - - -import { Messages, Global, Mode } from '@salesforce/core'; -import chalk from 'chalk'; -import { SfCommand } from '@salesforce/sf-plugins-core'; -import { Config } from '@oclif/core'; - -Messages.importMessagesDirectoryFromMetaUrl(import.meta.url) -const messages = Messages.loadMessages('@salesforce/plugin-auth', 'messages'); - -function dimMessage(message: string): string { - return chalk.dim(message); -} - -export abstract class AuthBaseCommand extends SfCommand { - public constructor(argv: string[], config: Config) { - super(argv, config); - } - - protected async askForHiddenResponse(messageKey: string, disableMasking = false): Promise { - const msg = dimMessage(messages.getMessage(messageKey)); - const hidden: { response: string } = await this.prompt({ - message: msg, - type: 'input', - name: 'response', - transformer: (input: string) => (disableMasking ? input : '*'.repeat(input.length)), - }); - return hidden.response; - } - - protected async shouldExitCommand(noPrompt?: boolean, message?: string): Promise { - if (Boolean(noPrompt) || Global.getEnvironmentMode() !== Mode.DEMO) { - return false; - } else { - const msg = dimMessage(message ?? messages.getMessage('warnAuth', [this.config.bin])); - const answer = await this.confirm(msg); - return !answer; - } - } - - protected async askForClientSecret(disableMasking = false): Promise { - return this.askForHiddenResponse('clientSecretStdin', disableMasking); - } - - protected async askForAccessToken(disableMasking = false): Promise { - return this.askForHiddenResponse('accessTokenStdin', disableMasking); - } -} diff --git a/src/commands/org/login/access-token.ts b/src/commands/org/login/access-token.ts index c952e7ae..d773dd38 100644 --- a/src/commands/org/login/access-token.ts +++ b/src/commands/org/login/access-token.ts @@ -5,26 +5,24 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ - - -import { Flags, loglevel } from '@salesforce/sf-plugins-core'; +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 { AuthBaseCommand } from '../../../authBaseCommand.js'; +import common from '../../../common.js'; -Messages.importMessagesDirectoryFromMetaUrl(import.meta.url) +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('@salesforce/plugin-auth', 'accesstoken.store'); const commonMessages = Messages.loadMessages('@salesforce/plugin-auth', 'messages'); const ACCESS_TOKEN_FORMAT = '"!"'; -export default class LoginAccessToken extends AuthBaseCommand { +export default class LoginAccessToken extends SfCommand { public static readonly summary = messages.getMessage('summary'); public static readonly description = messages.getMessage('description'); public static readonly examples = messages.getMessages('examples'); public static readonly deprecateAliases = true; - public static aliases = ['force:auth:accesstoken:store', 'auth:accesstoken:store']; + public static readonly aliases = ['force:auth:accesstoken:store', 'auth:accesstoken:store']; public static readonly flags = { 'instance-url': Flags.url({ @@ -72,7 +70,7 @@ export default class LoginAccessToken extends AuthBaseCommand { const { flags } = await this.parse(LoginAccessToken); this.flags = flags; const instanceUrl = flags['instance-url'].href; - const accessToken = await this.getAccessToken(); + const accessToken = await getAccessToken(); const authInfo = await this.getUserInfo(accessToken, instanceUrl); return this.storeAuthFromAccessToken(authInfo); } @@ -114,14 +112,14 @@ export default class LoginAccessToken extends AuthBaseCommand { } return true; } +} - private async getAccessToken(): Promise { - const accessToken = - env.getString('SF_ACCESS_TOKEN') ?? env.getString('SFDX_ACCESS_TOKEN') ?? (await this.askForAccessToken()); +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])); - } - return accessToken; + if (!matchesAccessToken(accessToken)) { + throw new SfError(messages.getMessage('invalidAccessTokenFormat', [ACCESS_TOKEN_FORMAT])); } -} + return accessToken; +}; diff --git a/src/commands/org/login/device.ts b/src/commands/org/login/device.ts index 9e4ff604..588b59be 100644 --- a/src/commands/org/login/device.ts +++ b/src/commands/org/login/device.ts @@ -5,26 +5,23 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ - - import { AuthFields, AuthInfo, DeviceOauthService, Messages, OAuth2Config } from '@salesforce/core'; -import { Flags, loglevel } from '@salesforce/sf-plugins-core'; +import { Flags, SfCommand, loglevel } from '@salesforce/sf-plugins-core'; import { DeviceCodeResponse } from '@salesforce/core/lib/deviceOauthService.js'; import { ux } from '@oclif/core'; -import { AuthBaseCommand } from '../../../authBaseCommand.js'; -import { Common } from '../../../common.js'; +import common from '../../../common.js'; -Messages.importMessagesDirectoryFromMetaUrl(import.meta.url) +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('@salesforce/plugin-auth', 'device.login'); const commonMessages = Messages.loadMessages('@salesforce/plugin-auth', 'messages'); export type DeviceLoginResult = (AuthFields & DeviceCodeResponse) | Record; -export default class LoginDevice extends AuthBaseCommand { +export default class LoginDevice extends SfCommand { public static readonly summary = messages.getMessage('summary'); public static readonly description = messages.getMessage('description'); public static readonly examples = messages.getMessages('examples'); - public static aliases = ['force:auth:device:login', 'auth:device:login']; + public static readonly aliases = ['force:auth:device:login', 'auth:device:login']; public static readonly flags = { 'client-id': Flags.string({ @@ -58,21 +55,15 @@ export default class LoginDevice extends AuthBaseCommand { deprecateAliases: true, aliases: ['setalias'], }), - 'disable-masking': Flags.boolean({ - summary: commonMessages.getMessage('flags.disable-masking.summary'), - hidden: true, - deprecateAliases: true, - aliases: ['disablemasking'], - }), loglevel, }; public async run(): Promise { const { flags } = await this.parse(LoginDevice); - if (await this.shouldExitCommand(false)) return {}; + if (await common.shouldExitCommand(false)) return {}; const oauthConfig: OAuth2Config = { - loginUrl: await Common.resolveLoginUrl(flags['instance-url']?.href), + loginUrl: await common.resolveLoginUrl(flags['instance-url']?.href), clientId: flags['client-id'], }; diff --git a/src/commands/org/login/jwt.ts b/src/commands/org/login/jwt.ts index 5b1d781c..9c37b42d 100644 --- a/src/commands/org/login/jwt.ts +++ b/src/commands/org/login/jwt.ts @@ -5,26 +5,24 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ - - -import { Flags, loglevel } from '@salesforce/sf-plugins-core'; +import { Flags, SfCommand, loglevel } from '@salesforce/sf-plugins-core'; import { AuthFields, AuthInfo, AuthRemover, Logger, Messages, SfError } from '@salesforce/core'; import { Interfaces } from '@oclif/core'; -import { AuthBaseCommand } from '../../../authBaseCommand.js'; -import { Common } from '../../../common.js'; +import common from '../../../common.js'; -Messages.importMessagesDirectoryFromMetaUrl(import.meta.url) +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('@salesforce/plugin-auth', 'jwt.grant'); const commonMessages = Messages.loadMessages('@salesforce/plugin-auth', 'messages'); -export default class LoginJwt extends AuthBaseCommand { +export default class LoginJwt extends SfCommand { public static readonly summary = messages.getMessage('summary'); public static readonly description = messages.getMessage('description'); public static readonly examples = messages.getMessages('examples'); - public static aliases = ['force:auth:jwt:grant', 'auth:jwt:grant']; + public static readonly aliases = ['force:auth:jwt:grant', 'auth:jwt:grant']; public static readonly flags = { username: Flags.string({ + // eslint-disable-next-line sf-plugin/dash-o char: 'o', summary: messages.getMessage('flags.username.summary'), required: true, @@ -88,7 +86,7 @@ export default class LoginJwt extends AuthBaseCommand { this.flags = flags; let result: AuthFields = {}; - if (await this.shouldExitCommand(flags['no-prompt'])) return {}; + if (await common.shouldExitCommand(flags['no-prompt'])) return {}; try { const authInfo = await this.initAuthInfo(); @@ -117,7 +115,7 @@ export default class LoginJwt extends AuthBaseCommand { privateKeyFile: this.flags['jwt-key-file'], }; - const loginUrl = await Common.resolveLoginUrl(this.flags['instance-url']?.href); + const loginUrl = await common.resolveLoginUrl(this.flags['instance-url']?.href); const oauth2Options = loginUrl ? Object.assign(oauth2OptionsBase, { loginUrl }) : oauth2OptionsBase; diff --git a/src/commands/org/login/sfdx-url.ts b/src/commands/org/login/sfdx-url.ts index 7945a9b9..83bc208f 100644 --- a/src/commands/org/login/sfdx-url.ts +++ b/src/commands/org/login/sfdx-url.ts @@ -5,16 +5,14 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ - - import fs from 'node:fs/promises'; -import { Flags, loglevel } from '@salesforce/sf-plugins-core'; +import { Flags, SfCommand, loglevel } from '@salesforce/sf-plugins-core'; import { AuthFields, AuthInfo, Messages } from '@salesforce/core'; import { AnyJson } from '@salesforce/ts-types'; import { parseJson } from '@salesforce/kit'; -import { AuthBaseCommand } from '../../../authBaseCommand.js'; +import common from '../../../common.js'; -Messages.importMessagesDirectoryFromMetaUrl(import.meta.url) +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('@salesforce/plugin-auth', 'sfdxurl.store'); const commonMessages = Messages.loadMessages('@salesforce/plugin-auth', 'messages'); @@ -24,11 +22,11 @@ type AuthJson = AnyJson & { result?: AnyJson & { sfdxAuthUrl: string }; sfdxAuthUrl: string; }; -export default class LoginSfdxUrl extends AuthBaseCommand { +export default class LoginSfdxUrl extends SfCommand { public static readonly summary = messages.getMessage('summary'); public static readonly description = messages.getMessage('description', [AUTH_URL_FORMAT]); public static readonly examples = messages.getMessages('examples'); - public static aliases = ['force:auth:sfdxurl:store', 'auth:sfdxurl:store']; + public static readonly aliases = ['force:auth:sfdxurl:store', 'auth:sfdxurl:store']; public static readonly flags = { 'sfdx-url-file': Flags.file({ @@ -69,7 +67,7 @@ export default class LoginSfdxUrl extends AuthBaseCommand { public async run(): Promise { const { flags } = await this.parse(LoginSfdxUrl); - if (await this.shouldExitCommand(flags['no-prompt'])) return {}; + if (await common.shouldExitCommand(flags['no-prompt'])) return {}; const authFile = flags['sfdx-url-file']; diff --git a/src/commands/org/login/web.ts b/src/commands/org/login/web.ts index 2e1ecae1..ce59e106 100644 --- a/src/commands/org/login/web.ts +++ b/src/commands/org/login/web.ts @@ -5,26 +5,23 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ - - import open, { apps, AppName } from 'open'; -import { Flags, loglevel } from '@salesforce/sf-plugins-core'; +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 { AuthBaseCommand } from '../../../authBaseCommand.js'; -import { Common } from '../../../common.js'; +import common from '../../../common.js'; -Messages.importMessagesDirectoryFromMetaUrl(import.meta.url) +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('@salesforce/plugin-auth', 'web.login'); const commonMessages = Messages.loadMessages('@salesforce/plugin-auth', 'messages'); -export default class LoginWeb extends AuthBaseCommand { +export default class LoginWeb extends SfCommand { public static readonly summary = messages.getMessage('summary'); public static readonly description = messages.getMessage('description'); public static readonly examples = messages.getMessages('examples'); public static readonly deprecateAliases = true; - public static aliases = ['force:auth:web:login', 'auth:web:login']; + public static readonly aliases = ['force:auth:web:login', 'auth:web:login']; public static readonly flags = { browser: Flags.string({ @@ -64,12 +61,6 @@ export default class LoginWeb extends AuthBaseCommand { deprecateAliases: true, aliases: ['setalias'], }), - 'disable-masking': Flags.boolean({ - summary: commonMessages.getMessage('flags.disable-masking.summary'), - hidden: true, - deprecateAliases: true, - aliases: ['disablemasking'], - }), 'no-prompt': Flags.boolean({ char: 'p', summary: commonMessages.getMessage('flags.no-prompt.summary'), @@ -90,17 +81,14 @@ export default class LoginWeb extends AuthBaseCommand { throw new SfError(messages.getMessage('deviceWarning'), 'DEVICE_WARNING'); } - if (await this.shouldExitCommand(flags['no-prompt'])) return {}; + if (await common.shouldExitCommand(flags['no-prompt'])) return {}; const oauthConfig: OAuth2Config = { - loginUrl: await Common.resolveLoginUrl(flags['instance-url']?.href), + loginUrl: await common.resolveLoginUrl(flags['instance-url']?.href), clientId: flags['client-id'], + ...(flags['client-id'] ? { clientSecret: await common.clientSecretPrompt() } : {}), }; - if (flags['client-id']) { - oauthConfig.clientSecret = await this.askForClientSecret(flags['disable-masking']); - } - try { const authInfo = await this.executeLoginFlow(oauthConfig); await authInfo.handleAliasAndDefaultSettings({ @@ -115,12 +103,11 @@ export default class LoginWeb extends AuthBaseCommand { this.logSuccess(successMsg); return fields; } catch (err) { - const error = err as Error; - Logger.childFromRoot('LoginWebCommand').debug(error); - if (error.name === 'AuthCodeExchangeError') { - throw new SfError(messages.getMessage('invalidClientId', [error.message]), undefined, undefined, error); + Logger.childFromRoot('LoginWebCommand').debug(err); + if (err instanceof Error && err.name === 'AuthCodeExchangeError') { + throw new SfError(messages.getMessage('invalidClientId', [err.message]), undefined, undefined, err); } - throw error; + throw err; } } diff --git a/src/commands/org/logout.ts b/src/commands/org/logout.ts index f13e92df..8d162358 100644 --- a/src/commands/org/logout.ts +++ b/src/commands/org/logout.ts @@ -5,8 +5,6 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ - - import os from 'node:os'; import { AuthInfo, @@ -18,25 +16,24 @@ import { OrgAuthorization, OrgConfigProperties, } from '@salesforce/core'; -import { Flags, loglevel, Separator } from '@salesforce/sf-plugins-core'; +import { Flags, loglevel, Separator, SfCommand } from '@salesforce/sf-plugins-core'; import { Interfaces } from '@oclif/core'; import chalk from 'chalk'; -import { AuthBaseCommand } from '../../authBaseCommand.js'; -Messages.importMessagesDirectoryFromMetaUrl(import.meta.url) +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('@salesforce/plugin-auth', 'logout'); const commonMessages = Messages.loadMessages('@salesforce/plugin-auth', 'messages'); type Choice = { name: string; value: OrgAuthorization }; export type AuthLogoutResults = string[]; -export default class Logout extends AuthBaseCommand { +export default class Logout extends SfCommand { public static readonly summary = messages.getMessage('summary'); public static readonly description = messages.getMessage('description'); public static readonly examples = messages.getMessages('examples'); public static readonly deprecateAliases = true; - public static aliases = ['force:auth:logout', 'auth:logout']; + public static readonly aliases = ['force:auth:logout', 'auth:logout']; public static readonly flags = { 'target-org': Flags.string({ diff --git a/src/common.ts b/src/common.ts index 265e41dd..2ecb8b45 100644 --- a/src/common.ts +++ b/src/common.ts @@ -5,36 +5,36 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ - -import { Logger, SfdcUrl, SfProject, Messages, SfError } from '@salesforce/core'; +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) +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('@salesforce/plugin-auth', 'messages'); -export class Common { - public static async resolveLoginUrl(instanceUrl?: string): Promise { - const logger = await Logger.child('Common', { tag: 'resolveLoginUrl' }); - if (instanceUrl) { - throwIfLightning(instanceUrl); - return instanceUrl; - } - let loginUrl: string; - try { - const project = await SfProject.resolve(); - const projectJson = await project.resolveProjectConfig(); - loginUrl = getString(projectJson, 'sfdcLoginUrl', SfdcUrl.PRODUCTION); - } catch (err) { - const message: string = (isObject(err) ? Reflect.get(err, 'message') ?? err : err) as string; - logger.debug(`error occurred while trying to determine loginUrl: ${message}`); - loginUrl = SfdcUrl.PRODUCTION; - } - throwIfLightning(loginUrl); - - logger.debug(`loginUrl: ${loginUrl}`); - return loginUrl; +const resolveLoginUrl = async (instanceUrl?: string): Promise => { + const logger = await Logger.child('Common', { tag: 'resolveLoginUrl' }); + if (instanceUrl) { + throwIfLightning(instanceUrl); + return instanceUrl; + } + let loginUrl: string; + try { + const project = await SfProject.resolve(); + const projectJson = await project.resolveProjectConfig(); + loginUrl = getString(projectJson, 'sfdcLoginUrl', SfdcUrl.PRODUCTION); + } catch (err) { + const message: string = (isObject(err) ? Reflect.get(err, 'message') ?? err : err) as string; + logger.debug(`error occurred while trying to determine loginUrl: ${message}`); + loginUrl = SfdcUrl.PRODUCTION; } -} + throwIfLightning(loginUrl); + + logger.debug(`loginUrl: ${loginUrl}`); + return loginUrl; +}; const throwIfLightning = (urlString?: string): void => { if (urlString?.match(/\.lightning\..*force\.com/)) { @@ -43,3 +43,20 @@ 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 91923437..e318ec56 100644 --- a/test/commands/org/login/access-token.test.ts +++ b/test/commands/org/login/access-token.test.ts @@ -8,17 +8,17 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ import { AuthFields, AuthInfo, SfError, StateAggregator } from '@salesforce/core'; -import { stubMethod } from '@salesforce/ts-sinon'; import { assert, expect } from 'chai'; import { TestContext } from '@salesforce/core/lib/testSetup.js'; import { Env } from '@salesforce/kit'; import { Config } from '@oclif/core'; 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: AuthFields; + let authFields: Required>; const accessToken = '00Dxx0000000000!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; /* eslint-disable camelcase */ @@ -43,30 +43,32 @@ describe('org:login:access-token', () => { username: 'foo@baz.org', }; - stubMethod($$.SANDBOX, StateAggregator, 'getInstance').resolves({ + $$.SANDBOX.stub(StateAggregator, 'getInstance').resolves({ + // @ts-expect-error because incomplete interface orgs: { exists: () => Promise.resolve(authFileExists), }, }); - stubMethod($$.SANDBOX, Store.prototype, 'saveAuthInfo').resolves(userInfo); - stubMethod($$.SANDBOX, AuthInfo.prototype, 'getUsername').returns(authFields.username); - stubMethod($$.SANDBOX, AuthInfo.prototype, 'getFields').returns({ + // @ts-expect-error because private method + $$.SANDBOX.stub(Store.prototype, 'saveAuthInfo').resolves(userInfo); + $$.SANDBOX.stub(AuthInfo.prototype, 'getUsername').returns(authFields.username); + $$.SANDBOX.stub(AuthInfo.prototype, 'getFields').returns({ accessToken, orgId: authFields.orgId, instanceUrl: authFields.instanceUrl, loginUrl: authFields.loginUrl, username: authFields.username, }); - stubMethod($$.SANDBOX, Store.prototype, 'getUserInfo').resolves(AuthInfo.prototype); + // @ts-expect-error because private method + $$.SANDBOX.stub(Store.prototype, 'getUserInfo').resolves(AuthInfo.prototype); if (useSfdxAccessTokenEnvVar) { - stubMethod($$.SANDBOX, Env.prototype, 'getString').callsFake(() => accessToken); + $$.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 ); - // @ts-ignore - $$.SANDBOX.stub(Store.prototype, 'askForAccessToken').resolves(promptAnswer); + $$.SANDBOX.stub(common, 'accessTokenPrompt').resolves(promptAnswer); return Promise.resolve(store); } diff --git a/test/commands/org/login/login.device.test.ts b/test/commands/org/login/login.device.test.ts index 562b959f..0aa59710 100644 --- a/test/commands/org/login/login.device.test.ts +++ b/test/commands/org/login/login.device.test.ts @@ -7,7 +7,7 @@ /* eslint-disable camelcase */ -import { AuthFields, AuthInfo, DeviceOauthService, Global, Mode } from '@salesforce/core'; +import { AuthFields, AuthInfo, DeviceOauthService } from '@salesforce/core'; import { MockTestOrgData, TestContext } from '@salesforce/core/lib/testSetup.js'; import { StubbedType, stubInterface, stubMethod } from '@salesforce/ts-sinon'; import { DeviceCodeResponse } from '@salesforce/core/lib/deviceOauthService.js'; @@ -15,7 +15,7 @@ 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; @@ -138,27 +138,9 @@ describe('org:login:device', () => { await prepareStubs(); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - $$.SANDBOX.stub(Login.prototype, 'askForHiddenResponse').returns(Promise.resolve('1234')); + $$.SANDBOX.stub(common, 'clientSecretPrompt').resolves('1234'); const login = new Login(['-i', 'CoffeeBeans', '--json'], {} as Config); const response = await login.run(); expect(response.username).to.equal(testData.username); }); - - it('should prompt for when in demo mode (SFDX_ENV=demo)', async () => { - await prepareStubs(); - $$.SANDBOX.stub(Global, 'getEnvironmentMode').returns(Mode.DEMO); - $$.SANDBOX.stub(SfCommand.prototype, 'confirm').resolves(true); - const login = new Login(['--json'], {} as Config); - const response = await login.run(); - expect(response.username).to.equal(testData.username); - }); - - it('should exit early when prompt is answered NO', async () => { - await prepareStubs(); - $$.SANDBOX.stub(Global, 'getEnvironmentMode').returns(Mode.DEMO); - $$.SANDBOX.stub(SfCommand.prototype, 'confirm').resolves(false); - const login = new Login(['--json'], {} as Config); - const response = await login.run(); - expect(response).to.deep.equal({}); - }); }); diff --git a/test/commands/org/login/login.jwt.test.ts b/test/commands/org/login/login.jwt.test.ts index 69e39e60..b679e2e4 100644 --- a/test/commands/org/login/login.jwt.test.ts +++ b/test/commands/org/login/login.jwt.test.ts @@ -5,12 +5,11 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -import { AuthFields, AuthInfo, Global, Mode, SfError } from '@salesforce/core'; +import { AuthFields, AuthInfo, SfError } from '@salesforce/core'; import { MockTestOrgData, TestContext } from '@salesforce/core/lib/testSetup.js'; import { StubbedType, stubInterface } from '@salesforce/ts-sinon'; import { expect } from 'chai'; import { Config } from '@oclif/core'; -import { SfCommand } from '@salesforce/sf-plugins-core'; import LoginJwt from '../../../../src/commands/org/login/jwt.js'; interface Options { @@ -201,39 +200,4 @@ describe('org:login:jwt', () => { expect.fail('Should not have thrown an error'); } }); - - it('should auth when in demo mode (SFDX_ENV=demo) and prompt is answered with yes', async () => { - await prepareStubs(); - $$.SANDBOX.stub(Global, 'getEnvironmentMode').returns(Mode.DEMO); - $$.SANDBOX.stub(SfCommand.prototype, 'confirm').resolves(true); - const grant = new LoginJwt( - ['-u', testData.username, '-f', 'path/to/key.json', '-i', '123456', '--json'], - {} as Config - ); - await grant.run(); - expect(authInfoStub.save.called); - }); - - it('should do nothing when in demo mode (SFDX_ENV=demo) and prompt is answered with no', async () => { - await prepareStubs(); - $$.SANDBOX.stub(Global, 'getEnvironmentMode').returns(Mode.DEMO); - $$.SANDBOX.stub(SfCommand.prototype, 'confirm').resolves(false); - const grant = new LoginJwt( - ['-u', testData.username, '-f', 'path/to/key.json', '-i', '123456', '--json'], - {} as Config - ); - await grant.run(); - expect(authInfoStub.save.callCount).to.equal(0); - }); - - it('should ignore prompt when in demo mode (SFDX_ENV=demo) and -p is provided', async () => { - await prepareStubs(); - $$.SANDBOX.stub(Global, 'getEnvironmentMode').returns(Mode.DEMO); - const grant = new LoginJwt( - ['-p', '-u', testData.username, '-f', 'path/to/key.json', '-i', '123456', '--json'], - {} as Config - ); - await grant.run(); - expect(authInfoStub.save.called); - }); }); diff --git a/test/commands/org/login/login.sfdx-url.test.ts b/test/commands/org/login/login.sfdx-url.test.ts index bcb94b9a..6de99378 100644 --- a/test/commands/org/login/login.sfdx-url.test.ts +++ b/test/commands/org/login/login.sfdx-url.test.ts @@ -6,12 +6,11 @@ */ import fs from 'node:fs/promises'; -import { AuthFields, AuthInfo, Global, Mode } from '@salesforce/core'; +import { AuthFields, AuthInfo } from '@salesforce/core'; import { MockTestOrgData, TestContext } from '@salesforce/core/lib/testSetup.js'; import { expect } from 'chai'; import { Config } from '@oclif/core'; import { StubbedType, stubInterface } from '@salesforce/ts-sinon'; -import { SfCommand } from '@salesforce/sf-plugins-core'; import LoginSfdxUrl from '../../../../src/commands/org/login/sfdx-url.js'; interface Options { @@ -186,31 +185,4 @@ describe('org:login:sfdx-url', () => { }, ]); }); - - it('should auth when in demo mode (SFDX_ENV=demo) and prompt is answered with yes', async () => { - await prepareStubs(); - $$.SANDBOX.stub(Global, 'getEnvironmentMode').returns(Mode.DEMO); - $$.SANDBOX.stub(SfCommand.prototype, 'confirm').resolves(true); - const store = new LoginSfdxUrl(['-f', keyPathTxt, '--json'], {} as Config); - await store.run(); - expect(authInfoStub.save.called); - }); - - it('should do nothing when in demo mode (SFDX_ENV=demo) and prompt is answered with no', async () => { - await prepareStubs(); - $$.SANDBOX.stub(Global, 'getEnvironmentMode').returns(Mode.DEMO); - $$.SANDBOX.stub(SfCommand.prototype, 'confirm').resolves(false); - const store = new LoginSfdxUrl(['-f', keyPathTxt, '--json'], {} as Config); - await store.run(); - expect(authInfoStub.save.callCount).to.equal(0); - }); - - it('should ignore prompt when in demo mode (SFDX_ENV=demo) and -p is provided', async () => { - await prepareStubs(); - $$.SANDBOX.stub(Global, 'getEnvironmentMode').returns(Mode.DEMO); - $$.SANDBOX.stub(SfCommand.prototype, 'confirm').resolves(false); - const store = new LoginSfdxUrl(['-p', '-f', keyPathTxt, '--json'], {} as Config); - await store.run(); - expect(authInfoStub.save.called); - }); }); diff --git a/test/commands/org/login/login.web.test.ts b/test/commands/org/login/login.web.test.ts index c599eff7..c7e035ad 100644 --- a/test/commands/org/login/login.web.test.ts +++ b/test/commands/org/login/login.web.test.ts @@ -8,13 +8,14 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ import { Config } from '@oclif/core'; -import { AuthFields, AuthInfo, Global, Mode, SfError } from '@salesforce/core'; +import { AuthFields, AuthInfo, SfError } from '@salesforce/core'; import { MockTestOrgData, TestContext } from '@salesforce/core/lib/testSetup.js'; import { StubbedType, stubInterface, stubMethod } from '@salesforce/ts-sinon'; 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(); @@ -30,8 +31,7 @@ describe('org:login:web', () => { clientSecret = '' ): Promise { authFields = await testData.getConfig(); - // @ts-ignore - $$.SANDBOX.stub(LoginWeb.prototype, 'askForClientSecret').resolves(clientSecret); + $$.SANDBOX.stub(common, 'clientSecretPrompt').resolves(clientSecret); $$.SANDBOX.stub(SfCommand.prototype, 'confirm').resolves(promptAnswer); authInfoStub = stubInterface($$.SANDBOX, { @@ -116,20 +116,6 @@ describe('org:login:web', () => { await login.run(); }); - it('should exit command if in demo and prompt is answered NO', async () => { - $$.SANDBOX.stub(Global, 'getEnvironmentMode').returns(Mode.DEMO); - const login = await createNewLoginCommand([], false, undefined); - const result = await login.run(); - expect(result).to.deep.equal({}); - }); - - it('should execute command if in demo and prompt is answered YES', async () => { - $$.SANDBOX.stub(Global, 'getEnvironmentMode').returns(Mode.DEMO); - const login = await createNewLoginCommand([], true, undefined); - const result = await login.run(); - expect(result).to.deep.equal(authFields); - }); - it('should show invalidClientId error if AuthCodeExchangeError', async () => { const login = await createNewLoginCommandWithError('AuthCodeExchangeError'); try { diff --git a/test/common.test.ts b/test/common.test.ts index 386cf207..b93a0ceb 100644 --- a/test/common.test.ts +++ b/test/common.test.ts @@ -5,10 +5,10 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -import { ConfigContents, SfdcUrl } from '@salesforce/core'; +import { ConfigContents, SfdcUrl, Global, Mode } from '@salesforce/core'; import { assert, expect } from 'chai'; import { TestContext, uniqid } from '@salesforce/core/lib/testSetup.js'; -import { Common } from '../src/common.js'; +import common from '../src/common.js'; const projectSetup = async ($$: TestContext, inProject = true, contents?: ConfigContents): Promise => { $$.inProject(inProject); @@ -33,7 +33,7 @@ describe('common unit tests', () => { describe('production url', () => { it('should return production URL if not in a dx project', async () => { await projectSetup($$, false); - const loginUrl = await Common.resolveLoginUrl(undefined); + const loginUrl = await common.resolveLoginUrl(undefined); expect(loginUrl).to.equal(SfdcUrl.PRODUCTION); }); it('should return production URL if project with property sfdcLoginUrl absent', async () => { @@ -46,7 +46,7 @@ describe('common unit tests', () => { ], sourceApiVersion: '50.0', }); - const loginUrl = await Common.resolveLoginUrl(undefined); + const loginUrl = await common.resolveLoginUrl(undefined); expect(loginUrl).to.equal(SfdcUrl.PRODUCTION); }); it('should return production URL if project with property sfdcLoginUrl present', async () => { @@ -60,7 +60,7 @@ describe('common unit tests', () => { sfdcLoginUrl: 'https://login.salesforce.com', sourceApiVersion: '50.0', }); - const loginUrl = await Common.resolveLoginUrl(undefined); + const loginUrl = await common.resolveLoginUrl(undefined); expect(loginUrl).to.equal(SfdcUrl.PRODUCTION); }); it('should throw on lightning login URL in sfdcLoginUrl property', async () => { @@ -75,7 +75,7 @@ describe('common unit tests', () => { sourceApiVersion: '50.0', }); try { - await Common.resolveLoginUrl(undefined); + await common.resolveLoginUrl(undefined); expect.fail('This test is failing because it is expecting an error that is never thrown'); } catch (error) { assert(error instanceof Error); @@ -85,7 +85,7 @@ describe('common unit tests', () => { it('should throw on lightning login URL passed in to resolveLoginUrl()', async () => { await projectSetup($$, true); try { - await Common.resolveLoginUrl('https://shanedevhub.lightning.force.com'); + await common.resolveLoginUrl('https://shanedevhub.lightning.force.com'); expect.fail('This test is failing because it is expecting an error that is never thrown'); } catch (error) { assert(error instanceof Error); @@ -95,14 +95,14 @@ describe('common unit tests', () => { it('should allow a domain containing lightning in its login URL', async () => { await projectSetup($$, true); - const loginUrl = await Common.resolveLoginUrl('https://mycompany-lightning.my.salesforce.com'); + const loginUrl = await common.resolveLoginUrl('https://mycompany-lightning.my.salesforce.com'); expect(loginUrl).equals('https://mycompany-lightning.my.salesforce.com'); }); it('should throw on internal lightning login URL passed in to resolveLoginUrl()', async () => { await projectSetup($$, true); try { - await Common.resolveLoginUrl('https://dro000000osjp2a0.test1.lightning.pc-rnd.force.com/'); + await common.resolveLoginUrl('https://dro000000osjp2a0.test1.lightning.pc-rnd.force.com/'); expect.fail('This test is failing because it is expecting an error that is never thrown'); } catch (error) { assert(error instanceof Error); @@ -116,7 +116,7 @@ describe('common unit tests', () => { it('should return custom login URL if not in a dx project and instance-url given', async () => { await projectSetup($$, false); - const loginUrl = await Common.resolveLoginUrl(INSTANCE_URL_1); + const loginUrl = await common.resolveLoginUrl(INSTANCE_URL_1); expect(loginUrl).to.equal(INSTANCE_URL_1); }); it('should return custom login URL if project with property sfdcLoginUrl absent and instance-url given', async () => { @@ -131,7 +131,7 @@ describe('common unit tests', () => { sourceApiVersion: '50.0', }, }); - const loginUrl = await Common.resolveLoginUrl(INSTANCE_URL_1); + const loginUrl = await common.resolveLoginUrl(INSTANCE_URL_1); expect(loginUrl).to.equal(INSTANCE_URL_1); }); it('should return custom login URL if project with property sfdcLoginUrl present and not equal to production URL', async () => { @@ -145,10 +145,10 @@ describe('common unit tests', () => { sfdcLoginUrl: INSTANCE_URL_2, sourceApiVersion: '50.0', }); - const loginUrl = await Common.resolveLoginUrl(undefined); + const loginUrl = await common.resolveLoginUrl(undefined); expect(loginUrl).to.equal(INSTANCE_URL_2); }); - it('should return custom login URL 1 if project with property sfdcLoginUrl equal to ciustom url 2', async () => { + it('should return custom login URL 1 if project with property sfdcLoginUrl equal to custom url 2', async () => { await projectSetup($$, true, { contents: { packageDirectories: [ @@ -161,8 +161,20 @@ describe('common unit tests', () => { sourceApiVersion: '50.0', }, }); - const loginUrl = await Common.resolveLoginUrl(INSTANCE_URL_1); + const loginUrl = await common.resolveLoginUrl(INSTANCE_URL_1); expect(loginUrl).to.equal(INSTANCE_URL_1); }); }); + + describe('shouldExitCommand', () => { + it('should not exit if noPrompt is true', async () => { + const shouldExit = await common.shouldExitCommand(true); + expect(shouldExit).to.be.false; + }); + it('should not exit if not DEMO mode', async () => { + $$.SANDBOX.stub(Global, 'getEnvironmentMode').returns(Mode.PRODUCTION); + const shouldExit = await common.shouldExitCommand(); + expect(shouldExit).to.be.false; + }); + }); }); diff --git a/yarn.lock b/yarn.lock index ecb9e39c..13563c31 100644 --- a/yarn.lock +++ b/yarn.lock @@ -445,6 +445,59 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz#e5211452df060fa8522b55c7b3c0c4d1981cb044" integrity sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw== +"@inquirer/confirm@^2.0.15": + version "2.0.15" + resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-2.0.15.tgz#b5512ed190efd8c5b96e0969115756b48546ab36" + integrity sha512-hj8Q/z7sQXsF0DSpLQZVDhWYGN6KLM/gNjjqGkpKwBzljbQofGjn0ueHADy4HUY+OqDHmXuwk/bY+tZyIuuB0w== + dependencies: + "@inquirer/core" "^5.1.1" + "@inquirer/type" "^1.1.5" + chalk "^4.1.2" + +"@inquirer/core@^5.1.1": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-5.1.1.tgz#849d4846aea68371c133df6ec9059f5e5bd30d30" + integrity sha512-IuJyZQUg75+L5AmopgnzxYrgcU6PJKL0hoIs332G1Gv55CnmZrhG6BzNOeZ5sOsTi1YCGOopw4rYICv74ejMFg== + dependencies: + "@inquirer/type" "^1.1.5" + "@types/mute-stream" "^0.0.4" + "@types/node" "^20.9.0" + "@types/wrap-ansi" "^3.0.0" + ansi-escapes "^4.3.2" + chalk "^4.1.2" + cli-spinners "^2.9.1" + cli-width "^4.1.0" + figures "^3.2.0" + mute-stream "^1.0.0" + run-async "^3.0.0" + signal-exit "^4.1.0" + strip-ansi "^6.0.1" + wrap-ansi "^6.2.0" + +"@inquirer/input@^1.2.14": + version "1.2.14" + resolved "https://registry.yarnpkg.com/@inquirer/input/-/input-1.2.14.tgz#8951867618bb5cd16dd096e02404eec225a92207" + integrity sha512-tISLGpUKXixIQue7jypNEShrdzJoLvEvZOJ4QRsw5XTfrIYfoWFqAjMQLerGs9CzR86yAI89JR6snHmKwnNddw== + dependencies: + "@inquirer/core" "^5.1.1" + "@inquirer/type" "^1.1.5" + chalk "^4.1.2" + +"@inquirer/password@^1.1.14": + version "1.1.14" + resolved "https://registry.yarnpkg.com/@inquirer/password/-/password-1.1.14.tgz#c1fc139efe84a38986870a1bcf80718050f82bbf" + integrity sha512-vL2BFxfMo8EvuGuZYlryiyAB3XsgtbxOcFs4H9WI9szAS/VZCAwdVqs8rqEeaAf/GV/eZOghIOYxvD91IsRWSg== + dependencies: + "@inquirer/input" "^1.2.14" + "@inquirer/type" "^1.1.5" + ansi-escapes "^4.3.2" + chalk "^4.1.2" + +"@inquirer/type@^1.1.5": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-1.1.5.tgz#b8c171f755859c8159b10e41e1e3a88f0ca99d7f" + integrity sha512-wmwHvHozpPo4IZkkNtbYenem/0wnfI6hvOcGKmPEa0DwuaH5XUQzFqy6OpEpjEegZMhYIk8HDYITI16BPLtrRA== + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" @@ -1247,6 +1300,13 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.6.tgz#818551d39113081048bdddbef96701b4e8bb9d1b" integrity sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg== +"@types/mute-stream@^0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@types/mute-stream/-/mute-stream-0.0.4.tgz#77208e56a08767af6c5e1237be8888e2f255c478" + integrity sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow== + 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" @@ -1264,6 +1324,13 @@ 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== + dependencies: + undici-types "~5.26.4" + "@types/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" @@ -1321,6 +1388,11 @@ "@types/expect" "^1.20.4" "@types/node" "*" +"@types/wrap-ansi@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz#18b97a972f94f60a679fd5c796d96421b9abb9fd" + integrity sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g== + "@typescript-eslint/eslint-plugin@^6.10.0": version "6.11.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.11.0.tgz#52aae65174ff526576351f9ccd41cea01001463f" @@ -2242,6 +2314,11 @@ cli-spinners@^2.5.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: + version "2.9.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== + cli-table@^0.3.1: version "0.3.11" resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.11.tgz#ac69cdecbe81dccdba4889b9a18b7da312a9d3ee" @@ -2254,6 +2331,11 @@ cli-width@^3.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== +cli-width@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.1.0.tgz#42daac41d3c254ef38ad8ac037672130173691c5" + integrity sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ== + cliui@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" @@ -3357,7 +3439,7 @@ faye@^1.4.0: tough-cookie "*" tunnel-agent "*" -figures@^3.0.0: +figures@^3.0.0, figures@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== @@ -5406,6 +5488,11 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +mute-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" + integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== + nanoid@3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" @@ -6577,6 +6664,11 @@ run-async@^2.0.0, run-async@^2.4.0: resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== +run-async@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-3.0.0.tgz#42a432f6d76c689522058984384df28be379daad" + integrity sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q== + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -6806,7 +6898,7 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -signal-exit@^4.0.1: +signal-exit@^4.0.1, signal-exit@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== From d02c83f59e5d45587a98e2d18611e13533c5d858 Mon Sep 17 00:00:00 2001 From: mshanemc Date: Thu, 4 Jan 2024 12:10:37 -0600 Subject: [PATCH 2/4] 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" From 42cf6d5b7cfba58ed69ea07e28c8350ffc8da67d Mon Sep 17 00:00:00 2001 From: mshanemc Date: Fri, 5 Jan 2024 16:12:34 -0600 Subject: [PATCH 3/4] refactor: real plugins-core, use built-in confirm prompt --- package.json | 3 +-- src/common.ts | 4 ++-- yarn.lock | 26 +++++++++++++------------- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 99b8ced8..96ee48eb 100644 --- a/package.json +++ b/package.json @@ -6,13 +6,12 @@ "bugs": "https://github.com/forcedotcom/cli/issues", "dependencies": { "@inquirer/checkbox": "^1.5.0", - "@inquirer/confirm": "^2.0.15", "@inquirer/password": "^1.1.14", "@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.14-dev.0", + "@salesforce/sf-plugins-core": "^7.0.0", "@salesforce/ts-types": "^2.0.9", "chalk": "^5.3.0", "open": "^9.1.0" diff --git a/src/common.ts b/src/common.ts index 0b32a517..89b77619 100644 --- a/src/common.ts +++ b/src/common.ts @@ -8,7 +8,7 @@ import { Logger, SfdcUrl, SfProject, Messages, SfError, Global, Mode } from '@salesforce/core'; import { getString, isObject } from '@salesforce/ts-types'; import chalk from 'chalk'; -import confirm from '@inquirer/confirm'; +import { prompts } from '@salesforce/sf-plugins-core'; Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('@salesforce/plugin-auth', 'messages'); @@ -46,7 +46,7 @@ const throwIfLightning = (urlString?: string): void => { const shouldExitCommand = async (noPrompt?: boolean): Promise => Boolean(noPrompt) || Global.getEnvironmentMode() !== Mode.DEMO ? false - : !(await confirm({ message: chalk.dim(messages.getMessage('warnAuth', ['sf'])) })); + : !(await prompts.confirm({ message: chalk.dim(messages.getMessage('warnAuth', ['sf'])), ms: 60_000 })); export default { shouldExitCommand, diff --git a/yarn.lock b/yarn.lock index 64dae6be..9ce5b477 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1102,19 +1102,6 @@ resolved "https://registry.yarnpkg.com/@salesforce/schemas/-/schemas-1.6.1.tgz#7d1c071e1e509ca9d2d8a6e48ac7447dd67a534d" integrity sha512-eVy947ZMxCJReKJdgfddUIsBIbPTa/i8RwQGwxq4/ss38H5sLOAeSTaun9V7HpJ1hkpDznWKfgzYvjsst9K6ig== -"@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" @@ -1128,6 +1115,19 @@ chalk "^4" inquirer "^8.2.5" +"@salesforce/sf-plugins-core@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@salesforce/sf-plugins-core/-/sf-plugins-core-7.0.0.tgz#56cb4eaafcd04a183938d86c5e93323e037b15ab" + integrity sha512-vl53Ee0/eg9wgvtWro6kX82/943s28Hph/o3lTQk6URorfqMC+zH0RGKJj1X0VKeLhDSOCRYXqIu54jE8AZ/3g== + 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/ts-sinon@^1.4.19": version "1.4.19" resolved "https://registry.yarnpkg.com/@salesforce/ts-sinon/-/ts-sinon-1.4.19.tgz#64157b6c8cf4a3c637867e2ddd90c2d058c334f7" From 41dbef0bbdeb123fbf223966cfa14389fae39a1d Mon Sep 17 00:00:00 2001 From: mshanemc Date: Tue, 9 Jan 2024 18:04:40 -0600 Subject: [PATCH 4/4] chore: bump deps --- package.json | 4 ++-- tsconfig.json | 3 ++- yarn.lock | 14 ++++---------- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index ac04d141..8a18b5c0 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,8 @@ "dependencies": { "@inquirer/checkbox": "^1.5.0", "@inquirer/select": "^1.3.1", - "@oclif/core": "^3.16.0", - "@salesforce/core": "^6.4.4", + "@oclif/core": "^3.18.0", + "@salesforce/core": "^6.4.6", "@salesforce/kit": "^3.0.15", "@salesforce/sf-plugins-core": "^7.1.1", "@salesforce/ts-types": "^2.0.9", diff --git a/tsconfig.json b/tsconfig.json index d024f738..8f514318 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "outDir": "lib", "rootDir": "src", - "strictNullChecks": true + "strictNullChecks": true, + "skipLibCheck": true }, "include": ["./src/**/*.ts"] } diff --git a/yarn.lock b/yarn.lock index e48b8770..89d70e9b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -768,10 +768,10 @@ node-gyp "^8.2.0" read-package-json-fast "^2.0.1" -"@oclif/core@^3.15.0", "@oclif/core@^3.15.1", "@oclif/core@^3.16.0": - 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== +"@oclif/core@^3.15.0", "@oclif/core@^3.15.1", "@oclif/core@^3.16.0", "@oclif/core@^3.18.0": + version "3.18.0" + resolved "https://registry.yarnpkg.com/@oclif/core/-/core-3.18.0.tgz#3f594d2509d9b4e8981f0b6b1a318826979dd2f2" + integrity sha512-xPuN3kT87WXHvkyZIrPyp87VLIEkreQqVpCIcLtIGjZ0a7/0c7c4j6NjGApHp5rz/5lprW/i1FPt3IvrrNlMeA== dependencies: ansi-escapes "^4.3.2" ansi-styles "^4.3.0" @@ -796,7 +796,6 @@ strip-ansi "^6.0.1" supports-color "^8.1.1" supports-hyperlinks "^2.2.0" - tsconfck "^3.0.0" widest-line "^3.1.0" wordwrap "^1.0.0" wrap-ansi "^7.0.0" @@ -7328,11 +7327,6 @@ ts-retry-promise@^0.7.1: resolved "https://registry.yarnpkg.com/ts-retry-promise/-/ts-retry-promise-0.7.1.tgz#176d6eee6415f07b6c7c286d3657355e284a6906" integrity sha512-NhHOCZ2AQORvH42hOPO5UZxShlcuiRtm7P2jIq2L2RY3PBxw2mLnUsEdHrIslVBFya1v5aZmrR55lWkzo13LrQ== -tsconfck@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/tsconfck/-/tsconfck-3.0.0.tgz#b469f1ced12973bbec3209a55ed8de3bb04223c9" - integrity sha512-w3wnsIrJNi7avf4Zb0VjOoodoO0woEqGgZGQm+LHH9przdUI+XDKsWAXwxHA1DaRTjeuZNcregSzr7RaA8zG9A== - tsconfig-paths@^3.14.2: version "3.14.2" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088"