diff --git a/.eslintignore b/.eslintignore index 3c95b8e09..ee638464f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1 @@ -node_modules -lib -ref \ No newline at end of file +*.cjs/ diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..0ef035dfe --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,22 @@ +version: 2 +updates: + - package-ecosystem: 'npm' + directory: '/' + schedule: + interval: 'weekly' + day: 'saturday' + versioning-strategy: 'increase' + labels: + - 'dependencies' + open-pull-requests-limit: 5 + pull-request-branch-name: + separator: '-' + commit-message: + # cause a release for non-dev-deps + prefix: fix(deps) + # no release for dev-deps + prefix-development: chore(dev-deps) + ignore: + - dependency-name: '@salesforce/dev-scripts' + - dependency-name: '*' + update-types: ['version-update:semver-major'] diff --git a/.github/linters/.cspell.json b/.github/linters/.cspell.json index d92f37bc5..c9b33bd68 100644 --- a/.github/linters/.cspell.json +++ b/.github/linters/.cspell.json @@ -1,830 +1,875 @@ { - "ignorePaths": [ - "**/node_modules/**", - "**/vscode-extension/**", - "**/.git/**", - "**/.gitignore", - "**/grafana/**", - ".vscode", - "package-lock.json", - "report", - ".cspell.json" - ], - "language": "en", - "version": "0.1", - "words": [ - "AAAS", - "AAAU", - "ACCESSTOKEN", - "ACCESSTOKEN) -- (process.env.SYSTEM_ACCESSTOKEN", - "AFFERO", - "AGPL", - "ALPHAID", - "APICIL", - "AULL", - "Accueil", - "Administrateur", - "Affero", - "Alainbates", - "Astran", - "BETAID", - "BRANCHNAME", - "BUILDID", - "Backuped", - "Badwords", - "Batchable", - "Buildx", - "Bulkify", - "CANARYID", - "CARRENET", - "CCAU", - "COLLECTIONURI", - "COMPTES", - "Callout", - "Chantier", - "Cloudi", - "Cloudity", - "Codecov", - "Commerciaux", - "Communit", - "Correspondances", - "Cyclomatic", - "DDHG", - "DYSA", - "Dandb", - "Defaut", - "Demarrage", - "Devhub", - "Dont", - "Dreamforce", - "Dreamin", - "EAAU", - "ECAU", - "ECONNABORTED", - "ECONNRESET", - "EDITORCONFIG", - "Eqoehef", - "Externe", - "FGGF", - "FHDHGDH", - "FILEIO", - "FSEDS", - "Facturation", - "Finet", - "Firstname", - "Flexi", - "Fswg", - "GHSA", - "GHTRGDHD", - "GITLEAKS", - "GRYPE", - "Gagne", - "Gmail", - "HADOLINT", - "Hardis", - "Hdhghg", - "Hentschke", - "Hfgfgjfh", - "IGHT", - "INTEG", - "IVMA", - "Iiwib", - "Ijoi", - "JSONLINT", - "Jokinen", - "KERNN", - "KVDB", - "Keyfile", - "Keyv", - "Kvdb", - "LISTVIEWNAME", - "LVMYAA", - "Licences", - "MARKDOWNLINT", - "MASTERLABEL", - "MYFOLDER", - "MYREPORTNAME", - "MYTASK", - "Metadatas", - "NOPMD", - "Ncss", - "Ndays", - "Novaxel", - "OCIs", - "ODNs", - "OOOOOPS", - "ORGALIAS", - "Ohana", - "Omnichannel", - "Orgs", - "Ozil", - "Ozil's", - "PQAF", - "PROFILENAME", - "PROSELINT", - "PUBLICENDPOINT", - "PULLREQUEST", - "PULLREQUESTID", - "Picklist", - "Picklists", - "Planification", - "Portail", - "Post", - "Psla", - "Pullrequests", - "QESRDTHFKGKH", - "Quickfix", - "REPOSITORYNAME", - "RWAAA", - "Recalc", - "Recrutement", - "Reseting", - "Rypple", - "SAST", - "SEMGREP", - "SOMENAMESPACE", - "SOURCEBRANCHNAME", - "STYLELINT", - "Scontrol", - "Scratches", - "Sfdc", - "Sfdx", - "Solva", - "Stmts", - "Sublicensing", - "Suspendre", - "Syst\u00e8me", - "TCRM", - "TEAMPROJECT", - "Trailmix", - "Transco", - "Transcos", - "Tuto", - "Typederendezvous", - "Ujut", - "Unallowed", - "Unstash", - "Upsert", - "VCAS", - "VERSIONNUMBER", - "Viewfile", - "Vuillamy", - "WIPO", - "XSLX", - "Xmls", - "YOURDEVHUBUSERNAME", - "YOURORGNAME", - "YOURSOURCEORG", - "YOURSOURCEORGUSERNAME", - "ZWSAU", - "accordionallowed", - "accordionapex", - "accordionauto", - "accordionavailable", - "accordionbranch", - "accordionclean", - "accordioncommands", - "accordioncustom", - "accordiondata", - "accordiondefault", - "accordiondeployment", - "accordiondev", - "accordiondevelopment", - "accordionextends", - "accordioninit", - "accordioninstall", - "accordioninstalled", - "accordioninstance", - "accordionlinter", - "accordionlist", - "accordionmonitoring", - "accordionms", - "accordionnotifications", - "accordionpool", - "accordionproduction", - "accordionproject", - "accordionretrofit", - "accordionruntests", - "accordionscratch", - "accordionsfdmu", - "accordionskip", - "accordionsources", - "accordiontarget", - "accordiontest", - "accordionuse", - "activateduser", - "activateinvalid", - "addeduserpackagelicense", - "administratif", - "allowfullscreen", - "allowpurgefailure", - "antislashes", - "apexlog", - "apexp", - "apextest", - "apiversion", - "apos", - "aquasecurity", - "assigneduserstomobileconfig", - "astran", - "astrea", - "audittrail", - "authprovider", - "authproviders", - "autocleantypes", - "autoplay", - "avec", - "azdev", - "badwords", - "bestpractices", - "blockquotes", - "boza", - "buildargs", - "buildx", - "bulletpoints", - "cacache", - "calcul", - "callincallout", - "callout", - "callouts", - "canmodify", - "caseentitlement", - "certaines", - "changedcommunitynickname", - "changedemail", - "changedfederationid", - "changedinteractionuseroffon", - "changedinteractionuseronoff", - "changedmanager", - "changedmarketinguseroffon", - "changedmarketinguseronoff", - "changedpassword", - "changedprofileforuser", - "changedprofileforusercusttostd", - "changedprofileforuserstdtocust", - "changedroleforuser", - "changedroleforuserfromnone", - "changedroleforusertonone", - "changedsenderemail", - "changemgmt", - "checkcoverage", - "checkcoverage) -- endArgs.indexOf(\"--checkcoverage\"", - "checkonly", - "choco", - "chunksize", - "cicd", - "classname", - "clientid", - "cloudiscore", - "codecov", - "codecoverage", - "codestyle", - "columnify", - "commandsstop", - "commitmode", - "commitsto", - "concat", - "confirmfreeze", - "confirmunfreeze", - "conta", - "contentassets", - "correspondance", - "cosmiconfig", - "cours", - "coverageformatters", - "createduser", - "crta", - "csvfile", - "csvfiles", - "currentgit", - "dans", - "datacategorygroup", - "datacategorygroups", - "datadotcom", - "deactivateduser", - "defaultdevhubusername", - "defaultmergerequest", - "defaultusername", - "definitionfile", - "deletable", - "deleteafter", - "deloyment", - "demand\u00e9", - "deploydir", - "derroman", - "destructivechanges", - "destructivepackagexml", - "developername", - "developpement", - "devhubusername", - "dfgdlf", - "dhgfh", - "difftool", - "dimitrimonge", - "dlrs", - "dockerfilelintrc", - "domainbuilder", - "domcontentloaded", - "dompurify", - "duplicatefiles", - "d\u00e9commissionn\u00e9es", - "d\u00e9ployer", - "d\u2019une", - "eatre", - "ects", - "elementsignored", - "elgohr", - "emailservices", - "emailvalid", - "emptyitems", - "errorprone", - "eventtype", - "everytime", - "exceljs", - "excludefilter", - "excludeprofiles", - "excludeusers", - "failiferror", - "fflib", - "fichiers", - "filteredmetadatas", - "filterlanguage", - "filtersections", - "findduplicates", - "flexipage", - "flexipages", - "flowpositions", - "flowwww", - "fonctions", - "fontawesome", - "forceignore", - "forcenew", - "forceoverwrite", - "fournir", - "fromcommit", - "fromorg", - "frontdoor", - "frozeuser", - "genrsa", - "geodata", - "gims", - "gimsu", - "gitbeaker", - "gitbranch", - "gitdelta", - "gitlab", - "glightbox", - "globby", - "globpattern", - "grafanacloud", - "granteduserpackagelicense", - "gtag", - "hardcode", - "hardis", - "hardisauthlogin", - "hardisconfigget", - "hardisdatatreeexport", - "hardisdocextractpermsetgroups", - "hardisdocplugingenerate", - "hardisgroupcom", - "hardismdapideploy", - "hardismisctoml", - "hardisorgconfigureconfig", - "hardisorgconfiguredata", - "hardisorgconfigurefiles", - "hardisorgconfiguremonitoring", - "hardisorgconnect", - "hardisorgcreate", - "hardisorgdataconfig", - "hardisorgdatadelete", - "hardisorgdataexport", - "hardisorgdataimport", - "hardisorgdiagnoselegacyapi", - "hardisorgfilesexport", - "hardisorgfixlistviewmine", - "hardisorgpurgeapexlog", - "hardisorgpurgeflow", - "hardisorgretrievepackageconfig", - "hardisorgretrievesourcesanalytics", - "hardisorgretrievesourcesdx", - "hardisorgretrievesourcesmetadata", - "hardisorgretrievesourcesretrofit", - "hardisorgselect", - "hardisorgtestapex", - "hardisorguseractivateinvalid", - "hardisorguseremailvalid", - "hardisorguserfreeze", - "hardisorgusermakeemailvalid", - "hardisorguserunfreeze", - "hardispackagecreate", - "hardispackageinstall", - "hardispackagemergexml", - "hardispackageversioncreate", - "hardispackageversionlist", - "hardispackageversionpromote", - "hardisprojectauditapiversion", - "hardisprojectauditcallincallout", - "hardisprojectauditduplicatefiles", - "hardisprojectauditremotesites", - "hardisprojectcleanemptyitems", - "hardisprojectcleanhiddenitems", - "hardisprojectcleanlistviews", - "hardisprojectcleanmanageditems", - "hardisprojectcleanminimizeprofiles", - "hardisprojectcleanorgmissingitems", - "hardisprojectcleanreferences", - "hardisprojectcleanretrievefolders", - "hardisprojectcleanstandarditems", - "hardisprojectcleansystemdebug", - "hardisprojectcleanxml", - "hardisprojectconfigureauth", - "hardisprojectconfiguredeployment", - "hardisprojectconvertprofilestopermsets", - "hardisprojectcreate", - "hardisprojectdeploysourcesdx", - "hardisprojectdeploysourcesmetadata", - "hardisprojectfixv", - "hardisprojectfixv53flexipages", - "hardisprojectgenerategitdelta", - "hardisprojectlint", - "hardisprojectworktasknew", - "hardisscratchcreate", - "hardisscratchdelete", - "hardisscratchpoolconfigure", - "hardisscratchpoolcreate", - "hardisscratchpoollocalauth", - "hardisscratchpoolrefresh", - "hardisscratchpoolreset", - "hardisscratchpoolview", - "hardisscratchpull", - "hardisscratchpush", - "hardissourcedeploy", - "hardissourcepush", - "hardissourceretrieve", - "hardisworknew", - "hardisworkpublish", - "hardisworkrefresh", - "hardisworkresetselection", - "hardisworksave", - "hardisworktaskcomplete", - "hardisworktasknew", - "hardisworktaskpublish", - "hardisworktaskrefresh", - "hardisworktasksave", - "hardisworkws", - "hhmm", - "hiddenitems", - "high", - "historization", - "hostnames", - "hotfixes", - "hthe", - "htmlvalue", - "iframe", - "ignoreerrors", - "ignorerights", - "ignorewarnings", - "iids", - "includemanaged", - "includepackages", - "includeprofiles", - "initial", - "initialisation", - "inputfolder", - "installable", - "installationkey", - "installationkeybypass", - "installkey", - "install\u00e9e", - "instanceupgrade", - "instanceurl", - "instantiate", - "inte", - "integ", - "interf", - "interf\u00e8re", - "isactive", - "isbuildercontent", - "isfrozen", - "javascripts", - "jeandupont", - "jlsfgd", - "jscpd", - "jsforce", - "jsonschema", - "jwtkeyfile", - "keepmetadatatypes", - "keyout", - "keyv", - "keyvalue", - "kvdb", - "lastndays", - "lcone", - "lcov", - "legacyapi", - "legetz", - "licenseidentifiers", - "licensetypes", - "lightningloginenroll", - "liste", - "listview", - "listviewmine", - "listviews", - "localauth", - "localconfig", - "localecsv", - "localfields", - "localtest", - "loginasgrantedtopartnerbt", - "loglevel", - "lors", - "lycheeignore", - "l\u2019acc\u00e8s", - "maintenant", - "makeemailvalid", - "mamasse", - "manageditems", - "manifestname", - "materialx", - "maxfields", - "maxuserdisplay", - "mdapi", - "mdapipkg", - "medium", - "megalinter", - "mergerequest", - "mergetool", - "mergexml", - "metadatastatus", - "metas", - "minimizeprofiles", - "minimumapiversion", - "missingattributes", - "mkdir", - "mkdocs", - "monitoringhardisgroup", - "mrkdwn", - "multiselect", - "mutingpermissionset", - "mutingpermissionsets", - "myclient", - "mycompany", - "myconfig", - "mypackage", - "mypassword", - "myproject", - "myusername", - "namespaceprefix", - "networkidle", - "nico", - "noclean", - "noconfig", - "nodelta", - "nogit", - "noinsight", - "noprompt", - "nopull", - "notestrunrunspecifiedtestsrunlocaltestsrunalltestsinorg", - "notif", - "notificationtypes", - "notifs", - "notiftype", - "npmignore", - "npmrc", - "numberfailed", - "nvuillam", - "oauthcustomscope", - "oauthcustomscopes", - "obligatoire", - "occurences", - "oclif", - "olas", - "op\u00e9ration", - "orgfreeze", - "orginstanceurl", - "orgmissingitems", - "orgs", - "outputdir", - "outputfile", - "outputfolder", - "outputstring", - "overridable", - "oxsecurity", - "package", - "packageconfig", - "packagenames", - "packagetype", - "packagexml", - "packagexmlfull", - "packagexmls", - "packagexmltargetorg", - "papaparse", - "parseable", - "partage", - "pascalcase", - "passin", - "passout", - "pckg", - "perfs", - "permissionset", - "permissionsetgroup", - "permissionsetgroups", - "permissionsets", - "permissivediff", - "permset", - "permsetgroups", - "personnalis", - "personnalis\u00e9", - "peut", - "picklist", - "pjson", - "ployer", - "pmdconfig", - "pocessed", - "polltimeout", - "poolstorage", - "postdestructivechanges", - "postpack", - "postrun", - "posttest", - "predestructivechanges", - "preid", - "prepatch", - "preprod", - "prerun", - "preselection", - "pris", - "productionbranch", - "productrequest", - "profilestopermsets", - "profiletabs", - "projectname", - "propri\u00e9t\u00e9", - "psga", - "psgc", - "psgm", - "psla", - "purgeondelete", - "pushmode", - "pymdown", - "pymdownx", - "qstn", - "quotepath", - "rbenv", - "recette", - "redislabs", - "refactorization", - "regexes", - "relatedentitytype", - "relatednotifications", - "remotesites", - "removedonly", - "removepackagexml", - "rendez", - "reportfile", - "reportname", - "resetpassword", - "resetsave", - "resetselection", - "resultformat", - "resultsdir", - "retrievefolders", - "retrievetargetdir", - "retrofitbranch", - "retrofited", - "retrofittargetbranch", - "returnactiveusers", - "revparse", - "rootdir", - "ruleset", - "runtests", - "r\u00e9cente", - "samlssoconfig", - "samlssoconfigs", - "scolladon", - "scontrols", - "scratchorg", - "secretlintignore", - "secur", - "securitytype", - "semver", - "setalias", - "setdefaultdevhubusername", - "setdefaultusername", - "setext", - "sfdmu", - "sfdx", - "sfdxhardis", - "sfdxurl", - "sforce", - "sfpowerkit", - "sharingcalc", - "signkey", - "singlepackage", - "skipauth", - "skiptransfo", - "slctd", - "snote", - "soapdeploy", - "soapenv", - "sobject", - "sobjectid", - "sobjects", - "sobjecttype", - "somecommand", - "somefolder", - "someparameter", - "somevalue", - "sont", - "soql", - "sourcepath", - "sourceusername", - "standarditems", - "startchunknumber", - "staticresources", - "stefanzweifel", - "suspiscious", - "systemdebug", - "s\u00e9lection", - "targetbranch", - "targetdevhubusername", - "targetusername", - "tempfolder", - "templatestyle", - "templatetype", - "testcase", - "testlevel", - "testsuite", - "testsuites", - "texei", - "tgci", - "thvd", - "tocommit", - "tocstop", - "tomlfile", - "tomlfileencoding", - "toplevel", - "toujours", - "tovalidate", - "tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal", - "tracksource", - "trait\u00e9", - "transco", - "transfo", - "transfoconfig", - "trivy", - "trivyignore", - "twemoji", - "uitype", - "unallowed", - "unfiled", - "unfrozeuser", - "unicity", - "uniquement", - "unmanaged", - "unpackaged", - "unparse", - "unrstanding", - "unstage", - "unusedlicenses", - "unusedmetadata", - "unusedmetadatas", - "unusedusers", - "updatedall", - "updateddatedexchrate", - "urlonly", - "usagestop", - "usedonly", - "useremailchangesent", - "userid", - "userlicense", - "usetoolingapi", - "utilis", - "utilisateurs", - "utilis\u00e9", - "uuidv", - "validateddeployrequestid", - "venv", - "vous", - "vuln", - "wapp", - "wcomp", - "wdash", - "wdpr", - "weblink", - "weblinks", - "webservice", - "whitespaces", - "wlens", - "workdotcom", - "workitems", - "xmldec", - "xpaths", - "xslx", - "yamioliva", - "yamllint", - "zipfile", - "\u00eatre", - "\u00ecntegration" - ] -} + "ignorePaths": [ + "**/node_modules/**", + "**/vscode-extension/**", + "**/.git/**", + "**/grafana/**", + ".vscode", + "package-lock.json", + "report", + ".cspell.json" + ], + "language": "en", + "version": "0.1", + "words": [ + "AAAS", + "AAAU", + "ACCESSTOKEN", + "ACCESSTOKEN) -- (process.env.SYSTEM_ACCESSTOKEN", + "AFFERO", + "AGPL", + "ALPHAID", + "APICIL", + "AULL", + "Accueil", + "Actv", + "Administrateur", + "Affero", + "Alainbates", + "Astran", + "Astro", + "BETAID", + "BRANCHNAME", + "BUILDID", + "Backuped", + "Badwords", + "Batchable", + "Buildx", + "Bulkify", + "CANARYID", + "CARRENET", + "CCAU", + "COLLECTIONURI", + "COMPTES", + "Callout", + "Catg", + "Chantier", + "Cloudi", + "Cloudity", + "Codecov", + "Commerciaux", + "Communit", + "Correspondances", + "Cyclomatic", + "DDHG", + "DYSA", + "Dandb", + "Defaut", + "Demarrage", + "Devhub", + "Dont", + "Dreamforce", + "Dreamin", + "EAAU", + "ECAU", + "ECONNABORTED", + "ECONNRESET", + "EDITORCONFIG", + "Eqoehef", + "Explainability", + "Externe", + "FGGF", + "FHDHGDH", + "FILEIO", + "FSEDS", + "Facturation", + "Finet", + "Firstname", + "Flexi", + "Fswg", + "GHSA", + "GHTRGDHD", + "GITLEAKS", + "GRYPE", + "Gagne", + "Gmail", + "HADOLINT", + "Hardis", + "Hdhghg", + "Hentschke", + "Hfgfgjfh", + "IGHT", + "INTEG", + "IVMA", + "Iiwib", + "Ijoi", + "JSONLINT", + "Jokinen", + "KERNN", + "KVDB", + "Keyfile", + "Keyv", + "Kvdb", + "LISTVIEWNAME", + "LVMYAA", + "Licences", + "MARKDOWNLINT", + "MASTERLABEL", + "MYFOLDER", + "MYREPORTNAME", + "MYTASK", + "Metadatas", + "NOPMD", + "Ncss", + "Ndays", + "Novaxel", + "OCIs", + "ODNs", + "OOOOOPS", + "ORGALIAS", + "Ohana", + "Omnichannel", + "Orgs", + "Ozil", + "Ozil's", + "PQAF", + "PROFILENAME", + "PROSELINT", + "PUBLICENDPOINT", + "PULLREQUEST", + "PULLREQUESTID", + "Picklist", + "Picklists", + "Planification", + "Portail", + "Post", + "Psla", + "Publishe", + "Pullrequests", + "QESRDTHFKGKH", + "Quickfix", + "REPOSITORYNAME", + "RWAAA", + "Recalc", + "Recrutement", + "Reseting", + "Rypple", + "SAST", + "SEMGREP", + "SOMENAMESPACE", + "SOURCEBRANCHNAME", + "STYLELINT", + "Scontrol", + "Scratches", + "Sfdc", + "Sfdx", + "Solva", + "Stmts", + "Sublicensing", + "Suspendre", + "Sustn", + "Syst\u00e8me", + "TCRM", + "TEAMPROJECT", + "THEREISNOT", + "Trailmix", + "Transco", + "Transcos", + "Tuto", + "Typederendezvous", + "Ujut", + "Unallowed", + "Unstash", + "Uoms", + "Upsert", + "VCAS", + "VERSIONNUMBER", + "Viewfile", + "Vuillamy", + "WIPO", + "XSLX", + "Xmls", + "YOURDEVHUBUSERNAME", + "YOURORGNAME", + "YOURSOURCEORG", + "YOURSOURCEORGUSERNAME", + "ZWSAU", + "accordionallowed", + "accordionapex", + "accordionauto", + "accordionavailable", + "accordionbranch", + "accordionclean", + "accordioncommands", + "accordioncustom", + "accordiondata", + "accordiondefault", + "accordiondeployment", + "accordiondev", + "accordiondevelopment", + "accordionextends", + "accordioninit", + "accordioninstall", + "accordioninstalled", + "accordioninstance", + "accordionlinter", + "accordionlist", + "accordionmonitoring", + "accordionms", + "accordionnotifications", + "accordionpool", + "accordionproduction", + "accordionproject", + "accordionretrofit", + "accordionruntests", + "accordionscratch", + "accordionsfdmu", + "accordionskip", + "accordionsources", + "accordiontarget", + "accordiontest", + "accordionuse", + "activateduser", + "activateinvalid", + "addeduserpackagelicense", + "administratif", + "aiapplicationconfig", + "allowfullscreen", + "allowpurgefailure", + "antislashes", + "apexlog", + "apexp", + "apextest", + "apiversion", + "apos", + "aquasecurity", + "assigneduserstomobileconfig", + "astran", + "astrea", + "audittrail", + "authprovider", + "authproviders", + "autocleantypes", + "autoplay", + "avec", + "azdev", + "badwords", + "bestpractices", + "blockquotes", + "boza", + "buildargs", + "buildx", + "bulletpoints", + "cacache", + "calcul", + "callincallout", + "callout", + "callouts", + "canmodify", + "caseentitlement", + "certaines", + "changedcommunitynickname", + "changedemail", + "changedfederationid", + "changedinteractionuseroffon", + "changedinteractionuseronoff", + "changedmanager", + "changedmarketinguseroffon", + "changedmarketinguseronoff", + "changedpassword", + "changedprofileforuser", + "changedprofileforusercusttostd", + "changedprofileforuserstdtocust", + "changedroleforuser", + "changedroleforuserfromnone", + "changedroleforusertonone", + "changedsenderemail", + "changemgmt", + "checkcoverage", + "checkcoverage) -- endArgs.indexOf(\"--checkcoverage\"", + "checkonly", + "choco", + "chunksize", + "cicd", + "classname", + "clientid", + "cloudiscore", + "codecov", + "codecoverage", + "codestyle", + "columnify", + "commandreference", + "commandsstop", + "commitlint", + "commitmode", + "commitsto", + "concat", + "configfile", + "confirmfreeze", + "confirmunfreeze", + "conta", + "contentassets", + "correspondance", + "cosmiconfig", + "cours", + "coverageformatters", + "createdcustomersuccessuser", + "createduser", + "crta", + "csvfile", + "csvfiles", + "currentgit", + "customindex", + "dans", + "datacategorygroup", + "datacategorygroups", + "datadotcom", + "deactivateduser", + "defaultdevhubusername", + "defaultmergerequest", + "defaultusername", + "definitionfile", + "deletable", + "deleteafter", + "deloyment", + "demand\u00e9", + "deploydir", + "derroman", + "destructivechanges", + "destructivepackagexml", + "developername", + "developpement", + "devhubusername", + "dfgdlf", + "dhgfh", + "difftool", + "dimitrimonge", + "dlrs", + "dockerfilelintrc", + "domainbuilder", + "domcontentloaded", + "dompurify", + "duplicatefiles", + "d\u00e9commissionn\u00e9es", + "d\u00e9ployer", + "d\u2019une", + "eatre", + "ects", + "elementsignored", + "elgohr", + "emailservices", + "emailvalid", + "emptyitems", + "errorflow", + "erroronwarnings", + "errorprone", + "eslintcache", + "eventtype", + "everytime", + "exceljs", + "excludefilter", + "excludeprofiles", + "excludeusers", + "expcloud", + "explainability", + "failiferror", + "fflib", + "fichiers", + "filteredmetadatas", + "filterlanguage", + "filtersections", + "findduplicates", + "flexipage", + "flexipages", + "flowpositions", + "flowtest", + "flowtests", + "flowwww", + "fonctions", + "fontawesome", + "forceignore", + "forcenew", + "forceoverwrite", + "fournir", + "fpath", + "friendlyname", + "fromcommit", + "fromorg", + "frontdoor", + "frozeuser", + "genrsa", + "geodata", + "gims", + "gimsu", + "gitbeaker", + "gitbranch", + "gitdelta", + "gitlab", + "glightbox", + "globby", + "globpattern", + "grafanacloud", + "granteduserpackagelicense", + "gtag", + "hardcode", + "hardis", + "hardisauthlogin", + "hardisconfigget", + "hardisdatatreeexport", + "hardisdocextractpermsetgroups", + "hardisdocplugingenerate", + "hardisgroupcom", + "hardismdapideploy", + "hardismisctoml", + "hardisorgconfigureconfig", + "hardisorgconfiguredata", + "hardisorgconfigurefiles", + "hardisorgconfiguremonitoring", + "hardisorgconnect", + "hardisorgcreate", + "hardisorgdataconfig", + "hardisorgdatadelete", + "hardisorgdataexport", + "hardisorgdataimport", + "hardisorgdiagnoselegacyapi", + "hardisorgfilesexport", + "hardisorgfixlistviewmine", + "hardisorgpurgeapexlog", + "hardisorgpurgeflow", + "hardisorgretrievepackageconfig", + "hardisorgretrievesourcesanalytics", + "hardisorgretrievesourcesdx", + "hardisorgretrievesourcesmetadata", + "hardisorgretrievesourcesretrofit", + "hardisorgselect", + "hardisorgtestapex", + "hardisorguseractivateinvalid", + "hardisorguseremailvalid", + "hardisorguserfreeze", + "hardisorgusermakeemailvalid", + "hardisorguserunfreeze", + "hardispackagecreate", + "hardispackageinstall", + "hardispackagemergexml", + "hardispackageversioncreate", + "hardispackageversionlist", + "hardispackageversionpromote", + "hardisprojectauditapiversion", + "hardisprojectauditcallincallout", + "hardisprojectauditduplicatefiles", + "hardisprojectauditremotesites", + "hardisprojectcleanemptyitems", + "hardisprojectcleanhiddenitems", + "hardisprojectcleanlistviews", + "hardisprojectcleanmanageditems", + "hardisprojectcleanminimizeprofiles", + "hardisprojectcleanorgmissingitems", + "hardisprojectcleanreferences", + "hardisprojectcleanretrievefolders", + "hardisprojectcleanstandarditems", + "hardisprojectcleansystemdebug", + "hardisprojectcleanxml", + "hardisprojectconfigureauth", + "hardisprojectconfiguredeployment", + "hardisprojectconvertprofilestopermsets", + "hardisprojectcreate", + "hardisprojectdeploysourcesdx", + "hardisprojectdeploysourcesmetadata", + "hardisprojectfixv", + "hardisprojectfixv53flexipages", + "hardisprojectgenerategitdelta", + "hardisprojectlint", + "hardisprojectworktasknew", + "hardisscratchcreate", + "hardisscratchdelete", + "hardisscratchpoolconfigure", + "hardisscratchpoolcreate", + "hardisscratchpoollocalauth", + "hardisscratchpoolrefresh", + "hardisscratchpoolreset", + "hardisscratchpoolview", + "hardisscratchpull", + "hardisscratchpush", + "hardissourcedeploy", + "hardissourcepush", + "hardissourceretrieve", + "hardisworknew", + "hardisworkpublish", + "hardisworkrefresh", + "hardisworkresetselection", + "hardisworksave", + "hardisworktaskcomplete", + "hardisworktasknew", + "hardisworktaskpublish", + "hardisworktaskrefresh", + "hardisworktasksave", + "hardisworkws", + "hhmm", + "hiddenitems", + "high", + "historization", + "hostnames", + "hotfixes", + "hthe", + "htmlvalue", + "iframe", + "ignoreerrors", + "ignorerights", + "ignorewarnings", + "iids", + "includemanaged", + "includepackages", + "includeprofiles", + "initial", + "initialisation", + "inputfolder", + "installable", + "installationkey", + "installationkeybypass", + "installkey", + "install\u00e9e", + "instanceupgrade", + "instanceurl", + "instantiate", + "inte", + "integ", + "interf", + "interf\u00e8re", + "isactive", + "isbuildercontent", + "isfrozen", + "javascripts", + "jeandupont", + "jlsfgd", + "jscpd", + "jsforce", + "jsonschema", + "jwtkeyfile", + "keepmetadatatypes", + "keyout", + "keyv", + "keyvalue", + "kvdb", + "lastndays", + "lcone", + "lcov", + "legacyapi", + "legetz", + "licenseidentifiers", + "licensetypes", + "lightningloginenroll", + "linkinator", + "liste", + "listview", + "listviewmine", + "listviews", + "localauth", + "localconfig", + "localecsv", + "localfields", + "localtest", + "loginasgrantedtopartnerbt", + "loglevel", + "lors", + "lycheeignore", + "l\u2019acc\u00e8s", + "maintenant", + "makeemailvalid", + "mamasse", + "manageditems", + "manifestname", + "marketingappextension", + "marketingappextensions", + "materialx", + "maxfields", + "maxuserdisplay", + "mdapi", + "mdapipkg", + "medium", + "megalinter", + "mergerequest", + "mergetool", + "mergexml", + "metadatastatus", + "metas", + "minimizeprofiles", + "minimumapiversion", + "missingattributes", + "mkdir", + "mkdocs", + "mocharc", + "monitoringhardisgroup", + "mrkdwn", + "multiselect", + "mutingpermissionset", + "mutingpermissionsets", + "myclient", + "mycompany", + "myconfig", + "mypackage", + "mypassword", + "myproject", + "myusername", + "namespaceprefix", + "networkidle", + "nico", + "noclean", + "noconfig", + "nodelta", + "nogit", + "noinsight", + "noprompt", + "nopull", + "nosmart", + "notestrunrunspecifiedtestsrunlocaltestsrunalltestsinorg", + "notif", + "notificationtypes", + "notifs", + "notiftype", + "npmignore", + "npmrc", + "numberfailed", + "nvuillam", + "nycrc", + "oauthcustomscope", + "oauthcustomscopes", + "obligatoire", + "occurences", + "oclif", + "olas", + "op\u00e9ration", + "orgfreeze", + "orginstanceurl", + "orgmissingitems", + "orgs", + "outputdir", + "outputfile", + "outputfolder", + "outputstring", + "overridable", + "oxsecurity", + "package", + "packageconfig", + "packagenames", + "packagetype", + "packagexml", + "packagexmlfull", + "packagexmls", + "packagexmltargetorg", + "papaparse", + "parseable", + "partage", + "pascalcase", + "passin", + "passout", + "pckg", + "perfs", + "permissionset", + "permissionsetgroup", + "permissionsetgroups", + "permissionsets", + "permissivediff", + "permset", + "permsetgroups", + "personnalis", + "personnalis\u00e9", + "peut", + "picklist", + "pjson", + "ployer", + "pmdconfig", + "pocessed", + "polltimeout", + "poolstorage", + "postdestructivechanges", + "postpack", + "postrun", + "posttest", + "predestructivechanges", + "preid", + "prepatch", + "preprod", + "prerun", + "preselection", + "pris", + "productionbranch", + "productrequest", + "profilestopermsets", + "profiletabs", + "projectname", + "propri\u00e9t\u00e9", + "psga", + "psgc", + "psgm", + "psla", + "purgeondelete", + "pushmode", + "pymdown", + "pymdownx", + "qstn", + "quotepath", + "rbenv", + "recette", + "redislabs", + "refactorization", + "regexes", + "relatedentitytype", + "relatednotifications", + "releaseupdates", + "remotesites", + "removedonly", + "removepackagexml", + "rendez", + "reportfile", + "reportname", + "resetpassword", + "resetsave", + "resetselection", + "resultformat", + "resultsdir", + "retrievefolders", + "retrievetargetdir", + "retrofitbranch", + "retrofited", + "retrofittargetbranch", + "returnactiveusers", + "revparse", + "rootdir", + "ruleset", + "runtests", + "r\u00e9cente", + "salesforcecli", + "salesforcedx", + "samlssoconfig", + "samlssoconfigs", + "scolladon", + "scontrols", + "scratchorg", + "secretlintignore", + "secur", + "securitytype", + "semver", + "setalias", + "setdefaultdevhubusername", + "setdefaultusername", + "setext", + "sfdevrc", + "sfdmu", + "sfdx", + "sfdxhardis", + "sfdxurl", + "sforce", + "sfpowerkit", + "sharingcalc", + "signkey", + "singlepackage", + "skipauth", + "skiptransfo", + "slackapp", + "slackapps", + "slctd", + "smmry", + "snote", + "soapdeploy", + "soapenv", + "sobject", + "sobjectid", + "sobjects", + "sobjecttype", + "somecommand", + "somefolder", + "someparameter", + "somevalue", + "sont", + "soql", + "sourcepath", + "sourceusername", + "standarditems", + "startchunknumber", + "staticresources", + "stefanzweifel", + "suspiscious", + "sustn", + "systemdebug", + "s\u00e9lection", + "targetbranch", + "targetdevhubusername", + "targetusername", + "tempfolder", + "templatestyle", + "templatetype", + "testcase", + "testkit", + "testlevel", + "testsuite", + "testsuites", + "texei", + "tgci", + "thvd", + "tocommit", + "tocstop", + "tomlfile", + "tomlfileencoding", + "toplevel", + "toujours", + "tovalidate", + "tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal", + "tracksource", + "trait\u00e9", + "transco", + "transfo", + "transfoconfig", + "trivy", + "trivyignore", + "twemoji", + "uitype", + "unallowed", + "unfiled", + "unfrozeuser", + "unicity", + "uniquement", + "unitcoverage", + "unmanaged", + "unpackaged", + "unparse", + "unrstanding", + "unstage", + "unusedlicenses", + "unusedmetadata", + "unusedmetadatas", + "unusedusers", + "updatedall", + "updateddatedexchrate", + "updatedfile", + "urlonly", + "usagestop", + "usedonly", + "useraccesspolicies", + "useraccesspolicy", + "useremailchangesent", + "userid", + "userlicense", + "usetoolingapi", + "utilis", + "utilisateurs", + "utilis\u00e9", + "uuidv", + "validateddeployrequestid", + "venv", + "viewdefinitions", + "vous", + "vuln", + "wapp", + "wcomp", + "wdash", + "wdpr", + "weblink", + "weblinks", + "webservice", + "webstoretemplate", + "whitespaces", + "wireit", + "wlens", + "workdotcom", + "workitems", + "xmldec", + "xpaths", + "xslx", + "xunit", + "yamioliva", + "yamllint", + "zipfile", + "\u00eatre", + "\u00ecntegration" + ] +} \ No newline at end of file diff --git a/.github/linters/.gitleaks.toml b/.github/linters/.gitleaks.toml new file mode 100644 index 000000000..525d9d85c --- /dev/null +++ b/.github/linters/.gitleaks.toml @@ -0,0 +1,20 @@ + +title = "gitleaks config" + +[extend] +# useDefault will extend the base configuration with the default gitleaks config: +# https://github.com/zricethezav/gitleaks/blob/master/config/gitleaks.toml +useDefault = true + +[allowlist] + description = "Allowlisted files" + paths = [ + '''.automation/test''', + '''megalinter-reports''', + '''.github/linters''', + '''node_modules''', + '''docs''', + '''.mypy_cache''', + '''(.*?)gitleaks\.toml$''', + '''(?i)(.*?)(png|jpeg|jpg|gif|doc|docx|pdf|bin|xls|xlsx|pyc|zip)$''', + '''(go.mod|go.sum)$'''] \ No newline at end of file diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 19540ca4e..fe102f79f 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -18,8 +18,8 @@ jobs: - run: yarn - run: yarn prepack - run: npm i @salesforce/cli -g - - run: echo y|sfdx plugins:install sfdx-hardis - - run: sfdx hardis:doc:plugin:generate + - run: echo y|sf plugins install sfdx-hardis + - run: sf hardis:doc:plugin:generate # Deploy docs with mkdocs-material - uses: actions/setup-python@v3 with: diff --git a/.github/workflows/create-github-release.yml.not-used b/.github/workflows/create-github-release.yml.not-used new file mode 100644 index 000000000..000d54eb5 --- /dev/null +++ b/.github/workflows/create-github-release.yml.not-used @@ -0,0 +1,34 @@ +name: create-github-release + +on: + push: + branches: + - main + - prerelease/** + tags-ignore: + - "*" + workflow_dispatch: + inputs: + prerelease: + type: string + description: "Name to use for the prerelease: beta, dev, etc. NOTE: If this is already set in the package.json, it does not need to be passed in here." + +jobs: + release: + uses: salesforcecli/github-workflows/.github/workflows/create-github-release.yml@main + secrets: + SVC_CLI_BOT_GITHUB_TOKEN: ${{ secrets.SVC_CLI_BOT_GITHUB_TOKEN }} + with: + prerelease: ${{ inputs.prerelease }} + # If this is a push event, we want to skip the release if there are no semantic commits + # However, if this is a manual release (workflow_dispatch), then we want to disable skip-on-empty + # This helps recover from forgetting to add semantic commits ('fix:', 'feat:', etc.) + skip-on-empty: ${{ github.event_name == 'push' }} + # docs: + # # Most repos won't use this + # # Depends on the 'release' job to avoid git collisions, not for any functionality reason + # needs: release + # secrets: + # SVC_CLI_BOT_GITHUB_TOKEN: ${{ secrets.SVC_CLI_BOT_GITHUB_TOKEN }} + # if: ${{ github.ref_name == 'main' }} + # uses: salesforcecli/github-workflows/.github/workflows/publishTypedoc.yml@main diff --git a/.github/workflows/deploy-ALPHA.yml b/.github/workflows/deploy-ALPHA.yml index d7db5962e..7dac94d79 100644 --- a/.github/workflows/deploy-ALPHA.yml +++ b/.github/workflows/deploy-ALPHA.yml @@ -7,11 +7,11 @@ ####################################### # Start the job on all push to master # ####################################### -name: "Build & Deploy - ALPHA" +name: 'Build & Deploy - ALPHA' on: push: branches: - - "alpha" + - 'alpha' ############### # Set the Job # @@ -29,14 +29,15 @@ jobs: - uses: actions/setup-node@v3 with: node-version: 20 - registry-url: "https://registry.npmjs.org" + registry-url: 'https://registry.npmjs.org' always-auth: true # Defaults to the user or organization that owns the workflow file - scope: "hardisgroupcom" - - run: yarn - - run: yarn config set version-git-tag false && tsc -b + scope: 'hardisgroupcom' + - run: yarn install --frozen-lockfile && yarn run compile + - run: yarn config set version-git-tag false - run: ALPHAID=$(date '+%Y%m%d%H%M') && yarn version --prepatch --preid="alpha$ALPHAID" - - run: yarn config set network-timeout 300000 && yarn publish --tag alpha + - run: yarn config set network-timeout 300000 + - run: yarn publish --tag alpha env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} @@ -91,13 +92,13 @@ jobs: - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master with: - image-ref: "docker.io/hardisgroupcom/sfdx-hardis:alpha" - format: "table" - exit-code: "1" + image-ref: 'docker.io/hardisgroupcom/sfdx-hardis:alpha' + format: 'table' + exit-code: '1' ignore-unfixed: true - vuln-type: "os,library" + vuln-type: 'os,library' security-checks: vuln - severity: "CRITICAL,HIGH" + severity: 'CRITICAL,HIGH' push_alpha_to_registry_sfdx_recommended: name: Push alpha Docker image to Docker Hub (with @salesforce/cli version recommended by hardis) @@ -150,10 +151,10 @@ jobs: - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master with: - image-ref: "docker.io/hardisgroupcom/sfdx-hardis:alpha-sfdx-recommended" - format: "table" - exit-code: "1" + image-ref: 'docker.io/hardisgroupcom/sfdx-hardis:alpha-sfdx-recommended' + format: 'table' + exit-code: '1' ignore-unfixed: true - vuln-type: "os,library" + vuln-type: 'os,library' security-checks: vuln - severity: "CRITICAL,HIGH" + severity: 'CRITICAL,HIGH' diff --git a/.github/workflows/deploy-CANARY.yml b/.github/workflows/deploy-CANARY.yml index e112f82b2..f8b1d98de 100644 --- a/.github/workflows/deploy-CANARY.yml +++ b/.github/workflows/deploy-CANARY.yml @@ -33,7 +33,7 @@ jobs: always-auth: true # Defaults to the user or organization that owns the workflow file scope: "hardisgroupcom" - - run: yarn + - run: yarn install --frozen-lockfile - run: yarn config set version-git-tag false && tsc -b - run: CANARYID=$(date '+%Y%m%d%H%M') && yarn version --prepatch --preid="canary$CANARYID" - run: yarn config set network-timeout 300000 && yarn publish --tag canary diff --git a/.github/workflows/deploy-PROD.yml b/.github/workflows/deploy-PROD.yml index 57cc1d181..15b79c063 100644 --- a/.github/workflows/deploy-PROD.yml +++ b/.github/workflows/deploy-PROD.yml @@ -33,7 +33,7 @@ jobs: always-auth: true # Defaults to the user or organization that owns the workflow file scope: "hardisgroupcom" - - run: yarn + - run: yarn install --frozen-lockfile - run: yarn config set version-git-tag false && tsc -b - run: BETAID=$(date '+%Y%m%d%H%M') && yarn version --prepatch --preid="beta$BETAID" - run: yarn config set network-timeout 300000 && yarn publish --tag beta diff --git a/.github/workflows/deploy-RELEASE.yml b/.github/workflows/deploy-RELEASE.yml index 938d338b1..d88d5c302 100644 --- a/.github/workflows/deploy-RELEASE.yml +++ b/.github/workflows/deploy-RELEASE.yml @@ -31,7 +31,7 @@ jobs: registry-url: "https://registry.npmjs.org" # Defaults to the user or organization that owns the workflow file scope: "nvuillam" - - run: yarn + - run: yarn install --frozen-lockfile - run: yarn config set network-timeout 300000 && yarn publish || echo "Unable to publish package version. Or published in background because of NPM bug ?" env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/devScripts.yml b/.github/workflows/devScripts.yml new file mode 100644 index 000000000..b7630fdcd --- /dev/null +++ b/.github/workflows/devScripts.yml @@ -0,0 +1,11 @@ +name: devScripts +on: + workflow_dispatch: + schedule: + - cron: '50 6 * * 0' + +jobs: + update: + uses: salesforcecli/github-workflows/.github/workflows/devScriptsUpdate.yml@main + secrets: + SVC_CLI_BOT_GITHUB_TOKEN: ${{ secrets.SVC_CLI_BOT_GITHUB_TOKEN }} diff --git a/.github/workflows/mega-linter.yml b/.github/workflows/mega-linter.yml index 1031e75a1..86ed339eb 100644 --- a/.github/workflows/mega-linter.yml +++ b/.github/workflows/mega-linter.yml @@ -43,9 +43,10 @@ jobs: # Upload Mega-Linter artifacts - name: Archive production artifacts if: success() || failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Mega-Linter reports + include-hidden-files: "true" path: | megalinter-reports mega-linter.log diff --git a/.github/workflows/onRelease.yml.not-used b/.github/workflows/onRelease.yml.not-used new file mode 100644 index 000000000..7f1237e1d --- /dev/null +++ b/.github/workflows/onRelease.yml.not-used @@ -0,0 +1,31 @@ +name: publish +on: + release: + # both release and prereleases + types: [published] + # support manual release in case something goes wrong and needs to be repeated or tested + workflow_dispatch: + inputs: + tag: + description: github tag that needs to publish + type: string + required: true +jobs: + getDistTag: + outputs: + tag: ${{ steps.distTag.outputs.tag }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.release.tag_name || inputs.tag }} + - uses: salesforcecli/github-workflows/.github/actions/getPreReleaseTag@main + id: distTag + npm: + uses: salesforcecli/github-workflows/.github/workflows/npmPublish.yml@main + needs: [getDistTag] + with: + tag: ${{ needs.getDistTag.outputs.tag || 'latest' }} + githubTag: ${{ github.event.release.tag_name || inputs.tag }} + + secrets: inherit diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a2e8d5c35..3bd27a7a5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,38 +1,27 @@ ---- -# -# Documentation: -# https://help.github.com/en/articles/workflow-syntax-for-github-actions -# - -####################################### -# Start the job on all push to master # -####################################### -name: "Test" +name: tests on: - push: # Comment this line to trigger action only on pull-requests (not recommended if you don't pay for GH Actions) - pull_request: - branches: [master, main] + push: + branches-ignore: [main] + workflow_dispatch: -############### -# Set the Job # -############### jobs: - test: - runs-on: ubuntu-latest - if: (github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository) && !contains(github.event.head_commit.message, 'skip deploy') - steps: - - uses: actions/checkout@v4 - # Setup .npmrc file to publish to npm - - uses: actions/setup-node@v3 - with: - node-version: 20 - registry-url: "https://registry.npmjs.org" - # Defaults to the user or organization that owns the workflow file - scope: "nvuillam" - - run: yarn - - run: yarn prepack - - run: yarn test -# - name: Submitting code coverage to codecov -# run: | -# ./node_modules/.bin/nyc report --reporter text-lcov > coverage.lcov -# curl -s https://codecov.io/bash | bash + linux-unit-tests: + uses: salesforcecli/github-workflows/.github/workflows/unitTestsLinux.yml@main + with: + skipTsDepCheck: true + + windows-unit-tests: + uses: salesforcecli/github-workflows/.github/workflows/unitTestsWindows.yml@main + + nuts: + needs: + - linux-unit-tests + - windows-unit-tests + uses: salesforcecli/github-workflows/.github/workflows/nut.yml@main + secrets: inherit + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + fail-fast: false + with: + os: ${{ matrix.os }} diff --git a/.gitignore b/.gitignore index 48f3806f7..5f9d4708c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,38 +1,55 @@ -*-debug.log -*-error.log -/. -/.sfdx -/dist -/lib -/package-lock.json -/tmp -node_modules -report -sfdx-hardis-*.tgz +# -- CLEAN +tmp/ +# use yarn by default, so ignore npm +package-lock.json # MacOS system files .DS_Store .nyc_output/ -site/ +# never checking npm config +.npmrc -config/user/ +# debug logs +npm-error.log +yarn-error.log -hardis-report/ -.nyc_output/ +# compile source +lib -site/ +# test artifacts +*xunit.xml +*checkstyle.xml +*unitcoverage +.nyc_output +coverage +test_session* -config/user/ +# ignore sfdx-trust files +*.tgz +*.sig +package.json.bak. -megalinter-reports/ -.idea +npm-shrinkwrap.json +oclif.manifest.json +oclif.lock + +# -- CLEAN ALL +*.tsbuildinfo +.eslintcache +.wireit +node_modules -.wireit/ +# -- +# put files here you don't want cleaned with sf-clean -tsconfig.tsbuildinfo +# os specific files +.DS_Store +.idea -.eslintcache +hardis-report/ +megalinter-reports/ +config/user/ diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100644 index 000000000..c21981371 --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn commitlint --edit || true diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 000000000..e8e2bfbc0 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,5 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn lint # && yarn pretty-quick --staged # Removed because it destroys markdowns table data +node build.cjs \ No newline at end of file diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100644 index 000000000..56b63c314 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn build && yarn test diff --git a/.jscpd.json b/.jscpd.json index 2573b7443..cbf94fc9d 100644 --- a/.jscpd.json +++ b/.jscpd.json @@ -1,6 +1,9 @@ { "threshold": 0, - "reporters": ["html", "markdown"], + "reporters": [ + "html", + "markdown" + ], "ignore": [ "**/node_modules/**", "**/.git/**", @@ -19,6 +22,7 @@ "**/apex.ts", "**/access.ts", "**/audittrail.ts", + "**/auth.ts", "**/freeze.ts", "**/legacyapi.ts", "**/unfreeze.ts", @@ -28,4 +32,4 @@ "**/*.yml", "**/*.xml" ] -} +} \ No newline at end of file diff --git a/.lintstagedrc.cjs b/.lintstagedrc.cjs new file mode 100644 index 000000000..9a99d41cd --- /dev/null +++ b/.lintstagedrc.cjs @@ -0,0 +1,3 @@ +module.exports = { + '**/*.{js,json,md}?(x)': () => 'npm run reformat', +}; diff --git a/.mega-linter.yml b/.mega-linter.yml index dc12ffaaf..8a2bc7608 100644 --- a/.mega-linter.yml +++ b/.mega-linter.yml @@ -29,6 +29,7 @@ FILTER_REGEX_EXCLUDE: "(vscode|defaults|workarounds)" TYPESCRIPT_DEFAULT_STYLE: prettier DOCKERFILE_HADOLINT_ARGUMENTS: "--ignore DL3007 --ignore DL3016 --ignore DL3018 --ignore DL4006" REPOSITORY_GITLEAKS_DISABLE_ERRORS_IF_LESS_THAN: 11 +ACTION_ACTIONLINT_DISABLE_ERRORS_IF_LESS_THAN: 2 SHOW_ELAPSED_TIME: true FILEIO_REPORTER: false SPELL_LYCHEE_ARGUMENTS: diff --git a/.mocharc.json b/.mocharc.json new file mode 100644 index 000000000..8311bd3e1 --- /dev/null +++ b/.mocharc.json @@ -0,0 +1,8 @@ +{ + "require": ["ts-node/register"], + "watch-extensions": "ts", + "recursive": true, + "reporter": "spec", + "timeout": 600000, + "node-option": ["loader=ts-node/esm"] +} diff --git a/.nycrc b/.nycrc new file mode 100644 index 000000000..eeb8a0b18 --- /dev/null +++ b/.nycrc @@ -0,0 +1,7 @@ +{ + "check-coverage": true, + "lines": 50, + "statements": 50, + "functions": 50, + "branches": 50 +} diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 000000000..514ba1769 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1 @@ +"@salesforce/prettier-config" diff --git a/.sfdevrc.json b/.sfdevrc.json new file mode 100644 index 000000000..66121e198 --- /dev/null +++ b/.sfdevrc.json @@ -0,0 +1,10 @@ +{ + "test": { + "testsPath": "test/**/*.test.ts" + }, + "wireit": { + "test": { + "dependencies": ["test:compile", "test:only", "lint"] + } + } +} diff --git a/.vscode/launch.json b/.vscode/launch.json index 0786ae7e5..ba66bf715 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,36 +7,16 @@ { "type": "node", "request": "attach", - "name": "Attach to Remote", - "address": "127.0.0.1", + "name": "Attach", "port": 9229, - "localRoot": "${workspaceFolder}", - "remoteRoot": "${workspaceFolder}", - "disableOptimisticBPs": true, - "smartStep": true, - "skipFiles": [ - "/**", - "**/node_modules/**/*.js", - "**/node_modules/**/*.cjs" - ], - "windows": { - "skipFiles": [ - "C:\\**\\node_modules\\**\\*", - "/**/*" - ] - } + "skipFiles": ["/**"] }, { "name": "Run All Tests", "type": "node", "request": "launch", - "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", - "args": [ - "--inspect", - "--no-timeouts", - "--colors", - "test/**/*.test.ts" - ], + "program": "${workspaceFolder}/node_modules/mocha/bin/mocha", + "args": ["--inspect", "--colors", "test/**/*.test.ts"], "env": { "NODE_ENV": "development", "SFDX_ENV": "development" @@ -44,19 +24,14 @@ "sourceMaps": true, "smartStep": true, "internalConsoleOptions": "openOnSessionStart", - "preLaunchTask": "Compile" + "preLaunchTask": "Compile tests" }, { "type": "node", "request": "launch", "name": "Run Current Test", - "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", - "args": [ - "--inspect", - "--no-timeouts", - "--colors", - "${file}" - ], + "program": "${workspaceFolder}/node_modules/mocha/bin/mocha", + "args": ["--inspect", "--colors", "${file}"], "env": { "NODE_ENV": "development", "SFDX_ENV": "development" @@ -64,7 +39,7 @@ "sourceMaps": true, "smartStep": true, "internalConsoleOptions": "openOnSessionStart", - "preLaunchTask": "Compile" + "preLaunchTask": "Compile tests" } ] -} \ No newline at end of file +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 397f920b2..6b5e40ff9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,5 +10,8 @@ "**/lib": true, "**/bin": true }, - "sarif-viewer.connectToGithubCodeScanning": "off" -} + "editor.tabSize": 2, + "editor.formatOnSave": true, + "rewrap.wrappingColumn": 80, + "workbench.colorCustomizations": {} +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 5a0260e92..efbb147c5 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,9 +1,9 @@ { "version": "2.0.0", - "problemMatcher": "$tsc-watch", + "problemMatcher": "$tsc", "tasks": [ { - "label": "Compile", + "label": "Compile tests", "group": { "kind": "build", "isDefault": true @@ -14,44 +14,8 @@ "focus": false, "panel": "dedicated" }, - "args": ["run", "prepack"], - "isBackground": false, - "problemMatcher": { - "owner": "typescript", - "fileLocation": "relative", - "pattern": { - "regexp": "^(.*\\.ts):(\\d*):(\\d*)(\\s*-\\s*)(error|warning|info)\\s*(TS\\d*):\\s*(.*)$", - "file": 1, - "line": 2, - "column": 3, - "severity": 5, - "code": 6, - "message": 7 - } - } - }, - { - "label": "Lint", - "command": "yarn", - "type": "shell", - "presentation": { - "focus": false, - "panel": "dedicated" - }, - "args": ["run", "lint"], - "isBackground": false, - "problemMatcher": { - "owner": "typescript", - "fileLocation": "relative", - "pattern": { - "regexp": "^(ERROR|WARNING|INFO):\\s*(.*\\.ts):(\\d*):(\\d*)(\\s*-\\s*)(.*)$", - "file": 2, - "line": 3, - "column": 4, - "severity": 1, - "message": 6 - } - } + "args": ["run", "pretest"], + "isBackground": false } ] } diff --git a/CHANGELOG.md b/CHANGELOG.md index f7827674e..7e40854c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,156 @@ Note: Can be used with `sfdx plugins:install sfdx-hardis@beta` and docker image `hardisgroupcom/sfdx-hardis@beta` +## [5.0.0] 2024-09-23 + +### Refactoring explanations + +The future [deprecation of sfdx force:source:\*\* commands on 6 november](https://github.com/forcedotcom/cli/issues/2974) finally convinced us to switch everything from SFDX core to SF CLI core. (otherwise existing CI/CD pipelines would not work anymore from this date !) + +Therefore, sfdx-hardis required a complete refactoring as described below, but this won't impact existing CI/CD and Monitoring pipelines. + +We made many tests but risk zero do not exist, so if you see any bug, please report them ASAP and we'll solve them quickly :) + +### Major changes + +- Migrate plugin from SFDX plugin core to SF Cli Plugin core + + - [Convert commands code from SfdxCommand base to SfCommand base](https://github.com/salesforcecli/cli/wiki/Migrate-Plugins-Built-for-sfdx) + - Migrate internal Bulk Api calls from Bulk API v1 to Bulk API v2 + - Upgrade all npm dependencies to their latest version (more secured) + +- Change background calls to legacy sfdx commands to call their SF Cli replacements + + - `sfdx force:mdapi:convert` -> `sf project convert mdapi` + - `sfdx force:mdapi:deploy` -> `sf project deploy start --metadata-dir` + - `sfdx force:source:retrieve` -> `sf project retrieve start` + - `sfdx force:source:deploy` -> `sf project deploy start` + - `sfdx force:source:pull` -> `sf project retrieve start` + - `sfdx force:source:push` -> `sf project deploy start` + - `sfdx force:source:tracking:clear` -> `sf project delete tracking` + - `sfdx force:source:manifest:create` -> `sf project generate manifest` + - `sfdx sgd:source:delta` -> `sf sgd:source:delta` + - `sfdx force:org:create` -> `sf org create sandbox` | `sf org create scratch` + - `sfdx force:org:list` -> `sf org list` + - `sfdx force:org:delete` -> `sf org delete scratch` + - `sfdx config:get` -> `sf config get` + - `sfdx config:set` -> `sf config set` + - `sfdx auth:web:login` -> `sf org login web` + - `sfdx auth:jwt:grant` -> `sf org login jwt` + - `sfdx auth:sfdxurl:store` -> `sf org login sfdx-url` + - `sfdx org:login:device` -> `sf org login device` + - `sfdx force:data:record:get` -> `sf data get record` + - `sfdx force:data:record:update` -> `sf data update record` + - `sfdx force:data:soql:query` -> `sf data query` + - `sfdx force:data:bulk:delete` -> `sf data delete bulk` + - `sfdx alias:list` -> `sf alias list` + - `sfdx alias:set` -> `sf alias set` + - `sfdx force:apex:test:run` -> `sf apex run test` + - `sfdx force:apex:execute` -> `sf apex run` + - `sfdx force:package:create` -> `sf package create` + - `sfdx force:package:version:create` -> `sf package version create` + - `sfdx force:package:version:delete` -> `sf package version delete` + - `sfdx force:package:version:list` -> `sf package version list` + - `sfdx force:package:version:promote` -> `sf package version promote` + - `sfdx force:package:installed:list` -> `sf package installed` + - `sfdx force:package:install` -> `sf package install` + - `sfdx force:user:password:generate` -> `sf org generate password` + - `sfdx force:user:permset:assign` -> `sf org assign permset` + - `sfdx hardis:_` -> `sf hardis:_` + +- New wrappers commands for SF Cli deployment commands + - `sf hardis project deploy validate` -> Wraps `sf project deploy validate` + - `sf hardis project deploy quick` -> Wraps `sf project deploy quick` + - `sf hardis project deploy start` -> Wraps `sf project deploy start` + +### New Features / Enhancements + +- **hardis:project:deploy:smart** + - New feature **useSmartDeploymentTests**: Improve performances by not running test classes when delta deployment contain only non impacting metadatas, and target org is not production + - Rename command **hardis:project:deploy:source:dx** into **hardis:project:deploy:smart** (previous command alias remains, no need to update your pipelines !) +- **commandsPreDeploy** and **commandsPostDeploy** + - New option **context** for a command, defining when it is run and when it is not: **all** (default), **check-deployment-only** or **process-deployment-only** + - New option **runOnlyOnceByOrg**: If set to `true`, the command will be run only one time per org. A record of SfdxHardisTrace__c is stored to make that possible (it needs to be existing in target org) +- New commands + - **hardis:project:deploy:simulate** to validate the deployment of a single metadata (used by VsCode extension) + - **hardis:org:diagnose:releaseupdates** to check for org Release Updates from Monitoring or locally + - **hardis:misc:purge-references** to partially automate the cleaning of related dependencies when you need to delete a field, or change its type (for example from master detail to lookup) + - **hardis:project:clean:sensitive-metadatas** to mask sensitive metadatas from git repo (ex: Certificate content) +- **hardis:work:save** and **hardis:project:deploy:sources:dx**: Improve runtime performances thanks to internalization of sfdx-essentials commands +- **hardis:work:new** + - Allow to add labels in property `availableTargetBranches`, using a comma. For examples, `- integration,Choose this branch if you are on the BUILD side of the project !` + - Add current default org in the choices when prompting which org to use +- **hardis:project:new** + - Initialize autoCleanTypes with **destructivechanges**, **flowPositions** and **minimizeProfiles** + - Initialize package-no-overwrite.xml with Certificate metadata. (certificates must be uploaded manually) +- **hardis:org:files:export**: Improve display with spinner +- **hardis:org:purge:flow**: If FlowInterview records are preventing Flow Versions to be deleted, prompt user to delete Flow Interviews before trying again to delete Flow Versions +- **hardis:project:generate:gitdelta**: Add option to generate package.xml related to a single commit +- **hardis:org:data:delete**: Check for property "runnableInProduction" in export.json before running deletion in production org. +- **hardis:org:diagnose:audittrail**: Add new filtered actions + - Customer Portal: createdcustomersuccessuser +- Authentication: do not use alias MY_ORG anymore + do not update local user config if no values to replace. +- When selecting an org, make sure it is still connected. If not, open browser so the user can authenticate again. +- Update sfdx-hardis Grafana Dashboards to import in your Grafana Cloud + - SF Instance name + - Next platform upgrade + - Release Updates to check + - Installed packages + - Org licenses +- AI Deployment assistant + - Add error `Change Matching Rule` +- Git Providers + - On Pull Requests / Merge Requests comments, add hyperlinks to errors documentation URL + +### Fixes + +- Avoid error when removing obsolete flows (workaround using SF CLI if tooling api connection fails). Fixes [#662](https://github.com/hardisgroupcom/sfdx-hardis/issues/662) +- Improve Slack/Teams notifications display +- Display explicit error message in case a password is required to install a managed package. + +### Documentation + +- Reorganize README content + - Add link to Dreamforce 24 session +- Deployment assistant: Improve documentation by adding examples of errors, and a standalone page for each tip +- Factorize the definition of DOC_ROOT_URL + +### Deprecations + +- Deprecate wrapper commands matching sfdx commands that will be removed. All replaced by sf hardis deploy start + + - `sfdx hardis:source:push` + - `sfdx hardis:source:deploy` + - `sfdx hardis:mdapi:retrieve` + - `sfdx hardis:mdapi:deploy` + +- Deprecate `hardis:deploy:sources:metadata` as nobody uses metadata format anymore + +### Removals + +- Replace puppeteer by puppeteer-core: it means that if you use a command requiring puppeteer, please make sure to have a Chrome available in your environment (already integrated within the Docker image) + +- Get rid of [sfdx-essentials](https://github.com/nvuillam/sfdx-essentials) plugin dependency by internalizing its used commands + + - `sf hardis:packagexml:append` + - `sf hardis:packagexml:remove` + - `sf hardis:project:clean:filter-xml-content` + +- Remove npm dependencies (some of them not maintained anymore) + + - @adobe/node-fetch-retry + - @amplitude/node + - @keyv/redis + - @oclif/command + - @oclif/config + - @oclif/errors + - @salesforce/command + - @salesforce/ts-types + - find-package-json + - node-fetch + +- Remove not used keyValueStores to keep only Salesforce one + ## [4.53.0] 2024-08-20 - Upgrade workflows to Node 20 (fixes ) @@ -66,7 +216,6 @@ Note: Can be used with `sfdx plugins:install sfdx-hardis@beta` and docker image - [hardis:project:deploy:sources:dx](https://sfdx-hardis.cloudity.com/hardis/project/deploy/sources/dx/): Allow new mode for running test during deployments: **RunRepositoryTestsExceptSeeAllData** (⚠️ Use with caution !) - ## [4.47.0] 2024-07-22 - Update emojis in prompts to make them more visible @@ -177,7 +326,7 @@ commandsPostDeploy: ## [4.38.2] 2024-06-06 - Fix npm packages installation for GitHub monitoring to avoid random failures -- Add _notifKey in Grafana notifications to be able to build unique alerts +- Add \_notifKey in Grafana notifications to be able to build unique alerts ## [4.38.1] 2024-06-04 @@ -240,7 +389,7 @@ commandsPostDeploy: ## [4.34.1] 2024-05-13 -- Notifications org identifier: replace dot by __ to avoid mess with Grafana label filters +- Notifications org identifier: replace dot by \_\_ to avoid mess with Grafana label filters ## [4.34.0] 2024-05-12 @@ -282,6 +431,7 @@ commandsPostDeploy: ## [4.32.0] 2024-04-24 - Enhance [BitBucket Integration](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-integrations-bitbucket/), by @Alainbates in + - Deployment status in Pull Request comments - Quick Deploy to enhance performance diff --git a/Dockerfile b/Dockerfile index 6035268fd..7cd5eb284 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # Docker image to run sfdx-hardis -FROM alpine:3.18 +FROM alpine:3.20 LABEL maintainer="Nicolas VUILLAMY " @@ -27,11 +27,10 @@ ARG SFDX_HARDIS_VERSION=latest RUN npm install --no-cache yarn -g && \ npm install --no-cache @salesforce/cli@${SFDX_CLI_VERSION} -g && \ sf plugins install @salesforce/plugin-packaging && \ - echo 'y' | sfdx plugins:install sfdx-hardis@${SFDX_HARDIS_VERSION} && \ - echo 'y' | sfdx plugins:install sfdmu && \ - echo 'y' | sfdx plugins:install sfdx-git-delta && \ - echo 'y' | sfdx plugins:install sfdx-essentials && \ - echo 'y' | sfdx plugins:install texei-sfdx-plugin && \ + echo 'y' | sf plugins install sfdx-hardis@${SFDX_HARDIS_VERSION} && \ + echo 'y' | sf plugins install sfdmu && \ + echo 'y' | sf plugins install sfdx-git-delta && \ + echo 'y' | sf plugins install texei-sfdx-plugin && \ sf version --verbose --json && \ rm -rf /root/.npm/_cacache diff --git a/README.md b/README.md index 86db80787..7a687ea34 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ + [![sfdx-hardis by Cloudity Banner](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/sfdx-hardis-banner.png)](https://sfdx-hardis.cloudity.com) # sfdx-hardis -[_Presented at Dreamforce 23!_](https://reg.salesforce.com/flow/plus/df23/sessioncatalog/page/catalog/session/1684196389783001OqEl) +_Presented at_ [_Dreamforce 23_](https://reg.salesforce.com/flow/plus/df23/sessioncatalog/page/catalog/session/1684196389783001OqEl) **_and soon at [_Dreamforce 24!_](https://reg.salesforce.com/flow/plus/df24/sessioncatalog/page/catalog/session/1718915808069001Q7HH)_** [![Version](https://img.shields.io/npm/v/sfdx-hardis.svg)](https://npmjs.org/package/sfdx-hardis) [![Downloads/week](https://img.shields.io/npm/dw/sfdx-hardis.svg)](https://npmjs.org/package/sfdx-hardis) @@ -16,25 +17,29 @@ [![License](https://img.shields.io/npm/l/sfdx-hardis.svg)](https://github.com/hardisgroupcom/sfdx-hardis/blob/main/package.json) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) -Toolbox for Salesforce DX, by [Cloudity](https://cloudity.com/), natively compliant with most platforms and tools +Toolbox for Salesforce DX, by [**Cloudity**](https://cloudity.com/) & friends, natively compliant with most platforms and tools. ![Native Integrations](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/integrations.png) It will allow you to: - Do with simple commands what could be done manually in minutes/hours -- [Define a complete CI/CD Pipeline for your Salesforce project](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-home/) -- [Backup Metadatas and monitor any Salesforce org](https://sfdx-hardis.cloudity.com/salesforce-monitoring-home/) +- [Define a **ready to use CI/CD Pipeline** for your Salesforce project](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-home/) +- [**Backup Metadatas** and **monitor any Salesforce org**](https://sfdx-hardis.cloudity.com/salesforce-monitoring-home/) + +[_Please see the full list of commands in Online documentation_](https://sfdx-hardis.cloudity.com) -[**Please see the full list of commands in Online documentation**](https://sfdx-hardis.cloudity.com) +___ **sfdx-hardis** commands are also available with UI in [**SFDX Hardis Visual Studio Code Extension**](https://marketplace.visualstudio.com/items?itemName=NicolasVuillamy.vscode-sfdx-hardis) [![VsCode SFDX Hardis](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/extension-demo.gif)](https://marketplace.visualstudio.com/items?itemName=NicolasVuillamy.vscode-sfdx-hardis) +___ + _See Dreamforce presentation_ -[![See Dreamforce presentation](https://img.youtube.com/vi/o0Mm9F07UFs/0.jpg)](https://www.youtube.com/watch?v=o0Mm9F07UFs) +[![See Dreamforce presentation](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/play-dreamforce-session.png)](https://www.youtube.com/watch?v=o0Mm9F07UFs) ## Installation @@ -44,7 +49,9 @@ You can install [Visual Studio Code](https://code.visualstudio.com/) extension [ Once installed, click on ![Hardis Group button](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/hardis-button.jpg) in VsCode left bar, and follow the additional installation instructions -[![Installation tutorial](https://img.youtube.com/vi/LA8m-t7CjHA/0.jpg)](https://www.youtube.com/watch?v=LA8m-t7CjHA) +[![Installation tutorial](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/play-install-tuto.png)](https://www.youtube.com/watch?v=LA8m-t7CjHA) + +___ ### As SFDX Plugin @@ -56,26 +63,28 @@ Once installed, click on ![Hardis Group button](https://github.com/hardisgroupco #### Plugin installation ```sh-session -sfdx plugins:install sfdx-hardis +sf plugins install sfdx-hardis ``` For advanced use, please also install dependencies ```sh-session sf plugins install @salesforce/plugin-packaging -sfdx plugins:install sfdmu -sfdx plugins:install sfdx-git-delta -sfdx plugins:install sfdx-essentials -sfdx plugins:install texei-sfdx-plugin +sf plugins install sfdmu +sf plugins install sfdx-git-delta +sf plugins install texei-sfdx-plugin ``` -If you are using CI/CD scripts, use `echo y | sfdx plugins:install ...` to bypass prompt. +If you are using CI/CD scripts, use `echo y | sf plugins install ...` to bypass prompt. + +___ ### Docker You can use sfdx-hardis docker images to run in CI - Docker Hub + - [**hardisgroupcom/sfdx-hardis:latest**](https://hub.docker.com/r/hardisgroupcom/sfdx-hardis) (with latest @salesforce/cli version) - [**hardisgroupcom/sfdx-hardis:latest-sfdx-recommended**](https://hub.docker.com/r/hardisgroupcom/sfdx-hardis) (with recommended @salesforce/cli version, in case the latest version of @salesforce/cli is buggy) @@ -88,7 +97,7 @@ _See [Dockerfile](https://github.com/hardisgroupcom/sfdx-hardis/blob/main/Docker ## Usage ```sh-session -sfdx hardis: +sf hardis: ``` ## Articles @@ -116,7 +125,7 @@ Here are some articles about [sfdx-hardis](https://sfdx-hardis.cloudity.com/) ## Contributing -Anyone is welcome to contribute to this sfdx-hardis +Everyone is welcome to contribute to sfdx-hardis (even juniors: we'll assist you !) - Install Node.js ([recommended version](https://nodejs.org/en/)) - Install typescript by running `npm install typescript --global` @@ -127,14 +136,13 @@ Anyone is welcome to contribute to this sfdx-hardis - Run `yarn` to install dependencies - Run `sf plugins link` to link the local sfdx-hardis to SFDX CLI - Run `tsc --watch` to transpile typescript into js everytime you update a TS file -- Debug commands using `NODE_OPTIONS=--inspect-brk sfdx hardis:somecommand -someparameter somevalue` +- Debug commands using `NODE_OPTIONS=--inspect-brk sf hardis:somecommand -someparameter somevalue` ## Dependencies **sfdx-hardis** partially relies on the following SFDX Open-Source packages - [Salesforce Data Move Utility](https://github.com/forcedotcom/SFDX-Data-Move-Utility) -- [SFDX Essentials](https://github.com/nvuillam/sfdx-essentials) - [SFDX Git Delta](https://github.com/scolladon/sfdx-git-delta) - [Texei Sfdx Plugin](https://github.com/texei/texei-sfdx-plugin) @@ -146,10 +154,6 @@ Anyone is welcome to contribute to this sfdx-hardis ## Commands -[**Read Online Documentation**](https://sfdx-hardis.cloudity.com) - - - - +[**Read Online Documentation to see everything you can do with SFDX Hardis :)**](https://sfdx-hardis.cloudity.com) \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md index d5b3b3322..e632af428 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -14,8 +14,4 @@ currently being supported with security updates. ## Reporting a Vulnerability -Use this section to tell people how to report a vulnerability. - -Tell them where to go, how often they can expect to get an update on a -reported vulnerability, what to expect if the vulnerability is accepted or -declined, etc. +In case of detected vulnerability, please write directly to [Nicolas Vuillamy on LinkedIn](https://www.linkedin.com/in/nicolas-vuillamy/) diff --git a/bin/dev.cmd b/bin/dev.cmd new file mode 100644 index 000000000..cec553be4 --- /dev/null +++ b/bin/dev.cmd @@ -0,0 +1,3 @@ +@echo off + +node --loader ts-node/esm --no-warnings=ExperimentalWarning "%~dp0\dev" %* diff --git a/bin/dev.js b/bin/dev.js new file mode 100644 index 000000000..89a549a7f --- /dev/null +++ b/bin/dev.js @@ -0,0 +1,8 @@ +#!/usr/bin/env -S node --loader ts-node/esm --no-warnings=ExperimentalWarning +// eslint-disable-next-line node/shebang +async function main() { + const { execute } = await import('@oclif/core'); + await execute({ development: true, dir: import.meta.url }); +} + +await main(); diff --git a/bin/run b/bin/run deleted file mode 100644 index 3c4ae3ac0..000000000 --- a/bin/run +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env node - -require('@oclif/command').run() -.catch(require('@oclif/errors/handle')) diff --git a/bin/run.js b/bin/run.js new file mode 100755 index 000000000..cf13fb937 --- /dev/null +++ b/bin/run.js @@ -0,0 +1,9 @@ +#!/usr/bin/env node + +// eslint-disable-next-line node/shebang +async function main() { + const { execute } = await import('@oclif/core'); + await execute({ dir: import.meta.url }); +} + +await main(); diff --git a/build.cjs b/build.cjs new file mode 100644 index 000000000..59b01d202 --- /dev/null +++ b/build.cjs @@ -0,0 +1,115 @@ +#!/usr/bin/node +/* eslint-disable */ +const fs = require("fs-extra"); + +class SfdxHardisBuilder { + async run() { + console.log("Start additional building of sfdx-hardis repository..."); + await this.buildDeployTipsDoc(); + this.truncateReadme(); + } + + async buildDeployTipsDoc() { + console.log("Building salesforce-deployment-assistant-error-list.md doc..."); + const deployTipsDocFile = "./docs/salesforce-deployment-assistant-error-list.md"; + const { getAllTips } = await import("./lib/common/utils/deployTipsList.js"); + const deployTips = getAllTips(); + const deployTipsMd = [ + "---", + "title: Sfdx-hardis deployment assistant list of errors", + "description: List of errors that are handled by sfdx-hardis deployment assistant", + "---", + "", + "", + "# Salesforce deployment assistant errors list", + "", + "sfdx-hardis can help solve solve deployment errors using a predefined list of issues and associated solutions", + "", + "See how to [setup sfdx-hardis deployment assistant](salesforce-deployment-assistant-setup.md)", + "", + "If you see a deployment error which is not here yet, please [add it in this file](https://github.com/hardisgroupcom/sfdx-hardis/blob/main/src/common/utils/deployTipsList.ts) :)", + "" + ]; + for (const tip of deployTips) { + const linkName = `sf-deployment-assistant/${tip.label.replace(/[^a-zA-Z0-9 -]|\s/g, '-')}.md` + const tipFile = `./docs/` + linkName; + this.buildIndividualMarkdownPageForTip(tip, tipFile); + this.buildMainDeployFixesMarkdown(tip, deployTipsMd, linkName); + } + fs.writeFileSync(deployTipsDocFile, deployTipsMd.join("\n") + "\n"); + console.log("Written doc file " + deployTipsDocFile); + } + + buildMainDeployFixesMarkdown(tip, deployTipsMd, linkName) { + if (!tip.label) { + throw new Error(`Missing label for ${JSON.stringify(tip)}`); + } + deployTipsMd.push(`## [${tip.label}](${linkName})`); + deployTipsMd.push(...["", "**Detection**", ""]); + if (tip.expressionRegex) { + deployTipsMd.push(...tip.expressionRegex.map((regEx) => "- RegExp: `" + regEx.toString().slice(1).replace("/gm", "") + "`")); + } + if (tip.expressionString) { + deployTipsMd.push(...tip.expressionString.map((str) => "- String: `" + str + "`")); + } + if (tip.examples) { + deployTipsMd.push(...["", "**Examples**", ""]); + deployTipsMd.push(...tip.examples.map((str) => "- `" + str + "`")); + } + deployTipsMd.push(...["", "**Resolution**", ""]); + if (!tip.tip) { + throw new Error(`Missing tip for ${JSON.stringify(tip)}`); + } + deployTipsMd.push("```shell"); + deployTipsMd.push(...tip.tip.split("\n")); + deployTipsMd.push("```"); + deployTipsMd.push(""); + deployTipsMd.push("---"); + } + + buildIndividualMarkdownPageForTip(tip, tipFile) { + const errorDescription = tip?.examples?.length > 0 ? tip.examples[0] : + tip?.expressionString?.length > 0 ? tip?.expressionString[0] : + tip.expressionRegex[0] + const tipFileMd = [ + "---", + `title: : ${tip.label} (Deployment assistant)`, + `description: How to solve Salesforce deployment error ${errorDescription}`, + "---", + "" + ]; + tipFileMd.push(`# ${tip.label}`); + tipFileMd.push(...["", "## Detection", ""]); + if (tip.expressionRegex) { + tipFileMd.push(...tip.expressionRegex.map((regEx) => "- RegExp: `" + regEx.toString().slice(1).replace("/gm", "") + "`")); + } + if (tip.expressionString) { + tipFileMd.push(...tip.expressionString.map((str) => "- String: `" + str + "`")); + } + if (tip.examples) { + tipFileMd.push(...["", "## Examples", ""]); + tipFileMd.push(...tip.examples.map((str) => "- `" + str + "`")); + } + tipFileMd.push(...["", "## Resolution", ""]); + if (!tip.tip) { + throw new Error(`Missing tip for ${JSON.stringify(tip)}`); + } + tipFileMd.push("```shell"); + tipFileMd.push(...tip.tip.split("\n")); + tipFileMd.push("```"); + fs.writeFileSync(tipFile, tipFileMd.join("\n") + "\n"); + } + + truncateReadme() { + const readmeFile = "./README.md"; + const readmeContent = fs.readFileSync(readmeFile, "utf-8"); + const chunks = readmeContent.split("") + fs.writeFileSync(readmeFile, chunks[0] + ""); + console.log("Removed README.md commands"); + } +} + +(async () => { + await new SfdxHardisBuilder().run(); +})(); + diff --git a/build.js b/build.js deleted file mode 100644 index 3f8015f9c..000000000 --- a/build.js +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/node -/* eslint-disable */ -const fs = require("fs-extra"); -const { getAllTips } = require("./lib/common/utils/deployTipsList"); - -class SfdxHardisBuilder { - run() { - console.log("Start additional building of sfdx-hardis repository..."); - this.buildDeployTipsDoc(); - this.truncateReadme(); - } - - buildDeployTipsDoc() { - console.log("Building salesforce-deployment-assistant-error-list.md doc..."); - const deployTipsDocFile = "./docs/salesforce-deployment-assistant-error-list.md"; - const deployTips = getAllTips(); - const deployTipsMd = [ - "---", - "title: Sfdx-hardis deployment assistant list of errors", - "description: List of errors that are handled by sfdx-hardis deployment assistant", - "---", - "", - "", - "# Salesforce deployment assistant errors list", - "", - "sfdx-hardis can help solve solve deployment errors using a predefined list of issues and associated solutions", - "", - "See how to [setup sfdx-hardis deployment assistant](salesforce-deployment-assistant-setup.md)", - "", - "If you see a deployment error which is not here yet, please [add it in this file](https://github.com/hardisgroupcom/sfdx-hardis/blob/main/src/common/utils/deployTipsList.ts) :)", - "" - ]; - for (const tip of deployTips) { - if (!tip.label) { - throw new Error(`Missing label for ${JSON.stringify(tip)}`); - } - deployTipsMd.push(`## ${tip.label}`); - deployTipsMd.push(""); - if (tip.expressionRegex) { - deployTipsMd.push(...tip.expressionRegex.map((regEx) => "- `" + regEx.toString().slice(1).replace("/gm", "") + "`")); - } - if (tip.expressionString) { - deployTipsMd.push(...tip.expressionString.map((str) => "- `" + str + "`")); - } - deployTipsMd.push(...["", "**Resolution tip**", ""]); - if (!tip.tip) { - throw new Error(`Missing tip for ${JSON.stringify(tip)}`); - } - deployTipsMd.push("```shell"); - deployTipsMd.push(...tip.tip.split("\n")); - deployTipsMd.push("```"); - deployTipsMd.push(""); - deployTipsMd.push("---"); - } - fs.writeFileSync(deployTipsDocFile, deployTipsMd.join("\n") + "\n"); - console.log("Written doc file " + deployTipsDocFile); - } - - truncateReadme() { - const readmeFile = "./README.md"; - const readmeContent = fs.readFileSync(readmeFile, "utf-8"); - const chunks = readmeContent.split("") - fs.writeFileSync(readmeFile, chunks[0]+"\n\n"); - console.log("Removed README.md commands"); - } -} - -new SfdxHardisBuilder().run(); diff --git a/commitlint.config.cjs b/commitlint.config.cjs new file mode 100644 index 000000000..422b19445 --- /dev/null +++ b/commitlint.config.cjs @@ -0,0 +1 @@ +module.exports = { extends: ['@commitlint/config-conventional'] }; diff --git a/config/sfdx-hardis.jsonschema.json b/config/sfdx-hardis.jsonschema.json index 582c8b227..8126dadcb 100644 --- a/config/sfdx-hardis.jsonschema.json +++ b/config/sfdx-hardis.jsonschema.json @@ -14,12 +14,22 @@ "UNUSED_METADATAS", "METADATA_STATUS", "MISSING_ATTRIBUTES", - "UNUSED_LICENSES" + "UNUSED_LICENSES", + "RELEASE_UPDATES" ], "type": "string" }, "enum_monitoring_commands": { - "enum": ["AUDIT_TRAIL", "LEGACY_API", "LINT_ACCESS", "UNUSED_METADATAS", "METADATA_STATUS", "MISSING_ATTRIBUTES", "UNUSED_LICENSES"], + "enum": [ + "AUDIT_TRAIL", + "LEGACY_API", + "LINT_ACCESS", + "UNUSED_METADATAS", + "METADATA_STATUS", + "MISSING_ATTRIBUTES", + "UNUSED_LICENSES", + "RELEASE_UPDATES" + ], "type": "string" } }, @@ -28,10 +38,17 @@ "allowedOrgTypes": { "$id": "#/properties/allowedOrgTypes", "description": "Types of orgs allowed for config & development. If not set, sandbox and scratch are allowed by default", - "examples": [["sandbox"]], + "examples": [ + [ + "sandbox" + ] + ], "items": { "type": "string", - "enum": ["sandbox", "scratch"] + "enum": [ + "sandbox", + "scratch" + ] }, "title": "Allowed org types", "type": "array" @@ -39,7 +56,13 @@ "autoCleanTypes": { "$id": "#/properties/autoCleanTypes", "description": "When saving a sfdx-hardis task, the list of cleanings will be automatically applied to sfdx sources", - "examples": [["dashboards", "datadotcom", "destructivechanges"]], + "examples": [ + [ + "dashboards", + "datadotcom", + "destructivechanges" + ] + ], "items": { "type": "string", "enum": [ @@ -53,6 +76,7 @@ "listViewsMine", "minimizeProfiles", "productrequest", + "sensitiveMetadatas", "systemDebug", "v60" ] @@ -63,7 +87,13 @@ "autoRemoveUserPermissions": { "$id": "#/properties/autoRemoveUserPermissions", "description": "When saving a sfdx-hardis task, these permissions will be removed from profiles", - "examples": [["EnableCommunityAppLauncher", "FieldServiceAccess", "OmnichannelInventorySync"]], + "examples": [ + [ + "EnableCommunityAppLauncher", + "FieldServiceAccess", + "OmnichannelInventorySync" + ] + ], "items": { "type": "string" }, @@ -73,7 +103,15 @@ "autoRetrieveWhenPull": { "$id": "#/properties/autoRetrieveWhenPull", "description": "When calling hardis:scratch:pull, if you define metadatas (named or not), they will also be retrieved using force:source:retrieve", - "examples": [["CustomApplication"], ["CustomApplication:MyApp1", "CustomApplication:MyApp2"]], + "examples": [ + [ + "CustomApplication" + ], + [ + "CustomApplication:MyApp1", + "CustomApplication:MyApp2" + ] + ], "items": { "type": "string" }, @@ -84,14 +122,23 @@ "$id": "#/properties/apexTestsMinCoverageOrgWide", "default": 75.0, "description": "Minimum percentage of apex code coverage accepted", - "examples": [80.0, 95.0], + "examples": [ + 80.0, + 95.0 + ], "title": "Minimum apex test coverage %", "type": "number" }, "availableProjects": { "$id": "#/properties/availableProjects", "description": "List of business projects that are managed in the same repository. Will be used to build git branch name when using hardis:work:new", - "examples": [["sales_cloud", "service_cloud", "community"]], + "examples": [ + [ + "sales_cloud", + "service_cloud", + "community" + ] + ], "items": { "type": "string" }, @@ -101,7 +148,12 @@ "availableTargetBranches": { "$id": "#/properties/availableTargetBranches", "description": "List of git branches that can be used as target for merge requests", - "examples": [["develop", "develop_next"]], + "examples": [ + [ + "develop", + "develop_next" + ] + ], "items": { "type": "string" }, @@ -146,11 +198,15 @@ [ { "globPattern": "/**/*.flexipage-meta.xml", - "xpaths": ["//ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')]"] + "xpaths": [ + "//ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')]" + ] }, { "globPattern": "/**/*.layout-meta.xml", - "xpaths": ["//ns:relatedLists//ns:relatedList[contains(text(),'RelatedSolutionList')]"] + "xpaths": [ + "//ns:relatedLists//ns:relatedList[contains(text(),'RelatedSolutionList')]" + ] } ] ], @@ -164,14 +220,20 @@ "globPattern": { "$id": "#/properties/cleanXmlPatterns/items/properties/globPattern", "description": "Glob pattern to identify XML files to clean", - "examples": ["/**/*.flexipage-meta.xml"], + "examples": [ + "/**/*.flexipage-meta.xml" + ], "title": "Glob pattern", "type": "string" }, "xpaths": { "$id": "#/properties/cleanXmlPatterns/items/properties/xpaths", "description": "XPaths to identify elements to remove", - "examples": [["//ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')]"]], + "examples": [ + [ + "//ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')]" + ] + ], "title": "XPath list", "items": { "type": "string" @@ -179,7 +241,10 @@ "type": "array" } }, - "required": ["globPattern", "xpaths"] + "required": [ + "globPattern", + "xpaths" + ] }, "title": "Clean XML Patterns", "type": "array" @@ -228,7 +293,11 @@ }, "title": "Command" }, - "required": ["id", "label", "command"], + "required": [ + "id", + "label", + "command" + ], "title": "Commands Pre-Deployment", "type": "array" }, @@ -273,6 +342,23 @@ "title": "Command", "type": "string" }, + "context": { + "$id": "#/properties/commandsPostDeploy/items/properties/context", + "description": "Context when the command must be run", + "title": "Context", + "default": "all", + "type": "string", + "enum": [ + "all", + "check-deployment-only", + "process-deployment-only" + ], + "examples": [ + "all", + "check-deployment-only", + "process-deployment-only" + ] + }, "skipIfError": { "$id": "#/properties/commandsPostDeploy/items/properties/skipIfError", "description": "Do not run the command if there is a deployment error", @@ -281,7 +367,11 @@ "type": "boolean" } }, - "required": ["id", "label", "command"], + "required": [ + "id", + "label", + "command" + ], "title": "Command" }, "title": "Commands Post-Deployment", @@ -301,14 +391,14 @@ "label": "Generate manifest", "icon": "file.svg", "tooltip": "Generates a manifest package.xml using local sfdx source files", - "command": "sfdx force:source:manifest:create --sourcepath force-app --manifestname myNewManifest" + "command": "sf project generate manifest --source-path force-app --name myNewManifest" }, { "id": "list-all-orgs", "label": "List all orgs", "icon": "salesforce.svg", "tooltip": "List all orgs that has already been authenticated using sfdx", - "command": "sfdx force:org:list --all" + "command": "sf org list --all" } ] }, @@ -391,13 +481,20 @@ "type": "string" } }, - "required": ["id", "label", "command"] + "required": [ + "id", + "label", + "command" + ] }, "title": "Label", "type": "array" } }, - "required": ["id", "label"] + "required": [ + "id", + "label" + ] }, "title": "Custom commands", "type": "array" @@ -406,8 +503,14 @@ "$id": "#/properties/customCommandsPosition", "description": "Position of custom commands in the menu (first or last)", "default": "first", - "enum": ["first", "last"], - "examples": ["first", "last"], + "enum": [ + "first", + "last" + ], + "examples": [ + "first", + "last" + ], "title": "Custom commands position", "type": "string" }, @@ -442,14 +545,20 @@ "name": { "$Id": "#/properties/customPlugins/items/properties/name", "description": "Name of the plugin npm package", - "examples": ["mo-dx-plugin", "shane-sfdx-plugins"], + "examples": [ + "mo-dx-plugin", + "shane-sfdx-plugins" + ], "title": "Name", "type": "string" }, "helpUrl": { "$Id": "#/properties/customPlugins/items/properties/helpUrl", "description": "Url of plugin documentation", - "examples": ["https://github.com/msrivastav13/mo-dx-plugin", "https://github.com/mshanemc/shane-sfdx-plugins"], + "examples": [ + "https://github.com/msrivastav13/mo-dx-plugin", + "https://github.com/mshanemc/shane-sfdx-plugins" + ], "title": "Name", "type": "string" } @@ -481,7 +590,10 @@ "type": "boolean" } }, - "required": ["dataPath", "importInScratchOrgs"] + "required": [ + "dataPath", + "importInScratchOrgs" + ] }, "title": "Data packages", "type": "array" @@ -489,7 +601,11 @@ "defaultPackageInstallationKey": { "$id": "#/properties/defaultPackageInstallationKey", "description": "When generating a new package version protected with password, use this value as default package installation key", - "examples": ["hardis", "myPassword", "dFGGF43656YfdFDG{{{dhgfh:::;FSEDSFd78"], + "examples": [ + "hardis", + "myPassword", + "dFGGF43656YfdFDG{{{dhgfh:::;FSEDSFd78" + ], "title": "Defaut package installation key", "type": "string" }, @@ -497,7 +613,11 @@ "$id": "#/properties/developmentBranch", "default": "developpement", "description": "When creating a new sfdx-hardis task, this git branch is used as base to create the feature/debug sub branch. The merge request will later have this branch as target.", - "examples": ["developpement", "dev_lot2", "hotfixes"], + "examples": [ + "developpement", + "dev_lot2", + "hotfixes" + ], "title": "Default pull/merge request target org", "type": "string" }, @@ -551,38 +671,55 @@ "properties": { "dataPath": { "$id": "#/properties/install/properties/packages/items/dataPath", - "examples": ["scripts/data/EmailTemplate"], + "examples": [ + "scripts/data/EmailTemplate" + ], "title": "Path to SFDMU data project to deploy", "type": "string" }, "label": { "$id": "#/properties/install/properties/packages/items/label", - "examples": ["Deploy EmailTemplate", "Import EmailTemplate records"], + "examples": [ + "Deploy EmailTemplate", + "Import EmailTemplate records" + ], "title": "Source or data package label", "type": "string" }, "order": { "$id": "#/properties/install/properties/packages/items/order", - "examples": [-20, 13, 50], + "examples": [ + -20, + 13, + 50 + ], "title": "Execution order in deployment plan", "type": "number" }, "packageXmlFile": { "$id": "#/properties/install/properties/packages/items/packageXmlFile", - "examples": ["manifest/splits/packageXmlEmails.xml"], + "examples": [ + "manifest/splits/packageXmlEmails.xml" + ], "title": "Path to package.xml file to use for deployment", "type": "string" }, "waitAfter": { "$id": "#/properties/install/properties/packages/items/waitAfter", "description": "Delay to wait before installing the next package", - "examples": [10, 20], + "examples": [ + 10, + 20 + ], "title": "Wait after install (seconds)", "type": "number" } } }, - "required": ["label", "order"], + "required": [ + "label", + "order" + ], "title": "List of packages to deploy", "type": "array" } @@ -594,7 +731,11 @@ "$id": "#/properties/devHubAlias", "default": "", "description": "Dev Hub alias, usually DevHub_ProjectName", - "examples": ["DevHub_MyClientMyProject", "DevHub_GoogleGmail", "DevHub_AppleIWatch"], + "examples": [ + "DevHub_MyClientMyProject", + "DevHub_GoogleGmail", + "DevHub_AppleIWatch" + ], "title": "Dev Hub org alias", "type": "string" }, @@ -602,31 +743,51 @@ "$id": "#/properties/devHubUsername", "default": "", "description": "Dev Hub username, used to authenticate to DevHub from CI jobs", - "examples": ["cicd-user@myclient.com", "scratch-user@google.fr", "scratch-user@apple.fr"], + "examples": [ + "cicd-user@myclient.com", + "scratch-user@google.fr", + "scratch-user@apple.fr" + ], "title": "Dev Hub Username", "type": "string" }, "extends": { "$id": "#/properties/extends", "description": "You can base your local sfdx-hardis configuration on a remote config file. That allows you to have the same config base for all your projects", - "examples": ["https://raw.githubusercontent.com/worldcompany/shared-config/main/.sfdx-hardis.yml"], + "examples": [ + "https://raw.githubusercontent.com/worldcompany/shared-config/main/.sfdx-hardis.yml" + ], "title": "Extends remote configuration URL", "type": "string" }, "initPermissionSets": { "$id": "#/properties/initPermissionSets", "description": "When creating a scratch org, Admin user will be automatically assigned to those permission sets", - "examples": [["MyPermissionSet", "MyPermissionSetGroup"]], + "examples": [ + [ + "MyPermissionSet", + "MyPermissionSetGroup" + ] + ], "items": { "$id": "#/properties/initPermissionSets/items", - "type": ["object", "string"], + "type": [ + "object", + "string" + ], "additionalProperties": false, "description": "Permission Set or Permission Set Group", "properties": { "name": { "$Id": "#/properties/initPermissionSets/items/properties/name", "description": "Permission Set or Permission Set Group name", - "examples": [["MyPermissionSet", "MyPermissionSetGroup", "MyPermissionSetGroup2"]], + "examples": [ + [ + "MyPermissionSet", + "MyPermissionSetGroup", + "MyPermissionSetGroup2" + ] + ], "title": "Name", "type": "string" } @@ -673,43 +834,60 @@ "properties": { "Id": { "$Id": "#/properties/install/properties/packages/items/Id", - "examples": ["0A35r000000GveVCAS"], + "examples": [ + "0A35r000000GveVCAS" + ], "title": "(unused) PackageId", "type": "string" }, "SubscriberPackageId": { "$Id": "#/properties/install/properties/packages/items/SubscriberPackageId", - "examples": ["033b0000000Pf2AAAS"], + "examples": [ + "033b0000000Pf2AAAS" + ], "title": "Subscriber Package Id", "type": "string" }, "SubscriberPackageName": { "$Id": "#/properties/install/properties/packages/items/SubscriberPackageName", - "examples": ["Files Attachment Notes"], + "examples": [ + "Files Attachment Notes" + ], "title": "Subscriber Package Name", "type": "string" }, "SubscriberPackageNamespace": { "$Id": "#/properties/install/properties/packages/items/SubscriberPackageNamespace", - "examples": ["fan_astrea"], + "examples": [ + "fan_astrea" + ], "title": "Subscriber Package NameSpace", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "SubscriberPackageVersionId": { "$Id": "#/properties/install/properties/packages/items/SubscriberPackageVersionId", - "examples": ["04t0o000003nRWAAA2"], + "examples": [ + "04t0o000003nRWAAA2" + ], "title": "Subscriber Version Id (IMPORTANT)", "type": "string" }, "SubscriberPackageVersionName": { "$Id": "#/properties/install/properties/packages/items/SubscriberPackageVersionName", - "examples": ["Summer2021"], + "examples": [ + "Summer2021" + ], "title": "Subscriber Version Name", "type": "string" }, "SubscriberPackageVersionNumber": { "$Id": "#/properties/install/properties/packages/items/SubscriberPackageVersionNumber", - "examples": ["1.22.0.2"], + "examples": [ + "1.22.0.2" + ], "title": "Subscriber Version Number", "type": "string" }, @@ -717,7 +895,10 @@ "$Id": "#/properties/install/properties/packages/items/installDuringDeployments", "default": false, "description": "If true, during deployments this package will be installed in target org if not installed yet", - "examples": [true, false], + "examples": [ + true, + false + ], "title": "Install during deployments", "type": "boolean" }, @@ -725,19 +906,27 @@ "$Id": "#/properties/install/properties/packages/items/installOnScratchOrgs", "default": false, "description": "If true, this package will be installed when creating a new scratch org with sfdx-hardis", - "examples": [true, false], + "examples": [ + true, + false + ], "title": "Install on scratch orgs", "type": "boolean" }, "installationkey": { "$Id": "#/properties/install/properties/packages/items/installationkey", - "examples": ["MyInstallationKey", "4FzkMzUSwFfP#@"], + "examples": [ + "MyInstallationKey", + "4FzkMzUSwFfP#@" + ], "description": "Installation key for key-protected package", "title": "Package installation key", "type": "string" } }, - "required": ["SubscriberPackageVersionId"], + "required": [ + "SubscriberPackageVersionId" + ], "title": "Salesforce package" }, "title": "Installed Packages", @@ -747,7 +936,9 @@ "$id": "#/properties/installPackagesDuringCheckDeploy", "default": false, "description": "When calling deployment check command, installs any package referred within installedPackages property", - "examples": [true], + "examples": [ + true + ], "title": "Install packages during deployment checks", "type": "boolean" }, @@ -755,7 +946,11 @@ "$id": "#/properties/instanceUrl", "default": "", "description": "Salesforce instance URL used by CI for deployment or backups", - "examples": ["https://myclient.force.com", "https://google.force.com", "https://apple.force.com"], + "examples": [ + "https://myclient.force.com", + "https://google.force.com", + "https://apple.force.com" + ], "title": "Instance URL", "type": "string" }, @@ -773,7 +968,13 @@ "sourcesToRetrofit": { "$id": "#/properties/sourcesToRetrofit", "description": "List of metadata to retrieve for retrofit job", - "examples": [["CustomField", "Layout", "PermissionSet"]], + "examples": [ + [ + "CustomField", + "Layout", + "PermissionSet" + ] + ], "items": { "type": "string" }, @@ -788,13 +989,13 @@ { "title": "Detect calls to deprecated API versions", "key": "LEGACYAPI", - "command": "sfdx hardis:org:diagnose:legacyapi", + "command": "sf hardis:org:diagnose:legacyapi", "frequency": "weekly" }, { "title": "My custom command", "key": "MY_CUSTOM_KEY", - "command": "sfdx my:custom:command", + "command": "sf my:custom:command", "frequency": "daily" } ] @@ -830,7 +1031,10 @@ "type": "string" } }, - "required": ["title", "command"] + "required": [ + "title", + "command" + ] }, "title": "Monitoring commands", "type": "array" @@ -845,7 +1049,12 @@ "monitoringDisable": { "$id": "#/properties/monitoringDisable", "description": "List of commands to skip during monitoring jobs", - "examples": [["METADATA_STATUS", "UNUSED_METADATAS"]], + "examples": [ + [ + "METADATA_STATUS", + "UNUSED_METADATAS" + ] + ], "items": { "$ref": "#/definitions/enum_monitoring_commands" }, @@ -855,7 +1064,12 @@ "monitoringExcludeUsernames": { "$id": "#/properties/monitoringExcludeUsernames", "description": "List of usernames to exclude while running monitoring commands", - "examples": [["deploymentuser@cloudity.com", "mc-cloud-user@cloudity.com"]], + "examples": [ + [ + "deploymentuser@cloudity.com", + "mc-cloud-user@cloudity.com" + ] + ], "items": { "type": "string" }, @@ -866,7 +1080,9 @@ "$id": "#/properties/msTeamsWebhookUrl", "default": "", "description": "Url of the Ms Teams channel Web Hook that can be used to send ALL notifications", - "examples": ["https://my.msteams.webhook.url"], + "examples": [ + "https://my.msteams.webhook.url" + ], "title": "MsTeams WebHook Url (ALL)", "type": "string" }, @@ -874,7 +1090,9 @@ "$id": "#/properties/msTeamsWebhookUrlCritical", "default": "", "description": "Url of the Ms Teams channel Web Hook that can be used to send CRITICAL notifications", - "examples": ["https://my.msteams.webhook.url"], + "examples": [ + "https://my.msteams.webhook.url" + ], "title": "MsTeams WebHook Url (CRITICAL)", "type": "string" }, @@ -882,7 +1100,9 @@ "$id": "#/properties/msTeamsWebhookUrlSevere", "default": "", "description": "Url of the Ms Teams channel Web Hook that can be used to send SEVERE notifications", - "examples": ["https://my.msteams.webhook.url"], + "examples": [ + "https://my.msteams.webhook.url" + ], "title": "MsTeams WebHook Url (SEVERE)", "type": "string" }, @@ -890,7 +1110,9 @@ "$id": "#/properties/msTeamsWebhookUrlWarning", "default": "", "description": "Url of the Ms Teams channel Web Hook that can be used to send WARNING notifications", - "examples": ["https://my.msteams.webhook.url"], + "examples": [ + "https://my.msteams.webhook.url" + ], "title": "MsTeams WebHook Url (WARNING)", "type": "string" }, @@ -898,14 +1120,21 @@ "$id": "#/properties/msTeamsWebhookUrlInfo", "default": "", "description": "Url of the Ms Teams channel Web Hook that can be used to send INFO notifications", - "examples": ["https://my.msteams.webhook.url"], + "examples": [ + "https://my.msteams.webhook.url" + ], "title": "MsTeams WebHook Url (INFO)", "type": "string" }, "notificationsDisable": { "$id": "#/properties/notificationsDisable", "description": "List of notifications types to skip sending", - "examples": [["METADATA_STATUS", "UNUSED_METADATAS"]], + "examples": [ + [ + "METADATA_STATUS", + "UNUSED_METADATAS" + ] + ], "items": { "$ref": "#/definitions/enum_notification_types" }, @@ -955,7 +1184,11 @@ "$id": "#/properties/productionBranch", "default": "", "description": "Name of the git branch corresponding to production environment", - "examples": ["master", "main", "production"], + "examples": [ + "master", + "main", + "production" + ], "title": "Production branch name", "type": "string" }, @@ -963,7 +1196,11 @@ "$id": "#/properties/projectName", "default": "", "description": "Identifier for the project (can be the client and project)", - "examples": ["MyClientMyProject", "GoogleGmail", "AppleIWatch"], + "examples": [ + "MyClientMyProject", + "GoogleGmail", + "AppleIWatch" + ], "title": "Project Name", "type": "string" }, @@ -971,7 +1208,11 @@ "$id": "#/properties/retrofitBranch", "default": "", "description": "Name of the git branch where retrofit merge requests targets to", - "examples": ["preprod", "dev", "maintenance"], + "examples": [ + "preprod", + "dev", + "maintenance" + ], "title": "Retrofit branch name", "type": "string" }, @@ -994,14 +1235,22 @@ "$id": "#/properties/runtests", "default": "", "description": "WARNING: Use with caution, only in branch scoped config ! Can be a list of test classes if testLevel=RunSpecifiedTests, or a regex if testLevel=RunRepositoryTests", - "examples": ["MyTestClass1,MyTestClass2", "^(?!FLI|fli|BatchableCodeSolvaTest|BatchableRemoveCodeSolvaTest|HelperNovaxelApiTest).*"], + "examples": [ + "MyTestClass1,MyTestClass2", + "^(?!FLI|fli|BatchableCodeSolvaTest|BatchableRemoveCodeSolvaTest|HelperNovaxelApiTest).*" + ], "title": "Selected tests to run (list or regex)", "type": "string" }, "scratchOrgInitApexScripts": { "$id": "#/properties/scratchOrgInitApexScripts", "description": "Apex scripts to call after scratch org initialization", - "examples": [["scripts/apex/init-scratch.apex", "scripts/apex/init-custom-settings.apex"]], + "examples": [ + [ + "scripts/apex/init-scratch.apex", + "scripts/apex/init-custom-settings.apex" + ] + ], "items": { "type": "string" }, @@ -1021,7 +1270,9 @@ "$id": "#/properties/sfdmuCanModify", "default": "", "description": "Instance host name to allow SFDMU to deploy data in a production org", - "examples": ["myproject.force.com"], + "examples": [ + "myproject.force.com" + ], "title": "SFDMU can modify", "type": "string" }, @@ -1035,7 +1286,12 @@ "skipMinimizeProfiles": { "$id": "#/properties/skipMinimizeProfiles", "description": "These profiles will not be reformatted by command hardis:project:clean:minimizeprofiles", - "examples": [["MyClient Customer Community Login User", "MyClientPortail Profile"]], + "examples": [ + [ + "MyClient Customer Community Login User", + "MyClientPortail Profile" + ] + ], "items": { "type": "string" }, @@ -1060,7 +1316,11 @@ "$id": "#/properties/targetUsername", "default": "", "description": "Salesforce username used by CI for deployment or backups", - "examples": ["deployments@myclient.com", "deployments@google.fr", "deployments@apple.com"], + "examples": [ + "deployments@myclient.com", + "deployments@google.fr", + "deployments@apple.com" + ], "title": "Target Username", "type": "string" }, @@ -1075,8 +1335,18 @@ "$id": "#/properties/testLevel", "description": "WARNING: Use with caution, only in branch scoped config ! You can override default test level for deployments for special use cases, for example when you have SeeAllData=true you can use RunRepositoryTests associated with a regex in runtests option", "default": "RunLocalTests", - "enum": ["NoTestRun", "RunSpecifiedTests", "RunRepositoryTests", "RunRepositoryTestsExceptSeeAllData", "RunLocalTests", "RunAllTestsInOrg"], - "examples": ["RunRepositoryTests", "RunSpecifiedTests"], + "enum": [ + "NoTestRun", + "RunSpecifiedTests", + "RunRepositoryTests", + "RunRepositoryTestsExceptSeeAllData", + "RunLocalTests", + "RunAllTestsInOrg" + ], + "examples": [ + "RunRepositoryTests", + "RunSpecifiedTests" + ], "title": "Test level for deployments", "type": "string" }, @@ -1087,15 +1357,27 @@ "title": "Use Delta Deployment", "type": "boolean" }, + "useSmartDeploymentTests": { + "$id": "#/properties/useSmartDeploymentTests", + "default": false, + "description": "Define if smart deployment tests will be activated and run test classes only if necessary (see more in hardis:project:deploy:smart documentation", + "title": "Use Smart Deployment Tests", + "type": "boolean" + }, "linterIgnoreRightMetadataFile": { "$id": "#/properties/linterIgnoreRightMetadataFile", "default": "", "description": "Ignore profiles or permission sets", - "examples": ["Profile", "Profile:ProfileA", "PermissionSet", "PermissionSet:PermissionSetA, Profile:ProfileA"], + "examples": [ + "Profile", + "Profile:ProfileA", + "PermissionSet", + "PermissionSet:PermissionSetA, Profile:ProfileA" + ], "title": "Linter ignore permission set or/and profile", "type": "string" } }, "title": "sfdx-hardis configuration", "type": "object" -} +} \ No newline at end of file diff --git a/defaults/ci/.github/workflows/check-deploy.yml b/defaults/ci/.github/workflows/check-deploy.yml index 22b630ba3..e56879b17 100644 --- a/defaults/ci/.github/workflows/check-deploy.yml +++ b/defaults/ci/.github/workflows/check-deploy.yml @@ -45,9 +45,8 @@ jobs: run: | npm install --no-cache @salesforce/cli --global sf plugins install @salesforce/plugin-packaging - echo 'y' | sfdx plugins:install sfdx-hardis - echo 'y' | sfdx plugins:install sfdx-essentials - echo 'y' | sfdx plugins:install sfdx-git-delta + echo 'y' | sf plugins install sfdx-hardis + echo 'y' | sf plugins install sfdx-git-delta sf version --verbose --json # Login & check deploy with test classes & code coverage - name: Login & Simulate deployment @@ -79,5 +78,5 @@ jobs: FORCE_COLOR: "1" run: | echo "Simulate SFDX deployment using Hardis against \"$CONFIG_BRANCH\"" - sfdx hardis:auth:login - sfdx hardis:project:deploy:sources:dx --check + sf hardis:auth:login + sf hardis:project:deploy:smart --check diff --git a/defaults/ci/.github/workflows/megalinter.yml b/defaults/ci/.github/workflows/megalinter.yml index 596637d5d..e617aa28c 100644 --- a/defaults/ci/.github/workflows/megalinter.yml +++ b/defaults/ci/.github/workflows/megalinter.yml @@ -52,9 +52,10 @@ jobs: # Upload Mega-Linter artifacts - name: Archive production artifacts if: success() || failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Mega-Linter reports + include-hidden-files: "true" path: | megalinter-reports mega-linter.log diff --git a/defaults/ci/.github/workflows/process-deploy.yml b/defaults/ci/.github/workflows/process-deploy.yml index 12e18bf40..41fda7f75 100644 --- a/defaults/ci/.github/workflows/process-deploy.yml +++ b/defaults/ci/.github/workflows/process-deploy.yml @@ -40,10 +40,9 @@ jobs: run: | npm install @salesforce/cli --global sf plugins install @salesforce/plugin-packaging - echo 'y' | sfdx plugins:install sfdx-hardis - echo 'y' | sfdx plugins:install sfdx-essentials - # echo 'y' | sfdx plugins:install sfdmu # Disabled while it does not play well with @salesforce/cli - echo 'y' | sfdx plugins:install sfdx-git-delta + echo 'y' | sf plugins install sfdx-hardis + # echo 'y' | sf plugins install sfdmu # Disabled while it does not play well with @salesforce/cli + echo 'y' | sf plugins install sfdx-git-delta sf version --verbose --json # Set env branch variable (github.ref_name seems to not work) - name: Set env.BRANCH @@ -75,5 +74,5 @@ jobs: FORCE_COLOR: "1" run: | echo "Process SFDX deployment using Hardis against \"$CONFIG_BRANCH\"" - sfdx hardis:auth:login - sfdx hardis:project:deploy:sources:dx + sf hardis:auth:login + sf hardis:project:deploy:smart diff --git a/defaults/ci/.gitlab-ci.yml b/defaults/ci/.gitlab-ci.yml index f0d356f23..09fbc2f8d 100644 --- a/defaults/ci/.gitlab-ci.yml +++ b/defaults/ci/.gitlab-ci.yml @@ -12,11 +12,11 @@ include: # Pipeline stages stages: - - build # Check code quality (+ create testing scratch org if necessary) - - test # Apex unit tests on testing scratch org (if used) - - clean # Delete testing scratch org (if used) - - check_deploy # Simulate deployment to target branch - - deploy # After a merge, automatically deploys the new commit state into the related Salesforce org + - build # Check code quality (+ create testing scratch org if necessary) + - test # Apex unit tests on testing scratch org (if used) + - clean # Delete testing scratch org (if used) + - check_deploy # Simulate deployment to target branch + - deploy # After a merge, automatically deploys the new commit state into the related Salesforce org # Jobs are run on sfdx-hardis image, that includes all required dependencies. # You can use latest, beta or latest-recommended @@ -83,8 +83,8 @@ check_deploy_to_target_branch_org: ORG_ALIAS: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME script: - '[ -z "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" ] && exit 0;' # Skip this job if it is launched from web UI and we are not in merge request context - - sfdx hardis:auth:login - - sfdx hardis:project:deploy:sources:dx --check + - sf hardis:auth:login + - sf hardis:project:deploy:smart --check # Create scratch org to check the sources push & the unit tests create_scratch_org: @@ -101,14 +101,15 @@ create_scratch_org: - $USE_SCRATCH_ORGS == "false" interruptible: true script: - - sfdx hardis:auth:login --devhub - - sfdx hardis:scratch:create + - sf hardis:auth:login --devhub + - sf hardis:scratch:create artifacts: when: always expire_in: 60 minutes paths: - .cache/sfdx-hardis/.sfdx - .sfdx + - .sf - config/user # Refresh scratch org pool: IF you use scratch orgs, job to schedule with variable SCRATCH_ORG_POOL: true @@ -121,8 +122,8 @@ refresh_scratch_org_pool: - $SCRATCH_ORG_POOL == "true" interruptible: true script: - - sfdx hardis:auth:login --devhub - - sfdx hardis:scratch:pool:refresh + - sf hardis:auth:login --devhub + - sf hardis:scratch:pool:refresh artifacts: when: always @@ -145,8 +146,8 @@ test_apex: artifacts: true script: - sleep 120 # Orgs just created can be not ready yet, let's wait a while before running tests - - sfdx hardis:auth:login --scratchorg || true - - sfdx hardis:org:test:apex + - sf hardis:auth:login --scratchorg || true + - sf hardis:org:test:apex artifacts: when: always paths: @@ -165,7 +166,7 @@ test_apex: # - job: create_scratch_org # artifacts: true # script: -# - sfdx hardis:auth:login --scratchorg +# - sf hardis:auth:login --scratchorg # - echo "Automated tests not implemented yet" # Delete testing scratch org @@ -187,9 +188,9 @@ clean: - job: create_scratch_org - job: test_apex script: - - sfdx hardis:auth:login --devhub - - sfdx hardis:auth:login --scratchorg || true - - sfdx force:org:delete --noprompt || true + - sf hardis:auth:login --devhub + - sf hardis:auth:login --scratchorg || true + - sf org delete scratch --no-prompt || true # Simulate deployment to related org # Is triggered only when scheduled, or via manual launch @@ -206,8 +207,8 @@ check_deploy_to_current_branch_org: - $SCRATCH_ORG_POOL == "true" interruptible: true script: - - sfdx hardis:auth:login - - sfdx hardis:project:deploy:sources:dx --check + - sf hardis:auth:login + - sf hardis:project:deploy:smart --check # Deploy to branch related org when detecting new commit (after a merged merge request) # Don't forget to define variable DEPLOY_BRANCHES to match your branches in .gitlab-ci-config.yml @@ -224,5 +225,5 @@ deploy_to_org: - $SCRATCH_ORG_POOL == "true" interruptible: true script: - - sfdx hardis:auth:login - - sfdx hardis:project:deploy:sources:dx + - sf hardis:auth:login + - sf hardis:project:deploy:smart diff --git a/defaults/ci/Jenkinsfile b/defaults/ci/Jenkinsfile index ef4270ce5..bb16acfea 100644 --- a/defaults/ci/Jenkinsfile +++ b/defaults/ci/Jenkinsfile @@ -59,8 +59,8 @@ pipeline { //Validation on the appropriate org steps { script { - sh 'sfdx hardis:auth:login' - sh 'sfdx hardis:project:deploy:sources:dx --check' + sh 'sf hardis:auth:login' + sh 'sf hardis:project:deploy:smart --check' } } post { @@ -77,21 +77,21 @@ pipeline { } //MANUAL: add your major branch if necessary when { - allOf{ + allOf { anyOf { - branch: 'integration'; //Example - branch: 'uat'; //Example - branch: 'preprod'; //Example + branch: 'integration' //Example + branch: 'uat' //Example + branch: 'preprod' //Example branch: 'main' //Example - }; - not {changeRequest()} + }; + not { changeRequest() } } } //deploy on the appropriate org steps { script { - sh 'sfdx hardis:auth:login' - sh 'sfdx hardis:project:deploy:sources:dx' + sh 'sf hardis:auth:login' + sh 'sf hardis:project:deploy:smart' } } post { diff --git a/defaults/ci/azure-pipelines-checks.yml b/defaults/ci/azure-pipelines-checks.yml index a8972638d..5efe55dd2 100644 --- a/defaults/ci/azure-pipelines-checks.yml +++ b/defaults/ci/azure-pipelines-checks.yml @@ -19,7 +19,7 @@ # - Tick "Override the YAML continuous integration trigger from here" and select "Disable continuous integration" # - Now go to Repos -> Branches and create a "Branch policy" for each of your major branches -# - In Build Validation, click + and then select your Build pipeline (even if it is prefilled, it's buggy and you +# - In Build Validation, click + and then select your Build pipeline (even if it is prefilled, it's buggy and you # need to manually select it again) and keep the default settings for the other options then save. # This causes the pipeline to be triggered on Pull Requests even if we previously disabled it. @@ -33,14 +33,12 @@ variables: value: $[replace(variables['System.PullRequest.TargetBranch'], 'refs/heads/', '')] jobs: - # Simulate SFDX deployment - job: DeploymentCheck timeoutInMinutes: 150 pool: vmImage: ubuntu-20.04 steps: - # Checkout repo - checkout: self fetchDepth: 0 @@ -52,22 +50,20 @@ jobs: inputs: version: ">=20.0.0" displayName: "Use Node.js" - # Install SFDX & Dependencies - script: | npm install @salesforce/cli --global sf plugins install @salesforce/plugin-packaging - echo 'y' | sfdx plugins:install sfdx-hardis - echo 'y' | sfdx plugins:install sfdx-essentials - echo 'y' | sfdx plugins:install sfdx-git-delta + echo 'y' | sf plugins install sfdx-hardis + echo 'y' | sf plugins install sfdx-git-delta sf version --verbose --json displayName: "Install SFDX & plugins" - + # Login & check deployment to PR target branch related org (configuration: https://hardisgroupcom.github.io/sfdx-hardis/salesforce-ci-cd-setup-auth/ ) - script: | - sfdx hardis:auth:login - sfdx hardis:project:deploy:sources:dx --check + sf hardis:auth:login + sf hardis:project:deploy:smart --check env: SFDX_CLIENT_ID_INTEGRATION: $(SFDX_CLIENT_ID_INTEGRATION) SFDX_CLIENT_KEY_INTEGRATION: $(SFDX_CLIENT_KEY_INTEGRATION) @@ -107,20 +103,20 @@ jobs: pool: vmImage: ubuntu-20.04 steps: - # Pull MegaLinter docker image - - script: docker pull oxsecurity/megalinter-salesforce:latest - displayName: Pull MegaLinter - # Run MegaLinter - - script: | - docker run -v $(System.DefaultWorkingDirectory):/tmp/lint \ - --env-file <(env | grep -e SYSTEM_ -e BUILD_ -e TF_ -e AGENT_) \ - -e CI=true \ - -e SYSTEM_ACCESSTOKEN=$(System.AccessToken) \ - -e GIT_AUTHORIZATION_BEARER=$(System.AccessToken) \ - oxsecurity/megalinter-salesforce:latest - displayName: Run MegaLinter - # Publish Megalinter reports - - publish: $(System.DefaultWorkingDirectory)/megalinter-reports/ - condition: succeededOrFailed() - artifact: megalinter-reports - displayName: Publish reports + # Pull MegaLinter docker image + - script: docker pull oxsecurity/megalinter-salesforce:latest + displayName: Pull MegaLinter + # Run MegaLinter + - script: | + docker run -v $(System.DefaultWorkingDirectory):/tmp/lint \ + --env-file <(env | grep -e SYSTEM_ -e BUILD_ -e TF_ -e AGENT_) \ + -e CI=true \ + -e SYSTEM_ACCESSTOKEN=$(System.AccessToken) \ + -e GIT_AUTHORIZATION_BEARER=$(System.AccessToken) \ + oxsecurity/megalinter-salesforce:latest + displayName: Run MegaLinter + # Publish Megalinter reports + - publish: $(System.DefaultWorkingDirectory)/megalinter-reports/ + condition: succeededOrFailed() + artifact: megalinter-reports + displayName: Publish reports diff --git a/defaults/ci/azure-pipelines-deployment.yml b/defaults/ci/azure-pipelines-deployment.yml index b98e814dd..624733828 100644 --- a/defaults/ci/azure-pipelines-deployment.yml +++ b/defaults/ci/azure-pipelines-deployment.yml @@ -34,7 +34,6 @@ jobs: pool: vmImage: ubuntu-20.04 steps: - # Checkout repo - checkout: self fetchDepth: 0 @@ -51,17 +50,16 @@ jobs: - script: | npm install @salesforce/cli --global sf plugins install @salesforce/plugin-packaging - echo 'y' | sfdx plugins:install sfdx-hardis - echo 'y' | sfdx plugins:install sfdx-essentials - echo 'y' | sfdx plugins:install sfdmu - echo 'y' | sfdx plugins:install sfdx-git-delta + echo 'y' | sf plugins install sfdx-hardis + echo 'y' | sf plugins install sfdmu + echo 'y' | sf plugins install sfdx-git-delta sf version --verbose --json displayName: "Install SFDX & plugins" # Login & Deploy sfdx sources to related org (configuration: https://hardisgroupcom.github.io/sfdx-hardis/salesforce-ci-cd-setup-auth/ ) - script: | - sfdx hardis:auth:login - sfdx hardis:project:deploy:sources:dx + sf hardis:auth:login + sf hardis:project:deploy:smart env: SFDX_CLIENT_ID_INTEGRATION: $(SFDX_CLIENT_ID_INTEGRATION) SFDX_CLIENT_KEY_INTEGRATION: $(SFDX_CLIENT_KEY_INTEGRATION) diff --git a/defaults/ci/bitbucket-pipelines.yml b/defaults/ci/bitbucket-pipelines.yml index 8214b5896..ec2b9bcaa 100644 --- a/defaults/ci/bitbucket-pipelines.yml +++ b/defaults/ci/bitbucket-pipelines.yml @@ -10,48 +10,46 @@ pipelines: pull-requests: "**": - parallel: - # Run MegaLinter - - step: - name: Run MegaLinter - image: oxsecurity/megalinter-salesforce:latest - script: - - export DEFAULT_WORKSPACE=$BITBUCKET_CLONE_DIR && bash /entrypoint.sh - artifacts: - - megalinter-reports/** - # Simulate deployment - - step: - name: Simulate SFDX deployment - script: - - npm install --no-cache @salesforce/cli --global - - sf plugins install @salesforce/plugin-packaging - - echo 'y' | sfdx plugins:install sfdx-hardis - - echo 'y' | sfdx plugins:install sfdx-essentials - - echo 'y' | sfdx plugins:install sfdx-git-delta - - sf version --verbose --json - - export BRANCH_NAME=$(echo "$BITBUCKET_PR_DESTINATION_BRANCH" | sed 's/refs\/heads\///') - - export CI_COMMIT_REF_NAME=$BRANCH_NAME - - export ORG_ALIAS=$BRANCH_NAME - - export CONFIG_BRANCH=$BRANCH_NAME - - export FORCE_COLOR=1 - - sfdx hardis:auth:login - - sfdx hardis:project:deploy:sources:dx --check + # Run MegaLinter + - step: + name: Run MegaLinter + image: oxsecurity/megalinter-salesforce:latest + script: + - export DEFAULT_WORKSPACE=$BITBUCKET_CLONE_DIR && bash /entrypoint.sh + artifacts: + - megalinter-reports/** + # Simulate deployment + - step: + name: Simulate SFDX deployment + script: + - npm install --no-cache @salesforce/cli --global + - sf plugins install @salesforce/plugin-packaging + - echo 'y' | sf plugins install sfdx-hardis + - echo 'y' | sf plugins install sfdx-git-delta + - sf version --verbose --json + - export BRANCH_NAME=$(echo "$BITBUCKET_PR_DESTINATION_BRANCH" | sed 's/refs\/heads\///') + - export CI_COMMIT_REF_NAME=$BRANCH_NAME + - export ORG_ALIAS=$BRANCH_NAME + - export CONFIG_BRANCH=$BRANCH_NAME + - export FORCE_COLOR=1 + - sf hardis:auth:login + - sf hardis:project:deploy:smart --check branches: # Add all your major branches here - '{integration,uat,preprod,production,main}': + "{integration,uat,preprod,production,main}": - step: name: Deploy to major org script: - - npm install --no-cache @salesforce/cli --global - - sf plugins install @salesforce/plugin-packaging - - echo 'y' | sfdx plugins:install sfdx-hardis - - echo 'y' | sfdx plugins:install sfdx-essentials - - echo 'y' | sfdx plugins:install sfdx-git-delta - - sf version --verbose --json - - export BRANCH_NAME=$(echo "$BITBUCKET_BRANCH" | sed 's/refs\/heads\///') - - export CI_COMMIT_REF_NAME=$BRANCH_NAME - - export CONFIG_BRANCH=$BRANCH_NAME - - export ORG_ALIAS=$BRANCH_NAME - - export FORCE_COLOR=1 - - sfdx hardis:auth:login - - sfdx hardis:project:deploy:sources:dx + - npm install --no-cache @salesforce/cli --global + - sf plugins install @salesforce/plugin-packaging + - echo 'y' | sf plugins install sfdx-hardis + - echo 'y' | sf plugins install sfdx-git-delta + - sf version --verbose --json + - export BRANCH_NAME=$(echo "$BITBUCKET_BRANCH" | sed 's/refs\/heads\///') + - export CI_COMMIT_REF_NAME=$BRANCH_NAME + - export CONFIG_BRANCH=$BRANCH_NAME + - export ORG_ALIAS=$BRANCH_NAME + - export FORCE_COLOR=1 + - sf hardis:auth:login + - sf hardis:project:deploy:smart diff --git a/defaults/ci/manifest/package-no-overwrite.xml b/defaults/ci/manifest/package-no-overwrite.xml index c6ff34357..86ef69897 100644 --- a/defaults/ci/manifest/package-no-overwrite.xml +++ b/defaults/ci/manifest/package-no-overwrite.xml @@ -4,6 +4,11 @@ * ApprovalProcess + + + + * + Certificate diff --git a/defaults/mkdocs/.github/workflows/build-deploy-docs.yml b/defaults/mkdocs/.github/workflows/build-deploy-docs.yml index a4ecece0f..073706b7b 100644 --- a/defaults/mkdocs/.github/workflows/build-deploy-docs.yml +++ b/defaults/mkdocs/.github/workflows/build-deploy-docs.yml @@ -18,8 +18,8 @@ jobs: - run: yarn - run: yarn prepack - run: npm i @salesforce/cli -g - - run: echo y|sfdx plugins:install sfdx-hardis - - run: sfdx hardis:doc:plugin:generate + - run: echo y|sf plugins install sfdx-hardis + - run: sf hardis:doc:plugin:generate # Deploy docs with mkdocs-material - uses: actions/setup-python@v3 with: diff --git a/defaults/monitoring/.github/workflows/org-monitoring.yml b/defaults/monitoring/.github/workflows/org-monitoring.yml index 0dc2a457f..cfde143c0 100644 --- a/defaults/monitoring/.github/workflows/org-monitoring.yml +++ b/defaults/monitoring/.github/workflows/org-monitoring.yml @@ -10,7 +10,7 @@ # - Do a CTRL+F and look for "MANUAL" # - Add your monitored git branches here where asked to replace # - Add your authentication variable names where asked to replace -# - Commit & push: there should be a SINGLE GitHub Actions job (using matrix) that will run the monitoring on all orgs +# - Commit & push: there should be a SINGLE GitHub Actions job (using matrix) that will run the monitoring on all orgs # You may also: # - Update manifest/package-skip-items.xml to filter elements to retrieve (must be done directly in monitored branches) @@ -21,10 +21,9 @@ on: push: # Automatically run every day at midnight schedule: - - cron: '0 0 * * *' # Cron format -> https://crontab.cronhub.io/ + - cron: "0 0 * * *" # Cron format -> https://crontab.cronhub.io/ workflow_dispatch: - name: Org Monitoring sfdx-hardis # concurrency: @@ -52,74 +51,73 @@ jobs: - monitoring_myclient__recette_sandbox - monitoring_myclient__preprod_sandbox steps: - # Checkout repo - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Faster code checkout fetching only latest commit - ref: ${{ matrix.branch }} - persist-credentials: true - # Setup node - - name: Setup Node - uses: actions/setup-node@v3 - with: - node-version: "20" - # SFDX & plugins - - name: Install SFDX and plugins - run: | - npm install --no-cache @salesforce/cli --global - sf plugins install --force @salesforce/plugin-packaging - echo 'y' | sfdx plugins:install --force sfdx-hardis - echo 'y' | sfdx plugins:install --force sfdx-essentials - echo 'y' | sfdx plugins:install --force sfdx-git-delta - sf version --verbose --json - # Login & check deploy with test classes & code coverage - - name: Login & Retrieve Metadata - env: - # MANUAL: Update variables below to add authentication variables you need - SFDX_CLIENT_ID_MONITORING_MYCLIENT: ${{ secrets.SFDX_CLIENT_ID_MONITORING_MYCLIENT}} - SFDX_CLIENT_KEY_MONITORING_MYCLIENT: ${{ secrets.SFDX_CLIENT_KEY_MONITORING_MYCLIENT}} - SFDX_CLIENT_ID_MONITORING_MYCLIENT__RECETTE_SANDBOX: ${{ secrets.SFDX_CLIENT_ID_MONITORING_MYCLIENT__RECETTE_SANDBOX}} - SFDX_CLIENT_KEY_MONITORING_MYCLIENT__RECETTE_SANDBOX: ${{ secrets.SFDX_CLIENT_KEY_MONITORING_MYCLIENT__RECETTE_SANDBOX}} - SFDX_CLIENT_ID_MONITORING_MYCLIENT__PREPROD_SANDBOX: ${{ secrets.SFDX_CLIENT_ID_MONITORING_MYCLIENT__PREPROD_SANDBOX}} - SFDX_CLIENT_KEY_MONITORING_MYCLIENT__PREPROD_SANDBOX: ${{ secrets.SFDX_CLIENT_KEY_MONITORING_MYCLIENT__PREPROD_SANDBOX}} - SFDX_DEPLOY_WAIT_MINUTES: 120 # Override if necessary - SFDX_TEST_WAIT_MINUTES: 120 # Override if necessary - CI_COMMIT_REF_NAME: ${{ matrix.branch }} - ORG_ALIAS: ${{ matrix.branch }} - CONFIG_BRANCH: ${{ matrix.branch }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} - SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} - NOTIF_EMAIL_ADDRESS: ${{ secrets.NOTIF_EMAIL_ADDRESS }} - FORCE_COLOR: "1" - NOTIF_API_URL: ${{ secrets.NOTIF_API_URL }} - NOTIF_API_BASIC_AUTH_USERNAME: ${{ secrets.NOTIF_API_BASIC_AUTH_USERNAME }} - NOTIF_API_BASIC_AUTH_PASSWORD: ${{ secrets.NOTIF_API_BASIC_AUTH_PASSWORD }} - NOTIF_API_METRICS_URL: ${{ secrets.NOTIF_API_METRICS_URL }} - NOTIF_API_METRICS_BASIC_AUTH_USERNAME: ${{ secrets.NOTIF_API_METRICS_BASIC_AUTH_USERNAME }} - NOTIF_API_METRICS_BASIC_AUTH_PASSWORD: ${{ secrets.NOTIF_API_METRICS_BASIC_AUTH_PASSWORD }} + # Checkout repo + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Faster code checkout fetching only latest commit + ref: ${{ matrix.branch }} + persist-credentials: true + # Setup node + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: "20" + # SFDX & plugins + - name: Install SFDX and plugins + run: | + npm install --no-cache @salesforce/cli --global + sf plugins install --force @salesforce/plugin-packaging + echo 'y' | sf plugins install --force sfdx-hardis + echo 'y' | sf plugins install --force sfdx-git-delta + sf version --verbose --json + # Login & check deploy with test classes & code coverage + - name: Login & Retrieve Metadata + env: + # MANUAL: Update variables below to add authentication variables you need + SFDX_CLIENT_ID_MONITORING_MYCLIENT: ${{ secrets.SFDX_CLIENT_ID_MONITORING_MYCLIENT}} + SFDX_CLIENT_KEY_MONITORING_MYCLIENT: ${{ secrets.SFDX_CLIENT_KEY_MONITORING_MYCLIENT}} + SFDX_CLIENT_ID_MONITORING_MYCLIENT__RECETTE_SANDBOX: ${{ secrets.SFDX_CLIENT_ID_MONITORING_MYCLIENT__RECETTE_SANDBOX}} + SFDX_CLIENT_KEY_MONITORING_MYCLIENT__RECETTE_SANDBOX: ${{ secrets.SFDX_CLIENT_KEY_MONITORING_MYCLIENT__RECETTE_SANDBOX}} + SFDX_CLIENT_ID_MONITORING_MYCLIENT__PREPROD_SANDBOX: ${{ secrets.SFDX_CLIENT_ID_MONITORING_MYCLIENT__PREPROD_SANDBOX}} + SFDX_CLIENT_KEY_MONITORING_MYCLIENT__PREPROD_SANDBOX: ${{ secrets.SFDX_CLIENT_KEY_MONITORING_MYCLIENT__PREPROD_SANDBOX}} + SFDX_DEPLOY_WAIT_MINUTES: 120 # Override if necessary + SFDX_TEST_WAIT_MINUTES: 120 # Override if necessary + CI_COMMIT_REF_NAME: ${{ matrix.branch }} + ORG_ALIAS: ${{ matrix.branch }} + CONFIG_BRANCH: ${{ matrix.branch }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} + SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} + NOTIF_EMAIL_ADDRESS: ${{ secrets.NOTIF_EMAIL_ADDRESS }} + FORCE_COLOR: "1" + NOTIF_API_URL: ${{ secrets.NOTIF_API_URL }} + NOTIF_API_BASIC_AUTH_USERNAME: ${{ secrets.NOTIF_API_BASIC_AUTH_USERNAME }} + NOTIF_API_BASIC_AUTH_PASSWORD: ${{ secrets.NOTIF_API_BASIC_AUTH_PASSWORD }} + NOTIF_API_METRICS_URL: ${{ secrets.NOTIF_API_METRICS_URL }} + NOTIF_API_METRICS_BASIC_AUTH_USERNAME: ${{ secrets.NOTIF_API_METRICS_BASIC_AUTH_USERNAME }} + NOTIF_API_METRICS_BASIC_AUTH_PASSWORD: ${{ secrets.NOTIF_API_METRICS_BASIC_AUTH_PASSWORD }} - run: | - echo "Monitoring sfdx-hardis: Metadata Backup for \"$CONFIG_BRANCH\"" - sfdx hardis:auth:login - sfdx hardis:org:monitor:backup + run: | + echo "Monitoring sfdx-hardis: Metadata Backup for \"$CONFIG_BRANCH\"" + sf hardis:auth:login + sf hardis:org:monitor:backup - # Push new commit if applicable - # (for now works only on PR from same repository, not from forks) - - name: Prepare commit - run: chown -Rc $UID .git/ + # Push new commit if applicable + # (for now works only on PR from same repository, not from forks) + - name: Prepare commit + run: chown -Rc $UID .git/ - - name: Get current date - run: echo "BUILD_DATE=$(date -u +'%Y-%m-%d %H:%M')" >> ${GITHUB_ENV} + - name: Get current date + run: echo "BUILD_DATE=$(date -u +'%Y-%m-%d %H:%M')" >> ${GITHUB_ENV} - - name: Commit and push - uses: stefanzweifel/git-auto-commit-action@v5 - with: - branch: ${{ matrix.branch }} - commit_message: "Org state on ${{ env.BUILD_DATE }} for ${{ matrix.branch }} [skip ci]" - commit_user_name: sfdx-hardis-bot - commit_user_email: contact@cloudity.com + - name: Commit and push + uses: stefanzweifel/git-auto-commit-action@v5 + with: + branch: ${{ matrix.branch }} + commit_message: "Org state on ${{ env.BUILD_DATE }} for ${{ matrix.branch }} [skip ci]" + commit_user_name: sfdx-hardis-bot + commit_user_email: contact@cloudity.com ###################### ### Run Apex Tests ### @@ -140,66 +138,66 @@ jobs: - monitoring_myclient__recette_sandbox - monitoring_myclient__preprod_sandbox steps: - # Checkout repo - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Faster code checkout fetching only latest commit - ref: ${{ matrix.branch }} - persist-credentials: true - # Setup node - - name: Setup Node - uses: actions/setup-node@v3 - with: - node-version: "20" - # SFDX & plugins - - name: Install SFDX and plugins - run: | - npm install --no-cache @salesforce/cli --global - sf plugins install --force @salesforce/plugin-packaging - echo 'y' | sfdx plugins:install --force sfdx-hardis - echo 'y' | sfdx plugins:install --force sfdx-essentials - echo 'y' | sfdx plugins:install --force sfdx-git-delta - sf version --verbose --json - # Login & check deploy with test classes & code coverage - - name: Login & Run apex tests - env: - # MANUAL: Update variables below to add authentication variables you need - SFDX_CLIENT_ID_MONITORING_MYCLIENT: ${{ secrets.SFDX_CLIENT_ID_MONITORING_MYCLIENT}} - SFDX_CLIENT_KEY_MONITORING_MYCLIENT: ${{ secrets.SFDX_CLIENT_KEY_MONITORING_MYCLIENT}} - SFDX_CLIENT_ID_MONITORING_MYCLIENT__RECETTE_SANDBOX: ${{ secrets.SFDX_CLIENT_ID_MONITORING_MYCLIENT__RECETTE_SANDBOX}} - SFDX_CLIENT_KEY_MONITORING_MYCLIENT__RECETTE_SANDBOX: ${{ secrets.SFDX_CLIENT_KEY_MONITORING_MYCLIENT__RECETTE_SANDBOX}} - SFDX_CLIENT_ID_MONITORING_MYCLIENT__PREPROD_SANDBOX: ${{ secrets.SFDX_CLIENT_ID_MONITORING_MYCLIENT__PREPROD_SANDBOX}} - SFDX_CLIENT_KEY_MONITORING_MYCLIENT__PREPROD_SANDBOX: ${{ secrets.SFDX_CLIENT_KEY_MONITORING_MYCLIENT__PREPROD_SANDBOX}} - SFDX_DEPLOY_WAIT_MINUTES: 120 # Override if necessary - SFDX_TEST_WAIT_MINUTES: 120 # Override if necessary - CI_COMMIT_REF_NAME: ${{ matrix.branch }} - ORG_ALIAS: ${{ matrix.branch }} - CONFIG_BRANCH: ${{ matrix.branch }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} - SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} - NOTIF_EMAIL_ADDRESS: ${{ secrets.NOTIF_EMAIL_ADDRESS }} - FORCE_COLOR: "1" - NOTIF_API_URL: ${{ secrets.NOTIF_API_URL }} - NOTIF_API_BASIC_AUTH_USERNAME: ${{ secrets.NOTIF_API_BASIC_AUTH_USERNAME }} - NOTIF_API_BASIC_AUTH_PASSWORD: ${{ secrets.NOTIF_API_BASIC_AUTH_PASSWORD }} - NOTIF_API_METRICS_URL: ${{ secrets.NOTIF_API_METRICS_URL }} - NOTIF_API_METRICS_BASIC_AUTH_USERNAME: ${{ secrets.NOTIF_API_METRICS_BASIC_AUTH_USERNAME }} - NOTIF_API_METRICS_BASIC_AUTH_PASSWORD: ${{ secrets.NOTIF_API_METRICS_BASIC_AUTH_PASSWORD }} - run: | - echo "Run apex tests against \"$CONFIG_BRANCH\"" - git pull origin ${{ matrix.branch }} - sfdx hardis:auth:login - sfdx hardis:org:test:apex + # Checkout repo + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Faster code checkout fetching only latest commit + ref: ${{ matrix.branch }} + persist-credentials: true + # Setup node + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: "20" + # SFDX & plugins + - name: Install SFDX and plugins + run: | + npm install --no-cache @salesforce/cli --global + sf plugins install --force @salesforce/plugin-packaging + echo 'y' | sf plugins install --force sfdx-hardis + echo 'y' | sf plugins install --force sfdx-git-delta + sf version --verbose --json + # Login & check deploy with test classes & code coverage + - name: Login & Run apex tests + env: + # MANUAL: Update variables below to add authentication variables you need + SFDX_CLIENT_ID_MONITORING_MYCLIENT: ${{ secrets.SFDX_CLIENT_ID_MONITORING_MYCLIENT}} + SFDX_CLIENT_KEY_MONITORING_MYCLIENT: ${{ secrets.SFDX_CLIENT_KEY_MONITORING_MYCLIENT}} + SFDX_CLIENT_ID_MONITORING_MYCLIENT__RECETTE_SANDBOX: ${{ secrets.SFDX_CLIENT_ID_MONITORING_MYCLIENT__RECETTE_SANDBOX}} + SFDX_CLIENT_KEY_MONITORING_MYCLIENT__RECETTE_SANDBOX: ${{ secrets.SFDX_CLIENT_KEY_MONITORING_MYCLIENT__RECETTE_SANDBOX}} + SFDX_CLIENT_ID_MONITORING_MYCLIENT__PREPROD_SANDBOX: ${{ secrets.SFDX_CLIENT_ID_MONITORING_MYCLIENT__PREPROD_SANDBOX}} + SFDX_CLIENT_KEY_MONITORING_MYCLIENT__PREPROD_SANDBOX: ${{ secrets.SFDX_CLIENT_KEY_MONITORING_MYCLIENT__PREPROD_SANDBOX}} + SFDX_DEPLOY_WAIT_MINUTES: 120 # Override if necessary + SFDX_TEST_WAIT_MINUTES: 120 # Override if necessary + CI_COMMIT_REF_NAME: ${{ matrix.branch }} + ORG_ALIAS: ${{ matrix.branch }} + CONFIG_BRANCH: ${{ matrix.branch }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} + SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} + NOTIF_EMAIL_ADDRESS: ${{ secrets.NOTIF_EMAIL_ADDRESS }} + FORCE_COLOR: "1" + NOTIF_API_URL: ${{ secrets.NOTIF_API_URL }} + NOTIF_API_BASIC_AUTH_USERNAME: ${{ secrets.NOTIF_API_BASIC_AUTH_USERNAME }} + NOTIF_API_BASIC_AUTH_PASSWORD: ${{ secrets.NOTIF_API_BASIC_AUTH_PASSWORD }} + NOTIF_API_METRICS_URL: ${{ secrets.NOTIF_API_METRICS_URL }} + NOTIF_API_METRICS_BASIC_AUTH_USERNAME: ${{ secrets.NOTIF_API_METRICS_BASIC_AUTH_USERNAME }} + NOTIF_API_METRICS_BASIC_AUTH_PASSWORD: ${{ secrets.NOTIF_API_METRICS_BASIC_AUTH_PASSWORD }} + run: | + echo "Run apex tests against \"$CONFIG_BRANCH\"" + git pull origin ${{ matrix.branch }} + sf hardis:auth:login + sf hardis:org:test:apex - - name: Upload artifacts - if: success() || failure() - uses: actions/upload-artifact@v3 - with: - name: Hardis Apex Tests reports - path: | - hardis-report + - name: Upload artifacts + if: success() || failure() + uses: actions/upload-artifact@v4 + with: + name: Hardis Apex Tests reports + include-hidden-files: "true" + path: | + hardis-report ########################################################### ## Run MegaLinter to detect quality and security issues ### @@ -218,7 +216,7 @@ jobs: fetch-depth: 0 - name: Git pull - run: git pull origin ${{ matrix.branch }} + run: git pull origin ${{ matrix.branch }} # Mega-Linter - name: Mega-Linter @@ -242,9 +240,10 @@ jobs: # Upload Mega-Linter artifacts - name: Archive production artifacts if: success() || failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Mega-Linter reports + include-hidden-files: "true" path: | megalinter-reports mega-linter.log @@ -266,64 +265,64 @@ jobs: - monitoring_myclient__recette_sandbox - monitoring_myclient__preprod_sandbox steps: - # Checkout repo - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Faster code checkout fetching only latest commit - ref: ${{ matrix.branch }} - persist-credentials: true - # Setup node - - name: Setup Node - uses: actions/setup-node@v3 - with: - node-version: "20" - # SFDX & plugins - - name: Install SFDX and plugins - run: | - npm install --no-cache @salesforce/cli --global - sf plugins install --force @salesforce/plugin-packaging - echo 'y' | sfdx plugins:install --force sfdx-hardis - echo 'y' | sfdx plugins:install --force sfdx-essentials - echo 'y' | sfdx plugins:install --force sfdx-git-delta - sf version --verbose --json - # Login & check deploy with test classes & code coverage - - name: Login & Run monitoring checks - env: - # MANUAL: Update variables below to add authentication variables you need - SFDX_CLIENT_ID_MONITORING_MYCLIENT: ${{ secrets.SFDX_CLIENT_ID_MONITORING_MYCLIENT}} - SFDX_CLIENT_KEY_MONITORING_MYCLIENT: ${{ secrets.SFDX_CLIENT_KEY_MONITORING_MYCLIENT}} - SFDX_CLIENT_ID_MONITORING_MYCLIENT__RECETTE_SANDBOX: ${{ secrets.SFDX_CLIENT_ID_MONITORING_MYCLIENT__RECETTE_SANDBOX}} - SFDX_CLIENT_KEY_MONITORING_MYCLIENT__RECETTE_SANDBOX: ${{ secrets.SFDX_CLIENT_KEY_MONITORING_MYCLIENT__RECETTE_SANDBOX}} - SFDX_CLIENT_ID_MONITORING_MYCLIENT__PREPROD_SANDBOX: ${{ secrets.SFDX_CLIENT_ID_MONITORING_MYCLIENT__PREPROD_SANDBOX}} - SFDX_CLIENT_KEY_MONITORING_MYCLIENT__PREPROD_SANDBOX: ${{ secrets.SFDX_CLIENT_KEY_MONITORING_MYCLIENT__PREPROD_SANDBOX}} - SFDX_DEPLOY_WAIT_MINUTES: 120 # Override if necessary - SFDX_TEST_WAIT_MINUTES: 120 # Override if necessary - CI_COMMIT_REF_NAME: ${{ matrix.branch }} - ORG_ALIAS: ${{ matrix.branch }} - CONFIG_BRANCH: ${{ matrix.branch }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} - SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} - NOTIF_EMAIL_ADDRESS: ${{ secrets.NOTIF_EMAIL_ADDRESS }} - FORCE_COLOR: "1" - NOTIF_API_URL: ${{ secrets.NOTIF_API_URL }} - NOTIF_API_BASIC_AUTH_USERNAME: ${{ secrets.NOTIF_API_BASIC_AUTH_USERNAME }} - NOTIF_API_BASIC_AUTH_PASSWORD: ${{ secrets.NOTIF_API_BASIC_AUTH_PASSWORD }} - NOTIF_API_METRICS_URL: ${{ secrets.NOTIF_API_METRICS_URL }} - NOTIF_API_METRICS_BASIC_AUTH_USERNAME: ${{ secrets.NOTIF_API_METRICS_BASIC_AUTH_USERNAME }} - NOTIF_API_METRICS_BASIC_AUTH_PASSWORD: ${{ secrets.NOTIF_API_METRICS_BASIC_AUTH_PASSWORD }} + # Checkout repo + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Faster code checkout fetching only latest commit + ref: ${{ matrix.branch }} + persist-credentials: true + # Setup node + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: "20" + # SFDX & plugins + - name: Install SFDX and plugins + run: | + npm install --no-cache @salesforce/cli --global + sf plugins install --force @salesforce/plugin-packaging + echo 'y' | sf plugins install --force sfdx-hardis + echo 'y' | sf plugins install --force sfdx-git-delta + sf version --verbose --json + # Login & check deploy with test classes & code coverage + - name: Login & Run monitoring checks + env: + # MANUAL: Update variables below to add authentication variables you need + SFDX_CLIENT_ID_MONITORING_MYCLIENT: ${{ secrets.SFDX_CLIENT_ID_MONITORING_MYCLIENT}} + SFDX_CLIENT_KEY_MONITORING_MYCLIENT: ${{ secrets.SFDX_CLIENT_KEY_MONITORING_MYCLIENT}} + SFDX_CLIENT_ID_MONITORING_MYCLIENT__RECETTE_SANDBOX: ${{ secrets.SFDX_CLIENT_ID_MONITORING_MYCLIENT__RECETTE_SANDBOX}} + SFDX_CLIENT_KEY_MONITORING_MYCLIENT__RECETTE_SANDBOX: ${{ secrets.SFDX_CLIENT_KEY_MONITORING_MYCLIENT__RECETTE_SANDBOX}} + SFDX_CLIENT_ID_MONITORING_MYCLIENT__PREPROD_SANDBOX: ${{ secrets.SFDX_CLIENT_ID_MONITORING_MYCLIENT__PREPROD_SANDBOX}} + SFDX_CLIENT_KEY_MONITORING_MYCLIENT__PREPROD_SANDBOX: ${{ secrets.SFDX_CLIENT_KEY_MONITORING_MYCLIENT__PREPROD_SANDBOX}} + SFDX_DEPLOY_WAIT_MINUTES: 120 # Override if necessary + SFDX_TEST_WAIT_MINUTES: 120 # Override if necessary + CI_COMMIT_REF_NAME: ${{ matrix.branch }} + ORG_ALIAS: ${{ matrix.branch }} + CONFIG_BRANCH: ${{ matrix.branch }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} + SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} + NOTIF_EMAIL_ADDRESS: ${{ secrets.NOTIF_EMAIL_ADDRESS }} + FORCE_COLOR: "1" + NOTIF_API_URL: ${{ secrets.NOTIF_API_URL }} + NOTIF_API_BASIC_AUTH_USERNAME: ${{ secrets.NOTIF_API_BASIC_AUTH_USERNAME }} + NOTIF_API_BASIC_AUTH_PASSWORD: ${{ secrets.NOTIF_API_BASIC_AUTH_PASSWORD }} + NOTIF_API_METRICS_URL: ${{ secrets.NOTIF_API_METRICS_URL }} + NOTIF_API_METRICS_BASIC_AUTH_USERNAME: ${{ secrets.NOTIF_API_METRICS_BASIC_AUTH_USERNAME }} + NOTIF_API_METRICS_BASIC_AUTH_PASSWORD: ${{ secrets.NOTIF_API_METRICS_BASIC_AUTH_PASSWORD }} - run: | - echo "Run Monitoring checks against \"$CONFIG_BRANCH\"" - git pull origin ${{ matrix.branch }} - sfdx hardis:auth:login - sfdx hardis:org:monitor:all + run: | + echo "Run Monitoring checks against \"$CONFIG_BRANCH\"" + git pull origin ${{ matrix.branch }} + sf hardis:th:login + sf hardis:org:monitor:all - - name: Upload artifacts - if: success() || failure() - uses: actions/upload-artifact@v3 - with: - name: Hardis Monitoring reports - path: | - hardis-report \ No newline at end of file + - name: Upload artifacts + if: success() || failure() + uses: actions/upload-artifact@v4 + with: + name: Hardis Monitoring reports + include-hidden-files: "true" + path: | + hardis-report diff --git a/defaults/monitoring/.gitlab-ci.yml b/defaults/monitoring/.gitlab-ci.yml index d55dbdca1..cf69d4ab9 100644 --- a/defaults/monitoring/.gitlab-ci.yml +++ b/defaults/monitoring/.gitlab-ci.yml @@ -30,8 +30,8 @@ backup: - git config --global user.email "${GITLAB_USER_EMAIL}" script: # Extract metadata folders (+other checks ^^) - - sfdx hardis:auth:login - - sfdx hardis:org:monitor:backup + - sf hardis:auth:login + - sf hardis:org:monitor:backup # Commit and push new state - git status - git add --all @@ -58,8 +58,8 @@ apex_tests: - git pull origin "${CI_COMMIT_REF_NAME}" script: # Login & run apex tests - - sfdx hardis:auth:login - - sfdx hardis:org:test:apex + - sf hardis:auth:login + - sf hardis:org:test:apex artifacts: when: always paths: @@ -79,8 +79,8 @@ monitoring_tools: - git pull origin "${CI_COMMIT_REF_NAME}" script: # Login & run apex tests - - sfdx hardis:auth:login - - sfdx hardis:org:monitor:all + - sf hardis:auth:login + - sf hardis:org:monitor:all artifacts: when: always paths: diff --git a/defaults/monitoring/README.md b/defaults/monitoring/README.md index 9de1287e3..de419d896 100644 --- a/defaults/monitoring/README.md +++ b/defaults/monitoring/README.md @@ -2,7 +2,7 @@ All documentation here -> https://sfdx-hardis.cloudity.com/salesforce-monitoring-home/ -Just use the command [sfdx hardis:org:configure:monitoring](https://sfdx-hardis.cloudity.com/) in case your monitored org is a sandbox that has been refreshed ! +Just use the command [sf hardis:org:configure:monitoring](https://sfdx-hardis.cloudity.com/) in case your monitored org is a sandbox that has been refreshed ! If you have any issue, question or remark, please notify us on [sfdx-hardis monitoring](https://github.com/hardisgroupcom/sfdx-hardis/issues) diff --git a/defaults/monitoring/azure-pipelines.yml b/defaults/monitoring/azure-pipelines.yml index 5907de579..950dff7d8 100644 --- a/defaults/monitoring/azure-pipelines.yml +++ b/defaults/monitoring/azure-pipelines.yml @@ -49,8 +49,7 @@ jobs: - script: | npm install @salesforce/cli -g sf plugins install @salesforce/plugin-packaging - echo y | sfdx plugins:install sfdx-hardis - echo y | sfdx plugins:install sfdx-essentials + echo y | sf plugins install sfdx-hardis sf version --verbose --json workingDirectory: $(System.DefaultWorkingDirectory) displayName: Install @salesforce/cli & sfdx-hardis @@ -62,8 +61,8 @@ jobs: CI: "true" - script: | - sfdx hardis:auth:login - sfdx hardis:org:monitor:backup + sf hardis:auth:login + sf hardis:org:monitor:backup displayName: sfdx-hardis login & Backup env: # MANUAL: Add your branch related variables here @@ -130,8 +129,7 @@ jobs: - script: | npm install @salesforce/cli -g sf plugins install @salesforce/plugin-packaging - echo y | sfdx plugins:install sfdx-hardis - echo y | sfdx plugins:install sfdx-essentials + echo y | sf plugins install sfdx-hardis sf version --verbose --json workingDirectory: $(System.DefaultWorkingDirectory) displayName: Install @salesforce/cli & sfdx-hardis @@ -143,8 +141,8 @@ jobs: CI: "true" - script: | - sfdx hardis:auth:login - sfdx hardis:org:test:apex + sf hardis:auth:login + sf hardis:org:test:apex continueOnError: "true" displayName: sfdx-hardis login & Apex tests env: @@ -248,8 +246,7 @@ jobs: - script: | npm install @salesforce/cli -g sf plugins install @salesforce/plugin-packaging - echo y | sfdx plugins:install sfdx-hardis - echo y | sfdx plugins:install sfdx-essentials + echo y | sf plugins install sfdx-hardis sf version --verbose --json workingDirectory: $(System.DefaultWorkingDirectory) displayName: Install @salesforce/cli & sfdx-hardis @@ -263,8 +260,8 @@ jobs: CI: "true" - script: | - sfdx hardis:auth:login - sfdx hardis:org:monitor:all + sf hardis:auth:login + sf hardis:org:monitor:all displayName: sfdx-hardis login & other checks continueOnError: "true" env: diff --git a/defaults/monitoring/bitbucket-pipelines.yml b/defaults/monitoring/bitbucket-pipelines.yml index 202dec996..104b30a2f 100644 --- a/defaults/monitoring/bitbucket-pipelines.yml +++ b/defaults/monitoring/bitbucket-pipelines.yml @@ -20,9 +20,8 @@ pipelines: # Install SF Cli & dependencies - npm install --no-cache @salesforce/cli --global - sf plugins install @salesforce/plugin-packaging - - echo 'y' | sfdx plugins:install sfdx-hardis - - echo 'y' | sfdx plugins:install sfdx-essentials - - echo 'y' | sfdx plugins:install sfdx-git-delta + - echo 'y' | sf plugins install sfdx-hardis + - echo 'y' | sf plugins install sfdx-git-delta - sf version --verbose --json - export BRANCH_NAME=$(echo "$BITBUCKET_BRANCH" | sed 's/refs\/heads\///') - export CI_COMMIT_REF_NAME=$BRANCH_NAME @@ -30,9 +29,9 @@ pipelines: - export ORG_ALIAS=$BRANCH_NAME - export FORCE_COLOR=1 # Login - - sfdx hardis:auth:login + - sf hardis:auth:login # Backup - - sfdx hardis:org:monitor:backup + - sf hardis:org:monitor:backup # Commit & push - git status - git add --all @@ -51,9 +50,8 @@ pipelines: # Install SF Cli & dependencies - npm install --no-cache @salesforce/cli --global - sf plugins install @salesforce/plugin-packaging - - echo 'y' | sfdx plugins:install sfdx-hardis - - echo 'y' | sfdx plugins:install sfdx-essentials - - echo 'y' | sfdx plugins:install sfdx-git-delta + - echo 'y' | sf plugins install sfdx-hardis + - echo 'y' | sf plugins install sfdx-git-delta - sf version --verbose --json - export BRANCH_NAME=$(echo "$BITBUCKET_BRANCH" | sed 's/refs\/heads\///') - export CI_COMMIT_REF_NAME=$BRANCH_NAME @@ -63,9 +61,9 @@ pipelines: # Get latest commit of the branch - git pull origin "${BRANCH_NAME}" # Login - - sfdx hardis:auth:login + - sf hardis:auth:login # Apex tests - - sfdx hardis:org:test:apex + - sf hardis:org:test:apex artifacts: - hardis-report/** @@ -91,9 +89,8 @@ pipelines: # Install SF Cli & dependencies - npm install --no-cache @salesforce/cli --global - sf plugins install @salesforce/plugin-packaging - - echo 'y' | sfdx plugins:install sfdx-hardis - - echo 'y' | sfdx plugins:install sfdx-essentials - - echo 'y' | sfdx plugins:install sfdx-git-delta + - echo 'y' | sf plugins install sfdx-hardis + - echo 'y' | sf plugins install sfdx-git-delta - sf version --verbose --json - export BRANCH_NAME=$(echo "$BITBUCKET_BRANCH" | sed 's/refs\/heads\///') - export CI_COMMIT_REF_NAME=$BRANCH_NAME @@ -103,8 +100,8 @@ pipelines: # Get latest commit of the branch - git pull origin "${BRANCH_NAME}" # Login - - sfdx hardis:auth:login + - sf hardis:auth:login # Other monitoring tools - - sfdx hardis:org:monitor:all + - sf hardis:org:monitor:all artifacts: - hardis-report/** diff --git a/docs/assets/images/play-dreamforce-session.png b/docs/assets/images/play-dreamforce-session.png new file mode 100644 index 000000000..6f326183e Binary files /dev/null and b/docs/assets/images/play-dreamforce-session.png differ diff --git a/docs/assets/images/play-install-tuto.png b/docs/assets/images/play-install-tuto.png new file mode 100644 index 000000000..6a6f17799 Binary files /dev/null and b/docs/assets/images/play-install-tuto.png differ diff --git a/docs/commands.md b/docs/commands.md index db15d60eb..db5d0c192 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -4,155 +4,179 @@ | Command | Title | |:----------------------------------------------|:------| -| [**hardis:auth:login**](hardis/auth/login.md) | Login | +| [**hardis:auth:login**](hardis/auth/login.md) | | ## hardis:cache -| Command | Title | -|:------------------------------------------------|:------------------------| -| [**hardis:cache:clear**](hardis/cache/clear.md) | Clear sfdx-hardis cache | +| Command | Title | +|:------------------------------------------------|:------| +| [**hardis:cache:clear**](hardis/cache/clear.md) | | ## hardis:config -| Command | Title | -|:----------------------------------------------|:-------------------------------| -| [**hardis:config:get**](hardis/config/get.md) | Deploy metadata sources to org | +| Command | Title | +|:----------------------------------------------|:------| +| [**hardis:config:get**](hardis/config/get.md) | | ## hardis:doc -| Command | Title | -|:----------------------------------------------------------------------------|:-----------------------------------| -| [**hardis:doc:extract:permsetgroups**](hardis/doc/extract/permsetgroups.md) | Generate project documentation | -| [**hardis:doc:plugin:generate**](hardis/doc/plugin/generate.md) | Generate SFDX Plugin Documentation | +| Command | Title | +|:----------------------------------------------------------------------------|:------| +| [**hardis:doc:extract:permsetgroups**](hardis/doc/extract/permsetgroups.md) | | +| [**hardis:doc:plugin:generate**](hardis/doc/plugin/generate.md) | | ## hardis:lint -| Command | Title | -|:----------------------------------------------------------------------|:-------------------------------------------| -| [**hardis:lint:access**](hardis/lint/access.md) | check permission access | -| [**hardis:lint:metadatastatus**](hardis/lint/metadatastatus.md) | check inactive metadatas | -| [**hardis:lint:missingattributes**](hardis/lint/missingattributes.md) | check missing description on custom fields | -| [**hardis:lint:unusedmetadatas**](hardis/lint/unusedmetadatas.md) | check unused labels and custom permissions | +| Command | Title | +|:----------------------------------------------------------------------|:------| +| [**hardis:lint:access**](hardis/lint/access.md) | | +| [**hardis:lint:metadatastatus**](hardis/lint/metadatastatus.md) | | +| [**hardis:lint:missingattributes**](hardis/lint/missingattributes.md) | | +| [**hardis:lint:unusedmetadatas**](hardis/lint/unusedmetadatas.md) | | ## hardis:mdapi -| Command | Title | -|:--------------------------------------------------|:-----------------------------------------------------------------------------------------------| -| [**hardis:mdapi:deploy**](hardis/mdapi/deploy.md) | sfdx-hardis wrapper for sfdx force:mdapi:deploy that displays tips to solve deployment errors. | +| Command | Title | +|:--------------------------------------------------|:------| +| [**hardis:mdapi:deploy**](hardis/mdapi/deploy.md) | | ## hardis:misc -| Command | Title | -|:----------------------------------------------------|:------------| -| [**hardis:misc:toml2csv**](hardis/misc/toml2csv.md) | TOML to CSV | +| Command | Title | +|:--------------------------------------------------------------------|:------| +| [**hardis:misc:purge-references**](hardis/misc/purge-references.md) | | +| [**hardis:misc:toml2csv**](hardis/misc/toml2csv.md) | | ## hardis:org -| Command | Title | -|:--------------------------------------------------------------------------------------|:------------------------------------------------------| -| [**hardis:org:configure:data**](hardis/org/configure/data.md) | Configure Data project | -| [**hardis:org:configure:files**](hardis/org/configure/files.md) | Configure File export project | -| [**hardis:org:configure:monitoring**](hardis/org/configure/monitoring.md) | Configure org monitoring | -| [**hardis:org:connect**](hardis/org/connect.md) | Connect to an org | -| [**hardis:org:create**](hardis/org/create.md) | Create sandbox org | -| [**hardis:org:data:delete**](hardis/org/data/delete.md) | Delete data | -| [**hardis:org:data:export**](hardis/org/data/export.md) | Export data | -| [**hardis:org:data:import**](hardis/org/data/import.md) | Import data | -| [**hardis:org:diagnose:audittrail**](hardis/org/diagnose/audittrail.md) | Diagnose content of Setup Audit Trail | -| [**hardis:org:diagnose:legacyapi**](hardis/org/diagnose/legacyapi.md) | Check for legacy API use | -| [**hardis:org:diagnose:licenses**](hardis/org/diagnose/licenses.md) | List licenses subscribed and used in a Salesforce org | -| [**hardis:org:diagnose:unusedlicenses**](hardis/org/diagnose/unusedlicenses.md) | Detect unused Permission Set Licenses | -| [**hardis:org:diagnose:unusedusers**](hardis/org/diagnose/unusedusers.md) | Detect unused Users in Salesforce | -| [**hardis:org:files:export**](hardis/org/files/export.md) | Export files | -| [**hardis:org:fix:listviewmine**](hardis/org/fix/listviewmine.md) | Fix listviews with | -| [**hardis:org:generate:packagexmlfull**](hardis/org/generate/packagexmlfull.md) | Generate Full Org package.xml | -| [**hardis:org:monitor:all**](hardis/org/monitor/all.md) | Monitor org | -| [**hardis:org:monitor:backup**](hardis/org/monitor/backup.md) | Backup DX sources | -| [**hardis:org:monitor:limits**](hardis/org/monitor/limits.md) | Check org limits | -| [**hardis:org:purge:apexlog**](hardis/org/purge/apexlog.md) | Purge Apex Logs | -| [**hardis:org:purge:flow**](hardis/org/purge/flow.md) | Purge Flow versions | -| [**hardis:org:retrieve:packageconfig**](hardis/org/retrieve/packageconfig.md) | Retrieve package configuration from an org | -| [**hardis:org:retrieve:sources:analytics**](hardis/org/retrieve/sources/analytics.md) | Retrieve CRM Analytics configuration from an org | -| [**hardis:org:retrieve:sources:dx**](hardis/org/retrieve/sources/dx.md) | Retrieve sfdx sources from org | -| [**hardis:org:retrieve:sources:dx2**](hardis/org/retrieve/sources/dx2.md) | Retrieve sfdx sources from org (2) | -| [**hardis:org:retrieve:sources:metadata**](hardis/org/retrieve/sources/metadata.md) | Retrieve sfdx sources from org | -| [**hardis:org:retrieve:sources:retrofit**](hardis/org/retrieve/sources/retrofit.md) | Retrofit changes from an org | -| [**hardis:org:select**](hardis/org/select.md) | Select org | -| [**hardis:org:test:apex**](hardis/org/test/apex.md) | Run apex tests | -| [**hardis:org:user:activateinvalid**](hardis/org/user/activateinvalid.md) | Reactivate sandbox invalid users | -| [**hardis:org:user:freeze**](hardis/org/user/freeze.md) | Freeze user logins | -| [**hardis:org:user:unfreeze**](hardis/org/user/unfreeze.md) | Unfreeze user logins | +| Command | Title | +|:--------------------------------------------------------------------------------------|:------| +| [**hardis:org:configure:data**](hardis/org/configure/data.md) | | +| [**hardis:org:configure:files**](hardis/org/configure/files.md) | | +| [**hardis:org:configure:monitoring**](hardis/org/configure/monitoring.md) | | +| [**hardis:org:connect**](hardis/org/connect.md) | | +| [**hardis:org:create**](hardis/org/create.md) | | +| [**hardis:org:data:delete**](hardis/org/data/delete.md) | | +| [**hardis:org:data:export**](hardis/org/data/export.md) | | +| [**hardis:org:data:import**](hardis/org/data/import.md) | | +| [**hardis:org:diagnose:audittrail**](hardis/org/diagnose/audittrail.md) | | +| [**hardis:org:diagnose:instanceupgrade**](hardis/org/diagnose/instanceupgrade.md) | | +| [**hardis:org:diagnose:legacyapi**](hardis/org/diagnose/legacyapi.md) | | +| [**hardis:org:diagnose:licenses**](hardis/org/diagnose/licenses.md) | | +| [**hardis:org:diagnose:releaseupdates**](hardis/org/diagnose/releaseupdates.md) | | +| [**hardis:org:diagnose:unusedlicenses**](hardis/org/diagnose/unusedlicenses.md) | | +| [**hardis:org:diagnose:unusedusers**](hardis/org/diagnose/unusedusers.md) | | +| [**hardis:org:files:export**](hardis/org/files/export.md) | | +| [**hardis:org:files:import**](hardis/org/files/import.md) | | +| [**hardis:org:fix:listviewmine**](hardis/org/fix/listviewmine.md) | | +| [**hardis:org:generate:packagexmlfull**](hardis/org/generate/packagexmlfull.md) | | +| [**hardis:org:monitor:all**](hardis/org/monitor/all.md) | | +| [**hardis:org:monitor:backup**](hardis/org/monitor/backup.md) | | +| [**hardis:org:monitor:limits**](hardis/org/monitor/limits.md) | | +| [**hardis:org:purge:apexlog**](hardis/org/purge/apexlog.md) | | +| [**hardis:org:purge:flow**](hardis/org/purge/flow.md) | | +| [**hardis:org:retrieve:packageconfig**](hardis/org/retrieve/packageconfig.md) | | +| [**hardis:org:retrieve:sources:analytics**](hardis/org/retrieve/sources/analytics.md) | | +| [**hardis:org:retrieve:sources:dx**](hardis/org/retrieve/sources/dx.md) | | +| [**hardis:org:retrieve:sources:dx2**](hardis/org/retrieve/sources/dx2.md) | | +| [**hardis:org:retrieve:sources:metadata**](hardis/org/retrieve/sources/metadata.md) | | +| [**hardis:org:retrieve:sources:retrofit**](hardis/org/retrieve/sources/retrofit.md) | | +| [**hardis:org:select**](hardis/org/select.md) | | +| [**hardis:org:test:apex**](hardis/org/test/apex.md) | | +| [**hardis:org:user:activateinvalid**](hardis/org/user/activateinvalid.md) | | +| [**hardis:org:user:freeze**](hardis/org/user/freeze.md) | | +| [**hardis:org:user:unfreeze**](hardis/org/user/unfreeze.md) | | ## hardis:package -| Command | Title | -|:------------------------------------------------------------------------|:-----------------------------------| -| [**hardis:package:create**](hardis/package/create.md) | Create a new package | -| [**hardis:package:install**](hardis/package/install.md) | Install packages in an org | -| [**hardis:package:mergexml**](hardis/package/mergexml.md) | Merge package.xml files | -| [**hardis:package:version:create**](hardis/package/version/create.md) | Create a new version of a package | -| [**hardis:package:version:list**](hardis/package/version/list.md) | Create a new version of a package | -| [**hardis:package:version:promote**](hardis/package/version/promote.md) | Promote new versions of package(s) | +| Command | Title | +|:------------------------------------------------------------------------|:------| +| [**hardis:package:create**](hardis/package/create.md) | | +| [**hardis:package:install**](hardis/package/install.md) | | +| [**hardis:package:mergexml**](hardis/package/mergexml.md) | | +| [**hardis:package:version:create**](hardis/package/version/create.md) | | +| [**hardis:package:version:list**](hardis/package/version/list.md) | | +| [**hardis:package:version:promote**](hardis/package/version/promote.md) | | + +## hardis:packagexml + +| Command | Title | +|:------------------------------------------------------------|:------| +| [**hardis:packagexml:append**](hardis/packagexml/append.md) | | +| [**hardis:packagexml:remove**](hardis/packagexml/remove.md) | | ## hardis:project -| Command | Title | -|:----------------------------------------------------------------------------------------------|:----------------------------------------------------------------| -| [**hardis:project:audit:apiversion**](hardis/project/audit/apiversion.md) | Audit Metadatas API Version | -| [**hardis:project:audit:callincallout**](hardis/project/audit/callincallout.md) | Audit CallIns and CallOuts | -| [**hardis:project:audit:duplicatefiles**](hardis/project/audit/duplicatefiles.md) | Find duplicate sfdx files | -| [**hardis:project:audit:remotesites**](hardis/project/audit/remotesites.md) | Audit Remote Sites | -| [**hardis:project:clean:emptyitems**](hardis/project/clean/emptyitems.md) | Clean retrieved empty items in dx sources | -| [**hardis:project:clean:flowpositions**](hardis/project/clean/flowpositions.md) | Clean Flow Positions | -| [**hardis:project:clean:hiddenitems**](hardis/project/clean/hiddenitems.md) | Clean retrieved hidden items in dx sources | -| [**hardis:project:clean:listviews**](hardis/project/clean/listviews.md) | Replace Mine by Everything in ListViews | -| [**hardis:project:clean:manageditems**](hardis/project/clean/manageditems.md) | Clean retrieved managed items in dx sources | -| [**hardis:project:clean:minimizeprofiles**](hardis/project/clean/minimizeprofiles.md) | Clean profiles of Permission Set attributes | -| [**hardis:project:clean:orgmissingitems**](hardis/project/clean/orgmissingitems.md) | Clean SFDX items using target org definition | -| [**hardis:project:clean:references**](hardis/project/clean/references.md) | Clean references in dx sources | -| [**hardis:project:clean:retrievefolders**](hardis/project/clean/retrievefolders.md) | Retrieve dashboards, documents and report folders in DX sources | -| [**hardis:project:clean:standarditems**](hardis/project/clean/standarditems.md) | Clean retrieved standard items in dx sources | -| [**hardis:project:clean:systemdebug**](hardis/project/clean/systemdebug.md) | Clean System debug | -| [**hardis:project:clean:xml**](hardis/project/clean/xml.md) | Clean retrieved empty items in dx sources | -| [**hardis:project:configure:auth**](hardis/project/configure/auth.md) | Configure authentication | -| [**hardis:project:convert:profilestopermsets**](hardis/project/convert/profilestopermsets.md) | Convert Profiles into Permission Sets | -| [**hardis:project:create**](hardis/project/create.md) | Login | -| [**hardis:project:deploy:sources:dx**](hardis/project/deploy/sources/dx.md) | Deploy sfdx sources to org | -| [**hardis:project:deploy:sources:metadata**](hardis/project/deploy/sources/metadata.md) | Deploy metadata sources to org | -| [**hardis:project:fix:profiletabs**](hardis/project/fix/profiletabs.md) | Fix profiles to add tabs that are not retrieved by SF CLI | -| [**hardis:project:fix:v53flexipages**](hardis/project/fix/v53flexipages.md) | Fix flexipages for v53 | -| [**hardis:project:generate:gitdelta**](hardis/project/generate/gitdelta.md) | Generate Git Delta | -| [**hardis:project:lint**](hardis/project/lint.md) | Lint | -| [**hardis:project:metadata:findduplicates**](hardis/project/metadata/findduplicates.md) | XML duplicate values finder | +| Command | Title | +|:----------------------------------------------------------------------------------------------|:------| +| [**hardis:project:audit:apiversion**](hardis/project/audit/apiversion.md) | | +| [**hardis:project:audit:callincallout**](hardis/project/audit/callincallout.md) | | +| [**hardis:project:audit:duplicatefiles**](hardis/project/audit/duplicatefiles.md) | | +| [**hardis:project:audit:remotesites**](hardis/project/audit/remotesites.md) | | +| [**hardis:project:clean:emptyitems**](hardis/project/clean/emptyitems.md) | | +| [**hardis:project:clean:filter-xml-content**](hardis/project/clean/filter-xml-content.md) | | +| [**hardis:project:clean:flowpositions**](hardis/project/clean/flowpositions.md) | | +| [**hardis:project:clean:hiddenitems**](hardis/project/clean/hiddenitems.md) | | +| [**hardis:project:clean:listviews**](hardis/project/clean/listviews.md) | | +| [**hardis:project:clean:manageditems**](hardis/project/clean/manageditems.md) | | +| [**hardis:project:clean:minimizeprofiles**](hardis/project/clean/minimizeprofiles.md) | | +| [**hardis:project:clean:orgmissingitems**](hardis/project/clean/orgmissingitems.md) | | +| [**hardis:project:clean:references**](hardis/project/clean/references.md) | | +| [**hardis:project:clean:retrievefolders**](hardis/project/clean/retrievefolders.md) | | +| [**hardis:project:clean:sensitive-metadatas**](hardis/project/clean/sensitive-metadatas.md) | | +| [**hardis:project:clean:standarditems**](hardis/project/clean/standarditems.md) | | +| [**hardis:project:clean:systemdebug**](hardis/project/clean/systemdebug.md) | | +| [**hardis:project:clean:xml**](hardis/project/clean/xml.md) | | +| [**hardis:project:configure:auth**](hardis/project/configure/auth.md) | | +| [**hardis:project:convert:profilestopermsets**](hardis/project/convert/profilestopermsets.md) | | +| [**hardis:project:create**](hardis/project/create.md) | | +| [**hardis:project:deploy:quick**](hardis/project/deploy/quick.md) | | +| [**hardis:project:deploy:simulate**](hardis/project/deploy/simulate.md) | | +| [**hardis:project:deploy:smart**](hardis/project/deploy/smart.md) | | +| [**hardis:project:deploy:sources:dx**](hardis/project/deploy/sources/dx.md) | | +| [**hardis:project:deploy:sources:metadata**](hardis/project/deploy/sources/metadata.md) | | +| [**hardis:project:deploy:start**](hardis/project/deploy/start.md) | | +| [**hardis:project:deploy:validate**](hardis/project/deploy/validate.md) | | +| [**hardis:project:fix:profiletabs**](hardis/project/fix/profiletabs.md) | | +| [**hardis:project:fix:v53flexipages**](hardis/project/fix/v53flexipages.md) | | +| [**hardis:project:generate:gitdelta**](hardis/project/generate/gitdelta.md) | | +| [**hardis:project:lint**](hardis/project/lint.md) | | +| [**hardis:project:metadata:findduplicates**](hardis/project/metadata/findduplicates.md) | | ## hardis:scratch -| Command | Title | -|:----------------------------------------------------------------------|:-----------------------------------------| -| [**hardis:scratch:create**](hardis/scratch/create.md) | Create and initialize scratch org | -| [**hardis:scratch:delete**](hardis/scratch/delete.md) | Delete scratch orgs(s) | -| [**hardis:scratch:pool:create**](hardis/scratch/pool/create.md) | Create and configure scratch org pool | -| [**hardis:scratch:pool:localauth**](hardis/scratch/pool/localauth.md) | Authenticate locally to scratch org pool | -| [**hardis:scratch:pool:refresh**](hardis/scratch/pool/refresh.md) | Refresh scratch org pool | -| [**hardis:scratch:pool:reset**](hardis/scratch/pool/reset.md) | Reset scratch org pool | -| [**hardis:scratch:pool:view**](hardis/scratch/pool/view.md) | View scratch org pool info | -| [**hardis:scratch:pull**](hardis/scratch/pull.md) | Scratch PULL | -| [**hardis:scratch:push**](hardis/scratch/push.md) | Scratch PUSH | +| Command | Title | +|:----------------------------------------------------------------------|:------| +| [**hardis:scratch:create**](hardis/scratch/create.md) | | +| [**hardis:scratch:delete**](hardis/scratch/delete.md) | | +| [**hardis:scratch:pool:create**](hardis/scratch/pool/create.md) | | +| [**hardis:scratch:pool:localauth**](hardis/scratch/pool/localauth.md) | | +| [**hardis:scratch:pool:refresh**](hardis/scratch/pool/refresh.md) | | +| [**hardis:scratch:pool:reset**](hardis/scratch/pool/reset.md) | | +| [**hardis:scratch:pool:view**](hardis/scratch/pool/view.md) | | +| [**hardis:scratch:pull**](hardis/scratch/pull.md) | | +| [**hardis:scratch:push**](hardis/scratch/push.md) | | ## hardis:source -| Command | Title | -|:--------------------------------------------------------|:------------------------------------------------------------------------------------------------| -| [**hardis:source:deploy**](hardis/source/deploy.md) | sfdx-hardis wrapper for sfdx force:source:deploy that displays tips to solve deployment errors. | -| [**hardis:source:push**](hardis/source/push.md) | sfdx-hardis wrapper for sfdx force:source:push that displays tips to solve deployment errors. | -| [**hardis:source:retrieve**](hardis/source/retrieve.md) | sfdx-hardis wrapper for sfdx force:source:retrieve | +| Command | Title | +|:--------------------------------------------------------|:------| +| [**hardis:source:deploy**](hardis/source/deploy.md) | | +| [**hardis:source:push**](hardis/source/push.md) | | +| [**hardis:source:retrieve**](hardis/source/retrieve.md) | | ## hardis:work -| Command | Title | -|:----------------------------------------------------------------|:---------------------| -| [**hardis:work:new**](hardis/work/new.md) | New work task | -| [**hardis:work:refresh**](hardis/work/refresh.md) | Refresh work task | -| [**hardis:work:resetselection**](hardis/work/resetselection.md) | Select again | -| [**hardis:work:save**](hardis/work/save.md) | Save work task | -| [**hardis:work:ws**](hardis/work/ws.md) | WebSocket operations | +| Command | Title | +|:----------------------------------------------------------------|:------| +| [**hardis:work:new**](hardis/work/new.md) | | +| [**hardis:work:refresh**](hardis/work/refresh.md) | | +| [**hardis:work:resetselection**](hardis/work/resetselection.md) | | +| [**hardis:work:save**](hardis/work/save.md) | | +| [**hardis:work:ws**](hardis/work/ws.md) | | + +## hello:world + +| Command | Title | +|:----------------------------------|:------| +| [**hello:world**](hello/world.md) | | diff --git a/docs/grafana/dashboards/DASH - 00_Home.json b/docs/grafana/dashboards/DASH - 00_Home.json index 6a52b2530..f9876f17b 100644 --- a/docs/grafana/dashboards/DASH - 00_Home.json +++ b/docs/grafana/dashboards/DASH - 00_Home.json @@ -21,7 +21,7 @@ "type": "grafana", "id": "grafana", "name": "Grafana", - "version": "11.1.0-70958" + "version": "11.3.0-75420" }, { "type": "datasource", @@ -123,8 +123,11 @@ "graphMode": "area", "justifyMode": "center", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "", "values": false }, @@ -132,7 +135,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -216,7 +219,9 @@ "minVizWidth": 75, "orientation": "auto", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "", "limit": 1, "values": false @@ -225,7 +230,7 @@ "showThresholdMarkers": true, "sizing": "auto" }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -343,7 +348,9 @@ "minVizWidth": 75, "orientation": "auto", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "", "limit": 1, "values": false @@ -352,7 +359,7 @@ "showThresholdMarkers": true, "sizing": "auto" }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -470,7 +477,9 @@ "minVizWidth": 75, "orientation": "auto", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "", "limit": 1, "values": false @@ -479,7 +488,7 @@ "showThresholdMarkers": true, "sizing": "auto" }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -597,7 +606,9 @@ "minVizWidth": 75, "orientation": "auto", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "", "limit": 1, "values": false @@ -606,7 +617,7 @@ "showThresholdMarkers": true, "sizing": "auto" }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -724,7 +735,9 @@ "minVizWidth": 75, "orientation": "auto", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "", "limit": 1, "values": false @@ -733,7 +746,7 @@ "showThresholdMarkers": true, "sizing": "auto" }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -855,8 +868,11 @@ "graphMode": "area", "justifyMode": "center", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "", "values": false }, @@ -864,7 +880,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -946,8 +962,11 @@ "graphMode": "area", "justifyMode": "center", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "", "values": false }, @@ -955,7 +974,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -1031,8 +1050,11 @@ "graphMode": "area", "justifyMode": "center", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "", "values": false }, @@ -1040,7 +1062,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -1110,7 +1132,7 @@ }, "gridPos": { "h": 4, - "w": 3, + "w": 4, "x": 12, "y": 6 }, @@ -1119,9 +1141,12 @@ "colorMode": "value", "graphMode": "area", "justifyMode": "center", - "orientation": "auto", + "orientation": "vertical", + "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "", "values": false }, @@ -1129,7 +1154,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -1173,7 +1198,7 @@ "links": [ { "title": "Show details", - "url": "/d/sfdx-hardis-indicator-details/02-indicator-details?${org:queryparam}&var-type=LEGACY_API&${__url_time_range}&var-indicatorLabel=Suspect Setup Actions" + "url": "/d/sfdx-hardis-indicator-details/02-indicator-details?${org:queryparam}&var-type=LEGACY_API&${__url_time_range}&var-indicatorLabel=Legacy API calls" } ], "mappings": [], @@ -1196,7 +1221,7 @@ "gridPos": { "h": 4, "w": 4, - "x": 15, + "x": 16, "y": 6 }, "id": 19, @@ -1205,8 +1230,11 @@ "graphMode": "area", "justifyMode": "center", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "", "values": false }, @@ -1214,7 +1242,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -1304,8 +1332,11 @@ "graphMode": "area", "justifyMode": "center", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "", "values": false }, @@ -1313,7 +1344,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -1407,8 +1438,11 @@ "graphMode": "area", "justifyMode": "center", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "", "values": false }, @@ -1416,7 +1450,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -1457,9 +1491,9 @@ "x": 0, "y": 15 }, - "id": 10, + "id": 27, "panels": [], - "title": "Technical Debt", + "title": "Org Info", "type": "row" }, { @@ -1472,11 +1506,11 @@ "color": { "mode": "thresholds" }, - "displayName": "Deactivated Flows & VR", + "displayName": "Release Updates to check", "links": [ { "title": "Show details", - "url": "/d/sfdx-hardis-indicator-details/02-indicator-details?${org:queryparam}&var-type=METADATA_STATUS&${__url_time_range}&var-indicatorLabel=Deactivated Flow & VR" + "url": "/d/sfdx-hardis-indicator-details/02-indicator-details?${org:queryparam}&var-type=RELEASE_UPDATES&${__url_time_range}&var-indicatorLabel=Release Updates to check" } ], "mappings": [], @@ -1493,7 +1527,7 @@ }, { "color": "red", - "value": 80 + "value": 10 } ] } @@ -1501,19 +1535,22 @@ "overrides": [] }, "gridPos": { - "h": 4, - "w": 6, + "h": 3, + "w": 5, "x": 0, "y": 16 }, - "id": 6, + "id": 34, "options": { "colorMode": "value", "graphMode": "area", "justifyMode": "center", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "", "values": false }, @@ -1521,7 +1558,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -1529,7 +1566,7 @@ "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" }, "editorMode": "builder", - "expr": "{type=\"METADATA_STATUS\", orgIdentifier=\"$org\"} |= ``", + "expr": "{type=\"RELEASE_UPDATES\", orgIdentifier=\"$org\"} |= ``", "maxLines": 1, "queryType": "range", "refId": "A" @@ -1556,61 +1593,51 @@ "type": "loki", "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" }, + "description": "", "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, - "displayName": "Attributes without permissions", - "links": [ - { - "title": "Show detailsl", - "url": "/d/sfdx-hardis-indicator-details/02-indicator-details?${org:queryparam}&var-type=LINT_ACCESS&${__url_time_range}&var-indicatorLabel=Attributes without permissions" - } - ], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { - "color": "green", + "color": "blue", "value": null - }, - { - "color": "#EAB839", - "value": 1 - }, - { - "color": "red", - "value": 80 } ] - } + }, + "unit": "days" }, "overrides": [] }, "gridPos": { - "h": 4, - "w": 6, - "x": 6, + "h": 3, + "w": 4, + "x": 5, "y": 16 }, - "id": 4, + "id": 29, "options": { "colorMode": "value", "graphMode": "area", "justifyMode": "center", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": ["lastNotNull"], - "fields": "", + "calcs": [ + "lastNotNull" + ], + "fields": "/^InstanceName$/", "values": false }, "showPercentChange": false, "textMode": "auto", "wideLayout": true }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -1618,12 +1645,13 @@ "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" }, "editorMode": "builder", - "expr": "{type=\"LINT_ACCESS\", orgIdentifier=\"$org\"} |= ``", + "expr": "{type=\"ORG_INFO\", orgIdentifier=\"$org\"} |= ``", "maxLines": 1, "queryType": "range", "refId": "A" } ], + "title": "SF Instance", "transformations": [ { "id": "extractFields", @@ -1631,11 +1659,34 @@ "format": "json", "jsonPaths": [ { - "path": "metric" + "path": "_logElements" } ], + "keepTime": false, + "replace": true, "source": "Line" } + }, + { + "id": "extractFields", + "options": { + "format": "auto", + "replace": true, + "source": "_logElements" + } + }, + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "InstanceName" + } + ], + "replace": true, + "source": "0" + } } ], "type": "stat" @@ -1645,61 +1696,51 @@ "type": "loki", "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" }, + "description": "", "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, - "displayName": "Unused Metadatas", - "links": [ - { - "title": "Show details", - "url": "/d/sfdx-hardis-indicator-details/02-indicator-details?${org:queryparam}&var-type=UNUSED_METADATAS&${__url_time_range}&var-indicatorLabel=Unused Metadatas" - } - ], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { - "color": "green", + "color": "blue", "value": null - }, - { - "color": "#EAB839", - "value": 1 - }, - { - "color": "red", - "value": 80 } ] - } + }, + "unit": "days" }, "overrides": [] }, "gridPos": { - "h": 4, - "w": 6, - "x": 12, + "h": 3, + "w": 4, + "x": 9, "y": 16 }, - "id": 3, + "id": 33, "options": { "colorMode": "value", "graphMode": "area", - "justifyMode": "center", + "justifyMode": "auto", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": ["lastNotNull"], - "fields": "", + "calcs": [ + "lastNotNull" + ], + "fields": "/^OrganizationType$/", "values": false }, "showPercentChange": false, "textMode": "auto", "wideLayout": true }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -1707,12 +1748,13 @@ "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" }, "editorMode": "builder", - "expr": "{type=\"UNUSED_METADATAS\", orgIdentifier=\"$org\"} |= ``", + "expr": "{type=\"ORG_INFO\", orgIdentifier=\"$org\"} |= ``", "maxLines": 1, "queryType": "range", "refId": "A" } ], + "title": "Org Type", "transformations": [ { "id": "extractFields", @@ -1720,11 +1762,34 @@ "format": "json", "jsonPaths": [ { - "path": "metric" + "path": "_logElements" } ], + "keepTime": false, + "replace": true, "source": "Line" } + }, + { + "id": "extractFields", + "options": { + "format": "auto", + "replace": true, + "source": "_logElements" + } + }, + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "OrganizationType" + } + ], + "replace": true, + "source": "0" + } } ], "type": "stat" @@ -1734,57 +1799,51 @@ "type": "loki", "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" }, + "description": "", "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, - "displayName": "Fields without description", - "links": [ - { - "title": "Show details", - "url": "/d/sfdx-hardis-indicator-details/02-indicator-details?${org:queryparam}&var-type=MISSING_ATTRIBUTES&${__url_time_range}&var-indicatorLabel=Fields without description" - } - ], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { - "color": "green", + "color": "blue", "value": null - }, - { - "color": "#EAB839", - "value": 1 } ] - } + }, + "unit": "days" }, "overrides": [] }, "gridPos": { - "h": 4, - "w": 6, - "x": 18, + "h": 3, + "w": 5, + "x": 13, "y": 16 }, - "id": 5, + "id": 32, "options": { "colorMode": "value", "graphMode": "area", "justifyMode": "center", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": ["lastNotNull"], - "fields": "", + "calcs": [ + "lastNotNull" + ], + "fields": "/.*/", "values": false }, "showPercentChange": false, "textMode": "auto", "wideLayout": true }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -1792,12 +1851,13 @@ "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" }, "editorMode": "builder", - "expr": "{type=\"MISSING_ATTRIBUTES\", orgIdentifier=\"$org\"} |= ``", + "expr": "{type=\"ORG_INFO\", orgIdentifier=\"$org\"} |= ``", "maxLines": 1, "queryType": "range", "refId": "A" } ], + "title": "Next Version", "transformations": [ { "id": "extractFields", @@ -1805,45 +1865,65 @@ "format": "json", "jsonPaths": [ { - "path": "metric" + "path": "_logElements" } ], + "keepTime": false, + "replace": true, "source": "Line" } + }, + { + "id": "extractFields", + "options": { + "format": "auto", + "replace": true, + "source": "_logElements" + } + }, + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "maintenanceNextUpgrade" + } + ], + "keepTime": false, + "replace": true, + "source": "0" + } + }, + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Next Version", + "path": "name" + } + ], + "keepTime": false, + "replace": true, + "source": "maintenanceNextUpgrade" + } } ], "type": "stat" }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 20 - }, - "id": 23, - "panels": [], - "title": "Licenses", - "type": "row" - }, { "datasource": { "type": "loki", "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" }, + "description": "", "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, "mappings": [], "thresholds": { "mode": "absolute", @@ -1853,20 +1933,548 @@ "value": null } ] - } + }, + "unit": "days" }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "MasterLabel" - }, - "properties": [ - { - "id": "custom.width", - "value": 248 - } - ] + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 5, + "x": 18, + "y": 16 + }, + "id": 31, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/.*/", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.3.0-75420", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "editorMode": "builder", + "expr": "{type=\"ORG_INFO\", orgIdentifier=\"$org\"} |= ``", + "maxLines": 1, + "queryType": "range", + "refId": "A" + } + ], + "title": "Next Upgrade", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "_logElements" + } + ], + "keepTime": false, + "replace": true, + "source": "Line" + } + }, + { + "id": "extractFields", + "options": { + "format": "auto", + "replace": true, + "source": "_logElements" + } + }, + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "maintenanceNextUpgrade" + } + ], + "keepTime": false, + "replace": true, + "source": "0" + } + }, + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Upgrade Date", + "path": "plannedStartTime" + } + ], + "keepTime": false, + "replace": true, + "source": "maintenanceNextUpgrade" + } + }, + { + "id": "convertFieldType", + "options": { + "conversions": [ + { + "dateFormat": "", + "destinationType": "time", + "targetField": "Upgrade Date" + } + ], + "fields": {} + } + } + ], + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 19 + }, + "id": 10, + "panels": [], + "title": "Technical Debt", + "type": "row" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "Deactivated Flows & VR", + "links": [ + { + "title": "Show details", + "url": "/d/sfdx-hardis-indicator-details/02-indicator-details?${org:queryparam}&var-type=METADATA_STATUS&${__url_time_range}&var-indicatorLabel=Deactivated Flow & VR" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 0, + "y": 20 + }, + "id": 6, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.3.0-75420", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "editorMode": "builder", + "expr": "{type=\"METADATA_STATUS\", orgIdentifier=\"$org\"} |= ``", + "maxLines": 1, + "queryType": "range", + "refId": "A" + } + ], + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "metric" + } + ], + "source": "Line" + } + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "Attributes without permissions", + "links": [ + { + "title": "Show detailsl", + "url": "/d/sfdx-hardis-indicator-details/02-indicator-details?${org:queryparam}&var-type=LINT_ACCESS&${__url_time_range}&var-indicatorLabel=Attributes without permissions" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 6, + "y": 20 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.3.0-75420", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "editorMode": "builder", + "expr": "{type=\"LINT_ACCESS\", orgIdentifier=\"$org\"} |= ``", + "maxLines": 1, + "queryType": "range", + "refId": "A" + } + ], + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "metric" + } + ], + "source": "Line" + } + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "Unused Metadatas", + "links": [ + { + "title": "Show details", + "url": "/d/sfdx-hardis-indicator-details/02-indicator-details?${org:queryparam}&var-type=UNUSED_METADATAS&${__url_time_range}&var-indicatorLabel=Unused Metadatas" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 12, + "y": 20 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.3.0-75420", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "editorMode": "builder", + "expr": "{type=\"UNUSED_METADATAS\", orgIdentifier=\"$org\"} |= ``", + "maxLines": 1, + "queryType": "range", + "refId": "A" + } + ], + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "metric" + } + ], + "source": "Line" + } + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "Fields without description", + "links": [ + { + "title": "Show details", + "url": "/d/sfdx-hardis-indicator-details/02-indicator-details?${org:queryparam}&var-type=MISSING_ATTRIBUTES&${__url_time_range}&var-indicatorLabel=Fields without description" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 18, + "y": 20 + }, + "id": 5, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.3.0-75420", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "editorMode": "builder", + "expr": "{type=\"MISSING_ATTRIBUTES\", orgIdentifier=\"$org\"} |= ``", + "maxLines": 1, + "queryType": "range", + "refId": "A" + } + ], + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "metric" + } + ], + "source": "Line" + } + } + ], + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 24 + }, + "id": 23, + "panels": [], + "title": "Licenses & Packages", + "type": "row" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "MasterLabel" + }, + "properties": [ + { + "id": "custom.width", + "value": 248 + } + ] }, { "matcher": { @@ -1891,14 +2499,38 @@ "value": 160 } ] + }, + { + "matcher": { + "id": "byName", + "options": "Total available" + }, + "properties": [ + { + "id": "custom.width", + "value": 220 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Used" + }, + "properties": [ + { + "id": "custom.width", + "value": 181 + } + ] } ] }, "gridPos": { - "h": 13, + "h": 11, "w": 12, "x": 0, - "y": 21 + "y": 25 }, "id": 25, "options": { @@ -1906,7 +2538,9 @@ "footer": { "countRows": false, "fields": "", - "reducer": ["sum"], + "reducer": [ + "sum" + ], "show": false }, "showHeader": true, @@ -1917,7 +2551,7 @@ } ] }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -1957,7 +2591,9 @@ { "id": "reduce", "options": { - "reducers": ["allValues"] + "reducers": [ + "allValues" + ] } }, { @@ -2082,14 +2718,38 @@ "value": 160 } ] + }, + { + "matcher": { + "id": "byName", + "options": "Total available" + }, + "properties": [ + { + "id": "custom.width", + "value": 199 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Used" + }, + "properties": [ + { + "id": "custom.width", + "value": 190 + } + ] } ] }, "gridPos": { - "h": 13, + "h": 11, "w": 12, "x": 12, - "y": 21 + "y": 25 }, "id": 24, "options": { @@ -2097,7 +2757,9 @@ "footer": { "countRows": false, "fields": "", - "reducer": ["sum"], + "reducer": [ + "sum" + ], "show": false }, "showHeader": true, @@ -2108,7 +2770,7 @@ } ] }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -2148,7 +2810,9 @@ { "id": "reduce", "options": { - "reducers": ["allValues"] + "reducers": [ + "allValues" + ] } }, { @@ -2208,11 +2872,247 @@ } ], "type": "table" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "MasterLabel" + }, + "properties": [ + { + "id": "custom.width", + "value": 248 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "UsedLicenses" + }, + "properties": [ + { + "id": "custom.width", + "value": 148 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "TotalLicenses" + }, + "properties": [ + { + "id": "custom.width", + "value": 160 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Package name" + }, + "properties": [ + { + "id": "custom.width", + "value": 276 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Namespace" + }, + "properties": [ + { + "id": "custom.width", + "value": 338 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Version Name" + }, + "properties": [ + { + "id": "custom.width", + "value": 284 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Installed package" + }, + "properties": [ + { + "id": "custom.width", + "value": 431 + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 36 + }, + "id": 26, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": false, + "displayName": "Installed package" + } + ] + }, + "pluginVersion": "11.3.0-75420", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "editorMode": "builder", + "expr": "{orgIdentifier=\"$org\", type=\"BACKUP\"} |= ``", + "maxLines": 1, + "queryType": "range", + "refId": "A" + } + ], + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "installedPackages" + } + ], + "keepTime": false, + "replace": true, + "source": "Line" + } + }, + { + "id": "extractFields", + "options": { + "keepTime": false, + "replace": true, + "source": "installedPackages" + } + }, + { + "id": "reduce", + "options": { + "labelsToFields": false, + "reducers": [ + "allValues" + ] + } + }, + { + "id": "extractFields", + "options": { + "format": "auto", + "replace": true, + "source": "All values" + } + }, + { + "id": "extractFields", + "options": { + "replace": true, + "source": "0" + } + }, + { + "id": "organize", + "options": { + "excludeByName": { + "Name": true, + "SubscriberPackageVersionId": true, + "type": true + }, + "includeByName": {}, + "indexByName": { + "MasterLabel": 0, + "Name": 3, + "TotalLicenses": 2, + "UsedLicenses": 1, + "type": 4 + }, + "renameByName": { + "MasterLabel": "User License", + "SubscriberPackageName": "Installed package", + "SubscriberPackageNamespace": "Namespace", + "SubscriberPackageVersionId": "Version Id", + "SubscriberPackageVersionName": "Version Name", + "SubscriberPackageVersionNumber": "Version Number", + "TotalLicenses": "Total available", + "UsedLicenses": "Used" + } + } + } + ], + "type": "table" } ], "refresh": "", "schemaVersion": 39, - "tags": ["sfdx-hardis", "salesforce", "monitoring"], + "tags": [ + "sfdx-hardis", + "salesforce", + "monitoring" + ], "templating": { "list": [ { @@ -2222,10 +3122,8 @@ "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" }, "definition": "", - "hide": 0, "includeAll": false, "label": "Salesforce Org", - "multi": false, "name": "org", "options": [], "query": { @@ -2236,7 +3134,6 @@ }, "refresh": 1, "regex": "^[^.]*$", - "skipUrlSync": false, "sort": 1, "type": "query" } @@ -2246,11 +3143,10 @@ "from": "now-7d", "to": "now" }, - "timeRangeUpdatedDuringEditOrView": false, "timepicker": {}, "timezone": "browser", "title": "DASH - 00_Home", "uid": "sfdx-hardis-today-summary", - "version": 91, + "version": 107, "weekStart": "" -} +} \ No newline at end of file diff --git a/docs/grafana/dashboards/DASH - Licenses for all orgs.json b/docs/grafana/dashboards/DASH - Licenses for all orgs.json new file mode 100644 index 000000000..c9cdab457 --- /dev/null +++ b/docs/grafana/dashboards/DASH - Licenses for all orgs.json @@ -0,0 +1,498 @@ +{ + "__inputs": [ + { + "name": "DS_GRAFANACLOUD-CLOUDITY-LOGS", + "label": "grafanacloud-cloudity-logs", + "description": "", + "type": "datasource", + "pluginId": "loki", + "pluginName": "Loki" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "11.3.0-75420" + }, + { + "type": "datasource", + "id": "loki", + "name": "Loki", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "panels": [], + "repeat": "org", + "title": "$org", + "type": "row" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "MasterLabel" + }, + "properties": [ + { + "id": "custom.width", + "value": 248 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "UsedLicenses" + }, + "properties": [ + { + "id": "custom.width", + "value": 148 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "TotalLicenses" + }, + "properties": [ + { + "id": "custom.width", + "value": 160 + } + ] + } + ] + }, + "gridPos": { + "h": 14, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 1, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Used" + } + ] + }, + "pluginVersion": "11.3.0-75420", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "editorMode": "builder", + "expr": "{orgIdentifier=\"$org\", type=\"LICENSES\"} |= ``", + "maxLines": 1, + "queryType": "range", + "refId": "A" + } + ], + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "_logElements" + } + ], + "keepTime": false, + "replace": true, + "source": "Line" + } + }, + { + "id": "extractFields", + "options": { + "keepTime": false, + "replace": true, + "source": "_logElements" + } + }, + { + "id": "reduce", + "options": { + "reducers": [ + "allValues" + ] + } + }, + { + "id": "extractFields", + "options": { + "format": "auto", + "replace": true, + "source": "All values" + } + }, + { + "id": "extractFields", + "options": { + "replace": true, + "source": "0" + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "equal", + "options": { + "value": "PermissionSetLicense" + } + }, + "fieldName": "type" + } + ], + "match": "any", + "type": "include" + } + }, + { + "id": "organize", + "options": { + "excludeByName": { + "Name": true, + "type": true + }, + "includeByName": {}, + "indexByName": { + "MasterLabel": 0, + "Name": 3, + "TotalLicenses": 2, + "UsedLicenses": 1, + "type": 4 + }, + "renameByName": { + "MasterLabel": "Permission Set License", + "TotalLicenses": "Total available", + "UsedLicenses": "Used" + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "MasterLabel" + }, + "properties": [ + { + "id": "custom.width", + "value": 248 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "UsedLicenses" + }, + "properties": [ + { + "id": "custom.width", + "value": 148 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "TotalLicenses" + }, + "properties": [ + { + "id": "custom.width", + "value": 160 + } + ] + } + ] + }, + "gridPos": { + "h": 14, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 3, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Used" + } + ] + }, + "pluginVersion": "11.3.0-75420", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "editorMode": "builder", + "expr": "{orgIdentifier=\"$org\", type=\"LICENSES\"} |= ``", + "maxLines": 1, + "queryType": "range", + "refId": "A" + } + ], + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "_logElements" + } + ], + "keepTime": false, + "replace": true, + "source": "Line" + } + }, + { + "id": "extractFields", + "options": { + "keepTime": false, + "replace": true, + "source": "_logElements" + } + }, + { + "id": "reduce", + "options": { + "reducers": [ + "allValues" + ] + } + }, + { + "id": "extractFields", + "options": { + "format": "auto", + "replace": true, + "source": "All values" + } + }, + { + "id": "extractFields", + "options": { + "replace": true, + "source": "0" + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "equal", + "options": { + "value": "UserLicense" + } + }, + "fieldName": "type" + } + ], + "match": "any", + "type": "include" + } + }, + { + "id": "organize", + "options": { + "excludeByName": { + "Name": true, + "type": true + }, + "includeByName": {}, + "indexByName": { + "MasterLabel": 0, + "Name": 3, + "TotalLicenses": 2, + "UsedLicenses": 1, + "type": 4 + }, + "renameByName": { + "MasterLabel": "User License", + "TotalLicenses": "Total available", + "UsedLicenses": "Used" + } + } + } + ], + "type": "table" + } + ], + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [ + { + "current": {}, + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "definition": "", + "includeAll": true, + "multi": true, + "name": "org", + "options": [], + "query": { + "label": "orgIdentifier", + "refId": "LokiVariableQueryEditor-VariableQuery", + "stream": "{source=\"sfdx-hardis\"}", + "type": 1 + }, + "refresh": 1, + "regex": "^(?!.*\\..*)(?!.*sandbox.*).*$", + "sort": 1, + "type": "query" + } + ] + }, + "time": { + "from": "now-30d", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "DASH - Licenses for all orgs", + "uid": "sfdx-hardis-licenses-all-orgs", + "version": 12, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/grafana/dashboards/DASH - Licenses.json b/docs/grafana/dashboards/DASH - Licenses.json new file mode 100644 index 000000000..95e93acc5 --- /dev/null +++ b/docs/grafana/dashboards/DASH - Licenses.json @@ -0,0 +1,900 @@ +{ + "__inputs": [ + { + "name": "DS_GRAFANACLOUD-CLOUDITY-LOGS", + "label": "grafanacloud-cloudity-logs", + "description": "", + "type": "datasource", + "pluginId": "loki", + "pluginName": "Loki" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "panel", + "id": "gauge", + "name": "Gauge", + "version": "" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "11.3.0-75420" + }, + { + "type": "datasource", + "id": "loki", + "name": "Loki", + "version": "1.0.0" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 41, + "panels": [], + "title": "All Orgs", + "type": "row" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "$org", + "fieldMinMax": true, + "links": [ + { + "title": "View all licenses for ${__data.fields[\"Salesforce Org Identifier\"]}", + "url": "/d/sfdx-hardis-licenses-all-orgs/dash-licenses-for-all-orgs?var-org=${__data.fields[\"Salesforce Org Identifier\"]}" + } + ], + "mappings": [], + "max": 5000, + "min": 0, + "noValue": "No stat", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "semi-dark-blue", + "value": null + }, + { + "color": "light-blue", + "value": 20 + }, + { + "color": "light-green", + "value": 50 + }, + { + "color": "light-yellow", + "value": 100 + }, + { + "color": "light-orange", + "value": 500 + }, + { + "color": "light-red", + "value": 1000 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "MasterLabel" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "UsedLicenses" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "TotalLicenses" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "usedLicenses" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "activeLicenses" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "Line" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "labels" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "Salesforce Org Identifier" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "Active Licenses" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "Analytics Cloud Integration User" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "Salesforce Licenses" + }, + "properties": [] + } + ] + }, + "gridPos": { + "h": 4, + "w": 24, + "x": 0, + "y": 1 + }, + "id": 1, + "maxPerRow": 6, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "11.2.0-73451", + "repeat": "org", + "repeatDirection": "h", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "editorMode": "builder", + "expr": "{type=\"LICENSES\", orgIdentifier=\"$org\"}", + "maxLines": 1, + "queryType": "range", + "refId": "A" + } + ], + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "licenses" + } + ], + "keepTime": false, + "replace": false, + "source": "Line" + } + }, + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "Salesforce" + } + ], + "replace": true, + "source": "licenses" + } + } + ], + "type": "gauge" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 29 + }, + "id": 80, + "panels": [], + "title": "Total", + "type": "row" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "Salesforce Licenses", + "fieldMinMax": true, + "links": [ + { + "title": "View all licenses for ${__data.fields[\"Salesforce Org Identifier\"]}", + "url": "/d/sfdx-hardis-licenses-all-orgs/dash-licenses-for-all-orgs?var-org=${__data.fields[\"Salesforce Org Identifier\"]}" + } + ], + "mappings": [], + "max": 5000, + "min": 0, + "noValue": "No stat", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "light-yellow", + "value": null + }, + { + "color": "semi-dark-yellow", + "value": 20 + }, + { + "color": "super-light-green", + "value": 50 + }, + { + "color": "light-green", + "value": 100 + }, + { + "color": "super-light-purple", + "value": 500 + }, + { + "color": "dark-purple", + "value": 1000 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "MasterLabel" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "UsedLicenses" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "TotalLicenses" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "usedLicenses" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "activeLicenses" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "Line" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "labels" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "Salesforce Org Identifier" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "Active Licenses" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "Analytics Cloud Integration User" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "Salesforce Licenses" + }, + "properties": [] + } + ] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 0, + "y": 30 + }, + "id": 40, + "maxPerRow": 6, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "sum" + ], + "fields": "/.*/", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "11.3.0-75420", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "editorMode": "builder", + "expr": "{type=\"LICENSES\"}", + "maxLines": 1000, + "queryType": "range", + "refId": "A" + } + ], + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "licenses" + } + ], + "keepTime": false, + "replace": false, + "source": "Line" + } + }, + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "Salesforce" + }, + { + "path": "Salesforce Platform" + } + ], + "keepTime": false, + "replace": false, + "source": "licenses" + } + }, + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "orgIdentifier" + } + ], + "keepTime": false, + "replace": false, + "source": "labels" + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "substring", + "options": { + "value": "sandbox" + } + }, + "fieldName": "orgIdentifier" + } + ], + "match": "any", + "type": "exclude" + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Salesforce": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "Salesforce Platform": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "orgIdentifier": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "calculateField", + "options": { + "mode": "reduceRow", + "reduce": { + "include": [ + "Salesforce (lastNotNull)" + ], + "reducer": "sum" + }, + "replaceFields": false + } + }, + { + "id": "organize", + "options": { + "excludeByName": { + "Salesforce (lastNotNull)": true, + "Salesforce Platform (lastNotNull)": true, + "orgIdentifier": true + }, + "includeByName": {}, + "indexByName": {}, + "renameByName": {} + } + } + ], + "type": "gauge" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "Salesforce Platform Licenses", + "fieldMinMax": true, + "links": [ + { + "title": "View all licenses for ${__data.fields[\"Salesforce Org Identifier\"]}", + "url": "/d/sfdx-hardis-licenses-all-orgs/dash-licenses-for-all-orgs?var-org=${__data.fields[\"Salesforce Org Identifier\"]}" + } + ], + "mappings": [], + "max": 5000, + "min": 0, + "noValue": "No stat", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "light-yellow", + "value": null + }, + { + "color": "semi-dark-yellow", + "value": 20 + }, + { + "color": "super-light-green", + "value": 50 + }, + { + "color": "light-green", + "value": 100 + }, + { + "color": "super-light-purple", + "value": 500 + }, + { + "color": "dark-purple", + "value": 1000 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "MasterLabel" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "UsedLicenses" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "TotalLicenses" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "usedLicenses" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "activeLicenses" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "Line" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "labels" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "Salesforce Org Identifier" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "Active Licenses" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "Analytics Cloud Integration User" + }, + "properties": [] + }, + { + "matcher": { + "id": "byName", + "options": "Salesforce Licenses" + }, + "properties": [] + } + ] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 4, + "y": 30 + }, + "id": 119, + "maxPerRow": 6, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "sum" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "11.3.0-75420", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "editorMode": "builder", + "expr": "{type=\"LICENSES\"}", + "maxLines": 1000, + "queryType": "range", + "refId": "A" + } + ], + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "orgIdentifier" + } + ], + "keepTime": false, + "replace": false, + "source": "labels" + } + }, + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "licenses" + } + ], + "keepTime": false, + "replace": false, + "source": "Line" + } + }, + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "Salesforce" + }, + { + "path": "Salesforce Platform" + } + ], + "keepTime": false, + "replace": false, + "source": "licenses" + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Salesforce": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "Salesforce Platform": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "orgIdentifier": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "equal", + "options": { + "value": "Not Found" + } + }, + "fieldName": "Salesforce Platform (lastNotNull)" + }, + { + "config": { + "id": "substring", + "options": { + "value": "sandbox" + } + }, + "fieldName": "orgIdentifier" + } + ], + "match": "any", + "type": "exclude" + } + }, + { + "id": "calculateField", + "options": { + "mode": "reduceRow", + "reduce": { + "include": [ + "Salesforce Platform (lastNotNull)" + ], + "reducer": "sum" + }, + "replaceFields": false + } + }, + { + "id": "organize", + "options": { + "excludeByName": { + "Salesforce (lastNotNull)": true, + "Salesforce Platform (lastNotNull)": true, + "orgIdentifier": false + }, + "includeByName": {}, + "indexByName": {}, + "renameByName": {} + } + } + ], + "type": "gauge" + } + ], + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [ + { + "current": {}, + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "definition": "", + "includeAll": true, + "multi": true, + "name": "org", + "options": [], + "query": { + "label": "orgIdentifier", + "refId": "LokiVariableQueryEditor-VariableQuery", + "stream": "", + "type": 1 + }, + "refresh": 1, + "regex": "^(?!.*\\..*)(?!.*sandbox.*).*$", + "type": "query" + } + ] + }, + "time": { + "from": "now-30d", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "DASH - Licenses", + "uid": "ddp6qgcjgk1dsd", + "version": 13, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/grafana/dashboards/DASH - Tech Debt for all orgs.json b/docs/grafana/dashboards/DASH - Tech Debt for all orgs.json new file mode 100644 index 000000000..9b07136ab --- /dev/null +++ b/docs/grafana/dashboards/DASH - Tech Debt for all orgs.json @@ -0,0 +1,478 @@ +{ + "__inputs": [ + { + "name": "DS_GRAFANACLOUD-CLOUDITY-LOGS", + "label": "grafanacloud-cloudity-logs", + "description": "", + "type": "datasource", + "pluginId": "loki", + "pluginName": "Loki" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "11.3.0-75420" + }, + { + "type": "datasource", + "id": "loki", + "name": "Loki", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 10, + "panels": [], + "repeat": "org", + "title": "$org", + "type": "row" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "Deactivated Flows & VR", + "links": [ + { + "title": "Show details", + "url": "/d/sfdx-hardis-indicator-details/02-indicator-details?${org:queryparam}&var-type=METADATA_STATUS&${__url_time_range}&var-indicatorLabel=Deactivated Flow & VR" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 0, + "y": 1 + }, + "id": 6, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.3.0-75420", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "editorMode": "builder", + "expr": "{type=\"METADATA_STATUS\", orgIdentifier=\"$org\"} |= ``", + "maxLines": 1, + "queryType": "range", + "refId": "A" + } + ], + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "metric" + } + ], + "source": "Line" + } + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "Attributes without permissions", + "links": [ + { + "title": "Show detailsl", + "url": "/d/sfdx-hardis-indicator-details/02-indicator-details?${org:queryparam}&var-type=LINT_ACCESS&${__url_time_range}&var-indicatorLabel=Attributes without permissions" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 6, + "y": 1 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.3.0-75420", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "editorMode": "builder", + "expr": "{type=\"LINT_ACCESS\", orgIdentifier=\"$org\"} |= ``", + "maxLines": 1, + "queryType": "range", + "refId": "A" + } + ], + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "metric" + } + ], + "source": "Line" + } + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "Unused Metadatas", + "links": [ + { + "title": "Show details", + "url": "/d/sfdx-hardis-indicator-details/02-indicator-details?${org:queryparam}&var-type=UNUSED_METADATAS&${__url_time_range}&var-indicatorLabel=Unused Metadatas" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 12, + "y": 1 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.3.0-75420", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "editorMode": "builder", + "expr": "{type=\"UNUSED_METADATAS\", orgIdentifier=\"$org\"} |= ``", + "maxLines": 1, + "queryType": "range", + "refId": "A" + } + ], + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "metric" + } + ], + "source": "Line" + } + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "Fields without description", + "links": [ + { + "title": "Show details", + "url": "/d/sfdx-hardis-indicator-details/02-indicator-details?${org:queryparam}&var-type=MISSING_ATTRIBUTES&${__url_time_range}&var-indicatorLabel=Fields without description" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 18, + "y": 1 + }, + "id": 5, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.3.0-75420", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "editorMode": "builder", + "expr": "{type=\"MISSING_ATTRIBUTES\", orgIdentifier=\"$org\"} |= ``", + "maxLines": 1, + "queryType": "range", + "refId": "A" + } + ], + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "metric" + } + ], + "source": "Line" + } + } + ], + "type": "stat" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [ + "sfdx-hardis", + "salesforce", + "monitoring" + ], + "templating": { + "list": [ + { + "current": {}, + "datasource": { + "type": "loki", + "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" + }, + "definition": "", + "includeAll": true, + "label": "Salesforce Org", + "multi": true, + "name": "org", + "options": [], + "query": { + "label": "orgIdentifier", + "refId": "LokiVariableQueryEditor-VariableQuery", + "stream": "{source=\"sfdx-hardis\"}", + "type": 1 + }, + "refresh": 1, + "regex": "^[^.]*$", + "sort": 1, + "type": "query" + } + ] + }, + "time": { + "from": "now-7d", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "DASH - Tech Debt for all orgs", + "uid": "sfdx-hardis-tech-debt-all-orgs", + "version": 7, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/grafana/dashboards/DTL - Active Users.json b/docs/grafana/dashboards/DTL - Active Users.json index fb9e11b00..f2375ef57 100644 --- a/docs/grafana/dashboards/DTL - Active Users.json +++ b/docs/grafana/dashboards/DTL - Active Users.json @@ -29,7 +29,7 @@ "type": "grafana", "id": "grafana", "name": "Grafana", - "version": "11.1.0-70903" + "version": "11.3.0-75420" }, { "type": "datasource", @@ -92,6 +92,7 @@ ], "panels": [ { + "collapsed": false, "gridPos": { "h": 1, "w": 24, @@ -101,7 +102,6 @@ "id": 6, "panels": [], "repeat": "org", - "repeatDirection": "h", "title": "$org", "type": "row" }, @@ -147,8 +147,11 @@ "graphMode": "area", "justifyMode": "center", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "", "values": false }, @@ -156,7 +159,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "11.1.0-70903", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -209,6 +212,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 20, "gradientMode": "none", @@ -268,7 +272,7 @@ "sort": "none" } }, - "pluginVersion": "11.1.0-69950", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -340,8 +344,11 @@ "graphMode": "area", "justifyMode": "center", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "/^_dateTime$/", "values": false }, @@ -352,7 +359,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "11.1.0-70903", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -627,7 +634,9 @@ "footer": { "countRows": false, "fields": "", - "reducer": ["sum"], + "reducer": [ + "sum" + ], "show": false }, "showHeader": true, @@ -638,7 +647,7 @@ } ] }, - "pluginVersion": "11.1.0-70903", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -681,7 +690,9 @@ "includeTimeField": false, "labelsToFields": false, "mode": "seriesToRows", - "reducers": ["allValues"] + "reducers": [ + "allValues" + ] } }, { @@ -710,7 +721,9 @@ "aggregations": [] }, "Profile.UserLicense.LicenseDefinitionKey": { - "aggregations": ["count"], + "aggregations": [ + "count" + ], "operation": "aggregate" }, "Profile.UserLicense.Name": { @@ -750,7 +763,7 @@ "cellOptions": { "type": "auto" }, - "inspect": false + "inspect": true }, "mappings": [], "thresholds": { @@ -974,7 +987,9 @@ "footer": { "countRows": false, "fields": "", - "reducer": ["sum"], + "reducer": [ + "sum" + ], "show": false }, "showHeader": true, @@ -985,7 +1000,7 @@ } ] }, - "pluginVersion": "11.1.0-70903", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -1028,7 +1043,9 @@ "includeTimeField": false, "labelsToFields": false, "mode": "seriesToRows", - "reducers": ["allValues"] + "reducers": [ + "allValues" + ] } }, { @@ -1096,7 +1113,6 @@ "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" }, "definition": "", - "hide": 0, "includeAll": true, "label": "Salesforce Org", "multi": true, @@ -1110,7 +1126,6 @@ }, "refresh": 1, "regex": "^(?!.*\\..*)(?!.*sandbox.*).*$", - "skipUrlSync": false, "sort": 1, "type": "query" }, @@ -1119,7 +1134,7 @@ "label": "Type", "name": "type", "query": "${VAR_TYPE}", - "skipUrlSync": false, + "skipUrlSync": true, "type": "constant", "current": { "value": "${VAR_TYPE}", @@ -1136,10 +1151,9 @@ }, { "hide": 2, - "label": "", "name": "indicatorLabel", "query": "${VAR_INDICATORLABEL}", - "skipUrlSync": false, + "skipUrlSync": true, "type": "constant", "current": { "value": "${VAR_INDICATORLABEL}", @@ -1160,13 +1174,10 @@ "from": "now-7d", "to": "now" }, - "timeRangeUpdatedDuringEditOrView": false, - "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] - }, + "timepicker": {}, "timezone": "browser", "title": "DTL - Active Users", "uid": "sfdx-hardis-dtl-active-users", - "version": 5, + "version": 7, "weekStart": "" -} +} \ No newline at end of file diff --git a/docs/grafana/dashboards/DTL - Indicator Details.json b/docs/grafana/dashboards/DTL - Indicator Details.json index 4296390cd..e062aa2b0 100644 --- a/docs/grafana/dashboards/DTL - Indicator Details.json +++ b/docs/grafana/dashboards/DTL - Indicator Details.json @@ -15,7 +15,7 @@ "type": "grafana", "id": "grafana", "name": "Grafana", - "version": "11.1.0-70903" + "version": "11.3.0-75420" }, { "type": "datasource", @@ -78,6 +78,7 @@ ], "panels": [ { + "collapsed": false, "gridPos": { "h": 1, "w": 24, @@ -87,7 +88,6 @@ "id": 6, "panels": [], "repeat": "org", - "repeatDirection": "h", "title": "$org", "type": "row" }, @@ -133,8 +133,11 @@ "graphMode": "area", "justifyMode": "center", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "", "values": false }, @@ -142,7 +145,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "11.1.0-70903", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -195,6 +198,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 20, "gradientMode": "none", @@ -254,7 +258,7 @@ "sort": "none" } }, - "pluginVersion": "11.1.0-69950", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -326,8 +330,11 @@ "graphMode": "area", "justifyMode": "center", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "/^_dateTime$/", "values": false }, @@ -338,7 +345,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "11.1.0-70903", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -364,6 +371,9 @@ }, { "path": "_dateTime" + }, + { + "path": "_logElementsTruncated" } ], "keepTime": false, @@ -401,7 +411,7 @@ "cellOptions": { "type": "auto" }, - "inspect": false + "inspect": true }, "mappings": [], "thresholds": { @@ -598,6 +608,66 @@ "value": 125 } ] + }, + { + "matcher": { + "id": "byName", + "options": "StepStage" + }, + "properties": [ + { + "id": "custom.width", + "value": 97 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Status" + }, + "properties": [ + { + "id": "custom.width", + "value": 99 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Category" + }, + "properties": [ + { + "id": "custom.width", + "value": 103 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "DurableId" + }, + "properties": [ + { + "id": "custom.width", + "value": 216 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Title" + }, + "properties": [ + { + "id": "custom.width", + "value": 479 + } + ] } ] }, @@ -613,18 +683,15 @@ "footer": { "countRows": false, "fields": "", - "reducer": ["sum"], + "reducer": [ + "sum" + ], "show": false }, "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Suspect" - } - ] + "sortBy": [] }, - "pluginVersion": "11.1.0-70903", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -667,7 +734,9 @@ "includeTimeField": false, "labelsToFields": false, "mode": "seriesToRows", - "reducers": ["allValues"] + "reducers": [ + "allValues" + ] } }, { @@ -735,7 +804,6 @@ "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" }, "definition": "", - "hide": 0, "includeAll": true, "label": "Salesforce Org", "multi": true, @@ -749,7 +817,6 @@ }, "refresh": 1, "regex": "^[^.]*$", - "skipUrlSync": false, "sort": 1, "type": "query" }, @@ -763,7 +830,6 @@ "hide": 2, "includeAll": false, "label": "Type", - "multi": false, "name": "type", "options": [], "query": { @@ -774,28 +840,23 @@ }, "refresh": 1, "regex": "", - "skipUrlSync": false, - "sort": 0, "type": "query" }, { "current": { - "selected": false, - "text": "Updated metadatas in org", - "value": "Updated metadatas in org" + "text": "Suspect Setup Actions", + "value": "Suspect Setup Actions" }, "hide": 2, - "label": "", "name": "indicatorLabel", "options": [ { "selected": true, - "text": "Indicator", - "value": "Indicator" + "text": "Suspect Setup Actions", + "value": "Suspect Setup Actions" } ], - "query": "Click on another dashboard to visit this dashboard", - "skipUrlSync": false, + "query": "Suspect Setup Actions", "type": "textbox" } ] @@ -804,13 +865,10 @@ "from": "now-7d", "to": "now" }, - "timeRangeUpdatedDuringEditOrView": false, - "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] - }, + "timepicker": {}, "timezone": "browser", "title": "DTL - Indicator Details", "uid": "sfdx-hardis-indicator-details", - "version": 41, + "version": 44, "weekStart": "" -} +} \ No newline at end of file diff --git a/docs/grafana/dashboards/DTL - Indicator Evolution (Long time).json b/docs/grafana/dashboards/DTL - Indicator Evolution (Long time).json index 2573dca0f..c785b8c6c 100644 --- a/docs/grafana/dashboards/DTL - Indicator Evolution (Long time).json +++ b/docs/grafana/dashboards/DTL - Indicator Evolution (Long time).json @@ -23,7 +23,7 @@ "type": "grafana", "id": "grafana", "name": "Grafana", - "version": "11.1.0-70903" + "version": "11.3.0-75420" }, { "type": "datasource", @@ -90,7 +90,6 @@ "id": 7, "panels": [], "repeat": "org", - "repeatDirection": "h", "title": "$org", "type": "row" }, @@ -218,7 +217,6 @@ "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" }, "definition": "", - "hide": 0, "includeAll": true, "label": "Salesforce Org", "multi": true, @@ -232,7 +230,6 @@ }, "refresh": 1, "regex": "^[^.]*$", - "skipUrlSync": false, "sort": 1, "type": "query" }, @@ -243,7 +240,6 @@ "uid": "${DS_GRAFANACLOUD-CLOUDITY-PROM}" }, "definition": "metrics(_metric)", - "hide": 0, "includeAll": true, "label": "Limit Identifier", "multi": true, @@ -256,8 +252,6 @@ }, "refresh": 1, "regex": "", - "skipUrlSync": false, - "sort": 0, "type": "query" } ] @@ -266,13 +260,10 @@ "from": "now-7d", "to": "now" }, - "timeRangeUpdatedDuringEditOrView": false, - "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] - }, + "timepicker": {}, "timezone": "browser", "title": "DTL - Indicator Evolution (Long time)", "uid": "sfdx-hardis-indicator-evol-prom", "version": 17, "weekStart": "" -} +} \ No newline at end of file diff --git a/docs/grafana/dashboards/DTL - Limits Details.json b/docs/grafana/dashboards/DTL - Limits Details.json index b7f42882a..6e1e587b0 100644 --- a/docs/grafana/dashboards/DTL - Limits Details.json +++ b/docs/grafana/dashboards/DTL - Limits Details.json @@ -15,7 +15,7 @@ "type": "grafana", "id": "grafana", "name": "Grafana", - "version": "11.1.0-70958" + "version": "11.3.0-75420" }, { "type": "datasource", @@ -67,6 +67,7 @@ ], "panels": [ { + "collapsed": false, "gridPos": { "h": 1, "w": 24, @@ -76,7 +77,6 @@ "id": 3, "panels": [], "repeat": "org", - "repeatDirection": "h", "title": "$org", "type": "row" }, @@ -204,7 +204,9 @@ "countRows": false, "enablePagination": false, "fields": "", - "reducer": ["sum"], + "reducer": [ + "sum" + ], "show": false }, "showHeader": true, @@ -215,7 +217,7 @@ } ] }, - "pluginVersion": "11.1.0-70958", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -255,7 +257,9 @@ "id": "reduce", "options": { "labelsToFields": false, - "reducers": ["allValues"] + "reducers": [ + "allValues" + ] } }, { @@ -341,7 +345,6 @@ "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" }, "definition": "", - "hide": 0, "includeAll": true, "label": "Salesforce Org", "multi": true, @@ -355,7 +358,6 @@ }, "refresh": 1, "regex": "^[^.]*$", - "skipUrlSync": false, "sort": 1, "type": "query" } @@ -365,11 +367,10 @@ "from": "now-30d", "to": "now" }, - "timeRangeUpdatedDuringEditOrView": false, "timepicker": {}, "timezone": "browser", "title": "DTL - Limits Details", "uid": "sfdx-hardis-limits-details", "version": 17, "weekStart": "" -} +} \ No newline at end of file diff --git a/docs/grafana/dashboards/DTL - Limits Evolution.json b/docs/grafana/dashboards/DTL - Limits Evolution.json index 5a753e444..98dda1baa 100644 --- a/docs/grafana/dashboards/DTL - Limits Evolution.json +++ b/docs/grafana/dashboards/DTL - Limits Evolution.json @@ -21,7 +21,7 @@ "type": "grafana", "id": "grafana", "name": "Grafana", - "version": "11.1.0-70903" + "version": "11.3.0-75420" }, { "type": "datasource", @@ -88,7 +88,6 @@ "id": 7, "panels": [], "repeat": "org", - "repeatDirection": "h", "title": "$org", "type": "row" }, @@ -136,7 +135,9 @@ "minVizWidth": 75, "orientation": "auto", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "", "values": false }, @@ -144,7 +145,7 @@ "showThresholdMarkers": true, "sizing": "auto" }, - "pluginVersion": "11.1.0-70903", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -244,6 +245,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 20, "gradientMode": "none", @@ -304,7 +306,7 @@ "sort": "none" } }, - "pluginVersion": "11.1.0-69950", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -424,8 +426,11 @@ "graphMode": "area", "justifyMode": "center", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": ["lastNotNull"], + "calcs": [ + "lastNotNull" + ], "fields": "/^_dateTime$/", "values": false }, @@ -436,7 +441,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "11.1.0-70903", + "pluginVersion": "11.3.0-75420", "targets": [ { "datasource": { @@ -497,7 +502,6 @@ "uid": "${DS_GRAFANACLOUD-CLOUDITY-LOGS}" }, "definition": "", - "hide": 0, "includeAll": true, "label": "Salesforce Org", "multi": true, @@ -511,7 +515,6 @@ }, "refresh": 1, "regex": "^[^.]*$", - "skipUrlSync": false, "sort": 1, "type": "query" }, @@ -525,7 +528,6 @@ "hide": 2, "includeAll": false, "label": "Type", - "multi": false, "name": "type", "options": [], "query": { @@ -536,28 +538,23 @@ }, "refresh": 1, "regex": "", - "skipUrlSync": false, - "sort": 0, "type": "query" }, { "current": { - "selected": false, "text": "DataStorageMB", "value": "DataStorageMB" }, - "hide": 0, "label": "Limit Identifier", "name": "limitId", "options": [ { "selected": true, - "text": "", - "value": "" + "text": "DataStorageMB", + "value": "DataStorageMB" } ], "query": "DataStorageMB", - "skipUrlSync": false, "type": "textbox" } ] @@ -566,13 +563,10 @@ "from": "now-7d", "to": "now" }, - "timeRangeUpdatedDuringEditOrView": false, - "timepicker": { - "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] - }, + "timepicker": {}, "timezone": "browser", "title": "DTL - Limits Evolution", "uid": "sfdx-hardis-limits-evolution", "version": 24, "weekStart": "" -} +} \ No newline at end of file diff --git a/docs/hardis/auth/login.md b/docs/hardis/auth/login.md index e5a4364b8..2686a64c2 100644 --- a/docs/hardis/auth/login.md +++ b/docs/hardis/auth/login.md @@ -1,4 +1,4 @@ - + # hardis:auth:login ## Description @@ -7,21 +7,21 @@ Login to salesforce org ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| devhub
-h | boolean | Also connect associated DevHub | | | | -| instanceurl
-r | option | URL of org instance | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| scratchorg
-s | boolean | Scratch org | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| devhub
-h | boolean | Also connect associated DevHub | | | | +| flags-dir | option | undefined | | | | +| instanceurl
-r | option | URL of org instance | | | | +| json | boolean | Format output as json. | | | | +| scratchorg
-s | boolean | Scratch org | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:auth:login +sf hardis:auth:login ``` diff --git a/docs/hardis/cache/clear.md b/docs/hardis/cache/clear.md index ed21200f8..6794d4e5c 100644 --- a/docs/hardis/cache/clear.md +++ b/docs/hardis/cache/clear.md @@ -1,4 +1,4 @@ - + # hardis:cache:clear ## Description @@ -7,18 +7,18 @@ Clear cache generated by sfdx-hardis ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:cache:clear +sf hardis:cache:clear ``` diff --git a/docs/hardis/config/get.md b/docs/hardis/config/get.md index 562bc518e..382cd2770 100644 --- a/docs/hardis/config/get.md +++ b/docs/hardis/config/get.md @@ -1,4 +1,4 @@ - + # hardis:config:get ## Description @@ -7,19 +7,19 @@ Returns sfdx-hardis project config for a given level ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| level
-l | option | project,branch or user | project | | project
branch
user | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:---------------------------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| level
-l | option | project,branch or user | project | | project
branch
user | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:deploy:sources:metadata +sf hardis:project:deploy:sources:metadata ``` diff --git a/docs/hardis/doc/extract/permsetgroups.md b/docs/hardis/doc/extract/permsetgroups.md index a54950d30..4738b162a 100644 --- a/docs/hardis/doc/extract/permsetgroups.md +++ b/docs/hardis/doc/extract/permsetgroups.md @@ -1,4 +1,4 @@ - + # hardis:doc:extract:permsetgroups ## Description @@ -7,19 +7,19 @@ Generate markdown files with project documentation ## Parameters -| Name | Type | Description | Default | Required | Options | -|:------------------|:-------:|:------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:------------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:doc:extract:permsetgroups +sf hardis:doc:extract:permsetgroups ``` diff --git a/docs/hardis/doc/plugin/generate.md b/docs/hardis/doc/plugin/generate.md index ab9bbb410..9bf97a717 100644 --- a/docs/hardis/doc/plugin/generate.md +++ b/docs/hardis/doc/plugin/generate.md @@ -1,4 +1,4 @@ - + # hardis:doc:plugin:generate ## Description @@ -18,18 +18,18 @@ At each merge into master/main branch, the GitHub Action build-deploy-docs will ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:doc:plugin:generate +sf hardis:doc:plugin:generate ``` diff --git a/docs/hardis/lint/access.md b/docs/hardis/lint/access.md index c0183e386..327888470 100644 --- a/docs/hardis/lint/access.md +++ b/docs/hardis/lint/access.md @@ -1,38 +1,39 @@ - + # hardis:lint:access ## Description Check if elements(apex class and field) are at least in one permission set + +This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-missing-access/) and can output Grafana, Slack and MsTeams Notifications. + ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-----------------------|:-------:|:--------------------------------------------------------------------|:---------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| elementsignored
-e | option | Ignore specific elements separated by commas | | | | -| folder
-f | option | Root folder | force-app | | | -| ignorerights
-i | option | Ignore permission sets or profiles | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-----------------------|:-------:|:------------------------------------------------------------------|:---------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| elementsignored
-e | option | Ignore specific elements separated by commas | | | | +| flags-dir | option | undefined | | | | +| folder
-f | option | Root folder | force-app | | | +| ignorerights
-i | option | Ignore permission sets or profiles | | | | +| json | boolean | Format output as json. | | | | +| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:lint:access +sf hardis:lint:access ``` ```shell -sfdx hardis:lint:access -e "ApexClass:ClassA, CustomField:Account.CustomField" +sf hardis:lint:access -e "ApexClass:ClassA, CustomField:Account.CustomField" ``` ```shell -sfdx hardis:lint:access -i "PermissionSet:permissionSetA, Profile" +sf hardis:lint:access -i "PermissionSet:permissionSetA, Profile" ``` diff --git a/docs/hardis/lint/metadatastatus.md b/docs/hardis/lint/metadatastatus.md index a0e6d0996..8644249cf 100644 --- a/docs/hardis/lint/metadatastatus.md +++ b/docs/hardis/lint/metadatastatus.md @@ -1,27 +1,28 @@ - + # hardis:lint:metadatastatus ## Description -Check if elements(flows) are inactive in the project +Check if elements (flows and validation rules) are inactive in the project + +This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-inactive-metadata/) and can output Grafana, Slack and MsTeams Notifications. + ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:------------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:lint:metadatastatus +sf hardis:lint:metadatastatus ``` diff --git a/docs/hardis/lint/missingattributes.md b/docs/hardis/lint/missingattributes.md index 510ade60e..112a025ae 100644 --- a/docs/hardis/lint/missingattributes.md +++ b/docs/hardis/lint/missingattributes.md @@ -1,4 +1,4 @@ - + # hardis:lint:missingattributes ## Description @@ -7,21 +7,19 @@ Check if elements(custom fields) aren't description ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:------------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:lint:missingattributes +sf hardis:lint:missingattributes ``` diff --git a/docs/hardis/lint/unusedmetadatas.md b/docs/hardis/lint/unusedmetadatas.md index 23044e48f..976183a28 100644 --- a/docs/hardis/lint/unusedmetadatas.md +++ b/docs/hardis/lint/unusedmetadatas.md @@ -1,27 +1,28 @@ - + # hardis:lint:unusedmetadatas ## Description Check if elements (custom labels and custom permissions) are used in the project +This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-unused-metadata/) and can output Grafana, Slack and MsTeams Notifications. + + ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:------------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:lint:unusedmetadatas +sf hardis:lint:unusedmetadatas ``` diff --git a/docs/hardis/mdapi/deploy.md b/docs/hardis/mdapi/deploy.md index b57e9b9fc..e1ca7d4cf 100644 --- a/docs/hardis/mdapi/deploy.md +++ b/docs/hardis/mdapi/deploy.md @@ -1,4 +1,4 @@ - + # hardis:mdapi:deploy ## Description @@ -12,28 +12,27 @@ sfdx-hardis wrapper for sfdx force:mdapi:deploy that displays tips to solve depl ## Parameters -| Name | Type | Description | Default | Required | Options | -|:--------------------------------|:-------:|:--------------------------------------------------------------------|:---------:|:--------:|:----------------------------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| checkonly
-c | boolean | checkOnly | | | | -| concise | boolean | concise | | | | -| debug | boolean | debug | | | | -| deploydir
-d | option | deployDir | | | | -| ignoreerrors
-o | boolean | ignoreErrors | | | | -| ignorewarnings
-g | boolean | ignoreWarnings | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| purgeondelete | boolean | purgeOnDelete | | | | -| runtests
-r | option | runTests | | | | -| singlepackage
-s | boolean | singlePackage | | | | -| soapdeploy | boolean | soapDeploy | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| testlevel
-l | option | testLevel | NoTestRun | | NoTestRun
RunSpecifiedTests
RunLocalTests
RunAllTestsInOrg | -| validateddeployrequestid
-q | option | validatedDeployRequestId | | | | -| verbose | boolean | verbose | | | | -| wait
-w | option | wait | 0 minutes | | | -| websocket | option | websocket | | | | -| zipfile
-f | option | zipFile | | | | +| Name | Type | Description | Default | Required | Options | +|:--------------------------------|:-------:|:-------------------------|:---------:|:--------:|:----------------------------------------------------------------------:| +| checkonly
-c | boolean | checkOnly | | | | +| concise | boolean | concise | | | | +| debug | boolean | debug | | | | +| deploydir
-d | option | deployDir | | | | +| flags-dir | option | undefined | | | | +| ignoreerrors
-o | boolean | ignoreErrors | | | | +| ignorewarnings
-g | boolean | ignoreWarnings | | | | +| json | boolean | Format output as json. | | | | +| purgeondelete | boolean | purgeOnDelete | | | | +| runtests
-r | option | runTests | | | | +| singlepackage
-s | boolean | singlePackage | | | | +| soapdeploy | boolean | soapDeploy | | | | +| target-org
-o | option | undefined | | | | +| testlevel
-l | option | testLevel | NoTestRun | | NoTestRun
RunSpecifiedTests
RunLocalTests
RunAllTestsInOrg | +| validateddeployrequestid
-q | option | validatedDeployRequestId | | | | +| verbose | boolean | verbose | | | | +| wait
-w | option | wait | 120 | | | +| websocket | option | websocket | | | | +| zipfile
-f | option | zipFile | | | | ## Examples diff --git a/docs/hardis/misc/purge-references.md b/docs/hardis/misc/purge-references.md new file mode 100644 index 000000000..7c7be2a73 --- /dev/null +++ b/docs/hardis/misc/purge-references.md @@ -0,0 +1,30 @@ + +# hardis:misc:purge-references + +## Description + +Purge references to any string in org metadatas before a deployment. + +For example, this can be handy if you need to change the type of a custom field from Master Detail to Lookup. + +USE WITH EXTREME CAUTION AND CAREFULLY READ THE MESSAGES ! + +## Parameters + +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| references
-r | option | Comma-separated list of references to find in metadatas | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | + +## Examples + +```shell +sf hardis:misc:purge-references +``` + + diff --git a/docs/hardis/misc/toml2csv.md b/docs/hardis/misc/toml2csv.md index 7cd2379cd..1611e23d6 100644 --- a/docs/hardis/misc/toml2csv.md +++ b/docs/hardis/misc/toml2csv.md @@ -1,4 +1,4 @@ - + # hardis:misc:toml2csv ## Description @@ -7,37 +7,36 @@ Split TOML file into distinct CSV files ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:-------------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| filtersections
-l | option | List of sections to process (if not set, all sections will be processed) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| outputdir
-o | option | Output directory | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| skiptransfo
-s | boolean | Do not apply transformation to input data | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| tomlfile
-f | option | Input TOML file path | | | | -| transfoconfig
-t | option | Path to JSON config file for mapping and transformation | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:----------------------|:-------:|:-------------------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| filtersections
-l | option | List of sections to process (if not set, all sections will be processed) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| outputdir
-o | option | Output directory | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| skiptransfo
-s | boolean | Do not apply transformation to input data | | | | +| target-org
-o | option | undefined | | | | +| tomlfile
-f | option | Input TOML file path | | | | +| transfoconfig
-t | option | Path to JSON config file for mapping and transformation | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:misc:toml2csv --tomlfile 'D:/clients/toto/V1_full.txt' +sf hardis:misc:toml2csv --tomlfile 'D:/clients/toto/V1_full.txt' ``` ```shell -sfdx hardis:misc:toml2csv --skiptransfo --tomlfile 'D:/clients/toto/V1_full.txt' +sf hardis:misc:toml2csv --skiptransfo --tomlfile 'D:/clients/toto/V1_full.txt' ``` ```shell -sfdx hardis:misc:toml2csv --skiptransfo --tomlfile 'D:/clients/toto/V1_full.txt' --outputdir 'C:/tmp/rrrr' +sf hardis:misc:toml2csv --skiptransfo --tomlfile 'D:/clients/toto/V1_full.txt' --outputdir 'C:/tmp/rrrr' ``` ```shell -NODE_OPTIONS=--max_old_space_size=9096 sfdx hardis:misc:toml2csv --skiptransfo --tomlfile './input/V1.txt' --outputdir './output' --filtersections 'COMPTES,SOUS' +NODE_OPTIONS=--max_old_space_size=9096 sf hardis:misc:toml2csv --skiptransfo --tomlfile './input/V1.txt' --outputdir './output' --filtersections 'COMPTES,SOUS' ``` diff --git a/docs/hardis/org/configure/data.md b/docs/hardis/org/configure/data.md index f7009140d..4f9ecb039 100644 --- a/docs/hardis/org/configure/data.md +++ b/docs/hardis/org/configure/data.md @@ -1,4 +1,4 @@ - + # hardis:org:configure:data ## Description @@ -12,18 +12,18 @@ See article: ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:configure:data +sf hardis:org:configure:data ``` diff --git a/docs/hardis/org/configure/files.md b/docs/hardis/org/configure/files.md index 1174cbc7f..a0104b543 100644 --- a/docs/hardis/org/configure/files.md +++ b/docs/hardis/org/configure/files.md @@ -1,4 +1,4 @@ - + # hardis:org:configure:files ## Description @@ -12,18 +12,18 @@ See article below ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:configure:files +sf hardis:org:configure:files ``` diff --git a/docs/hardis/org/configure/monitoring.md b/docs/hardis/org/configure/monitoring.md index fbf3ade38..74fc9facc 100644 --- a/docs/hardis/org/configure/monitoring.md +++ b/docs/hardis/org/configure/monitoring.md @@ -1,4 +1,4 @@ - + # hardis:org:configure:monitoring ## Description @@ -7,21 +7,20 @@ Configure monitoring of an org ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| orginstanceurl | option | Org instance url (technical param, do not use manually) | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| orginstanceurl | option | Org instance url (technical param, do not use manually) | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:configure:monitoring +sf hardis:org:configure:monitoring ``` diff --git a/docs/hardis/org/connect.md b/docs/hardis/org/connect.md index 66d350a04..7ecda1ad4 100644 --- a/docs/hardis/org/connect.md +++ b/docs/hardis/org/connect.md @@ -1,4 +1,4 @@ - + # hardis:org:connect ## Description @@ -8,18 +8,18 @@ Connect to an org without setting it as default username, then proposes to open ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:connect +sf hardis:org:connect ``` diff --git a/docs/hardis/org/create.md b/docs/hardis/org/create.md index a414f7ef0..22d1e5466 100644 --- a/docs/hardis/org/create.md +++ b/docs/hardis/org/create.md @@ -1,4 +1,4 @@ - + # hardis:org:create ## Description @@ -7,20 +7,18 @@ Create and initialize sandbox org ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------------|:-------:|:---------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetdevhubusername
-v | option | username or alias for the dev hub org; overrides default dev hub org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:create +sf hardis:org:create ``` diff --git a/docs/hardis/org/data/delete.md b/docs/hardis/org/data/delete.md index 20504e534..1f62f3ea7 100644 --- a/docs/hardis/org/data/delete.md +++ b/docs/hardis/org/data/delete.md @@ -1,27 +1,32 @@ - + # hardis:org:data:delete ## Description -Delete data in org using sfdmu +Delete records in multiple objects using SFDMU Workspace + +If you need to run this command in production, you need to: + +- define runnableInProduction in export.json +- define sfdmuCanModify: YOUR_INSTANCE_URL in config/branches/.sfdx-hardis.YOUR_BRANCH.yml + ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| path
-p | option | Path to the sfdmu workspace folder | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| path
-p | option | Path to the sfdmu workspace folder | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:data:delete +sf hardis:org:data:delete ``` diff --git a/docs/hardis/org/data/export.md b/docs/hardis/org/data/export.md index 6ac8f3234..11ee83af0 100644 --- a/docs/hardis/org/data/export.md +++ b/docs/hardis/org/data/export.md @@ -1,4 +1,4 @@ - + # hardis:org:data:export ## Description @@ -12,21 +12,20 @@ See article: ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| path
-p | option | Path to the sfdmu workspace folder | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| path
-p | option | Path to the sfdmu workspace folder | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:data:export +sf hardis:org:data:export ``` diff --git a/docs/hardis/org/data/import.md b/docs/hardis/org/data/import.md index a2a85da7b..651419d0a 100644 --- a/docs/hardis/org/data/import.md +++ b/docs/hardis/org/data/import.md @@ -1,4 +1,4 @@ - + # hardis:org:data:import ## Description @@ -12,21 +12,20 @@ See article: ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| path
-p | option | Path to the sfdmu workspace folder | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| path
-p | option | Path to the sfdmu workspace folder | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:data:import +sf hardis:org:data:import ``` diff --git a/docs/hardis/org/diagnose/audittrail.md b/docs/hardis/org/diagnose/audittrail.md index 557cc08ed..0d894ce2b 100644 --- a/docs/hardis/org/diagnose/audittrail.md +++ b/docs/hardis/org/diagnose/audittrail.md @@ -1,4 +1,4 @@ - + # hardis:org:diagnose:audittrail ## Description @@ -17,6 +17,8 @@ Regular setup actions performed in major orgs are filtered. - Custom App Licenses - addeduserpackagelicense - granteduserpackagelicense +- Customer Portal + - createdcustomersuccessuser - Currency - updateddatedexchrate - Data Management @@ -98,39 +100,40 @@ monitoringAllowedSectionsActions: "Some section": [] // Will ignore all actions from such section "Some other section": ["actionType1","actionType2","actionType3"] // Will ignore only those 3 actions from section "Some other section". Other actions in the same section will be considered as suspect. ``` - + +This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-suspect-audit-trail/) and can output Grafana, Slack and MsTeams Notifications. + ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| excludeusers
-e | option | Comma-separated list of usernames to exclude | | | | -| json | boolean | format output as json | | | | -| lastndays
-t | option | Number of days to extract from today (included) | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:--------------------|:-------:|:------------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| excludeusers
-e | option | Comma-separated list of usernames to exclude | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| lastndays
-t | option | Number of days to extract from today (included) | | | | +| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:diagnose:audittrail +sf hardis:org:diagnose:audittrail ``` ```shell -sfdx hardis:org:diagnose:audittrail --excludeusers baptiste@titi.com +sf hardis:org:diagnose:audittrail --excludeusers baptiste@titi.com ``` ```shell -sfdx hardis:org:diagnose:audittrail --excludeusers baptiste@titi.com,bertrand@titi.com +sf hardis:org:diagnose:audittrail --excludeusers baptiste@titi.com,bertrand@titi.com ``` ```shell -sfdx hardis:org:diagnose:audittrail --lastndays 5 +sf hardis:org:diagnose:audittrail --lastndays 5 ``` diff --git a/docs/hardis/org/diagnose/instanceupgrade.md b/docs/hardis/org/diagnose/instanceupgrade.md new file mode 100644 index 000000000..0b31c8adb --- /dev/null +++ b/docs/hardis/org/diagnose/instanceupgrade.md @@ -0,0 +1,26 @@ + +# hardis:org:diagnose:instanceupgrade + +## Description + +Get the date when the org instance will be upgraded (to Spring, Summer or Winter) + + +## Parameters + +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | + +## Examples + +```shell +sf hardis:org:diagnose:instanceupgrade +``` + + diff --git a/docs/hardis/org/diagnose/legacyapi.md b/docs/hardis/org/diagnose/legacyapi.md index e7abf247e..25e05aec7 100644 --- a/docs/hardis/org/diagnose/legacyapi.md +++ b/docs/hardis/org/diagnose/legacyapi.md @@ -1,4 +1,4 @@ - + # hardis:org:diagnose:legacyapi ## Description @@ -10,39 +10,39 @@ See article below [![Handle Salesforce API versions Deprecation like a pro](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-deprecated-api.jpg)](https://nicolas.vuillamy.fr/handle-salesforce-api-versions-deprecation-like-a-pro-335065f52238) +This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-deprecated-api-calls/) and can output Grafana, Slack and MsTeams Notifications. ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| eventtype
-e | option | Type of EventLogFile event to analyze | ApiTotalUsage | | | -| json | boolean | format output as json | | | | -| limit
-l | option | Number of latest EventLogFile events to analyze | 999 | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:------------------------------------------------------------------|:-------------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| eventtype
-e | option | Type of EventLogFile event to analyze | ApiTotalUsage | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| limit
-l | option | Number of latest EventLogFile events to analyze | 999 | | | +| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:diagnose:legacyapi +sf hardis:org:diagnose:legacyapi ``` ```shell -sfdx hardis:org:diagnose:legacyapi -u hardis@myclient.com +sf hardis:org:diagnose:legacyapi -u hardis@myclient.com ``` ```shell -sfdx hardis:org:diagnose:legacyapi --outputfile 'c:/path/to/folder/legacyapi.csv' +sf hardis:org:diagnose:legacyapi --outputfile 'c:/path/to/folder/legacyapi.csv' ``` ```shell -sfdx hardis:org:diagnose:legacyapi -u hardis@myclient.com --outputfile ./tmp/legacyapi.csv +sf hardis:org:diagnose:legacyapi -u hardis@myclient.com --outputfile ./tmp/legacyapi.csv ``` diff --git a/docs/hardis/org/diagnose/licenses.md b/docs/hardis/org/diagnose/licenses.md index 77759aa63..59d821018 100644 --- a/docs/hardis/org/diagnose/licenses.md +++ b/docs/hardis/org/diagnose/licenses.md @@ -1,4 +1,4 @@ - + # hardis:org:diagnose:licenses ## Description @@ -7,22 +7,21 @@ Mostly used for monitoring (Grafana) but you can also use it manually :) ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| usedonly
-u | boolean | Filter to have only used licenses | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:------------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| usedonly
-u | boolean | Filter to have only used licenses | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:diagnose:licenses +sf hardis:org:diagnose:licenses ``` diff --git a/docs/hardis/org/diagnose/releaseupdates.md b/docs/hardis/org/diagnose/releaseupdates.md new file mode 100644 index 000000000..7fe641db7 --- /dev/null +++ b/docs/hardis/org/diagnose/releaseupdates.md @@ -0,0 +1,33 @@ + +# hardis:org:diagnose:releaseupdates + +## Description + +Export Release Updates into a CSV file with selected criteria, and highlight Release Updates that should be checked. + +Before publishing **Breaking Changes** ❌, Salesforce announce them in the setup menu [**Release Updates**](https://help.salesforce.com/s/articleView?id=sf.release_updates.htm&type=5) + +⚠️ Some of them are very important, because if you don't make the related upgrades in time (ex: before Winter 25) , your production org can crash ! + +This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-release-updates/) and can output Grafana, Slack and MsTeams Notifications. + + +## Parameters + +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:------------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | + +## Examples + +```shell +sf hardis:org:diagnose:releaseupdates +``` + + diff --git a/docs/hardis/org/diagnose/unusedlicenses.md b/docs/hardis/org/diagnose/unusedlicenses.md index e54cdcbab..76f0eb821 100644 --- a/docs/hardis/org/diagnose/unusedlicenses.md +++ b/docs/hardis/org/diagnose/unusedlicenses.md @@ -1,40 +1,41 @@ - + # hardis:org:diagnose:unusedlicenses ## Description When you assign a Permission Set to a user, and that this Permission Set is related to a Permission Set License, a Permission Set License Assignment is automatically created for the user. - But when you unassign this Permission Set from the user, **the Permission Set License Assignment is not deleted**. +But when you unassign this Permission Set from the user, **the Permission Set License Assignment is not deleted**. - This leads that you can be **charged for Permission Set Licenses that are not used** ! +This leads that you can be **charged for Permission Set Licenses that are not used** ! - This command detects such useless Permission Set Licenses Assignments and suggests to delete them. +This command detects such useless Permission Set Licenses Assignments and suggests to delete them. + +Many thanks to [Vincent Finet](https://www.linkedin.com/in/vincentfinet/) for the inspiration during his great speaker session at [French Touch Dreamin '23](https://frenchtouchdreamin.com/), and his kind agreement for reusing such inspiration in this command :) + +This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-unused-licenses/) and can output Grafana, Slack and MsTeams Notifications. - Many thanks to [Vincent Finet](https://www.linkedin.com/in/vincentfinet/) for the inspiration during his great speaker session at [French Touch Dreamin '23](https://frenchtouchdreamin.com/), and his kind agreement for reusing such inspiration in this command :) - ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:------------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:diagnose:unusedlicenses +sf hardis:org:diagnose:unusedlicenses ``` ```shell -sfdx hardis:org:diagnose:unusedlicenses --fix +sf hardis:org:diagnose:unusedlicenses --fix ``` diff --git a/docs/hardis/org/diagnose/unusedusers.md b/docs/hardis/org/diagnose/unusedusers.md index 8982ae89d..e63888831 100644 --- a/docs/hardis/org/diagnose/unusedusers.md +++ b/docs/hardis/org/diagnose/unusedusers.md @@ -1,4 +1,4 @@ - + # hardis:org:diagnose:unusedusers ## Description @@ -17,44 +17,45 @@ Note: You can see the full list of available license identifiers in [Salesforce Use --returnactiveusers to revert the command and retrieve active users that has logged in during the period. +This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-inactive-users/) and can output Grafana, Slack and MsTeams Notifications. + ## Parameters -| Name | Type | Description | Default | Required | Options | -|:--------------------------|:-------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| days
-t | option | Extracts the users that have been inactive for the amount of days specified. In CI, default is 180 days | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| licenseidentifiers
-i | option | Comma-separated list of license identifiers, in case licensetypes is not used.. Identifiers available at | | | | -| licensetypes
-l | option | Type of licenses to check. If set, do not use licenseidentifiers option. In CI, default is all-crm | | | all
all-crm
all-paying | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | -| returnactiveusers | boolean | Inverts the command by returning the active users | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:--------------------------|:-------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------:|:--------:|:------------------------------:| +| days
-t | option | Extracts the users that have been inactive for the amount of days specified. In CI, default is 180 days | | | | +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| licenseidentifiers
-i | option | Comma-separated list of license identifiers, in case licensetypes is not used.. Identifiers available at | | | | +| licensetypes
-l | option | Type of licenses to check. If set, do not use licenseidentifiers option. In CI, default is all-crm | | | all
all-crm
all-paying | +| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | +| returnactiveusers | boolean | Inverts the command by returning the active users | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:diagnose:unusedusers +sf hardis:org:diagnose:unusedusers ``` ```shell -sfdx hardis:org:diagnose:unusedusers --days 365 +sf hardis:org:diagnose:unusedusers --days 365 ``` ```shell -sfdx hardis:org:diagnose:unusedusers --days 60 --licensetypes all-crm +sf hardis:org:diagnose:unusedusers --days 60 --licensetypes all-crm ``` ```shell -sfdx hardis:org:diagnose:unusedusers --days 60 --licenseidentifiers SFDC,AUL,AUL1 +sf hardis:org:diagnose:unusedusers --days 60 --licenseidentifiers SFDC,AUL,AUL1 ``` ```shell -sfdx hardis:org:diagnose:unusedusers --days 60 --licensetypes all-crm --returnactiveusers +sf hardis:org:diagnose:unusedusers --days 60 --licensetypes all-crm --returnactiveusers ``` diff --git a/docs/hardis/org/files/export.md b/docs/hardis/org/files/export.md index 39893d547..8cee3aa0d 100644 --- a/docs/hardis/org/files/export.md +++ b/docs/hardis/org/files/export.md @@ -1,4 +1,4 @@ - + # hardis:org:files:export ## Description @@ -12,24 +12,23 @@ See article below ## Parameters -| Name | Type | Description | Default | Required | Options | -|:------------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| chunksize
-c | option | Number of records to add in a chunk before it is processed | 1000 | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| path
-p | option | Path to the file export project | | | | -| polltimeout
-t | option | Timeout in MS for Bulk API calls | 300000 | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| startchunknumber
-s | option | Chunk number to start from | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| chunksize
-c | option | Number of records to add in a chunk before it is processed | 1000 | | | +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| path
-p | option | Path to the file export project | | | | +| polltimeout
-t | option | Timeout in MS for Bulk API calls | 300000 | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| startchunknumber
-s | option | Chunk number to start from | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:files:export +sf hardis:org:files:export ``` diff --git a/docs/hardis/org/files/import.md b/docs/hardis/org/files/import.md new file mode 100644 index 000000000..146356faf --- /dev/null +++ b/docs/hardis/org/files/import.md @@ -0,0 +1,32 @@ + +# hardis:org:files:import + +## Description + +Import file attachments into a Salesforce org + +See article below to see how to Export them. + +[![How to mass download notes and attachments files from a Salesforce org](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-mass-download.jpg)](https://nicolas.vuillamy.fr/how-to-mass-download-notes-and-attachments-files-from-a-salesforce-org-83a028824afd) + + +## Parameters + +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| overwrite
-o | boolean | Override existing files (doubles the number of API calls) | | | | +| path
-p | option | Path to the file export project | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | + +## Examples + +```shell +sf hardis:org:files:import +``` + + diff --git a/docs/hardis/org/fix/listviewmine.md b/docs/hardis/org/fix/listviewmine.md index 9d560a974..b2aaf8d47 100644 --- a/docs/hardis/org/fix/listviewmine.md +++ b/docs/hardis/org/fix/listviewmine.md @@ -1,4 +1,4 @@ - + # hardis:org:fix:listviewmine ## Description @@ -56,26 +56,25 @@ ENV PUPPETEER_EXECUTABLE_PATH="$\{CHROMIUM_PATH}" // remove \ before { ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------|:-------:|:--------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | |listviews
-l|option|Comma-separated list of listviews following format Object:ListViewName Example: Contact:MyContacts,Contact:MyActiveContacts,Opportunity:MYClosedOpportunities|||| -|loglevel|option|logging level for this command invocation|warn||trace
debug
info
warn
error
fatal| |skipauth|boolean|Skip authentication check when a default username is required|||| -|targetusername
-u|option|username or alias for the target org; overrides default target org|||| +|target-org
-o|option|undefined|||| |websocket|option|Websocket host:port for VsCode SFDX Hardis UI integration|||| ## Examples ```shell -sfdx hardis:org:fix:listviewmine +sf hardis:org:fix:listviewmine ``` ```shell -sfdx hardis:org:fix:listviewmine --listviews Opportunity:MySubscriptions,Account:MyActivePartners +sf hardis:org:fix:listviewmine --listviews Opportunity:MySubscriptions,Account:MyActivePartners ``` diff --git a/docs/hardis/org/generate/packagexmlfull.md b/docs/hardis/org/generate/packagexmlfull.md index 5303e1277..b045e5944 100644 --- a/docs/hardis/org/generate/packagexmlfull.md +++ b/docs/hardis/org/generate/packagexmlfull.md @@ -1,4 +1,4 @@ - + # hardis:org:generate:packagexmlfull ## Description @@ -7,29 +7,28 @@ Generates full org package.xml, including managed items ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| outputfile | option | Output package.xml file | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| outputfile | option | Output package.xml file | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:generate:packagexmlfull +sf hardis:org:generate:packagexmlfull ``` ```shell -sfdx hardis:org:generate:packagexmlfull --outputfile /tmp/packagexmlfull.xml +sf hardis:org:generate:packagexmlfull --outputfile /tmp/packagexmlfull.xml ``` ```shell -sfdx hardis:org:generate:packagexmlfull --targetusername nico@example.com +sf hardis:org:generate:packagexmlfull --target-org nico@example.com ``` diff --git a/docs/hardis/org/monitor/all.md b/docs/hardis/org/monitor/all.md index 93bef4a0b..f96223bac 100644 --- a/docs/hardis/org/monitor/all.md +++ b/docs/hardis/org/monitor/all.md @@ -1,4 +1,4 @@ - + # hardis:org:monitor:all ## Description @@ -29,9 +29,9 @@ Example: ```yaml monitoringCommands: - title: My Custom command - command: sfdx my:custom:command + command: sf my:custom:command - title: My Custom command 2 - command: sfdx my:other:custom:command + command: sf my:other:custom:command ``` You can force the daily run of all commands by defining env var `MONITORING_IGNORE_FREQUENCY=true` @@ -40,20 +40,19 @@ You can force the daily run of all commands by defining env var `MONITORING_IGNO ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:monitor:all +sf hardis:org:monitor:all ``` diff --git a/docs/hardis/org/monitor/backup.md b/docs/hardis/org/monitor/backup.md index e0037b258..111852c72 100644 --- a/docs/hardis/org/monitor/backup.md +++ b/docs/hardis/org/monitor/backup.md @@ -1,34 +1,37 @@ - + # hardis:org:monitor:backup ## Description Retrieve sfdx sources in the context of a monitoring backup - + +Automatically skips metadatas from installed packages with namespace. + You can remove more metadata types from backup, especially in case you have too many metadatas and that provokes a crash, using: - Manual update of `manifest/package-skip-items.xml` config file (then commit & push in the same branch) - Environment variable MONITORING_BACKUP_SKIP_METADATA_TYPES (example: `MONITORING_BACKUP_SKIP_METADATA_TYPES=CustomLabel,StaticResource,Translation`): that will be applied to all monitoring branches. +This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-metadata-backup/) and can output Grafana, Slack and MsTeams Notifications. + ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:------------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:monitor:backup +sf hardis:org:monitor:backup ``` diff --git a/docs/hardis/org/monitor/limits.md b/docs/hardis/org/monitor/limits.md index 869a70d2b..817315055 100644 --- a/docs/hardis/org/monitor/limits.md +++ b/docs/hardis/org/monitor/limits.md @@ -1,27 +1,29 @@ - + # hardis:org:monitor:limits ## Description -Check limits of a SF org and send relatednotifications +Check limits of a SF org and send notifications about limits are superior to 50%, 75% or 100%. + +This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-org-limits/) and can output Grafana, Slack and MsTeams Notifications. + ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:------------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| outputfile
-o | option | Force the path and name of output report file. Must end with .csv | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:monitor:limits +sf hardis:org:monitor:limits ``` diff --git a/docs/hardis/org/purge/apexlog.md b/docs/hardis/org/purge/apexlog.md index 9d69e7a7a..6ceaf1703 100644 --- a/docs/hardis/org/purge/apexlog.md +++ b/docs/hardis/org/purge/apexlog.md @@ -1,4 +1,4 @@ - + # hardis:org:purge:apexlog ## Description @@ -7,25 +7,24 @@ Purge apex logs in selected org ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| prompt
-z | boolean | Prompt for confirmation (true by default, use --no-prompt to skip) | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:-------------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| prompt
-z | boolean | Prompt for confirmation (true by default, use --no-prompt to skip) | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:purge:apexlog +sf hardis:org:purge:apexlog ``` ```shell -sfdx hardis:org:purge:apexlog --targetusername nicolas.vuillamy@gmail.com +sf hardis:org:purge:apexlog --target-org nicolas.vuillamy@gmail.com ``` diff --git a/docs/hardis/org/purge/flow.md b/docs/hardis/org/purge/flow.md index 0ea5a83d0..c0644db7d 100644 --- a/docs/hardis/org/purge/flow.md +++ b/docs/hardis/org/purge/flow.md @@ -1,4 +1,4 @@ - + # hardis:org:purge:flow ## Description @@ -7,29 +7,29 @@ Purge Obsolete flow versions to avoid the 50 max versions limit. Filters on Stat ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------------------|:-------:|:------------------------------------------------------------------------------------|:------------------------------:|:--------:|:-----------------------------------------------------:| -| allowpurgefailure
-f | boolean | Allows purges to fail without exiting with 1. Use --no-allowpurgefailure to disable | | | | -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| instanceurl
-r | option | URL of org instance | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| name
-n | option | Filter according to Name criteria | | | | -| prompt
-z | boolean | Prompt for confirmation (true by default, use --no-prompt to skip) | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| status
-s | option | Filter according to Status criteria | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------------------|:-------:|:-------------------------------------------------------------------------------------------------------------------------|:------------------------------:|:--------:|:-------:| +| allowpurgefailure
-f | boolean | Allows purges to fail without exiting with 1. Use --no-allowpurgefailure to disable | | | | +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| delete-flow-interviews
-f | boolean | If the presence of Flow interviews prevent to delete flows versions, delete them before retrying to delete flow versions | | | | +| flags-dir | option | undefined | | | | +| instanceurl
-r | option | URL of org instance | | | | +| json | boolean | Format output as json. | | | | +| name
-n | option | Filter according to Name criteria | | | | +| prompt
-z | boolean | Prompt for confirmation (true by default, use --no-prompt to skip) | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| status
-s | option | Filter according to Status criteria | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:purge:flow --no-prompt +sf hardis:org:purge:flow --no-prompt ``` ```shell -$ sfdx hardis:org:purge:flow --targetusername nicolas.vuillamy@gmail.com +$ sf hardis:org:purge:flow --target-org nicolas.vuillamy@gmail.com Found 1 records: ID MASTERLABEL VERSIONNUMBER DESCRIPTION STATUS 30109000000kX7uAAE TestFlow 2 test flowwww Obsolete @@ -42,7 +42,7 @@ $ sfdx hardis:org:purge:flow --targetusername nicolas.vuillamy@gmail.com ``` ```shell -$ sfdx hardis:org:purge:flow --targetusername nicolas.vuillamy@gmail.com --status "Obsolete,Draft,InvalidDraft --name TestFlow" +$ sf hardis:org:purge:flow --target-org nicolas.vuillamy@gmail.com --status "Obsolete,Draft,InvalidDraft --name TestFlow" Found 4 records: ID MASTERLABEL VERSIONNUMBER DESCRIPTION STATUS 30109000000kX7uAAE TestFlow 2 test flowwww Obsolete diff --git a/docs/hardis/org/retrieve/packageconfig.md b/docs/hardis/org/retrieve/packageconfig.md index f8e564981..450ab1f32 100644 --- a/docs/hardis/org/retrieve/packageconfig.md +++ b/docs/hardis/org/retrieve/packageconfig.md @@ -1,4 +1,4 @@ - + # hardis:org:retrieve:packageconfig ## Description @@ -7,24 +7,23 @@ Retrieve package configuration from an org ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:retrieve:packageconfig +sf hardis:org:retrieve:packageconfig ``` ```shell -sfdx hardis:org:retrieve:packageconfig -u myOrg +sf hardis:org:retrieve:packageconfig -u myOrg ``` diff --git a/docs/hardis/org/retrieve/sources/analytics.md b/docs/hardis/org/retrieve/sources/analytics.md index 0447cb732..69a2c5fb6 100644 --- a/docs/hardis/org/retrieve/sources/analytics.md +++ b/docs/hardis/org/retrieve/sources/analytics.md @@ -1,4 +1,4 @@ - + # hardis:org:retrieve:sources:analytics ## Description @@ -7,20 +7,19 @@ Retrieve all CRM Analytics sources from an org, with workarounds for SFDX bugs ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:retrieve:sources:analytics +sf hardis:org:retrieve:sources:analytics ``` diff --git a/docs/hardis/org/retrieve/sources/dx.md b/docs/hardis/org/retrieve/sources/dx.md index b2b7143b7..177589084 100644 --- a/docs/hardis/org/retrieve/sources/dx.md +++ b/docs/hardis/org/retrieve/sources/dx.md @@ -1,4 +1,4 @@ - + # hardis:org:retrieve:sources:dx ## Description @@ -7,26 +7,25 @@ Retrieve Salesforce DX project from org ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------------------|:-------:|:-----------------------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| filteredmetadatas
-m | option | Comma separated list of Metadatas keys to remove from PackageXml file | | | | -| folder
-f | option | Folder | . | | | -| instanceurl
-r | option | URL of org instance | | | | -| json | boolean | format output as json | | | | -| keepmetadatatypes
-k | option | Comma separated list of metadatas types that will be the only ones to be retrieved | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| shape
-o | boolean | Updates project-scratch-def.json from org shape | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| tempfolder
-t | option | Temporary folder | ./tmp | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------------------|:-------:|:-----------------------------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| filteredmetadatas
-m | option | Comma separated list of Metadatas keys to remove from PackageXml file | | | | +| flags-dir | option | undefined | | | | +| folder
-f | option | Folder | . | | | +| instanceurl
-r | option | URL of org instance | | | | +| json | boolean | Format output as json. | | | | +| keepmetadatatypes
-k | option | Comma separated list of metadatas types that will be the only ones to be retrieved | | | | +| shape
-o | boolean | Updates project-scratch-def.json from org shape | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| tempfolder
-t | option | Temporary folder | ./tmp | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:retrieve:sources:dx +sf hardis:org:retrieve:sources:dx ``` diff --git a/docs/hardis/org/retrieve/sources/dx2.md b/docs/hardis/org/retrieve/sources/dx2.md index 872ffb76e..2ebf30a46 100644 --- a/docs/hardis/org/retrieve/sources/dx2.md +++ b/docs/hardis/org/retrieve/sources/dx2.md @@ -1,4 +1,4 @@ - + # hardis:org:retrieve:sources:dx2 ## Description @@ -7,22 +7,21 @@ Retrieve Salesforce DX project from org ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| packagexml
-x | option | Path to package.xml file | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| template
-t | option | sfdx-hardis package.xml Template name. ex: wave | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| packagexml
-x | option | Path to package.xml file | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| template
-t | option | sfdx-hardis package.xml Template name. ex: wave | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:retrieve:sources:dx2 +sf hardis:org:retrieve:sources:dx2 ``` diff --git a/docs/hardis/org/retrieve/sources/metadata.md b/docs/hardis/org/retrieve/sources/metadata.md index 97d1621f0..fd6ce25ba 100644 --- a/docs/hardis/org/retrieve/sources/metadata.md +++ b/docs/hardis/org/retrieve/sources/metadata.md @@ -1,4 +1,4 @@ - + # hardis:org:retrieve:sources:metadata ## Description @@ -7,28 +7,27 @@ Retrieve Salesforce DX project from org ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| folder
-f | option | Folder | . | | | -| includemanaged | boolean | Include items from managed packages | | | | -| instanceurl
-r | option | URL of org instance | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| packagexml
-p | option | Path to package.xml manifest file | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| folder
-f | option | Folder | . | | | +| includemanaged | boolean | Include items from managed packages | | | | +| instanceurl
-r | option | URL of org instance | | | | +| json | boolean | Format output as json. | | | | +| packagexml
-p | option | Path to package.xml manifest file | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:retrieve:sources:metadata +sf hardis:org:retrieve:sources:metadata ``` ```shell -SFDX_RETRIEVE_WAIT_MINUTES=200 sfdx hardis:org:retrieve:sources:metadata +SFDX_RETRIEVE_WAIT_MINUTES=200 sf hardis:org:retrieve:sources:metadata ``` diff --git a/docs/hardis/org/retrieve/sources/retrofit.md b/docs/hardis/org/retrieve/sources/retrofit.md index 64a579548..e6637f22c 100644 --- a/docs/hardis/org/retrieve/sources/retrofit.md +++ b/docs/hardis/org/retrieve/sources/retrofit.md @@ -1,4 +1,4 @@ - + # hardis:org:retrieve:sources:retrofit ## Description @@ -56,14 +56,13 @@ Retrieve changes from org link to a ref branch not present in sources ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------|:-------:|:-------------------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| commit | boolean | If true, a commit will be performed after the retrofit | | | | -| commitmode | option | Defines if we commit all retrieved updates, or all updates including creations | updated | | updated
all | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | +| Name | Type | Description | Default | Required | Options | +|:-------------|:-------:|:-------------------------------------------------------------------------------|:-------:|:--------:|:---------------:| +| commit | boolean | If true, a commit will be performed after the retrofit | | | | +| commitmode | option | Defines if we commit all retrieved updates, or all updates including creations | updated | | updated
all | +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | |productionbranch|option|Name of the git branch corresponding to the org we want to perform the retrofit on. Can be defined in productionBranch property in .sfdx-hardis.yml|||| |push|boolean|If true, a push will be performed after the retrofit|||| @@ -71,21 +70,21 @@ Can be defined in productionBranch property in .sfdx-hardis.yml|||| |retrofittargetbranch|option|Name of branch the merge request will have as target Can be defined in retrofitBranch property in .sfdx-hardis.yml|||| |skipauth|boolean|Skip authentication check when a default username is required|||| -|targetusername
-u|option|username or alias for the target org; overrides default target org|||| +|target-org
-o|option|undefined|||| |websocket|option|Websocket host:port for VsCode SFDX Hardis UI integration|||| ## Examples ```shell -sfdx hardis:org:retrieve:sources:retrofit +sf hardis:org:retrieve:sources:retrofit ``` ```shell -sfdx hardis:org:retrieve:sources:retrofit --productionbranch master --commit --commitmode updated +sf hardis:org:retrieve:sources:retrofit --productionbranch master --commit --commitmode updated ``` ```shell -sfdx hardis:org:retrieve:sources:retrofit --productionbranch master --retrofitbranch preprod --commit --commitmode updated --push --pushmode mergerequest +sf hardis:org:retrieve:sources:retrofit --productionbranch master --retrofitbranch preprod --commit --commitmode updated --push --pushmode mergerequest ``` diff --git a/docs/hardis/org/select.md b/docs/hardis/org/select.md index 6d279573b..59dac1fcb 100644 --- a/docs/hardis/org/select.md +++ b/docs/hardis/org/select.md @@ -1,4 +1,4 @@ - + # hardis:org:select ## Description @@ -7,20 +7,20 @@ Interactive org selection for user ## Parameters -| Name | Type | Description | Default | Required | Options | -|:---------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| devhub
-h | boolean | Also connect associated DevHub | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| scratch
-s | boolean | Select scratch org related to default DevHub | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:---------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| devhub
-h | boolean | Also connect associated DevHub | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| scratch
-s | boolean | Select scratch org related to default DevHub | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:select +sf hardis:org:select ``` diff --git a/docs/hardis/org/test/apex.md b/docs/hardis/org/test/apex.md index e2df5da00..537c7a5f8 100644 --- a/docs/hardis/org/test/apex.md +++ b/docs/hardis/org/test/apex.md @@ -1,4 +1,4 @@ - + # hardis:org:test:apex ## Description @@ -10,26 +10,27 @@ If following configuration is defined, it will fail if apex coverage target is n - Env `APEX_TESTS_MIN_COVERAGE_ORG_WIDE` or `.sfdx-hardis` property `apexTestsMinCoverageOrgWide` - Env `APEX_TESTS_MIN_COVERAGE_ORG_WIDE` or `.sfdx-hardis` property `apexTestsMinCoverageOrgWide` -You can override env var SFDX_TEST_WAIT_MINUTES to wait more than 60 minutes +You can override env var SFDX_TEST_WAIT_MINUTES to wait more than 60 minutes. + +This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-apex-tests/) and can output Grafana, Slack and MsTeams Notifications. ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------------:|:--------:|:----------------------------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| testlevel
-l | option | Level of tests to apply to validate deployment | RunLocalTests | | NoTestRun
RunSpecifiedTests
RunLocalTests
RunAllTestsInOrg | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-------------:|:--------:|:----------------------------------------------------------------------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| testlevel
-l | option | Level of tests to apply to validate deployment | RunLocalTests | | NoTestRun
RunSpecifiedTests
RunLocalTests
RunAllTestsInOrg | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:test:apex +sf hardis:org:test:apex ``` diff --git a/docs/hardis/org/user/activateinvalid.md b/docs/hardis/org/user/activateinvalid.md index 258e1f772..fe3ccbf80 100644 --- a/docs/hardis/org/user/activateinvalid.md +++ b/docs/hardis/org/user/activateinvalid.md @@ -1,4 +1,4 @@ - + # hardis:org:user:activateinvalid ## Description @@ -14,29 +14,28 @@ See article below ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:-------------------------------------------------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| profiles
-p | option | Comma-separated list of profiles names that you want to reactive users assigned to and with a .invalid email | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:-------------------------------------------------------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| profiles
-p | option | Comma-separated list of profiles names that you want to reactive users assigned to and with a .invalid email | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:user:activateinvalid +sf hardis:org:user:activateinvalid ``` ```shell -sfdx hardis:org:user:activateinvalid --targetusername myuser@myorg.com +sf hardis:org:user:activateinvalid --target-org myuser@myorg.com ``` ```shell -sfdx hardis:org:user:activateinvalid --profiles 'System Administrator,MyCustomProfile' --targetusername myuser@myorg.com +sf hardis:org:user:activateinvalid --profiles 'System Administrator,MyCustomProfile' --target-org myuser@myorg.com ``` diff --git a/docs/hardis/org/user/freeze.md b/docs/hardis/org/user/freeze.md index 316ee2914..f76bc21f0 100644 --- a/docs/hardis/org/user/freeze.md +++ b/docs/hardis/org/user/freeze.md @@ -1,4 +1,4 @@ - + # hardis:org:user:freeze ## Description @@ -13,36 +13,35 @@ See user guide in the following article ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| excludeprofiles
-e | option | List of profiles that you want to NOT freeze, separated by commas | | | | -| includeprofiles
-p | option | List of profiles that you want to freeze, separated by commas | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| maxuserdisplay
-m | option | Maximum users to display in logs | 100 | | | -| name
-n | option | Filter according to Name criteria | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-----------------------|:-------:|:------------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| excludeprofiles
-e | option | List of profiles that you want to NOT freeze, separated by commas | | | | +| flags-dir | option | undefined | | | | +| includeprofiles
-p | option | List of profiles that you want to freeze, separated by commas | | | | +| json | boolean | Format output as json. | | | | +| maxuserdisplay
-m | option | Maximum users to display in logs | 100 | | | +| name
-n | option | Filter according to Name criteria | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:user:freeze +sf hardis:org:user:freeze ``` ```shell -sfdx hardis:org:user:freeze --targetusername myuser@myorg.com +sf hardis:org:user:freeze --target-org myuser@myorg.com ``` ```shell -sfdx hardis:org:user:freeze --includeprofiles 'Standard' +sf hardis:org:user:freeze --includeprofiles 'Standard' ``` ```shell -sfdx hardis:org:user:freeze --excludeprofiles 'System Administrator,Some Other Profile' +sf hardis:org:user:freeze --excludeprofiles 'System Administrator,Some Other Profile' ``` diff --git a/docs/hardis/org/user/unfreeze.md b/docs/hardis/org/user/unfreeze.md index b1e8d31a7..1bedc639f 100644 --- a/docs/hardis/org/user/unfreeze.md +++ b/docs/hardis/org/user/unfreeze.md @@ -1,4 +1,4 @@ - + # hardis:org:user:unfreeze ## Description @@ -13,36 +13,35 @@ See user guide in the following article ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| excludeprofiles
-e | option | List of profiles that you want to NOT unfreeze, separated by commas | | | | -| includeprofiles
-p | option | List of profiles that you want to unfreeze, separated by commas | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| maxuserdisplay
-m | option | Maximum users to display in logs | 100 | | | -| name
-n | option | Filter according to Name criteria | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| excludeprofiles
-e | option | List of profiles that you want to NOT unfreeze, separated by commas | | | | +| flags-dir | option | undefined | | | | +| includeprofiles
-p | option | List of profiles that you want to unfreeze, separated by commas | | | | +| json | boolean | Format output as json. | | | | +| maxuserdisplay
-m | option | Maximum users to display in logs | 100 | | | +| name
-n | option | Filter according to Name criteria | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:org:user:unfreeze +sf hardis:org:user:unfreeze ``` ```shell -sfdx hardis:org:user:unfreeze --targetusername myuser@myorg.com +sf hardis:org:user:unfreeze --target-org myuser@myorg.com ``` ```shell -sfdx hardis:org:user:unfreeze --includeprofiles 'Standard' +sf hardis:org:user:unfreeze --includeprofiles 'Standard' ``` ```shell -sfdx hardis:org:user:unfreeze --excludeprofiles 'System Administrator,Some Other Profile' +sf hardis:org:user:unfreeze --excludeprofiles 'System Administrator,Some Other Profile' ``` diff --git a/docs/hardis/package/create.md b/docs/hardis/package/create.md index 9aeea5169..b44907234 100644 --- a/docs/hardis/package/create.md +++ b/docs/hardis/package/create.md @@ -1,4 +1,4 @@ - + # hardis:package:create ## Description @@ -7,20 +7,19 @@ Create a new package ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------------|:-------:|:---------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetdevhubusername
-v | option | username or alias for the dev hub org; overrides default dev hub org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:----------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-dev-hub
-v | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:package:create +sf hardis:package:create ``` diff --git a/docs/hardis/package/install.md b/docs/hardis/package/install.md index 5d7706ad2..67df20aef 100644 --- a/docs/hardis/package/install.md +++ b/docs/hardis/package/install.md @@ -1,4 +1,4 @@ - + # hardis:package:install ## Description @@ -10,22 +10,21 @@ Assisted menu to propose to update `installedPackages` property in `.sfdx-hardis ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| installationkey
-k | option | installation key for key-protected package (default: null) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| package
-p | option | Package Version Id to install (04t...) | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-----------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| installationkey
-k | option | installation key for key-protected package (default: null) | | | | +| json | boolean | Format output as json. | | | | +| package
-p | option | Package Version Id to install (04t...) | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:package:install +sf hardis:package:install ``` diff --git a/docs/hardis/package/mergexml.md b/docs/hardis/package/mergexml.md index c0325d662..00d50c1af 100644 --- a/docs/hardis/package/mergexml.md +++ b/docs/hardis/package/mergexml.md @@ -1,4 +1,4 @@ - + # hardis:package:mergexml ## Description @@ -7,29 +7,30 @@ Select and merge package.xml files ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------------|:-------:|:---------------------------------------------------------------------------------------------|:-----------------:|:--------:|:-----------------------------------------------------:| -| folder
-f | option | Root folder | manifest | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| packagexmls
-p | option | Comma separated list of package.xml files to merge. Will be prompted to user if not provided | | | | -| pattern
-x | option | Name criteria to list package.xml files | /**/*package*.xml | | | -| result
-r | option | Result package.xml file name | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------------|:-------:|:---------------------------------------------------------------------------------------------|:-----------------:|:--------:|:-------:| +| debug | boolean | debug | | | | +| flags-dir | option | undefined | | | | +| folder
-f | option | Root folder | manifest | | | +| json | boolean | Format output as json. | | | | +| packagexmls
-p | option | Comma separated list of package.xml files to merge. Will be prompted to user if not provided | | | | +| pattern
-x | option | Name criteria to list package.xml files | /**/*package*.xml | | | +| result
-r | option | Result package.xml file name | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:package:mergexml +sf hardis:package:mergexml ``` ```shell -sfdx hardis:package:mergexml --folder packages --pattern /**/*.xml --result myMergedPackage.xml +sf hardis:package:mergexml --folder packages --pattern /**/*.xml --result myMergedPackage.xml ``` ```shell -sfdx hardis:package:mergexml --packagexmls "config/mypackage1.xml,config/mypackage2.xml,config/mypackage3.xml" --result myMergedPackage.xml +sf hardis:package:mergexml --packagexmls "config/mypackage1.xml,config/mypackage2.xml,config/mypackage3.xml" --result myMergedPackage.xml ``` diff --git a/docs/hardis/package/version/create.md b/docs/hardis/package/version/create.md index 359e512e3..d58928355 100644 --- a/docs/hardis/package/version/create.md +++ b/docs/hardis/package/version/create.md @@ -1,4 +1,4 @@ - + # hardis:package:version:create ## Description @@ -7,24 +7,23 @@ Create a new version of an unlocked package ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------------|:-------:|:--------------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| deleteafter | boolean | Delete package version after creating it | | | | -| install
-i | boolean | Install package version on default org after generation | | | | -| installkey
-k | option | Package installation key | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| package
-p | option | Package identifier that you want to use to generate a new package version | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetdevhubusername
-v | option | username or alias for the dev hub org; overrides default dev hub org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:----------------------|:-------:|:--------------------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| deleteafter | boolean | Delete package version after creating it | | | | +| flags-dir | option | undefined | | | | +| install
-i | boolean | Install package version on default org after generation | | | | +| installkey
-k | option | Package installation key | | | | +| json | boolean | Format output as json. | | | | +| package
-p | option | Package identifier that you want to use to generate a new package version | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-dev-hub
-v | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:package:version:create +sf hardis:package:version:create ``` diff --git a/docs/hardis/package/version/list.md b/docs/hardis/package/version/list.md index 719b45fb7..d690cf5dd 100644 --- a/docs/hardis/package/version/list.md +++ b/docs/hardis/package/version/list.md @@ -1,4 +1,4 @@ - + # hardis:package:version:list ## Description @@ -7,20 +7,19 @@ List versions of unlocked package ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------------|:-------:|:---------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetdevhubusername
-v | option | username or alias for the dev hub org; overrides default dev hub org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:----------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-dev-hub
-v | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:package:version:list +sf hardis:package:version:list ``` diff --git a/docs/hardis/package/version/promote.md b/docs/hardis/package/version/promote.md index a63d73fc4..d3c97b42a 100644 --- a/docs/hardis/package/version/promote.md +++ b/docs/hardis/package/version/promote.md @@ -1,4 +1,4 @@ - + # hardis:package:version:promote ## Description @@ -7,25 +7,24 @@ Promote package(s) version(s): convert it from beta to released ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------------|:-------:|:---------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| auto
-d | boolean | Auto-detect which versions of which packages need to be promoted | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetdevhubusername
-v | option | username or alias for the dev hub org; overrides default dev hub org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:----------------------|:-------:|:-----------------------------------------------------------------|:-------:|:--------:|:-------:| +| auto
-d | boolean | Auto-detect which versions of which packages need to be promoted | | | | +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-dev-hub
-v | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:package:version:promote +sf hardis:package:version:promote ``` ```shell -sfdx hardis:package:version:promote --auto +sf hardis:package:version:promote --auto ``` diff --git a/docs/hardis/packagexml/append.md b/docs/hardis/packagexml/append.md new file mode 100644 index 000000000..29df7ff67 --- /dev/null +++ b/docs/hardis/packagexml/append.md @@ -0,0 +1,25 @@ + +# hardis:packagexml:append + +## Description + +Append one or multiple package.xml files into a single one + +## Parameters + +| Name | Type | Description | Default | Required | Options | +|:-------------------|:-------:|:---------------------------------------------|:-------:|:--------:|:-------:| +| debug | boolean | debug | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| outputfile
-o | option | package.xml output file | | | | +| packagexmls
-p | option | package.xml files path (separated by commas) | | | | +| websocket | option | websocket | | | | + +## Examples + +```shell +sf hardis packagexml append -p package1.xml,package2.xml -o package3.xml +``` + + diff --git a/docs/hardis/packagexml/remove.md b/docs/hardis/packagexml/remove.md new file mode 100644 index 000000000..cbe973f28 --- /dev/null +++ b/docs/hardis/packagexml/remove.md @@ -0,0 +1,27 @@ + +# hardis:packagexml:remove + +## Description + +Removes the content of a package.xml file matching another package.xml file + +## Parameters + +| Name | Type | Description | Default | Required | Options | +|:------------------------|:-------:|:----------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug | boolean | debug | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| outputfile
-o | option | package.xml output file | | | | +| packagexml
-p | option | package.xml file to reduce | | | | +| removedonly
-z | boolean | Use this flag to generate a package.xml with only removed items | | | | +| removepackagexml
-r | option | package.xml file to use to filter input package.xml | | | | +| websocket | option | websocket | | | | + +## Examples + +```shell +sf hardis packagexml:remove -p package.xml -r destructiveChanges.xml -o my-reduced-package.xml +``` + + diff --git a/docs/hardis/project/audit/apiversion.md b/docs/hardis/project/audit/apiversion.md index 34634121d..ce4534776 100644 --- a/docs/hardis/project/audit/apiversion.md +++ b/docs/hardis/project/audit/apiversion.md @@ -1,4 +1,4 @@ - + # hardis:project:audit:apiversion ## Description @@ -7,20 +7,20 @@ Audit API version ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| failiferror
-f | boolean | Fails (exit code 1) if an error is found | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| minimumapiversion
-m | option | Minimum allowed API version | 20 | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| failiferror
-f | boolean | Fails (exit code 1) if an error is found | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| minimumapiversion
-m | option | Minimum allowed API version | 20 | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:audit:apiversion +sf hardis:project:audit:apiversion ``` diff --git a/docs/hardis/project/audit/callincallout.md b/docs/hardis/project/audit/callincallout.md index f05249787..beb50d40b 100644 --- a/docs/hardis/project/audit/callincallout.md +++ b/docs/hardis/project/audit/callincallout.md @@ -1,4 +1,4 @@ - + # hardis:project:audit:callincallout ## Description @@ -7,18 +7,18 @@ Generate list of callIn and callouts from sfdx project ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:audit:callouts +sf hardis:project:audit:callouts ``` diff --git a/docs/hardis/project/audit/duplicatefiles.md b/docs/hardis/project/audit/duplicatefiles.md index d1f50550a..f34bac150 100644 --- a/docs/hardis/project/audit/duplicatefiles.md +++ b/docs/hardis/project/audit/duplicatefiles.md @@ -1,4 +1,4 @@ - + # hardis:project:audit:duplicatefiles ## Description @@ -7,19 +7,19 @@ Find duplicate files in sfdx folder (often from past @salesforce/cli bugs) ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------|:-------:|:--------------------------------------------------------------|:-----------------------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| path
-p | option | Root path to check | C:\git\pro\sfdx-hardis2 | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------|:-------:|:--------------------------------------------------------------|:-----------------------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| path
-p | option | Root path to check | C:\git\pro\sfdx-hardis2 | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:audit:duplicatefiles +sf hardis:project:audit:duplicatefiles ``` diff --git a/docs/hardis/project/audit/remotesites.md b/docs/hardis/project/audit/remotesites.md index 4b128e215..f302af99a 100644 --- a/docs/hardis/project/audit/remotesites.md +++ b/docs/hardis/project/audit/remotesites.md @@ -1,4 +1,4 @@ - + # hardis:project:audit:remotesites ## Description @@ -7,18 +7,18 @@ Generate list of remote sites ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:audit:remotesites +sf hardis:project:audit:remotesites ``` diff --git a/docs/hardis/project/clean/emptyitems.md b/docs/hardis/project/clean/emptyitems.md index 2a02eb365..9bb673db9 100644 --- a/docs/hardis/project/clean/emptyitems.md +++ b/docs/hardis/project/clean/emptyitems.md @@ -1,4 +1,4 @@ - + # hardis:project:clean:emptyitems ## Description @@ -7,19 +7,19 @@ Remove unwanted empty items within sfdx project sources ## Parameters -| Name | Type | Description | Default | Required | Options | -|:--------------|:-------:|:--------------------------------------------------------------|:---------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| folder
-f | option | Root folder | force-app | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:--------------|:-------:|:--------------------------------------------------------------|:---------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| folder
-f | option | Root folder | force-app | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:clean:emptyitems +sf hardis:project:clean:emptyitems ``` diff --git a/docs/hardis/project/clean/filter-xml-content.md b/docs/hardis/project/clean/filter-xml-content.md new file mode 100644 index 000000000..2783db018 --- /dev/null +++ b/docs/hardis/project/clean/filter-xml-content.md @@ -0,0 +1,36 @@ + +# hardis:project:clean:filter-xml-content + +## Description + +Filter content of metadatas (XML) in order to be able to deploy only part of them on an org (See [Example configuration](https://github.com/nvuillam/sfdx-essentials/blob/master/examples/filter-xml-content-config.json)) + +When you perform deployments from one org to another, the features activated in the target org may not fit the content of the sfdx/metadata files extracted from the source org. + +You may need to filter some elements in the XML files, for example in the Profiles + +This script requires a filter-config.json file + +## Parameters + +| Name | Type | Description | Default | Required | Options | +|:--------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| configfile
-c | option | Config JSON file path | | | | +| debug | boolean | debug | | | | +| flags-dir | option | undefined | | | | +| inputfolder
-i | option | Input folder (default: "." ) | | | | +| json | boolean | Format output as json. | | | | +| outputfolder
-o | option | Output folder (default: parentFolder + _xml_content_filtered) | | | | +| websocket | option | websocket | | | | + +## Examples + +```shell +sf hardis:project:clean:filter-xml-content -i "./mdapi_output" +``` + +```shell +sf hardis:project:clean:filter-xml-content -i "retrieveUnpackaged" +``` + + diff --git a/docs/hardis/project/clean/flowpositions.md b/docs/hardis/project/clean/flowpositions.md index 1747bf060..ae3f84f60 100644 --- a/docs/hardis/project/clean/flowpositions.md +++ b/docs/hardis/project/clean/flowpositions.md @@ -1,4 +1,4 @@ - + # hardis:project:clean:flowpositions ## Description @@ -34,19 +34,19 @@ autoCleanTypes: ## Parameters -| Name | Type | Description | Default | Required | Options | -|:--------------|:-------:|:--------------------------------------------------------------|:---------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| folder
-f | option | Root folder | force-app | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:--------------|:-------:|:--------------------------------------------------------------|:---------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| folder
-f | option | Root folder | force-app | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:clean:flowpositions +sf hardis:project:clean:flowpositions ``` diff --git a/docs/hardis/project/clean/hiddenitems.md b/docs/hardis/project/clean/hiddenitems.md index 594463b97..2ed4726a8 100644 --- a/docs/hardis/project/clean/hiddenitems.md +++ b/docs/hardis/project/clean/hiddenitems.md @@ -1,4 +1,4 @@ - + # hardis:project:clean:hiddenitems ## Description @@ -7,19 +7,19 @@ Remove unwanted hidden items within sfdx project sources ## Parameters -| Name | Type | Description | Default | Required | Options | -|:--------------|:-------:|:--------------------------------------------------------------|:---------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| folder
-f | option | Root folder | force-app | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:--------------|:-------:|:--------------------------------------------------------------|:---------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| folder
-f | option | Root folder | force-app | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:clean:hiddenitems +sf hardis:project:clean:hiddenitems ``` diff --git a/docs/hardis/project/clean/listviews.md b/docs/hardis/project/clean/listviews.md index 6f3f68452..525ac0a4f 100644 --- a/docs/hardis/project/clean/listviews.md +++ b/docs/hardis/project/clean/listviews.md @@ -1,4 +1,4 @@ - + # hardis:project:clean:listviews ## Description @@ -7,19 +7,19 @@ Replace Mine by Everything in ListView, and log the replacements in sfdx-hardis. ## Parameters -| Name | Type | Description | Default | Required | Options | -|:--------------|:-------:|:--------------------------------------------------------------|:---------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| folder
-f | option | Root folder | force-app | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:--------------|:-------:|:--------------------------------------------------------------|:---------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| folder
-f | option | Root folder | force-app | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:clean:listviews +sf hardis:project:clean:listviews ``` diff --git a/docs/hardis/project/clean/manageditems.md b/docs/hardis/project/clean/manageditems.md index 68be00cff..8d23b0c03 100644 --- a/docs/hardis/project/clean/manageditems.md +++ b/docs/hardis/project/clean/manageditems.md @@ -1,4 +1,4 @@ - + # hardis:project:clean:manageditems ## Description @@ -7,20 +7,20 @@ Remove unwanted managed items within sfdx project sources ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-----------------|:-------:|:--------------------------------------------------------------|:---------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| folder
-f | option | Root folder | force-app | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| namespace
-n | option | Namespace to remove | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-----------------|:-------:|:--------------------------------------------------------------|:---------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| folder
-f | option | Root folder | force-app | | | +| json | boolean | Format output as json. | | | | +| namespace
-n | option | Namespace to remove | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:clean:manageditems --namespace crta +sf hardis:project:clean:manageditems --namespace crta ``` diff --git a/docs/hardis/project/clean/minimizeprofiles.md b/docs/hardis/project/clean/minimizeprofiles.md index c0f6c4be6..439d05237 100644 --- a/docs/hardis/project/clean/minimizeprofiles.md +++ b/docs/hardis/project/clean/minimizeprofiles.md @@ -1,4 +1,4 @@ - + # hardis:project:clean:minimizeprofiles ## Description @@ -36,19 +36,19 @@ skipMinimizeProfiles ## Parameters -| Name | Type | Description | Default | Required | Options | -|:--------------|:-------:|:--------------------------------------------------------------|:---------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| folder
-f | option | Root folder | force-app | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:--------------|:-------:|:--------------------------------------------------------------|:---------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| folder
-f | option | Root folder | force-app | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:clean:minimizeprofiles +sf hardis:project:clean:minimizeprofiles ``` diff --git a/docs/hardis/project/clean/orgmissingitems.md b/docs/hardis/project/clean/orgmissingitems.md index 1cf2cc5c5..4e148f6cb 100644 --- a/docs/hardis/project/clean/orgmissingitems.md +++ b/docs/hardis/project/clean/orgmissingitems.md @@ -1,4 +1,4 @@ - + # hardis:project:clean:orgmissingitems ## Description @@ -7,16 +7,16 @@ Clean SFDX sources from items present neither in target org nor local package.xm ## Parameters -| Name | Type | Description | Default | Required | Options | -|:--------------|:-------:|:------------------------------------------|:---------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| folder
-f | option | Root folder | force-app | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | +| Name | Type | Description | Default | Required | Options | +|:--------------|:-------:|:--------------------------------|:---------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| folder
-f | option | Root folder | force-app | | | +| json | boolean | Format output as json. | | | | |packagexmlfull
-p|option|Path to packagexml used for cleaning. Must contain also standard CustomObject and CustomField elements. If not provided, it will be generated from a remote org|||| -|packagexmltargetorg
-t|option|Target org username or alias to build package.xml (sfdx must be authenticated). +|packagexmltargetorg
-t|option|Target org username or alias to build package.xml (SF CLI must be authenticated). If not provided, will be prompted to the user.|||| |skipauth|boolean|Skip authentication check when a default username is required|||| |websocket|option|Websocket host:port for VsCode SFDX Hardis UI integration|||| @@ -24,7 +24,7 @@ If not provided, will be prompted to the user.|||| ## Examples ```shell -sfdx hardis:project:clean:orgmissingitems +sf hardis:project:clean:orgmissingitems ``` diff --git a/docs/hardis/project/clean/references.md b/docs/hardis/project/clean/references.md index 90036650e..0b750dfe4 100644 --- a/docs/hardis/project/clean/references.md +++ b/docs/hardis/project/clean/references.md @@ -1,4 +1,4 @@ - + # hardis:project:clean:references ## Description @@ -7,32 +7,32 @@ Remove unwanted references within sfdx project sources ## Parameters -| Name | Type | Description | Default | Required | Options | -|:--------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------------------------------------------------------------------------------------------------------------------------------:| -| config
-c | option | Path to a JSON config file or a destructiveChanges.xml file | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| type
-t | option | Cleaning type | | | all
caseentitlement
dashboards
datadotcom
destructivechanges
localfields
productrequest
entitlement | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:--------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:| +| config
-c | option | Path to a JSON config file or a destructiveChanges.xml file | | | | +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| type
-t | option | Cleaning type | | | all
caseentitlement
dashboards
datadotcom
destructivechanges
localfields
productrequest
entitlement
flowPositions
sensitiveMetadatas
minimizeProfiles | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:clean:references +sf hardis:project:clean:references ``` ```shell -sfdx hardis:project:clean:references --type all +sf hardis:project:clean:references --type all ``` ```shell -sfdx hardis:project:clean:references --config ./cleaning/myconfig.json +sf hardis:project:clean:references --config ./cleaning/myconfig.json ``` ```shell -sfdx hardis:project:clean:references --config ./somefolder/myDestructivePackage.xml +sf hardis:project:clean:references --config ./somefolder/myDestructivePackage.xml ``` diff --git a/docs/hardis/project/clean/retrievefolders.md b/docs/hardis/project/clean/retrievefolders.md index c69e96fd6..cab50fab5 100644 --- a/docs/hardis/project/clean/retrievefolders.md +++ b/docs/hardis/project/clean/retrievefolders.md @@ -1,4 +1,4 @@ - + # hardis:project:clean:retrievefolders ## Description @@ -7,20 +7,19 @@ Retrieve dashboards, documents and report folders in DX sources. Use -u ORGALIAS ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:clean:retrievefolders +sf hardis:project:clean:retrievefolders ``` diff --git a/docs/hardis/project/clean/sensitive-metadatas.md b/docs/hardis/project/clean/sensitive-metadatas.md new file mode 100644 index 000000000..18dc26fdc --- /dev/null +++ b/docs/hardis/project/clean/sensitive-metadatas.md @@ -0,0 +1,38 @@ + +# hardis:project:clean:sensitive-metadatas + +## Description + +Sensitive data like credentials and certificates are not supposed to be stored in Git, to avoid security breaches. + +This command detects the related metadata and replaces their sensitive content by "HIDDEN_BY_SFDX_HARDIS" + +Can be automated at each **hardis:work:save** if **sensitiveMetadatas** is added in .sfdx-hardis.yml **autoCleanTypes** property + +Example in config/.sfdx-hardis.yml: + +```yaml +autoCleanTypes: + - destructivechanges + - sensitiveMetadatas +``` + + +## Parameters + +| Name | Type | Description | Default | Required | Options | +|:--------------|:-------:|:--------------------------------------------------------------|:---------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| folder
-f | option | Root folder | force-app | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | + +## Examples + +```shell +sf hardis:project:clean:sensitive-metadatas +``` + + diff --git a/docs/hardis/project/clean/standarditems.md b/docs/hardis/project/clean/standarditems.md index cb8c16473..05a79255d 100644 --- a/docs/hardis/project/clean/standarditems.md +++ b/docs/hardis/project/clean/standarditems.md @@ -1,4 +1,4 @@ - + # hardis:project:clean:standarditems ## Description @@ -7,18 +7,18 @@ Remove unwanted standard items within sfdx project sources ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:clean:standarditems +sf hardis:project:clean:standarditems ``` diff --git a/docs/hardis/project/clean/systemdebug.md b/docs/hardis/project/clean/systemdebug.md index c8fbd3a2c..e4d6511de 100644 --- a/docs/hardis/project/clean/systemdebug.md +++ b/docs/hardis/project/clean/systemdebug.md @@ -1,4 +1,4 @@ - + # hardis:project:clean:systemdebug ## Description @@ -7,19 +7,19 @@ Clean System.debug() lines in APEX Code (classes and triggers) ## Parameters -| Name | Type | Description | Default | Required | Options | -|:--------------|:-------:|:--------------------------------------------------------------|:---------:|:--------:|:-----------------------------------------------------:| -| delete
-d | boolean | Delete lines with System.debug | | | | -| folder
-f | option | Root folder | force-app | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:--------------|:-------:|:--------------------------------------------------------------|:---------:|:--------:|:-------:| +| delete
-d | boolean | Delete lines with System.debug | | | | +| flags-dir | option | undefined | | | | +| folder
-f | option | Root folder | force-app | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:clean:systemdebug +sf hardis:project:clean:systemdebug ``` diff --git a/docs/hardis/project/clean/xml.md b/docs/hardis/project/clean/xml.md index cc79dc73a..b844e7677 100644 --- a/docs/hardis/project/clean/xml.md +++ b/docs/hardis/project/clean/xml.md @@ -1,4 +1,4 @@ - + # hardis:project:clean:xml ## Description @@ -19,26 +19,26 @@ Note: If globpattern and xpath are not sent, elements defined in property **clea ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------------|:-------:|:--------------------------------------------------------------------------------------------------------------------|:-----------------------------------------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| folder
-f | option | Root folder | force-app | | | -| globpattern
-p | option | Glob pattern to find files to clean. Ex: /**/*.flexipage-meta.xml | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| namespace
-n | option | XML Namespace to use | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | -| xpath
-x | option | XPath to use to detect the elements to remove. Ex: //ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')] | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------------|:-------:|:--------------------------------------------------------------------------------------------------------------------|:-----------------------------------------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| folder
-f | option | Root folder | force-app | | | +| globpattern
-p | option | Glob pattern to find files to clean. Ex: /**/*.flexipage-meta.xml | | | | +| json | boolean | Format output as json. | | | | +| namespace
-n | option | XML Namespace to use | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| xpath
-x | option | XPath to use to detect the elements to remove. Ex: //ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')] | | | | ## Examples ```shell -sfdx hardis:project:clean:xml +sf hardis:project:clean:xml ``` ```shell -sfdx hardis:project:clean:xml --globpattern "/**/*.flexipage-meta.xml" --xpath "//ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')]" +sf hardis:project:clean:xml --globpattern "/**/*.flexipage-meta.xml" --xpath "//ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')]" ``` diff --git a/docs/hardis/project/configure/auth.md b/docs/hardis/project/configure/auth.md index 149a8b220..530a36328 100644 --- a/docs/hardis/project/configure/auth.md +++ b/docs/hardis/project/configure/auth.md @@ -1,4 +1,4 @@ - + # hardis:project:configure:auth ## Description @@ -7,22 +7,21 @@ Configure authentication from git branch to target org ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------------|:-------:|:---------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| devhub
-b | boolean | Configure project DevHub | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetdevhubusername
-v | option | username or alias for the dev hub org; overrides default dev hub org | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:----------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| devhub
-b | boolean | Configure project DevHub | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-dev-hub
-v | option | undefined | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:configure:auth +sf hardis:project:configure:auth ``` diff --git a/docs/hardis/project/convert/profilestopermsets.md b/docs/hardis/project/convert/profilestopermsets.md index 90b6e9d6c..bdcf05f70 100644 --- a/docs/hardis/project/convert/profilestopermsets.md +++ b/docs/hardis/project/convert/profilestopermsets.md @@ -1,4 +1,4 @@ - + # hardis:project:convert:profilestopermsets ## Description @@ -7,19 +7,19 @@ Creates permission sets from existing profiles, with id PS_PROFILENAME ## Parameters -| Name | Type | Description | Default | Required | Options | -|:--------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| except
-e | option | List of filters | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:--------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| except
-e | option | List of filters | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:convert:profilestopermsets +sf hardis:project:convert:profilestopermsets ``` diff --git a/docs/hardis/project/create.md b/docs/hardis/project/create.md index 5c5d048e2..f76da5f0c 100644 --- a/docs/hardis/project/create.md +++ b/docs/hardis/project/create.md @@ -1,4 +1,4 @@ - + # hardis:project:create ## Description @@ -7,18 +7,18 @@ Create a new SFDX Project ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:create +sf hardis:project:create ``` diff --git a/docs/hardis/project/deploy/quick.md b/docs/hardis/project/deploy/quick.md new file mode 100644 index 000000000..f0f2f4e40 --- /dev/null +++ b/docs/hardis/project/deploy/quick.md @@ -0,0 +1,68 @@ + +# hardis:project:deploy:quick + +## Description + +sfdx-hardis wrapper for **sf project deploy quick** that displays tips to solve deployment errors. + +[![Assisted solving of Salesforce deployments errors](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-deployment-errors.jpg)](https://nicolas.vuillamy.fr/assisted-solving-of-salesforce-deployments-errors-47f3666a9ed0) + +[See documentation of Salesforce command](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_project_commands_unified.htm#cli_reference_project_deploy_quick_unified) + +### Deployment pre or post commands + +You can define command lines to run before or after a deployment, with parameters: + +- **id**: Unique Id for the command +- **label**: Human readable label for the command +- **skipIfError**: If defined to "true", the post-command won't be run if there is a deployment failure +- **context**: Defines the context where the command will be run. Can be **all** (default), **check-deployment-only** or **process-deployment-only** +- **runOnlyOnceByOrg**: If set to true, the command will be run only one time per org. A record of SfdxHardisTrace__c is stored to make that possible (it needs to be existing in target org) + +If the commands are not the same depending on the target org, you can define them into **config/branches/.sfdx-hardis-BRANCHNAME.yml** instead of root **config/.sfdx-hardis.yml** + +Example: + +```yaml +commandsPreDeploy: + - id: knowledgeUnassign + label: Remove KnowledgeUser right to the user who has it + command: sf data update record --sobject User --where "UserPermissionsKnowledgeUser='true'" --values "UserPermissionsKnowledgeUser='false'" --json + - id: knowledgeAssign + label: Assign Knowledge user to the deployment user + command: sf data update record --sobject User --where "Username='deploy.github@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + +commandsPostDeploy: + - id: knowledgeUnassign + label: Remove KnowledgeUser right to the user who has it + command: sf data update record --sobject User --where "UserPermissionsKnowledgeUser='true'" --values "UserPermissionsKnowledgeUser='false'" --json + - id: knowledgeAssign + label: Assign Knowledge user to desired username + command: sf data update record --sobject User --where "Username='admin-yser@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + - id: someActionToRunJustOneTime + label: And to run only if deployment is success + command: sf sfdmu:run ... + skipIfError: true + context: process-deployment-only + runOnlyOnceByOrg: true +``` + + +## Parameters + +| Name | Type | Description | Default | Required | Options | +|:-------------------------|:-------:|:-----------------------|:-------:|:--------:|:-------:| +| --job-id
-i | option | job-id | | | | +| --use-most-recent
-r | boolean | use-most-recent | | | | +| api-version
-a | option | api-version | | | | +| async | boolean | async | | | | +| debug | boolean | debug | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| target-org
-o | option | undefined | | | | +| tests | option | tests | | | | +| wait
-w | option | wait | 33 | | | + +## Examples + + diff --git a/docs/hardis/project/deploy/simulate.md b/docs/hardis/project/deploy/simulate.md new file mode 100644 index 000000000..c341df002 --- /dev/null +++ b/docs/hardis/project/deploy/simulate.md @@ -0,0 +1,30 @@ + +# hardis:project:deploy:simulate + +## Description + +Simulate the deployment of a metadata in an org prompted to the user + +For example, helps to solve the issue in a Permission Set without having to run a CI/CD job. + +Used by VsCode Extension + +## Parameters + +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| source-dir
-d | option | Source file or directory to simulate the deployment | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | + +## Examples + +```shell +sf hardis:project:deploy:simulate --source-dir force-app/defaut/main/permissionset/PS_Admin.permissionset-meta.xml +``` + + diff --git a/docs/hardis/project/deploy/smart.md b/docs/hardis/project/deploy/smart.md new file mode 100644 index 000000000..cf1fb3a03 --- /dev/null +++ b/docs/hardis/project/deploy/smart.md @@ -0,0 +1,250 @@ + +# hardis:project:deploy:smart + +## Description + +Smart deploy of SFDX sources to target org, with many useful options. + +In case of errors, [tips to fix them](https://sfdx-hardis.cloudity.com/deployTips/) will be included within the error messages. + +### Quick Deploy + +In case Pull Request comments are configured on the project, Quick Deploy will try to be used (equivalent to button Quick Deploy) + +If you do not want to use QuickDeploy, define variable `SFDX_HARDIS_QUICK_DEPLOY=false` + +- [GitHub Pull Requests comments config](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-integration-github/) +- [Gitlab Merge requests notes config](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-integration-gitlab/) +- [Azure Pull Requests comments config](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-integration-azure/) + +### Delta deployments + +To activate delta deployments, define property `useDeltaDeployment: true` in `config/.sfdx-hardis.yml`. + +This will activate delta deployments only between minor and major branches (major to major remains full deployment mode) + +If you want to force the delta deployment into major orgs (ex: preprod to prod), this is not recommended but you can use env variable ALWAYS_ENABLE_DELTA_DEPLOYMENT=true + +### Smart Deployments Tests + +Not all metadata updates can break test classes, use Smart Deployment Tests to skip running test classes if ALL the following conditions are met: + +- Delta deployment is activated and applicable to the source and target branches +- Delta deployed metadatas are all matching the list of **NOT_IMPACTING_METADATA_TYPES** (see below) +- Target org is not a production org + +Activate Smart Deployment tests with: + +- env variable `USE_SMART_DEPLOYMENT_TESTS=true` +- .sfdx-hardis.yml config property `useSmartDeploymentTests: true` + +Defaut list for **NOT_IMPACTING_METADATA_TYPES** (can be overridden with comma-separated list on env var NOT_IMPACTING_METADATA_TYPES) + +- Audience +- AuraDefinitionBundle +- Bot +- BotVersion +- ContentAsset +- CustomObjectTranslation +- CustomSite +- CustomTab +- Dashboard +- ExperienceBundle +- Flexipage +- GlobalValueSetTranslation +- Layout +- LightningComponentBundle +- NavigationMenu +- ReportType +- Report +- SiteDotCom +- StandardValueSetTranslation +- StaticResource +- Translations + +Note: if you want to disable Smart test classes for a PR, add **nosmart** in the text of the latest commit. + +### Dynamic deployment items / Overwrite management + +If necessary,you can define the following files (that supports wildcards *): + +- `manifest/package-no-overwrite.xml`: Every element defined in this file will be deployed only if it is not existing yet in the target org (can be useful with ListView for example, if the client wants to update them directly in production org) +- `manifest/packageXmlOnChange.xml`: Every element defined in this file will not be deployed if it already has a similar definition in target org (can be useful for SharingRules for example) + +See [Overwrite management documentation](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-config-overwrite/) + +### Deployment plan + +If you need to deploy in multiple steps, you can define a property `deploymentPlan` in `.sfdx-hardis.yml`. + +- If a file `manifest/package.xml` is found, it will be placed with order 0 in the deployment plan + +- If a file `manifest/destructiveChanges.xml` is found, it will be executed as --postdestructivechanges + +- If env var `SFDX_HARDIS_DEPLOY_IGNORE_SPLIT_PACKAGES` is defined as `false` , split of package.xml will be applied + +Example: + +```yaml +deploymentPlan: + packages: + - label: Deploy Flow-Workflow + packageXmlFile: manifest/splits/packageXmlFlowWorkflow.xml + order: 6 + - label: Deploy SharingRules - Case + packageXmlFile: manifest/splits/packageXmlSharingRulesCase.xml + order: 30 + waitAfter: 30 +``` + +### Packages installation + +You can define a list of package to install during deployments using property `installedPackages` + +- If `INSTALL_PACKAGES_DURING_CHECK_DEPLOY` is defined as `true` (or `installPackagesDuringCheckDeploy: true` in `.sfdx-hardis.yml`), packages will be installed even if the command is called with `--check` mode +- You can automatically update this property by listing all packages installed on an org using command `sf hardis:org:retrieve:packageconfig` + +Example: + +```yaml +installedPackages: + - Id: 0A35r0000009EtECAU + SubscriberPackageId: 033i0000000LVMYAA4 + SubscriberPackageName: Marketing Cloud + SubscriberPackageNamespace: et4ae5 + SubscriberPackageVersionId: 04t6S000000l11iQAA + SubscriberPackageVersionName: Marketing Cloud + SubscriberPackageVersionNumber: 236.0.0.2 + installOnScratchOrgs: true // true or false depending you want to install this package when creating a new scratch org + installDuringDeployments: true // set as true to install package during a deployment using sf hardis:project:deploy:smart + installationkey: xxxxxxxxxxxxxxxxxxxx // if the package has a password, write it in this property + - Id: 0A35r0000009F9CCAU + SubscriberPackageId: 033b0000000Pf2AAAS + SubscriberPackageName: Declarative Lookup Rollup Summaries Tool + SubscriberPackageNamespace: dlrs + SubscriberPackageVersionId: 04t5p000001BmLvAAK + SubscriberPackageVersionName: Release + SubscriberPackageVersionNumber: 2.15.0.9 + installOnScratchOrgs: true + installDuringDeployments: true +``` + +### Deployment pre or post commands + +You can define command lines to run before or after a deployment, with parameters: + +- **id**: Unique Id for the command +- **label**: Human readable label for the command +- **skipIfError**: If defined to "true", the post-command won't be run if there is a deployment failure +- **context**: Defines the context where the command will be run. Can be **all** (default), **check-deployment-only** or **process-deployment-only** +- **runOnlyOnceByOrg**: If set to true, the command will be run only one time per org. A record of SfdxHardisTrace__c is stored to make that possible (it needs to be existing in target org) + +If the commands are not the same depending on the target org, you can define them into **config/branches/.sfdx-hardis-BRANCHNAME.yml** instead of root **config/.sfdx-hardis.yml** + +Example: + +```yaml +commandsPreDeploy: + - id: knowledgeUnassign + label: Remove KnowledgeUser right to the user who has it + command: sf data update record --sobject User --where "UserPermissionsKnowledgeUser='true'" --values "UserPermissionsKnowledgeUser='false'" --json + - id: knowledgeAssign + label: Assign Knowledge user to the deployment user + command: sf data update record --sobject User --where "Username='deploy.github@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + +commandsPostDeploy: + - id: knowledgeUnassign + label: Remove KnowledgeUser right to the user who has it + command: sf data update record --sobject User --where "UserPermissionsKnowledgeUser='true'" --values "UserPermissionsKnowledgeUser='false'" --json + - id: knowledgeAssign + label: Assign Knowledge user to desired username + command: sf data update record --sobject User --where "Username='admin-yser@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + - id: someActionToRunJustOneTime + label: And to run only if deployment is success + command: sf sfdmu:run ... + skipIfError: true + context: process-deployment-only + runOnlyOnceByOrg: true +``` + +### Automated fixes post deployments + +#### List view with scope Mine + +If you defined a property **listViewsToSetToMine** in your .sfdx-hardis.yml, related ListViews will be set to Mine ( see command ) + +Example: + +```yaml +listViewsToSetToMine: + - "Operation__c:MyCurrentOperations" + - "Operation__c:MyFinalizedOperations" + - "Opportunity:Default_Opportunity_Pipeline" + - "Opportunity:MyCurrentSubscriptions" + - "Opportunity:MySubscriptions" + - "Account:MyActivePartners" +``` + +Troubleshooting: if you need to fix ListViews with mine from an alpine-linux based docker image, use this workaround in your dockerfile: + +```dockerfile +# Do not use puppeteer embedded chromium +RUN apk add --update --no-cache chromium +ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD="true" +ENV CHROMIUM_PATH="/usr/bin/chromium-browser" +ENV PUPPETEER_EXECUTABLE_PATH="$\{CHROMIUM_PATH}" // remove \ before { +``` + +If you need to increase the deployment waiting time (sf project deploy start --wait arg), you can define env variable SFDX_DEPLOY_WAIT_MINUTES + +If you need notifications to be sent using the current Pull Request and not the one just merged ([see use case](https://github.com/hardisgroupcom/sfdx-hardis/issues/637#issuecomment-2230798904)), define env variable SFDX_HARDIS_DEPLOY_BEFORE_MERGE=true + + +## Parameters + +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:------------------------------------------------------------------------|:-------:|:--------:|:-------:| +| check
-c | boolean | Only checks the deployment, there is no impact on target org | | | | +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| delta | boolean | Applies sfdx-git-delta to package.xml before other deployment processes | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| packagexml
-p | option | Path to package.xml containing what you want to deploy in target org | | | | +|runtests
-r|option|If testlevel=RunSpecifiedTests, please provide a list of classes. +If testlevel=RunRepositoryTests, can contain a regular expression to keep only class names matching it. If not set, will run all test classes found in the repo.|||| +|skipauth|boolean|Skip authentication check when a default username is required|||| +|target-org
-o|option|undefined|||| +|testlevel
-l|option|Level of tests to validate deployment. RunRepositoryTests auto-detect and run all repository test classes|||NoTestRun
RunSpecifiedTests
RunRepositoryTests
RunRepositoryTestsExceptSeeAllData
RunLocalTests
RunAllTestsInOrg| +|websocket|option|Websocket host:port for VsCode SFDX Hardis UI integration|||| + +## Examples + +```shell +sf hardis:project:deploy:smart +``` + +```shell +sf hardis:project:deploy:smart --check +``` + +```shell +sf hardis:project:deploy:smart --check --testlevel RunRepositoryTests +``` + +```shell +sf hardis:project:deploy:smart --check --testlevel RunRepositoryTests --runtests '^(?!FLI|MyPrefix).*' +``` + +```shell +sf hardis:project:deploy:smart --check --testlevel RunRepositoryTestsExceptSeeAllData +``` + +```shell +sf hardis:project:deploy:smart +``` + +```shell +FORCE_TARGET_BRANCH=preprod NODE_OPTIONS=--inspect-brk sf hardis:project:deploy:smart --check --websocket localhost:2702 --skipauth --target-org nicolas.vuillamy@myclient.com.preprod +``` + + diff --git a/docs/hardis/project/deploy/sources/dx.md b/docs/hardis/project/deploy/sources/dx.md index e6e36596e..fd5abecd6 100644 --- a/docs/hardis/project/deploy/sources/dx.md +++ b/docs/hardis/project/deploy/sources/dx.md @@ -1,9 +1,9 @@ - + # hardis:project:deploy:sources:dx ## Description -Deploy SFDX source to org, following deploymentPlan in .sfdx-hardis.yml +Smart deploy of SFDX sources to target org, with many useful options. In case of errors, [tips to fix them](https://sfdx-hardis.cloudity.com/deployTips/) will be included within the error messages. @@ -25,6 +25,45 @@ This will activate delta deployments only between minor and major branches (majo If you want to force the delta deployment into major orgs (ex: preprod to prod), this is not recommended but you can use env variable ALWAYS_ENABLE_DELTA_DEPLOYMENT=true +### Smart Deployments Tests + +Not all metadata updates can break test classes, use Smart Deployment Tests to skip running test classes if ALL the following conditions are met: + +- Delta deployment is activated and applicable to the source and target branches +- Delta deployed metadatas are all matching the list of **NOT_IMPACTING_METADATA_TYPES** (see below) +- Target org is not a production org + +Activate Smart Deployment tests with: + +- env variable `USE_SMART_DEPLOYMENT_TESTS=true` +- .sfdx-hardis.yml config property `useSmartDeploymentTests: true` + +Defaut list for **NOT_IMPACTING_METADATA_TYPES** (can be overridden with comma-separated list on env var NOT_IMPACTING_METADATA_TYPES) + +- Audience +- AuraDefinitionBundle +- Bot +- BotVersion +- ContentAsset +- CustomObjectTranslation +- CustomSite +- CustomTab +- Dashboard +- ExperienceBundle +- Flexipage +- GlobalValueSetTranslation +- Layout +- LightningComponentBundle +- NavigationMenu +- ReportType +- Report +- SiteDotCom +- StandardValueSetTranslation +- StaticResource +- Translations + +Note: if you want to disable Smart test classes for a PR, add **nosmart** in the text of the latest commit. + ### Dynamic deployment items / Overwrite management If necessary,you can define the following files (that supports wildcards *): @@ -63,7 +102,7 @@ deploymentPlan: You can define a list of package to install during deployments using property `installedPackages` - If `INSTALL_PACKAGES_DURING_CHECK_DEPLOY` is defined as `true` (or `installPackagesDuringCheckDeploy: true` in `.sfdx-hardis.yml`), packages will be installed even if the command is called with `--check` mode -- You can automatically update this property by listing all packages installed on an org using command `sfdx hardis:org:retrieve:packageconfig` +- You can automatically update this property by listing all packages installed on an org using command `sf hardis:org:retrieve:packageconfig` Example: @@ -77,7 +116,7 @@ installedPackages: SubscriberPackageVersionName: Marketing Cloud SubscriberPackageVersionNumber: 236.0.0.2 installOnScratchOrgs: true // true or false depending you want to install this package when creating a new scratch org - installDuringDeployments: true // set as true to install package during a deployment using sfdx hardis:project:deploy:sources:dx + installDuringDeployments: true // set as true to install package during a deployment using sf hardis:project:deploy:smart installationkey: xxxxxxxxxxxxxxxxxxxx // if the package has a password, write it in this property - Id: 0A35r0000009F9CCAU SubscriberPackageId: 033b0000000Pf2AAAS @@ -92,7 +131,13 @@ installedPackages: ### Deployment pre or post commands -You can define command lines to run before or after a deployment +You can define command lines to run before or after a deployment, with parameters: + +- **id**: Unique Id for the command +- **label**: Human readable label for the command +- **skipIfError**: If defined to "true", the post-command won't be run if there is a deployment failure +- **context**: Defines the context where the command will be run. Can be **all** (default), **check-deployment-only** or **process-deployment-only** +- **runOnlyOnceByOrg**: If set to true, the command will be run only one time per org. A record of SfdxHardisTrace__c is stored to make that possible (it needs to be existing in target org) If the commands are not the same depending on the target org, you can define them into **config/branches/.sfdx-hardis-BRANCHNAME.yml** instead of root **config/.sfdx-hardis.yml** @@ -106,6 +151,7 @@ commandsPreDeploy: - id: knowledgeAssign label: Assign Knowledge user to the deployment user command: sf data update record --sobject User --where "Username='deploy.github@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + commandsPostDeploy: - id: knowledgeUnassign label: Remove KnowledgeUser right to the user who has it @@ -113,6 +159,12 @@ commandsPostDeploy: - id: knowledgeAssign label: Assign Knowledge user to desired username command: sf data update record --sobject User --where "Username='admin-yser@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + - id: someActionToRunJustOneTime + label: And to run only if deployment is success + command: sf sfdmu:run ... + skipIfError: true + context: process-deployment-only + runOnlyOnceByOrg: true ``` ### Automated fixes post deployments @@ -143,34 +195,56 @@ ENV CHROMIUM_PATH="/usr/bin/chromium-browser" ENV PUPPETEER_EXECUTABLE_PATH="$\{CHROMIUM_PATH}" // remove \ before { ``` -If you need to increase the deployment waiting time (force:source:deploy --wait arg), you can define env var SFDX_DEPLOY_WAIT_MINUTES - +If you need to increase the deployment waiting time (sf project deploy start --wait arg), you can define env variable SFDX_DEPLOY_WAIT_MINUTES + +If you need notifications to be sent using the current Pull Request and not the one just merged ([see use case](https://github.com/hardisgroupcom/sfdx-hardis/issues/637#issuecomment-2230798904)), define env variable SFDX_HARDIS_DEPLOY_BEFORE_MERGE=true + ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:----------------------------------------------------------------------------------------------------------|:-------------:|:--------:|:---------------------------------------------------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| check
-c | boolean | Only checks the deployment, there is no impact on target org | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| delta | boolean | Applies sfdx-git-delta to package.xml before other deployment processes | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| packagexml
-p | option | Path to package.xml containing what you want to deploy in target org | | | | -| runtests
-r | option | Apex test classes to run if --testlevel is RunSpecifiedTests | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| testlevel
-l | option | Level of tests to validate deployment. RunRepositoryTests auto-detect and run all repository test classes | RunLocalTests | | NoTestRun
RunSpecifiedTests
RunRepositoryTests
RunLocalTests
RunAllTestsInOrg | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:------------------------------------------------------------------------|:-------:|:--------:|:-------:| +| check
-c | boolean | Only checks the deployment, there is no impact on target org | | | | +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| delta | boolean | Applies sfdx-git-delta to package.xml before other deployment processes | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| packagexml
-p | option | Path to package.xml containing what you want to deploy in target org | | | | +|runtests
-r|option|If testlevel=RunSpecifiedTests, please provide a list of classes. +If testlevel=RunRepositoryTests, can contain a regular expression to keep only class names matching it. If not set, will run all test classes found in the repo.|||| +|skipauth|boolean|Skip authentication check when a default username is required|||| +|target-org
-o|option|undefined|||| +|testlevel
-l|option|Level of tests to validate deployment. RunRepositoryTests auto-detect and run all repository test classes|||NoTestRun
RunSpecifiedTests
RunRepositoryTests
RunRepositoryTestsExceptSeeAllData
RunLocalTests
RunAllTestsInOrg| +|websocket|option|Websocket host:port for VsCode SFDX Hardis UI integration|||| ## Examples ```shell -sfdx hardis:project:deploy:sources:dx +sf hardis:project:deploy:smart +``` + +```shell +sf hardis:project:deploy:smart --check +``` + +```shell +sf hardis:project:deploy:smart --check --testlevel RunRepositoryTests +``` + +```shell +sf hardis:project:deploy:smart --check --testlevel RunRepositoryTests --runtests '^(?!FLI|MyPrefix).*' +``` + +```shell +sf hardis:project:deploy:smart --check --testlevel RunRepositoryTestsExceptSeeAllData +``` + +```shell +sf hardis:project:deploy:smart ``` ```shell -sfdx hardis:project:deploy:sources:dx --check +FORCE_TARGET_BRANCH=preprod NODE_OPTIONS=--inspect-brk sf hardis:project:deploy:smart --check --websocket localhost:2702 --skipauth --target-org nicolas.vuillamy@myclient.com.preprod ``` diff --git a/docs/hardis/project/deploy/sources/metadata.md b/docs/hardis/project/deploy/sources/metadata.md index e4c3d91cd..cedba2903 100644 --- a/docs/hardis/project/deploy/sources/metadata.md +++ b/docs/hardis/project/deploy/sources/metadata.md @@ -1,4 +1,4 @@ - + # hardis:project:deploy:sources:metadata ## Description @@ -7,26 +7,25 @@ Deploy metadatas to source org ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-----------------------------|:-------:|:--------------------------------------------------------------------|:-------------:|:--------:|:----------------------------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| check
-c | boolean | Only checks the deployment, there is no impact on target org | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| deploydir
-x | option | Deploy directory | . | | | -| destructivepackagexml
-k | option | Path to destructiveChanges.xml file to deploy | | | | -| filter
-f | boolean | Filter metadatas before deploying | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| packagexml
-p | option | Path to package.xml file to deploy | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| testlevel
-l | option | Level of tests to apply to validate deployment | RunLocalTests | | NoTestRun
RunSpecifiedTests
RunLocalTests
RunAllTestsInOrg | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-----------------------------|:-------:|:--------------------------------------------------------------|:-------------:|:--------:|:----------------------------------------------------------------------:| +| check
-c | boolean | Only checks the deployment, there is no impact on target org | | | | +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| deploydir
-x | option | Deploy directory | . | | | +| destructivepackagexml
-k | option | Path to destructiveChanges.xml file to deploy | | | | +| filter
-f | boolean | Filter metadatas before deploying | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| packagexml
-p | option | Path to package.xml file to deploy | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| testlevel
-l | option | Level of tests to apply to validate deployment | RunLocalTests | | NoTestRun
RunSpecifiedTests
RunLocalTests
RunAllTestsInOrg | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:deploy:sources:metadata +sf hardis:project:deploy:sources:metadata ``` diff --git a/docs/hardis/project/deploy/start.md b/docs/hardis/project/deploy/start.md new file mode 100644 index 000000000..847f6938d --- /dev/null +++ b/docs/hardis/project/deploy/start.md @@ -0,0 +1,82 @@ + +# hardis:project:deploy:start + +## Description + +sfdx-hardis wrapper for **sf project deploy start** that displays tips to solve deployment errors. + +[![Assisted solving of Salesforce deployments errors](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-deployment-errors.jpg)](https://nicolas.vuillamy.fr/assisted-solving-of-salesforce-deployments-errors-47f3666a9ed0) + +[See documentation of Salesforce command](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_project_commands_unified.htm#cli_reference_project_deploy_start_unified) + +### Deployment pre or post commands + +You can define command lines to run before or after a deployment, with parameters: + +- **id**: Unique Id for the command +- **label**: Human readable label for the command +- **skipIfError**: If defined to "true", the post-command won't be run if there is a deployment failure +- **context**: Defines the context where the command will be run. Can be **all** (default), **check-deployment-only** or **process-deployment-only** +- **runOnlyOnceByOrg**: If set to true, the command will be run only one time per org. A record of SfdxHardisTrace__c is stored to make that possible (it needs to be existing in target org) + +If the commands are not the same depending on the target org, you can define them into **config/branches/.sfdx-hardis-BRANCHNAME.yml** instead of root **config/.sfdx-hardis.yml** + +Example: + +```yaml +commandsPreDeploy: + - id: knowledgeUnassign + label: Remove KnowledgeUser right to the user who has it + command: sf data update record --sobject User --where "UserPermissionsKnowledgeUser='true'" --values "UserPermissionsKnowledgeUser='false'" --json + - id: knowledgeAssign + label: Assign Knowledge user to the deployment user + command: sf data update record --sobject User --where "Username='deploy.github@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + +commandsPostDeploy: + - id: knowledgeUnassign + label: Remove KnowledgeUser right to the user who has it + command: sf data update record --sobject User --where "UserPermissionsKnowledgeUser='true'" --values "UserPermissionsKnowledgeUser='false'" --json + - id: knowledgeAssign + label: Assign Knowledge user to desired username + command: sf data update record --sobject User --where "Username='admin-yser@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + - id: someActionToRunJustOneTime + label: And to run only if deployment is success + command: sf sfdmu:run ... + skipIfError: true + context: process-deployment-only + runOnlyOnceByOrg: true +``` + + +## Parameters + +| Name | Type | Description | Default | Required | Options | +|:-------------------------|:-------:|:-------------------------|:-------:|:--------:|:-------:| +| api-version
-a | option | api-version | | | | +| async | boolean | async | | | | +| coverage-formatters | option | coverage-formatters | | | | +| debug | boolean | debug | | | | +| dry-run | boolean | dry-run | | | | +| flags-dir | option | undefined | | | | +| ignore-conflicts
-c | boolean | ignore-conflicts | | | | +| ignore-errors
-r | boolean | ignore-errors | | | | +| ignore-warnings
-g | boolean | ignore-warnings | | | | +| json | boolean | Format output as json. | | | | +| junit | boolean | junit | | | | +| manifest
-x | option | manifest | | | | +| metadata
-m | option | metadata | | | | +| metadata-dir | option | metadata-dir | | | | +| post-destructive-changes | option | post-destructive-changes | | | | +| pre-destructive-changes | option | pre-destructive-changes | | | | +| purge-on-delete | boolean | purge-on-delete | | | | +| results-dir | option | results-dir | | | | +| single-package | boolean | single-package | | | | +| source-dir
-d | option | source-dir | | | | +| target-org
-o | option | undefined | | | | +| test-level | option | test-level | | | | +| tests | option | tests | | | | +| wait
-w | option | wait | 33 | | | + +## Examples + + diff --git a/docs/hardis/project/deploy/validate.md b/docs/hardis/project/deploy/validate.md new file mode 100644 index 000000000..473337386 --- /dev/null +++ b/docs/hardis/project/deploy/validate.md @@ -0,0 +1,82 @@ + +# hardis:project:deploy:validate + +## Description + +sfdx-hardis wrapper for **sf project deploy validate** that displays tips to solve deployment errors. + +[![Assisted solving of Salesforce deployments errors](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-deployment-errors.jpg)](https://nicolas.vuillamy.fr/assisted-solving-of-salesforce-deployments-errors-47f3666a9ed0) + +[See documentation of Salesforce command](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_project_commands_unified.htm#cli_reference_project_deploy_validate_unified) + +### Deployment pre or post commands + +You can define command lines to run before or after a deployment, with parameters: + +- **id**: Unique Id for the command +- **label**: Human readable label for the command +- **skipIfError**: If defined to "true", the post-command won't be run if there is a deployment failure +- **context**: Defines the context where the command will be run. Can be **all** (default), **check-deployment-only** or **process-deployment-only** +- **runOnlyOnceByOrg**: If set to true, the command will be run only one time per org. A record of SfdxHardisTrace__c is stored to make that possible (it needs to be existing in target org) + +If the commands are not the same depending on the target org, you can define them into **config/branches/.sfdx-hardis-BRANCHNAME.yml** instead of root **config/.sfdx-hardis.yml** + +Example: + +```yaml +commandsPreDeploy: + - id: knowledgeUnassign + label: Remove KnowledgeUser right to the user who has it + command: sf data update record --sobject User --where "UserPermissionsKnowledgeUser='true'" --values "UserPermissionsKnowledgeUser='false'" --json + - id: knowledgeAssign + label: Assign Knowledge user to the deployment user + command: sf data update record --sobject User --where "Username='deploy.github@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + +commandsPostDeploy: + - id: knowledgeUnassign + label: Remove KnowledgeUser right to the user who has it + command: sf data update record --sobject User --where "UserPermissionsKnowledgeUser='true'" --values "UserPermissionsKnowledgeUser='false'" --json + - id: knowledgeAssign + label: Assign Knowledge user to desired username + command: sf data update record --sobject User --where "Username='admin-yser@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + - id: someActionToRunJustOneTime + label: And to run only if deployment is success + command: sf sfdmu:run ... + skipIfError: true + context: process-deployment-only + runOnlyOnceByOrg: true +``` + + +## Parameters + +| Name | Type | Description | Default | Required | Options | +|:-------------------------|:-------:|:-------------------------|:-------:|:--------:|:-------:| +| api-version
-a | option | api-version | | | | +| async | boolean | async | | | | +| coverage-formatters | option | coverage-formatters | | | | +| debug | boolean | debug | | | | +| dry-run | boolean | dry-run | | | | +| flags-dir | option | undefined | | | | +| ignore-conflicts
-c | boolean | ignore-conflicts | | | | +| ignore-errors
-r | boolean | ignore-errors | | | | +| ignore-warnings
-g | boolean | ignore-warnings | | | | +| json | boolean | Format output as json. | | | | +| junit | boolean | junit | | | | +| manifest
-x | option | manifest | | | | +| metadata
-m | option | metadata | | | | +| metadata-dir | option | metadata-dir | | | | +| post-destructive-changes | option | post-destructive-changes | | | | +| pre-destructive-changes | option | pre-destructive-changes | | | | +| purge-on-delete | boolean | purge-on-delete | | | | +| results-dir | option | results-dir | | | | +| single-package | boolean | single-package | | | | +| source-dir
-d | option | source-dir | | | | +| target-org
-o | option | undefined | | | | +| test-level | option | test-level | | | | +| tests | option | tests | | | | +| wait
-w | option | wait | 33 | | | + +## Examples + + diff --git a/docs/hardis/project/fix/profiletabs.md b/docs/hardis/project/fix/profiletabs.md index af71ccf50..4a4d42804 100644 --- a/docs/hardis/project/fix/profiletabs.md +++ b/docs/hardis/project/fix/profiletabs.md @@ -1,27 +1,26 @@ - + # hardis:project:fix:profiletabs ## Description -Interactive prompts to add tab visibilities that are not retrieved by force:source:pull +Interactive prompts to add tab visibilities that are not retrieved by project retrieve start ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-----------------------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| path
-p | option | Root folder | C:\git\pro\sfdx-hardis2 | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-----------------------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| path
-p | option | Root folder | C:\git\pro\sfdx-hardis2 | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:fix:profiletabs +sf hardis:project:fix:profiletabs ``` diff --git a/docs/hardis/project/fix/v53flexipages.md b/docs/hardis/project/fix/v53flexipages.md index 9db9cf51d..dbf925633 100644 --- a/docs/hardis/project/fix/v53flexipages.md +++ b/docs/hardis/project/fix/v53flexipages.md @@ -1,4 +1,4 @@ - + # hardis:project:fix:v53flexipages ## Description @@ -9,19 +9,19 @@ Note: Update api version to 53.0 in package.xml and sfdx-project.json ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------|:-------:|:--------------------------------------------------------------|:-----------------------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| path
-p | option | Root folder | C:\git\pro\sfdx-hardis2 | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------|:-------:|:--------------------------------------------------------------|:-----------------------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| path
-p | option | Root folder | C:\git\pro\sfdx-hardis2 | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:fix:v53flexipages +sf hardis:project:fix:v53flexipages ``` diff --git a/docs/hardis/project/generate/gitdelta.md b/docs/hardis/project/generate/gitdelta.md index 0683355f1..82db20589 100644 --- a/docs/hardis/project/generate/gitdelta.md +++ b/docs/hardis/project/generate/gitdelta.md @@ -1,4 +1,4 @@ - + # hardis:project:generate:gitdelta ## Description @@ -7,21 +7,21 @@ Generate package.xml git delta between 2 commits ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| branch | option | Git branch to use to generate delta | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| fromcommit | option | Hash of commit to start from | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| tocommit | option | Hash of commit to stop at | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| branch | option | Git branch to use to generate delta | | | | +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| fromcommit | option | Hash of commit to start from | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| tocommit | option | Hash of commit to stop at | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:generate:gitdelta +sf hardis:project:generate:gitdelta ``` diff --git a/docs/hardis/project/lint.md b/docs/hardis/project/lint.md index 4fd9af2f0..f82fd3060 100644 --- a/docs/hardis/project/lint.md +++ b/docs/hardis/project/lint.md @@ -1,4 +1,4 @@ - + # hardis:project:lint ## Description @@ -7,25 +7,23 @@ Apply syntactic analysis (linters) on the repository sources, using Mega-Linter ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| fix
-f | boolean | Apply linters fixes | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| fix
-f | boolean | Apply linters fixes | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:project:lint +sf hardis:project:lint ``` ```shell -sfdx hardis:project:lint --fix +sf hardis:project:lint --fix ``` diff --git a/docs/hardis/project/metadata/findduplicates.md b/docs/hardis/project/metadata/findduplicates.md index 801322cb3..9c7ae960b 100644 --- a/docs/hardis/project/metadata/findduplicates.md +++ b/docs/hardis/project/metadata/findduplicates.md @@ -1,4 +1,4 @@ - + # hardis:project:metadata:findduplicates ## Description @@ -13,13 +13,13 @@ metadataDuplicateFindKeys : ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| files
-f | option | XML metadata files path | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| files
-f | option | XML metadata files path | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples @@ -46,7 +46,7 @@ metadataDuplicateFindKeys : ```shell -$ sfdx hardis:project:metadata:findduplicates --file layout.layout-meta.xml +$ sf hardis:project:metadata:findduplicates --file layout.layout-meta.xml [sfdx-hardis] Duplicate values in layout.layout-meta.xml - Key : Layout.layoutSections.layoutColumns.layoutItems.field - Values : Name @@ -55,7 +55,7 @@ $ sfdx hardis:project:metadata:findduplicates --file layout.layout-meta.xml ```shell -$ sfdx hardis:project.metadata:findduplicates -f "force-app/main/default/**/*.xml" +$ sf hardis:project.metadata:findduplicates -f "force-app/main/default/**/*.xml" [sfdx-hardis] hardis:project:metadata:findduplicates execution time 0:00:00.397 [sfdx-hardis] Duplicate values in layout1.layout-meta.xml - Key : Layout.layoutSections.layoutColumns.layoutItems.field diff --git a/docs/hardis/scratch/create.md b/docs/hardis/scratch/create.md index 4ec5b3ac0..06b893aa3 100644 --- a/docs/hardis/scratch/create.md +++ b/docs/hardis/scratch/create.md @@ -1,4 +1,4 @@ - + # hardis:scratch:create ## Description @@ -18,22 +18,21 @@ Create and initialize a scratch org or a source-tracked sandbox (config can be d ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------------|:-------:|:------------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| forcenew
-n | boolean | If an existing scratch org exists, do not reuse it but create a new one | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| pool
-d | boolean | Creates the scratch org for a scratch org pool | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetdevhubusername
-v | option | username or alias for the dev hub org; overrides default dev hub org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:----------------------|:-------:|:------------------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| forcenew
-n | boolean | If an existing scratch org exists, do not reuse it but create a new one | | | | +| json | boolean | Format output as json. | | | | +| pool
-d | boolean | Creates the scratch org for a scratch org pool | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-dev-hub
-v | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:scratch:create +sf hardis:scratch:create ``` diff --git a/docs/hardis/scratch/delete.md b/docs/hardis/scratch/delete.md index a0ffb9821..c8ab8f9dd 100644 --- a/docs/hardis/scratch/delete.md +++ b/docs/hardis/scratch/delete.md @@ -1,4 +1,4 @@ - + # hardis:scratch:delete ## Description @@ -7,20 +7,19 @@ Assisted menu to delete scratch orgs associated to a DevHub ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------------|:-------:|:---------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetdevhubusername
-v | option | username or alias for the dev hub org; overrides default dev hub org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:----------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-dev-hub
-v | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:scratch:delete +sf hardis:scratch:delete ``` diff --git a/docs/hardis/scratch/pool/create.md b/docs/hardis/scratch/pool/create.md index 93fd41652..8d8cbeb1c 100644 --- a/docs/hardis/scratch/pool/create.md +++ b/docs/hardis/scratch/pool/create.md @@ -1,4 +1,4 @@ - + # hardis:scratch:pool:create ## Description @@ -12,27 +12,26 @@ Select a data storage service and configure information to build a scratch org p - Call the following lines in the CI job: ```shell - sfdx hardis:auth:login --devhub - sfdx hardis:scratch:pool:refresh + sf hardis:auth:login --devhub + sf hardis:scratch:pool:refresh ``` ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------------|:-------:|:---------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetdevhubusername
-v | option | username or alias for the dev hub org; overrides default dev hub org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:----------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-dev-hub
-v | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:scratch:pool:configure +sf hardis:scratch:pool:configure ``` diff --git a/docs/hardis/scratch/pool/localauth.md b/docs/hardis/scratch/pool/localauth.md index 8d578e2e9..e69eba56d 100644 --- a/docs/hardis/scratch/pool/localauth.md +++ b/docs/hardis/scratch/pool/localauth.md @@ -1,4 +1,4 @@ - + # hardis:scratch:pool:localauth ## Description @@ -7,20 +7,19 @@ Calls the related storage service to request api keys and secrets that allows a ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------------|:-------:|:---------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetdevhubusername
-v | option | username or alias for the dev hub org; overrides default dev hub org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:----------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-dev-hub
-v | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:scratch:pool:localauth +sf hardis:scratch:pool:localauth ``` diff --git a/docs/hardis/scratch/pool/refresh.md b/docs/hardis/scratch/pool/refresh.md index 738020768..7c6d4b4c8 100644 --- a/docs/hardis/scratch/pool/refresh.md +++ b/docs/hardis/scratch/pool/refresh.md @@ -1,4 +1,4 @@ - + # hardis:scratch:pool:refresh ## Description @@ -7,20 +7,19 @@ Create enough scratch orgs to fill the pool ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------------|:-------:|:---------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetdevhubusername
-v | option | username or alias for the dev hub org; overrides default dev hub org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:----------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-dev-hub
-v | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:scratch:pool:refresh +sf hardis:scratch:pool:refresh ``` diff --git a/docs/hardis/scratch/pool/reset.md b/docs/hardis/scratch/pool/reset.md index ddc08a8cf..5ec141972 100644 --- a/docs/hardis/scratch/pool/reset.md +++ b/docs/hardis/scratch/pool/reset.md @@ -1,4 +1,4 @@ - + # hardis:scratch:pool:reset ## Description @@ -7,20 +7,19 @@ Reset scratch org pool (delete all scratches in the pool) ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------------|:-------:|:---------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetdevhubusername
-v | option | username or alias for the dev hub org; overrides default dev hub org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:----------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-dev-hub
-v | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:scratch:pool:refresh +sf hardis:scratch:pool:refresh ``` diff --git a/docs/hardis/scratch/pool/view.md b/docs/hardis/scratch/pool/view.md index c275c57bd..a2f05a97b 100644 --- a/docs/hardis/scratch/pool/view.md +++ b/docs/hardis/scratch/pool/view.md @@ -1,4 +1,4 @@ - + # hardis:scratch:pool:view ## Description @@ -7,20 +7,19 @@ Displays all stored content of project scratch org pool if defined ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------------|:-------:|:---------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetdevhubusername
-v | option | username or alias for the dev hub org; overrides default dev hub org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:----------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-dev-hub
-v | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:scratch:pool:view +sf hardis:scratch:pool:view ``` diff --git a/docs/hardis/scratch/pull.md b/docs/hardis/scratch/pull.md index 680d9cfd6..4e86d5529 100644 --- a/docs/hardis/scratch/pull.md +++ b/docs/hardis/scratch/pull.md @@ -1,4 +1,4 @@ - + # hardis:scratch:pull ## Description @@ -9,9 +9,9 @@ Then, you probably want to stage and commit the files containing the updates you -- Calls sfdx force:source:pull under the hood +- Calls sf project retrieve start under the hood - If there are errors, proposes to automatically add erroneous item in `.forceignore`, then pull again -- If you want to always retrieve sources like CustomApplication that are not always detected as updates by force:source:pull , you can define property **autoRetrieveWhenPull** in .sfdx-hardis.yml +- If you want to always retrieve sources like CustomApplication that are not always detected as updates by project:retrieve:start , you can define property **autoRetrieveWhenPull** in .sfdx-hardis.yml Example: ```yaml @@ -24,20 +24,19 @@ autoRetrieveWhenPull: ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:scratch:pull +sf hardis:scratch:pull ``` diff --git a/docs/hardis/scratch/push.md b/docs/hardis/scratch/push.md index 7636654f0..d504ca953 100644 --- a/docs/hardis/scratch/push.md +++ b/docs/hardis/scratch/push.md @@ -1,29 +1,28 @@ - + # hardis:scratch:push ## Description Push local files to scratch org -Calls `sfdx force:source:push` under the hood +Calls `sf project deploy start` under the hood ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:scratch:push +sf hardis:scratch:push ``` diff --git a/docs/hardis/source/deploy.md b/docs/hardis/source/deploy.md index 524c12b6b..44b7d4b55 100644 --- a/docs/hardis/source/deploy.md +++ b/docs/hardis/source/deploy.md @@ -1,4 +1,4 @@ - + # hardis:source:deploy ## Description @@ -20,7 +20,13 @@ You can also have deployment results as pull request comments, on: ### Deployment pre or post commands -You can define command lines to run before or after a deployment +You can define command lines to run before or after a deployment, with parameters: + +- **id**: Unique Id for the command +- **label**: Human readable label for the command +- **skipIfError**: If defined to "true", the post-command won't be run if there is a deployment failure +- **context**: Defines the context where the command will be run. Can be **all** (default), **check-deployment-only** or **process-deployment-only** +- **runOnlyOnceByOrg**: If set to true, the command will be run only one time per org. A record of SfdxHardisTrace__c is stored to make that possible (it needs to be existing in target org) If the commands are not the same depending on the target org, you can define them into **config/branches/.sfdx-hardis-BRANCHNAME.yml** instead of root **config/.sfdx-hardis.yml** @@ -34,6 +40,7 @@ commandsPreDeploy: - id: knowledgeAssign label: Assign Knowledge user to the deployment user command: sf data update record --sobject User --where "Username='deploy.github@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + commandsPostDeploy: - id: knowledgeUnassign label: Remove KnowledgeUser right to the user who has it @@ -41,6 +48,12 @@ commandsPostDeploy: - id: knowledgeAssign label: Assign Knowledge user to desired username command: sf data update record --sobject User --where "Username='admin-yser@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + - id: someActionToRunJustOneTime + label: And to run only if deployment is success + command: sf sfdmu:run ... + skipIfError: true + context: process-deployment-only + runOnlyOnceByOrg: true ``` Notes: @@ -52,39 +65,38 @@ Notes: ## Parameters -| Name | Type | Description | Default | Required | Options | -|:--------------------------------|:-------:|:--------------------------------------------------------------------|:----------:|:--------:|:----------------------------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| checkcoverage | boolean | Check Apex org coverage | | | | -| checkonly
-c | boolean | checkonly | | | | -| coverageformatters | option | coverageformatters | | | | -| debug | boolean | debug | | | | -| forceoverwrite
-f | boolean | forceoverwrite | | | | -| ignoreerrors
-o | boolean | ignoreErrors | | | | -| ignorewarnings
-g | boolean | ignoreWarnings | | | | -| json | boolean | format output as json | | | | -| junit | boolean | junit | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| manifest
-x | option | flagsLong.manifest | | | | -| metadata
-m | option | metadata | | | | -| postdestructivechanges | option | postdestructivechanges | | | | -| predestructivechanges | option | predestructivechanges | | | | -| resultsdir | option | resultsdir | | | | -| runtests
-r | option | runTests | | | | -| soapdeploy | boolean | soapDeploy | | | | -| sourcepath
-p | option | sourcePath | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| testlevel
-l | option | testlevel | NoTestRun | | NoTestRun
RunSpecifiedTests
RunLocalTests
RunAllTestsInOrg | -| tracksource
-t | boolean | tracksource | | | | -| validateddeployrequestid
-q | option | validateDeployRequestId | | | | -| verbose | boolean | verbose | | | | -| wait
-w | option | wait | 60 minutes | | | -| websocket | option | websocket | | | | +| Name | Type | Description | Default | Required | Options | +|:--------------------------------|:-------:|:------------------------|:---------:|:--------:|:----------------------------------------------------------------------:| +| checkcoverage | boolean | Check Apex org coverage | | | | +| checkonly
-c | boolean | checkonly | | | | +| coverageformatters | option | coverageformatters | | | | +| debug | boolean | debug | | | | +| flags-dir | option | undefined | | | | +| forceoverwrite
-f | boolean | forceoverwrite | | | | +| ignoreerrors
-o | boolean | ignoreErrors | | | | +| ignorewarnings
-g | boolean | ignoreWarnings | | | | +| json | boolean | Format output as json. | | | | +| junit | boolean | junit | | | | +| manifest
-x | option | flagsLong.manifest | | | | +| metadata
-m | option | metadata | | | | +| postdestructivechanges | option | postdestructivechanges | | | | +| predestructivechanges | option | predestructivechanges | | | | +| resultsdir | option | resultsdir | | | | +| runtests
-r | option | runTests | | | | +| soapdeploy | boolean | soapDeploy | | | | +| sourcepath
-p | option | sourcePath | | | | +| target-org
-o | option | undefined | | | | +| testlevel
-l | option | testlevel | NoTestRun | | NoTestRun
RunSpecifiedTests
RunLocalTests
RunAllTestsInOrg | +| tracksource
-t | boolean | tracksource | | | | +| validateddeployrequestid
-q | option | validateDeployRequestId | | | | +| verbose | boolean | verbose | | | | +| wait
-w | option | wait | 60 | | | +| websocket | option | websocket | | | | ## Examples ```shell -sfdx hardis:source:deploy -x manifest/package.xml --wait 60 --ignorewarnings --testlevel RunLocalTests --postdestructivechanges ./manifest/destructiveChanges.xml --targetusername nicolas.vuillamy@cloudity.com.sfdxhardis --checkonly --checkcoverage --verbose --coverageformatters json-summary +sf hardis:source:deploy -x manifest/package.xml --wait 60 --ignorewarnings --testlevel RunLocalTests --postdestructivechanges ./manifest/destructiveChanges.xml --target-org nicolas.vuillamy@cloudity.com.sfdxhardis --checkonly --checkcoverage --verbose --coverageformatters json-summary ``` diff --git a/docs/hardis/source/push.md b/docs/hardis/source/push.md index c998d5fe2..53f9f15c6 100644 --- a/docs/hardis/source/push.md +++ b/docs/hardis/source/push.md @@ -1,4 +1,4 @@ - + # hardis:source:push ## Description @@ -12,18 +12,17 @@ sfdx-hardis wrapper for sfdx force:source:push that displays tips to solve deplo ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:----------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug | boolean | debug | | | | -| forceoverwrite
-f | boolean | forceoverwrite | | | | -| ignorewarnings
-g | boolean | ignorewarnings | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| quiet | boolean | quiet | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| wait
-w | option | wait | 60 minutes | | | -| websocket | option | websocket | | | | +| Name | Type | Description | Default | Required | Options | +|:----------------------|:-------:|:-----------------------|:-------:|:--------:|:-------:| +| debug | boolean | debug | | | | +| flags-dir | option | undefined | | | | +| forceoverwrite
-f | boolean | forceoverwrite | | | | +| ignorewarnings
-g | boolean | ignorewarnings | | | | +| json | boolean | Format output as json. | | | | +| quiet | boolean | quiet | | | | +| target-org
-o | option | undefined | | | | +| wait
-w | option | wait | 60 | | | +| websocket | option | websocket | | | | ## Examples diff --git a/docs/hardis/source/retrieve.md b/docs/hardis/source/retrieve.md index ad7a24610..06009ddb3 100644 --- a/docs/hardis/source/retrieve.md +++ b/docs/hardis/source/retrieve.md @@ -1,4 +1,4 @@ - + # hardis:source:retrieve ## Description @@ -13,23 +13,23 @@ sfdx-hardis wrapper for sfdx force:source:retrieve ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion
-a | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | debugMode | | | | -| forceoverwrite
-f | boolean | forceoverwrite | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| manifest
-x | option | manifest | | | | -| metadata
-m | option | metadata | | | | -| packagenames
-n | option | packagenames | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| sourcepath
-p | option | sourcePath | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| tracksource
-t | boolean | tracksource | | | | -| verbose | boolean | verbose | | | | -| wait
-w | option | wait | | | | -| websocket | option | websocket | | | | +| Name | Type | Description | Default | Required | Options | +|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-------:| +| apiversion
-a | option | Override the api version used for api requests made by this command | | | | +| debug
-d | boolean | debugMode | | | | +| flags-dir | option | undefined | | | | +| forceoverwrite
-f | boolean | forceoverwrite | | | | +| json | boolean | Format output as json. | | | | +| manifest
-x | option | manifest | | | | +| metadata
-m | option | metadata | | | | +| packagenames
-n | option | packagenames | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| sourcepath
-p | option | sourcePath | | | | +| target-org
-o | option | undefined | | | | +| tracksource
-t | boolean | tracksource | | | | +| verbose | boolean | verbose | | | | +| wait
-w | option | wait | | | | +| websocket | option | websocket | | | | ## Examples diff --git a/docs/hardis/work/new.md b/docs/hardis/work/new.md index a7e30e894..fcc1c574a 100644 --- a/docs/hardis/work/new.md +++ b/docs/hardis/work/new.md @@ -1,4 +1,4 @@ - + # hardis:work:new ## Description @@ -28,20 +28,20 @@ Under the hood, it can: ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------------|:-------:|:---------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetdevhubusername
-v | option | username or alias for the dev hub org; overrides default dev hub org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:----------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-dev-hub
-v | option | undefined | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:work:task:new +sf hardis:work:task:new ``` diff --git a/docs/hardis/work/refresh.md b/docs/hardis/work/refresh.md index e9f2c1452..d08c00df8 100644 --- a/docs/hardis/work/refresh.md +++ b/docs/hardis/work/refresh.md @@ -1,4 +1,4 @@ - + # hardis:work:refresh ## Description @@ -7,21 +7,20 @@ Make my local branch and my scratch org up to date with the most recent sources ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| nopull
-n | boolean | No scratch pull before save (careful if you use that!) | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| nopull
-n | boolean | No scratch pull before save (careful if you use that!) | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:work:refresh +sf hardis:work:refresh ``` diff --git a/docs/hardis/work/resetselection.md b/docs/hardis/work/resetselection.md index b166701cf..73e956b71 100644 --- a/docs/hardis/work/resetselection.md +++ b/docs/hardis/work/resetselection.md @@ -1,4 +1,4 @@ - + # hardis:work:resetselection ## Description @@ -10,20 +10,19 @@ Calls a soft git reset behind the hood ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:work:resetsave +sf hardis:work:resetsave ``` diff --git a/docs/hardis/work/save.md b/docs/hardis/work/save.md index cc2b287e8..3f8a9f8f1 100644 --- a/docs/hardis/work/save.md +++ b/docs/hardis/work/save.md @@ -1,4 +1,4 @@ - + # hardis:work:save ## Description @@ -39,29 +39,28 @@ autoRemoveUserPermissions: ## Parameters -| Name | Type | Description | Default | Required | Options | -|:----------------------|:-------:|:--------------------------------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| apiversion | option | override the api version used for api requests made by this command | | | | -| auto | boolean | No user prompts (when called from CI for example) | | | | -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| noclean
-c | boolean | No cleaning of local sources | | | | -| nogit
-g | boolean | No automated git operations | | | | -| nopull
-n | boolean | No scratch pull before save | | | | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| targetbranch | option | Name of the Merge Request target branch. Will be guessed or prompted if not provided. | | | | -| targetusername
-u | option | username or alias for the target org; overrides default target org | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:------------------|:-------:|:--------------------------------------------------------------------------------------|:-------:|:--------:|:-------:| +| auto | boolean | No user prompts (when called from CI for example) | | | | +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| noclean
-c | boolean | No cleaning of local sources | | | | +| nogit
-g | boolean | No automated git operations | | | | +| nopull
-n | boolean | No scratch pull before save | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| target-org
-o | option | undefined | | | | +| targetbranch | option | Name of the Merge Request target branch. Will be guessed or prompted if not provided. | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:work:task:save +sf hardis:work:task:save ``` ```shell -sfdx hardis:work:task:save --nopull --nogit --noclean +sf hardis:work:task:save --nopull --nogit --noclean ``` diff --git a/docs/hardis/work/ws.md b/docs/hardis/work/ws.md index f12127e25..ee5740f12 100644 --- a/docs/hardis/work/ws.md +++ b/docs/hardis/work/ws.md @@ -1,4 +1,4 @@ - + # hardis:work:ws ## Description @@ -7,19 +7,19 @@ Technical calls to WebSocket functions ## Parameters -| Name | Type | Description | Default | Required | Options | -|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-----------------------------------------------------:| -| debug
-d | boolean | Activate debug mode (more logs) | | | | -| event
-e | option | WebSocket event | | | | -| json | boolean | format output as json | | | | -| loglevel | option | logging level for this command invocation | warn | | trace
debug
info
warn
error
fatal | -| skipauth | boolean | Skip authentication check when a default username is required | | | | -| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | +| Name | Type | Description | Default | Required | Options | +|:-------------|:-------:|:--------------------------------------------------------------|:-------:|:--------:|:-------:| +| debug
-d | boolean | Activate debug mode (more logs) | | | | +| event
-e | option | WebSocket event | | | | +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| skipauth | boolean | Skip authentication check when a default username is required | | | | +| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration | | | | ## Examples ```shell -sfdx hardis:work:ws --event refreshStatus +sf hardis:work:ws --event refreshStatus ``` diff --git a/docs/hello/world.md b/docs/hello/world.md new file mode 100644 index 000000000..04d2e7d73 --- /dev/null +++ b/docs/hello/world.md @@ -0,0 +1,28 @@ + +# hello:world + +## Description + +Say hello either to the world or someone you know. + +## Parameters + +| Name | Type | Description | Default | Required | Options | +|:------------|:-------:|:----------------------------------------|:-------:|:--------:|:-------:| +| flags-dir | option | undefined | | | | +| json | boolean | Format output as json. | | | | +| name
-n | option | This person can be anyone in the world! | World | | | + +## Examples + +```shell +Say hello to the world: +<%= config.bin %> <%= command.id %> +``` + +```shell +Say hello to someone you know: +<%= config.bin %> <%= command.id %> --name Astro +``` + + diff --git a/docs/index.md b/docs/index.md index e1f8d9a3e..4b200b7cc 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,10 +1,11 @@ - + + [![sfdx-hardis by Cloudity Banner](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/sfdx-hardis-banner.png)](https://sfdx-hardis.cloudity.com) # sfdx-hardis -[_Presented at Dreamforce 23!_](https://reg.salesforce.com/flow/plus/df23/sessioncatalog/page/catalog/session/1684196389783001OqEl) +_Presented at_ [_Dreamforce 23_](https://reg.salesforce.com/flow/plus/df23/sessioncatalog/page/catalog/session/1684196389783001OqEl) **_and soon at [_Dreamforce 24!_](https://reg.salesforce.com/flow/plus/df24/sessioncatalog/page/catalog/session/1718915808069001Q7HH)_** [![Version](https://img.shields.io/npm/v/sfdx-hardis.svg)](https://npmjs.org/package/sfdx-hardis) [![Downloads/week](https://img.shields.io/npm/dw/sfdx-hardis.svg)](https://npmjs.org/package/sfdx-hardis) @@ -17,25 +18,29 @@ [![License](https://img.shields.io/npm/l/sfdx-hardis.svg)](https://github.com/hardisgroupcom/sfdx-hardis/blob/main/package.json) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) -Toolbox for Salesforce DX, by [Cloudity](https://cloudity.com/), natively compliant with most platforms and tools +Toolbox for Salesforce DX, by [**Cloudity**](https://cloudity.com/) & friends, natively compliant with most platforms and tools. ![Native Integrations](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/integrations.png) It will allow you to: - Do with simple commands what could be done manually in minutes/hours -- [Define a complete CI/CD Pipeline for your Salesforce project](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-home/) -- [Backup Metadatas and monitor any Salesforce org](https://sfdx-hardis.cloudity.com/salesforce-monitoring-home/) +- [Define a **ready to use CI/CD Pipeline** for your Salesforce project](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-home/) +- [**Backup Metadatas** and **monitor any Salesforce org**](https://sfdx-hardis.cloudity.com/salesforce-monitoring-home/) + +[_Please see the full list of commands in Online documentation_](https://sfdx-hardis.cloudity.com) -[**Please see the full list of commands in Online documentation**](https://sfdx-hardis.cloudity.com) +___ **sfdx-hardis** commands are also available with UI in [**SFDX Hardis Visual Studio Code Extension**](https://marketplace.visualstudio.com/items?itemName=NicolasVuillamy.vscode-sfdx-hardis) [![VsCode SFDX Hardis](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/extension-demo.gif)](https://marketplace.visualstudio.com/items?itemName=NicolasVuillamy.vscode-sfdx-hardis) +___ + _See Dreamforce presentation_ -[![See Dreamforce presentation](https://img.youtube.com/vi/o0Mm9F07UFs/0.jpg)](https://www.youtube.com/watch?v=o0Mm9F07UFs) +[![See Dreamforce presentation](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/play-dreamforce-session.png)](https://www.youtube.com/watch?v=o0Mm9F07UFs) ## Installation @@ -45,7 +50,9 @@ You can install [Visual Studio Code](https://code.visualstudio.com/) extension [ Once installed, click on ![Hardis Group button](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/hardis-button.jpg) in VsCode left bar, and follow the additional installation instructions -[![Installation tutorial](https://img.youtube.com/vi/LA8m-t7CjHA/0.jpg)](https://www.youtube.com/watch?v=LA8m-t7CjHA) +[![Installation tutorial](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/play-install-tuto.png)](https://www.youtube.com/watch?v=LA8m-t7CjHA) + +___ ### As SFDX Plugin @@ -57,26 +64,28 @@ Once installed, click on ![Hardis Group button](https://github.com/hardisgroupco #### Plugin installation ```sh-session -sfdx plugins:install sfdx-hardis +sf plugins install sfdx-hardis ``` For advanced use, please also install dependencies ```sh-session sf plugins install @salesforce/plugin-packaging -sfdx plugins:install sfdmu -sfdx plugins:install sfdx-git-delta -sfdx plugins:install sfdx-essentials -sfdx plugins:install texei-sfdx-plugin +sf plugins install sfdmu +sf plugins install sfdx-git-delta +sf plugins install texei-sfdx-plugin ``` -If you are using CI/CD scripts, use `echo y | sfdx plugins:install ...` to bypass prompt. +If you are using CI/CD scripts, use `echo y | sf plugins install ...` to bypass prompt. + +___ ### Docker You can use sfdx-hardis docker images to run in CI - Docker Hub + - [**hardisgroupcom/sfdx-hardis:latest**](https://hub.docker.com/r/hardisgroupcom/sfdx-hardis) (with latest @salesforce/cli version) - [**hardisgroupcom/sfdx-hardis:latest-sfdx-recommended**](https://hub.docker.com/r/hardisgroupcom/sfdx-hardis) (with recommended @salesforce/cli version, in case the latest version of @salesforce/cli is buggy) @@ -89,7 +98,7 @@ _See [Dockerfile](https://github.com/hardisgroupcom/sfdx-hardis/blob/main/Docker ## Usage ```sh-session -sfdx hardis: +sf hardis: ``` ## Articles @@ -98,6 +107,7 @@ Here are some articles about [sfdx-hardis](https://sfdx-hardis.cloudity.com/) - English +[![Conga Deployment Cheat Sheet](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-conga-banner.jpg)](https://nicolas.vuillamy.fr/how-to-deploy-conga-composer-configuration-using-salesforce-cli-plugins-c2899641f36b) [![Questions/Answers](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-questions-answers.jpg)](https://nicolas.vuillamy.fr/what-devops-experts-want-to-know-about-salesforce-ci-cd-with-sfdx-hardis-q-a-1f412db34476) [![Salesforce Developers Podcast](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-sfdev.jpg)](https://developer.salesforce.com/podcast/2023/06/sfdx) [![sfdx-hardis: A release management tool for open-source](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-cicd-salesforcedevopsnet.jpg)](https://salesforcedevops.net/index.php/2023/03/01/sfdx-hardis-open-source-salesforce-release-management/) @@ -116,7 +126,7 @@ Here are some articles about [sfdx-hardis](https://sfdx-hardis.cloudity.com/) ## Contributing -Anyone is welcome to contribute to this sfdx-hardis +Everyone is welcome to contribute to sfdx-hardis (even juniors: we'll assist you !) - Install Node.js ([recommended version](https://nodejs.org/en/)) - Install typescript by running `npm install typescript --global` @@ -127,14 +137,13 @@ Anyone is welcome to contribute to this sfdx-hardis - Run `yarn` to install dependencies - Run `sf plugins link` to link the local sfdx-hardis to SFDX CLI - Run `tsc --watch` to transpile typescript into js everytime you update a TS file -- Debug commands using `NODE_OPTIONS=--inspect-brk sfdx hardis:somecommand -someparameter somevalue` +- Debug commands using `NODE_OPTIONS=--inspect-brk sf hardis:somecommand -someparameter somevalue` ## Dependencies **sfdx-hardis** partially relies on the following SFDX Open-Source packages - [Salesforce Data Move Utility](https://github.com/forcedotcom/SFDX-Data-Move-Utility) -- [SFDX Essentials](https://github.com/nvuillam/sfdx-essentials) - [SFDX Git Delta](https://github.com/scolladon/sfdx-git-delta) - [Texei Sfdx Plugin](https://github.com/texei/texei-sfdx-plugin) @@ -152,155 +161,179 @@ Anyone is welcome to contribute to this sfdx-hardis | Command | Title | |:----------------------------------------------|:------| -| [**hardis:auth:login**](hardis/auth/login.md) | Login | +| [**hardis:auth:login**](hardis/auth/login.md) | | ### hardis:cache -| Command | Title | -|:------------------------------------------------|:------------------------| -| [**hardis:cache:clear**](hardis/cache/clear.md) | Clear sfdx-hardis cache | +| Command | Title | +|:------------------------------------------------|:------| +| [**hardis:cache:clear**](hardis/cache/clear.md) | | ### hardis:config -| Command | Title | -|:----------------------------------------------|:-------------------------------| -| [**hardis:config:get**](hardis/config/get.md) | Deploy metadata sources to org | +| Command | Title | +|:----------------------------------------------|:------| +| [**hardis:config:get**](hardis/config/get.md) | | ### hardis:doc -| Command | Title | -|:----------------------------------------------------------------------------|:-----------------------------------| -| [**hardis:doc:extract:permsetgroups**](hardis/doc/extract/permsetgroups.md) | Generate project documentation | -| [**hardis:doc:plugin:generate**](hardis/doc/plugin/generate.md) | Generate SFDX Plugin Documentation | +| Command | Title | +|:----------------------------------------------------------------------------|:------| +| [**hardis:doc:extract:permsetgroups**](hardis/doc/extract/permsetgroups.md) | | +| [**hardis:doc:plugin:generate**](hardis/doc/plugin/generate.md) | | ### hardis:lint -| Command | Title | -|:----------------------------------------------------------------------|:-------------------------------------------| -| [**hardis:lint:access**](hardis/lint/access.md) | check permission access | -| [**hardis:lint:metadatastatus**](hardis/lint/metadatastatus.md) | check inactive metadatas | -| [**hardis:lint:missingattributes**](hardis/lint/missingattributes.md) | check missing description on custom fields | -| [**hardis:lint:unusedmetadatas**](hardis/lint/unusedmetadatas.md) | check unused labels and custom permissions | +| Command | Title | +|:----------------------------------------------------------------------|:------| +| [**hardis:lint:access**](hardis/lint/access.md) | | +| [**hardis:lint:metadatastatus**](hardis/lint/metadatastatus.md) | | +| [**hardis:lint:missingattributes**](hardis/lint/missingattributes.md) | | +| [**hardis:lint:unusedmetadatas**](hardis/lint/unusedmetadatas.md) | | ### hardis:mdapi -| Command | Title | -|:--------------------------------------------------|:-----------------------------------------------------------------------------------------------| -| [**hardis:mdapi:deploy**](hardis/mdapi/deploy.md) | sfdx-hardis wrapper for sfdx force:mdapi:deploy that displays tips to solve deployment errors. | +| Command | Title | +|:--------------------------------------------------|:------| +| [**hardis:mdapi:deploy**](hardis/mdapi/deploy.md) | | ### hardis:misc -| Command | Title | -|:----------------------------------------------------|:------------| -| [**hardis:misc:toml2csv**](hardis/misc/toml2csv.md) | TOML to CSV | +| Command | Title | +|:--------------------------------------------------------------------|:------| +| [**hardis:misc:purge-references**](hardis/misc/purge-references.md) | | +| [**hardis:misc:toml2csv**](hardis/misc/toml2csv.md) | | ### hardis:org -| Command | Title | -|:--------------------------------------------------------------------------------------|:------------------------------------------------------| -| [**hardis:org:configure:data**](hardis/org/configure/data.md) | Configure Data project | -| [**hardis:org:configure:files**](hardis/org/configure/files.md) | Configure File export project | -| [**hardis:org:configure:monitoring**](hardis/org/configure/monitoring.md) | Configure org monitoring | -| [**hardis:org:connect**](hardis/org/connect.md) | Connect to an org | -| [**hardis:org:create**](hardis/org/create.md) | Create sandbox org | -| [**hardis:org:data:delete**](hardis/org/data/delete.md) | Delete data | -| [**hardis:org:data:export**](hardis/org/data/export.md) | Export data | -| [**hardis:org:data:import**](hardis/org/data/import.md) | Import data | -| [**hardis:org:diagnose:audittrail**](hardis/org/diagnose/audittrail.md) | Diagnose content of Setup Audit Trail | -| [**hardis:org:diagnose:legacyapi**](hardis/org/diagnose/legacyapi.md) | Check for legacy API use | -| [**hardis:org:diagnose:licenses**](hardis/org/diagnose/licenses.md) | List licenses subscribed and used in a Salesforce org | -| [**hardis:org:diagnose:unusedlicenses**](hardis/org/diagnose/unusedlicenses.md) | Detect unused Permission Set Licenses | -| [**hardis:org:diagnose:unusedusers**](hardis/org/diagnose/unusedusers.md) | Detect unused Users in Salesforce | -| [**hardis:org:files:export**](hardis/org/files/export.md) | Export files | -| [**hardis:org:fix:listviewmine**](hardis/org/fix/listviewmine.md) | Fix listviews with | -| [**hardis:org:generate:packagexmlfull**](hardis/org/generate/packagexmlfull.md) | Generate Full Org package.xml | -| [**hardis:org:monitor:all**](hardis/org/monitor/all.md) | Monitor org | -| [**hardis:org:monitor:backup**](hardis/org/monitor/backup.md) | Backup DX sources | -| [**hardis:org:monitor:limits**](hardis/org/monitor/limits.md) | Check org limits | -| [**hardis:org:purge:apexlog**](hardis/org/purge/apexlog.md) | Purge Apex Logs | -| [**hardis:org:purge:flow**](hardis/org/purge/flow.md) | Purge Flow versions | -| [**hardis:org:retrieve:packageconfig**](hardis/org/retrieve/packageconfig.md) | Retrieve package configuration from an org | -| [**hardis:org:retrieve:sources:analytics**](hardis/org/retrieve/sources/analytics.md) | Retrieve CRM Analytics configuration from an org | -| [**hardis:org:retrieve:sources:dx**](hardis/org/retrieve/sources/dx.md) | Retrieve sfdx sources from org | -| [**hardis:org:retrieve:sources:dx2**](hardis/org/retrieve/sources/dx2.md) | Retrieve sfdx sources from org (2) | -| [**hardis:org:retrieve:sources:metadata**](hardis/org/retrieve/sources/metadata.md) | Retrieve sfdx sources from org | -| [**hardis:org:retrieve:sources:retrofit**](hardis/org/retrieve/sources/retrofit.md) | Retrofit changes from an org | -| [**hardis:org:select**](hardis/org/select.md) | Select org | -| [**hardis:org:test:apex**](hardis/org/test/apex.md) | Run apex tests | -| [**hardis:org:user:activateinvalid**](hardis/org/user/activateinvalid.md) | Reactivate sandbox invalid users | -| [**hardis:org:user:freeze**](hardis/org/user/freeze.md) | Freeze user logins | -| [**hardis:org:user:unfreeze**](hardis/org/user/unfreeze.md) | Unfreeze user logins | +| Command | Title | +|:--------------------------------------------------------------------------------------|:------| +| [**hardis:org:configure:data**](hardis/org/configure/data.md) | | +| [**hardis:org:configure:files**](hardis/org/configure/files.md) | | +| [**hardis:org:configure:monitoring**](hardis/org/configure/monitoring.md) | | +| [**hardis:org:connect**](hardis/org/connect.md) | | +| [**hardis:org:create**](hardis/org/create.md) | | +| [**hardis:org:data:delete**](hardis/org/data/delete.md) | | +| [**hardis:org:data:export**](hardis/org/data/export.md) | | +| [**hardis:org:data:import**](hardis/org/data/import.md) | | +| [**hardis:org:diagnose:audittrail**](hardis/org/diagnose/audittrail.md) | | +| [**hardis:org:diagnose:instanceupgrade**](hardis/org/diagnose/instanceupgrade.md) | | +| [**hardis:org:diagnose:legacyapi**](hardis/org/diagnose/legacyapi.md) | | +| [**hardis:org:diagnose:licenses**](hardis/org/diagnose/licenses.md) | | +| [**hardis:org:diagnose:releaseupdates**](hardis/org/diagnose/releaseupdates.md) | | +| [**hardis:org:diagnose:unusedlicenses**](hardis/org/diagnose/unusedlicenses.md) | | +| [**hardis:org:diagnose:unusedusers**](hardis/org/diagnose/unusedusers.md) | | +| [**hardis:org:files:export**](hardis/org/files/export.md) | | +| [**hardis:org:files:import**](hardis/org/files/import.md) | | +| [**hardis:org:fix:listviewmine**](hardis/org/fix/listviewmine.md) | | +| [**hardis:org:generate:packagexmlfull**](hardis/org/generate/packagexmlfull.md) | | +| [**hardis:org:monitor:all**](hardis/org/monitor/all.md) | | +| [**hardis:org:monitor:backup**](hardis/org/monitor/backup.md) | | +| [**hardis:org:monitor:limits**](hardis/org/monitor/limits.md) | | +| [**hardis:org:purge:apexlog**](hardis/org/purge/apexlog.md) | | +| [**hardis:org:purge:flow**](hardis/org/purge/flow.md) | | +| [**hardis:org:retrieve:packageconfig**](hardis/org/retrieve/packageconfig.md) | | +| [**hardis:org:retrieve:sources:analytics**](hardis/org/retrieve/sources/analytics.md) | | +| [**hardis:org:retrieve:sources:dx**](hardis/org/retrieve/sources/dx.md) | | +| [**hardis:org:retrieve:sources:dx2**](hardis/org/retrieve/sources/dx2.md) | | +| [**hardis:org:retrieve:sources:metadata**](hardis/org/retrieve/sources/metadata.md) | | +| [**hardis:org:retrieve:sources:retrofit**](hardis/org/retrieve/sources/retrofit.md) | | +| [**hardis:org:select**](hardis/org/select.md) | | +| [**hardis:org:test:apex**](hardis/org/test/apex.md) | | +| [**hardis:org:user:activateinvalid**](hardis/org/user/activateinvalid.md) | | +| [**hardis:org:user:freeze**](hardis/org/user/freeze.md) | | +| [**hardis:org:user:unfreeze**](hardis/org/user/unfreeze.md) | | ### hardis:package -| Command | Title | -|:------------------------------------------------------------------------|:-----------------------------------| -| [**hardis:package:create**](hardis/package/create.md) | Create a new package | -| [**hardis:package:install**](hardis/package/install.md) | Install packages in an org | -| [**hardis:package:mergexml**](hardis/package/mergexml.md) | Merge package.xml files | -| [**hardis:package:version:create**](hardis/package/version/create.md) | Create a new version of a package | -| [**hardis:package:version:list**](hardis/package/version/list.md) | Create a new version of a package | -| [**hardis:package:version:promote**](hardis/package/version/promote.md) | Promote new versions of package(s) | +| Command | Title | +|:------------------------------------------------------------------------|:------| +| [**hardis:package:create**](hardis/package/create.md) | | +| [**hardis:package:install**](hardis/package/install.md) | | +| [**hardis:package:mergexml**](hardis/package/mergexml.md) | | +| [**hardis:package:version:create**](hardis/package/version/create.md) | | +| [**hardis:package:version:list**](hardis/package/version/list.md) | | +| [**hardis:package:version:promote**](hardis/package/version/promote.md) | | + +### hardis:packagexml + +| Command | Title | +|:------------------------------------------------------------|:------| +| [**hardis:packagexml:append**](hardis/packagexml/append.md) | | +| [**hardis:packagexml:remove**](hardis/packagexml/remove.md) | | ### hardis:project -| Command | Title | -|:----------------------------------------------------------------------------------------------|:----------------------------------------------------------------| -| [**hardis:project:audit:apiversion**](hardis/project/audit/apiversion.md) | Audit Metadatas API Version | -| [**hardis:project:audit:callincallout**](hardis/project/audit/callincallout.md) | Audit CallIns and CallOuts | -| [**hardis:project:audit:duplicatefiles**](hardis/project/audit/duplicatefiles.md) | Find duplicate sfdx files | -| [**hardis:project:audit:remotesites**](hardis/project/audit/remotesites.md) | Audit Remote Sites | -| [**hardis:project:clean:emptyitems**](hardis/project/clean/emptyitems.md) | Clean retrieved empty items in dx sources | -| [**hardis:project:clean:flowpositions**](hardis/project/clean/flowpositions.md) | Clean Flow Positions | -| [**hardis:project:clean:hiddenitems**](hardis/project/clean/hiddenitems.md) | Clean retrieved hidden items in dx sources | -| [**hardis:project:clean:listviews**](hardis/project/clean/listviews.md) | Replace Mine by Everything in ListViews | -| [**hardis:project:clean:manageditems**](hardis/project/clean/manageditems.md) | Clean retrieved managed items in dx sources | -| [**hardis:project:clean:minimizeprofiles**](hardis/project/clean/minimizeprofiles.md) | Clean profiles of Permission Set attributes | -| [**hardis:project:clean:orgmissingitems**](hardis/project/clean/orgmissingitems.md) | Clean SFDX items using target org definition | -| [**hardis:project:clean:references**](hardis/project/clean/references.md) | Clean references in dx sources | -| [**hardis:project:clean:retrievefolders**](hardis/project/clean/retrievefolders.md) | Retrieve dashboards, documents and report folders in DX sources | -| [**hardis:project:clean:standarditems**](hardis/project/clean/standarditems.md) | Clean retrieved standard items in dx sources | -| [**hardis:project:clean:systemdebug**](hardis/project/clean/systemdebug.md) | Clean System debug | -| [**hardis:project:clean:xml**](hardis/project/clean/xml.md) | Clean retrieved empty items in dx sources | -| [**hardis:project:configure:auth**](hardis/project/configure/auth.md) | Configure authentication | -| [**hardis:project:convert:profilestopermsets**](hardis/project/convert/profilestopermsets.md) | Convert Profiles into Permission Sets | -| [**hardis:project:create**](hardis/project/create.md) | Login | -| [**hardis:project:deploy:sources:dx**](hardis/project/deploy/sources/dx.md) | Deploy sfdx sources to org | -| [**hardis:project:deploy:sources:metadata**](hardis/project/deploy/sources/metadata.md) | Deploy metadata sources to org | -| [**hardis:project:fix:profiletabs**](hardis/project/fix/profiletabs.md) | Fix profiles to add tabs that are not retrieved by SF CLI | -| [**hardis:project:fix:v53flexipages**](hardis/project/fix/v53flexipages.md) | Fix flexipages for v53 | -| [**hardis:project:generate:gitdelta**](hardis/project/generate/gitdelta.md) | Generate Git Delta | -| [**hardis:project:lint**](hardis/project/lint.md) | Lint | -| [**hardis:project:metadata:findduplicates**](hardis/project/metadata/findduplicates.md) | XML duplicate values finder | +| Command | Title | +|:----------------------------------------------------------------------------------------------|:------| +| [**hardis:project:audit:apiversion**](hardis/project/audit/apiversion.md) | | +| [**hardis:project:audit:callincallout**](hardis/project/audit/callincallout.md) | | +| [**hardis:project:audit:duplicatefiles**](hardis/project/audit/duplicatefiles.md) | | +| [**hardis:project:audit:remotesites**](hardis/project/audit/remotesites.md) | | +| [**hardis:project:clean:emptyitems**](hardis/project/clean/emptyitems.md) | | +| [**hardis:project:clean:filter-xml-content**](hardis/project/clean/filter-xml-content.md) | | +| [**hardis:project:clean:flowpositions**](hardis/project/clean/flowpositions.md) | | +| [**hardis:project:clean:hiddenitems**](hardis/project/clean/hiddenitems.md) | | +| [**hardis:project:clean:listviews**](hardis/project/clean/listviews.md) | | +| [**hardis:project:clean:manageditems**](hardis/project/clean/manageditems.md) | | +| [**hardis:project:clean:minimizeprofiles**](hardis/project/clean/minimizeprofiles.md) | | +| [**hardis:project:clean:orgmissingitems**](hardis/project/clean/orgmissingitems.md) | | +| [**hardis:project:clean:references**](hardis/project/clean/references.md) | | +| [**hardis:project:clean:retrievefolders**](hardis/project/clean/retrievefolders.md) | | +| [**hardis:project:clean:sensitive-metadatas**](hardis/project/clean/sensitive-metadatas.md) | | +| [**hardis:project:clean:standarditems**](hardis/project/clean/standarditems.md) | | +| [**hardis:project:clean:systemdebug**](hardis/project/clean/systemdebug.md) | | +| [**hardis:project:clean:xml**](hardis/project/clean/xml.md) | | +| [**hardis:project:configure:auth**](hardis/project/configure/auth.md) | | +| [**hardis:project:convert:profilestopermsets**](hardis/project/convert/profilestopermsets.md) | | +| [**hardis:project:create**](hardis/project/create.md) | | +| [**hardis:project:deploy:quick**](hardis/project/deploy/quick.md) | | +| [**hardis:project:deploy:simulate**](hardis/project/deploy/simulate.md) | | +| [**hardis:project:deploy:smart**](hardis/project/deploy/smart.md) | | +| [**hardis:project:deploy:sources:dx**](hardis/project/deploy/sources/dx.md) | | +| [**hardis:project:deploy:sources:metadata**](hardis/project/deploy/sources/metadata.md) | | +| [**hardis:project:deploy:start**](hardis/project/deploy/start.md) | | +| [**hardis:project:deploy:validate**](hardis/project/deploy/validate.md) | | +| [**hardis:project:fix:profiletabs**](hardis/project/fix/profiletabs.md) | | +| [**hardis:project:fix:v53flexipages**](hardis/project/fix/v53flexipages.md) | | +| [**hardis:project:generate:gitdelta**](hardis/project/generate/gitdelta.md) | | +| [**hardis:project:lint**](hardis/project/lint.md) | | +| [**hardis:project:metadata:findduplicates**](hardis/project/metadata/findduplicates.md) | | ### hardis:scratch -| Command | Title | -|:----------------------------------------------------------------------|:-----------------------------------------| -| [**hardis:scratch:create**](hardis/scratch/create.md) | Create and initialize scratch org | -| [**hardis:scratch:delete**](hardis/scratch/delete.md) | Delete scratch orgs(s) | -| [**hardis:scratch:pool:create**](hardis/scratch/pool/create.md) | Create and configure scratch org pool | -| [**hardis:scratch:pool:localauth**](hardis/scratch/pool/localauth.md) | Authenticate locally to scratch org pool | -| [**hardis:scratch:pool:refresh**](hardis/scratch/pool/refresh.md) | Refresh scratch org pool | -| [**hardis:scratch:pool:reset**](hardis/scratch/pool/reset.md) | Reset scratch org pool | -| [**hardis:scratch:pool:view**](hardis/scratch/pool/view.md) | View scratch org pool info | -| [**hardis:scratch:pull**](hardis/scratch/pull.md) | Scratch PULL | -| [**hardis:scratch:push**](hardis/scratch/push.md) | Scratch PUSH | +| Command | Title | +|:----------------------------------------------------------------------|:------| +| [**hardis:scratch:create**](hardis/scratch/create.md) | | +| [**hardis:scratch:delete**](hardis/scratch/delete.md) | | +| [**hardis:scratch:pool:create**](hardis/scratch/pool/create.md) | | +| [**hardis:scratch:pool:localauth**](hardis/scratch/pool/localauth.md) | | +| [**hardis:scratch:pool:refresh**](hardis/scratch/pool/refresh.md) | | +| [**hardis:scratch:pool:reset**](hardis/scratch/pool/reset.md) | | +| [**hardis:scratch:pool:view**](hardis/scratch/pool/view.md) | | +| [**hardis:scratch:pull**](hardis/scratch/pull.md) | | +| [**hardis:scratch:push**](hardis/scratch/push.md) | | ### hardis:source -| Command | Title | -|:--------------------------------------------------------|:------------------------------------------------------------------------------------------------| -| [**hardis:source:deploy**](hardis/source/deploy.md) | sfdx-hardis wrapper for sfdx force:source:deploy that displays tips to solve deployment errors. | -| [**hardis:source:push**](hardis/source/push.md) | sfdx-hardis wrapper for sfdx force:source:push that displays tips to solve deployment errors. | -| [**hardis:source:retrieve**](hardis/source/retrieve.md) | sfdx-hardis wrapper for sfdx force:source:retrieve | +| Command | Title | +|:--------------------------------------------------------|:------| +| [**hardis:source:deploy**](hardis/source/deploy.md) | | +| [**hardis:source:push**](hardis/source/push.md) | | +| [**hardis:source:retrieve**](hardis/source/retrieve.md) | | ### hardis:work -| Command | Title | -|:----------------------------------------------------------------|:---------------------| -| [**hardis:work:new**](hardis/work/new.md) | New work task | -| [**hardis:work:refresh**](hardis/work/refresh.md) | Refresh work task | -| [**hardis:work:resetselection**](hardis/work/resetselection.md) | Select again | -| [**hardis:work:save**](hardis/work/save.md) | Save work task | -| [**hardis:work:ws**](hardis/work/ws.md) | WebSocket operations | +| Command | Title | +|:----------------------------------------------------------------|:------| +| [**hardis:work:new**](hardis/work/new.md) | | +| [**hardis:work:refresh**](hardis/work/refresh.md) | | +| [**hardis:work:resetselection**](hardis/work/resetselection.md) | | +| [**hardis:work:save**](hardis/work/save.md) | | +| [**hardis:work:ws**](hardis/work/ws.md) | | + +### hello:world + +| Command | Title | +|:----------------------------------|:------| +| [**hello:world**](hello/world.md) | | diff --git a/docs/salesforce-ci-cd-config-cleaning.md b/docs/salesforce-ci-cd-config-cleaning.md index 5a49a43f1..e1a3f6c95 100644 --- a/docs/salesforce-ci-cd-config-cleaning.md +++ b/docs/salesforce-ci-cd-config-cleaning.md @@ -22,7 +22,7 @@ ___ Salesforce CI/CD Pipelines does not natively work without many manual operations to update the XML... so the deployments passes ! -sfdx-hardis provides a set of commands to automate those boring XML updates that can be called every time a user [prepares a merge request](salesforce-ci-cd-publish-task.md#prepare-merge-request) using command [sfdx hardis:work:save](https://sfdx-hardis.cloudity.com/hardis/work/save/) +sfdx-hardis provides a set of commands to automate those boring XML updates that can be called every time a user [prepares a merge request](salesforce-ci-cd-publish-task.md#prepare-merge-request) using command [sf hardis:work:save](https://sfdx-hardis.cloudity.com/hardis/work/save/) Here is the list of available automated cleanings, that can also be called manually using command ![](assets/images/btn-clean-sources.jpg) diff --git a/docs/salesforce-ci-cd-packaging.md b/docs/salesforce-ci-cd-packaging.md index 865795aa2..6647ccb5b 100644 --- a/docs/salesforce-ci-cd-packaging.md +++ b/docs/salesforce-ci-cd-packaging.md @@ -16,7 +16,7 @@ sfdx-hardis menus allow to create new packages and new package versions, but to - Instructions in [Salesforce Documentation](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_dev2gp_create_namespace.htm) -- Then you need a sfdx-hardis flavored sfdx project, that you can create with `sfdx hardis:project:create`, using scratch orgs only option. +- Then you need a sfdx-hardis flavored sfdx project, that you can create with `sf hardis:project:create`, using scratch orgs only option. - Last, create your package folder (ex: `my-package`) at the root of the repository, following the same structure than `force-app`: it will contain your package content. @@ -120,9 +120,9 @@ Once you merged your PR in packaging branch (after checking the control jobs are To promote a package version, run the following command -`sfdx force:package:version:promote -p PACKAGE_VERSION_ID` +`sf hardis:package:version:promote` -Example: `sfdx force:package:version:promote -p 04t7S000000gYp7QAG` +Example: `sf hardis:package:version:promote` Note: When later you will **create a new scratch org** in a new development branch, if you have issues, just increment again the `versionNumber` in `sfdx-project.json` diff --git a/docs/salesforce-ci-cd-publish-task.md b/docs/salesforce-ci-cd-publish-task.md index 4a817b12d..4d2d12d8b 100644 --- a/docs/salesforce-ci-cd-publish-task.md +++ b/docs/salesforce-ci-cd-publish-task.md @@ -24,7 +24,7 @@ _The following video shows how to perform theses operations_ ### Retrieve metadatas -If you made updates on your org that you have not pulled yet, Use command ![Pull from org button](assets/images/btn-pull-from-org.jpg) to **pull your latest updates in local files** +If you made updates on your org that you have not pulled yet, Use command [![Pull from org button](assets/images/btn-pull-from-org.jpg)](https://sfdx-hardis.cloudity.com/hardis/scratch/pull/) to **pull your latest updates in local files** If you updated config elements that you do not see in your local files, you may discuss with your release manager to [automate force retrieve metadatas](salesforce-ci-cd-retrieve.md) diff --git a/docs/salesforce-ci-cd-retrieve.md b/docs/salesforce-ci-cd-retrieve.md index 79e6699e5..582762ace 100644 --- a/docs/salesforce-ci-cd-retrieve.md +++ b/docs/salesforce-ci-cd-retrieve.md @@ -6,7 +6,7 @@ description: Learn how to retrieve updated metadatas when force:source:pull forg ## Automated force retrieve -It happens that when calling [sfdx hardis:scratch:pull](https://sfdx-hardis.cloudity.com/hardis/scratch/pull/), some elements are not retrieved. +It happens that when calling [sf hardis:scratch:pull](https://sfdx-hardis.cloudity.com/hardis/scratch/pull/), some elements are not retrieved. The most usual cases are updates on: diff --git a/docs/salesforce-ci-cd-setup-auth.md b/docs/salesforce-ci-cd-setup-auth.md index b5879ca75..8141df58b 100644 --- a/docs/salesforce-ci-cd-setup-auth.md +++ b/docs/salesforce-ci-cd-setup-auth.md @@ -11,7 +11,7 @@ To automate [deployments from major branches to their related org](salesforce-ci Note: _You need [openssl](https://www.openssl.org/) installed on your computer (available in `Git bash`)_ - Remain in your initialization branch `cicd`, or a sub branch of your lowest level major branch (usually `integration`) -- For each major branch to link to an org, run the sfdx-hardis command **Configuration ->** ![Configure Org CI Authentication](assets/images/btn-configure-ci-auth.jpg) (`sfdx hardis:project:configure:auth`) +- For each major branch to link to an org, run the sfdx-hardis command **Configuration ->** ![Configure Org CI Authentication](assets/images/btn-configure-ci-auth.jpg) (`sf hardis:project:configure:auth`) For example, run the command for `integration`, `uat`, `preprod` and `production` major branches. @@ -49,7 +49,7 @@ If you are **using scratch orgs**, you need to also **configure authentication f To do that, run the following command ```shell -sfdx hardis:project:configure:auth --devhub +sf hardis:project:configure:auth --devhub ``` diff --git a/docs/salesforce-ci-cd-setup-existing-org.md b/docs/salesforce-ci-cd-setup-existing-org.md index bf352054d..2f3003293 100644 --- a/docs/salesforce-ci-cd-setup-existing-org.md +++ b/docs/salesforce-ci-cd-setup-existing-org.md @@ -24,7 +24,7 @@ If you want to go for a **full init setup**, follow the steps below ! - Run the following command that will retrieve locally all the metadatas of production org -`sfdx hardis:org:retrieve:sources:dx --shape -u YOURSOURCEORGUSERNAME` +`sf hardis:org:retrieve:sources:dx --shape -u YOURSOURCEORGUSERNAME` - In case you get an error: - Run the generate package xml command : [hardis:org:generate:packagexmlfull](https://sfdx-hardis.cloudity.com/hardis/org/generate/packagexmlfull/) @@ -33,7 +33,7 @@ If you want to go for a **full init setup**, follow the steps below ! Example : -- `sfdx hardis:org:generate:packagexmlfull --targetusername nico@example.com --outputfile ./packagexmlfull.xml` +- `sf hardis:org:generate:packagexmlfull --targetusername nico@example.com --outputfile ./packagexmlfull.xml` - Remove Document part on packagexmlfull.xml ```xml @@ -43,7 +43,7 @@ Example : Document ``` -- `sfdx hardis:source:retrieve -x ./packagexmlfull.xml` +- `sf hardis:source:retrieve -x ./packagexmlfull.xml` @@ -59,7 +59,7 @@ Proceed to the following steps to automatically remove many of them, then procee Run the following command to delete all elements with a namespace. ```shell -sfdx hardis:project:clean:manageditems --namespace SOMENAMESPACE +sf hardis:project:clean:manageditems --namespace SOMENAMESPACE ``` ### Remove (hidden) files @@ -67,7 +67,7 @@ sfdx hardis:project:clean:manageditems --namespace SOMENAMESPACE Some items have no namespace but are managed anyway, and contain `(hidden)`, so they must me deleted with the following command. ```shell -sfdx hardis:project:clean:hiddenitems +sf hardis:project:clean:hiddenitems ``` ### Remove empty items @@ -75,7 +75,7 @@ sfdx hardis:project:clean:hiddenitems Some files are empty and do not need to be kept in repository, remove them using the following command. ```shell -sfdx hardis:project:clean:emptyitems +sf hardis:project:clean:emptyitems ``` ### Standard objects without custom @@ -85,7 +85,7 @@ The retrieve command pulled all standard objects and fields. Those which has never been customized do not need to remain in repository, delete them using the following command (that can take some time) ```shell -sfdx hardis:project:clean:standarditems +sf hardis:project:clean:standarditems ``` ## Manual Metadata Cleaning @@ -111,7 +111,7 @@ Manually delete files (or even folders) that are maintained directly in producti Run the following command to retrieve packages installed on production org -`sfdx hardis:org:retrieve:packageconfig -u YOUR_PROD_ORG_USER` +`sf hardis:org:retrieve:packageconfig -u YOUR_PROD_ORG_USER` This will update file **config/.sfdx-hardis.yml** diff --git a/docs/salesforce-ci-cd-setup-init-project.md b/docs/salesforce-ci-cd-setup-init-project.md index 71b493a49..a3ae183fe 100644 --- a/docs/salesforce-ci-cd-setup-init-project.md +++ b/docs/salesforce-ci-cd-setup-init-project.md @@ -8,7 +8,7 @@ description: Learn how to initialize a Salesforce DX Project for CI/CD - Create a new git branch named **cicd** under your lower major branch (usually **integration**) -- Run command **Configuration ->** ![Create new sfdx project](assets/images/btn-create-project.jpg) (`sfdx hardis:project:create`) and select options to create a new sfdx-hardis project. +- Run command **Configuration ->** ![Create new sfdx project](assets/images/btn-create-project.jpg) (`sf hardis:project:create`) and select options to create a new sfdx-hardis project. - Open file **manifest/package.xml** and replace the content by the following code diff --git a/docs/salesforce-ci-cd-setup-integration-azure.md b/docs/salesforce-ci-cd-setup-integration-azure.md index a1df6587b..26f474038 100644 --- a/docs/salesforce-ci-cd-setup-integration-azure.md +++ b/docs/salesforce-ci-cd-setup-integration-azure.md @@ -34,7 +34,7 @@ Everytime you will make a pull request, the CI job will post its result as comme Notes: -- This integration works with sfdx-hardis pipeline, but also on home-made pipelines, just call [sfdx hardis:source:deploy](https://sfdx-hardis.cloudity.com/hardis/source/deploy/) instead of `sfdx force:source:deploy` ! +- This integration works with sfdx-hardis pipeline, but also on home-made pipelines, just call [sf hardis:project:deploy:start](https://sfdx-hardis.cloudity.com/hardis/project/deploy/start/) instead of `sf project:deploy:start` ! - This integration use the following variables: - SYSTEM_ACCESSTOKEN: $(System.AccessToken) diff --git a/docs/salesforce-ci-cd-setup-integration-bitbucket.md b/docs/salesforce-ci-cd-setup-integration-bitbucket.md index 164c07323..06b9b5c56 100644 --- a/docs/salesforce-ci-cd-setup-integration-bitbucket.md +++ b/docs/salesforce-ci-cd-setup-integration-bitbucket.md @@ -24,7 +24,7 @@ Everytime you will make a pull request, the CI job will post its result as a com Notes: -- This integration works with sfdx-hardis pipeline, but also on home-made pipelines, just call [sfdx hardis:source:deploy](https://sfdx-hardis.cloudity.com/hardis/source/deploy/) instead of `sfdx force:source:deploy` ! +- This integration works with sfdx-hardis pipeline, but also on home-made pipelines, just call [sf hardis:project:deploy:start](https://sfdx-hardis.cloudity.com/hardis/project/deploy/start/) instead of `sf project:deploy:start` ! - This integration uses the following variables: - CI_SFDX_HARDIS_BITBUCKET_TOKEN diff --git a/docs/salesforce-ci-cd-setup-integration-github.md b/docs/salesforce-ci-cd-setup-integration-github.md index 5c0d7ebd3..b20f5252b 100644 --- a/docs/salesforce-ci-cd-setup-integration-github.md +++ b/docs/salesforce-ci-cd-setup-integration-github.md @@ -32,7 +32,7 @@ Everytime you will make a Pull Request, the CI job will post its result as comme Notes: -- This integration works with sfdx-hardis pipeline, but also on home-made pipelines, just call [sfdx hardis:source:deploy](https://sfdx-hardis.cloudity.com/hardis/source/deploy/) instead of `sfdx force:source:deploy` ! +- This integration works with sfdx-hardis pipeline, but also on home-made pipelines, just call [sf hardis:project:deploy:start](https://sfdx-hardis.cloudity.com/hardis/project/deploy/start/) instead of `sf project:deploy:start` ! - This integration use the following variables: diff --git a/docs/salesforce-ci-cd-setup-integration-gitlab.md b/docs/salesforce-ci-cd-setup-integration-gitlab.md index c9a215f90..a3c272d5a 100644 --- a/docs/salesforce-ci-cd-setup-integration-gitlab.md +++ b/docs/salesforce-ci-cd-setup-integration-gitlab.md @@ -32,7 +32,7 @@ Everytime you will make a merge request, the CI job will post its result as comm Notes: -- This integration works with sfdx-hardis pipeline, but also on home-made pipelines, just call [sfdx hardis:source:deploy](https://sfdx-hardis.cloudity.com/hardis/source/deploy/) instead of `sfdx force:source:deploy` ! +- This integration works with sfdx-hardis pipeline, but also on home-made pipelines, just call [sf hardis:project:deploy:start](https://sfdx-hardis.cloudity.com/hardis/project/deploy/start/) instead of `sf project:deploy:start` ! - This integration use the following variables: - CI_SFDX_HARDIS_GITLAB_TOKEN diff --git a/docs/salesforce-ci-cd-use-install.md b/docs/salesforce-ci-cd-use-install.md index 8ea06a4f6..3365f4791 100644 --- a/docs/salesforce-ci-cd-use-install.md +++ b/docs/salesforce-ci-cd-use-install.md @@ -37,7 +37,6 @@ _See tutorial_ > - Salesforce DX plugins > - [sfdx-hardis](https://github.com/hardisgroupcom/sfdx-hardis) > - [Salesforce Data Move Utility](https://github.com/forcedotcom/SFDX-Data-Move-Utility) -> - [SFDX Essentials](https://github.com/nvuillam/sfdx-essentials) > - [SFDX Git Delta](https://github.com/scolladon/sfdx-git-delta) > - [Texei Sfdx Plugin](https://github.com/texei/texei-sfdx-plugin) diff --git a/docs/salesforce-ci-cd-work-on-task-development.md b/docs/salesforce-ci-cd-work-on-task-development.md index 58b947d94..8a0f84d2f 100644 --- a/docs/salesforce-ci-cd-work-on-task-development.md +++ b/docs/salesforce-ci-cd-work-on-task-development.md @@ -10,7 +10,7 @@ description: Learn how to develop on a task with a tracked sandbox or a scratch - You can update code and XML metadatas using VsCode IDE - When you need to upload the updates to your org, use command ![Push to org button](assets/images/btn-push-to-org.jpg) -- If you made updates directly on your org, use command ![Pull from org button](assets/images/btn-pull-from-org.jpg) to retrieve into local files the updates that you performed online with point & click +- If you made updates directly on your org, use command [![Pull from org button](assets/images/btn-pull-from-org.jpg)](https://sfdx-hardis.cloudity.com/hardis/scratch/pull/) to retrieve into local files the updates that you performed online with point & click - Once you have finished, you can [publish your task](salesforce-ci-cd-publish-task.md) ### Recommendations diff --git a/docs/salesforce-deployment-assistant-error-list.md b/docs/salesforce-deployment-assistant-error-list.md index 3a43b1cc9..1b1356c2d 100644 --- a/docs/salesforce-deployment-assistant-error-list.md +++ b/docs/salesforce-deployment-assistant-error-list.md @@ -12,25 +12,29 @@ See how to [setup sfdx-hardis deployment assistant](salesforce-deployment-assist If you see a deployment error which is not here yet, please [add it in this file](https://github.com/hardisgroupcom/sfdx-hardis/blob/main/src/common/utils/deployTipsList.ts) :) -## API Version error +## [API Version error](sf-deployment-assistant/API-Version-error.md) -- `Error (.*) The (.*) apiVersion can't be "([0-9]+)"` +**Detection** -**Resolution tip** +- RegExp: `Error (.*) The (.*) apiVersion can't be "([0-9]+)"` + +**Resolution** ```shell {1} metadata has probably been created/updated in a sandbox already upgraded to next platform version (ex: Sandbox in Summer'23 and Production in Spring'23) - First, try to update the api version in the XML of {1} metadata file (decrement the number in {3}.0) -- If it still doesn't work because the metadata structure has changed between version, you may try a force:source:retrieve of the metadata by forcing --apiversion at the end of the command. +- If it still doesn't work because the metadata structure has changed between version, you may try a sf project:retrieve:start of the metadata by forcing --api-version at the end of the command. ``` --- -## Allow deployment with pending Apex Jobs +## [Allow deployment with pending Apex Jobs](sf-deployment-assistant/Allow-deployment-with-pending-Apex-Jobs.md) + +**Detection** -- `You can bypass this error by allowing deployments with Apex jobs in the Deployment Settings page in Setup.` +- String: `You can bypass this error by allowing deployments with Apex jobs in the Deployment Settings page in Setup.` -**Resolution tip** +**Resolution** ```shell Go to target org, in Setup -> Deployment Settings -> Activate option "Allow deployments of components when corresponding Apex jobs are pending or in progress." @@ -38,11 +42,13 @@ Go to target org, in Setup -> Deployment Settings -> Activate option "Allow depl ``` --- -## Can not change field type to a formula field +## [Can not change field type to a formula field](sf-deployment-assistant/Can-not-change-field-type-to-a-formula-field.md) -- `Error (.*) Cannot update a field from a Formula to something else` +**Detection** -**Resolution tip** +- RegExp: `Error (.*) Cannot update a field from a Formula to something else` + +**Resolution** ```shell You need to manually delete or rename the field in the target org to allow the deployment to pass @@ -51,11 +57,13 @@ You need to manually delete or rename the field in the target org to allow the d ``` --- -## Can not change type due to existing data +## [Can not change type due to existing data](sf-deployment-assistant/Can-not-change-type-due-to-existing-data.md) + +**Detection** -- `Error (.*) Cannot change type due to existing data` +- RegExp: `Error (.*) Cannot change type due to existing data` -**Resolution tip** +**Resolution** ```shell It is usually not recommended to change types of fields, but if it's really necessary you can: @@ -67,11 +75,13 @@ It is usually not recommended to change types of fields, but if it's really nece ``` --- -## Can not change field type with picklist +## [Can not change field type with picklist](sf-deployment-assistant/Can-not-change-field-type-with-picklist.md) -- `Error (.*) Cannot change which global value set this picklist uses` +**Detection** -**Resolution tip** +- RegExp: `Error (.*) Cannot change which global value set this picklist uses` + +**Resolution** ```shell You probably updated the type of field {1}, and Salesforce does not allows that with deployments. You can: @@ -81,12 +91,14 @@ You probably updated the type of field {1}, and Salesforce does not allows that ``` --- -## Can not delete custom field +## [Can not delete custom field](sf-deployment-assistant/Can-not-delete-custom-field.md) + +**Detection** -- `This (.*) is referenced elsewhere in salesforce.com` -- `Le champ personnalisé (.*) est utilisé dans (.*)` +- RegExp: `This (.*) is referenced elsewhere in salesforce.com` +- RegExp: `Le champ personnalisé (.*) est utilisé dans (.*)` -**Resolution tip** +**Resolution** ```shell Custom field {1} can not be deleted because it is used elsewhere. Remove its references ans try again @@ -94,11 +106,13 @@ THIS MAY BE A FALSE POSITIVE if you are just testing the deployment, as destruct ``` --- -## Can not delete record type +## [Can not delete record type](sf-deployment-assistant/Can-not-delete-record-type.md) + +**Detection** -- `Error (.*) Cannot delete record type through API` +- RegExp: `Error (.*) Cannot delete record type through API` -**Resolution tip** +**Resolution** ```shell You need to manually delete record type {1} in target org @@ -107,26 +121,30 @@ You need to manually delete record type {1} in target org ``` --- -## Can not find folder +## [Can not find folder](sf-deployment-assistant/Can-not-find-folder.md) -- `Error (.*) Cannot find folder:(.*)` +**Detection** -**Resolution tip** +- RegExp: `Error (.*) Cannot find folder:(.*)` + +**Resolution** ```shell Folder {2} is missing. - If folder {2} is existing in sources, add it in related package.xml -- If folder {2} is not existing in DX sources, please use sfdx hardis:project:clean:retrievefolders to retrieve it +- If folder {2} is not existing in DX sources, please use sf hardis:project:clean:retrievefolders to retrieve it - If both previous solutions did not work, go create manually folder {2} in target org ``` --- -## Can not find user +## [Can not find user](sf-deployment-assistant/Can-not-find-user.md) + +**Detection** -- `Error (.*) Cannot find a user that matches any of the following usernames` +- RegExp: `Error (.*) Cannot find a user that matches any of the following usernames` -**Resolution tip** +**Resolution** ```shell You made reference to username(s) in {1}, and those users probably do not exist in target org. @@ -144,11 +162,13 @@ Example of XML you have to remove in {1}: ``` --- -## Can not find user (2) +## [Can not find user (2)](sf-deployment-assistant/Can-not-find-user--2-.md) + +**Detection** -- `Error (.*) In field: (.*) - no User named (.*) found` +- RegExp: `Error (.*) In field: (.*) - no User named (.*) found` -**Resolution tip** +**Resolution** ```shell You made reference to username {3} in {1}, and it probably does not exist in the target org. @@ -158,11 +178,13 @@ You made reference to username {3} in {1}, and it probably does not exist in the ``` --- -## Cannot update a field to a Summary from something else +## [Cannot update a field to a Summary from something else](sf-deployment-assistant/Cannot-update-a-field-to-a-Summary-from-something-else.md) -- `Error (.*) Cannot update a field to a (.*) from something else` +**Detection** -**Resolution tip** +- RegExp: `Error (.*) Cannot update a field to a (.*) from something else` + +**Resolution** ```shell You probably updated the type of field {1} to type {2}, and Salesforce does not allows that with deployments. You can: @@ -172,11 +194,26 @@ You probably updated the type of field {1} to type {2}, and Salesforce does not ``` --- -## Condition missing reference +## [Change Matching Rule](sf-deployment-assistant/Change-Matching-Rule.md) + +**Detection** -- `Error (.*) field integrity exception: unknown \(A condition has a reference to (.*), which doesn't exist.\)` +- RegExp: `Error (.*) Before you change a matching rule, you must deactivate it` -**Resolution tip** +**Resolution** + +```shell +To be able to deploy, you must go in target org setup to manually deactivate matching rule {1} +``` + +--- +## [Condition missing reference](sf-deployment-assistant/Condition-missing-reference.md) + +**Detection** + +- RegExp: `Error (.*) field integrity exception: unknown \(A condition has a reference to (.*), which doesn't exist.\)` + +**Resolution** ```shell There is a reference to {2} in {1}, and {2} is not found. You can either: @@ -186,27 +223,35 @@ There is a reference to {2} in {1}, and {2} is not found. You can either: ``` --- -## Custom object not found +## [Custom object not found](sf-deployment-assistant/Custom-object-not-found.md) + +**Detection** -- `Error (.*) In field: field - no CustomObject named (.*) found` +- RegExp: `Error (.*) In field: field - no CustomObject named (.*) found` -**Resolution tip** +**Resolution** ```shell A reference to a custom object {2} is not found in {1}: - If you renamed the custom object, do a search/replace in sources with previous object name and new object name - If you deleted the custom object, or if you don't want to deploy it, do a search on the custom object name, and remove XML elements referencing it - If the object should exist, make sure it is in force-app/main/default/objects and that the object name is in manifest/package.xml in CustomObject section -You may also have a look to command sfdx hardis:project:clean:references +You may also have a look to command sf hardis:project:clean:references ``` --- -## Custom field not found +## [Custom field not found](sf-deployment-assistant/Custom-field-not-found.md) + +**Detection** -- `Error (.*) In field: (.*) - no CustomField named (.*)\.(.*) found` +- RegExp: `Error (.*) In field: (.*) - no CustomField named (.*)\.(.*) found` -**Resolution tip** +**Examples** + +- `Error PS_Admin In field: field - no CustomField named User.expcloud__Portal_Username__c found` + +**Resolution** ```shell A reference to a custom field {3}.{4} is not found in {1}: @@ -214,17 +259,19 @@ A reference to a custom field {3}.{4} is not found in {1}: - If you deleted {3}.{4}, or if you don't want to deploy it, do a search on {4} in all sources, and remove all XML elements referring to {3}.{4} (except in destructiveChanges.xml) - If {3}.{4} should exist, make sure it is in force-app/main/default/objects/{3}/fields and that {3}.{4} is in manifest/package.xml in CustomField section - If {3}.{4} is standard, the error is because {3}.{4} is not available in the org you are trying to deploy to. You can: - - Remove the reference to {4} in the XML of {1} ( maybe sfdx hardis:project:clean:references can clean automatically for you ! ) + - Remove the reference to {4} in the XML of {1} ( maybe sf hardis:project:clean:references can clean automatically for you ! ) - Activate the required features/license in the target org ``` --- -## Mandatory custom field can not be in a profile/permission set +## [Mandatory custom field can not be in a profile/permission set](sf-deployment-assistant/Mandatory-custom-field-can-not-be-in-a-profile-permission-set.md) + +**Detection** -- `Error (.*) You cannot deploy to a required field: (.*)` +- RegExp: `Error (.*) You cannot deploy to a required field: (.*)` -**Resolution tip** +**Resolution** ```shell @@ -239,11 +286,13 @@ Example of element to delete: ``` --- -## Custom metadata entry not found +## [Custom metadata entry not found](sf-deployment-assistant/Custom-metadata-entry-not-found.md) -- `Error (.*) In field: (.*) - no CustomMetadata named (.*) found` +**Detection** -**Resolution tip** +- RegExp: `Error (.*) In field: (.*) - no CustomMetadata named (.*) found` + +**Resolution** ```shell A reference to a custom metadata {3} of type {2} is not found in {1}: @@ -253,11 +302,26 @@ A reference to a custom metadata {3} of type {2} is not found in {1}: ``` --- -## Missing Data Category Group +## [Expired Access / Refresh Token](sf-deployment-assistant/Expired-Access---Refresh-Token.md) + +**Detection** + +- String: `expired access/refresh token` + +**Resolution** + +```shell +Run command "Select another org" from Status panel (or sf hardis:org:select) to authenticate again to your org +``` + +--- +## [Missing Data Category Group](sf-deployment-assistant/Missing-Data-Category-Group.md) -- `Error (.*) In field: DeveloperName - no DataCategoryGroup named (.*) found` +**Detection** -**Resolution tip** +- RegExp: `Error (.*) In field: DeveloperName - no DataCategoryGroup named (.*) found` + +**Resolution** ```shell If Data Category Group {2} is not existing yet in target org, you might need to: @@ -267,11 +331,13 @@ If Data Category Group {2} is not existing yet in target org, you might need to: ``` --- -## Dependent class is invalid and needs recompilation +## [Dependent class is invalid and needs recompilation](sf-deployment-assistant/Dependent-class-is-invalid-and-needs-recompilation.md) + +**Detection** -- `Error (.*) Dependent class is invalid and needs recompilation` +- RegExp: `Error (.*) Dependent class is invalid and needs recompilation` -**Resolution tip** +**Resolution** ```shell Solve the other errors and this one will disappear ! @@ -279,47 +345,55 @@ Solve the other errors and this one will disappear ! ``` --- -## Duplicate value Platform Action Id List +## [Duplicate value Platform Action Id List](sf-deployment-assistant/Duplicate-value-Platform-Action-Id-List.md) + +**Detection** -- `duplicate value found: PlatformActionListId duplicates value on record with id` +- String: `duplicate value found: PlatformActionListId duplicates value on record with id` -**Resolution tip** +**Resolution** ```shell There are probably issue with conflict management. Open the XML of the source item, and replace all numbers to make an ascending order, starting with 0 ``` --- -## Duplicate label +## [Duplicate label](sf-deployment-assistant/Duplicate-label.md) -- `Error (.*) Duplicate label: (.*)` +**Detection** -**Resolution tip** +- RegExp: `Error (.*) Duplicate label: (.*)` + +**Resolution** ```shell You probably renamed the picklist API name for {2}. Please update manually the picklist {1} in the target org to avoid to have a duplicate label ``` --- -## Missing e-mail template +## [Missing e-mail template](sf-deployment-assistant/Missing-e-mail-template.md) + +**Detection** -- `In field: template - no EmailTemplate named (.*) found` +- RegExp: `In field: template - no EmailTemplate named (.*) found` -**Resolution tip** +**Resolution** ```shell An email template should be present in the sources. To retrieve it, you can run: -sfdx force:source:retrieve -m EmailTemplate:{1} -u YOUR_ORG_USERNAME +sf project retrieve start -m EmailTemplate:{1} -o YOUR_ORG_USERNAME ``` --- -## Empty source items +## [Empty source items](sf-deployment-assistant/Empty-source-items.md) + +**Detection** -- `Required field is missing: sharingOwnerRules` -- `Required field is missing: standardValue` -- `Required field is missing: valueTranslation` +- String: `Required field is missing: sharingOwnerRules` +- String: `Required field is missing: standardValue` +- String: `Required field is missing: valueTranslation` -**Resolution tip** +**Resolution** ```shell You probably retrieved empty items, that must not be included within the SFDX project @@ -327,11 +401,13 @@ To remove them, please run sfdx:hardis:project:clean:emptyitems ``` --- -## Enable CRM Analytics +## [Enable CRM Analytics](sf-deployment-assistant/Enable-CRM-Analytics.md) -- `It should be created by enabling the CRM Analytics Cloud preference` +**Detection** -**Resolution tip** +- String: `It should be created by enabling the CRM Analytics Cloud preference` + +**Resolution** ```shell You must enable CRM Analytics (ex Wave, Einstein Analytics & Tableau CRM) in the target org. @@ -339,11 +415,13 @@ You probably also need to add CRM Analytics Admin Permission Set assignment to t ``` --- -## Error parsing file +## [Error parsing file](sf-deployment-assistant/Error-parsing-file.md) + +**Detection** -- `Error (.*) Error parsing file: (.*) ` +- RegExp: `Error (.*) Error parsing file: (.*)` -**Resolution tip** +**Resolution** ```shell There has been an error parsing the XML file of {1}: {2} @@ -351,11 +429,13 @@ There has been an error parsing the XML file of {1}: {2} ``` --- -## Formula picklist field issue +## [Formula picklist field issue](sf-deployment-assistant/Formula-picklist-field-issue.md) + +**Detection** -- `Field:(.*) must not be Required` +- RegExp: `Field:(.*) must not be Required` -**Resolution tip** +**Resolution** ```shell You probably made read only field {1} that was required before. @@ -363,11 +443,13 @@ Find field {1} in the layout source XML, then replace Required by Readonly ``` --- -## Field not available for element +## [Field not available for element](sf-deployment-assistant/Field-not-available-for-element.md) -- `Field (.*) is not available for` +**Detection** -**Resolution tip** +- RegExp: `Field (.*) is not available for` + +**Resolution** ```shell You probably changed the type of field {1}. @@ -375,11 +457,13 @@ Find field {1} in the source XML, and remove the section using it ``` --- -## Formula picklist field issue +## [Formula picklist field issue](sf-deployment-assistant/Formula-picklist-field-issue.md) + +**Detection** -- `Les champs de liste de sélection sont pris en charge uniquement dans certaines fonctions.` +- String: `Les champs de liste de sélection sont pris en charge uniquement dans certaines fonctions.` -**Resolution tip** +**Resolution** ```shell You probably changed the type of a field that is used in a formula. @@ -388,22 +472,26 @@ More details at https://help.salesforce.com/articleView?id=sf.tips_on_building_f ``` --- -## Flow must be deleted manually +## [Flow must be deleted manually](sf-deployment-assistant/Flow-must-be-deleted-manually.md) + +**Detection** -- `.flow (.*) insufficient access rights on cross-reference id` +- RegExp: `.flow (.*) insufficient access rights on cross-reference id` -**Resolution tip** +**Resolution** ```shell Flow {1} can not be deleted using deployments, please delete it manually in the target org using menu Setup -> Flows , context menu on {1} -> View details and versions -> Deactivate all versions -> Delete flow ``` --- -## Insufficient access rights on cross-reference id +## [Insufficient access rights on cross-reference id](sf-deployment-assistant/Insufficient-access-rights-on-cross-reference-id.md) -- `Error (.*) insufficient access rights on cross-reference id` +**Detection** -**Resolution tip** +- RegExp: `Error (.*) insufficient access rights on cross-reference id` + +**Resolution** ```shell - If {1} is a Flow, it can not be deleted using deployments, please delete it manually in the target org using menu Setup -> Flows , context menu on {1} -> View details and versions -> Deactivate all versions -> Delete flow @@ -411,22 +499,26 @@ Flow {1} can not be deleted using deployments, please delete it manually in the ``` --- -## Invalid formula grouping context +## [Invalid formula grouping context](sf-deployment-assistant/Invalid-formula-grouping-context.md) + +**Detection** -- `Invalid custom summary formula definition: You must select a grouping context to use any report summary function` +- String: `Invalid custom summary formula definition: You must select a grouping context to use any report summary function` -**Resolution tip** +**Resolution** ```shell You need to update your Report definition. See workaround here -> https://salesforce.stackexchange.com/questions/294850/grouping-error-with-prevgroupval-function ``` --- -## Invalid report type +## [Invalid report type](sf-deployment-assistant/Invalid-report-type.md) + +**Detection** -- `Error (.*) invalid report type` +- RegExp: `Error (.*) invalid report type` -**Resolution tip** +**Resolution** ```shell Report type is missing for report {1} @@ -435,25 +527,29 @@ Report type is missing for report {1} ``` --- -## Invalid scope:Mine, not allowed +## [Invalid scope:Mine, not allowed](sf-deployment-assistant/Invalid-scope-Mine--not-allowed.md) -- `Invalid scope:Mine, not allowed` +**Detection** -**Resolution tip** +- String: `Invalid scope:Mine, not allowed` + +**Resolution** ```shell Replace Mine by Everything in the list view SFDX source XML. Have a look at this command to manage that automatically :) -https://sfdx-hardis.cloudity.com/hardis/org/fix/listviewmine/ +https://sfdx-hardis.cloudity.com/hardis/org/fix/listviewmine/ ``` --- -## Invalid field in related list +## [Invalid field in related list](sf-deployment-assistant/Invalid-field-in-related-list.md) + +**Detection** -- `Error (.*) Invalid field:(.*) in related list:(.*)` +- RegExp: `Error (.*) Invalid field:(.*) in related list:(.*)` -**Resolution tip** +**Resolution** ```shell Field {2} is unknown. You can: @@ -472,11 +568,13 @@ Example of XML to remove: ``` --- -## Invalid field for upsert +## [Invalid field for upsert](sf-deployment-assistant/Invalid-field-for-upsert.md) -- `Error (.*) Invalid field for upsert, must be an External Id custom or standard indexed field: (.*) \((.*)\)` +**Detection** -**Resolution tip** +- RegExp: `Error (.*) Invalid field for upsert, must be an External Id custom or standard indexed field: (.*) \((.*)\)` + +**Resolution** ```shell You tried to use field {2} for an upsert call in {1}. @@ -487,22 +585,26 @@ You tried to use field {2} for an upsert call in {1}. ``` --- -## Invalid type +## [Invalid type](sf-deployment-assistant/Invalid-type.md) + +**Detection** -- `Error (.*) Invalid type: (.*) \((.*)\)` +- RegExp: `Error (.*) Invalid type: (.*) \((.*)\)` -**Resolution tip** +**Resolution** ```shell Apex error in {1} with unknown type {2} at position {3}. If {2} is a class name, try to fix it, or maybe it is missing in the files or in package.xml ! ``` --- -## Campaign can not be updated +## [Campaign can not be updated](sf-deployment-assistant/Campaign-can-not-be-updated.md) -- `The object "Campaign" can't be updated` +**Detection** -**Resolution tip** +- String: `The object "Campaign" can't be updated` + +**Resolution** ```shell Add "MarketingUser" in project-scratch-def.json features @@ -510,12 +612,14 @@ If it is already done, you may manually check "MarketingUser" field on the scrat ``` --- -## Missing field MiddleName +## [Missing field MiddleName](sf-deployment-assistant/Missing-field-MiddleName.md) + +**Detection** -- `field MiddleName` -- `Variable does not exist: MiddleName` +- String: `field MiddleName` +- String: `Variable does not exist: MiddleName` -**Resolution tip** +**Resolution** ```shell MiddleNames must be activated in the target org. @@ -527,11 +631,13 @@ MiddleNames must be activated in the target org. ``` --- -## Missing field Suffix +## [Missing field Suffix](sf-deployment-assistant/Missing-field-Suffix.md) + +**Detection** -- `field Suffix` +- String: `field Suffix` -**Resolution tip** +**Resolution** ```shell Suffix must be activated in the target org. @@ -543,13 +649,15 @@ Suffix must be activated in the target org. ``` --- -## Missing field SyncedQuoteId +## [Missing field SyncedQuoteId](sf-deployment-assistant/Missing-field-SyncedQuoteId.md) -- `field SyncedQuoteId` -- `Error force-app/main/default/objects/Quote/Quote.object-meta.xml` -- `Error force-app/main/default/objects/Opportunity/fields/SyncedQuoteId.field-meta.xml` +**Detection** -**Resolution tip** +- String: `field SyncedQuoteId` +- String: `Error force-app/main/default/objects/Quote/Quote.object-meta.xml` +- String: `Error force-app/main/default/objects/Opportunity/fields/SyncedQuoteId.field-meta.xml` + +**Resolution** ```shell Quotes must be activated in the target org. @@ -561,12 +669,14 @@ Quotes must be activated in the target org. ``` --- -## Missing feature ContactToMultipleAccounts +## [Missing feature ContactToMultipleAccounts](sf-deployment-assistant/Missing-feature-ContactToMultipleAccounts.md) + +**Detection** -- `no CustomObject named AccountContactRelation found` -- `Invalid field:ACCOUNT.NAME in related list:RelatedContactAccountRelationList` +- String: `no CustomObject named AccountContactRelation found` +- String: `Invalid field:ACCOUNT.NAME in related list:RelatedContactAccountRelationList` -**Resolution tip** +**Resolution** ```shell Contacts to multiple accounts be activated in the target org. @@ -576,11 +686,13 @@ Contacts to multiple accounts be activated in the target org. ``` --- -## Missing feature Chatter Collaboration Group +## [Missing feature Chatter Collaboration Group](sf-deployment-assistant/Missing-feature-Chatter-Collaboration-Group.md) + +**Detection** -- `CollaborationGroup` +- String: `CollaborationGroup` -**Resolution tip** +**Resolution** ```shell Quotes must be activated in the target org. @@ -592,11 +704,13 @@ Quotes must be activated in the target org. ``` --- -## Missing feature Enhanced notes +## [Missing feature Enhanced notes](sf-deployment-assistant/Missing-feature-Enhanced-notes.md) -- `FeedItem.ContentNote` +**Detection** -**Resolution tip** +- String: `FeedItem.ContentNote` + +**Resolution** ```shell Enhanced Notes must be activated in the target org. @@ -608,11 +722,13 @@ Enhanced Notes must be activated in the target org. ``` --- -## Missing feature Ideas notes +## [Missing feature Ideas notes](sf-deployment-assistant/Missing-feature-Ideas-notes.md) + +**Detection** -- `Idea.InternalIdeasIdeaRecordType` +- String: `Idea.InternalIdeasIdeaRecordType` -**Resolution tip** +**Resolution** ```shell Ideas must be activated in the target org. @@ -624,11 +740,13 @@ Ideas must be activated in the target org. ``` --- -## Missing feature Live Agent +## [Missing feature Live Agent](sf-deployment-assistant/Missing-feature-Live-Agent.md) + +**Detection** -- `FeedItem.ContentNote` +- String: `FeedItem.ContentNote` -**Resolution tip** +**Resolution** ```shell Live Agent must be activated in the target org. @@ -637,24 +755,28 @@ Live Agent must be activated in the target org. ``` --- -## Missing feature Product Request +## [Missing feature Product Request](sf-deployment-assistant/Missing-feature-Product-Request.md) -- `ProductRequest` +**Detection** -**Resolution tip** +- String: `ProductRequest` + +**Resolution** ```shell ProductRequest object is not available in the target org. Maybe you would like to clean its references within Profiles / PS using the following command ? -sfdx hardis:project:clean:references , then select "ProductRequest references" +sf hardis:project:clean:references , then select "ProductRequest references" ``` --- -## Missing feature Social Customer Service +## [Missing feature Social Customer Service](sf-deployment-assistant/Missing-feature-Social-Customer-Service.md) + +**Detection** -- `SocialPersona.AreWeFollowing` +- String: `SocialPersona.AreWeFollowing` -**Resolution tip** +**Resolution** ```shell Social Custom Service must be activated in the target org. @@ -663,11 +785,13 @@ Social Custom Service must be activated in the target org. ``` --- -## Missing feature Translation Workbench +## [Missing feature Translation Workbench](sf-deployment-assistant/Missing-feature-Translation-Workbench.md) + +**Detection** -- `report-meta.xml(.*)filterlanguage` +- RegExp: `report-meta.xml(.*)filterlanguage` -**Resolution tip** +**Resolution** ```shell Translation workbench must be activated in the target org. @@ -680,11 +804,13 @@ Translation workbench must be activated in the target org. ``` --- -## Missing feature Opportunity Teams +## [Missing feature Opportunity Teams](sf-deployment-assistant/Missing-feature-Opportunity-Teams.md) -- `OpportunityTeam` +**Detection** -**Resolution tip** +- String: `OpportunityTeam` + +**Resolution** ```shell Opportunity Teams must be activated in the target org. @@ -696,11 +822,13 @@ Opportunity Teams must be activated in the target org. ``` --- -## Missing Feature Work.Com +## [Missing Feature Work.Com](sf-deployment-assistant/Missing-Feature-Work-Com.md) + +**Detection** -- `WorkBadgeDefinition` +- String: `WorkBadgeDefinition` -**Resolution tip** +**Resolution** ```shell Work.com feature must be activated in the target org. @@ -708,22 +836,26 @@ Work.com feature must be activated in the target org. ``` --- -## Missing multi-currency field +## [Missing multi-currency field](sf-deployment-assistant/Missing-multi-currency-field.md) + +**Detection** -- `A reference to a custom field (.*)CurrencyIsoCode` +- RegExp: `A reference to a custom field (.*)CurrencyIsoCode` -**Resolution tip** +**Resolution** ```shell You probably need to activate MultiCurrency (from Setup -> Company information) ``` --- -## Missing object referenced in package.xml +## [Missing object referenced in package.xml](sf-deployment-assistant/Missing-object-referenced-in-package-xml.md) -- `An object (.*) of type (.*) was named in package.xml, but was not found in zipped directory` +**Detection** -**Resolution tip** +- RegExp: `An object (.*) of type (.*) was named in package.xml, but was not found in zipped directory` + +**Resolution** ```shell You can either: @@ -732,11 +864,13 @@ You can either: ``` --- -## Missing Quick Action +## [Missing Quick Action](sf-deployment-assistant/Missing-Quick-Action.md) + +**Detection** -- `Error (.*) In field: QuickAction - no QuickAction named (.*) found` +- RegExp: `Error (.*) In field: QuickAction - no QuickAction named (.*) found` -**Resolution tip** +**Resolution** ```shell QuickAction {2} referred in {1} is unknown. You can either: @@ -750,25 +884,29 @@ QuickAction {2} referred in {1} is unknown. You can either: ``` --- -## Missing report +## [Missing report](sf-deployment-assistant/Missing-report.md) + +**Detection** -- `Error (.*) The (.*) report chart has a problem with the "reportName" field` +- RegExp: `Error (.*) The (.*) report chart has a problem with the "reportName" field` -**Resolution tip** +**Resolution** ```shell {1} is referring to unknown report {2}. To retrieve it, you can run: -- sfdx force:source:retrieve -m Report:{2} -u YOUR_ORG_USERNAME +- sf project retrieve start -m Report:{2} -o YOUR_ORG_USERNAME - If it fails, looks for the report folder and add it before report name to the retrieve command (ex: MYFOLDER/MYREPORTNAME) ``` --- -## Missing Sales Team +## [Missing Sales Team](sf-deployment-assistant/Missing-Sales-Team.md) -- `related list:RelatedAccountSalesTeam` +**Detection** -**Resolution tip** +- String: `related list:RelatedAccountSalesTeam` + +**Resolution** ```shell Account Teams must be activated in the target org. @@ -781,27 +919,31 @@ Account Teams must be activated in the target org. ``` --- -## sharing operation already in progress +## [sharing operation already in progress](sf-deployment-assistant/sharing-operation-already-in-progress.md) + +**Detection** -- `sharing operation already in progress` +- String: `sharing operation already in progress` -**Resolution tip** +**Resolution** ```shell You can not deploy multiple SharingRules at the same time. You can either: - Remove SharingOwnerRules and SharingRule from package.xml (so it becomes a manual operation) -- Use sfdx hardis:work:save to generate a deploymentPlan in .sfdx-hardis.json, +- Use sf hardis:work:save to generate a deploymentPlan in .sfdx-hardis.json, - If you are trying to create a scratch org, add DeferSharingCalc in features in project-scratch-def.json ``` --- -## Network issue +## [Network issue](sf-deployment-assistant/Network-issue.md) + +**Detection** -- `ECONNABORTED` -- `ECONNRESET` +- String: `ECONNABORTED` +- String: `ECONNRESET` -**Resolution tip** +**Resolution** ```shell The network connection has been aborted, this is a purely technical issue. @@ -809,11 +951,13 @@ Try again, and if you still see errors, check the status of Salesforce instance ``` --- -## Not available for deploy for this organization +## [Not available for deploy for this organization](sf-deployment-assistant/Not-available-for-deploy-for-this-organization.md) -- `Error (.*) Not available for deploy for this organization` +**Detection** -**Resolution tip** +- RegExp: `Error (.*) Not available for deploy for this organization` + +**Resolution** ```shell The user you use for deployments probably lacks of the rights (Profiles, Permission sets...) to manage {1}. @@ -821,11 +965,13 @@ The user you use for deployments probably lacks of the rights (Profiles, Permiss ``` --- -## Not valid sharing model +## [Not valid sharing model](sf-deployment-assistant/Not-valid-sharing-model.md) + +**Detection** -- `Error (.*) (.*) is not a valid sharing model for (.*) when (.*) sharing model is (.*)` +- RegExp: `Error (.*) (.*) is not a valid sharing model for (.*) when (.*) sharing model is (.*)` -**Resolution tip** +**Resolution** ```shell It seems that Sharing Models of {1} and {4} are not compatible in target org. @@ -836,11 +982,13 @@ It seems that Sharing Models of {1} and {4} are not compatible in target org. ``` --- -## Picklist sharing is not supported +## [Picklist sharing is not supported](sf-deployment-assistant/Picklist-sharing-is-not-supported.md) + +**Detection** -- `Picklist sharing is not supported` +- String: `Picklist sharing is not supported` -**Resolution tip** +**Resolution** ```shell You probably changed the type of a field. @@ -849,25 +997,29 @@ Go manually make the change in the target org, so the deployment will pass ``` --- -## Picklist value not found +## [Picklist value not found](sf-deployment-assistant/Picklist-value-not-found.md) -- `Picklist value: (.*) in picklist: (.*) not found` +**Detection** -**Resolution tip** +- RegExp: `Picklist value: (.*) in picklist: (.*) not found` + +**Resolution** ```shell Sources have references to value {1} of picklist {2} -- If picklist {2} is standard, add the picklist to sfdx sources by using "sfdx force:source:retrieve -m StandardValueSet:{2}", then save again +- If picklist {2} is standard, add the picklist to sfdx sources by using "sf project retrieve start -m StandardValueSet:{2}", then save again - Else, perform a search in all code of {1}, then remove XML tags referring to {1} (for example in record types metadatas) ``` --- -## Please choose a different name +## [Please choose a different name](sf-deployment-assistant/Please-choose-a-different-name.md) + +**Detection** -- `Error (.*) This (.*) already exists or has been previously used(.*)Please choose a different name.` +- RegExp: `Error (.*) This (.*) already exists or has been previously used(.*)Please choose a different name.` -**Resolution tip** +**Resolution** ```shell - Rename {1} in the target org, then try again the deployment. if it succeeds, delete the renamed item. @@ -876,11 +1028,13 @@ Sources have references to value {1} of picklist {2} ``` --- -## Missing profile default application +## [Missing profile default application](sf-deployment-assistant/Missing-profile-default-application.md) + +**Detection** -- `You can't remove the only default app from the profile.` +- String: `You can't remove the only default app from the profile.` -**Resolution tip** +**Resolution** ```shell You must have a default application for a profile. You can: @@ -895,26 +1049,30 @@ You must have a default application for a profile. You can: ``` --- -## CRM Analytics: A Recipe must specify a DataFlow +## [CRM Analytics: A Recipe must specify a DataFlow](sf-deployment-assistant/CRM-Analytics--A-Recipe-must-specify-a-DataFlow.md) -- `Error (.*) A Recipe must specify a Dataflow` +**Detection** -**Resolution tip** +- RegExp: `Error (.*) A Recipe must specify a Dataflow` + +**Resolution** ```shell You must include related WaveDataFlow {1} in sources (and probably in package.xml too). -To retrieve it, run: sfdx force:source:retrieve -m WaveDataFlow:{1} -u SOURCE_ORG_USERNAME -You can also retrieve all analytics sources in one shot using sfdx hardis:org:retrieve:source:analytics -u SOURCE_ORG_USERNAME +To retrieve it, run: sf project retrieve start -m WaveDataFlow:{1} -u SOURCE_ORG_USERNAME +You can also retrieve all analytics sources in one shot using sf hardis:org:retrieve:source:analytics -u SOURCE_ORG_USERNAME - https://salesforce.stackexchange.com/a/365453/33522 - https://help.salesforce.com/s/articleView?id=000319274&type=1 ``` --- -## Record Type not found +## [Record Type not found](sf-deployment-assistant/Record-Type-not-found.md) + +**Detection** -- `Error (.*) In field: recordType - no RecordType named (.*) found` +- RegExp: `Error (.*) In field: recordType - no RecordType named (.*) found` -**Resolution tip** +**Resolution** ```shell An unknown record type {2} is referenced in {1} @@ -924,11 +1082,13 @@ An unknown record type {2} is referenced in {1} ``` --- -## Objects rights on a role is below org default +## [Objects rights on a role is below org default](sf-deployment-assistant/Objects-rights-on-a-role-is-below-org-default.md) + +**Detection** -- `access level below organization default` +- String: `access level below organization default` -**Resolution tip** +**Resolution** ```shell Your org wide settings default must be lower than the level defined in roles: @@ -938,11 +1098,13 @@ Your org wide settings default must be lower than the level defined in roles: ``` --- -## Unsupported sharing configuration +## [Unsupported sharing configuration](sf-deployment-assistant/Unsupported-sharing-configuration.md) -- `not supported for (.*) since it's org wide default is` +**Detection** -**Resolution tip** +- RegExp: `not supported for (.*) since it's org wide default is` + +**Resolution** ```shell Consistency error between {1} sharing settings and {1} object configuration @@ -951,46 +1113,54 @@ If you already did that, please try again to run the job ``` --- -## A sharing rule may be useless +## [A sharing rule may be useless](sf-deployment-assistant/A-sharing-rule-may-be-useless.md) + +**Detection** -- `Required field is missing: sharingCriteriaRules` +- String: `Required field is missing: sharingCriteriaRules` -**Resolution tip** +**Resolution** ```shell Are you sure you need this sharing rule ? You may remove it from the sfdx project ``` --- -## Sharing recalculation lock +## [Sharing recalculation lock](sf-deployment-assistant/Sharing-recalculation-lock.md) + +**Detection** -- `because it interferes with another operation already in progress` -- `Le calcul de partage demandé ne peut être traité maintenant car il interfère avec une autre opération en cours` +- String: `because it interferes with another operation already in progress` +- String: `Le calcul de partage demandé ne peut être traité maintenant car il interfère avec une autre opération en cours` -**Resolution tip** +**Resolution** ```shell If you changed a field from MasterDetail to Lookup, you must do it manually in the target org before being able to deploy ``` --- -## Send email is disabled +## [Send email is disabled](sf-deployment-assistant/Send-email-is-disabled.md) -- `Send Email is disabled or activities are not allowed` -- `Unknown user permission: SendExternalEmailAvailable` +**Detection** -**Resolution tip** +- String: `Send Email is disabled or activities are not allowed` +- String: `Unknown user permission: SendExternalEmailAvailable` + +**Resolution** ```shell Go to Email -> Deliverability -> Select value "All emails" ``` --- -## Sort order must be in sequential order +## [Sort order must be in sequential order](sf-deployment-assistant/Sort-order-must-be-in-sequential-order.md) + +**Detection** -- `Error (.*) SortOrder must be in sequential order from` +- RegExp: `Error (.*) SortOrder must be in sequential order from` -**Resolution tip** +**Resolution** ```shell You probably have a default DuplicateRule in the target org. Retrieve it from target org, or delete it manually in target org, so you can deploy. @@ -998,11 +1168,13 @@ Ref: https://developer.salesforce.com/forums/?id=9060G000000I6SoQAK ``` --- -## Async exception in test class +## [Async exception in test class](sf-deployment-assistant/Async-exception-in-test-class.md) + +**Detection** -- `System.AsyncException: (.*) Apex` +- RegExp: `System.AsyncException: (.*) Apex` -**Resolution tip** +**Resolution** ```shell This may be a test class implementation issue in {1}. @@ -1010,22 +1182,26 @@ Please check https://developer.salesforce.com/forums/?id=9060G0000005kVLQAY ``` --- -## Test classes with 0% coverage +## [Test classes with 0% coverage](sf-deployment-assistant/Test-classes-with-0--coverage.md) -- ` 0%` +**Detection** -**Resolution tip** +- RegExp: `0%` + +**Resolution** ```shell Please make sure that none of the test classes are 0% covered ``` --- -## Can not test item deployment in simulation mode +## [Can not test item deployment in simulation mode](sf-deployment-assistant/Can-not-test-item-deployment-in-simulation-mode.md) + +**Detection** -- `Test only deployment cannot update` +- RegExp: `Test only deployment cannot update` -**Resolution tip** +**Resolution** ```shell THIS IS A FALSE POSITIVE @@ -1033,11 +1209,13 @@ When effective deployment will happen, it should pass ``` --- -## Unknown user permission: CreateAuditFields +## [Unknown user permission: CreateAuditFields](sf-deployment-assistant/Unknown-user-permission--CreateAuditFields.md) + +**Detection** -- `Unknown user permission: CreateAuditFields` +- String: `Unknown user permission: CreateAuditFields` -**Resolution tip** +**Resolution** ```shell You need to enable the "Create audit field" permission in the target org @@ -1045,11 +1223,13 @@ Please check https://help.salesforce.com/articleView?id=000334139&type=1&mode=1 ``` --- -## Unknown user permission: FieldServiceAccess +## [Unknown user permission: FieldServiceAccess](sf-deployment-assistant/Unknown-user-permission--FieldServiceAccess.md) -- `Unknown user permission: FieldServiceAccess` +**Detection** -**Resolution tip** +- String: `Unknown user permission: FieldServiceAccess` + +**Resolution** ```shell You need to enable the "Field Service Access" permission in the target org @@ -1057,11 +1237,13 @@ Please check https://help.salesforce.com/articleView?id=sf.fs_enable.htm&type=5 ``` --- -## Unknown user permission +## [Unknown user permission](sf-deployment-assistant/Unknown-user-permission.md) + +**Detection** -- `Unknown user permission:` +- String: `Unknown user permission:` -**Resolution tip** +**Resolution** ```shell You can: @@ -1070,44 +1252,52 @@ You can: ``` --- -## Variable does not exist +## [Variable does not exist](sf-deployment-assistant/Variable-does-not-exist.md) + +**Detection** -- `Error (.*) Variable does not exist: (.*) \((.*)\)` +- RegExp: `Error (.*) Variable does not exist: (.*) \((.*)\)` -**Resolution tip** +**Resolution** ```shell Apex error in {1} with unknown variable {2} at position {3}. If {2} is a class name, try to fix it, or maybe it is missing in the files or in package.xml ! ``` --- -## Visibility is not allowed for type +## [Visibility is not allowed for type](sf-deployment-assistant/Visibility-is-not-allowed-for-type.md) -- `Error (.*) set the visibility for a (.*) to Protected unless you are in a developer` +**Detection** -**Resolution tip** +- RegExp: `Error (.*) set the visibility for a (.*) to Protected unless you are in a developer` + +**Resolution** ```shell Update the visibility of {1} to "Public" ``` --- -## Tableau CRM / Wave digest error +## [Tableau CRM / Wave digest error](sf-deployment-assistant/Tableau-CRM---Wave-digest-error.md) + +**Detection** -- `Fix the sfdcDigest node errors and then upload the file again` +- String: `Fix the sfdcDigest node errors and then upload the file again` -**Resolution tip** +**Resolution** ```shell Go to the target org, open profile "Analytics Cloud Integration User" and add READ rights to the missing object fields ``` --- -## XML item appears more than once +## [XML item appears more than once](sf-deployment-assistant/XML-item-appears-more-than-once.md) + +**Detection** -- `Error (.*) Field:(.*), value:(.*) appears more than once` +- RegExp: `Error (.*) Field:(.*), value:(.*) appears more than once` -**Resolution tip** +**Resolution** ```shell You probably made an error while merging conflicts diff --git a/docs/salesforce-deployment-assistant-setup.md b/docs/salesforce-deployment-assistant-setup.md index e2f67cecd..2a3469199 100644 --- a/docs/salesforce-deployment-assistant-setup.md +++ b/docs/salesforce-deployment-assistant-setup.md @@ -18,11 +18,14 @@ If you want to **supercharge Salesforce deployment assistant with AI**, process Replace your calls to Salesforce CLI by calls to sfdx-hardis commands wrapper. -| sfdx command | Corresponding sfdx-hardis wrapper command | -|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------| -| [sfdx force:source:deploy](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_force_source.htm#cli_reference_force_source_deploy) | [sfdx hardis:source:deploy](https://sfdx-hardis.cloudity.com/hardis/source/deploy/) | -| [sfdx force:source:push](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_force_source.htm#cli_reference_force_source_push) | [sfdx hardis:source:push](https://sfdx-hardis.cloudity.com/hardis/source/push/) | -| [sfdx force:mdapi:deploy](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_force_mdapi.htm#cli_reference_force_mdapi_beta_deploy) | [sfdx hardis:mdapi:deploy](https://sfdx-hardis.cloudity.com/hardis/mdapi/deploy/) | +| sfdx command | Corresponding sfdx-hardis wrapper command | +|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------| +| [sf project deploy start](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_project_commands_unified.htm#cli_reference_project_deploy_start_unified) | [sf hardis:project:deploy:start](https://sfdx-hardis.cloudity.com/hardis/project/deploy/start/) | +| [sf project deploy validate](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_project_commands_unified.htm#cli_reference_project_deploy_validate_unified) | [sf hardis:project:deploy:validate](https://sfdx-hardis.cloudity.com/hardis/project/deploy/validate/) | +| [sf project deploy quick](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_project_commands_unified.htm#cli_reference_project_deploy_quick_unified) | [sf hardis:project:deploy:quick](https://sfdx-hardis.cloudity.com/hardis/project/deploy/quick/) | +| [sfdx force:source:deploy](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_force_source.htm#cli_reference_force_source_deploy) ([**removed on 6 november**](https://github.com/forcedotcom/cli/issues/2974)) | [sf hardis:source:deploy](https://sfdx-hardis.cloudity.com/hardis/source/deploy/) | +| [sfdx force:source:push](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_force_source.htm#cli_reference_force_source_push) ([**removed on 6 november**](https://github.com/forcedotcom/cli/issues/2974)) | [sf hardis:source:push](https://sfdx-hardis.cloudity.com/hardis/source/push/) | +| [sfdx force:mdapi:deploy](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_force_mdapi.htm#cli_reference_force_mdapi_beta_deploy) ([**removed on 6 november**](https://github.com/forcedotcom/cli/issues/2974)) | [sf hardis:mdapi:deploy](https://sfdx-hardis.cloudity.com/hardis/mdapi/deploy/) | Configure your [GitHub](salesforce-ci-cd-setup-integration-github.md), [Gitlab](salesforce-ci-cd-setup-integration-gitlab.md), [Azure Pipelines](salesforce-ci-cd-setup-integration-azure.md) or [BitBucket](salesforce-ci-cd-setup-integration-bitbucket.md) integration so the deployment assistant can post its help in Pull Request comments. @@ -39,4 +42,4 @@ Replace: by -`sfdx hardis:source:deploy -x manifest/package.xml --checkonly` \ No newline at end of file +`sf hardis:source:deploy -x manifest/package.xml --checkonly` \ No newline at end of file diff --git a/docs/salesforce-monitoring-apex-tests.md b/docs/salesforce-monitoring-apex-tests.md index 7fd9e0035..1bec5e377 100644 --- a/docs/salesforce-monitoring-apex-tests.md +++ b/docs/salesforce-monitoring-apex-tests.md @@ -8,7 +8,7 @@ description: Schedule daily apex test runs with sfdx-hardis monitoring Runs all local test classes of the org and calculate coverage. -Sfdx-hardis command: [sfdx hardis:org:test:apex](https://sfdx-hardis.cloudity.com/hardis/org/test/apex/) +Sfdx-hardis command: [sf hardis:org:test:apex](https://sfdx-hardis.cloudity.com/hardis/org/test/apex/) ### Grafana example diff --git a/docs/salesforce-monitoring-deprecated-api-calls.md b/docs/salesforce-monitoring-deprecated-api-calls.md index d15e83cdb..1ecef7cff 100644 --- a/docs/salesforce-monitoring-deprecated-api-calls.md +++ b/docs/salesforce-monitoring-deprecated-api-calls.md @@ -8,7 +8,7 @@ description: Schedule daily checks of suspect actions in setup with sfdx-hardis Will check if [legacy API versions are called by external tools](https://nicolas.vuillamy.fr/handle-salesforce-api-versions-deprecation-like-a-pro-335065f52238). -Sfdx-hardis command: [sfdx hardis:org:diagnose:legacyapi](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/legacyapi/) +Sfdx-hardis command: [sf hardis:org:diagnose:legacyapi](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/legacyapi/) Key: **LEGACY_API** diff --git a/docs/salesforce-monitoring-home.md b/docs/salesforce-monitoring-home.md index 3ad252db9..93c9f1e3b 100644 --- a/docs/salesforce-monitoring-home.md +++ b/docs/salesforce-monitoring-home.md @@ -91,6 +91,7 @@ You can force the daily run of all commands by defining env var `MONITORING_IGNO | [Quality Checks with MegaLinter](salesforce-monitoring-quality-checks.md) | Daily | | [Detect limits issues](salesforce-monitoring-org-limits.md) | Daily | | [Detect calls to deprecated API versions](salesforce-monitoring-deprecated-api-calls.md) | Daily | +| [Check Release Updates](salesforce-monitoring-release-updates.md) | Weekly | | [Detect inactive users](salesforce-monitoring-inactive-users.md) | Weekly | | [Detect unused licenses](salesforce-monitoring-unused-licenses.md) | Weekly | | [Detect custom elements with no access rights defined in permission sets](salesforce-monitoring-missing-access.md) | Weekly | diff --git a/docs/salesforce-monitoring-inactive-metadata.md b/docs/salesforce-monitoring-inactive-metadata.md index d690938ba..85c9298d5 100644 --- a/docs/salesforce-monitoring-inactive-metadata.md +++ b/docs/salesforce-monitoring-inactive-metadata.md @@ -12,7 +12,7 @@ And what about this **deactivated Validation** Rule ? Maybe it's time to remove them ! -Sfdx-hardis command: [sfdx hardis:lint:metadatastatus](https://sfdx-hardis.cloudity.com/hardis/lint/metadatastatus/) +Sfdx-hardis command: [sf hardis:lint:metadatastatus](https://sfdx-hardis.cloudity.com/hardis/lint/metadatastatus/) Key: **METADATA_STATUS** diff --git a/docs/salesforce-monitoring-inactive-users.md b/docs/salesforce-monitoring-inactive-users.md index bf655621a..6cdc5223c 100644 --- a/docs/salesforce-monitoring-inactive-users.md +++ b/docs/salesforce-monitoring-inactive-users.md @@ -8,7 +8,7 @@ description: Schedule daily checks of inactive users (not connected for 6 months Detect if you are paying licenses for users that did not login for more than 6 months ! -Sfdx-hardis command: [sfdx hardis:org:diagnose:unusedusers](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unusedusers/) +Sfdx-hardis command: [sf hardis:org:diagnose:unusedusers](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unusedusers/) Key: **UNUSED_USERS** diff --git a/docs/salesforce-monitoring-metadata-backup.md b/docs/salesforce-monitoring-metadata-backup.md index fa87e9bf8..5fa87ca6d 100644 --- a/docs/salesforce-monitoring-metadata-backup.md +++ b/docs/salesforce-monitoring-metadata-backup.md @@ -8,7 +8,7 @@ description: Schedule daily metadata backups with sfdx-hardis Monitoring Adds a new commit in the git branch with the newest updates since latest monitoring run. -Sfdx-hardis command: [sfdx hardis:org:monitor:backup](https://sfdx-hardis.cloudity.com/hardis/org/monitor/backup/) +Sfdx-hardis command: [sf hardis:org:monitor:backup](https://sfdx-hardis.cloudity.com/hardis/org/monitor/backup/) ### Grafana example diff --git a/docs/salesforce-monitoring-missing-access.md b/docs/salesforce-monitoring-missing-access.md index 9a2b1927d..42da9ac50 100644 --- a/docs/salesforce-monitoring-missing-access.md +++ b/docs/salesforce-monitoring-missing-access.md @@ -8,7 +8,7 @@ description: Schedule daily checks of metadata without access with sfdx-hardis M If there are elements that nobody has access to (not existing on any Profile or Permission Set), maybe they should be removed ! -Sfdx-hardis command: [sfdx hardis:lint:access](https://sfdx-hardis.cloudity.com/hardis/lint/access/) +Sfdx-hardis command: [sf hardis:lint:access](https://sfdx-hardis.cloudity.com/hardis/lint/access/) Key: **LINT_ACCESS** diff --git a/docs/salesforce-monitoring-missing-metadata-attributes.md b/docs/salesforce-monitoring-missing-metadata-attributes.md index 269a6aaad..ac062c41a 100644 --- a/docs/salesforce-monitoring-missing-metadata-attributes.md +++ b/docs/salesforce-monitoring-missing-metadata-attributes.md @@ -8,7 +8,7 @@ description: Schedule daily check for missing metadata attributes with sfdx-hard Follow best practices by documenting your data model ! -Sfdx-hardis command: [sfdx hardis:lint:missingattributes](https://sfdx-hardis.cloudity.com/hardis/lint/missingattributes/) +Sfdx-hardis command: [sf hardis:lint:missingattributes](https://sfdx-hardis.cloudity.com/hardis/lint/missingattributes/) Key: **MISSING_ATTRIBUTES** diff --git a/docs/salesforce-monitoring-org-limits.md b/docs/salesforce-monitoring-org-limits.md index b5704825e..d15603144 100644 --- a/docs/salesforce-monitoring-org-limits.md +++ b/docs/salesforce-monitoring-org-limits.md @@ -21,7 +21,7 @@ This feature controls that they are not reached, and will send notifications: - Warning > 75% - Error > 100% -Sfdx-hardis command: [sfdx hardis:org:monitor:limits](https://sfdx-hardis.cloudity.com/hardis/org/monitor/limits/) +Sfdx-hardis command: [sf hardis:org:monitor:limits](https://sfdx-hardis.cloudity.com/hardis/org/monitor/limits/) Key: **ORG_LIMITS** diff --git a/docs/salesforce-monitoring-release-updates.md b/docs/salesforce-monitoring-release-updates.md new file mode 100644 index 000000000..b43fcd2f8 --- /dev/null +++ b/docs/salesforce-monitoring-release-updates.md @@ -0,0 +1,25 @@ +--- +title: Check Release Updates (Salesforce monitoring) +description: Schedule weekly checks of Setup Release Updates with sfdx-hardis Monitoring +--- + + +## Check Release Updates + +Before publishing **Breaking Changes** ❌, Salesforce announce them in the setup menu [**Release Updates**](https://help.salesforce.com/s/articleView?id=sf.release_updates.htm&type=5) + +⚠️ Some of them are very important, because if you don't make the related upgrades in time (ex: before Winter 25) , your production org can crash ! + +This command will extract the Release Updates that needs to be checked in your org ! + +Sfdx-hardis command: [sf hardis:org:diagnose:releaseupdates](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/releaseupdates/) + +Key: **RELEASE_UPDATES*** + +### Grafana example + +![](assets/images/screenshot-monitoring-release-updates-grafana.jpg) + +### Slack example + +![](assets/images/screenshot-monitoring-release-updates.jpg) \ No newline at end of file diff --git a/docs/salesforce-monitoring-suspect-audit-trail.md b/docs/salesforce-monitoring-suspect-audit-trail.md index 3a22e47af..8c09d1d3c 100644 --- a/docs/salesforce-monitoring-suspect-audit-trail.md +++ b/docs/salesforce-monitoring-suspect-audit-trail.md @@ -8,7 +8,7 @@ description: Schedule daily checks of suspect actions in setup with sfdx-hardis Will extract from audit trail all actions that are considered as suspect, excepted the ones related to the deployment user and a given list of users, like the release manager. -Sfdx-hardis command: [sfdx hardis:org:diagnose:audittrail](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/audittrail/) +Sfdx-hardis command: [sf hardis:org:diagnose:audittrail](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/audittrail/) Key: **AUDIT_TRAIL** diff --git a/docs/salesforce-monitoring-unused-licenses.md b/docs/salesforce-monitoring-unused-licenses.md index 6e1bf7076..a27aa7233 100644 --- a/docs/salesforce-monitoring-unused-licenses.md +++ b/docs/salesforce-monitoring-unused-licenses.md @@ -17,7 +17,7 @@ This command detects such useless Permission Set Licenses Assignments and sugges Many thanks to [Vincent Finet](https://www.linkedin.com/in/vincentfinet/) for the inspiration during his great speaker session at [French Touch Dreamin '23](https://frenchtouchdreamin.com/), and his kind agreement for reusing such inspiration in this command :) -Sfdx-hardis command: [sfdx hardis:org:diagnose:unusedlicenses](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unusedlicenses/) +Sfdx-hardis command: [sf hardis:org:diagnose:unusedlicenses](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unusedlicenses/) Key: **UNUSED_LICENSES** diff --git a/docs/salesforce-monitoring-unused-metadata.md b/docs/salesforce-monitoring-unused-metadata.md index 375e55727..2cfdd90a7 100644 --- a/docs/salesforce-monitoring-unused-metadata.md +++ b/docs/salesforce-monitoring-unused-metadata.md @@ -13,7 +13,7 @@ Today working with: - Custom Labels - Custom Permissions -Sfdx-hardis command: [sfdx hardis:lint:unusedmetadatas](https://sfdx-hardis.cloudity.com/hardis/lint/unusedmetadatas/) +Sfdx-hardis command: [sf hardis:lint:unusedmetadatas](https://sfdx-hardis.cloudity.com/hardis/lint/unusedmetadatas/) Key: **UNUSED_METADATAS** diff --git a/docs/schema/sfdx-hardis-json-schema-parameters.html b/docs/schema/sfdx-hardis-json-schema-parameters.html index f53ec6456..ee3f3f16d 100644 --- a/docs/schema/sfdx-hardis-json-schema-parameters.html +++ b/docs/schema/sfdx-hardis-json-schema-parameters.html @@ -148,7 +148,7 @@

autoCleanTypes itemsType: enum (of string)

Must be one of:

-
  • "caseentitlement"
  • "checkPermissions"
  • "dashboards"
  • "datadotcom"
  • "destructivechanges"
  • "flowPositions"
  • "localfields"
  • "listViewsMine"
  • "minimizeProfiles"
  • "productrequest"
  • "systemDebug"
  • "v60"
+
  • "caseentitlement"
  • "checkPermissions"
  • "dashboards"
  • "datadotcom"
  • "destructivechanges"
  • "flowPositions"
  • "localfields"
  • "listViewsMine"
  • "minimizeProfiles"
  • "productrequest"
  • "sensitiveMetadatas"
  • "systemDebug"
  • "v60"
@@ -1191,6 +1191,65 @@

+ + + + +
+
+
+

+ +

+
+ +
+
+ +

Context

Type: enum (of string) Default: "all"
+

Context when the command must be run

+
+

Must be one of:

+
  • "all"
  • "check-deployment-only"
  • "process-deployment-only"
+
+ + + + + +
+
Examples:
+
"all"
+
+
"check-deployment-only"
+
+
"process-deployment-only"
+
+
@@ -1893,14 +1952,14 @@

"label": "Generate manifest", "icon": "file.svg", "tooltip": "Generates a manifest package.xml using local sfdx source files", - "command": "sfdx force:source:manifest:create --sourcepath force-app --manifestname myNewManifest" + "command": "sf project generate manifest --source-path force-app --name myNewManifest" }, { "id": "list-all-orgs", "label": "List all orgs", "icon": "salesforce.svg", "tooltip": "List all orgs that has already been authenticated using sfdx", - "command": "sfdx force:org:list --all" + "command": "sf org list --all" } ] }, @@ -4244,13 +4303,13 @@

{ "title": "Detect calls to deprecated API versions", "key": "LEGACYAPI", - "command": "sfdx hardis:org:diagnose:legacyapi", + "command": "sf hardis:org:diagnose:legacyapi", "frequency": "weekly" }, { "title": "My custom command", "key": "MY_CUSTOM_KEY", - "command": "sfdx my:custom:command", + "command": "sf my:custom:command", "frequency": "daily" } ] @@ -4387,7 +4446,7 @@

Must be one of:

-
  • "AUDIT_TRAIL"
  • "LEGACY_API"
  • "LINT_ACCESS"
  • "UNUSED_METADATAS"
  • "METADATA_STATUS"
  • "MISSING_ATTRIBUTES"
  • "UNUSED_LICENSES"
+
  • "AUDIT_TRAIL"
  • "LEGACY_API"
  • "LINT_ACCESS"
  • "UNUSED_METADATAS"
  • "METADATA_STATUS"
  • "MISSING_ATTRIBUTES"
  • "UNUSED_LICENSES"
  • "RELEASE_UPDATES"
@@ -4720,7 +4779,7 @@

Must be one of:

-
  • "AUDIT_TRAIL"
  • "APEX_TESTS"
  • "BACKUP"
  • "DEPLOYMENT"
  • "LEGACY_API"
  • "LINT_ACCESS"
  • "UNUSED_METADATAS"
  • "METADATA_STATUS"
  • "MISSING_ATTRIBUTES"
  • "UNUSED_LICENSES"
+
  • "AUDIT_TRAIL"
  • "APEX_TESTS"
  • "BACKUP"
  • "DEPLOYMENT"
  • "LEGACY_API"
  • "LINT_ACCESS"
  • "UNUSED_METADATAS"
  • "METADATA_STATUS"
  • "MISSING_ATTRIBUTES"
  • "UNUSED_LICENSES"
  • "RELEASE_UPDATES"
@@ -5642,6 +5701,40 @@

+

+ + + +
+
+
+

+ +

+
+ +
+
+ +

Use Smart Deployment Tests

Type: boolean Default: false
+

Define if smart deployment tests will be activated and run test classes only if necessary (see more in hardis:project:deploy:smart documentation

+
+ + + + + +
@@ -5692,6 +5785,6 @@

\ No newline at end of file diff --git a/docs/sf-deployment-assistant/A-sharing-rule-may-be-useless.md b/docs/sf-deployment-assistant/A-sharing-rule-may-be-useless.md new file mode 100644 index 000000000..2db5cd248 --- /dev/null +++ b/docs/sf-deployment-assistant/A-sharing-rule-may-be-useless.md @@ -0,0 +1,16 @@ +--- +title: : A sharing rule may be useless (Deployment assistant) +description: How to solve Salesforce deployment error Required field is missing: sharingCriteriaRules +--- + +# A sharing rule may be useless + +## Detection + +- String: `Required field is missing: sharingCriteriaRules` + +## Resolution + +```shell +Are you sure you need this sharing rule ? You may remove it from the sfdx project +``` diff --git a/docs/sf-deployment-assistant/API-Version-error.md b/docs/sf-deployment-assistant/API-Version-error.md new file mode 100644 index 000000000..a63afe6ce --- /dev/null +++ b/docs/sf-deployment-assistant/API-Version-error.md @@ -0,0 +1,19 @@ +--- +title: : API Version error (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) The (.*) apiVersion can't be "([0-9]+)"/gm +--- + +# API Version error + +## Detection + +- RegExp: `Error (.*) The (.*) apiVersion can't be "([0-9]+)"` + +## Resolution + +```shell +{1} metadata has probably been created/updated in a sandbox already upgraded to next platform version (ex: Sandbox in Summer'23 and Production in Spring'23) +- First, try to update the api version in the XML of {1} metadata file (decrement the number in {3}.0) +- If it still doesn't work because the metadata structure has changed between version, you may try a sf project:retrieve:start of the metadata by forcing --api-version at the end of the command. + +``` diff --git a/docs/sf-deployment-assistant/Allow-deployment-with-pending-Apex-Jobs.md b/docs/sf-deployment-assistant/Allow-deployment-with-pending-Apex-Jobs.md new file mode 100644 index 000000000..5b7f976d4 --- /dev/null +++ b/docs/sf-deployment-assistant/Allow-deployment-with-pending-Apex-Jobs.md @@ -0,0 +1,17 @@ +--- +title: : Allow deployment with pending Apex Jobs (Deployment assistant) +description: How to solve Salesforce deployment error You can bypass this error by allowing deployments with Apex jobs in the Deployment Settings page in Setup. +--- + +# Allow deployment with pending Apex Jobs + +## Detection + +- String: `You can bypass this error by allowing deployments with Apex jobs in the Deployment Settings page in Setup.` + +## Resolution + +```shell +Go to target org, in Setup -> Deployment Settings -> Activate option "Allow deployments of components when corresponding Apex jobs are pending or in progress." + +``` diff --git a/docs/sf-deployment-assistant/Async-exception-in-test-class.md b/docs/sf-deployment-assistant/Async-exception-in-test-class.md new file mode 100644 index 000000000..6f787e1df --- /dev/null +++ b/docs/sf-deployment-assistant/Async-exception-in-test-class.md @@ -0,0 +1,17 @@ +--- +title: : Async exception in test class (Deployment assistant) +description: How to solve Salesforce deployment error /System.AsyncException: (.*) Apex/gm +--- + +# Async exception in test class + +## Detection + +- RegExp: `System.AsyncException: (.*) Apex` + +## Resolution + +```shell +This may be a test class implementation issue in {1}. +Please check https://developer.salesforce.com/forums/?id=9060G0000005kVLQAY +``` diff --git a/docs/sf-deployment-assistant/CRM-Analytics--A-Recipe-must-specify-a-DataFlow.md b/docs/sf-deployment-assistant/CRM-Analytics--A-Recipe-must-specify-a-DataFlow.md new file mode 100644 index 000000000..f21127cc1 --- /dev/null +++ b/docs/sf-deployment-assistant/CRM-Analytics--A-Recipe-must-specify-a-DataFlow.md @@ -0,0 +1,20 @@ +--- +title: : CRM Analytics: A Recipe must specify a DataFlow (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) A Recipe must specify a Dataflow/gm +--- + +# CRM Analytics: A Recipe must specify a DataFlow + +## Detection + +- RegExp: `Error (.*) A Recipe must specify a Dataflow` + +## Resolution + +```shell +You must include related WaveDataFlow {1} in sources (and probably in package.xml too). +To retrieve it, run: sf project retrieve start -m WaveDataFlow:{1} -u SOURCE_ORG_USERNAME +You can also retrieve all analytics sources in one shot using sf hardis:org:retrieve:source:analytics -u SOURCE_ORG_USERNAME + - https://salesforce.stackexchange.com/a/365453/33522 + - https://help.salesforce.com/s/articleView?id=000319274&type=1 +``` diff --git a/docs/sf-deployment-assistant/Campaign-can-not-be-updated.md b/docs/sf-deployment-assistant/Campaign-can-not-be-updated.md new file mode 100644 index 000000000..db7d08d6a --- /dev/null +++ b/docs/sf-deployment-assistant/Campaign-can-not-be-updated.md @@ -0,0 +1,17 @@ +--- +title: : Campaign can not be updated (Deployment assistant) +description: How to solve Salesforce deployment error The object "Campaign" can't be updated +--- + +# Campaign can not be updated + +## Detection + +- String: `The object "Campaign" can't be updated` + +## Resolution + +```shell +Add "MarketingUser" in project-scratch-def.json features +If it is already done, you may manually check "MarketingUser" field on the scratch org user +``` diff --git a/docs/sf-deployment-assistant/Can-not-change-field-type-to-a-formula-field.md b/docs/sf-deployment-assistant/Can-not-change-field-type-to-a-formula-field.md new file mode 100644 index 000000000..5a2f2534d --- /dev/null +++ b/docs/sf-deployment-assistant/Can-not-change-field-type-to-a-formula-field.md @@ -0,0 +1,18 @@ +--- +title: : Can not change field type to a formula field (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) Cannot update a field from a Formula to something else/gm +--- + +# Can not change field type to a formula field + +## Detection + +- RegExp: `Error (.*) Cannot update a field from a Formula to something else` + +## Resolution + +```shell +You need to manually delete or rename the field in the target org to allow the deployment to pass +- First, try to manually delete field {1} in the target org +- if you can't delete {1}, rename it into {1}_ToDel, then once the deployment done, delete {1}_ToDel +``` diff --git a/docs/sf-deployment-assistant/Can-not-change-field-type-with-picklist.md b/docs/sf-deployment-assistant/Can-not-change-field-type-with-picklist.md new file mode 100644 index 000000000..b07fb6b70 --- /dev/null +++ b/docs/sf-deployment-assistant/Can-not-change-field-type-with-picklist.md @@ -0,0 +1,19 @@ +--- +title: : Can not change field type with picklist (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) Cannot change which global value set this picklist uses/gm +--- + +# Can not change field type with picklist + +## Detection + +- RegExp: `Error (.*) Cannot change which global value set this picklist uses` + +## Resolution + +```shell +You probably updated the type of field {1}, and Salesforce does not allows that with deployments. You can: +- Try to manually change the type of {1} directly in target org, but it may not be technically possible +- Delete field {1} in target org: it will be recreated after deployment (but you will loose data on existing records, so be careful if your target is a production org) +- Create another field with desired type and manage data recovery if the target is a production org +``` diff --git a/docs/sf-deployment-assistant/Can-not-change-type-due-to-existing-data.md b/docs/sf-deployment-assistant/Can-not-change-type-due-to-existing-data.md new file mode 100644 index 000000000..aa8bc3215 --- /dev/null +++ b/docs/sf-deployment-assistant/Can-not-change-type-due-to-existing-data.md @@ -0,0 +1,21 @@ +--- +title: : Can not change type due to existing data (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) Cannot change type due to existing data/gm +--- + +# Can not change type due to existing data + +## Detection + +- RegExp: `Error (.*) Cannot change type due to existing data` + +## Resolution + +```shell +It is usually not recommended to change types of fields, but if it's really necessary you can: +- Manually change the type of {1} in the target org +- If you can't manually change the type: + - you may modify the dependencies (Formulas, Flows...) using {1}, so they don't use this field + - you can also delete dependencies (Formulas, Flows...) using {1}, but make sure they are deployed again later +- More help: https://help.salesforce.com/s/articleView?id=000327186&type=1 +``` diff --git a/docs/sf-deployment-assistant/Can-not-delete-custom-field.md b/docs/sf-deployment-assistant/Can-not-delete-custom-field.md new file mode 100644 index 000000000..6a396e04f --- /dev/null +++ b/docs/sf-deployment-assistant/Can-not-delete-custom-field.md @@ -0,0 +1,18 @@ +--- +title: : Can not delete custom field (Deployment assistant) +description: How to solve Salesforce deployment error /This (.*) is referenced elsewhere in salesforce.com/gm +--- + +# Can not delete custom field + +## Detection + +- RegExp: `This (.*) is referenced elsewhere in salesforce.com` +- RegExp: `Le champ personnalisé (.*) est utilisé dans (.*)` + +## Resolution + +```shell +Custom field {1} can not be deleted because it is used elsewhere. Remove its references ans try again +THIS MAY BE A FALSE POSITIVE if you are just testing the deployment, as destructiveChanges are deployed separately from updated items deployment check +``` diff --git a/docs/sf-deployment-assistant/Can-not-delete-record-type.md b/docs/sf-deployment-assistant/Can-not-delete-record-type.md new file mode 100644 index 000000000..4cef2aa64 --- /dev/null +++ b/docs/sf-deployment-assistant/Can-not-delete-record-type.md @@ -0,0 +1,18 @@ +--- +title: : Can not delete record type (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) Cannot delete record type through API/gm +--- + +# Can not delete record type + +## Detection + +- RegExp: `Error (.*) Cannot delete record type through API` + +## Resolution + +```shell +You need to manually delete record type {1} in target org +- Edit record type {1}, uncheck "Active" +- Delete record type {1} +``` diff --git a/docs/sf-deployment-assistant/Can-not-find-folder.md b/docs/sf-deployment-assistant/Can-not-find-folder.md new file mode 100644 index 000000000..b53f5d7b9 --- /dev/null +++ b/docs/sf-deployment-assistant/Can-not-find-folder.md @@ -0,0 +1,20 @@ +--- +title: : Can not find folder (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) Cannot find folder:(.*)/gm +--- + +# Can not find folder + +## Detection + +- RegExp: `Error (.*) Cannot find folder:(.*)` + +## Resolution + +```shell +Folder {2} is missing. +- If folder {2} is existing in sources, add it in related package.xml +- If folder {2} is not existing in DX sources, please use sf hardis:project:clean:retrievefolders to retrieve it +- If both previous solutions did not work, go create manually folder {2} in target org + +``` diff --git a/docs/sf-deployment-assistant/Can-not-find-user--2-.md b/docs/sf-deployment-assistant/Can-not-find-user--2-.md new file mode 100644 index 000000000..a0d98430c --- /dev/null +++ b/docs/sf-deployment-assistant/Can-not-find-user--2-.md @@ -0,0 +1,19 @@ +--- +title: : Can not find user (2) (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) In field: (.*) - no User named (.*) found/gm +--- + +# Can not find user (2) + +## Detection + +- RegExp: `Error (.*) In field: (.*) - no User named (.*) found` + +## Resolution + +```shell +You made reference to username {3} in {1}, and it probably does not exist in the target org. +- Do not use named users, but user public groups for assignments -> https://help.salesforce.com/s/articleView?id=sf.creating_and_editing_groups.htm&type=5 +- or Create matching user {3} in the target deployment org +- or open {1} metadata and remove the XML part referring to hardcoded username {3} +``` diff --git a/docs/sf-deployment-assistant/Can-not-find-user.md b/docs/sf-deployment-assistant/Can-not-find-user.md new file mode 100644 index 000000000..93800e35a --- /dev/null +++ b/docs/sf-deployment-assistant/Can-not-find-user.md @@ -0,0 +1,27 @@ +--- +title: : Can not find user (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) Cannot find a user that matches any of the following usernames/gm +--- + +# Can not find user + +## Detection + +- RegExp: `Error (.*) Cannot find a user that matches any of the following usernames` + +## Resolution + +```shell +You made reference to username(s) in {1}, and those users probably do not exist in target org. +- Do not use named users, but user public groups for assignments -> https://help.salesforce.com/s/articleView?id=sf.creating_and_editing_groups.htm&type=5 +- or Create matching user(s) in the target deployment org +- or Remove the XML part referring to hardcoded usernames + +Example of XML you have to remove in {1}: + + + Manage + nicolas.vuillamy@hardis-scratch-po-tgci-root-develop_20220412_0604.com + User + +``` diff --git a/docs/sf-deployment-assistant/Can-not-test-item-deployment-in-simulation-mode.md b/docs/sf-deployment-assistant/Can-not-test-item-deployment-in-simulation-mode.md new file mode 100644 index 000000000..7cf874915 --- /dev/null +++ b/docs/sf-deployment-assistant/Can-not-test-item-deployment-in-simulation-mode.md @@ -0,0 +1,17 @@ +--- +title: : Can not test item deployment in simulation mode (Deployment assistant) +description: How to solve Salesforce deployment error /Test only deployment cannot update/gm +--- + +# Can not test item deployment in simulation mode + +## Detection + +- RegExp: `Test only deployment cannot update` + +## Resolution + +```shell +THIS IS A FALSE POSITIVE +When effective deployment will happen, it should pass +``` diff --git a/docs/sf-deployment-assistant/Cannot-update-a-field-to-a-Summary-from-something-else.md b/docs/sf-deployment-assistant/Cannot-update-a-field-to-a-Summary-from-something-else.md new file mode 100644 index 000000000..d6351a4d4 --- /dev/null +++ b/docs/sf-deployment-assistant/Cannot-update-a-field-to-a-Summary-from-something-else.md @@ -0,0 +1,19 @@ +--- +title: : Cannot update a field to a Summary from something else (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) Cannot update a field to a (.*) from something else/gm +--- + +# Cannot update a field to a Summary from something else + +## Detection + +- RegExp: `Error (.*) Cannot update a field to a (.*) from something else` + +## Resolution + +```shell +You probably updated the type of field {1} to type {2}, and Salesforce does not allows that with deployments. You can: +- Try to manually change the type of {1} directly in target org, but it may not be technically possible +- Delete field {1} in target org: it will be recreated after deployment (but you will loose data on existing records, so be careful if your target is a production org) +- Create another field with desired type and manage data recovery if the target is a production org +``` diff --git a/docs/sf-deployment-assistant/Change-Matching-Rule.md b/docs/sf-deployment-assistant/Change-Matching-Rule.md new file mode 100644 index 000000000..c7e8c9b9c --- /dev/null +++ b/docs/sf-deployment-assistant/Change-Matching-Rule.md @@ -0,0 +1,16 @@ +--- +title: : Change Matching Rule (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) Before you change a matching rule, you must deactivate it/gm +--- + +# Change Matching Rule + +## Detection + +- RegExp: `Error (.*) Before you change a matching rule, you must deactivate it` + +## Resolution + +```shell +To be able to deploy, you must go in target org setup to manually deactivate matching rule {1} +``` diff --git a/docs/sf-deployment-assistant/Condition-missing-reference.md b/docs/sf-deployment-assistant/Condition-missing-reference.md new file mode 100644 index 000000000..d8a209392 --- /dev/null +++ b/docs/sf-deployment-assistant/Condition-missing-reference.md @@ -0,0 +1,19 @@ +--- +title: : Condition missing reference (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) field integrity exception: unknown \(A condition has a reference to (.*), which doesn't exist.\)/gm +--- + +# Condition missing reference + +## Detection + +- RegExp: `Error (.*) field integrity exception: unknown \(A condition has a reference to (.*), which doesn't exist.\)` + +## Resolution + +```shell +There is a reference to {2} in {1}, and {2} is not found. You can either: +- Add {2} in your deployment sources and make sure it is named in package.xml +- Remove the reference to {2} in {1} + +``` diff --git a/docs/sf-deployment-assistant/Custom-field-not-found.md b/docs/sf-deployment-assistant/Custom-field-not-found.md new file mode 100644 index 000000000..aa76b40a4 --- /dev/null +++ b/docs/sf-deployment-assistant/Custom-field-not-found.md @@ -0,0 +1,27 @@ +--- +title: : Custom field not found (Deployment assistant) +description: How to solve Salesforce deployment error Error PS_Admin In field: field - no CustomField named User.expcloud__Portal_Username__c found +--- + +# Custom field not found + +## Detection + +- RegExp: `Error (.*) In field: (.*) - no CustomField named (.*)\.(.*) found` + +## Examples + +- `Error PS_Admin In field: field - no CustomField named User.expcloud__Portal_Username__c found` + +## Resolution + +```shell +A reference to a custom field {3}.{4} is not found in {1}: +- If you renamed {3}.{4}, do a search/replace in {1} with previous field name and {4} +- If you deleted {3}.{4}, or if you don't want to deploy it, do a search on {4} in all sources, and remove all XML elements referring to {3}.{4} (except in destructiveChanges.xml) +- If {3}.{4} should exist, make sure it is in force-app/main/default/objects/{3}/fields and that {3}.{4} is in manifest/package.xml in CustomField section +- If {3}.{4} is standard, the error is because {3}.{4} is not available in the org you are trying to deploy to. You can: + - Remove the reference to {4} in the XML of {1} ( maybe sf hardis:project:clean:references can clean automatically for you ! ) + - Activate the required features/license in the target org + +``` diff --git a/docs/sf-deployment-assistant/Custom-metadata-entry-not-found.md b/docs/sf-deployment-assistant/Custom-metadata-entry-not-found.md new file mode 100644 index 000000000..be78ca579 --- /dev/null +++ b/docs/sf-deployment-assistant/Custom-metadata-entry-not-found.md @@ -0,0 +1,19 @@ +--- +title: : Custom metadata entry not found (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) In field: (.*) - no CustomMetadata named (.*) found/gm +--- + +# Custom metadata entry not found + +## Detection + +- RegExp: `Error (.*) In field: (.*) - no CustomMetadata named (.*) found` + +## Resolution + +```shell +A reference to a custom metadata {3} of type {2} is not found in {1}: +- Are you sure you deployed {3} ? +- If you use a package.xml, is {3} present within type CustomMetadata ? + +``` diff --git a/docs/sf-deployment-assistant/Custom-object-not-found.md b/docs/sf-deployment-assistant/Custom-object-not-found.md new file mode 100644 index 000000000..46bce0b58 --- /dev/null +++ b/docs/sf-deployment-assistant/Custom-object-not-found.md @@ -0,0 +1,21 @@ +--- +title: : Custom object not found (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) In field: field - no CustomObject named (.*) found/gm +--- + +# Custom object not found + +## Detection + +- RegExp: `Error (.*) In field: field - no CustomObject named (.*) found` + +## Resolution + +```shell +A reference to a custom object {2} is not found in {1}: +- If you renamed the custom object, do a search/replace in sources with previous object name and new object name +- If you deleted the custom object, or if you don't want to deploy it, do a search on the custom object name, and remove XML elements referencing it +- If the object should exist, make sure it is in force-app/main/default/objects and that the object name is in manifest/package.xml in CustomObject section +You may also have a look to command sf hardis:project:clean:references + +``` diff --git a/docs/sf-deployment-assistant/Dependent-class-is-invalid-and-needs-recompilation.md b/docs/sf-deployment-assistant/Dependent-class-is-invalid-and-needs-recompilation.md new file mode 100644 index 000000000..a3b4c8c03 --- /dev/null +++ b/docs/sf-deployment-assistant/Dependent-class-is-invalid-and-needs-recompilation.md @@ -0,0 +1,17 @@ +--- +title: : Dependent class is invalid and needs recompilation (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) Dependent class is invalid and needs recompilation/gm +--- + +# Dependent class is invalid and needs recompilation + +## Detection + +- RegExp: `Error (.*) Dependent class is invalid and needs recompilation` + +## Resolution + +```shell +Solve the other errors and this one will disappear ! + +``` diff --git a/docs/sf-deployment-assistant/Duplicate-label.md b/docs/sf-deployment-assistant/Duplicate-label.md new file mode 100644 index 000000000..5b7863c44 --- /dev/null +++ b/docs/sf-deployment-assistant/Duplicate-label.md @@ -0,0 +1,16 @@ +--- +title: : Duplicate label (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) Duplicate label: (.*)/gm +--- + +# Duplicate label + +## Detection + +- RegExp: `Error (.*) Duplicate label: (.*)` + +## Resolution + +```shell +You probably renamed the picklist API name for {2}. Please update manually the picklist {1} in the target org to avoid to have a duplicate label +``` diff --git a/docs/sf-deployment-assistant/Duplicate-value-Platform-Action-Id-List.md b/docs/sf-deployment-assistant/Duplicate-value-Platform-Action-Id-List.md new file mode 100644 index 000000000..bb03e3bb3 --- /dev/null +++ b/docs/sf-deployment-assistant/Duplicate-value-Platform-Action-Id-List.md @@ -0,0 +1,16 @@ +--- +title: : Duplicate value Platform Action Id List (Deployment assistant) +description: How to solve Salesforce deployment error duplicate value found: PlatformActionListId duplicates value on record with id +--- + +# Duplicate value Platform Action Id List + +## Detection + +- String: `duplicate value found: PlatformActionListId duplicates value on record with id` + +## Resolution + +```shell +There are probably issue with conflict management. Open the XML of the source item, and replace all numbers to make an ascending order, starting with 0 +``` diff --git a/docs/sf-deployment-assistant/Empty-source-items.md b/docs/sf-deployment-assistant/Empty-source-items.md new file mode 100644 index 000000000..7db8326a4 --- /dev/null +++ b/docs/sf-deployment-assistant/Empty-source-items.md @@ -0,0 +1,19 @@ +--- +title: : Empty source items (Deployment assistant) +description: How to solve Salesforce deployment error Required field is missing: sharingOwnerRules +--- + +# Empty source items + +## Detection + +- String: `Required field is missing: sharingOwnerRules` +- String: `Required field is missing: standardValue` +- String: `Required field is missing: valueTranslation` + +## Resolution + +```shell +You probably retrieved empty items, that must not be included within the SFDX project +To remove them, please run sfdx:hardis:project:clean:emptyitems +``` diff --git a/docs/sf-deployment-assistant/Enable-CRM-Analytics.md b/docs/sf-deployment-assistant/Enable-CRM-Analytics.md new file mode 100644 index 000000000..3e364b838 --- /dev/null +++ b/docs/sf-deployment-assistant/Enable-CRM-Analytics.md @@ -0,0 +1,17 @@ +--- +title: : Enable CRM Analytics (Deployment assistant) +description: How to solve Salesforce deployment error It should be created by enabling the CRM Analytics Cloud preference +--- + +# Enable CRM Analytics + +## Detection + +- String: `It should be created by enabling the CRM Analytics Cloud preference` + +## Resolution + +```shell +You must enable CRM Analytics (ex Wave, Einstein Analytics & Tableau CRM) in the target org. +You probably also need to add CRM Analytics Admin Permission Set assignment to the deployment user +``` diff --git a/docs/sf-deployment-assistant/Error-parsing-file.md b/docs/sf-deployment-assistant/Error-parsing-file.md new file mode 100644 index 000000000..b57853ceb --- /dev/null +++ b/docs/sf-deployment-assistant/Error-parsing-file.md @@ -0,0 +1,17 @@ +--- +title: : Error parsing file (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) Error parsing file: (.*)/gm +--- + +# Error parsing file + +## Detection + +- RegExp: `Error (.*) Error parsing file: (.*)` + +## Resolution + +```shell +There has been an error parsing the XML file of {1}: {2} +- Open file {1} and look where the error can be ! (merge issue, typo, XML tag not closed...) +``` diff --git a/docs/sf-deployment-assistant/Expired-Access---Refresh-Token.md b/docs/sf-deployment-assistant/Expired-Access---Refresh-Token.md new file mode 100644 index 000000000..7eaa2e47d --- /dev/null +++ b/docs/sf-deployment-assistant/Expired-Access---Refresh-Token.md @@ -0,0 +1,16 @@ +--- +title: : Expired Access / Refresh Token (Deployment assistant) +description: How to solve Salesforce deployment error expired access/refresh token +--- + +# Expired Access / Refresh Token + +## Detection + +- String: `expired access/refresh token` + +## Resolution + +```shell +Run command "Select another org" from Status panel (or sf hardis:org:select) to authenticate again to your org +``` diff --git a/docs/sf-deployment-assistant/Field-not-available-for-element.md b/docs/sf-deployment-assistant/Field-not-available-for-element.md new file mode 100644 index 000000000..e3fc89879 --- /dev/null +++ b/docs/sf-deployment-assistant/Field-not-available-for-element.md @@ -0,0 +1,17 @@ +--- +title: : Field not available for element (Deployment assistant) +description: How to solve Salesforce deployment error /Field (.*) is not available for/gm +--- + +# Field not available for element + +## Detection + +- RegExp: `Field (.*) is not available for` + +## Resolution + +```shell +You probably changed the type of field {1}. +Find field {1} in the source XML, and remove the section using it +``` diff --git a/docs/sf-deployment-assistant/Flow-must-be-deleted-manually.md b/docs/sf-deployment-assistant/Flow-must-be-deleted-manually.md new file mode 100644 index 000000000..b430d40bf --- /dev/null +++ b/docs/sf-deployment-assistant/Flow-must-be-deleted-manually.md @@ -0,0 +1,16 @@ +--- +title: : Flow must be deleted manually (Deployment assistant) +description: How to solve Salesforce deployment error /.flow (.*) insufficient access rights on cross-reference id/gm +--- + +# Flow must be deleted manually + +## Detection + +- RegExp: `.flow (.*) insufficient access rights on cross-reference id` + +## Resolution + +```shell +Flow {1} can not be deleted using deployments, please delete it manually in the target org using menu Setup -> Flows , context menu on {1} -> View details and versions -> Deactivate all versions -> Delete flow +``` diff --git a/docs/sf-deployment-assistant/Formula-picklist-field-issue.md b/docs/sf-deployment-assistant/Formula-picklist-field-issue.md new file mode 100644 index 000000000..5fc118672 --- /dev/null +++ b/docs/sf-deployment-assistant/Formula-picklist-field-issue.md @@ -0,0 +1,18 @@ +--- +title: : Formula picklist field issue (Deployment assistant) +description: How to solve Salesforce deployment error Les champs de liste de sélection sont pris en charge uniquement dans certaines fonctions. +--- + +# Formula picklist field issue + +## Detection + +- String: `Les champs de liste de sélection sont pris en charge uniquement dans certaines fonctions.` + +## Resolution + +```shell +You probably changed the type of a field that is used in a formula. +Update the formula to use a field compliant with formulas. +More details at https://help.salesforce.com/articleView?id=sf.tips_on_building_formulas.htm&type=5 +``` diff --git a/docs/sf-deployment-assistant/Insufficient-access-rights-on-cross-reference-id.md b/docs/sf-deployment-assistant/Insufficient-access-rights-on-cross-reference-id.md new file mode 100644 index 000000000..ae7cf62b2 --- /dev/null +++ b/docs/sf-deployment-assistant/Insufficient-access-rights-on-cross-reference-id.md @@ -0,0 +1,17 @@ +--- +title: : Insufficient access rights on cross-reference id (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) insufficient access rights on cross-reference id/gm +--- + +# Insufficient access rights on cross-reference id + +## Detection + +- RegExp: `Error (.*) insufficient access rights on cross-reference id` + +## Resolution + +```shell +- If {1} is a Flow, it can not be deleted using deployments, please delete it manually in the target org using menu Setup -> Flows , context menu on {1} -> View details and versions -> Deactivate all versions -> Delete flow +- If you changed a custom field from unique to not unique, you need to manually make the change in the target org +``` diff --git a/docs/sf-deployment-assistant/Invalid-field-for-upsert.md b/docs/sf-deployment-assistant/Invalid-field-for-upsert.md new file mode 100644 index 000000000..1bdeed1b6 --- /dev/null +++ b/docs/sf-deployment-assistant/Invalid-field-for-upsert.md @@ -0,0 +1,20 @@ +--- +title: : Invalid field for upsert (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) Invalid field for upsert, must be an External Id custom or standard indexed field: (.*) \((.*)\)/gm +--- + +# Invalid field for upsert + +## Detection + +- RegExp: `Error (.*) Invalid field for upsert, must be an External Id custom or standard indexed field: (.*) \((.*)\)` + +## Resolution + +```shell +You tried to use field {2} for an upsert call in {1}. +- Is it declared as externalId ? +- Is the customIndex source file present in the deployment ? +- If it is declared as externalId and customIndex is present, you may have to go manually define the field as externalId in the target org + +``` diff --git a/docs/sf-deployment-assistant/Invalid-field-in-related-list.md b/docs/sf-deployment-assistant/Invalid-field-in-related-list.md new file mode 100644 index 000000000..1a925d2b5 --- /dev/null +++ b/docs/sf-deployment-assistant/Invalid-field-in-related-list.md @@ -0,0 +1,28 @@ +--- +title: : Invalid field in related list (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) Invalid field:(.*) in related list:(.*)/gm +--- + +# Invalid field in related list + +## Detection + +- RegExp: `Error (.*) Invalid field:(.*) in related list:(.*)` + +## Resolution + +```shell +Field {2} is unknown. You can: +- Activate the related feature license or option to make {2} existing in target org +- Update XML of {1} to remove reference to field {2} in the related list {3} +- Update XML of {1} to remove the whole related list {3} +Example of XML to remove: + + SOLUTION.ISSUE + SOLUTION.SOLUTION_NUMBER + SOLUTION.STATUS + CORE.USERS.ALIAS + RelatedSolutionList + + +``` diff --git a/docs/sf-deployment-assistant/Invalid-formula-grouping-context.md b/docs/sf-deployment-assistant/Invalid-formula-grouping-context.md new file mode 100644 index 000000000..011c43e07 --- /dev/null +++ b/docs/sf-deployment-assistant/Invalid-formula-grouping-context.md @@ -0,0 +1,16 @@ +--- +title: : Invalid formula grouping context (Deployment assistant) +description: How to solve Salesforce deployment error Invalid custom summary formula definition: You must select a grouping context to use any report summary function +--- + +# Invalid formula grouping context + +## Detection + +- String: `Invalid custom summary formula definition: You must select a grouping context to use any report summary function` + +## Resolution + +```shell +You need to update your Report definition. See workaround here -> https://salesforce.stackexchange.com/questions/294850/grouping-error-with-prevgroupval-function +``` diff --git a/docs/sf-deployment-assistant/Invalid-report-type.md b/docs/sf-deployment-assistant/Invalid-report-type.md new file mode 100644 index 000000000..65e253a1c --- /dev/null +++ b/docs/sf-deployment-assistant/Invalid-report-type.md @@ -0,0 +1,18 @@ +--- +title: : Invalid report type (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) invalid report type/gm +--- + +# Invalid report type + +## Detection + +- RegExp: `Error (.*) invalid report type` + +## Resolution + +```shell +Report type is missing for report {1} +- Open report {1} to se what report type is used +- Retrieve the report type from an org and add it to the sfdx sources +``` diff --git a/docs/sf-deployment-assistant/Invalid-scope-Mine--not-allowed.md b/docs/sf-deployment-assistant/Invalid-scope-Mine--not-allowed.md new file mode 100644 index 000000000..00e9362f5 --- /dev/null +++ b/docs/sf-deployment-assistant/Invalid-scope-Mine--not-allowed.md @@ -0,0 +1,19 @@ +--- +title: : Invalid scope:Mine, not allowed (Deployment assistant) +description: How to solve Salesforce deployment error Invalid scope:Mine, not allowed +--- + +# Invalid scope:Mine, not allowed + +## Detection + +- String: `Invalid scope:Mine, not allowed` + +## Resolution + +```shell +Replace Mine by Everything in the list view SFDX source XML. +Have a look at this command to manage that automatically :) +https://sfdx-hardis.cloudity.com/hardis/org/fix/listviewmine/ + +``` diff --git a/docs/sf-deployment-assistant/Invalid-type.md b/docs/sf-deployment-assistant/Invalid-type.md new file mode 100644 index 000000000..78c4ad5d3 --- /dev/null +++ b/docs/sf-deployment-assistant/Invalid-type.md @@ -0,0 +1,16 @@ +--- +title: : Invalid type (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) Invalid type: (.*) \((.*)\)/gm +--- + +# Invalid type + +## Detection + +- RegExp: `Error (.*) Invalid type: (.*) \((.*)\)` + +## Resolution + +```shell +Apex error in {1} with unknown type {2} at position {3}. If {2} is a class name, try to fix it, or maybe it is missing in the files or in package.xml ! +``` diff --git a/docs/sf-deployment-assistant/Mandatory-custom-field-can-not-be-in-a-profile-permission-set.md b/docs/sf-deployment-assistant/Mandatory-custom-field-can-not-be-in-a-profile-permission-set.md new file mode 100644 index 000000000..f93aceb23 --- /dev/null +++ b/docs/sf-deployment-assistant/Mandatory-custom-field-can-not-be-in-a-profile-permission-set.md @@ -0,0 +1,24 @@ +--- +title: : Mandatory custom field can not be in a profile/permission set (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) You cannot deploy to a required field: (.*)/gm +--- + +# Mandatory custom field can not be in a profile/permission set + +## Detection + +- RegExp: `Error (.*) You cannot deploy to a required field: (.*)` + +## Resolution + +```shell + +- Search for {2} in source file XML of {1}, then remove the entries matching the results +Example of element to delete: + + true + {2} + true + + +``` diff --git a/docs/sf-deployment-assistant/Missing-Data-Category-Group.md b/docs/sf-deployment-assistant/Missing-Data-Category-Group.md new file mode 100644 index 000000000..8846bdfa2 --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-Data-Category-Group.md @@ -0,0 +1,19 @@ +--- +title: : Missing Data Category Group (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) In field: DeveloperName - no DataCategoryGroup named (.*) found/gm +--- + +# Missing Data Category Group + +## Detection + +- RegExp: `Error (.*) In field: DeveloperName - no DataCategoryGroup named (.*) found` + +## Resolution + +```shell +If Data Category Group {2} is not existing yet in target org, you might need to: +- create it manually in target org before deployment +- comment DataCategoryGroup in {1} XML + +``` diff --git a/docs/sf-deployment-assistant/Missing-Feature-Work-Com.md b/docs/sf-deployment-assistant/Missing-Feature-Work-Com.md new file mode 100644 index 000000000..b9ed5a6bf --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-Feature-Work-Com.md @@ -0,0 +1,17 @@ +--- +title: : Missing Feature Work.Com (Deployment assistant) +description: How to solve Salesforce deployment error WorkBadgeDefinition +--- + +# Missing Feature Work.Com + +## Detection + +- String: `WorkBadgeDefinition` + +## Resolution + +```shell +Work.com feature must be activated in the target org. +- Org & Scratch: https://developer.salesforce.com/docs/atlas.en-us.workdotcom_dev_guide.meta/workdotcom_dev_guide/wdc_cc_setup_dev_org.htm +``` diff --git a/docs/sf-deployment-assistant/Missing-Quick-Action.md b/docs/sf-deployment-assistant/Missing-Quick-Action.md new file mode 100644 index 000000000..7a5b4b9e5 --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-Quick-Action.md @@ -0,0 +1,23 @@ +--- +title: : Missing Quick Action (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) In field: QuickAction - no QuickAction named (.*) found/gm +--- + +# Missing Quick Action + +## Detection + +- RegExp: `Error (.*) In field: QuickAction - no QuickAction named (.*) found` + +## Resolution + +```shell +QuickAction {2} referred in {1} is unknown. You can either: +- Make sure your QuickAction {2} is present in source files and in package.xml +- If {2} is a standard QuickAction, activate related feature in target org +- Solve other errors that could impact QuickAction {2} +- Remove QuickAction {2} in the source XML of {1}. Example of XML to remove below: + + FeedItem.RypplePost + +``` diff --git a/docs/sf-deployment-assistant/Missing-Sales-Team.md b/docs/sf-deployment-assistant/Missing-Sales-Team.md new file mode 100644 index 000000000..74324f9b1 --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-Sales-Team.md @@ -0,0 +1,22 @@ +--- +title: : Missing Sales Team (Deployment assistant) +description: How to solve Salesforce deployment error related list:RelatedAccountSalesTeam +--- + +# Missing Sales Team + +## Detection + +- String: `related list:RelatedAccountSalesTeam` + +## Resolution + +```shell +Account Teams must be activated in the target org. +- Org: Setup -> Account Teams -> Enable +- Scratch org setting: +"accountSettings": { + "enableAccountTeams": true +} +} +``` diff --git a/docs/sf-deployment-assistant/Missing-e-mail-template.md b/docs/sf-deployment-assistant/Missing-e-mail-template.md new file mode 100644 index 000000000..1a1359094 --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-e-mail-template.md @@ -0,0 +1,17 @@ +--- +title: : Missing e-mail template (Deployment assistant) +description: How to solve Salesforce deployment error /In field: template - no EmailTemplate named (.*) found/gm +--- + +# Missing e-mail template + +## Detection + +- RegExp: `In field: template - no EmailTemplate named (.*) found` + +## Resolution + +```shell +An email template should be present in the sources. To retrieve it, you can run: +sf project retrieve start -m EmailTemplate:{1} -o YOUR_ORG_USERNAME +``` diff --git a/docs/sf-deployment-assistant/Missing-feature-Chatter-Collaboration-Group.md b/docs/sf-deployment-assistant/Missing-feature-Chatter-Collaboration-Group.md new file mode 100644 index 000000000..a2318571e --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-feature-Chatter-Collaboration-Group.md @@ -0,0 +1,21 @@ +--- +title: : Missing feature Chatter Collaboration Group (Deployment assistant) +description: How to solve Salesforce deployment error CollaborationGroup +--- + +# Missing feature Chatter Collaboration Group + +## Detection + +- String: `CollaborationGroup` + +## Resolution + +```shell +Quotes must be activated in the target org. +- Org: Setup -> Chatter settings -> Allow Records in Groups +- Scratch org setting: +"chatterSettings": { + "allowRecordsInChatterGroup": true +}, +``` diff --git a/docs/sf-deployment-assistant/Missing-feature-ContactToMultipleAccounts.md b/docs/sf-deployment-assistant/Missing-feature-ContactToMultipleAccounts.md new file mode 100644 index 000000000..2ff4f8fbf --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-feature-ContactToMultipleAccounts.md @@ -0,0 +1,20 @@ +--- +title: : Missing feature ContactToMultipleAccounts (Deployment assistant) +description: How to solve Salesforce deployment error no CustomObject named AccountContactRelation found +--- + +# Missing feature ContactToMultipleAccounts + +## Detection + +- String: `no CustomObject named AccountContactRelation found` +- String: `Invalid field:ACCOUNT.NAME in related list:RelatedContactAccountRelationList` + +## Resolution + +```shell +Contacts to multiple accounts be activated in the target org. +- Help: https://help.salesforce.com/articleView?id=sf.shared_contacts_set_up.htm&type=5 +- Scratch org setting: +"features": ["ContactsToMultipleAccounts"] +``` diff --git a/docs/sf-deployment-assistant/Missing-feature-Enhanced-notes.md b/docs/sf-deployment-assistant/Missing-feature-Enhanced-notes.md new file mode 100644 index 000000000..984a12240 --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-feature-Enhanced-notes.md @@ -0,0 +1,21 @@ +--- +title: : Missing feature Enhanced notes (Deployment assistant) +description: How to solve Salesforce deployment error FeedItem.ContentNote +--- + +# Missing feature Enhanced notes + +## Detection + +- String: `FeedItem.ContentNote` + +## Resolution + +```shell +Enhanced Notes must be activated in the target org. +- Org: Setup -> Notes settings -> Enable Notes +- Scratch org setting: +"enhancedNotesSettings": { + "enableEnhancedNotes": true +}, +``` diff --git a/docs/sf-deployment-assistant/Missing-feature-Ideas-notes.md b/docs/sf-deployment-assistant/Missing-feature-Ideas-notes.md new file mode 100644 index 000000000..ca3e8d828 --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-feature-Ideas-notes.md @@ -0,0 +1,21 @@ +--- +title: : Missing feature Ideas notes (Deployment assistant) +description: How to solve Salesforce deployment error Idea.InternalIdeasIdeaRecordType +--- + +# Missing feature Ideas notes + +## Detection + +- String: `Idea.InternalIdeasIdeaRecordType` + +## Resolution + +```shell +Ideas must be activated in the target org. +- Org: https://help.salesforce.com/articleView?id=networks_enable_ideas.htm&type=0 +- Scratch org setting: +"ideasSettings": { + "enableIdeas": true +} +``` diff --git a/docs/sf-deployment-assistant/Missing-feature-Live-Agent.md b/docs/sf-deployment-assistant/Missing-feature-Live-Agent.md new file mode 100644 index 000000000..b56d7a8d0 --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-feature-Live-Agent.md @@ -0,0 +1,18 @@ +--- +title: : Missing feature Live Agent (Deployment assistant) +description: How to solve Salesforce deployment error FeedItem.ContentNote +--- + +# Missing feature Live Agent + +## Detection + +- String: `FeedItem.ContentNote` + +## Resolution + +```shell +Live Agent must be activated in the target org. +- Org: Setup -> Live Agent Settings -> Enable Live Agent +- Scratch org feature: LiveAgent +``` diff --git a/docs/sf-deployment-assistant/Missing-feature-Opportunity-Teams.md b/docs/sf-deployment-assistant/Missing-feature-Opportunity-Teams.md new file mode 100644 index 000000000..e57929853 --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-feature-Opportunity-Teams.md @@ -0,0 +1,21 @@ +--- +title: : Missing feature Opportunity Teams (Deployment assistant) +description: How to solve Salesforce deployment error OpportunityTeam +--- + +# Missing feature Opportunity Teams + +## Detection + +- String: `OpportunityTeam` + +## Resolution + +```shell +Opportunity Teams must be activated in the target org. +- Org: Setup -> Opportunity Team Settings -> Enable Team Selling +- Scratch org: +"opportunitySettings": { + "enableOpportunityTeam": true +} +``` diff --git a/docs/sf-deployment-assistant/Missing-feature-Product-Request.md b/docs/sf-deployment-assistant/Missing-feature-Product-Request.md new file mode 100644 index 000000000..2f4fea3ee --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-feature-Product-Request.md @@ -0,0 +1,18 @@ +--- +title: : Missing feature Product Request (Deployment assistant) +description: How to solve Salesforce deployment error ProductRequest +--- + +# Missing feature Product Request + +## Detection + +- String: `ProductRequest` + +## Resolution + +```shell +ProductRequest object is not available in the target org. +Maybe you would like to clean its references within Profiles / PS using the following command ? +sf hardis:project:clean:references , then select "ProductRequest references" +``` diff --git a/docs/sf-deployment-assistant/Missing-feature-Social-Customer-Service.md b/docs/sf-deployment-assistant/Missing-feature-Social-Customer-Service.md new file mode 100644 index 000000000..b267feadf --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-feature-Social-Customer-Service.md @@ -0,0 +1,18 @@ +--- +title: : Missing feature Social Customer Service (Deployment assistant) +description: How to solve Salesforce deployment error SocialPersona.AreWeFollowing +--- + +# Missing feature Social Customer Service + +## Detection + +- String: `SocialPersona.AreWeFollowing` + +## Resolution + +```shell +Social Custom Service must be activated in the target org. +- Org: Setup -> https://help.salesforce.com/articleView?id=sf.social_customer_service_setup_enable.htm&type=5 +- Scratch org feature: SocialCustomerService +``` diff --git a/docs/sf-deployment-assistant/Missing-feature-Translation-Workbench.md b/docs/sf-deployment-assistant/Missing-feature-Translation-Workbench.md new file mode 100644 index 000000000..904f27986 --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-feature-Translation-Workbench.md @@ -0,0 +1,22 @@ +--- +title: : Missing feature Translation Workbench (Deployment assistant) +description: How to solve Salesforce deployment error /report-meta.xml(.*)filterlanguage/gm +--- + +# Missing feature Translation Workbench + +## Detection + +- RegExp: `report-meta.xml(.*)filterlanguage` + +## Resolution + +```shell +Translation workbench must be activated in the target org. +- Org: Setup -> https://help.salesforce.com/articleView?id=sf.customize_wbench.htm&type=5 +- Scratch org: +"languageSettings": { + "enableTranslationWorkbench": true, + "enableEndUserLanguages": true +} +``` diff --git a/docs/sf-deployment-assistant/Missing-field-MiddleName.md b/docs/sf-deployment-assistant/Missing-field-MiddleName.md new file mode 100644 index 000000000..74048ec18 --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-field-MiddleName.md @@ -0,0 +1,22 @@ +--- +title: : Missing field MiddleName (Deployment assistant) +description: How to solve Salesforce deployment error field MiddleName +--- + +# Missing field MiddleName + +## Detection + +- String: `field MiddleName` +- String: `Variable does not exist: MiddleName` + +## Resolution + +```shell +MiddleNames must be activated in the target org. +- Help: https://help.salesforce.com/articleView?id=000332623&type=1&mode=1 +- Scratch org setting: +"nameSettings": { + "enableMiddleName": true +} +``` diff --git a/docs/sf-deployment-assistant/Missing-field-Suffix.md b/docs/sf-deployment-assistant/Missing-field-Suffix.md new file mode 100644 index 000000000..065b9021b --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-field-Suffix.md @@ -0,0 +1,21 @@ +--- +title: : Missing field Suffix (Deployment assistant) +description: How to solve Salesforce deployment error field Suffix +--- + +# Missing field Suffix + +## Detection + +- String: `field Suffix` + +## Resolution + +```shell +Suffix must be activated in the target org. +- Help: https://help.salesforce.com/articleView?id=000332623&type=1&mode=1 +- Scratch org setting: +"nameSettings": { + "enableNameSuffix": true +}, +``` diff --git a/docs/sf-deployment-assistant/Missing-field-SyncedQuoteId.md b/docs/sf-deployment-assistant/Missing-field-SyncedQuoteId.md new file mode 100644 index 000000000..d0e49fa30 --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-field-SyncedQuoteId.md @@ -0,0 +1,23 @@ +--- +title: : Missing field SyncedQuoteId (Deployment assistant) +description: How to solve Salesforce deployment error field SyncedQuoteId +--- + +# Missing field SyncedQuoteId + +## Detection + +- String: `field SyncedQuoteId` +- String: `Error force-app/main/default/objects/Quote/Quote.object-meta.xml` +- String: `Error force-app/main/default/objects/Opportunity/fields/SyncedQuoteId.field-meta.xml` + +## Resolution + +```shell +Quotes must be activated in the target org. +- Help: https://help.salesforce.com/articleView?id=sf.quotes_enable.htm&type=5 +- Scratch org setting: +"quoteSettings": { + "enableQuote": true +} +``` diff --git a/docs/sf-deployment-assistant/Missing-multi-currency-field.md b/docs/sf-deployment-assistant/Missing-multi-currency-field.md new file mode 100644 index 000000000..37d8573a5 --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-multi-currency-field.md @@ -0,0 +1,16 @@ +--- +title: : Missing multi-currency field (Deployment assistant) +description: How to solve Salesforce deployment error /A reference to a custom field (.*)CurrencyIsoCode/gm +--- + +# Missing multi-currency field + +## Detection + +- RegExp: `A reference to a custom field (.*)CurrencyIsoCode` + +## Resolution + +```shell +You probably need to activate MultiCurrency (from Setup -> Company information) +``` diff --git a/docs/sf-deployment-assistant/Missing-object-referenced-in-package-xml.md b/docs/sf-deployment-assistant/Missing-object-referenced-in-package-xml.md new file mode 100644 index 000000000..b45068650 --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-object-referenced-in-package-xml.md @@ -0,0 +1,18 @@ +--- +title: : Missing object referenced in package.xml (Deployment assistant) +description: How to solve Salesforce deployment error /An object (.*) of type (.*) was named in package.xml, but was not found in zipped directory/gm +--- + +# Missing object referenced in package.xml + +## Detection + +- RegExp: `An object (.*) of type (.*) was named in package.xml, but was not found in zipped directory` + +## Resolution + +```shell +You can either: +- Update the package.xml to remove the reference to the missing {2} {1} +- Add the missing {2} {1} in your project source files +``` diff --git a/docs/sf-deployment-assistant/Missing-profile-default-application.md b/docs/sf-deployment-assistant/Missing-profile-default-application.md new file mode 100644 index 000000000..aefe4c792 --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-profile-default-application.md @@ -0,0 +1,24 @@ +--- +title: : Missing profile default application (Deployment assistant) +description: How to solve Salesforce deployment error You can't remove the only default app from the profile. +--- + +# Missing profile default application + +## Detection + +- String: `You can't remove the only default app from the profile.` + +## Resolution + +```shell +You must have a default application for a profile. You can: + - Update it in UI + - Update the XML of the profile to set "true" in the tag of one of the applicationVisibilities item. + Ex: + + standard__LightningSales + true + true + +``` diff --git a/docs/sf-deployment-assistant/Missing-report.md b/docs/sf-deployment-assistant/Missing-report.md new file mode 100644 index 000000000..6026ba4d0 --- /dev/null +++ b/docs/sf-deployment-assistant/Missing-report.md @@ -0,0 +1,19 @@ +--- +title: : Missing report (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) The (.*) report chart has a problem with the "reportName" field/gm +--- + +# Missing report + +## Detection + +- RegExp: `Error (.*) The (.*) report chart has a problem with the "reportName" field` + +## Resolution + +```shell +{1} is referring to unknown report {2}. To retrieve it, you can run: +- sf project retrieve start -m Report:{2} -o YOUR_ORG_USERNAME +- If it fails, looks for the report folder and add it before report name to the retrieve command (ex: MYFOLDER/MYREPORTNAME) + +``` diff --git a/docs/sf-deployment-assistant/Network-issue.md b/docs/sf-deployment-assistant/Network-issue.md new file mode 100644 index 000000000..81527b7bf --- /dev/null +++ b/docs/sf-deployment-assistant/Network-issue.md @@ -0,0 +1,18 @@ +--- +title: : Network issue (Deployment assistant) +description: How to solve Salesforce deployment error ECONNABORTED +--- + +# Network issue + +## Detection + +- String: `ECONNABORTED` +- String: `ECONNRESET` + +## Resolution + +```shell +The network connection has been aborted, this is a purely technical issue. +Try again, and if you still see errors, check the status of Salesforce instance on https://status.salesforce.com +``` diff --git a/docs/sf-deployment-assistant/Not-available-for-deploy-for-this-organization.md b/docs/sf-deployment-assistant/Not-available-for-deploy-for-this-organization.md new file mode 100644 index 000000000..8f77eb4f7 --- /dev/null +++ b/docs/sf-deployment-assistant/Not-available-for-deploy-for-this-organization.md @@ -0,0 +1,17 @@ +--- +title: : Not available for deploy for this organization (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) Not available for deploy for this organization/gm +--- + +# Not available for deploy for this organization + +## Detection + +- RegExp: `Error (.*) Not available for deploy for this organization` + +## Resolution + +```shell +The user you use for deployments probably lacks of the rights (Profiles, Permission sets...) to manage {1}. +- Assign the deployment user to the good Permission Sets, or modify its profile rights, then try again +``` diff --git a/docs/sf-deployment-assistant/Not-valid-sharing-model.md b/docs/sf-deployment-assistant/Not-valid-sharing-model.md new file mode 100644 index 000000000..896e0242c --- /dev/null +++ b/docs/sf-deployment-assistant/Not-valid-sharing-model.md @@ -0,0 +1,20 @@ +--- +title: : Not valid sharing model (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) (.*) is not a valid sharing model for (.*) when (.*) sharing model is (.*)/gm +--- + +# Not valid sharing model + +## Detection + +- RegExp: `Error (.*) (.*) is not a valid sharing model for (.*) when (.*) sharing model is (.*)` + +## Resolution + +```shell +It seems that Sharing Models of {1} and {4} are not compatible in target org. +- Use compatible sharing models between {1} and {4} by updating Sharing model of {1} or {4} +- Make sure that sfdx sources {1}.object-meta.xml and {4}.object-meta.xml and in the files, and that {1} and {4} are in package.xml in CustomObject block +- You may directly update sharingModel in XML. For example, replace ReadWrite by Private in {3}.object-meta.xml + +``` diff --git a/docs/sf-deployment-assistant/Objects-rights-on-a-role-is-below-org-default.md b/docs/sf-deployment-assistant/Objects-rights-on-a-role-is-below-org-default.md new file mode 100644 index 000000000..3ec870741 --- /dev/null +++ b/docs/sf-deployment-assistant/Objects-rights-on-a-role-is-below-org-default.md @@ -0,0 +1,19 @@ +--- +title: : Objects rights on a role is below org default (Deployment assistant) +description: How to solve Salesforce deployment error access level below organization default +--- + +# Objects rights on a role is below org default + +## Detection + +- String: `access level below organization default` + +## Resolution + +```shell +Your org wide settings default must be lower than the level defined in roles: +- If you are in a scratch org, it can be fixable using "objectProperties" in project-scratch-def.json (see "Set Object-Level Sharing Settings" paragraph in page https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_scratch_orgs_def_file.htm) +- If you are in a sandbox/dev/prod org, you need to update default org wide settings before deployment. See https://www.sfdcpoint.com/salesforce/organization-wide-defaults-owd-in-salesforce/ + +``` diff --git a/docs/sf-deployment-assistant/Picklist-sharing-is-not-supported.md b/docs/sf-deployment-assistant/Picklist-sharing-is-not-supported.md new file mode 100644 index 000000000..e2eb6e566 --- /dev/null +++ b/docs/sf-deployment-assistant/Picklist-sharing-is-not-supported.md @@ -0,0 +1,18 @@ +--- +title: : Picklist sharing is not supported (Deployment assistant) +description: How to solve Salesforce deployment error Picklist sharing is not supported +--- + +# Picklist sharing is not supported + +## Detection + +- String: `Picklist sharing is not supported` + +## Resolution + +```shell +You probably changed the type of a field. +Go manually make the change in the target org, so the deployment will pass + +``` diff --git a/docs/sf-deployment-assistant/Picklist-value-not-found.md b/docs/sf-deployment-assistant/Picklist-value-not-found.md new file mode 100644 index 000000000..615b0dbab --- /dev/null +++ b/docs/sf-deployment-assistant/Picklist-value-not-found.md @@ -0,0 +1,19 @@ +--- +title: : Picklist value not found (Deployment assistant) +description: How to solve Salesforce deployment error /Picklist value: (.*) in picklist: (.*) not found/gm +--- + +# Picklist value not found + +## Detection + +- RegExp: `Picklist value: (.*) in picklist: (.*) not found` + +## Resolution + +```shell +Sources have references to value {1} of picklist {2} +- If picklist {2} is standard, add the picklist to sfdx sources by using "sf project retrieve start -m StandardValueSet:{2}", then save again +- Else, perform a search in all code of {1}, then remove XML tags referring to {1} (for example in record types metadatas) + +``` diff --git a/docs/sf-deployment-assistant/Please-choose-a-different-name.md b/docs/sf-deployment-assistant/Please-choose-a-different-name.md new file mode 100644 index 000000000..f2f75b310 --- /dev/null +++ b/docs/sf-deployment-assistant/Please-choose-a-different-name.md @@ -0,0 +1,18 @@ +--- +title: : Please choose a different name (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) This (.*) already exists or has been previously used(.*)Please choose a different name./gm +--- + +# Please choose a different name + +## Detection + +- RegExp: `Error (.*) This (.*) already exists or has been previously used(.*)Please choose a different name.` + +## Resolution + +```shell +- Rename {1} in the target org, then try again the deployment. if it succeeds, delete the renamed item. +- or Delete {1} in the target org, then try again the deployment + +``` diff --git a/docs/sf-deployment-assistant/Record-Type-not-found.md b/docs/sf-deployment-assistant/Record-Type-not-found.md new file mode 100644 index 000000000..591b14a1b --- /dev/null +++ b/docs/sf-deployment-assistant/Record-Type-not-found.md @@ -0,0 +1,19 @@ +--- +title: : Record Type not found (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) In field: recordType - no RecordType named (.*) found/gm +--- + +# Record Type not found + +## Detection + +- RegExp: `Error (.*) In field: recordType - no RecordType named (.*) found` + +## Resolution + +```shell +An unknown record type {2} is referenced in {1} +- If record type {2} is not supposed to exist, perform a search in all files of {1}, then remove matching XML elements referring to this record type +- If record type {2} is supposed to exist, you may have to create it manually in the target org to make the deployment pass + +``` diff --git a/docs/sf-deployment-assistant/Send-email-is-disabled.md b/docs/sf-deployment-assistant/Send-email-is-disabled.md new file mode 100644 index 000000000..abb75a874 --- /dev/null +++ b/docs/sf-deployment-assistant/Send-email-is-disabled.md @@ -0,0 +1,17 @@ +--- +title: : Send email is disabled (Deployment assistant) +description: How to solve Salesforce deployment error Send Email is disabled or activities are not allowed +--- + +# Send email is disabled + +## Detection + +- String: `Send Email is disabled or activities are not allowed` +- String: `Unknown user permission: SendExternalEmailAvailable` + +## Resolution + +```shell +Go to Email -> Deliverability -> Select value "All emails" +``` diff --git a/docs/sf-deployment-assistant/Sharing-recalculation-lock.md b/docs/sf-deployment-assistant/Sharing-recalculation-lock.md new file mode 100644 index 000000000..bfc27de85 --- /dev/null +++ b/docs/sf-deployment-assistant/Sharing-recalculation-lock.md @@ -0,0 +1,17 @@ +--- +title: : Sharing recalculation lock (Deployment assistant) +description: How to solve Salesforce deployment error because it interferes with another operation already in progress +--- + +# Sharing recalculation lock + +## Detection + +- String: `because it interferes with another operation already in progress` +- String: `Le calcul de partage demandé ne peut être traité maintenant car il interfère avec une autre opération en cours` + +## Resolution + +```shell +If you changed a field from MasterDetail to Lookup, you must do it manually in the target org before being able to deploy +``` diff --git a/docs/sf-deployment-assistant/Sort-order-must-be-in-sequential-order.md b/docs/sf-deployment-assistant/Sort-order-must-be-in-sequential-order.md new file mode 100644 index 000000000..a7060a509 --- /dev/null +++ b/docs/sf-deployment-assistant/Sort-order-must-be-in-sequential-order.md @@ -0,0 +1,17 @@ +--- +title: : Sort order must be in sequential order (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) SortOrder must be in sequential order from/gm +--- + +# Sort order must be in sequential order + +## Detection + +- RegExp: `Error (.*) SortOrder must be in sequential order from` + +## Resolution + +```shell +You probably have a default DuplicateRule in the target org. Retrieve it from target org, or delete it manually in target org, so you can deploy. +Ref: https://developer.salesforce.com/forums/?id=9060G000000I6SoQAK +``` diff --git a/docs/sf-deployment-assistant/Tableau-CRM---Wave-digest-error.md b/docs/sf-deployment-assistant/Tableau-CRM---Wave-digest-error.md new file mode 100644 index 000000000..043f91098 --- /dev/null +++ b/docs/sf-deployment-assistant/Tableau-CRM---Wave-digest-error.md @@ -0,0 +1,16 @@ +--- +title: : Tableau CRM / Wave digest error (Deployment assistant) +description: How to solve Salesforce deployment error Fix the sfdcDigest node errors and then upload the file again +--- + +# Tableau CRM / Wave digest error + +## Detection + +- String: `Fix the sfdcDigest node errors and then upload the file again` + +## Resolution + +```shell +Go to the target org, open profile "Analytics Cloud Integration User" and add READ rights to the missing object fields +``` diff --git a/docs/sf-deployment-assistant/Test-classes-with-0--coverage.md b/docs/sf-deployment-assistant/Test-classes-with-0--coverage.md new file mode 100644 index 000000000..eeb5788c2 --- /dev/null +++ b/docs/sf-deployment-assistant/Test-classes-with-0--coverage.md @@ -0,0 +1,16 @@ +--- +title: : Test classes with 0% coverage (Deployment assistant) +description: How to solve Salesforce deployment error / 0%/gm +--- + +# Test classes with 0% coverage + +## Detection + +- RegExp: `0%` + +## Resolution + +```shell +Please make sure that none of the test classes are 0% covered +``` diff --git a/docs/sf-deployment-assistant/Unknown-user-permission--CreateAuditFields.md b/docs/sf-deployment-assistant/Unknown-user-permission--CreateAuditFields.md new file mode 100644 index 000000000..b4db58984 --- /dev/null +++ b/docs/sf-deployment-assistant/Unknown-user-permission--CreateAuditFields.md @@ -0,0 +1,17 @@ +--- +title: : Unknown user permission: CreateAuditFields (Deployment assistant) +description: How to solve Salesforce deployment error Unknown user permission: CreateAuditFields +--- + +# Unknown user permission: CreateAuditFields + +## Detection + +- String: `Unknown user permission: CreateAuditFields` + +## Resolution + +```shell +You need to enable the "Create audit field" permission in the target org +Please check https://help.salesforce.com/articleView?id=000334139&type=1&mode=1 +``` diff --git a/docs/sf-deployment-assistant/Unknown-user-permission--FieldServiceAccess.md b/docs/sf-deployment-assistant/Unknown-user-permission--FieldServiceAccess.md new file mode 100644 index 000000000..02447f2ce --- /dev/null +++ b/docs/sf-deployment-assistant/Unknown-user-permission--FieldServiceAccess.md @@ -0,0 +1,17 @@ +--- +title: : Unknown user permission: FieldServiceAccess (Deployment assistant) +description: How to solve Salesforce deployment error Unknown user permission: FieldServiceAccess +--- + +# Unknown user permission: FieldServiceAccess + +## Detection + +- String: `Unknown user permission: FieldServiceAccess` + +## Resolution + +```shell +You need to enable the "Field Service Access" permission in the target org +Please check https://help.salesforce.com/articleView?id=sf.fs_enable.htm&type=5 +``` diff --git a/docs/sf-deployment-assistant/Unknown-user-permission.md b/docs/sf-deployment-assistant/Unknown-user-permission.md new file mode 100644 index 000000000..0786af3cb --- /dev/null +++ b/docs/sf-deployment-assistant/Unknown-user-permission.md @@ -0,0 +1,18 @@ +--- +title: : Unknown user permission (Deployment assistant) +description: How to solve Salesforce deployment error Unknown user permission: +--- + +# Unknown user permission + +## Detection + +- String: `Unknown user permission:` + +## Resolution + +```shell +You can: +- enable the related permission in the target org +- or remove references to the permission in source XML files (Probably a Profile or a Permission set) +``` diff --git a/docs/sf-deployment-assistant/Unsupported-sharing-configuration.md b/docs/sf-deployment-assistant/Unsupported-sharing-configuration.md new file mode 100644 index 000000000..e897b448a --- /dev/null +++ b/docs/sf-deployment-assistant/Unsupported-sharing-configuration.md @@ -0,0 +1,18 @@ +--- +title: : Unsupported sharing configuration (Deployment assistant) +description: How to solve Salesforce deployment error /not supported for (.*) since it's org wide default is/gm +--- + +# Unsupported sharing configuration + +## Detection + +- RegExp: `not supported for (.*) since it's org wide default is` + +## Resolution + +```shell +Consistency error between {1} sharing settings and {1} object configuration +Please check https://salesforce.stackexchange.com/questions/260923/sfdx-deploying-contact-sharing-rules-on-a-fresh-deployment +If you already did that, please try again to run the job +``` diff --git a/docs/sf-deployment-assistant/Variable-does-not-exist.md b/docs/sf-deployment-assistant/Variable-does-not-exist.md new file mode 100644 index 000000000..84ffc475b --- /dev/null +++ b/docs/sf-deployment-assistant/Variable-does-not-exist.md @@ -0,0 +1,16 @@ +--- +title: : Variable does not exist (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) Variable does not exist: (.*) \((.*)\)/gm +--- + +# Variable does not exist + +## Detection + +- RegExp: `Error (.*) Variable does not exist: (.*) \((.*)\)` + +## Resolution + +```shell +Apex error in {1} with unknown variable {2} at position {3}. If {2} is a class name, try to fix it, or maybe it is missing in the files or in package.xml ! +``` diff --git a/docs/sf-deployment-assistant/Visibility-is-not-allowed-for-type.md b/docs/sf-deployment-assistant/Visibility-is-not-allowed-for-type.md new file mode 100644 index 000000000..d57352fd9 --- /dev/null +++ b/docs/sf-deployment-assistant/Visibility-is-not-allowed-for-type.md @@ -0,0 +1,16 @@ +--- +title: : Visibility is not allowed for type (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) set the visibility for a (.*) to Protected unless you are in a developer/gm +--- + +# Visibility is not allowed for type + +## Detection + +- RegExp: `Error (.*) set the visibility for a (.*) to Protected unless you are in a developer` + +## Resolution + +```shell +Update the visibility of {1} to "Public" +``` diff --git a/docs/sf-deployment-assistant/XML-item-appears-more-than-once.md b/docs/sf-deployment-assistant/XML-item-appears-more-than-once.md new file mode 100644 index 000000000..9ea3c8fc7 --- /dev/null +++ b/docs/sf-deployment-assistant/XML-item-appears-more-than-once.md @@ -0,0 +1,18 @@ +--- +title: : XML item appears more than once (Deployment assistant) +description: How to solve Salesforce deployment error /Error (.*) Field:(.*), value:(.*) appears more than once/gm +--- + +# XML item appears more than once + +## Detection + +- RegExp: `Error (.*) Field:(.*), value:(.*) appears more than once` + +## Resolution + +```shell +You probably made an error while merging conflicts +Look for {3} in the XML of {1} +If you see two {2} XML blocks with {3}, please decide which one you keep and remove the other one +``` diff --git a/docs/sf-deployment-assistant/sharing-operation-already-in-progress.md b/docs/sf-deployment-assistant/sharing-operation-already-in-progress.md new file mode 100644 index 000000000..8735fe298 --- /dev/null +++ b/docs/sf-deployment-assistant/sharing-operation-already-in-progress.md @@ -0,0 +1,20 @@ +--- +title: : sharing operation already in progress (Deployment assistant) +description: How to solve Salesforce deployment error sharing operation already in progress +--- + +# sharing operation already in progress + +## Detection + +- String: `sharing operation already in progress` + +## Resolution + +```shell +You can not deploy multiple SharingRules at the same time. You can either: +- Remove SharingOwnerRules and SharingRule from package.xml (so it becomes a manual operation) +- Use sf hardis:work:save to generate a deploymentPlan in .sfdx-hardis.json, +- If you are trying to create a scratch org, add DeferSharingCalc in features in project-scratch-def.json + +``` diff --git a/messages/hello.world.md b/messages/hello.world.md new file mode 100644 index 000000000..804f848de --- /dev/null +++ b/messages/hello.world.md @@ -0,0 +1,29 @@ +# summary + +Say hello. + +# description + +Say hello either to the world or someone you know. + +# flags.name.summary + +The name of the person you'd like to say hello to. + +# flags.name.description + +This person can be anyone in the world! + +# examples + +- Say hello to the world: + + <%= config.bin %> <%= command.id %> + +- Say hello to someone you know: + + <%= config.bin %> <%= command.id %> --name Astro + +# info.hello + +Hello %s at %s. diff --git a/messages/org.json b/messages/org.json deleted file mode 100644 index de413d705..000000000 --- a/messages/org.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "allowPurgeFailure": "Allows purges to fail without exiting with 1. Use --no-allowpurgefailure to disable", - "apexTests": "Run apex test cases on org", - "auditApiVersion": "Audit API version", - "auditCallInCallOut": "Generate list of callIn and callouts from sfdx project", - "auditRemoteSites": "Generate list of remote sites", - "checkOnly": "Only checks the deployment, there is no impact on target org", - "createOrgShape": "Updates project-scratch-def.json from org shape", - "completeWorkTask": "When a work task is completed, guide user to create a merge request", - "dataTreeExport": "Assisted export data defined in .sfdx-hardis.yml", - "debugMode": "Activate debug mode (more logs)", - "deployMetadatas": "Deploy metadatas to source org", - "exceptFilter": "Allow to take all item except these criteria", - "failIfError": "Fails (exit code 1) if an error is found", - "filteredMetadatas": "Comma separated list of Metadatas keys to remove from PackageXml file", - "folder": "Folder", - "forceNewScratch": "If an existing scratch org exists, do not reuse it but create a new one", - "installFFLib": "Install FFLib in current project", - "instanceUrl": "URL of org instance", - "loginToOrg": "Login to salesforce org", - "minimumApiVersion": "Minimum allowed API version", - "nameFilter": "Filter according to Name criteria", - "newWorkTask": "New work task", - "orgFreezeUser": "Freeze mass users in org for maintenance or go live purpose", - "orgDataExport": "Export data from org using sfdmu", - "orgDataImport": "Import data in org using sfdmu", - "orgDataDelete": "Delete data in org using sfdmu", - "orgPurgeFlow": "Purge Obsolete flow versions to avoid the 50 max versions limit. Filters on Status and Name", - "orgfreezeUser": "Mass freeze users in org before a maintenance or go live\n\nSee user guide in the following article\n\n\n\n[![How to freeze / unfreeze users during a Salesforce deployment](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-freeze.jpg)](https://medium.com/@dimitrimonge/freeze-unfreeze-users-during-salesforce-deployment-8a1488bf8dd3)", - "orgUnfreezeUser": "Mass unfreeze users in org after a maintenance or go live\n\nSee user guide in the following article\n\n\n\n[![How to freeze / unfreeze users during a Salesforce deployment](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-freeze.jpg)](https://medium.com/@dimitrimonge/freeze-unfreeze-users-during-salesforce-deployment-8a1488bf8dd3)", - "packageCreate": "Create a new package", - "packageInstall": "Install a package", - "packageInstallationKey": "installation key for key-protected package (default: null)", - "packageVersionCreate": "Create a new version of an unlocked package", - "packageVersionList": "List versions of unlocked package", - "packageXml": "Path to package.xml manifest file", - "prompt": "Prompt for confirmation (true by default, use --no-prompt to skip)", - "refreshWorkTask": "Make my local branch and my scratch org up to date with the most recent sources", - "retrieveDx": "Retrieve Salesforce DX project from org", - "retrieveMetadatas": "Retrieve metadatas from an org with package.xml manifest", - "sandboxLogin": "Use if the environment is a sandbox", - "scratch": "Scratch org", - "scratchCreate": "Create and initialize a scratch org so it is ready to use", - "rebuildSelection": "Process again the selection of the items that you want to publish to upper level", - "selectOrg": "Interactive org selection for user", - "statusFilter": "Filter according to Status criteria", - "tempFolder": "Temporary folder", - "testLevel": "Level of tests to apply to validate deployment", - "testLevelExtended": "Level of tests to validate deployment. RunRepositoryTests auto-detect and run all repository test classes", - "websocket": "Websocket host:port for VsCode SFDX Hardis UI integration", - "withDevHub": "Also connect associated DevHub", - "runtests": "Apex test classes to run if --testlevel is RunSpecifiedTests" -} diff --git a/messages/org.md b/messages/org.md new file mode 100644 index 000000000..87c0b4628 --- /dev/null +++ b/messages/org.md @@ -0,0 +1,211 @@ +# allowPurgeFailure + +Allows purges to fail without exiting with 1. Use --no-allowpurgefailure to disable + +# apexTests + +Run apex test cases on org + +# auditApiVersion + +Audit API version + +# auditCallInCallOut + +Generate list of callIn and callouts from sfdx project + +# auditRemoteSites + +Generate list of remote sites + +# checkOnly + +Only checks the deployment, there is no impact on target org + +# createOrgShape + +Updates project-scratch-def.json from org shape + +# completeWorkTask + +When a work task is completed, guide user to create a merge request + +# dataTreeExport + +Assisted export data defined in .sfdx-hardis.yml + +# debugMode + +Activate debug mode (more logs) + +# deployMetadatas + +Deploy metadatas to source org + +# exceptFilter + +Allow to take all item except these criteria + +# failIfError + +Fails (exit code 1) if an error is found + +# filteredMetadatas + +Comma separated list of Metadatas keys to remove from PackageXml file + +# folder + +Folder + +# forceNewScratch + +If an existing scratch org exists, do not reuse it but create a new one + +# installFFLib + +Install FFLib in current project + +# instanceUrl + +URL of org instance + +# loginToOrg + +Login to salesforce org + +# minimumApiVersion + +Minimum allowed API version + +# nameFilter + +Filter according to Name criteria + +# newWorkTask + +New work task + +# orgFreezeUser + +Freeze mass users in org for maintenance or go live purpose + +# orgDataExport + +Export data from org using sfdmu + +# orgDataImport + +Import data in org using sfdmu + +# orgPurgeFlow + +Purge Obsolete flow versions to avoid the 50 max versions limit. Filters on Status and Name + +# orgfreezeUser + +Mass freeze users in org before a maintenance or go live + +See user guide in the following article + + + +[![How to freeze / unfreeze users during a Salesforce deployment](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-freeze.jpg)](https://medium.com/@dimitrimonge/freeze-unfreeze-users-during-salesforce-deployment-8a1488bf8dd3) + +# orgUnfreezeUser + +Mass unfreeze users in org after a maintenance or go live + +See user guide in the following article + + + +[![How to freeze / unfreeze users during a Salesforce deployment](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-freeze.jpg)](https://medium.com/@dimitrimonge/freeze-unfreeze-users-during-salesforce-deployment-8a1488bf8dd3) + +# packageCreate + +Create a new package + +# packageInstall + +Install a package + +# packageInstallationKey + +installation key for key-protected package (default: null) + +# packageVersionCreate + +Create a new version of an unlocked package + +# packageVersionList + +List versions of unlocked package + +# packageXml + +Path to package.xml manifest file + +# prompt + +Prompt for confirmation (true by default, use --no-prompt to skip) + +# refreshWorkTask + +Make my local branch and my scratch org up to date with the most recent sources + +# retrieveDx + +Retrieve Salesforce DX project from org + +# retrieveMetadatas + +Retrieve metadatas from an org with package.xml manifest + +# sandboxLogin + +Use if the environment is a sandbox + +# scratch + +Scratch org + +# scratchCreate + +Create and initialize a scratch org so it is ready to use + +# rebuildSelection + +Process again the selection of the items that you want to publish to upper level + +# selectOrg + +Interactive org selection for user + +# statusFilter + +Filter according to Status criteria + +# tempFolder + +Temporary folder + +# testLevel + +Level of tests to apply to validate deployment + +# testLevelExtended + +Level of tests to validate deployment. RunRepositoryTests auto-detect and run all repository test classes + +# websocket + +Websocket host:port for VsCode SFDX Hardis UI integration + +# withDevHub + +Also connect associated DevHub + +# runtests + +Apex test classes to run if --testlevel is RunSpecifiedTests \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 6e4c0dbce..305d0b587 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -73,6 +73,7 @@ nav: mdapi: deploy: hardis/mdapi/deploy.md misc: + purge-references: hardis/misc/purge-references.md toml2csv: hardis/misc/toml2csv.md org: configure: @@ -87,12 +88,15 @@ nav: import: hardis/org/data/import.md diagnose: audittrail: hardis/org/diagnose/audittrail.md + instanceupgrade: hardis/org/diagnose/instanceupgrade.md legacyapi: hardis/org/diagnose/legacyapi.md licenses: hardis/org/diagnose/licenses.md + releaseupdates: hardis/org/diagnose/releaseupdates.md unusedlicenses: hardis/org/diagnose/unusedlicenses.md unusedusers: hardis/org/diagnose/unusedusers.md files: export: hardis/org/files/export.md + import: hardis/org/files/import.md fix: listviewmine: hardis/org/fix/listviewmine.md generate: @@ -127,6 +131,9 @@ nav: create: hardis/package/version/create.md list: hardis/package/version/list.md promote: hardis/package/version/promote.md + packagexml: + append: hardis/packagexml/append.md + remove: hardis/packagexml/remove.md project: audit: apiversion: hardis/project/audit/apiversion.md @@ -135,6 +142,7 @@ nav: remotesites: hardis/project/audit/remotesites.md clean: emptyitems: hardis/project/clean/emptyitems.md + filter-xml-content: hardis/project/clean/filter-xml-content.md flowpositions: hardis/project/clean/flowpositions.md hiddenitems: hardis/project/clean/hiddenitems.md listviews: hardis/project/clean/listviews.md @@ -143,6 +151,7 @@ nav: orgmissingitems: hardis/project/clean/orgmissingitems.md references: hardis/project/clean/references.md retrievefolders: hardis/project/clean/retrievefolders.md + sensitive-metadatas: hardis/project/clean/sensitive-metadatas.md standarditems: hardis/project/clean/standarditems.md systemdebug: hardis/project/clean/systemdebug.md xml: hardis/project/clean/xml.md @@ -152,9 +161,14 @@ nav: profilestopermsets: hardis/project/convert/profilestopermsets.md create: hardis/project/create.md deploy: + quick: hardis/project/deploy/quick.md + simulate: hardis/project/deploy/simulate.md + smart: hardis/project/deploy/smart.md sources: dx: hardis/project/deploy/sources/dx.md metadata: hardis/project/deploy/sources/metadata.md + start: hardis/project/deploy/start.md + validate: hardis/project/deploy/validate.md fix: profiletabs: hardis/project/fix/profiletabs.md v53flexipages: hardis/project/fix/v53flexipages.md @@ -184,6 +198,8 @@ nav: resetselection: hardis/work/resetselection.md save: hardis/work/save.md ws: hardis/work/ws.md + hello: + world: hello/world.md - Salesforce CI/CD: - CI/CD Home: salesforce-ci-cd-home.md - Contributor Guide: diff --git a/package.json b/package.json index 444386f8e..30a1c893b 100644 --- a/package.json +++ b/package.json @@ -1,99 +1,93 @@ { "name": "sfdx-hardis", - "description": "Swiss-army-knife Toolbox for Salesforce\nAllows you to define a complete CD/CD Pipeline\nOrchestrate base commands and assist users with interactive wizards", - "version": "4.53.0", + "description": "Swiss-army-knife Toolbox for Salesforce.\n Allows you to define a complete CD/CD Pipeline.\n Orchestrate base commands and assist users with interactive wizards", "author": "NicolasVuillamy @nvuillam", "bugs": "https://github.com/hardisgroupcom/sfdx-hardis/issues", + "version": "5.0.0", "dependencies": { - "@actions/github": "^5.1.1", - "@adobe/node-fetch-retry": "^1.1.2", - "@amplitude/node": "^1.3.2", - "@gitbeaker/node": "^35.8.0", - "@keyv/redis": "^2.1.2", - "@oclif/command": "^1", - "@oclif/config": "^1.18.3", - "@oclif/errors": "^1", - "@salesforce/command": "^4.2.1", - "@salesforce/core": "^2.33.1", - "@salesforce/ts-sinon": "^1.2.4", - "@salesforce/ts-types": "^1.5.20", - "@slack/web-api": "^6.9.0", + "@actions/github": "^6.0.0", + "@gitbeaker/node": "^35.8.1", + "@oclif/core": "^4.0.21", + "@salesforce/core": "^8.5.3", + "@salesforce/sf-plugins-core": "^11.3.7", + "@slack/types": "^2.12.0", + "@slack/web-api": "^7.3.4", "@supercharge/promise-pool": "^3.2.0", - "@types/mocha": "^8.2.1", - "@types/ws": "^7.4.0", - "@typescript-eslint/eslint-plugin": "^4.17.0", - "@typescript-eslint/parser": "^4.17.0", - "@xmldom/xmldom": "^0.8.6", - "axios": "^1.7.4", - "azure-devops-node-api": "^12.0.0", - "bitbucket": "^2.11.0", - "chalk": "^4.1.0", - "columnify": "^1.5.4", - "cosmiconfig": "^7.0.0", + "@xmldom/xmldom": "^0.8.10", + "axios": "^1.7.5", + "azure-devops-node-api": "^14.0.2", + "bitbucket": "^2.12.0", + "chalk": "^5.3.0", + "columnify": "^1.6.0", + "cosmiconfig": "^9.0.0", "cross-spawn": "^7.0.3", - "csv-stringify": "^5.6.1", - "debug": "^4.3.4", + "csv-stringify": "^6.5.1", + "debug": "^4.3.6", "email-validator": "^2.0.4", - "eslint": "^7.21.0", "exceljs": "^4.4.0", "extract-zip": "^2.0.1", - "find-package-json": "^1.2.0", - "fs-extra": "^9.1.0", + "fs-extra": "^11.2.0", "fs-readdir-recursive": "^1.1.0", "glob": "^11.0.0", "he": "^1.2.0", "inquirer": "^10.1.8", - "isomorphic-dompurify": "^2.3.0", + "isomorphic-dompurify": "^2.15.0", "jira-client": "^8.2.2", - "js-yaml": "^4.0.0", - "jsforce": "^1.11.0", - "keyv": "^4.0.3", + "js-yaml": "^4.1.0", + "keyv": "^5.0.1", + "make-fetch-happen": "^13.0.1", "markdown-toc": "^1.2.0", - "marked": "^12.0.0", - "mega-linter-runner": "^4.39.0", + "marked": "^14.1.0", + "mega-linter-runner": "^8.0.0", "moment": "^2.30.1", - "node-fetch": "^3.2.10", - "open": "8.4.2", - "openai": "^4.52.0", - "ora": "5.4.1", - "papaparse": "^5.3.1", - "pascalcase": "^1.0.0", - "psl": "^1.8.0", - "puppeteer": "^13.5.2", - "read-pkg-up": "^7.0.1", - "semver": "^7.6.0", - "set-value": "^4.0.0", - "simple-git": "^3.20.0", - "sort-array": "^4.1.3", + "open": "^10.1.0", + "openai": "^4.56.1", + "ora": "^8.1.0", + "papaparse": "^5.4.1", + "pascalcase": "^2.0.0", + "psl": "^1.9.0", + "puppeteer-core": "^23.3.0", + "read-package-up": "^11.0.0", + "semver": "^7.6.3", + "set-value": "^4.1.0", + "simple-git": "^3.25.0", + "sort-array": "^4.1.5", "split": "^1.0.1", "string-template": "^1.0.0", - "tslib": "^2.1.0", - "update-notifier": "^5.1.0", - "which": "^2.0.2", - "ws": "^7.4.4", - "xml2js": "^0.5.0", - "xpath": "^0.0.32" + "update-notifier": "^7.3.0", + "which": "^4.0.0", + "ws": "^8.18.0", + "xml2js": "^0.6.2", + "xpath": "^0.0.34" }, "devDependencies": { - "@oclif/dev-cli": "^1.26.0", - "@oclif/plugin-help": "^3.2.2", - "@oclif/test": "^1", - "@salesforce/dev-config": "^2.1.0", - "@types/chai": "^4", - "@types/dompurify": "^3.0.5", + "@oclif/plugin-command-snapshot": "^5.1.9", + "@salesforce/cli-plugins-testkit": "^5.3.28", + "@salesforce/dev-config": "^4.3.1", + "@salesforce/dev-scripts": "^10", + "@types/columnify": "^1.5.4", + "@types/cosmiconfig": "^6.0.0", + "@types/cross-spawn": "^6.0.6", + "@types/extract-zip": "^2.0.1", + "@types/fs-extra": "^11.0.4", + "@types/fs-readdir-recursive": "^1.1.3", + "@types/glob": "^8.1.0", + "@types/inquirer": "^9.0.7", "@types/jira-client": "^7.1.9", - "@types/node": "^14.14.32", - "chai": "^4", - "globby": "^11.0.2", - "mocha": "^8.3.1", - "nyc": "^15.1.0", - "rimraf": "^3.0.2", - "ts-node": "^10.7.0", - "typescript": "^5.5.2", - "yarn-audit-fix": "^9.3.7" - }, - "resolutions": { - "graceful-fs": "^4.2.4" + "@types/js-yaml": "^4.0.9", + "@types/make-fetch-happen": "^10.0.4", + "@types/papaparse": "^5.3.14", + "@types/psl": "^1.1.3", + "@types/set-value": "^4.0.3", + "@types/sort-array": "^4.1.2", + "@types/update-notifier": "^6.0.8", + "@types/which": "^3.0.4", + "@types/ws": "^8.5.12", + "@types/xml2js": "^0.4.14", + "eslint-plugin-sf-plugin": "^1.18.6", + "oclif": "^4.14.31", + "ts-node": "^10.9.2", + "typescript": "^5.4.5" }, "engines": { "node": ">=20.0.0" @@ -102,12 +96,16 @@ "/lib", "/messages", "/defaults", - "/npm-shrinkwrap.json", - "/oclif.manifest.json" + "/oclif.manifest.json", + "/oclif.lock" ], "homepage": "https://sfdx-hardis.cloudity.com", "keywords": [ + "force", "salesforce", + "salesforcedx", + "sf", + "sf-plugin", "sfdx", "devops", "automation", @@ -132,7 +130,20 @@ "license": "AGPL-3.0", "oclif": { "commands": "./lib/commands", - "bin": "sfdx", + "bin": "sf", + "topicSeparator": " ", + "devPlugins": [ + "@oclif/plugin-help" + ], + "topics": { + "hello": { + "description": "Commands to say hello." + }, + "hardis": { + "description": "Hardis tools by Cloudity" + } + }, + "flexibleTaxonomy": true, "hooks": { "init": [ "./lib/hooks/init/log", @@ -145,36 +156,148 @@ "./lib/hooks/prerun/check-dependencies" ], "auth": [ - "./lib/hooks/prerun/auth" + "./lib/hooks/auth/auth" ], "postrun": [ "./lib/hooks/postrun/notify", "./lib/hooks/postrun/store-cache" ] - }, - "topics": { - "hardis": { - "description": "Hardis tools" - } - }, - "devPlugins": [ - "@oclif/plugin-help" - ] + } }, "repository": "https://github.com/hardisgroupcom/sfdx-hardis", - "prettier": { - "printWidth": 150 - }, "scripts": { - "build": "tsc -b && node build.js && generate-schema-doc config/sfdx-hardis.jsonschema.json docs/schema/sfdx-hardis-json-schema-parameters.html --config minify=false --config link_to_reused_ref=false || echo 'Please run \"pip install json-schema-for-humans\" (you need python installed on your computer)'", - "build:doc": "sfdx hardis:doc:plugin:generate", - "compile": "tsc --watch", - "lint": "eslint . --ext .ts", - "lint:fix": "eslint . --ext .ts --fix", - "postpack": "rimraf oclif.manifest.json", - "posttest": "npm run lint", - "prepack": "rimraf lib && tsc -b && oclif-dev manifest && oclif-dev readme || true && yarn build", - "test": "exit 0 && nyc --extension .ts mocha --forbid-only \"test/**/*.test.ts\"", - "version": "yarn build && git add docs/* && oclif-dev readme || true && git add README.md" - } + "build": "wireit", + "build:doc": "sf hardis:doc:plugin:generate", + "clean": "sf-clean", + "clean-all": "sf-clean all", + "compile": "wireit", + "docs": "sf-docs", + "format": "wireit", + "link-check": "wireit", + "lint": "wireit", + "postinstall": "yarn husky install || echo \"Unable to install Husky. If you are in a CI/CD job, that's ok !\"", + "postpack": "sf-clean --ignore-signing-artifacts", + "prepack": "sf-prepack", + "test": "wireit", + "test:nuts": "nyc mocha \"**/*.nut.ts\" --slow 4500 --timeout 600000 --parallel", + "test:only": "wireit", + "version": "oclif readme" + }, + "publishConfig": { + "access": "public" + }, + "wireit": { + "build": { + "command": "node build.cjs && generate-schema-doc config/sfdx-hardis.jsonschema.json docs/schema/sfdx-hardis-json-schema-parameters.html --config minify=false --config link_to_reused_ref=false || echo 'Please run \"pip install json-schema-for-humans\" (you need python installed on your computer)'", + "dependencies": [ + "compile", + "lint" + ] + }, + "compile": { + "command": "tsc -p . --pretty --incremental", + "files": [ + "src/**/*.ts", + "**/tsconfig.json", + "messages/**" + ], + "output": [ + "lib/**", + "*.tsbuildinfo" + ], + "clean": "if-file-deleted" + }, + "format": { + "command": "prettier --write \"+(src|test|schemas)/**/*.+(ts|js|json)|command-snapshot.json\"", + "files": [ + "src/**/*.ts", + "test/**/*.ts", + "schemas/**/*.json", + "command-snapshot.json", + ".prettier*" + ], + "output": [] + }, + "lint": { + "command": "eslint src test --color --cache --cache-location .eslintcache", + "files": [ + "src/**/*.ts", + "test/**/*.ts", + "messages/**", + "**/.eslint*", + "**/tsconfig.json" + ], + "output": [] + }, + "test:compile": { + "command": "tsc -p \"./test\" --pretty", + "files": [ + "test/**/*.ts", + "**/tsconfig.json" + ], + "output": [] + }, + "test": { + "dependencies": [ + "test:compile", + "test:only", + "lint" + ] + }, + "test:only": { + "command": "nyc mocha \"test/**/*.test.ts\"", + "env": { + "FORCE_COLOR": "2" + }, + "files": [ + "test/**/*.ts", + "src/**/*.ts", + "**/tsconfig.json", + ".mocha*", + "!*.nut.ts", + ".nycrc" + ], + "output": [] + }, + "test:command-reference": { + "command": "node --loader ts-node/esm --no-warnings=ExperimentalWarning \"./bin/dev.js\" commandreference:generate --erroronwarnings", + "files": [ + "src/**/*.ts", + "messages/**", + "package.json" + ], + "output": [ + "tmp/root" + ] + }, + "test:deprecation-policy": { + "command": "node --loader ts-node/esm --no-warnings=ExperimentalWarning \"./bin/dev.js\" snapshot:compare", + "files": [ + "src/**/*.ts" + ], + "output": [], + "dependencies": [ + "compile" + ] + }, + "test:json-schema": { + "command": "node --loader ts-node/esm --no-warnings=ExperimentalWarning \"./bin/dev.js\" schema:compare", + "files": [ + "src/**/*.ts", + "schemas" + ], + "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|my.salesforce.com|%s\" --markdown --retry --directory-listing --verbosity error", + "files": [ + "./*.md", + "./!(CHANGELOG).md", + "messages/**/*.md" + ], + "output": [] + } + }, + "exports": "./lib/index.js", + "type": "module" } diff --git a/src/commands/hardis/auth/login.ts b/src/commands/hardis/auth/login.ts index 61c9c9863..c1ab4d158 100644 --- a/src/commands/hardis/auth/login.ts +++ b/src/commands/hardis/auth/login.ts @@ -1,67 +1,58 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class Login extends SfCommand { + public static title = 'Login'; -export default class Login extends SfdxCommand { - public static title = "Login"; + public static description = messages.getMessage('loginToOrg'); - public static description = messages.getMessage("loginToOrg"); - - public static examples = ["$ sfdx hardis:auth:login"]; + public static examples = ['$ sf hardis:auth:login']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - instanceurl: flags.string({ - char: "r", - description: messages.getMessage("instanceUrl"), + public static flags: any = { + instanceurl: Flags.string({ + char: 'r', + description: messages.getMessage('instanceUrl'), }), - devhub: flags.boolean({ - char: "h", + devhub: Flags.boolean({ + char: 'h', default: false, - description: messages.getMessage("withDevHub"), + description: messages.getMessage('withDevHub'), }), - scratchorg: flags.boolean({ - char: "s", + scratchorg: Flags.boolean({ + char: 's', default: false, - description: messages.getMessage("scratch"), + description: messages.getMessage('scratch'), }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; /* jscpd:ignore-end */ public async run(): Promise { - const devHub = this.flags.devhub || false; - const scratch = this.flags.scratchorg || false; - await this.config.runHook("auth", { + const { flags } = await this.parse(Login); + const devHub = flags.devhub || false; + const scratch = flags.scratchorg || false; + await this.config.runHook('auth', { checkAuth: !devHub, Command: this, devHub, @@ -69,6 +60,6 @@ export default class Login extends SfdxCommand { }); // Return an object to be displayed with --json - return { outputString: "Logged to Salesforce org" }; + return { outputString: 'Logged to Salesforce org' }; } } diff --git a/src/commands/hardis/cache/clear.ts b/src/commands/hardis/cache/clear.ts index 29bf20b86..19e677ba3 100644 --- a/src/commands/hardis/cache/clear.ts +++ b/src/commands/hardis/cache/clear.ts @@ -1,52 +1,43 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import { clearCache } from "../../../common/cache"; -import { uxLog } from "../../../common/utils"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { clearCache } from '../../../common/cache/index.js'; +import { uxLog } from '../../../common/utils/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class DxSources extends SfCommand { + public static title = 'Clear sfdx-hardis cache'; -export default class DxSources extends SfdxCommand { - public static title = "Clear sfdx-hardis cache"; + public static description = 'Clear cache generated by sfdx-hardis'; - public static description = "Clear cache generated by sfdx-hardis"; + public static examples = ['$ sf hardis:cache:clear']; - public static examples = ["$ sfdx hardis:cache:clear"]; - - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), - }; - - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + }; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default + public static requiresProject = false; /* jscpd:ignore-end */ public async run(): Promise { await clearCache(); - uxLog(this, "sfdx-hardis cache cleared"); + uxLog(this, 'sfdx-hardis cache cleared'); return { - message: "sfdx-hardis cache cleared", + message: 'sfdx-hardis cache cleared', }; } } diff --git a/src/commands/hardis/config/get.ts b/src/commands/hardis/config/get.ts index 634381c81..9ef9ac109 100644 --- a/src/commands/hardis/config/get.ts +++ b/src/commands/hardis/config/get.ts @@ -1,57 +1,49 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import { uxLog } from "../../../common/utils"; -import { getConfig } from "../../../config"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { uxLog } from '../../../common/utils/index.js'; +import { getConfig } from '../../../config/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class ConfigGet extends SfCommand { + public static title = 'Deploy metadata sources to org'; -export default class DxSources extends SfdxCommand { - public static title = "Deploy metadata sources to org"; + public static description = 'Returns sfdx-hardis project config for a given level'; - public static description = "Returns sfdx-hardis project config for a given level"; + public static examples = ['$ sf hardis:project:deploy:sources:metadata']; - public static examples = ["$ sfdx hardis:project:deploy:sources:metadata"]; - - protected static flagsConfig = { - level: flags.string({ - char: "l", - default: "project", - description: "project,branch or user", - options: ["project", "branch", "user"], + public static flags: any = { + level: Flags.string({ + char: 'l', + default: 'project', + description: 'project,branch or user', + options: ['project', 'branch', 'user'], }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), - }; - - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + }; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default + public static requiresProject = false; protected configInfo: any = {}; /* jscpd:ignore-end */ public async run(): Promise { - const level = this.flags.level || "project"; + const { flags } = await this.parse(ConfigGet); + const level = flags.level || 'project'; this.configInfo = await getConfig(level); uxLog(this, JSON.stringify(this.configInfo)); return { diff --git a/src/commands/hardis/doc/extract/permsetgroups.ts b/src/commands/hardis/doc/extract/permsetgroups.ts index 2d065d5b9..13d79cbb3 100644 --- a/src/commands/hardis/doc/extract/permsetgroups.ts +++ b/src/commands/hardis/doc/extract/permsetgroups.ts @@ -1,75 +1,65 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import { glob } from "glob"; -import * as path from "path"; -import * as toc from "markdown-toc"; -import { uxLog } from "../../../../common/utils"; -import { parseXmlFile } from "../../../../common/utils/xmlUtils"; -import { getReportDirectory } from "../../../../config"; -import { WebSocketClient } from "../../../../common/websocketClient"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class DocGenerate extends SfdxCommand { - public static title = "Generate project documentation"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import { glob } from 'glob'; +import * as path from 'path'; +import toc from 'markdown-toc'; +import { uxLog } from '../../../../common/utils/index.js'; +import { parseXmlFile } from '../../../../common/utils/xmlUtils.js'; +import { getReportDirectory } from '../../../../config/index.js'; +import { WebSocketClient } from '../../../../common/websocketClient.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class ExtractPermSetGroups extends SfCommand { + public static title = 'Generate project documentation'; public static description = `Generate markdown files with project documentation`; - public static examples = ["$ sfdx hardis:doc:extract:permsetgroups"]; + public static examples = ['$ sf hardis:doc:extract:permsetgroups']; - protected static flagsConfig = { - outputfile: flags.string({ - char: "o", - description: "Force the path and name of output report file. Must end with .csv", + public static flags: any = { + outputfile: Flags.string({ + char: 'o', + description: 'Force the path and name of output report file. Must end with .csv', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; protected outputFile; protected debugMode = false; public async run(): Promise { - this.outputFile = this.flags.outputfile || null; - this.debugMode = this.flags.debug || false; - + const { flags } = await this.parse(ExtractPermSetGroups); + this.outputFile = flags.outputfile || null; + this.debugMode = flags.debug || false; // Delete standard files when necessary uxLog(this, c.cyan(`Generating CSV and Markdown with Permission Set Groups and their related Permission Sets`)); /* jscpd:ignore-end */ - const psgList = []; + const psgList: any[] = []; const globPatternPSG = process.cwd() + `/**/*.permissionsetgroup-meta.xml`; const psgFiles = await glob(globPatternPSG); uxLog(this, c.grey(`Found ${psgFiles.length} permission set groups`)); for (const psgFile of psgFiles) { - const psgName = psgFile.replace(/\\/g, "/").split("/").pop().replace(".permissionsetgroup-meta.xml", ""); + const psgName = (psgFile.replace(/\\/g, '/').split('/').pop() || '').replace('.permissionsetgroup-meta.xml', ''); const psg = await parseXmlFile(psgFile); const psgItem = { name: psgName, @@ -81,12 +71,12 @@ export default class DocGenerate extends SfdxCommand { } // Build CSV - const csvLines = []; - const header = ["Permission set group", "Permission sets"]; + const csvLines: any[] = []; + const header = ['Permission set group', 'Permission sets']; csvLines.push(header); for (const psg of psgList) { const psgLine = [psg.name]; - psgLine.push(`"${psg.permissionSetsNames.join(",")}"`); + psgLine.push(`"${psg.permissionSetsNames.join(',')}"`); csvLines.push(psgLine); } @@ -94,41 +84,41 @@ export default class DocGenerate extends SfdxCommand { if (this.outputFile == null) { // Default file in system temp directory if --outputfile not provided const reportDir = await getReportDirectory(); - this.outputFile = path.join(reportDir, "permission-set-groups.csv"); + this.outputFile = path.join(reportDir, 'permission-set-groups.csv'); } else { // Ensure directories to provided --outputfile are existing await fs.ensureDir(path.dirname(this.outputFile)); } try { - const csvText = csvLines.map((e) => e.join(",")).join("\n"); - await fs.writeFile(this.outputFile, csvText, "utf8"); + const csvText = csvLines.map((e) => e.join(',')).join('\n'); + await fs.writeFile(this.outputFile, csvText, 'utf8'); uxLog(this, c.cyan(`Permission set groups CSV file generated in ${c.bold(c.green(this.outputFile))}`)); // Trigger command to open CSV file in VsCode extension WebSocketClient.requestOpenFile(this.outputFile); - } catch (e) { - uxLog(this, c.yellow("Error while generating CSV log file:\n" + e.message + "\n" + e.stack)); + } catch (e: any) { + uxLog(this, c.yellow('Error while generating CSV log file:\n' + (e as Error).message + '\n' + e.stack)); this.outputFile = null; } // Build markdown file - const mdPsg = ["# Permission set groups", "", "", ""]; + const mdPsg = ['# Permission set groups', '', '', '']; for (const psg of psgList) { - mdPsg.push(...[`## ${psg.name}`, "", psg.label, "", psg.description, ""]); + mdPsg.push(...[`## ${psg.name}`, '', psg.label, '', psg.description, '']); for (const psName of psg.permissionSetsNames) { mdPsg.push(` - ${psName} `); } - mdPsg.push(""); + mdPsg.push(''); } - const docFile = "docs/permission-set-groups.md"; - await fs.ensureDir("docs"); - let mdPsgText = mdPsg.join("\n"); + const docFile = 'docs/permission-set-groups.md'; + await fs.ensureDir('docs'); + let mdPsgText = mdPsg.join('\n'); mdPsgText = toc.insert(mdPsgText); - await fs.writeFile(docFile, mdPsgText, "utf8"); + await fs.writeFile(docFile, mdPsgText, 'utf8'); uxLog(this, c.cyan(`Permission set groups Markdown file generated in ${c.bold(c.green(docFile))}`)); // Trigger command to open CSV file in VsCode extension WebSocketClient.requestOpenFile(docFile); // Return an object to be displayed with --json - return { outputString: "Permission set groups Documentation generated" }; + return { outputString: 'Permission set groups Documentation generated' }; } } diff --git a/src/commands/hardis/doc/plugin/generate.ts b/src/commands/hardis/doc/plugin/generate.ts index a35010faa..4e3979903 100644 --- a/src/commands/hardis/doc/plugin/generate.ts +++ b/src/commands/hardis/doc/plugin/generate.ts @@ -1,26 +1,22 @@ /* jscpd:ignore-start */ -import * as Config from "@oclif/config"; -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; -import * as sortArray from "sort-array"; -import * as set from "set-value"; -import * as yaml from "js-yaml"; -import { uxLog } from "../../../../common/utils"; -import { PACKAGE_ROOT_DIR } from "../../../../settings"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class DocPluginGenerate extends SfdxCommand { - public static title = "Generate SFDX Plugin Documentation"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; +import sortArray from 'sort-array'; +import set from 'set-value'; +import * as yaml from 'js-yaml'; +import { uxLog } from '../../../../common/utils/index.js'; +import { PACKAGE_ROOT_DIR } from '../../../../settings.js'; +import { Config } from '@oclif/core'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class DocPluginGenerate extends SfCommand { + public static title = 'Generate SF Cli Plugin Documentation'; public static description = `Generate Markdown documentation ready for HTML conversion with mkdocs @@ -35,55 +31,50 @@ Then, activate Github pages, with "gh_pages" as target branch At each merge into master/main branch, the GitHub Action build-deploy-docs will rebuild documentation and publish it in GitHub pages `; - public static examples = ["$ sfdx hardis:doc:plugin:generate"]; + public static examples = ['$ sf hardis:doc:plugin:generate']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; protected debugMode = false; /* jscpd:ignore-end */ public async run(): Promise { - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(DocPluginGenerate); + this.debugMode = flags.debug || false; // Load plugin configuration const cwd = process.cwd(); const config = await Config.load({ root: cwd, devPlugins: false, userPlugins: false }); // Generate commands markdowns - const commandsNav = { "All commands": "commands.md" }; + const commandsNav = { 'All commands': 'commands.md' }; const commandsLinks = {}; for (const command of config.commands) { await this.generateCommandDoc(command); - const commandsSplit = command.id.split(":"); + const commandsSplit = command.id.split(':'); const commandName = commandsSplit.pop(); - const commandMdPath = commandsSplit.join("/") + `/${commandName}.md`; + const commandMdPath = commandsSplit.join('/') + `/${commandName}.md`; const navItem = {}; - navItem[commandName] = commandMdPath; - set(commandsNav, commandsSplit.join("."), navItem, { preservePaths: true, merge: true }); + navItem[commandName || ''] = commandMdPath; + set(commandsNav, commandsSplit.join('.'), navItem, { preservePaths: true, merge: true }); commandsLinks[command.id] = commandMdPath; } uxLog(this, yaml.dump(commandsNav)); @@ -91,40 +82,51 @@ At each merge into master/main branch, the GitHub Action build-deploy-docs will // Generate index.md await this.generateIndexDoc(config, commandsLinks); - // Copy default files (mkdocs.yml and other files can be updated by the sfdx plugin developer later) - const mkdocsYmlFile = path.join(process.cwd(), "mkdocs.yml"); + // Copy default files (mkdocs.yml and other files can be updated by the SF Cli plugin developer later) + const mkdocsYmlFile = path.join(process.cwd(), 'mkdocs.yml'); const mkdocsYmlFileExists = fs.existsSync(mkdocsYmlFile); - await fs.copy(path.join(PACKAGE_ROOT_DIR, "defaults/mkdocs", "."), process.cwd(), { overwrite: false }); + await fs.copy(path.join(PACKAGE_ROOT_DIR, 'defaults/mkdocs', '.'), process.cwd(), { overwrite: false }); if (!mkdocsYmlFileExists) { - uxLog(this, c.blue("Base mkdocs files copied in your sfdx plugin folder")); - uxLog(this, c.yellow("You should probably manually update mkdocs.yml and build-deploy-docs.yml with your repo & plugin information")); + uxLog(this, c.blue('Base mkdocs files copied in your SF Cli plugin folder')); + uxLog( + this, + c.yellow( + 'You should probably manually update mkdocs.yml and build-deploy-docs.yml with your repo & plugin information' + ) + ); } // Remove changelog if not existing - if (!fs.existsSync(path.join(process.cwd(), "CHANGELOG.md")) && fs.existsSync(path.join(process.cwd(), "docs", "CHANGELOG.md"))) { - await fs.remove(path.join(process.cwd(), "docs", "CHANGELOG.md")); + if ( + !fs.existsSync(path.join(process.cwd(), 'CHANGELOG.md')) && + fs.existsSync(path.join(process.cwd(), 'docs', 'CHANGELOG.md')) + ) { + await fs.remove(path.join(process.cwd(), 'docs', 'CHANGELOG.md')); } // Remove license if not existing - if (!fs.existsSync(path.join(process.cwd(), "LICENSE")) && fs.existsSync(path.join(process.cwd(), "docs", "license.md"))) { - await fs.remove(path.join(process.cwd(), "docs", "license.md")); + if ( + !fs.existsSync(path.join(process.cwd(), 'LICENSE')) && + fs.existsSync(path.join(process.cwd(), 'docs', 'license.md')) + ) { + await fs.remove(path.join(process.cwd(), 'docs', 'license.md')); } // Update mkdocs nav items - const mkdocsYml = yaml.load( + const mkdocsYml: any = yaml.load( fs - .readFileSync(mkdocsYmlFile, "utf-8") - .replace("!!python/name:materialx.emoji.twemoji", "'!!python/name:materialx.emoji.twemoji'") - .replace("!!python/name:materialx.emoji.to_svg", "'!!python/name:materialx.emoji.to_svg'"), + .readFileSync(mkdocsYmlFile, 'utf-8') + .replace('!!python/name:materialx.emoji.twemoji', "'!!python/name:materialx.emoji.twemoji'") + .replace('!!python/name:materialx.emoji.to_svg', "'!!python/name:materialx.emoji.to_svg'") ); mkdocsYml.nav = mkdocsYml.nav.map((navItem: any) => { - if (navItem["Commands"]) { - navItem["Commands"] = commandsNav; + if (navItem['Commands']) { + navItem['Commands'] = commandsNav; } return navItem; }); const mkdocsYmlStr = yaml .dump(mkdocsYml) - .replace("'!!python/name:materialx.emoji.twemoji'", "!!python/name:materialx.emoji.twemoji") - .replace("'!!python/name:materialx.emoji.to_svg'", "!!python/name:materialx.emoji.to_svg"); + .replace("'!!python/name:materialx.emoji.twemoji'", '!!python/name:materialx.emoji.twemoji') + .replace("'!!python/name:materialx.emoji.to_svg'", '!!python/name:materialx.emoji.to_svg'); await fs.writeFile(mkdocsYmlFile, mkdocsYmlStr); uxLog(this, c.cyan(`Updated ${c.green(mkdocsYmlFile)}`)); @@ -135,12 +137,12 @@ At each merge into master/main branch, the GitHub Action build-deploy-docs will // Generate index file private async generateIndexDoc(config: any, commandsLinks: any) { const lines = [ - "", + "", ]; - const readme = await fs.readFile(path.join(process.cwd(), "README.md"), "utf8"); + const readme = await fs.readFile(path.join(process.cwd(), 'README.md'), 'utf8'); let reusableReadmePartFound = false; // Try to find README content until auto-generated commands - const limitStrings = ["## Commands", "## COMMANDS", "# Commands", ""]; + const limitStrings = ['## Commands', '## COMMANDS', '# Commands', '']; for (const limitString of limitStrings) { if (readme.indexOf(limitString) > 0) { lines.push(...readme.substring(0, readme.indexOf(limitString)).split(/\r?\n/)); @@ -150,87 +152,102 @@ At each merge into master/main branch, the GitHub Action build-deploy-docs will } // Default index.md if (reusableReadmePartFound === false) { - lines.push(...["", `# ${config.pjson.name}`, "", "## Description", "", config.pjson.description.split("\n").join("
"), ""]); + lines.push( + ...[ + '', + `# ${config.pjson.name}`, + '', + '## Description', + '', + config.pjson.description.split('\n').join('
'), + '', + ] + ); } // Build commands (for index.md and commands.md) - const cmdLines = []; - lines.push(...["", "## Commands"]); - cmdLines.push("# Commands"); - let currentSection = ""; - for (const command of sortArray(config.commands, { by: ["id"], order: ["asc"] })) { - const section = command.id.split(":")[0] + ":" + command.id.split(":")[1]; + const cmdLines: any[] = []; + lines.push(...['', '## Commands']); + cmdLines.push('# Commands'); + let currentSection = ''; + for (const command of sortArray(config.commands, { by: ['id'], order: ['asc'] }) as any[]) { + const section = command.id.split(':')[0] + ':' + command.id.split(':')[1]; if (section !== currentSection) { - lines.push(...["", `### ${section}`, "", "|Command|Title|", "|:------|:----------|"]); - cmdLines.push(...["", `## ${section}`, "", "|Command|Title|", "|:------|:----------|"]); + lines.push(...['', `### ${section}`, '', '|Command|Title|', '|:------|:----------|']); + cmdLines.push(...['', `## ${section}`, '', '|Command|Title|', '|:------|:----------|']); currentSection = section; } const commandInstance = command.load(); - const title = commandInstance.title ? commandInstance.title : commandInstance.description ? commandInstance.description.split("\n")[0] : ""; + const title = commandInstance.title + ? commandInstance.title + : commandInstance.description + ? commandInstance.description.split('\n')[0] + : ''; lines.push(...[`|[**${command.id}**](${commandsLinks[command.id]})|${title}|`]); cmdLines.push(...[`|[**${command.id}**](${commandsLinks[command.id]})|${title}|`]); } // Create docs dir if not existing yet - await fs.ensureDir(path.join(process.cwd(), "docs")); + await fs.ensureDir(path.join(process.cwd(), 'docs')); // write in index.md - const indexMdFile = path.join(process.cwd(), "docs", "index.md"); - const indexMdString = lines.join("\n") + "\n"; + const indexMdFile = path.join(process.cwd(), 'docs', 'index.md'); + const indexMdString = lines.join('\n') + '\n'; await fs.writeFile(indexMdFile, indexMdString); // write in commands.md - const commandsMdFile = path.join(process.cwd(), "docs", "commands.md"); - const commandsMdString = cmdLines.join("\n") + "\n"; + const commandsMdFile = path.join(process.cwd(), 'docs', 'commands.md'); + const commandsMdString = cmdLines.join('\n') + '\n'; await fs.writeFile(commandsMdFile, commandsMdString); } // Generate markdown doc for a single command private async generateCommandDoc(command: any) { const lines = [ - "", + "", ]; // Title - const titleLines = [`# ${command.id}`, ""]; + const titleLines = [`# ${command.id}`, '']; lines.push(...titleLines); // Description - const descriptionLines = [`## Description`, "", ...(command.description || "").split("\n"), ""]; + const descriptionLines = [`## Description`, '', ...(command.description || '').split('\n'), '']; lines.push(...descriptionLines); // Flags const flagLines = [ `## Parameters`, - "", - "|Name|Type|Description|Default|Required|Options|", - "|:---|:--:|:----------|:-----:|:------:|:-----:|", + '', + '|Name|Type|Description|Default|Required|Options|', + '|:---|:--:|:----------|:-----:|:------:|:-----:|', ...Object.keys(command.flags || {}) .sort() .map((flagKey: string) => { const flag = command.flags[flagKey]; - const optionsUnique = []; + const optionsUnique: any[] = []; for (const option of flag.options || []) { if (optionsUnique.filter((o) => o.toLowerCase() === option.toLowerCase()).length === 0) { optionsUnique.push(option); } } - return `|${flag.name + (flag.char ? `
-${flag.char}` : "")}|${flag.type}|${flag.description}|${flag.default || ""}|${ - flag.required ? "" : "" - }|${optionsUnique.join("
")}|`; + return `|${flag.name + (flag.char ? `
-${flag.char}` : '')}|${flag.type}|${flag.description}|${flag.default || '' + }|${flag.required ? '' : ''}|${optionsUnique.join('
')}|`; }), - "", + '', ]; lines.push(...flagLines); // Examples const exampleLines = [ `## Examples`, - "", - ...(command.examples || []).map((example: string) => ["```shell", ...(example || "").split("\n"), "```", ""]).flat(), - "", + '', + ...(command.examples || []) + .map((example: string) => ['```shell', ...(example || '').split('\n'), '```', '']) + .flat(), + '', ]; lines.push(...exampleLines); // Write to file - const mdFileName = path.join(process.cwd(), "docs", path.sep, command.id.replace(/:/g, "/") + ".md"); + const mdFileName = path.join(process.cwd(), 'docs', path.sep, command.id.replace(/:/g, '/') + '.md'); await fs.ensureDir(path.dirname(mdFileName)); - const yamlString = lines.join("\n") + "\n"; + const yamlString = lines.join('\n') + '\n'; await fs.writeFile(mdFileName, yamlString); - uxLog(this, c.grey("Generated file " + c.bold(mdFileName))); + uxLog(this, c.grey('Generated file ' + c.bold(mdFileName))); } } diff --git a/src/commands/hardis/lint/access.ts b/src/commands/hardis/lint/access.ts index a6c85e72f..6e9ff6e18 100644 --- a/src/commands/hardis/lint/access.ts +++ b/src/commands/hardis/lint/access.ts @@ -1,91 +1,83 @@ /* jscpd:ignore-start */ // External Libraries -import * as c from "chalk"; -import { glob } from "glob"; -import * as path from "path"; -import * as sortArray from "sort-array"; +import c from 'chalk'; +import { glob } from 'glob'; +import * as path from 'path'; +import sortArray from 'sort-array'; // Salesforce Specific -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages, SfdxError } from "@salesforce/core"; -import * as fs from "fs-extra"; -import { AnyJson } from "@salesforce/ts-types"; +import { SfCommand, Flags, optionalOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages, SfError } from '@salesforce/core'; +import fs from 'fs-extra'; +import { AnyJson } from '@salesforce/ts-types'; // Common Utilities -import { isCI, uxLog } from "../../../common/utils"; -import { prompts } from "../../../common/utils/prompts"; -import { parseXmlFile, writeXmlFile } from "../../../common/utils/xmlUtils"; -import { generateCsvFile, generateReportPath } from "../../../common/utils/filesUtils"; -import { NotifProvider, NotifSeverity } from "../../../common/notifProvider"; -import { Parser } from "xml2js"; +import { isCI, uxLog } from '../../../common/utils/index.js'; +import { prompts } from '../../../common/utils/prompts.js'; +import { parseXmlFile, writeXmlFile } from '../../../common/utils/xmlUtils.js'; +import { generateCsvFile, generateReportPath } from '../../../common/utils/filesUtils.js'; +import { NotifProvider, NotifSeverity } from '../../../common/notifProvider/index.js'; +import { Parser } from 'xml2js'; // Config -import { getConfig } from "../../../config"; -import { getBranchMarkdown, getNotificationButtons, getSeverityIcon } from "../../../common/utils/notifUtils"; +import { CONSTANTS, getConfig } from '../../../config/index.js'; +import { getBranchMarkdown, getNotificationButtons, getSeverityIcon } from '../../../common/utils/notifUtils.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class Access extends SfdxCommand { - public static title = "check permission access"; +export default class LintAccess extends SfCommand { + public static title = 'check permission access'; public static description = `Check if elements(apex class and field) are at least in one permission set -This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-missing-access/) and can output Grafana, Slack and MsTeams Notifications. +This command is part of [sfdx-hardis Monitoring](${CONSTANTS.DOC_URL_ROOT}/salesforce-monitoring-missing-access/) and can output Grafana, Slack and MsTeams Notifications. `; public static examples = [ - "$ sfdx hardis:lint:access", - '$ sfdx hardis:lint:access -e "ApexClass:ClassA, CustomField:Account.CustomField"', - '$ sfdx hardis:lint:access -i "PermissionSet:permissionSetA, Profile"', + '$ sf hardis:lint:access', + '$ sf hardis:lint:access -e "ApexClass:ClassA, CustomField:Account.CustomField"', + '$ sf hardis:lint:access -i "PermissionSet:permissionSetA, Profile"', ]; - protected static flagsConfig = { - elementsignored: flags.string({ - char: "e", - default: "", - description: "Ignore specific elements separated by commas", + public static flags: any = { + elementsignored: Flags.string({ + char: 'e', + default: '', + description: 'Ignore specific elements separated by commas', }), - ignorerights: flags.string({ - char: "i", - default: "", - description: "Ignore permission sets or profiles", + ignorerights: Flags.string({ + char: 'i', + default: '', + description: 'Ignore permission sets or profiles', }), - folder: flags.string({ - char: "f", - default: "force-app", - description: "Root folder", + folder: Flags.string({ + char: 'f', + default: 'force-app', + description: 'Root folder', }), - outputfile: flags.string({ - char: "o", - description: "Force the path and name of output report file. Must end with .csv", + outputfile: Flags.string({ + char: 'o', + description: 'Force the path and name of output report file. Must end with .csv', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': optionalOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - protected static supportsUsername = true; - - // Comment this out if your command does not support a hub org username protected static supportsDevhubUsername = false; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; protected folder: string; protected customSettingsNames: string[] = []; @@ -94,13 +86,13 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co protected outputFile; protected outputFilesRes: any = {}; - protected static sourceElements = [ + protected static sourceElements: any[] = [ { regex: `/**/*.cls`, - type: "ApexClass", - xmlField: "apexClass", - xmlChildren: "classAccesses", - xmlAccessField: "enabled", + type: 'ApexClass', + xmlField: 'apexClass', + xmlChildren: 'classAccesses', + xmlAccessField: 'enabled', ignore: { all: false, elements: [], @@ -108,10 +100,10 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co }, { regex: `/**/objects/**/fields/*__c.field-meta.xml`, - type: "CustomField", - xmlField: "field", - xmlChildren: "fieldPermissions", - xmlAccessField: "readable", + type: 'CustomField', + xmlField: 'field', + xmlChildren: 'fieldPermissions', + xmlAccessField: 'readable', ignore: { all: false, elements: [], @@ -119,26 +111,26 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co }, ]; - private permissionSet = { + private permissionSet: any = { regex: `/**/permissionsets/*.permissionset-meta.xml`, - type: "Permission sets", - name: "PermissionSet", + type: 'Permission sets', + name: 'PermissionSet', isIgnoredAll: false, elementsIgnored: [], }; - private profiles = { + private profiles: any = { regex: `/**/profiles/*.profile-meta.xml`, - type: "Profiles", - name: "Profile", + type: 'Profiles', + name: 'Profile', isIgnoredAll: false, elementsIgnored: [], }; private static messages = { - header: "Check if elements(apex class and field) are at least in one permission set", - allElementsHaveRights: "All elements are included in at least one Permission set or Profile", - someElementsDontHaveRights: "Some elements are not included in at least one Permission set or Profile", + header: 'Check if elements(apex class and field) are at least in one permission set', + allElementsHaveRights: 'All elements are included in at least one Permission set or Profile', + someElementsDontHaveRights: 'Some elements are not included in at least one Permission set or Profile', }; private hasElementsWithNoRights = false; @@ -146,38 +138,47 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co private hasToDisplayJsonOnly = false; public async run(): Promise { - const config = await getConfig("user"); - this.folder = this.flags.folder || "./force-app"; - this.hasToDisplayJsonOnly = process.argv.includes("--json"); + const { flags } = await this.parse(LintAccess); + const config = await getConfig('user'); + this.folder = flags.folder || './force-app'; + this.hasToDisplayJsonOnly = this.argv.includes('--json'); - this.ignoreSourceElementsIfDefined(); - this.ignoreRightElementsIfDefined(config); + this.ignoreSourceElementsIfDefined(flags); + this.ignoreRightElementsIfDefined(config, flags); this.customSettingsNames = (await this.listLocalCustomSettings()).map((cs) => cs.name); - uxLog(this, c.green(Access.messages.header)); + uxLog(this, c.green(LintAccess.messages.header)); /* jscpd:ignore-end */ const rootFolder = path.resolve(this.folder); - const elementsToCheckByType = { apexClass: [], field: [] }; + const elementsToCheckByType: any = { apexClass: [], field: [] }; /* ELEMENTS TO CHECK */ - for (const sourceElement of Access.sourceElements) { + for (const sourceElement of LintAccess.sourceElements) { //if the type(apex class, field) is ignored we pass to the next type if (sourceElement.ignore.all) { continue; } - const findManagedPattern = rootFolder + sourceElement["regex"]; + const findManagedPattern = rootFolder + sourceElement['regex']; const matchedElements = await glob(findManagedPattern, { cwd: process.cwd() }); switch (sourceElement.type) { - case "CustomField": - elementsToCheckByType.field = await this.retrieveElementToCheck(matchedElements, sourceElement.xmlField, sourceElement.ignore.elements); + case 'CustomField': + elementsToCheckByType.field = await this.retrieveElementToCheck( + matchedElements, + sourceElement.xmlField, + sourceElement.ignore.elements + ); break; - case "ApexClass": - elementsToCheckByType.apexClass = await this.retrieveElementToCheck(matchedElements, sourceElement.xmlField, sourceElement.ignore.elements); + case 'ApexClass': + elementsToCheckByType.apexClass = await this.retrieveElementToCheck( + matchedElements, + sourceElement.xmlField, + sourceElement.ignore.elements + ); break; default: @@ -186,99 +187,106 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co } const remainingElements = await this.listElementIfNotInProfileOrPermission(rootFolder, elementsToCheckByType); - await this.verifyMultipleObjectsInPermissionSets(path.join(process.cwd(), this.folder, "**/permissionsets/*.permissionset-meta.xml")); + await this.verifyMultipleObjectsInPermissionSets( + path.join(process.cwd(), this.folder, '**/permissionsets/*.permissionset-meta.xml') + ); // Write report await this.writeOutputFile(); // Send notification - await this.manageNotification(); + await this.manageNotification(flags); // Prompt user if he/she wants to update a Permission set with missing elements await this.handleFixIssues(); // Handle output status & exitCode const statusCode = this.hasElementsWithNoRights ? 1 : 0; - if ((this.argv || []).includes("audittrail")) { + if ((this.argv || []).includes('audittrail')) { process.exitCode = statusCode; } return { statusCode: statusCode, outputString: remainingElements }; } - private ignoreSourceElementsIfDefined() { - const ignoreElements = this.flags.elementsignored; + private ignoreSourceElementsIfDefined(flags) { + const ignoreElements = flags.elementsignored; - for (const ignoredElement of ignoreElements.split(",")) { - const elementTrimmed = ignoredElement.trim(); + for (const ignoredElement of ignoreElements.split(',')) { + const elementTrimmed: string = ignoredElement.trim(); //check if all elements of a type are ignored - if (elementTrimmed === "ApexClass") { - Access.sourceElements[0].ignore.all = true; - } else if (elementTrimmed === "CustomField") { - Access.sourceElements[1].ignore.all = true; + if (elementTrimmed === 'ApexClass') { + LintAccess.sourceElements[0].ignore.all = true; + } else if (elementTrimmed === 'CustomField') { + LintAccess.sourceElements[1].ignore.all = true; } //check individual elements (ex : ApexClass:ClassB) - else if (elementTrimmed.startsWith("ApexClass")) { - Access.sourceElements[0].ignore.elements.push(elementTrimmed.substring(elementTrimmed.indexOf(":") + 1).trim()); - } else if (elementTrimmed.startsWith("CustomField")) { - Access.sourceElements[1].ignore.elements.push(elementTrimmed.substring(elementTrimmed.indexOf(":") + 1).trim()); + else if (elementTrimmed.startsWith('ApexClass')) { + LintAccess.sourceElements[0].ignore.elements.push( + elementTrimmed.substring(elementTrimmed.indexOf(':') + 1).trim() + ); + } else if (elementTrimmed.startsWith('CustomField')) { + LintAccess.sourceElements[1].ignore.elements.push( + elementTrimmed.substring(elementTrimmed.indexOf(':') + 1).trim() + ); } } } - private ignoreRightElementsIfDefined(projectConfig) { - const ignoreElements = this.flags.ignorerights ? this.flags.ignorerights : projectConfig.linterIgnoreRightMetadataFile; + private ignoreRightElementsIfDefined(projectConfig, flags) { + const ignoreElements = flags.ignorerights ? flags.ignorerights : projectConfig.linterIgnoreRightMetadataFile; if (!ignoreElements) { return; } - for (const ignoredElement of ignoreElements.split(",")) { - const elementTrimmed = ignoredElement.trim(); + for (const ignoredElement of ignoreElements.split(',')) { + const elementTrimmed: string = ignoredElement.trim(); if (elementTrimmed === this.profiles.name) { this.profiles.isIgnoredAll = true; } else if (elementTrimmed.startsWith(this.profiles.name)) { - this.profiles.elementsIgnored.push(elementTrimmed.substring(elementTrimmed.indexOf(":") + 1).trim()); + this.profiles.elementsIgnored.push(elementTrimmed.substring(elementTrimmed.indexOf(':') + 1).trim()); } if (elementTrimmed === this.permissionSet.name) { this.permissionSet.isIgnoredAll = true; } else if (elementTrimmed.startsWith(this.permissionSet.name)) { - this.permissionSet.elementsIgnored.push(elementTrimmed.substring(elementTrimmed.indexOf(":") + 1).trim()); + this.permissionSet.elementsIgnored.push(elementTrimmed.substring(elementTrimmed.indexOf(':') + 1).trim()); } } } private formatElementNameFromPath(path, type): string { - if (type === "field") { - const fieldRoute = path.substring(path.indexOf("objects/")); + if (type === 'field') { + const fieldRoute = path.substring(path.indexOf('objects/')); const objectField = fieldRoute - .substring(fieldRoute.indexOf("/") + 1) - .replace("/fields/", ".") - .replace(".field-meta.xml", ""); + .substring(fieldRoute.indexOf('/') + 1) + .replace('/fields/', '.') + .replace('.field-meta.xml', ''); return objectField; - } else if (type === "apexClass") { - return path.substring(path.indexOf("classes/")).replace("classes/", "").replace(".cls", "").split("/").pop(); + } else if (type === 'apexClass') { + return path.substring(path.indexOf('classes/')).replace('classes/', '').replace('.cls', '').split('/').pop(); } - return ""; + return ''; } private async retrieveElementToCheck(elements, xmlField, excludedElements): Promise> { - let fieldsToSearch = []; + let fieldsToSearch: any[] = []; - for (const element of elements) { + for (let element of elements) { + element = element.replace(/\\/g, '/'); // Exclude mandatory fields - if (element.endsWith(".field-meta.xml")) { + if (element.endsWith('.field-meta.xml')) { const fieldXml = await parseXmlFile(element); // Mater detail - if (fieldXml?.CustomField?.type && fieldXml?.CustomField?.type[0] === "MasterDetail") { + if (fieldXml?.CustomField?.type && fieldXml?.CustomField?.type[0] === 'MasterDetail') { continue; } // Required - if (fieldXml?.CustomField?.required && fieldXml?.CustomField?.required[0] === "true") { + if (fieldXml?.CustomField?.required && fieldXml?.CustomField?.required[0] === 'true') { continue; } // Check Parent is not eligible to fields access - const parentObject = element.substring(element.indexOf("objects/")).split("/")[1]; + const parentObject = element.substring(element.indexOf('objects/')).split('/')[1]; // Custom Metadata or DataCloud - if (parentObject.endsWith("__mdt") || parentObject.endsWith("__dll") || parentObject.endsWith("__dlm")) { + if (parentObject.endsWith('__mdt') || parentObject.endsWith('__dll') || parentObject.endsWith('__dlm')) { continue; } // Custom Setting @@ -303,73 +311,93 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co } private ruleBasedCheckForFields(el: string): Array { - const otherElementsToCheck = []; + const otherElementsToCheck: any[] = []; // Activity is the parent object of Task and Event: check also rights to avoid false positives - if (el.startsWith("Activity.")) { - const field = el.split(".")[1]; - otherElementsToCheck.push("Task." + field); - otherElementsToCheck.push("Event." + field); + if (el.startsWith('Activity.')) { + const field = el.split('.')[1]; + otherElementsToCheck.push('Task.' + field); + otherElementsToCheck.push('Event.' + field); } return otherElementsToCheck; } private async listElementIfNotInProfileOrPermission(rootFolder, elementsToCheckByType) { - const profilesFiles = await glob(rootFolder + this.profiles["regex"], { cwd: process.cwd() }); + const profilesFiles = await glob(rootFolder + this.profiles['regex'], { cwd: process.cwd() }); let remainingElements = elementsToCheckByType; //CHECK PROFILES FIRST if (!this.profiles.isIgnoredAll) { - remainingElements = await this.retrieveElementsWithoutRights(this.profiles.name, profilesFiles, elementsToCheckByType); + remainingElements = await this.retrieveElementsWithoutRights( + this.profiles.name, + profilesFiles, + elementsToCheckByType + ); } if (this.hasRemainingElementsToCheck(remainingElements) && !this.permissionSet.isIgnoredAll) { - const permissionSetFiles = await glob(rootFolder + this.permissionSet["regex"], { cwd: process.cwd() }); - remainingElements = await this.retrieveElementsWithoutRights(this.permissionSet.name, permissionSetFiles, remainingElements); + const permissionSetFiles = await glob(rootFolder + this.permissionSet['regex'], { cwd: process.cwd() }); + remainingElements = await this.retrieveElementsWithoutRights( + this.permissionSet.name, + permissionSetFiles, + remainingElements + ); } if (!this.hasRemainingElementsToCheck(remainingElements)) { - uxLog(this, c.green(Access.messages.allElementsHaveRights)); - return Access.messages.allElementsHaveRights; + uxLog(this, c.green(LintAccess.messages.allElementsHaveRights)); + return LintAccess.messages.allElementsHaveRights; } else { //list remaining elements after checking on profiles and permissions sets this.missingElementsMap = Object.assign({}, remainingElements); this.missingElements = []; - const severityIcon = getSeverityIcon("warning"); + const severityIcon = getSeverityIcon('warning'); for (const missingType of Object.keys(this.missingElementsMap)) { for (const missingItem of this.missingElementsMap[missingType]) { - this.missingElements.push({ type: missingType, element: missingItem, severity: "warning", severityIcon: severityIcon }); + this.missingElements.push({ + type: missingType, + element: missingItem, + severity: 'warning', + severityIcon: severityIcon, + }); } } remainingElements = this.constructLogAndDisplayTable(remainingElements); } - return this.hasToDisplayJsonOnly ? remainingElements : ""; + return this.hasToDisplayJsonOnly ? remainingElements : ''; } private formatPathPermissionSetOrProfile(typeFile, path) { if (typeFile == this.profiles.name) { - return path.substring(path.indexOf("profiles/")).replace("profiles/", "").replace(".profile-meta.xml", ""); + return path.substring(path.indexOf('profiles/')).replace('profiles/', '').replace('.profile-meta.xml', ''); } else if (typeFile == this.permissionSet.name) { - return path.substring(path.indexOf("permissionsets/")).replace("permissionsets/", "").replace(".permissionset-meta.xml", ""); + return path + .substring(path.indexOf('permissionsets/')) + .replace('permissionsets/', '') + .replace('.permissionset-meta.xml', ''); } - return ""; + return ''; } private async retrieveElementsWithoutRights(typeFile, files, elementsToCheckByType) { const remainingElements = elementsToCheckByType; if (typeFile == this.profiles.name) { - files = files.filter((e) => !this.profiles.elementsIgnored.includes(this.formatPathPermissionSetOrProfile(typeFile, e))); + files = files.filter( + (e) => !this.profiles.elementsIgnored.includes(this.formatPathPermissionSetOrProfile(typeFile, e)) + ); } else if (typeFile === this.permissionSet.name) { - files = files.filter((e) => !this.permissionSet.elementsIgnored.includes(this.formatPathPermissionSetOrProfile(typeFile, e))); + files = files.filter( + (e) => !this.permissionSet.elementsIgnored.includes(this.formatPathPermissionSetOrProfile(typeFile, e)) + ); } for (const file of files) { const fileXml = await parseXmlFile(file); //checking all elements in the current type - for (const currentType of Access.sourceElements) { + for (const currentType of LintAccess.sourceElements) { //checking if current type is at least once in the current profile or permission set if (!(currentType.xmlChildren in fileXml[typeFile]) || fileXml[typeFile][currentType.xmlChildren].length == 0) { continue; @@ -379,11 +407,11 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co //only readable(for fields) or enabled(apex class) rights are relevant if ( permission && - permission[currentType.xmlAccessField][0] == "true" && + permission[currentType.xmlAccessField][0] == 'true' && elementsToCheckByType[currentType.xmlField].includes(permission[currentType.xmlField][0]) ) { remainingElements[currentType.xmlField] = remainingElements[currentType.xmlField].filter( - (e) => e !== permission[currentType.xmlField][0], + (e) => e !== permission[currentType.xmlField][0] ); } } @@ -402,17 +430,17 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co } private constructLogAndDisplayTable(remainingElements) { - const remainingElementsTable = []; + const remainingElementsTable: any[] = []; let counterTable = 0; - for (const currentType of Access.sourceElements) { + for (const currentType of LintAccess.sourceElements) { for (const e of remainingElements[currentType.xmlField]) { if (!remainingElementsTable[counterTable]) { remainingElementsTable[counterTable] = {}; } - remainingElementsTable[counterTable]["Type"] = currentType.type; - remainingElementsTable[counterTable]["Element"] = e; + remainingElementsTable[counterTable]['Type'] = currentType.type; + remainingElementsTable[counterTable]['Element'] = e; counterTable++; this.hasElementsWithNoRights = true; } @@ -420,7 +448,7 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co //we create an object to have a custom header in the table if (!this.hasToDisplayJsonOnly) { - uxLog(this, c.red(Access.messages.someElementsDontHaveRights)); + uxLog(this, c.red(LintAccess.messages.someElementsDontHaveRights)); console.table(remainingElementsTable); } @@ -431,19 +459,19 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co if (this.missingElements.length === 0) { return; } - this.outputFile = await generateReportPath("lint-access", this.outputFile); + this.outputFile = await generateReportPath('lint-access', this.outputFile); this.outputFilesRes = await generateCsvFile(this.missingElements, this.outputFile); } - private async manageNotification() { + private async manageNotification(flags) { const branchMd = await getBranchMarkdown(); const notifButtons = await getNotificationButtons(); - let notifSeverity: NotifSeverity = "log"; + let notifSeverity: NotifSeverity = 'log'; let notifText = `No custom elements have no access defined in any Profile or Permission set in ${branchMd}`; - let attachments = []; + let attachments: any[] = []; // Manage detail in case there are issues if (this.missingElements.length > 0) { - notifSeverity = "warning"; + notifSeverity = 'warning'; notifText = `${this.missingElements.length} custom elements have no access defined in any Profile or Permission set in ${branchMd}`; let notifDetailText = ``; for (const missingType of Object.keys(this.missingElementsMap)) { @@ -457,9 +485,9 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co attachments = [{ text: notifDetailText }]; } - globalThis.jsForceConn = this?.org?.getConnection(); // Required for some notifications providers like Email + globalThis.jsForceConn = flags['target-org']?.getConnection(); // Required for some notifications providers like Email NotifProvider.postNotifications({ - type: "LINT_ACCESS", + type: 'LINT_ACCESS', text: notifText, attachments: attachments, buttons: notifButtons, @@ -474,37 +502,37 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co } private async handleFixIssues() { - if (!isCI && this.missingElements.length > 0 && this.argv.includes("--websocket")) { + if (!isCI && this.missingElements.length > 0 && this.argv.includes('--websocket')) { const promptUpdate = await prompts({ - type: "confirm", - message: c.cyanBright("Do you want to add the missing accesses in permission sets ?"), + type: 'confirm', + message: c.cyanBright('Do you want to add the missing accesses in permission sets ?'), }); if (promptUpdate.value === true) { const availablePermissionSets = await this.listLocalPermissionSets(); const promptsElementsPs = await prompts([ { - type: "multiselect", - name: "elements", - message: "Please select the elements you want to add in Permission Set(s)", + type: 'multiselect', + name: 'elements', + message: 'Please select the elements you want to add in Permission Set(s)', choices: this.missingElements.map((elt) => { return { title: `${elt.type}: ${elt.element}`, value: elt }; }), }, { - type: "multiselect", - name: "permissionSets", - message: "Please select the permission sets you want to update with selected elements", + type: 'multiselect', + name: 'permissionSets', + message: 'Please select the permission sets you want to update with selected elements', choices: availablePermissionSets.map((elt) => { return { title: elt.name, value: elt.filePath }; }), }, { - type: "select", - name: "access", - message: "Please select the accesses to set for the custom fields", + type: 'select', + name: 'access', + message: 'Please select the accesses to set for the custom fields', choices: [ - { title: "Readable", value: "readable" }, - { title: "Readable & Editable", value: "editable" }, + { title: 'Readable', value: 'readable' }, + { title: 'Readable & Editable', value: 'editable' }, ], }, ]); @@ -513,24 +541,26 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co await this.updatePermissionSets( promptsElementsPs.permissionSets, promptsElementsPs.elements, - promptsElementsPs.access === "editable" ? { readable: true, editable: true } : { readable: true, editable: false }, + promptsElementsPs.access === 'editable' + ? { readable: true, editable: true } + : { readable: true, editable: false } ); } } } else if (this.missingElements.length > 0) { - uxLog(this, c.yellow("Please add missing access on permission set(s)")); - uxLog(this, c.yellow("You can do it by running VsCode SFDX Hardis command Audit -> Detect missing permissions")); + uxLog(this, c.yellow('Please add missing access on permission set(s)')); + uxLog(this, c.yellow('You can do it by running VsCode SFDX Hardis command Audit -> Detect missing permissions')); } } private async listLocalCustomSettings() { const globPatternObjects = process.cwd() + `/**/*.object-meta.xml`; const objectFiles = await glob(globPatternObjects); - const csList = []; + const csList: any[] = []; for (const objectFile of objectFiles) { const objectXml = await parseXmlFile(objectFile); if (objectXml?.CustomObject?.customSettingsType?.length > 0) { - csList.push({ name: path.basename(objectFile).replace(".object-meta.xml", ""), filePath: objectFile }); + csList.push({ name: path.basename(objectFile).replace('.object-meta.xml', ''), filePath: objectFile }); } } return csList; @@ -539,9 +569,9 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co private async listLocalPermissionSets() { const globPatternPS = process.cwd() + `/**/*.permissionset-meta.xml`; const psFiles = await glob(globPatternPS); - const psList = []; + const psList: any[] = []; for (const ps of psFiles) { - psList.push({ name: path.basename(ps).replace(".permissionset-meta.xml", ""), filePath: ps }); + psList.push({ name: path.basename(ps).replace('.permissionset-meta.xml', ''), filePath: ps }); } return psList; } @@ -550,9 +580,10 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co for (const permissionSetFile of permissionSetFiles) { const psFileXml = await parseXmlFile(permissionSetFile); for (const element of elements) { + element.element = element.element.replace(/\\/g, '/'); // Apex class access - if (element.type === "apexClass") { - const className = element.element.split("/").pop(); + if (element.type === 'apexClass') { + const className = element.element.split('/').pop(); let classAccesses = psFileXml.PermissionSet?.classAccesses || []; let updated = false; classAccesses = classAccesses.map((item) => { @@ -569,12 +600,12 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co }); } psFileXml.PermissionSet.classAccesses = sortArray(classAccesses, { - by: ["apexClass"], - order: ["asc"], + by: ['apexClass'], + order: ['asc'], }); } // Custom field permission - else if (element.type === "field") { + else if (element.type === 'field') { let fieldPermissions = psFileXml.PermissionSet?.fieldPermissions || []; let updated = false; fieldPermissions = fieldPermissions.map((item) => { @@ -593,18 +624,18 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co }); } psFileXml.PermissionSet.fieldPermissions = sortArray(fieldPermissions, { - by: ["field"], - order: ["asc"], + by: ['field'], + order: ['asc'], }); } } await writeXmlFile(permissionSetFile, psFileXml); } - throw new SfdxError(c.red("Your permission sets has been updated: please CHECK THE UPDATES then commit and push !")); + throw new SfError(c.red('Your permission sets has been updated: please CHECK THE UPDATES then commit and push !')); } private async readFile(filePath: string): Promise { - return fs.readFile(filePath, "utf8"); + return fs.readFile(filePath, 'utf8'); } private async parseString(xml: string): Promise { @@ -635,11 +666,11 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co this.hasElementsWithNoRights = true; const permissionSetName = path.basename(permissionFile); for (const obj of multipleOccurrences) { - this.missingElements.push({ type: "MultipleObjectPermissions", element: `${obj} in ${permissionSetName}` }); - if (!this.missingElementsMap["MultipleObjectPermissions"]) { - this.missingElementsMap["MultipleObjectPermissions"] = []; + this.missingElements.push({ type: 'MultipleObjectPermissions', element: `${obj} in ${permissionSetName}` }); + if (!this.missingElementsMap['MultipleObjectPermissions']) { + this.missingElementsMap['MultipleObjectPermissions'] = []; } - this.missingElementsMap["MultipleObjectPermissions"].push(`${obj} in ${permissionSetName}`); + this.missingElementsMap['MultipleObjectPermissions'].push(`${obj} in ${permissionSetName}`); } } } diff --git a/src/commands/hardis/lint/metadatastatus.ts b/src/commands/hardis/lint/metadatastatus.ts index b8e4fd4c0..fcbb7aaa3 100644 --- a/src/commands/hardis/lint/metadatastatus.ts +++ b/src/commands/hardis/lint/metadatastatus.ts @@ -1,104 +1,103 @@ /* jscpd:ignore-start */ // External Libraries and Node.js Modules -import { glob } from "glob"; -import * as fs from "fs-extra"; -import * as path from "path"; +import { glob } from 'glob'; +import fs from 'fs-extra'; +import * as path from 'path'; // Salesforce Specific -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; +import { SfCommand, Flags, optionalOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; // Project Specific Utilities -import { uxLog } from "../../../common/utils"; -import { NotifProvider, NotifSeverity } from "../../../common/notifProvider"; -import { MessageAttachment } from "@slack/types"; -import { getBranchMarkdown, getNotificationButtons, getSeverityIcon } from "../../../common/utils/notifUtils"; -import { generateCsvFile, generateReportPath } from "../../../common/utils/filesUtils"; -import { GLOB_IGNORE_PATTERNS } from "../../../common/utils/projectUtils"; +import { uxLog } from '../../../common/utils/index.js'; +import { NotifProvider, NotifSeverity } from '../../../common/notifProvider/index.js'; +import { MessageAttachment } from '@slack/types'; +import { getBranchMarkdown, getNotificationButtons, getSeverityIcon } from '../../../common/utils/notifUtils.js'; +import { generateCsvFile, generateReportPath } from '../../../common/utils/filesUtils.js'; +import { GLOB_IGNORE_PATTERNS } from '../../../common/utils/projectUtils.js'; +import { CONSTANTS } from '../../../config/index.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Initialize and Load Messages -Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages("sfdx-hardis", "org"); /* jscpd:ignore-end */ -export default class metadatastatus extends SfdxCommand { - public static title = "check inactive metadatas"; +export default class LintMetadataStatus extends SfCommand { + public static title = 'check inactive metadatas'; public static description = `Check if elements (flows and validation rules) are inactive in the project -This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-inactive-metadata/) and can output Grafana, Slack and MsTeams Notifications. +This command is part of [sfdx-hardis Monitoring](${CONSTANTS.DOC_URL_ROOT}/salesforce-monitoring-inactive-metadata/) and can output Grafana, Slack and MsTeams Notifications. `; - public static examples = ["$ sfdx hardis:lint:metadatastatus"]; + public static examples = ['$ sf hardis:lint:metadatastatus']; /* jscpd:ignore-start */ - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - outputfile: flags.string({ - char: "o", - description: "Force the path and name of output report file. Must end with .csv", + outputfile: Flags.string({ + char: 'o', + description: 'Force the path and name of output report file. Must end with .csv', }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': optionalOrgFlagWithDeprecations, }; /* jscpd:ignore-end */ - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - protected static supportsUsername = true; - // Comment this out if your command does not support a hub org username protected static supportsDevhubUsername = false; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; - private flowFilePattern = "**/flows/**/*.flow-meta.xml"; - private validationRuleFilePattern = "**/objects/**/validationRules/*.validationRule-meta.xml"; + public static requiresProject = true; + private flowFilePattern = '**/flows/**/*.flow-meta.xml'; + private validationRuleFilePattern = '**/objects/**/validationRules/*.validationRule-meta.xml'; private ignorePatterns: string[] = GLOB_IGNORE_PATTERNS; - protected inactiveItems = []; + protected inactiveItems: any[] = []; protected outputFile: string; protected outputFilesRes: any = {}; public async run(): Promise { + const { flags } = await this.parse(LintMetadataStatus); const draftFlows = await this.verifyFlows(); const inactiveValidationRules = await this.verifyValidationRules(); // Prepare notifications const branchMd = await getBranchMarkdown(); const notifButtons = await getNotificationButtons(); - let notifSeverity: NotifSeverity = "log"; + let notifSeverity: NotifSeverity = 'log'; let notifText = `No inactive configuration elements has been found in ${branchMd}`; const attachments: MessageAttachment[] = []; if (draftFlows.length > 0 || inactiveValidationRules.length > 0) { - notifSeverity = "warning"; + notifSeverity = 'warning'; if (draftFlows.length > 0) { attachments.push({ - text: `*Inactive Flows*\n${draftFlows.map((file) => `• ${file.name}`).join("\n")}`, + text: `*Inactive Flows*\n${draftFlows.map((file) => `• ${file.name}`).join('\n')}`, }); } if (inactiveValidationRules.length > 0) { attachments.push({ - text: `*Inactive Validation Rules*\n${inactiveValidationRules.map((file) => `• ${file.name}`).join("\n")}`, + text: `*Inactive Validation Rules*\n${inactiveValidationRules.map((file) => `• ${file.name}`).join('\n')}`, }); } const numberInactive = draftFlows.length + inactiveValidationRules.length; notifText = `${numberInactive} inactive configuration elements have been found in ${branchMd}`; await this.buildCsvFile(draftFlows, inactiveValidationRules); } else { - uxLog(this, "No draft flow or validation rule files detected."); + uxLog(this, 'No draft flow or validation rule files detected.'); } // Post notifications - globalThis.jsForceConn = this?.org?.getConnection(); // Required for some notifications providers like Email + globalThis.jsForceConn = flags['target-org']?.getConnection(); // Required for some notifications providers like Email NotifProvider.postNotifications({ - type: "METADATA_STATUS", + type: 'METADATA_STATUS', text: notifText, attachments: attachments, buttons: notifButtons, severity: notifSeverity, - sideImage: "flow", + sideImage: 'flow', attachedFiles: this.outputFilesRes.xlsxFile ? [this.outputFilesRes.xlsxFile] : [], logElements: this.inactiveItems, data: { metric: this.inactiveItems.length }, @@ -120,12 +119,12 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co private async verifyFlows(): Promise { const draftFiles: any[] = []; const flowFiles: string[] = await glob(this.flowFilePattern, { ignore: this.ignorePatterns }); - const severityIcon = getSeverityIcon("warning"); + const severityIcon = getSeverityIcon('warning'); for (const file of flowFiles) { - const flowContent: string = await fs.readFile(file, "utf-8"); - if (flowContent.includes("Draft")) { - const fileName = path.basename(file, ".flow-meta.xml"); - draftFiles.push({ type: "Draft Flow", name: fileName, severity: "warning", severityIcon: severityIcon }); + const flowContent: string = await fs.readFile(file, 'utf-8'); + if (flowContent.includes('Draft')) { + const fileName = path.basename(file, '.flow-meta.xml'); + draftFiles.push({ type: 'Draft Flow', name: fileName, severity: 'warning', severityIcon: severityIcon }); } } @@ -142,13 +141,18 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co private async verifyValidationRules(): Promise { const inactiveRules: any[] = []; const validationRuleFiles: string[] = await glob(this.validationRuleFilePattern, { ignore: this.ignorePatterns }); - const severityIcon = getSeverityIcon("warning"); + const severityIcon = getSeverityIcon('warning'); for (const file of validationRuleFiles) { - const ruleContent: string = await fs.readFile(file, "utf-8"); - if (ruleContent.includes("false")) { - const ruleName = path.basename(file, ".validationRule-meta.xml"); + const ruleContent: string = await fs.readFile(file, 'utf-8'); + if (ruleContent.includes('false')) { + const ruleName = path.basename(file, '.validationRule-meta.xml'); const objectName = path.basename(path.dirname(path.dirname(file))); - inactiveRules.push({ type: "Inactive VR", name: `${objectName} - ${ruleName}`, severity: "warning", severityIcon: severityIcon }); + inactiveRules.push({ + type: 'Inactive VR', + name: `${objectName} - ${ruleName}`, + severity: 'warning', + severityIcon: severityIcon, + }); } } @@ -166,7 +170,7 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co * @returns {Promise} - A Promise that resolves when the CSV file has been successfully generated. */ private async buildCsvFile(draftFlows: string[], inactiveValidationRules: string[]): Promise { - this.outputFile = await generateReportPath("lint-metadatastatus", this.outputFile); + this.outputFile = await generateReportPath('lint-metadatastatus', this.outputFile); this.inactiveItems = [...draftFlows, ...inactiveValidationRules]; this.outputFilesRes = await generateCsvFile(this.inactiveItems, this.outputFile); diff --git a/src/commands/hardis/lint/missingattributes.ts b/src/commands/hardis/lint/missingattributes.ts index 9a23bf8d9..611e899a2 100644 --- a/src/commands/hardis/lint/missingattributes.ts +++ b/src/commands/hardis/lint/missingattributes.ts @@ -1,96 +1,93 @@ /* jscpd:ignore-start */ // External Libraries and Node.js Modules -import * as fs from "fs-extra"; -import * as xml2js from "xml2js"; -import { glob } from "glob"; -import * as path from "path"; +import fs from 'fs-extra'; +import * as xml2js from 'xml2js'; +import { glob } from 'glob'; +import * as path from 'path'; // Salesforce Specific -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; +import { SfCommand, Flags, optionalOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; // Project Specific Utilities -import { uxLog } from "../../../common/utils"; -import { NotifProvider, NotifSeverity } from "../../../common/notifProvider"; -import { MessageAttachment } from "@slack/types"; -import { getBranchMarkdown, getNotificationButtons, getSeverityIcon } from "../../../common/utils/notifUtils"; -import { generateCsvFile, generateReportPath } from "../../../common/utils/filesUtils"; -import { GLOB_IGNORE_PATTERNS } from "../../../common/utils/projectUtils"; +import { uxLog } from '../../../common/utils/index.js'; +import { NotifProvider, NotifSeverity } from '../../../common/notifProvider/index.js'; +import { MessageAttachment } from '@slack/types'; +import { getBranchMarkdown, getNotificationButtons, getSeverityIcon } from '../../../common/utils/notifUtils.js'; +import { generateCsvFile, generateReportPath } from '../../../common/utils/filesUtils.js'; +import { GLOB_IGNORE_PATTERNS } from '../../../common/utils/projectUtils.js'; -// Initialize and Load Messages -Messages.importMessagesDirectory(__dirname); -const messages = Messages.loadMessages("sfdx-hardis", "org"); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); /* jscpd:ignore-end */ -export default class metadatastatus extends SfdxCommand { - public static title = "check missing description on custom fields"; +export default class MetadataStatus extends SfCommand { + public static title = 'check missing description on custom fields'; public static description = "Check if elements(custom fields) aren't description"; - public static examples = ["$ sfdx hardis:lint:missingattributes"]; + public static examples = ['$ sf hardis:lint:missingattributes']; /* jscpd:ignore-start */ - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - outputfile: flags.string({ - char: "o", - description: "Force the path and name of output report file. Must end with .csv", + outputfile: Flags.string({ + char: 'o', + description: 'Force the path and name of output report file. Must end with .csv', }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': optionalOrgFlagWithDeprecations, }; /* jscpd:ignore-end */ - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - protected static supportsUsername = true; - // Comment this out if your command does not support a hub org username protected static supportsDevhubUsername = false; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; - private objectFileDirectory = "**/objects/**/fields/*.*"; - protected fieldsWithoutDescription = []; + public static requiresProject = true; + private objectFileDirectory = '**/objects/**/fields/*.*'; + protected fieldsWithoutDescription: any[] = []; protected outputFile: string; protected outputFilesRes: any = {}; private nonCustomSettingsFieldDirectories: string[] = []; private ignorePatterns: string[] = GLOB_IGNORE_PATTERNS; public async run(): Promise { + const { flags } = await this.parse(MetadataStatus); await this.filterOutCustomSettings(); this.fieldsWithoutDescription = await this.verifyFieldDescriptions(); // Build notifications const branchMd = await getBranchMarkdown(); const notifButtons = await getNotificationButtons(); - let notifSeverity: NotifSeverity = "log"; + let notifSeverity: NotifSeverity = 'log'; let notifText = `No missing descriptions on fields has been found in ${branchMd}`; let attachments: MessageAttachment[] = []; if (this.fieldsWithoutDescription.length > 0) { - notifSeverity = "warning"; + notifSeverity = 'warning'; notifText = `${this.fieldsWithoutDescription.length} missing descriptions on fields have been found in ${branchMd}`; await this.buildCsvFile(this.fieldsWithoutDescription); attachments = [ { - text: `*Missing descriptions*\n${this.fieldsWithoutDescription.map((file) => `• ${file.name}`).join("\n")}`, + text: `*Missing descriptions*\n${this.fieldsWithoutDescription.map((file) => `• ${file.name}`).join('\n')}`, }, ]; } else { - uxLog(this, "No missing descriptions on fields have been found"); + uxLog(this, 'No missing descriptions on fields have been found'); } // Post notifications - globalThis.jsForceConn = this?.org?.getConnection(); // Required for some notifications providers like Email + globalThis.jsForceConn = flags['target-org']?.getConnection(); // Required for some notifications providers like Email NotifProvider.postNotifications({ - type: "MISSING_ATTRIBUTES", + type: 'MISSING_ATTRIBUTES', text: notifText, attachments: attachments, buttons: notifButtons, severity: notifSeverity, - sideImage: "flow", + sideImage: 'flow', logElements: this.fieldsWithoutDescription, data: { metric: this.fieldsWithoutDescription.length }, metrics: { @@ -109,7 +106,7 @@ export default class metadatastatus extends SfdxCommand { if (fs.existsSync(objectMetaFilePath)) { try { - const objectMetaFileContent = fs.readFileSync(objectMetaFilePath, "utf8"); + const objectMetaFileContent = fs.readFileSync(objectMetaFilePath, 'utf8'); let isCustomSettingsObject = false; const result = await parserCS.parseStringPromise(objectMetaFileContent); @@ -135,22 +132,22 @@ export default class metadatastatus extends SfdxCommand { this.nonCustomSettingsFieldDirectories.map(async (fieldFile) => { const fieldContent = await this.readFileAsync(fieldFile); return await this.parseXmlStringAsync(fieldContent); - }), + }) ); - const severityIconInfo = getSeverityIcon("info"); + const severityIconInfo = getSeverityIcon('info'); for (let i = 0; i < fieldResults.length; i++) { const fieldResult = fieldResults[i]; if (fieldResult && fieldResult.CustomField) { const fieldName = fieldResult.CustomField.fullName[0]; - if (fieldName.endsWith("__c") && !fieldResult.CustomField.description) { - const fieldFile = this.nonCustomSettingsFieldDirectories[i]; - const objectName = fieldFile.split("/").slice(-3, -2)[0]; + if (fieldName.endsWith('__c') && !fieldResult.CustomField.description) { + const fieldFile = this.nonCustomSettingsFieldDirectories[i].replace(/\\/g, '/'); + const objectName = fieldFile.split('/').slice(-3, -2)[0]; const fullFieldName = `${objectName}.${fieldName}`; fieldsWithoutDescription.push({ name: fullFieldName, object: objectName, field: fieldName, - severity: "info", + severity: 'info', severityIcon: severityIconInfo, }); } @@ -173,7 +170,7 @@ export default class metadatastatus extends SfdxCommand { private readFileAsync(filePath: string): Promise { return new Promise((resolve, reject) => { - fs.readFile(filePath, "utf8", (err, data) => { + fs.readFile(filePath, 'utf8', (err, data) => { if (err) { reject(err); } else { @@ -183,9 +180,9 @@ export default class metadatastatus extends SfdxCommand { }); } - private async buildCsvFile(fieldsWithoutDescription: string[]): Promise { - this.outputFile = await generateReportPath("lint-missingattributes", this.outputFile); - const csvData = fieldsWithoutDescription.map((field) => ({ type: "Field", name: field })); + private async buildCsvFile(fieldsWithoutDescription: any[]): Promise { + this.outputFile = await generateReportPath('lint-missingattributes', this.outputFile); + const csvData = fieldsWithoutDescription.map((field) => ({ type: 'Field', name: field.name })); this.outputFilesRes = await generateCsvFile(csvData, this.outputFile); } } diff --git a/src/commands/hardis/lint/unusedmetadatas.ts b/src/commands/hardis/lint/unusedmetadatas.ts index 417fa7985..e1f6124d3 100644 --- a/src/commands/hardis/lint/unusedmetadatas.ts +++ b/src/commands/hardis/lint/unusedmetadatas.ts @@ -1,71 +1,69 @@ /* jscpd:ignore-start */ // External Libraries -import { glob } from "glob"; -import * as fs from "fs-extra"; -import * as xml2js from "xml2js"; -import * as path from "path"; +import { glob } from 'glob'; +import fs from 'fs-extra'; +import * as xml2js from 'xml2js'; +import * as path from 'path'; // Salesforce Specific -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; +import { SfCommand, Flags, optionalOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; // Project Specific Utilities -import { NotifProvider, NotifSeverity } from "../../../common/notifProvider"; -import { MessageAttachment } from "@slack/types"; -import { getNotificationButtons, getBranchMarkdown, getSeverityIcon } from "../../../common/utils/notifUtils"; -import { generateCsvFile, generateReportPath } from "../../../common/utils/filesUtils"; -import { uxLog } from "../../../common/utils"; -import { GLOB_IGNORE_PATTERNS } from "../../../common/utils/projectUtils"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); -// Load Messages -const messages = Messages.loadMessages("sfdx-hardis", "org"); +import { NotifProvider, NotifSeverity } from '../../../common/notifProvider/index.js'; +import { MessageAttachment } from '@slack/types'; +import { getNotificationButtons, getBranchMarkdown, getSeverityIcon } from '../../../common/utils/notifUtils.js'; +import { generateCsvFile, generateReportPath } from '../../../common/utils/filesUtils.js'; +import { uxLog } from '../../../common/utils/index.js'; +import { GLOB_IGNORE_PATTERNS } from '../../../common/utils/projectUtils.js'; +import { CONSTANTS } from '../../../config/index.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); /* jscpd:ignore-end */ -export default class UnusedMetadatas extends SfdxCommand { - public static title = "check unused labels and custom permissions"; +export default class UnusedMetadatas extends SfCommand { + public static title = 'check unused labels and custom permissions'; public static description = `Check if elements (custom labels and custom permissions) are used in the project -This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-unused-metadata/) and can output Grafana, Slack and MsTeams Notifications. +This command is part of [sfdx-hardis Monitoring](${CONSTANTS.DOC_URL_ROOT}/salesforce-monitoring-unused-metadata/) and can output Grafana, Slack and MsTeams Notifications. `; - public static examples = ["$ sfdx hardis:lint:unusedmetadatas"]; + public static examples = ['$ sf hardis:lint:unusedmetadatas']; /* jscpd:ignore-start */ - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - outputfile: flags.string({ - char: "o", - description: "Force the path and name of output report file. Must end with .csv", + outputfile: Flags.string({ + char: 'o', + description: 'Force the path and name of output report file. Must end with .csv', }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': optionalOrgFlagWithDeprecations, }; /* jscpd:ignore-end */ - protected unusedData = []; + protected unusedData: any[] = []; protected outputFile: string; protected outputFilesRes: any = {}; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - protected static supportsUsername = true; - // Comment this out if your command does not support a hub org username + protected static supportsDevhubUsername = false; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; private ignorePatterns: string[] = GLOB_IGNORE_PATTERNS; private projectFiles: string[]; - private labelFilePattern = "**/CustomLabels.labels-meta.xml"; - private customPermissionFilePattern = "**/customPermissions/*.xml"; + private labelFilePattern = '**/CustomLabels.labels-meta.xml'; + private customPermissionFilePattern = '**/customPermissions/*.xml'; public async run(): Promise { + const { flags } = await this.parse(UnusedMetadatas); await this.setProjectFiles(); const unusedLabels = await this.verifyLabels(); const unusedCustomPermissions = await this.verifyCustomPermissions(); @@ -73,35 +71,37 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co // Build notification const branchMd = await getBranchMarkdown(); const notifButtons = await getNotificationButtons(); - let notifSeverity: NotifSeverity = "log"; + let notifSeverity: NotifSeverity = 'log'; let notifText = `No unused metadatas has been detected in ${branchMd}`; const attachments: MessageAttachment[] = []; if (unusedLabels.length > 0) { attachments.push({ - text: `*Unused Labels*\n${unusedLabels.map((label) => `• ${label.name}`).join("\n")}`, + text: `*Unused Labels*\n${unusedLabels.map((label) => `• ${label.name}`).join('\n')}`, }); } if (unusedCustomPermissions.length > 0) { attachments.push({ - text: `*Unused Custom Permissions*\n${unusedCustomPermissions.map((permission) => `• ${permission.name}`).join("\n")}`, + text: `*Unused Custom Permissions*\n${unusedCustomPermissions + .map((permission) => `• ${permission.name}`) + .join('\n')}`, }); } if (unusedLabels.length > 0 || unusedCustomPermissions.length > 0) { - notifSeverity = "warning"; + notifSeverity = 'warning'; notifText = `${this.unusedData.length} unused metadatas have been detected in ${branchMd}`; await this.buildCsvFile(unusedLabels, unusedCustomPermissions); } else { - uxLog(this, "No unused labels or custom permissions detected."); + uxLog(this, 'No unused labels or custom permissions detected.'); } // Post notification - globalThis.jsForceConn = this?.org?.getConnection(); // Required for some notifications providers like Email + globalThis.jsForceConn = flags['target-org']?.getConnection(); // Required for some notifications providers like Email NotifProvider.postNotifications({ - type: "UNUSED_METADATAS", + type: 'UNUSED_METADATAS', text: notifText, attachments: attachments, buttons: notifButtons, severity: notifSeverity, - sideImage: "flow", + sideImage: 'flow', logElements: this.unusedData, data: { metric: this.unusedData.length }, metrics: { @@ -121,12 +121,12 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co const labelFilePath = labelFiles[0]; if (!labelFilePath) { - console.warn("No label file found."); + console.warn('No label file found.'); return []; } return new Promise((resolve, reject) => { - fs.readFile(labelFilePath, "utf-8", (errorReadingFile, data) => { + fs.readFile(labelFilePath, 'utf-8', (errorReadingFile, data) => { if (errorReadingFile) { reject(errorReadingFile); return; @@ -137,7 +137,7 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co reject(errorParseString); return; } - const severityIconInfo = getSeverityIcon("info"); + const severityIconInfo = getSeverityIcon('info'); const labelsArray: string[] = result.CustomLabels.labels.map((label: any) => label.fullName[0]); const unusedLabels: any[] = labelsArray .filter((label) => { @@ -145,14 +145,16 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co const cLower = `c.${label.toLowerCase()}`; const auraPattern = `{!$Label.c.${label.toLowerCase()}}`; return !this.projectFiles.some((filePath) => { - const fileContent = fs.readFileSync(filePath, "utf-8").toLowerCase(); - return fileContent.includes(labelLower) || fileContent.includes(cLower) || fileContent.includes(auraPattern); + const fileContent = fs.readFileSync(filePath, 'utf-8').toLowerCase(); + return ( + fileContent.includes(labelLower) || fileContent.includes(cLower) || fileContent.includes(auraPattern) + ); }); }) .map((label) => { return { name: label, - severity: "info", + severity: 'info', severityIcon: severityIconInfo, }; }); @@ -169,17 +171,19 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co */ private async verifyCustomPermissions(): Promise { const foundLabels = new Map(); - const customPermissionFiles: string[] = await glob(this.customPermissionFilePattern, { ignore: this.ignorePatterns }); + const customPermissionFiles: string[] = await glob(this.customPermissionFilePattern, { + ignore: this.ignorePatterns, + }); if (!customPermissionFiles) { - console.warn("No custom permission file found."); + console.warn('No custom permission file found.'); return []; } for (const file of customPermissionFiles) { - const fileData = await fs.readFile(file, "utf-8"); - const fileName = path.basename(file, ".customPermission-meta.xml"); - let label = ""; + const fileData = await fs.readFile(file, 'utf-8'); + const fileName = path.basename(file, '.customPermission-meta.xml'); + let label = ''; xml2js.parseString(fileData, (error, result) => { if (error) { @@ -189,20 +193,20 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co label = result.CustomPermission.label[0]; }); for (const filePath of this.projectFiles) { - const fileContent: string = fs.readFileSync(filePath, "utf-8"); + const fileContent: string = fs.readFileSync(filePath, 'utf-8'); if (fileContent.includes(fileName) || fileContent.includes(label)) { const currentCount = foundLabels.get(fileName) || 0; foundLabels.set(fileName, currentCount + 1); } } } - const severityIconInfo = getSeverityIcon("info"); + const severityIconInfo = getSeverityIcon('info'); const result = [...foundLabels.keys()] .filter((key) => (foundLabels.get(key) || 0) < 2) .map((name) => { return { name: name, - severity: "info", + severity: 'info', severityIcon: severityIconInfo, }; }); @@ -210,14 +214,14 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co } private async setProjectFiles(): Promise { - this.projectFiles = await glob("**/*.{cls,trigger,js,html,xml,cmp,email,page}", { ignore: this.ignorePatterns }); + this.projectFiles = await glob('**/*.{cls,trigger,js,html,xml,cmp,email,page}', { ignore: this.ignorePatterns }); } private async buildCsvFile(unusedLabels: string[], unusedCustomPermissions: string[]): Promise { - this.outputFile = await generateReportPath("lint-unusedmetadatas", this.outputFile); + this.outputFile = await generateReportPath('lint-unusedmetadatas', this.outputFile); this.unusedData = [ - ...unusedLabels.map((label) => ({ type: "Label", name: label })), - ...unusedCustomPermissions.map((permission) => ({ type: "Custom Permission", name: permission })), + ...unusedLabels.map((label) => ({ type: 'Label', name: label })), + ...unusedCustomPermissions.map((permission) => ({ type: 'Custom Permission', name: permission })), ]; this.outputFilesRes = await generateCsvFile(this.unusedData, this.outputFile); diff --git a/src/commands/hardis/mdapi/deploy.ts b/src/commands/hardis/mdapi/deploy.ts index 43ed29e90..f75871afa 100644 --- a/src/commands/hardis/mdapi/deploy.ts +++ b/src/commands/hardis/mdapi/deploy.ts @@ -1,11 +1,12 @@ /* jscpd:ignore-start */ -import { flags, FlagsConfig, SfdxCommand } from "@salesforce/command"; -import { Duration } from "@salesforce/kit"; -import { AnyJson } from "@salesforce/ts-types"; -import { wrapSfdxCoreCommand } from "../../../common/utils/wrapUtils"; +import { Flags, requiredOrgFlagWithDeprecations, SfCommand } from '@salesforce/sf-plugins-core'; +import c from 'chalk'; +import { AnyJson } from '@salesforce/ts-types'; +import { wrapSfdxCoreCommand } from '../../../common/utils/wrapUtils.js'; +import { uxLog } from '../../../common/utils/index.js'; -const xorFlags = ["zipfile", "validateddeployrequestid", "deploydir"]; -export class Deploy extends SfdxCommand { +const xorFlags = ['zipfile', 'validateddeployrequestid', 'deploydir']; +export class Deploy extends SfCommand { public static readonly description = `sfdx-hardis wrapper for sfdx force:mdapi:deploy that displays tips to solve deployment errors. [![Assisted solving of Salesforce deployments errors](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-deployment-errors.jpg)](https://nicolas.vuillamy.fr/assisted-solving-of-salesforce-deployments-errors-47f3666a9ed0) @@ -13,79 +14,89 @@ export class Deploy extends SfdxCommand { [See documentation of Salesforce command](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_force_mdapi.htm#cli_reference_force_mdapi_deploy) `; public static readonly examples = []; - public static readonly requiresUsername = true; - public static readonly flagsConfig: FlagsConfig = { - checkonly: flags.boolean({ - char: "c", - description: "checkOnly", - }), - deploydir: flags.directory({ - char: "d", - description: "deployDir", + public static readonly flags: any = { + checkonly: Flags.boolean({ + char: 'c', + description: 'checkOnly', + }), + deploydir: Flags.directory({ + char: 'd', + description: 'deployDir', exactlyOne: xorFlags, }), - wait: flags.minutes({ - char: "w", - description: "wait", - default: Duration.minutes(0), + wait: Flags.integer({ + char: 'w', + description: 'wait', + default: 120, min: -1, }), - testlevel: flags.enum({ - char: "l", - description: "testLevel", - options: ["NoTestRun", "RunSpecifiedTests", "RunLocalTests", "RunAllTestsInOrg"], - default: "NoTestRun", + testlevel: Flags.string({ + char: 'l', + description: 'testLevel', + options: ['NoTestRun', 'RunSpecifiedTests', 'RunLocalTests', 'RunAllTestsInOrg'], + default: 'NoTestRun', }), - runtests: flags.array({ - char: "r", - description: "runTests", + runtests: Flags.string({ + char: 'r', + description: 'runTests', default: [], + multiple: true, }), - ignoreerrors: flags.boolean({ - char: "o", - description: "ignoreErrors", + ignoreerrors: Flags.boolean({ + char: 'o', + description: 'ignoreErrors', }), - ignorewarnings: flags.boolean({ - char: "g", - description: "ignoreWarnings", + ignorewarnings: Flags.boolean({ + char: 'g', + description: 'ignoreWarnings', }), - validateddeployrequestid: flags.id({ - char: "q", - description: "validatedDeployRequestId", + validateddeployrequestid: Flags.string({ + char: 'q', + description: 'validatedDeployRequestId', exactlyOne: xorFlags, - exclusive: ["testlevel", "runtests", "ignoreerrors", "ignorewarnings", "checkonly"], + exclusive: ['testlevel', 'runtests', 'ignoreerrors', 'ignorewarnings', 'checkonly'], }), - verbose: flags.builtin({ - description: "verbose", + verbose: Flags.boolean({ + description: 'verbose', }), - zipfile: flags.filepath({ - char: "f", - description: "zipFile", + zipfile: Flags.file({ + char: 'f', + description: 'zipFile', exactlyOne: xorFlags, }), - singlepackage: flags.boolean({ - char: "s", - description: "singlePackage", + singlepackage: Flags.boolean({ + char: 's', + description: 'singlePackage', }), - soapdeploy: flags.boolean({ - description: "soapDeploy", + soapdeploy: Flags.boolean({ + description: 'soapDeploy', }), - purgeondelete: flags.boolean({ - description: "purgeOnDelete", + purgeondelete: Flags.boolean({ + description: 'purgeOnDelete', }), - concise: flags.builtin({ - description: "concise", + concise: Flags.boolean({ + description: 'concise', }), - debug: flags.boolean({ + debug: Flags.boolean({ default: false, - description: "debug", + description: 'debug', }), - websocket: flags.string({ - description: "websocket", + websocket: Flags.string({ + description: 'websocket', }), + 'target-org': requiredOrgFlagWithDeprecations, }; /* jscpd:ignore-end */ public async run(): Promise { - return await wrapSfdxCoreCommand("sfdx force:mdapi:deploy", this.argv, this, this.flags.debug); + const { flags } = await this.parse(Deploy); + uxLog(this, c.red('This command will be removed by Salesforce in November 2024.')); + uxLog(this, c.red('Please migrate to command sf hardis project deploy start')); + uxLog( + this, + c.red( + 'See https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_mig_deploy_retrieve.htm' + ) + ); + return await wrapSfdxCoreCommand('sfdx force:mdapi:deploy', this.argv, this, flags.debug); } } diff --git a/src/commands/hardis/misc/purge-references.ts b/src/commands/hardis/misc/purge-references.ts new file mode 100644 index 000000000..320647ddb --- /dev/null +++ b/src/commands/hardis/misc/purge-references.ts @@ -0,0 +1,162 @@ +/* jscpd:ignore-start */ +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages, SfError } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import ora, { Ora } from 'ora'; +import * as path from 'path'; + +import { execCommand, uxLog } from '../../../common/utils/index.js'; +import { prompts } from '../../../common/utils/prompts.js'; +import { MetadataUtils } from '../../../common/metadata-utils/index.js'; +import { glob } from 'glob'; +import { GLOB_IGNORE_PATTERNS } from '../../../common/utils/projectUtils.js'; +import { applyAllReplacementsDefinitions } from '../../../common/utils/xmlUtils.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class PurgeRef extends SfCommand { + public static title = 'Purge References'; + + public static description = `Purge references to any string in org metadatas before a deployment. + +For example, this can be handy if you need to change the type of a custom field from Master Detail to Lookup. + +USE WITH EXTREME CAUTION AND CAREFULLY READ THE MESSAGES !`; + + public static examples = ['$ sf hardis:misc:purge-references']; + + public static flags: any = { + references: Flags.string({ + char: 'r', + description: 'Comma-separated list of references to find in metadatas', + }), + debug: Flags.boolean({ + char: 'd', + default: false, + description: messages.getMessage('debugMode'), + }), + websocket: Flags.string({ + description: messages.getMessage('websocket'), + }), + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', + }), + 'target-org': requiredOrgFlagWithDeprecations, + }; + + // Set this to true if your command requires a project workspace; 'requiresProject' is false by default + public static requiresProject = true; + + /* jscpd:ignore-end */ + private ignorePatterns: string[] = GLOB_IGNORE_PATTERNS; + protected referenceStrings: string[] = []; + protected referenceStringsLabel: string; + protected allMatchingSourceFiles: string[] = []; + protected spinnerCustom: Ora; + + public async run(): Promise { + uxLog(this, c.yellow(c.bold(PurgeRef.description))); + const { flags } = await this.parse(PurgeRef); + // Collect input parameters + this.referenceStrings = (flags?.references || '').split(','); + if (this.referenceStrings.length == 1 && this.referenceStrings[0] === '') { + const refPromptResult = await prompts({ + type: 'text', + message: 'Please input a comma-separated list of strings that you want to purge (example: Affaire__c)', + }); + this.referenceStrings = refPromptResult.value.split(','); + } + if (this.referenceStrings.length == 1 && this.referenceStrings[0] === '') { + throw new SfError('You must input at least one string to check for references'); + } + for (const refString of this.referenceStrings) { + if (refString.endsWith('__c') && !this.referenceStrings.includes(refString.replace('__c', '__r'))) { + this.referenceStrings.push(refString.replace('__c', '__r')); + } + } + this.referenceStringsLabel = this.referenceStrings.join(','); + + // Retrieve metadatas if necessary + const retrieveNeedRes = await prompts({ + type: 'select', + message: `Are your local sources up to date with target org ${flags[ + 'target-org' + ].getUsername()}, or do you need to retrieve some of them ?`, + choices: [ + { value: true, title: 'My local sfdx sources are up to date with the target org' }, + { value: false, title: 'I need to retrieve metadatas :)' }, + ], + }); + if (retrieveNeedRes.value === false) { + const metadatas = await MetadataUtils.promptMetadataTypes(); + const metadataArg = metadatas.map((metadataType: any) => metadataType.xmlName).join(' '); + await execCommand(`sf project retrieve start --ignore-conflicts --metadata ${metadataArg}`, this, { fail: true }); + } + + // Find sources that contain references + this.spinnerCustom = ora({ + text: `Browsing sources to find references to ${this.referenceStringsLabel}...`, + spinner: 'moon', + }).start(); + const packageDirectories = this.project?.getPackageDirectories() || []; + this.allMatchingSourceFiles = []; + for (const packageDirectory of packageDirectories) { + const sourceFiles = await glob('*/**/*.{cls,trigger,xml}', { + ignore: this.ignorePatterns, + cwd: packageDirectory.fullPath, + }); + const matchingSourceFiles = sourceFiles + .filter((sourceFile) => { + sourceFile = path.join(packageDirectory.path, sourceFile); + const fileContent = fs.readFileSync(sourceFile, 'utf8'); + return this.referenceStrings.some((refString) => fileContent.includes(refString)); + }) + .map((sourceFile) => path.join(packageDirectory.path, sourceFile)); + this.allMatchingSourceFiles.push(...matchingSourceFiles); + } + this.spinnerCustom.succeed(`Found ${this.allMatchingSourceFiles.length} sources with references`); + this.allMatchingSourceFiles.sort(); + uxLog(this, 'Matching files:\n' + c.grey(this.allMatchingSourceFiles.join('\n'))); + + // Handling Apex classes + await applyAllReplacementsDefinitions( + this.allMatchingSourceFiles, + this.referenceStrings, + this.getAllReplacements() + ); + + return { message: 'Command completed' }; + } + + private getAllReplacements() { + return [ + // Apex + { + extensions: ['.cls', '.trigger'], + label: 'Apex', + type: 'code', + replaceMode: ['line'], + refRegexes: [ + // , REF , + { regex: `,{{REF}},`, replace: ',' }, + { regex: `, {{REF}},`, replace: ',' }, + { regex: `,{{REF}} ,`, replace: ',' }, + { regex: `, {{REF}} ,`, replace: ',' }, + // , REF = xxx , + { regex: `,{{REF}}[ |=].+\\,`, replace: ',' }, + { regex: `, {{REF}}[ |=].+\\,`, replace: ',' }, + { regex: `,{{REF}}[ |=].+\\, `, replace: ',' }, + { regex: `, {{REF}}[ |=].+\\ ,`, replace: ',' }, + // , REF = xxx ) + { regex: `,{{REF}}[ |=].+\\)`, replace: ')' }, + { regex: `, {{REF}}[ |=].+\\)`, replace: ')' }, + // REF = xxx , + { regex: `{{REF}}[ |=].+\\)`, replace: ')' }, + ], + }, + ]; + } +} diff --git a/src/commands/hardis/misc/toml2csv.ts b/src/commands/hardis/misc/toml2csv.ts index 3caf04842..a48e02390 100644 --- a/src/commands/hardis/misc/toml2csv.ts +++ b/src/commands/hardis/misc/toml2csv.ts @@ -1,92 +1,84 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages, SfdxError } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as moment from "moment"; -import * as ora from "ora"; -import * as path from "path"; -import * as readline from "readline"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages, SfError } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import moment from 'moment'; +import ora from 'ora'; +import * as path from 'path'; +import * as readline from 'readline'; -import { stripAnsi, uxLog } from "../../../common/utils"; -import { countLinesInFile } from "../../../common/utils/filesUtils"; -import { getRecordTypeId } from "../../../common/utils/orgUtils"; +import { stripAnsi, uxLog } from '../../../common/utils/index.js'; +import { countLinesInFile } from '../../../common/utils/filesUtils.js'; +import { getRecordTypeId } from '../../../common/utils/orgUtils.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class Toml2Csv extends SfCommand { + public static title = 'TOML to CSV'; -export default class Toml2Csv extends SfdxCommand { - public static title = "TOML to CSV"; - - public static description = "Split TOML file into distinct CSV files"; + public static description = 'Split TOML file into distinct CSV files'; public static examples = [ - "$ sfdx hardis:misc:toml2csv --tomlfile 'D:/clients/toto/V1_full.txt' ", - "$ sfdx hardis:misc:toml2csv --skiptransfo --tomlfile 'D:/clients/toto/V1_full.txt' ", - "$ sfdx hardis:misc:toml2csv --skiptransfo --tomlfile 'D:/clients/toto/V1_full.txt' --outputdir 'C:/tmp/rrrr'", - "$ NODE_OPTIONS=--max_old_space_size=9096 sfdx hardis:misc:toml2csv --skiptransfo --tomlfile './input/V1.txt' --outputdir './output' --filtersections 'COMPTES,SOUS'", + "$ sf hardis:misc:toml2csv --tomlfile 'D:/clients/toto/V1_full.txt' ", + "$ sf hardis:misc:toml2csv --skiptransfo --tomlfile 'D:/clients/toto/V1_full.txt' ", + "$ sf hardis:misc:toml2csv --skiptransfo --tomlfile 'D:/clients/toto/V1_full.txt' --outputdir 'C:/tmp/rrrr'", + "$ NODE_OPTIONS=--max_old_space_size=9096 sf hardis:misc:toml2csv --skiptransfo --tomlfile './input/V1.txt' --outputdir './output' --filtersections 'COMPTES,SOUS'", ]; - protected static flagsConfig = { - tomlfile: flags.string({ - char: "f", - description: "Input TOML file path", + public static flags: any = { + tomlfile: Flags.string({ + char: 'f', + description: 'Input TOML file path', required: true, }), - transfoconfig: flags.string({ - char: "t", - description: "Path to JSON config file for mapping and transformation", + transfoconfig: Flags.string({ + char: 't', + description: 'Path to JSON config file for mapping and transformation', }), - filtersections: flags.array({ - char: "l", - description: "List of sections to process (if not set, all sections will be processed)", + filtersections: Flags.string({ + char: 'l', + description: 'List of sections to process (if not set, all sections will be processed)', default: [], + multiple: true, }), - skiptransfo: flags.boolean({ - char: "s", + skiptransfo: Flags.boolean({ + char: 's', default: false, - description: "Do not apply transformation to input data", + description: 'Do not apply transformation to input data', }), - outputdir: flags.string({ - char: "o", - description: "Output directory", + outputdir: Flags.string({ + char: 'o', + description: 'Output directory', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; protected transfoConfig: any = {}; protected transfoConfigFile: string; protected rootConfigDirectory: string; protected outputDir: string; protected skipTransfo = false; - protected filterSections = []; + protected filterSections: any[] = []; protected doFilterSections = false; - protected spinner: any; + protected spinnerCustom: any; protected spinnerInterval: any; protected inputFileSeparator: string; protected outputFileSeparator: string; @@ -118,14 +110,15 @@ export default class Toml2Csv extends SfdxCommand { public async run(): Promise { // Collect input parameters - const tomlFile = this.flags.tomlfile; - const tomlFileEncoding = this.flags.tomlfileencoding || "utf8"; - this.transfoConfigFile = this.flags.transfoconfig || path.join(process.cwd(), "transfoConfig.json"); + const { flags } = await this.parse(Toml2Csv); + const tomlFile = flags.tomlfile; + const tomlFileEncoding = 'utf8'; + this.transfoConfigFile = flags.transfoconfig || path.join(process.cwd(), 'transfoConfig.json'); this.rootConfigDirectory = path.dirname(this.transfoConfigFile); - this.outputDir = this.flags.outputdir || path.join(process.cwd(), path.parse(tomlFile).name); - const debugMode = this.flags.debug || false; - this.skipTransfo = this.flags.skiptransfo || false; - this.filterSections = this.flags.filtersections || []; + this.outputDir = flags.outputdir || path.join(process.cwd(), path.parse(tomlFile).name); + const debugMode = flags.debug || false; + this.skipTransfo = flags.skiptransfo || false; + this.filterSections = flags.filtersections || []; this.doFilterSections = this.filterSections.length > 0; // Check TOML file is existing @@ -137,31 +130,38 @@ export default class Toml2Csv extends SfdxCommand { if (!fs.existsSync(this.transfoConfigFile)) { this.triggerError(c.red(`Mapping/Transco config ${c.bold(this.transfoConfigFile)} not found`)); } - const transfoConfigInit = JSON.parse(fs.readFileSync(this.transfoConfigFile, "utf-8")); + const transfoConfigInit = JSON.parse(fs.readFileSync(this.transfoConfigFile, 'utf-8')); this.transfoConfig = this.completeTransfoConfig(transfoConfigInit); // Set separators - this.inputFileSeparator = this.transfoConfig?.inputFile?.separator || ","; - this.outputFileSeparator = this.transfoConfig?.outputFile?.separator || ","; + this.inputFileSeparator = this.transfoConfig?.inputFile?.separator || ','; + this.outputFileSeparator = this.transfoConfig?.outputFile?.separator || ','; // Create output directory if not existing yet await fs.ensureDir(this.outputDir); // Empty output dir if (!this.transfoConfig?.skipResetOutputDir === true) { await fs.emptyDir(this.outputDir); - await fs.ensureDir(path.join(this.outputDir, "errors")); + await fs.ensureDir(path.join(this.outputDir, 'errors')); } - uxLog(this, c.cyan(`Generating CSV files from ${c.green(tomlFile)} (encoding ${tomlFileEncoding}) into folder ${c.green(this.outputDir)}`)); + uxLog( + this, + c.cyan( + `Generating CSV files from ${c.green(tomlFile)} (encoding ${tomlFileEncoding}) into folder ${c.green( + this.outputDir + )}` + ) + ); // Start spinner - this.spinner = ora({ text: `Processing...`, spinner: "moon" }).start(); + this.spinnerCustom = ora({ text: `Processing...`, spinner: 'moon' }).start(); this.spinnerInterval = setInterval(() => { this.updateSpinner(); }, 10000); // Read TOML file and process lines section by section - const fileStream = fs.createReadStream(tomlFile, { encoding: this.transfoConfig?.inputFile?.encoding || "utf8" }); + const fileStream = fs.createReadStream(tomlFile, { encoding: this.transfoConfig?.inputFile?.encoding || 'utf8' }); const rl = readline.createInterface({ input: fileStream, crlfDelay: Infinity, @@ -177,9 +177,9 @@ export default class Toml2Csv extends SfdxCommand { continue; } // Section line - if (line.startsWith("[")) { + if (line.startsWith('[')) { this.stats.sectionLinesNb++; - this.currentSection = /\[(.*)\]/gm.exec(line)[1]; // ex: get COMPTES from [COMPTES] + this.currentSection = (/\[(.*)\]/gm.exec(line) || '')[1]; // ex: get COMPTES from [COMPTES] if (this.doFilterSections && !this.filterSections.includes(this.currentSection)) { continue; } @@ -197,9 +197,15 @@ export default class Toml2Csv extends SfdxCommand { this.sectionLines[this.currentSection] = this.sectionLines[this.currentSection] || []; // Init section files writeStreams if (this.tomlSectionsFileWriters[this.currentSection] == null) { - this.tomlSectionsFileWriters[this.currentSection] = await this.createSectionWriteStream(this.currentSection, false); + this.tomlSectionsFileWriters[this.currentSection] = await this.createSectionWriteStream( + this.currentSection, + false + ); if (!this.skipTransfo) { - this.tomlSectionsErrorsFileWriters[this.currentSection] = await this.createSectionWriteStream(this.currentSection, true); + this.tomlSectionsErrorsFileWriters[this.currentSection] = await this.createSectionWriteStream( + this.currentSection, + true + ); } } } @@ -240,7 +246,7 @@ export default class Toml2Csv extends SfdxCommand { } else { // With transformation try { - await this.convertLineToSfThenWrite(this.currentSection, lineSplit); + await this.convertLineToSfThenWrite(this.currentSection, lineSplit, flags); } catch (e) { // Manage error this.stats.dataErrorLinesNb++; @@ -251,16 +257,16 @@ export default class Toml2Csv extends SfdxCommand { .map((val) => (this.inputFileSeparator !== this.outputFileSeparator ? this.formatCsvCell(val) : val)) // Add quotes if value contains a separator .join(this.outputFileSeparator) + this.outputFileSeparator + - stripAnsi(`"${e.message.replace(/"/g, "'")}"`); + stripAnsi(`"${(e as Error).message.replace(/"/g, "'")}"`); if (this.checkNotDuplicate(this.currentSection, lineError)) { await this.writeLine(lineError, this.tomlSectionsErrorsFileWriters[this.currentSection]); this.addLineInCache(this.currentSection, lineSplit, lineError, false); } - if (this.lineErrorMessages[e.message]) { - this.lineErrorMessages[e.message]++; + if (this.lineErrorMessages[(e as Error).message]) { + this.lineErrorMessages[(e as Error).message]++; } else { - this.lineErrorMessages[e.message] = 1; - uxLog(this, c.red(e.message)); + this.lineErrorMessages[(e as Error).message] = 1; + uxLog(this, c.red((e as Error).message)); } } } @@ -283,7 +289,9 @@ export default class Toml2Csv extends SfdxCommand { // Stop spinner clearInterval(this.spinnerInterval); - this.spinner.succeed(`File processing complete of ${this.stats.dataLinesNb} data lines (${this.stats.dataErrorLinesNb} in error)`); + this.spinnerCustom.succeed( + `File processing complete of ${this.stats.dataLinesNb} data lines (${this.stats.dataErrorLinesNb} in error)` + ); // Manage file copy to data workspace folders for (const sectionKey of Object.keys(this.transfoConfig.entities)) { @@ -294,41 +302,53 @@ export default class Toml2Csv extends SfdxCommand { } if (fs.existsSync(this.tomlSectionsFileWriters[sectionKey].path)) { await fs.copy(this.tomlSectionsFileWriters[sectionKey].path, sectionData.outputFile.copyFilePath); - uxLog(this, c.grey(`- copied ${this.tomlSectionsFileWriters[sectionKey].path} to ${sectionData.outputFile.copyFilePath}`)); + uxLog( + this, + c.grey( + `- copied ${this.tomlSectionsFileWriters[sectionKey].path} to ${sectionData.outputFile.copyFilePath}` + ) + ); } } } // Display full stats - uxLog(this, c.grey("Stats: \n" + JSON.stringify(this.stats, null, 2))); + uxLog(this, c.grey('Stats: \n' + JSON.stringify(this.stats, null, 2))); // Display errors summary if (Object.keys(this.lineErrorMessages).length > 0) { - uxLog(this, c.yellow("There have been parsing errors:")); + uxLog(this, c.yellow('There have been parsing errors:')); for (const errMsg of Object.keys(this.lineErrorMessages)) { - uxLog(this, c.yellow("- " + this.lineErrorMessages[errMsg] + " lines: " + errMsg)); + uxLog(this, c.yellow('- ' + this.lineErrorMessages[errMsg] + ' lines: ' + errMsg)); } - uxLog(this, ""); + uxLog(this, ''); } // Display human-readable stats for (const section of Object.keys(this.stats.sections)) { const sectionStats = this.stats.sections[section]; if (sectionStats.dataLinesNb > 0) { - uxLog(this, c.grey(`[${section}] kept ${sectionStats.dataSuccessLinesNb} entries on ${sectionStats.dataLinesNb}`)); + uxLog( + this, + c.grey(`[${section}] kept ${sectionStats.dataSuccessLinesNb} entries on ${sectionStats.dataLinesNb}`) + ); } } uxLog(this, c.grey(`[TOTAL] kept ${this.stats.dataSuccessLinesNb} entries on ${this.stats.dataLinesNb}`)); const message = `TOML file ${tomlFile} has been split into ${this.csvFiles.length} CSV files in directory ${this.outputDir}`; uxLog( this, - c.cyan(`TOML file ${c.green(tomlFile)} has been split into ${c.green(this.csvFiles.length)} CSV files in directory ${c.green(this.outputDir)}`), + c.cyan( + `TOML file ${c.green(tomlFile)} has been split into ${c.green( + this.csvFiles.length + )} CSV files in directory ${c.green(this.outputDir)}` + ) ); return { outputString: message, csvfiles: this.csvFiles, stats: this.stats }; } updateSpinner() { - this.spinner.text = + this.spinnerCustom.text = `Processing section ${this.currentSection} (total lines: ${this.stats.dataLinesNb},` + ` success: ${this.stats.dataSuccessLinesNb},` + ` errors: ${this.stats.dataErrorLinesNb}, filtered: ${this.stats.dataFilteredLinesNb})`; @@ -340,7 +360,7 @@ export default class Toml2Csv extends SfdxCommand { if (this.skipTransfo) { const outputFile = path.join(this.outputDir, `${section}.csv`); // Init writeStream - const fileWriteStream = fs.createWriteStream(path.resolve(outputFile), { encoding: "utf8" }); + const fileWriteStream = fs.createWriteStream(path.resolve(outputFile), { encoding: 'utf8' }); uxLog(this, c.cyan(`- Initialized output CSV file ${c.green(c.bold(outputFile))}`)); this.csvFiles.push(outputFile); return fileWriteStream; @@ -350,28 +370,29 @@ export default class Toml2Csv extends SfdxCommand { // Create SF Object output file name const outputFile = path.join( this.outputDir, - `${errMode ? "errors" + path.sep + "err__" : ""}${this.transfoConfig.entities[section].outputFile.salesforceObjectApiName}___${section}.csv`, + `${errMode ? 'errors' + path.sep + 'err__' : ''}${this.transfoConfig.entities[section].outputFile.salesforceObjectApiName + }___${section}.csv` ); // Init writeStream - const fileWriteStream = fs.createWriteStream(path.resolve(outputFile), { encoding: "utf8" }); + const fileWriteStream = fs.createWriteStream(path.resolve(outputFile), { encoding: 'utf8' }); // Create CSV Header let headerLine = (this.transfoConfig?.entities[section]?.outputFile?.cols || []) .map((colDescription: any) => colDescription.name) .join(this.outputFileSeparator); if (errMode) { - headerLine += this.outputFileSeparator + "Error"; + headerLine += this.outputFileSeparator + 'Error'; } // Initialize with header - fileWriteStream.write(headerLine + "\n"); - uxLog(this, c.cyan(`- Initialized ${errMode ? "errors" : "output"} CSV file ${c.green(c.bold(outputFile))}`)); + fileWriteStream.write(headerLine + '\n'); + uxLog(this, c.cyan(`- Initialized ${errMode ? 'errors' : 'output'} CSV file ${c.green(c.bold(outputFile))}`)); this.csvFiles.push(outputFile); return fileWriteStream; } else if (errMode === false) { // Section has not been described in config file !! uxLog(this, c.yellow(`Section ${section} as entity is not described with columns in ${this.transfoConfigFile}`)); - const outputFile = path.join(this.outputDir, "errors", `noconfig__${section}.csv`); + const outputFile = path.join(this.outputDir, 'errors', `noconfig__${section}.csv`); // Init writeStream - const fileWriteStream = fs.createWriteStream(path.resolve(outputFile), { encoding: "utf8" }); + const fileWriteStream = fs.createWriteStream(path.resolve(outputFile), { encoding: 'utf8' }); uxLog(this, c.cyan(`- Initialized default output CSV file ${c.green(c.bold(outputFile))}`)); this.csvFiles.push(outputFile); return fileWriteStream; @@ -385,15 +406,15 @@ export default class Toml2Csv extends SfdxCommand { const ableToWrite = streamWriter.write(`${lineSf}\n`); if (!ableToWrite) { await new Promise((resolve) => { - streamWriter.once("drain", resolve); + streamWriter.once('drain', resolve); }); } } } // Convert input CSV line into SF Bulk API expected CSV line - async convertLineToSfThenWrite(section: string, lineSplit: string[]) { - const linesSfArray = []; + async convertLineToSfThenWrite(section: string, lineSplit: string[], flags) { + const linesSfArray: any[] = []; // convert into input format const inputCols: any = {}; @@ -401,34 +422,38 @@ export default class Toml2Csv extends SfdxCommand { // Case when cols are defined line [ {"Name": 0, "FirstName: 1" ...}] for (let i = 0; i < this.transfoConfig.entities[section].inputFile.cols.length; i++) { const inputColKey = this.transfoConfig.entities[section].inputFile.cols[i]; - inputCols[inputColKey] = lineSplit[i] || ""; + inputCols[inputColKey] = lineSplit[i] || ''; } } else { // Case when cols are not defined: just use positions for (let i = 0; i < lineSplit.length; i++) { const humanInputColPos = i + 1; - inputCols[humanInputColPos] = lineSplit[i] || ""; + inputCols[humanInputColPos] = lineSplit[i] || ''; } } // convert into output format for (const colDefinition of this.transfoConfig.entities[section]?.outputFile?.cols || []) { // Col definition is the position or the name of a column in input file if (colDefinition.inputColKey || colDefinition.inputColKey === 0) { - if (inputCols[colDefinition.inputColKey] || inputCols[colDefinition.inputColKey] === "" || inputCols[colDefinition.inputColKey] === 0) { + if ( + inputCols[colDefinition.inputColKey] || + inputCols[colDefinition.inputColKey] === '' || + inputCols[colDefinition.inputColKey] === 0 + ) { let colVal: string = inputCols[colDefinition.inputColKey]; // Transform if necessary if (colDefinition.transfo) { colVal = this.manageTransformation(colDefinition.transfo, colVal, colDefinition); } // Manage missing required value - if (colDefinition?.required === true && colVal === "") { + if (colDefinition?.required === true && colVal === '') { this.triggerError( c.red( - `${c.bold(this.transfoConfig.entities[this.currentSection].outputFile.salesforceObjectApiName)}.${c.bold( - colDefinition.name, - )}: Missing required value`, + `${c.bold( + this.transfoConfig.entities[this.currentSection || ''].outputFile.salesforceObjectApiName + )}.${c.bold(colDefinition.name)}: Missing required value` ), - false, + false ); } // Manage truncate value @@ -438,7 +463,10 @@ export default class Toml2Csv extends SfdxCommand { // Add cell in line linesSfArray.push(colVal); // Add quotes if value contains output file separator } else { - this.triggerError(c.red(`You must have a correspondance in input cols for output col ${JSON.stringify(colDefinition)}`), false); + this.triggerError( + c.red(`You must have a correspondance in input cols for output col ${JSON.stringify(colDefinition)}`), + false + ); } } // Col definition is a hardcoded value @@ -453,14 +481,14 @@ export default class Toml2Csv extends SfdxCommand { } // Col definition is a composite concatenated value (Virtual unique key for SFDMU) else if (colDefinition.concatComposite) { - const concatFields = colDefinition.name.split("$").filter((fieldName) => fieldName !== ""); - colDefinition.separator = colDefinition.separator || ";"; + const concatFields = colDefinition.name.split('$').filter((fieldName) => fieldName !== ''); + colDefinition.separator = colDefinition.separator || ';'; const concatenatedValue = this.processConcat(concatFields, section, linesSfArray, colDefinition); linesSfArray.push(concatenatedValue); } // Get record type Id else if (colDefinition.recordType) { - const recordTypeId = await getRecordTypeId(colDefinition.recordType, this.org.getConnection()); + const recordTypeId = await getRecordTypeId(colDefinition.recordType, flags['target-org'].getConnection()); if (recordTypeId === null) { this.triggerError(`No RecordTypeId found for ${JSON.stringify(colDefinition.recordType)}`, true); } @@ -486,34 +514,35 @@ export default class Toml2Csv extends SfdxCommand { if (concatColName.hardcoded) { return concatColName.hardcoded; } - const colNamePosition = this.transfoConfig?.entities[section]?.outputFile?.colOutputPositions?.indexOf(concatColName); + const colNamePosition = + this.transfoConfig?.entities[section]?.outputFile?.colOutputPositions?.indexOf(concatColName); if (colNamePosition === null || colNamePosition < 0) { this.triggerError( `Concat error: Unable to find output field "${concatColName}" in ${JSON.stringify( - this.transfoConfig.entities[section].outputFile.colOutputPositions, + this.transfoConfig.entities[section].outputFile.colOutputPositions )}`, - false, + false ); } const colNameValue = linesSfArray[colNamePosition]; return colNameValue; }) - .join(colDefinition.separator || " "); + .join(colDefinition.separator || ' '); return concatenatedValues; } // Apply transformations defined in transfoconfig file manageTransformation(transfo: any, colVal: any, colDefinition: any) { // Date transfo - if (transfo.type === "date") { - if (colVal === "") { - return ""; + if (transfo.type === 'date') { + if (colVal === '') { + return ''; } if (transfo.addZero && colVal.length === 7) { - colVal = "0" + colVal; + colVal = '0' + colVal; } const formattedDate = moment(colVal, transfo.from, true).format(transfo.to); - if (formattedDate === "Invalid date") { + if (formattedDate === 'Invalid date') { this.triggerError(`Unable to reformat date ${colVal} for column ${JSON.stringify(colDefinition)}`, false); } return formattedDate; @@ -528,15 +557,17 @@ export default class Toml2Csv extends SfdxCommand { // Manage transco value getTranscoValue(transfo: any, colVal: string, colDefinition: any) { const enumValues = this.getTranscoValues(transfo); - const transcodedValue = enumValues[colVal] !== null ? enumValues[colVal] : transfo.default || ""; - if (transcodedValue === "" && colVal !== "") { + const transcodedValue = enumValues[colVal] !== null ? enumValues[colVal] : transfo.default || ''; + if (transcodedValue === '' && colVal !== '') { this.triggerError( c.red( - `${c.bold(this.transfoConfig.entities[this.currentSection].outputFile.salesforceObjectApiName)}.${c.bold( - colDefinition.name, - )}: Missing matching value for ${c.bold(colVal)} in ${c.grey(JSON.stringify(Object.keys(enumValues)))}`, + `${c.bold( + this.transfoConfig.entities[this.currentSection || ''].outputFile.salesforceObjectApiName + )}.${c.bold(colDefinition.name)}: Missing matching value for ${c.bold(colVal)} in ${c.grey( + JSON.stringify(Object.keys(enumValues)) + )}` ), - false, + false ); } return transcodedValue; @@ -551,11 +582,11 @@ export default class Toml2Csv extends SfdxCommand { return this.loadedTranscos[transfo.enum]; } // Load enum in memory - const transcoFile = path.join(this.rootConfigDirectory, "enums", `${transfo.enum}.json`); + const transcoFile = path.join(this.rootConfigDirectory, 'enums', `${transfo.enum}.json`); if (!fs.existsSync(transcoFile)) { this.triggerError(`Missing transco file ${c.bold(transcoFile)} for enum ${c.bold(transfo.enum)}`, false); } - this.loadedTranscos[transfo.enum] = JSON.parse(fs.readFileSync(transcoFile, "utf-8")); + this.loadedTranscos[transfo.enum] = JSON.parse(fs.readFileSync(transcoFile, 'utf-8')); return this.loadedTranscos[transfo.enum]; } this.triggerError(`Missing transco definition in ${c.bold(JSON.stringify(transfo))}`, false); @@ -565,15 +596,15 @@ export default class Toml2Csv extends SfdxCommand { let checkRes: boolean | null = false; try { checkRes = - filter.type === "date" + filter.type === 'date' ? this.checkFilterDate(filter, lineSplit) - : filter.type === "parentId" + : filter.type === 'parentId' ? this.checkFilterParentId(filter, lineSplit) - : filter.type === "colValue" + : filter.type === 'colValue' ? this.checkFilterColValue(filter, lineSplit) : null; if (checkRes === null) { - throw Error("Unknown filter type " + JSON.stringify(filter)); + throw Error('Unknown filter type ' + JSON.stringify(filter)); } // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (e) { @@ -587,11 +618,11 @@ export default class Toml2Csv extends SfdxCommand { const dateStart = moment(filter.date, filter.dateFormat, true); const colValue = moment(lineSplit[filter.colNumber - 1], filter.colDateFormat, true); const res = - filter.typeDtl === "higherThan" - ? colValue.isAfter(dateStart, "day") - : filter.typeDtl === "lowerThan" - ? colValue.isBefore(dateStart, "day") - : colValue.isSame(dateStart, "day"); + filter.typeDtl === 'higherThan' + ? colValue.isAfter(dateStart, 'day') + : filter.typeDtl === 'lowerThan' + ? colValue.isBefore(dateStart, 'day') + : colValue.isSame(dateStart, 'day'); return res; } @@ -643,9 +674,9 @@ export default class Toml2Csv extends SfdxCommand { triggerError(errorMsg: string, fatal = true) { if (fatal && this.spinner) { clearInterval(this.spinnerInterval); - this.spinner.fail(errorMsg); + this.spinnerCustom.fail(errorMsg); } - throw new SfdxError(errorMsg); + throw new SfError(errorMsg); } formatCsvCell(cellVal: string) { diff --git a/src/commands/hardis/org/configure/data.ts b/src/commands/hardis/org/configure/data.ts index d7c5f4a1b..0b3f80f9c 100644 --- a/src/commands/hardis/org/configure/data.ts +++ b/src/commands/hardis/org/configure/data.ts @@ -1,27 +1,23 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages, SfdxError } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as pascalcase from "pascalcase"; -import * as path from "path"; -import { uxLog } from "../../../../common/utils"; -import { dataFolderRoot } from "../../../../common/utils/dataUtils"; -import { prompts } from "../../../../common/utils/prompts"; -import { WebSocketClient } from "../../../../common/websocketClient"; -import { getConfig, setConfig } from "../../../../config"; -import { PACKAGE_ROOT_DIR } from "../../../../settings"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class ConfigureData extends SfdxCommand { - public static title = "Configure Data project"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages, SfError } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import pascalcase from 'pascalcase'; +import * as path from 'path'; +import { uxLog } from '../../../../common/utils/index.js'; +import { dataFolderRoot } from '../../../../common/utils/dataUtils.js'; +import { prompts } from '../../../../common/utils/prompts.js'; +import { WebSocketClient } from '../../../../common/websocketClient.js'; +import { getConfig, setConfig } from '../../../../config/index.js'; +import { PACKAGE_ROOT_DIR } from '../../../../settings.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class ConfigureData extends SfCommand { + public static title = 'Configure Data project'; public static description = `Configure Data Export/Import with a [SFDX Data Loader](https://help.sfdmu.com/) Project @@ -30,33 +26,27 @@ See article: [![How to detect bad words in Salesforce records using SFDX Data Loader and sfdx-hardis](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-badwords.jpg)](https://nicolas.vuillamy.fr/how-to-detect-bad-words-in-salesforce-records-using-sfdx-data-loader-and-sfdx-hardis-171db40a9bac) `; - public static examples = ["$ sfdx hardis:org:configure:data"]; + public static examples = ['$ sf hardis:org:configure:data']; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; // List required plugins, their presence will be tested before running the command - protected static requiresSfdxPlugins = ["sfdmu"]; + protected static requiresSfdxPlugins = ['sfdmu']; additionalFiles: any = []; dataPath: string; sfdmuConfig: any; @@ -67,7 +57,7 @@ See article: public async run(): Promise { const template = await this.selectTemplate(); - if (template === "blank") { + if (template === 'blank') { // Request info to build sfdmu workspace await this.buildExportJsonInfo(); } else { @@ -80,7 +70,9 @@ See article: await this.promptImportInScratchOrgs(sfdmuProjectFolder); // Set bac initial cwd - const message = c.cyan(`Successfully initialized sfdmu project ${c.green(sfdmuProjectFolder)}, with ${c.green("export.json")} file. + const message = c.cyan(`Successfully initialized sfdmu project ${c.green(sfdmuProjectFolder)}, with ${c.green( + 'export.json' + )} file. You can now configure it using SFDMU documentation: https://help.sfdmu.com/plugin-basics/basic-usage/minimal-configuration If you don't have unique field to identify an object, use composite external ids: https://help.sfdmu.com/full-documentation/advanced-features/composite-external-id-keys `); @@ -95,19 +87,19 @@ If you don't have unique field to identify an object, use composite external ids private async generateConfigurationFiles() { const sfdmuProjectFolder = path.join(dataFolderRoot, this.dataPath); if (fs.existsSync(sfdmuProjectFolder)) { - throw new SfdxError(`[sfdx-hardis]${c.red(`Folder ${c.bold(sfdmuProjectFolder)} already exists`)}`); + throw new SfError(`[sfdx-hardis]${c.red(`Folder ${c.bold(sfdmuProjectFolder)} already exists`)}`); } // Create folder & export.json await fs.ensureDir(sfdmuProjectFolder); - const exportJsonFile = path.join(sfdmuProjectFolder, "export.json"); + const exportJsonFile = path.join(sfdmuProjectFolder, 'export.json'); await fs.writeFile(exportJsonFile, JSON.stringify(this.sfdmuConfig, null, 2)); - uxLog(this, "Generated SFDMU config file " + exportJsonFile); + uxLog(this, 'Generated SFDMU config file ' + exportJsonFile); for (const additionalFile of this.additionalFiles) { const additionalFileFull = path.join(sfdmuProjectFolder, additionalFile.path); await fs.writeFile(additionalFileFull, additionalFile.text); - uxLog(this, c.cyan(additionalFile.message + ": ") + c.yellow(additionalFileFull)); + uxLog(this, c.cyan(additionalFile.message + ': ') + c.yellow(additionalFileFull)); WebSocketClient.requestOpenFile(additionalFileFull); } return { exportJsonFile, sfdmuProjectFolder }; @@ -128,28 +120,28 @@ If you don't have unique field to identify an object, use composite external ids objects: [ { query: "SELECT all FROM Account WHERE Name='sfdx-hardis'", - operation: "Upsert", - externalId: "Name", + operation: 'Upsert', + externalId: 'Name', }, ], }; // Manage badwords filter option - if (additionalConfig.includes("badwordsFilter")) { - const badwordsFileName = "badwords.json"; + if (additionalConfig.includes('badwordsFilter')) { + const badwordsFileName = 'badwords.json'; this.sfdmuConfig.objects[0] = [ { - query: "SELECT all FROM Lead", - operation: "Readonly", - targetRecordsFilter: "core:DetectBadwords", + query: 'SELECT all FROM Lead', + operation: 'Readonly', + targetRecordsFilter: 'core:DetectBadwords', filterRecordsAddons: [ { - module: "core:RecordsFilter", + module: 'core:RecordsFilter', args: { - filterType: "BadWords", + filterType: 'BadWords', settings: { badwordsFile: badwordsFileName, - detectFields: ["Description"], + detectFields: ['Description'], highlightWords: true, outputMatches: false, }, @@ -158,14 +150,14 @@ If you don't have unique field to identify an object, use composite external ids ], }, ]; - if (!fs.existsSync("badwords.json")) { + if (!fs.existsSync('badwords.json')) { const badwordsSample = { - badwords: ["write", "your", "bad", "words", "and expressions", "here"], + badwords: ['write', 'your', 'bad', 'words', 'and expressions', 'here'], }; this.additionalFiles.push({ path: badwordsFileName, text: JSON.stringify(badwordsSample, null, 2), - message: "Sample badwords file has been generated and needs to be updated", + message: 'Sample badwords file has been generated and needs to be updated', }); } } @@ -174,31 +166,33 @@ If you don't have unique field to identify an object, use composite external ids private async promptExportInfo() { return await prompts([ { - type: "text", - name: "dataPath", + type: 'text', + name: 'dataPath', message: c.cyanBright('Please input the SFDMU folder name (PascalCase format). Ex: "ProductsActive"'), }, { - type: "text", - name: "sfdxHardisLabel", + type: 'text', + name: 'sfdxHardisLabel', message: c.cyanBright('Please input the SFDMU config label. Ex: "Active Products"'), }, { - type: "text", - name: "sfdxHardisDescription", + type: 'text', + name: 'sfdxHardisDescription', message: c.cyanBright( - 'Please input the SFDMU config description. Ex: "Active products are used for scratch org initialization and in deployments"', + 'Please input the SFDMU config description. Ex: "Active products are used for scratch org initialization and in deployments"' ), }, { - type: "multiselect", - name: "additional", - message: c.cyanBright("Please select additional options if you need them. If not, just select nothing and continue"), + type: 'multiselect', + name: 'additional', + message: c.cyanBright( + 'Please select additional options if you need them. If not, just select nothing and continue' + ), choices: [ { - title: "Bad words detector", - description: "Can detect a list of bad words in records", - value: "badwordsFilter", + title: 'Bad words detector', + description: 'Can detect a list of bad words in records', + value: 'badwordsFilter', }, ], }, @@ -206,11 +200,11 @@ If you don't have unique field to identify an object, use composite external ids } private async selectTemplate() { - const templateChoices = []; - const templatesFolder = path.join(PACKAGE_ROOT_DIR, "defaults/templates/sfdmu"); + const templateChoices: any[] = []; + const templatesFolder = path.join(PACKAGE_ROOT_DIR, 'defaults/templates/sfdmu'); const templateFiles = fs.readdirSync(templatesFolder); for (const templateFile of templateFiles) { - const templateName = path.basename(templateFile).replace(".json", ""); + const templateName = path.basename(templateFile).replace('.json', ''); templateChoices.push({ title: `📝 ${templateName}`, value: path.join(templatesFolder, templateFile), @@ -218,38 +212,44 @@ If you don't have unique field to identify an object, use composite external ids }); } - const defaultTemplateChoice = { title: "📄 Blank template", value: "blank", description: "Configure your data import/export from scratch :)" }; + const defaultTemplateChoice = { + title: '📄 Blank template', + value: 'blank', + description: 'Configure your data import/export from scratch :)', + }; const templateResp = await prompts({ - type: "select", - name: "template", - message: c.cyanBright("Please select a SFDMU template, or the blank one"), + type: 'select', + name: 'template', + message: c.cyanBright('Please select a SFDMU template, or the blank one'), choices: [...[defaultTemplateChoice], ...templateChoices], }); return templateResp.template; } private async buildExportJsonInfoFromTemplate(templateFile) { - const templateName = path.basename(templateFile).replace(".json", ""); + const templateName = path.basename(templateFile).replace('.json', ''); this.dataPath = pascalcase(templateName); - this.sfdmuConfig = JSON.parse(fs.readFileSync(templateFile, "utf-8")); + this.sfdmuConfig = JSON.parse(fs.readFileSync(templateFile, 'utf-8')); } private async promptImportInScratchOrgs(sfdmuProjectFolder) { const importResp = await prompts({ - type: "confirm", - name: "importInScratchOrgs", - message: c.cyanBright("Do you want this SFDMU config to be used to import data when initializing a new scratch org ?"), + type: 'confirm', + name: 'importInScratchOrgs', + message: c.cyanBright( + 'Do you want this SFDMU config to be used to import data when initializing a new scratch org ?' + ), default: false, }); this.importInScratchOrgs = importResp.importInScratchOrgs === true; // Manage dataPackages if importInScratchOrgs is true if (this.importInScratchOrgs === true) { - const config = await getConfig("project"); + const config = await getConfig('project'); const dataPackages = config.dataPackages || []; - dataPackages.push({ dataPath: sfdmuProjectFolder.replace(/\\/g, "/"), importInScratchOrgs: true }); - await setConfig("project", { dataPackages: dataPackages }); + dataPackages.push({ dataPath: sfdmuProjectFolder.replace(/\\/g, '/'), importInScratchOrgs: true }); + await setConfig('project', { dataPackages: dataPackages }); } } } diff --git a/src/commands/hardis/org/configure/files.ts b/src/commands/hardis/org/configure/files.ts index 7cbd1f6ef..b1d70e3cf 100644 --- a/src/commands/hardis/org/configure/files.ts +++ b/src/commands/hardis/org/configure/files.ts @@ -1,27 +1,23 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages, SfdxError } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as pascalcase from "pascalcase"; -import * as path from "path"; -import { uxLog } from "../../../../common/utils"; -import { filesFolderRoot } from "../../../../common/utils/filesUtils"; -import { promptFilesExportConfiguration } from "../../../../common/utils/filesUtils"; -import { WebSocketClient } from "../../../../common/websocketClient"; -import { PACKAGE_ROOT_DIR } from "../../../../settings"; -import { prompts } from "../../../../common/utils/prompts"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class ConfigureData extends SfdxCommand { - public static title = "Configure File export project"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages, SfError } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import pascalcase from 'pascalcase'; +import * as path from 'path'; +import { uxLog } from '../../../../common/utils/index.js'; +import { filesFolderRoot } from '../../../../common/utils/filesUtils.js'; +import { promptFilesExportConfiguration } from '../../../../common/utils/filesUtils.js'; +import { WebSocketClient } from '../../../../common/websocketClient.js'; +import { PACKAGE_ROOT_DIR } from '../../../../settings.js'; +import { prompts } from '../../../../common/utils/prompts.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class ConfigureData extends SfCommand { + public static title = 'Configure File export project'; public static description = `Configure export of file attachments from a Salesforce org @@ -30,30 +26,24 @@ See article below [![How to mass download notes and attachments files from a Salesforce org](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-mass-download.jpg)](https://nicolas.vuillamy.fr/how-to-mass-download-notes-and-attachments-files-from-a-salesforce-org-83a028824afd) `; - public static examples = ["$ sfdx hardis:org:configure:files"]; + public static examples = ['$ sf hardis:org:configure:files']; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; exportConfig: any; filesExportPath: any; @@ -62,7 +52,7 @@ See article below public async run(): Promise { const template = await this.selectTemplate(); - if (template === "blank") { + if (template === 'blank') { // Request info to build sfdmu workspace await this.buildExportJsonInfo(); } else { @@ -76,8 +66,10 @@ See article below WebSocketClient.requestOpenFile(exportJsonFile); // Set bac initial cwd - const message = c.cyan(`Successfully initialized files export project ${c.green(filesProjectFolder)}, with ${c.green("export.json")} file. -You can now call it using ${c.white("sfdx hardis:org:files:export")} + const message = c.cyan(`Successfully initialized files export project ${c.green( + filesProjectFolder + )}, with ${c.green('export.json')} file. +You can now call it using ${c.white('sf hardis:org:files:export')} `); uxLog(this, message); return { outputString: message }; @@ -86,22 +78,22 @@ You can now call it using ${c.white("sfdx hardis:org:files:export")} private async createConfigFiles() { const filesProjectFolder = path.join(filesFolderRoot, this.filesExportPath); if (fs.existsSync(filesProjectFolder)) { - throw new SfdxError(`[sfdx-hardis]${c.red(`Folder ${c.bold(filesProjectFolder)} already exists`)}`); + throw new SfError(`[sfdx-hardis]${c.red(`Folder ${c.bold(filesProjectFolder)} already exists`)}`); } // Create folder & export.json await fs.ensureDir(filesProjectFolder); - const exportJsonFile = path.join(filesProjectFolder, "export.json"); + const exportJsonFile = path.join(filesProjectFolder, 'export.json'); await fs.writeFile(exportJsonFile, JSON.stringify(this.exportConfig, null, 2)); return { exportJsonFile, filesProjectFolder }; } private async selectTemplate() { - const templateFileChoices = []; - const templatesFilesFolder = path.join(PACKAGE_ROOT_DIR, "defaults/templates/files"); + const templateFileChoices: any[] = []; + const templatesFilesFolder = path.join(PACKAGE_ROOT_DIR, 'defaults/templates/files'); const templateFiles = fs.readdirSync(templatesFilesFolder); for (const templateFile of templateFiles) { - const templateName = path.basename(templateFile).replace(".json", ""); + const templateName = path.basename(templateFile).replace('.json', ''); templateFileChoices.push({ title: `📝 ${templateName}`, value: path.join(templatesFilesFolder, templateFile), @@ -109,12 +101,16 @@ You can now call it using ${c.white("sfdx hardis:org:files:export")} }); } - const defaultTemplateChoice = { title: "📄 Blank template", value: "blank", description: "Configure your files import/export from scratch :)" }; + const defaultTemplateChoice = { + title: '📄 Blank template', + value: 'blank', + description: 'Configure your files import/export from scratch :)', + }; const templateResp = await prompts({ - type: "select", - name: "template", - message: c.cyanBright("Please select a Files import/export template, or the blank one"), + type: 'select', + name: 'template', + message: c.cyanBright('Please select a Files import/export template, or the blank one'), choices: [...[defaultTemplateChoice], ...templateFileChoices], }); return templateResp.template; @@ -122,12 +118,12 @@ You can now call it using ${c.white("sfdx hardis:org:files:export")} private async buildExportJsonInfo() { const defaultConfig = { - sfdxHardisLabel: "", - sfdxHardisDescription: "", - soqlQuery: "SELECT Id,Name FROM Opportunity", - fileTypes: "all", - outputFolderNameField: "Name", - outputFileNameFormat: "title", + sfdxHardisLabel: '', + sfdxHardisDescription: '', + soqlQuery: 'SELECT Id,Name FROM Opportunity', + fileTypes: 'all', + outputFolderNameField: 'Name', + outputFileNameFormat: 'title', overwriteParentRecords: true, overwriteFiles: false, }; @@ -139,8 +135,8 @@ You can now call it using ${c.white("sfdx hardis:org:files:export")} } private async buildExportJsonInfoFromTemplate(templateFile) { - const templateName = path.basename(templateFile).replace(".json", ""); + const templateName = path.basename(templateFile).replace('.json', ''); this.filesExportPath = pascalcase(templateName); - this.exportConfig = JSON.parse(fs.readFileSync(templateFile, "utf-8")); + this.exportConfig = JSON.parse(fs.readFileSync(templateFile, 'utf-8')); } } diff --git a/src/commands/hardis/org/configure/monitoring.ts b/src/commands/hardis/org/configure/monitoring.ts index 18c2b9670..5d6959106 100644 --- a/src/commands/hardis/org/configure/monitoring.ts +++ b/src/commands/hardis/org/configure/monitoring.ts @@ -1,11 +1,11 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages, SfdxError } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; -import * as open from "open"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages, SfError } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; +import open from 'open'; import { ensureGitBranch, ensureGitRepository, @@ -15,114 +15,117 @@ import { getGitRepoName, gitAddCommitPush, uxLog, -} from "../../../../common/utils"; -import { prompts } from "../../../../common/utils/prompts"; -import { setInConfigFile } from "../../../../config"; -import { PACKAGE_ROOT_DIR } from "../../../../settings"; -import { promptOrg } from "../../../../common/utils/orgUtils"; -import { WebSocketClient } from "../../../../common/websocketClient"; +} from '../../../../common/utils/index.js'; +import { prompts } from '../../../../common/utils/prompts.js'; +import { CONSTANTS, setInConfigFile } from '../../../../config/index.js'; +import { PACKAGE_ROOT_DIR } from '../../../../settings.js'; +import { promptOrg } from '../../../../common/utils/orgUtils.js'; +import { WebSocketClient } from '../../../../common/websocketClient.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class OrgConfigureMonitoring extends SfCommand { + public static title = 'Configure org monitoring'; -export default class OrgConfigureMonitoring extends SfdxCommand { - public static title = "Configure org monitoring"; + public static description = 'Configure monitoring of an org'; - public static description = "Configure monitoring of an org"; + public static examples = ['$ sf hardis:org:configure:monitoring']; - public static examples = ["$ sfdx hardis:org:configure:monitoring"]; - - protected static flagsConfig = { - orginstanceurl: flags.string({ - description: "Org instance url (technical param, do not use manually)", + public static flags: any = { + orginstanceurl: Flags.string({ + description: 'Org instance url (technical param, do not use manually)', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; // Comment this out if your command does not require an org username - protected static supportsUsername = true; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; /* jscpd:ignore-end */ - protected static requiresDependencies = ["openssl"]; + protected static requiresDependencies = ['openssl']; public async run(): Promise { + const { flags } = await this.parse(OrgConfigureMonitoring); // Make sure that we are located in a git repository await ensureGitRepository(); // Check git repo name is valid (contains monitoring) - const repoName = await getGitRepoName(); - if (!repoName.includes("monitoring")) { + const repoName = (await getGitRepoName()) || ''; + if (!repoName.includes('monitoring')) { const confirmMix = await prompts({ - type: "select", - name: "value", + type: 'select', + name: 'value', choices: [ - { title: "Yes, I'm sure because I know what I'm doing, like Roman :)", value: "yes" }, - { title: 'Mmmmm no, let me create another repo with the word "monitoring" in its name !', value: "no" }, + { title: "Yes, I'm sure because I know what I'm doing, like Roman :)", value: 'yes' }, + { title: 'Mmmmm no, let me create another repo with the word "monitoring" in its name !', value: 'no' }, ], - message: c.cyanBright("It's safer to have monitoring in a separate repo. Are you sure you want to mix monitoring and deployment sources ?"), + message: c.cyanBright( + "It's safer to have monitoring in a separate repo. Are you sure you want to mix monitoring and deployment sources ?" + ), }); - if (confirmMix.value === "no") { - throw new SfdxError('Your git repository name must contain the expression "monitoring"'); + if (confirmMix.value === 'no') { + throw new SfError('Your git repository name must contain the expression "monitoring"'); } } - const preRequisitesUrl = "https://sfdx-hardis.cloudity.com/salesforce-monitoring-config-home/#instructions"; - uxLog(this, c.yellow("Monitoring pre-requisites documentation: " + c.bold(preRequisitesUrl))); + const preRequisitesUrl = `${CONSTANTS.DOC_URL_ROOT}/salesforce-monitoring-config-home/#instructions`; + uxLog(this, c.yellow('Monitoring pre-requisites documentation: ' + c.bold(preRequisitesUrl))); const confirmPreRequisites = await prompts({ - type: "select", - name: "value", + type: 'select', + name: 'value', choices: [ - { title: "Yes", value: "yes" }, - { title: "No, help me !", value: "no" }, + { title: 'Yes', value: 'yes' }, + { title: 'No, help me !', value: 'no' }, ], - message: c.cyanBright("Did you configure the sfdx-hardis monitoring pre-requisites on your Git server ?"), + message: c.cyanBright('Did you configure the sfdx-hardis monitoring pre-requisites on your Git server ?'), }); - if (confirmPreRequisites.value === "no") { - const msg = "Please follow the instructions to configure the sfdx-hardis monitoring pre-requisites on your Git server\n" + preRequisitesUrl; + if (confirmPreRequisites.value === 'no') { + const msg = + 'Please follow the instructions to configure the sfdx-hardis monitoring pre-requisites on your Git server\n' + + preRequisitesUrl; uxLog(this, c.yellow(msg)); await open(preRequisitesUrl, { wait: true }); return { outputString: msg }; } // Get current default org - const currentOrgId = this.org?.getOrgId() || ""; - if (this.flags.orginstanceurl && this.org?.getConnection()?.instanceUrl === this.flags.orginstanceurl) { - uxLog(this, c.cyan(`Default org ${this.org.getConnection()?.instanceUrl} is selected, let's configure its monitoring !`)); + const currentOrgId = flags['target-org']?.getOrgId() || ''; + if (flags.orginstanceurl && flags['target-org']?.getConnection()?.instanceUrl === flags.orginstanceurl) { + uxLog( + this, + c.cyan( + `Default org ${flags['target-org'].getConnection()?.instanceUrl + } is selected, let's configure its monitoring !` + ) + ); } else { // Select the org that must be monitored const org = await promptOrg(this, { devHub: false, setDefault: true, scratch: false, - promptMessage: "Please select or connect to the org that you want to monitor", + promptMessage: 'Please select or connect to the org that you want to monitor', }); // Restart command so the org is selected as default org (will help to select profiles) if (currentOrgId !== org.orgId) { - const infoMsg = "Default org changed. Please restart the same command if VsCode does not do that automatically for you :)"; + const infoMsg = + 'Default org changed. Please restart the same command if VsCode does not do that automatically for you :)'; uxLog(this, c.yellow(infoMsg)); - const currentCommand = "sfdx " + this.id + " " + this.argv.join(" ") + " --orginstanceurl " + org.instanceUrl; + const currentCommand = 'sf ' + this.id + ' ' + this.argv.join(' ') + ' --orginstanceurl ' + org.instanceUrl; WebSocketClient.sendMessage({ - event: "runSfdxHardisCommand", + event: 'runSfdxHardisCommand', sfdxHardisCommand: currentCommand, }); return { outputString: infoMsg }; @@ -131,77 +134,86 @@ export default class OrgConfigureMonitoring extends SfdxCommand { // Build monitoring branch name const branchName = - "monitoring_" + - this.org + 'monitoring_' + + flags['target-org'] ?.getConnection() - .instanceUrl.replace("https://", "") - .replace(".my.salesforce.com", "") - .replace(/\./gm, "_") - .replace(/--/gm, "__") - .replace(/-/gm, "_"); + .instanceUrl.replace('https://', '') + .replace('.my.salesforce.com', '') + .replace(/\./gm, '_') + .replace(/--/gm, '__') + .replace(/-/gm, '_'); // Checkout branch, or create it if not existing (stash before if necessary) - await execCommand("git add --all", this, { output: true, fail: false }); - await execCommand("git stash", this, { output: true, fail: false }); - await ensureGitBranch(branchName, { parent: "main" }); + await execCommand('git add --all', this, { output: true, fail: false }); + await execCommand('git stash', this, { output: true, fail: false }); + await ensureGitBranch(branchName, { parent: 'main' }); // Create sfdx project if not existing yet - if (!fs.existsSync("sfdx-project.json")) { - const createCommand = "sfdx force:project:create" + ` --projectname "sfdx-hardis-monitoring"`; - uxLog(this, c.cyan("Creating sfdx-project...")); + if (!fs.existsSync('sfdx-project.json')) { + const createCommand = 'sf project generate' + ` --name "sfdx-hardis-monitoring"`; + uxLog(this, c.cyan('Creating sfdx-project...')); await execCommand(createCommand, this, { output: true, fail: true, }); - uxLog(this, c.cyan("Moving sfdx-project to root...")); - await fs.copy("sfdx-hardis-monitoring", process.cwd(), { overwrite: true }); - await fs.remove("sfdx-hardis-monitoring"); + uxLog(this, c.cyan('Moving sfdx-project to root...')); + await fs.copy('sfdx-hardis-monitoring', process.cwd(), { overwrite: true }); + await fs.remove('sfdx-hardis-monitoring'); // Copying monitoring folder structure - uxLog(this, "Copying default monitoring files..."); - if (fs.existsSync("README.md") && fs.readFileSync("README.md", "utf8").toString().split("\n").length < 5) { + uxLog(this, 'Copying default monitoring files...'); + if (fs.existsSync('README.md') && fs.readFileSync('README.md', 'utf8').toString().split('\n').length < 5) { // Remove default README if necessary - await fs.remove("README.md"); + await fs.remove('README.md'); } - await fs.copy(path.join(PACKAGE_ROOT_DIR, "defaults/monitoring", "."), process.cwd(), { overwrite: true }); + await fs.copy(path.join(PACKAGE_ROOT_DIR, 'defaults/monitoring', '.'), process.cwd(), { overwrite: true }); } // Update config file await setInConfigFile( [], { - targetUsername: this.org.getUsername(), - instanceUrl: this.org.getConnection().instanceUrl, + targetUsername: flags['target-org'].getUsername(), + instanceUrl: flags['target-org'].getConnection().instanceUrl, }, - "./.sfdx-hardis.yml", + './.sfdx-hardis.yml' ); // Generate SSL certificate (requires openssl to be installed on computer) - await generateSSLCertificate(branchName, "./.ssh", this, this.org.getConnection(), {}); + await generateSSLCertificate(branchName, './.ssh', this, flags['target-org'].getConnection(), {}); // Confirm & push on server const confirmPush = await prompts({ - type: "confirm", - name: "value", + type: 'confirm', + name: 'value', initial: true, - message: c.cyanBright("(RECOMMENDED) Do you want sfdx-hardis to save your configuration on server ? (git stage, commit & push)"), + message: c.cyanBright( + '(RECOMMENDED) Do you want sfdx-hardis to save your configuration on server ? (git stage, commit & push)' + ), }); if (confirmPush.value === true) { await gitAddCommitPush({ - message: "[sfdx-hardis] Update monitoring configuration", + message: '[sfdx-hardis] Update monitoring configuration', }); - uxLog(this, c.green("Your configuration for org monitoring is now ready :)")); + uxLog(this, c.green('Your configuration for org monitoring is now ready :)')); } else { - uxLog(this, c.yellow("Please manually git add, commit and push to the remote repository :)")); + uxLog(this, c.yellow('Please manually git add, commit and push to the remote repository :)')); } const branch = await getCurrentGitBranch(); - uxLog(this, c.greenBright(`Now you must schedule monitoring to run the job automatically every night on branch ${c.bold(branch)}:)`)); - const scheduleMonitoringUrl = "https://sfdx-hardis.cloudity.com/salesforce-monitoring-config-home/#instructions"; - const msg = "Please follow the instructions to schedule sfdx-hardis monitoring on your Git server: " + c.bold(scheduleMonitoringUrl); + uxLog( + this, + c.greenBright( + `Now you must schedule monitoring to run the job automatically every night on branch ${c.bold(branch)}:)` + ) + ); + const scheduleMonitoringUrl = `${CONSTANTS.DOC_URL_ROOT}/salesforce-monitoring-config-home/#instructions`; + const msg = + 'Please follow the instructions to schedule sfdx-hardis monitoring on your Git server: ' + + c.bold(scheduleMonitoringUrl); uxLog(this, c.yellow(msg)); await open(scheduleMonitoringUrl, { wait: true }); // Return an object to be displayed with --json - return { outputString: "Configured branch for authentication" }; + return { outputString: 'Configured branch for authentication' }; } } diff --git a/src/commands/hardis/org/connect.ts b/src/commands/hardis/org/connect.ts index 0f9267791..534ac1600 100644 --- a/src/commands/hardis/org/connect.ts +++ b/src/commands/hardis/org/connect.ts @@ -1,57 +1,48 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import { execCommand, isCI } from "../../../common/utils"; -import { promptOrg } from "../../../common/utils/orgUtils"; -import { prompts } from "../../../common/utils/prompts"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { execCommand, isCI } from '../../../common/utils/index.js'; +import { promptOrg } from '../../../common/utils/orgUtils.js'; +import { prompts } from '../../../common/utils/prompts.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class OrgSelect extends SfdxCommand { - public static title = "Connect to an org"; +export default class OrgConnect extends SfCommand { + public static title = 'Connect to an org'; public static description = `Connect to an org without setting it as default username, then proposes to open the org in web browser `; - public static examples = ["$ sfdx hardis:org:connect"]; + public static examples = ['$ sf hardis:org:connect']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; protected debugMode = false; /* jscpd:ignore-end */ public async run(): Promise { - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(OrgConnect); + this.debugMode = flags.debug || false; // Prompt org to connect to const org = await promptOrg(this, { devHub: false, setDefault: false }); @@ -59,9 +50,9 @@ export default class OrgSelect extends SfdxCommand { // Prompt user if he/she wants to open org in Web Browser if (!isCI) { const openRes = await prompts({ - type: "confirm", - name: "value", - message: "Do you want to open this org in Web Browser ?", + type: 'confirm', + name: 'value', + message: 'Do you want to open this org in Web Browser ?', }); if (openRes.value === true) { const openCommand = `sf org open --target-org ${org.username}`; diff --git a/src/commands/hardis/org/create.ts b/src/commands/hardis/org/create.ts index fccda1763..cb58e8204 100644 --- a/src/commands/hardis/org/create.ts +++ b/src/commands/hardis/org/create.ts @@ -1,61 +1,56 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { assert } from "console"; -import * as fs from "fs-extra"; -import * as moment from "moment"; -import * as os from "os"; -import * as path from "path"; -import { clearCache } from "../../../common/cache"; -import { elapseEnd, elapseStart, execSfdxJson, getCurrentGitBranch, uxLog } from "../../../common/utils"; -import { initApexScripts, initOrgData, initPermissionSetAssignments, promptUserEmail } from "../../../common/utils/orgUtils"; -import { WebSocketClient } from "../../../common/websocketClient"; -import { getConfig } from "../../../config"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class SandboxCreate extends SfdxCommand { - public static title = "Create sandbox org"; - - public static description = "Create and initialize sandbox org"; - - public static examples = ["$ sfdx hardis:org:create"]; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { assert } from 'console'; +import fs from 'fs-extra'; +import moment from 'moment'; +import * as os from 'os'; +import * as path from 'path'; +import { clearCache } from '../../../common/cache/index.js'; +import { elapseEnd, elapseStart, execSfdxJson, getCurrentGitBranch, uxLog } from '../../../common/utils/index.js'; +import { + initApexScripts, + initOrgData, + initPermissionSetAssignments, + promptUserEmail, +} from '../../../common/utils/orgUtils.js'; +import { WebSocketClient } from '../../../common/websocketClient.js'; +import { getConfig } from '../../../config/index.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class SandboxCreate extends SfCommand { + public static title = 'Create sandbox org'; + + public static description = 'Create and initialize sandbox org'; + + public static examples = ['$ sf hardis:org:create']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; protected static supportsDevhubUsername = true; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; // List required plugins, their presence will be tested before running the command - protected static requiresSfdxPlugins = ["sfdmu"]; + protected static requiresSfdxPlugins = ['sfdmu']; /* jscpd:ignore-end */ @@ -75,7 +70,8 @@ export default class SandboxCreate extends SfdxCommand { protected sandboxOrgFromPool: any; public async run(): Promise { - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(SandboxCreate); + this.debugMode = flags.debug || false; elapseStart(`Create and initialize sandbox org`); await this.initConfig(); await this.createSandboxOrg(); @@ -83,10 +79,10 @@ export default class SandboxCreate extends SfdxCommand { await this.updateSandboxOrgUser(); await initPermissionSetAssignments(this.configInfo.initPermissionSets || [], this.sandboxOrgUsername); await initApexScripts(this.configInfo.sandboxOrgInitApexScripts || [], this.sandboxOrgUsername); - await initOrgData(path.join(".", "scripts", "data", "SandboxInit"), this.sandboxOrgUsername); + await initOrgData(path.join('.', 'scripts', 'data', 'SandboxInit'), this.sandboxOrgUsername); } catch (e) { elapseEnd(`Create and initialize sandbox org`); - uxLog(this, c.grey("Error: " + e.message + "\n" + e.stack)); + uxLog(this, c.grey('Error: ' + (e as Error).message + '\n' + (e as Error).stack)); throw e; } elapseEnd(`Create and initialize sandbox org`); @@ -98,15 +94,20 @@ export default class SandboxCreate extends SfdxCommand { sandboxOrgUsername: this.sandboxOrgUsername, sandboxOrgSfdxAuthUrl: this.sandboxOrgSfdxAuthUrl, authFileJson: this.authFileJson, - outputString: "Created and initialized sandbox org", + outputString: 'Created and initialized sandbox org', }; } // Initialize configuration from .sfdx-hardis.yml + .gitbranch.sfdx-hardis.yml + .username.sfdx-hardis.yml public async initConfig() { - this.configInfo = await getConfig("user"); - this.gitBranch = await getCurrentGitBranch({ formatted: true }); - const newSandboxName = os.userInfo().username + "-" + this.gitBranch.split("/").pop().slice(0, 15) + "_" + moment().format("YYYYMMDD_hhmm"); + this.configInfo = await getConfig('user'); + this.gitBranch = (await getCurrentGitBranch({ formatted: true })) || ''; + const newSandboxName = + os.userInfo().username + + '-' + + (this.gitBranch.split('/').pop() || '').slice(0, 15) + + '_' + + moment().format('YYYYMMDD_hhmm'); this.sandboxOrgAlias = process.env.SANDBOX_ORG_ALIAS || newSandboxName; this.projectName = process.env.PROJECT_NAME || this.configInfo.projectName; @@ -123,16 +124,16 @@ export default class SandboxCreate extends SfdxCommand { // Create a new sandbox org or reuse existing one public async createSandboxOrg() { // Build project-sandbox-def-branch-user.json - uxLog(this, c.cyan("Building custom project-sandbox-def.json...")); - if (fs.existsSync("./config/project-sandbox-def.json")) { - this.projectSandboxDef = JSON.parse(fs.readFileSync("./config/project-sandbox-def.json", "utf-8")); + uxLog(this, c.cyan('Building custom project-sandbox-def.json...')); + if (fs.existsSync('./config/project-sandbox-def.json')) { + this.projectSandboxDef = JSON.parse(fs.readFileSync('./config/project-sandbox-def.json', 'utf-8')); } else { - uxLog(this, c.yellow(`Default values used: you may define a file ${c.bold("config/project-sandbox-def.json")}`)); + uxLog(this, c.yellow(`Default values used: you may define a file ${c.bold('config/project-sandbox-def.json')}`)); this.projectSandboxDef = { - sandboxName: "", - description: "SFDX Hardis developer sandbox", - licenseType: "Developer", - sourceSandbox: "", + sandboxName: '', + description: 'SFDX Hardis developer sandbox', + licenseType: 'Developer', + sourceSandbox: '', }; } this.projectSandboxDef.sandboxName = os.userInfo().username.substring(0, 10); @@ -141,79 +142,96 @@ export default class SandboxCreate extends SfdxCommand { await fs.writeFile(projectSandboxDefLocal, JSON.stringify(this.projectSandboxDef, null, 2)); // Fix @salesforce/cli bug: remove shape.zip if found - const tmpShapeFolder = path.join(os.tmpdir(), "shape"); + const tmpShapeFolder = path.join(os.tmpdir(), 'shape'); if (fs.existsSync(tmpShapeFolder)) { await fs.remove(tmpShapeFolder); - uxLog(this, c.grey("Deleted " + tmpShapeFolder)); + uxLog(this, c.grey('Deleted ' + tmpShapeFolder)); } // Create new sandbox org - uxLog(this, c.cyan("Creating new sandbox org...")); - const waitTime = process.env.SANDBOX_ORG_WAIT || "60"; + uxLog(this, c.cyan('Creating new sandbox org...')); + const waitTime = process.env.SANDBOX_ORG_WAIT || '60'; const createCommand = - "sfdx force:org:create --setdefaultusername " + - "--type sandbox " + - `--definitionfile ${projectSandboxDefLocal} ` + - `--setalias ${this.sandboxOrgAlias} ` + + 'sf org create sandbox --set-default ' + + `--definition-file ${projectSandboxDefLocal} ` + + `--alias ${this.sandboxOrgAlias} ` + `--wait ${waitTime} ` + - `--targetusername ${this.devHubAlias} `; + `--target-org ${this.devHubAlias} `; const createResult = await execSfdxJson(createCommand, this, { fail: false, output: false, debug: this.debugMode, }); - await clearCache("force:org:list"); + await clearCache('sf org list'); assert(createResult.status === 0 && createResult.result, this.buildSandboxCreateErrorMessage(createResult)); this.sandboxOrgInfo = createResult.result; this.sandboxOrgUsername = this.sandboxOrgInfo.username; // Trigger a status refresh on VsCode WebSocket Client - WebSocketClient.sendMessage({ event: "refreshStatus" }); + WebSocketClient.sendMessage({ event: 'refreshStatus' }); // Open sandbox org for user if not in CI - await execSfdxJson("sf org open", this, { + await execSfdxJson('sf org open', this, { fail: true, output: false, debug: this.debugMode, }); - uxLog(this, c.cyan(`Created sandbox org ${c.green(this.sandboxOrgAlias)} with user ${c.green(this.sandboxOrgUsername)}`)); + uxLog( + this, + c.cyan(`Created sandbox org ${c.green(this.sandboxOrgAlias)} with user ${c.green(this.sandboxOrgUsername)}`) + ); } public buildSandboxCreateErrorMessage(createResult) { if (createResult.status === 0 && createResult.result) { - return c.green("Sandbox create OK"); - } else if (createResult.status === 1 && createResult.errorMessage.includes("Socket timeout occurred while listening for results")) { + return c.green('Sandbox create OK'); + } else if ( + createResult.status === 1 && + createResult.errorMessage.includes('Socket timeout occurred while listening for results') + ) { return c.red( `[sfdx-hardis] Error creating sandbox org. ${c.bold( - "This is probably a Salesforce error, try again manually or launch again CI job", - )}\n${JSON.stringify(createResult, null, 2)}`, + 'This is probably a Salesforce error, try again manually or launch again CI job' + )}\n${JSON.stringify(createResult, null, 2)}` ); } return c.red( - `[sfdx-hardis] Error creating sandbox org. Maybe try ${c.yellow(c.bold("sfdx hardis:sandbox:create --forcenew"))} ?\n${JSON.stringify( - createResult, - null, - 2, - )}`, + `[sfdx-hardis] Error creating sandbox org. Maybe try ${c.yellow( + c.bold('sf hardis:sandbox:create --forcenew') + )} ?\n${JSON.stringify(createResult, null, 2)}` ); } // Update sandbox org user public async updateSandboxOrgUser() { - const config = await getConfig("user"); + const config = await getConfig('user'); // Update sandbox org main user - uxLog(this, c.cyan("Update / fix sandbox org user " + this.sandboxOrgUsername)); - const userQueryCommand = `sfdx force:data:record:get -s User -w "Username=${this.sandboxOrgUsername}" -u ${this.sandboxOrgAlias}`; - const userQueryRes = await execSfdxJson(userQueryCommand, this, { fail: true, output: false, debug: this.debugMode }); + uxLog(this, c.cyan('Update / fix sandbox org user ' + this.sandboxOrgUsername)); + const userQueryCommand = `sf data get record --sobject User --where "Username=${this.sandboxOrgUsername}" --target-org ${this.sandboxOrgAlias}`; + const userQueryRes = await execSfdxJson(userQueryCommand, this, { + fail: true, + output: false, + debug: this.debugMode, + }); let updatedUserValues = `LastName='SFDX-HARDIS' FirstName='Sandbox Org'`; // Fix country value is State & Country picklist activated - if ((this.projectSandboxDef.features || []).includes("StateAndCountryPicklist") && userQueryRes.result.CountryCode == null) { - updatedUserValues += ` CountryCode='${config.defaultCountryCode || "FR"}' Country='${config.defaultCountry || "France"}'`; + /* jscpd:ignore-start */ + if ( + (this.projectSandboxDef.features || []).includes('StateAndCountryPicklist') && + userQueryRes.result.CountryCode == null + ) { + updatedUserValues += ` CountryCode='${config.defaultCountryCode || 'FR'}' Country='${ + config.defaultCountry || 'France' + }'`; } - if ((this.projectSandboxDef.features || []).includes("MarketingUser") && userQueryRes.result.UserPermissionsMarketingUser === false) { + if ( + (this.projectSandboxDef.features || []).includes('MarketingUser') && + userQueryRes.result.UserPermissionsMarketingUser === false + ) { // Make sure MarketingUser is checked on sandbox org user if it is supposed to be - updatedUserValues += " UserPermissionsMarketingUser=true"; + updatedUserValues += ' UserPermissionsMarketingUser=true'; } - const userUpdateCommand = `sfdx force:data:record:update -s User -i ${userQueryRes.result.Id} -v "${updatedUserValues}" -u ${this.sandboxOrgAlias}`; + const userUpdateCommand = `sf data update record --sobject User --record-id ${userQueryRes.result.Id} --values "${updatedUserValues}" --target-org ${this.sandboxOrgAlias}`; await execSfdxJson(userUpdateCommand, this, { fail: false, output: true, debug: this.debugMode }); + /* jscpd:ignore-end */ } } diff --git a/src/commands/hardis/org/data/delete.ts b/src/commands/hardis/org/data/delete.ts index 2ade0dd72..48c4f36aa 100644 --- a/src/commands/hardis/org/data/delete.ts +++ b/src/commands/hardis/org/data/delete.ts @@ -1,79 +1,79 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { isCI, uxLog } from "../../../../common/utils"; -import { deleteData, selectDataWorkspace } from "../../../../common/utils/dataUtils"; -import { promptOrgUsernameDefault } from "../../../../common/utils/orgUtils"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class DataExport extends SfdxCommand { - public static title = "Delete data"; - - public static description = messages.getMessage("orgDataDelete"); - - public static examples = ["$ sfdx hardis:org:data:delete"]; - - protected static flagsConfig = { - path: flags.string({ - char: "p", - description: "Path to the sfdmu workspace folder", +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { isCI, uxLog } from '../../../../common/utils/index.js'; +import { deleteData, selectDataWorkspace } from '../../../../common/utils/dataUtils.js'; +import { promptOrgUsernameDefault } from '../../../../common/utils/orgUtils.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class DataExport extends SfCommand { + public static title = 'Delete data'; + + public static description = `Delete records in multiple objects using SFDMU Workspace + +If you need to run this command in production, you need to: + +- define runnableInProduction in export.json +- define sfdmuCanModify: YOUR_INSTANCE_URL in config/branches/.sfdx-hardis.YOUR_BRANCH.yml +`; + + public static examples = ['$ sf hardis:org:data:delete']; + + public static flags: any = { + path: Flags.string({ + char: 'p', + description: 'Path to the sfdmu workspace folder', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; // List required plugins, their presence will be tested before running the command - protected static requiresSfdxPlugins = ["sfdmu"]; + protected static requiresSfdxPlugins = ['sfdmu']; /* jscpd:ignore-end */ public async run(): Promise { - let sfdmuPath = this.flags.path || null; + const { flags } = await this.parse(DataExport); + let sfdmuPath = flags.path || null; // Identify sfdmu workspace if not defined if (sfdmuPath == null) { - sfdmuPath = await selectDataWorkspace({ selectDataLabel: "Please select a data workspace to use for DELETION" }); + sfdmuPath = await selectDataWorkspace({ selectDataLabel: 'Please select a data workspace to use for DELETION' }); } // Select org that where records will be imported - let orgUsername = this.org.getUsername(); + let orgUsername = flags['target-org'].getUsername(); if (!isCI) { - orgUsername = await promptOrgUsernameDefault(this, orgUsername, { devHub: false, setDefault: false }); + orgUsername = await promptOrgUsernameDefault(this, orgUsername || '', { devHub: false, setDefault: false }); } // Export data from org - await deleteData(sfdmuPath, this, { + await deleteData(sfdmuPath || '', this, { targetUsername: orgUsername, }); // Output message - const message = `Successfully deleted data from org ${c.green(orgUsername)} using SFDMU project ${c.green(sfdmuPath)}`; + const message = `Successfully deleted data from org ${c.green(orgUsername)} using SFDMU project ${c.green( + sfdmuPath + )}`; uxLog(this, c.cyan(message)); return { outputString: message }; } diff --git a/src/commands/hardis/org/data/export.ts b/src/commands/hardis/org/data/export.ts index b68d9de6d..1ccc46ea7 100644 --- a/src/commands/hardis/org/data/export.ts +++ b/src/commands/hardis/org/data/export.ts @@ -1,21 +1,17 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { isCI, uxLog } from "../../../../common/utils"; -import { exportData, selectDataWorkspace } from "../../../../common/utils/dataUtils"; -import { promptOrgUsernameDefault } from "../../../../common/utils/orgUtils"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { isCI, uxLog } from '../../../../common/utils/index.js'; +import { exportData, selectDataWorkspace } from '../../../../common/utils/dataUtils.js'; +import { promptOrgUsernameDefault } from '../../../../common/utils/orgUtils.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class DataExport extends SfdxCommand { - public static title = "Export data"; +export default class DataExport extends SfCommand { + public static title = 'Export data'; public static description = `Export data from an org using a [SFDX Data Loader](https://help.sfdmu.com/) Project @@ -24,62 +20,60 @@ See article: [![How to detect bad words in Salesforce records using SFDX Data Loader and sfdx-hardis](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-badwords.jpg)](https://nicolas.vuillamy.fr/how-to-detect-bad-words-in-salesforce-records-using-sfdx-data-loader-and-sfdx-hardis-171db40a9bac) `; - public static examples = ["$ sfdx hardis:org:data:export"]; + public static examples = ['$ sf hardis:org:data:export']; - protected static flagsConfig = { - path: flags.string({ - char: "p", - description: "Path to the sfdmu workspace folder", + public static flags: any = { + path: Flags.string({ + char: 'p', + description: 'Path to the sfdmu workspace folder', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; // List required plugins, their presence will be tested before running the command - protected static requiresSfdxPlugins = ["sfdmu"]; + protected static requiresSfdxPlugins = ['sfdmu']; /* jscpd:ignore-end */ public async run(): Promise { - let sfdmuPath = this.flags.path || null; - //const debugMode = this.flags.debug || false; + const { flags } = await this.parse(DataExport); + let sfdmuPath = flags.path || null; + //const debugMode = flags.debug || false; // Identify sfdmu workspace if not defined if (sfdmuPath == null) { - sfdmuPath = await selectDataWorkspace({ selectDataLabel: "Please select a data workspace to EXPORT" }); + sfdmuPath = await selectDataWorkspace({ selectDataLabel: 'Please select a data workspace to EXPORT' }); } // Select org that will be used to export records - let orgUsername = this.org.getUsername(); + let orgUsername = flags['target-org'].getUsername(); if (!isCI) { - orgUsername = await promptOrgUsernameDefault(this, orgUsername, { devHub: false, setDefault: false }); + orgUsername = await promptOrgUsernameDefault(this, orgUsername || '', { devHub: false, setDefault: false }); } // Export data from org - await exportData(sfdmuPath, this, { + await exportData(sfdmuPath || '', this, { sourceUsername: orgUsername, }); // Output message - const message = `Successfully exported data from sfdmu project ${c.green(sfdmuPath)} from org ${c.green(orgUsername)}`; + const message = `Successfully exported data from sfdmu project ${c.green(sfdmuPath)} from org ${c.green( + orgUsername + )}`; uxLog(this, c.cyan(message)); return { outputString: message }; } diff --git a/src/commands/hardis/org/data/import.ts b/src/commands/hardis/org/data/import.ts index a3404321b..b087c6d63 100644 --- a/src/commands/hardis/org/data/import.ts +++ b/src/commands/hardis/org/data/import.ts @@ -1,21 +1,17 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { isCI, uxLog } from "../../../../common/utils"; -import { importData, selectDataWorkspace } from "../../../../common/utils/dataUtils"; -import { promptOrgUsernameDefault } from "../../../../common/utils/orgUtils"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { isCI, uxLog } from '../../../../common/utils/index.js'; +import { importData, selectDataWorkspace } from '../../../../common/utils/dataUtils.js'; +import { promptOrgUsernameDefault } from '../../../../common/utils/orgUtils.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class DataExport extends SfdxCommand { - public static title = "Import data"; +export default class DataImport extends SfCommand { + public static title = 'Import data'; public static description = `Import/Load data in an org using a [SFDX Data Loader](https://help.sfdmu.com/) Project @@ -24,61 +20,59 @@ See article: [![How to detect bad words in Salesforce records using SFDX Data Loader and sfdx-hardis](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-badwords.jpg)](https://nicolas.vuillamy.fr/how-to-detect-bad-words-in-salesforce-records-using-sfdx-data-loader-and-sfdx-hardis-171db40a9bac) `; - public static examples = ["$ sfdx hardis:org:data:import"]; + public static examples = ['$ sf hardis:org:data:import']; - protected static flagsConfig = { - path: flags.string({ - char: "p", - description: "Path to the sfdmu workspace folder", + public static flags: any = { + path: Flags.string({ + char: 'p', + description: 'Path to the sfdmu workspace folder', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; // List required plugins, their presence will be tested before running the command - protected static requiresSfdxPlugins = ["sfdmu"]; + protected static requiresSfdxPlugins = ['sfdmu']; /* jscpd:ignore-end */ public async run(): Promise { - let sfdmuPath = this.flags.path || null; + const { flags } = await this.parse(DataImport); + let sfdmuPath = flags.path || null; // Identify sfdmu workspace if not defined if (sfdmuPath == null) { - sfdmuPath = await selectDataWorkspace({ selectDataLabel: "Please select a data workspace to IMPORT" }); + sfdmuPath = await selectDataWorkspace({ selectDataLabel: 'Please select a data workspace to IMPORT' }); } // Select org that where records will be imported - let orgUsername = this.org.getUsername(); + let orgUsername = flags['target-org'].getUsername(); if (!isCI) { - orgUsername = await promptOrgUsernameDefault(this, orgUsername, { devHub: false, setDefault: false }); + orgUsername = await promptOrgUsernameDefault(this, orgUsername || '', { devHub: false, setDefault: false }); } // Export data from org - await importData(sfdmuPath, this, { + await importData(sfdmuPath || '', this, { targetUsername: orgUsername, }); // Output message - const message = `Successfully import data from sfdmu project ${c.green(sfdmuPath)} into org ${c.green(orgUsername)}`; + const message = `Successfully import data from sfdmu project ${c.green(sfdmuPath)} into org ${c.green( + orgUsername + )}`; uxLog(this, c.cyan(message)); return { outputString: message }; } diff --git a/src/commands/hardis/org/diagnose/audittrail.ts b/src/commands/hardis/org/diagnose/audittrail.ts index 7e1b308a5..a93a51995 100644 --- a/src/commands/hardis/org/diagnose/audittrail.ts +++ b/src/commands/hardis/org/diagnose/audittrail.ts @@ -1,25 +1,21 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { isCI, uxLog } from "../../../../common/utils"; -import { bulkQuery } from "../../../../common/utils/apiUtils"; -import { getConfig } from "../../../../config"; -import { NotifProvider, NotifSeverity } from "../../../../common/notifProvider"; -import { prompts } from "../../../../common/utils/prompts"; -import { generateCsvFile, generateReportPath } from "../../../../common/utils/filesUtils"; -import { getNotificationButtons, getOrgMarkdown, getSeverityIcon } from "../../../../common/utils/notifUtils"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class DiagnoseAuditTrail extends SfdxCommand { - public static title = "Diagnose content of Setup Audit Trail"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { isCI, uxLog } from '../../../../common/utils/index.js'; +import { bulkQuery } from '../../../../common/utils/apiUtils.js'; +import { CONSTANTS, getConfig } from '../../../../config/index.js'; +import { NotifProvider, NotifSeverity } from '../../../../common/notifProvider/index.js'; +import { prompts } from '../../../../common/utils/prompts.js'; +import { generateCsvFile, generateReportPath } from '../../../../common/utils/filesUtils.js'; +import { getNotificationButtons, getOrgMarkdown, getSeverityIcon } from '../../../../common/utils/notifUtils.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class DiagnoseAuditTrail extends SfCommand { + public static title = 'Diagnose content of Setup Audit Trail'; public static description = `Export Audit trail into a CSV file with selected criteria, and highlight suspect actions @@ -35,6 +31,8 @@ Regular setup actions performed in major orgs are filtered. - Custom App Licenses - addeduserpackagelicense - granteduserpackagelicense +- Customer Portal + - createdcustomersuccessuser - Currency - updateddatedexchrate - Data Management @@ -117,75 +115,71 @@ monitoringAllowedSectionsActions: "Some other section": ["actionType1","actionType2","actionType3"] // Will ignore only those 3 actions from section "Some other section". Other actions in the same section will be considered as suspect. \`\`\` -This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-suspect-audit-trail/) and can output Grafana, Slack and MsTeams Notifications. +This command is part of [sfdx-hardis Monitoring](${CONSTANTS.DOC_URL_ROOT}/salesforce-monitoring-suspect-audit-trail/) and can output Grafana, Slack and MsTeams Notifications. `; public static examples = [ - "$ sfdx hardis:org:diagnose:audittrail", - "$ sfdx hardis:org:diagnose:audittrail --excludeusers baptiste@titi.com", - "$ sfdx hardis:org:diagnose:audittrail --excludeusers baptiste@titi.com,bertrand@titi.com", - "$ sfdx hardis:org:diagnose:audittrail --lastndays 5", + '$ sf hardis:org:diagnose:audittrail', + '$ sf hardis:org:diagnose:audittrail --excludeusers baptiste@titi.com', + '$ sf hardis:org:diagnose:audittrail --excludeusers baptiste@titi.com,bertrand@titi.com', + '$ sf hardis:org:diagnose:audittrail --lastndays 5', ]; - protected static flagsConfig = { - excludeusers: flags.string({ - char: "e", - description: "Comma-separated list of usernames to exclude", + public static flags: any = { + excludeusers: Flags.string({ + char: 'e', + description: 'Comma-separated list of usernames to exclude', }), - lastndays: flags.number({ - char: "t", - description: "Number of days to extract from today (included)", + lastndays: Flags.integer({ + char: 't', + description: 'Number of days to extract from today (included)', }), - outputfile: flags.string({ - char: "o", - description: "Force the path and name of output report file. Must end with .csv", + outputfile: Flags.string({ + char: 'o', + description: 'Force the path and name of output report file. Must end with .csv', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; + public static requiresProject = false; - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; - - protected excludeUsers = []; - protected lastNdays: number; + protected excludeUsers: any[] = []; + protected lastNdays: number | undefined; protected allowedSectionsActions = {}; protected debugMode = false; - protected auditTrailRecords = []; + protected auditTrailRecords: any[] = []; protected outputFile; protected outputFilesRes: any = {}; /* jscpd:ignore-end */ public async run(): Promise { - this.debugMode = this.flags.debug || false; - this.excludeUsers = this.flags.excludeusers ? this.flags.excludeusers.split(",") : []; - this.lastNdays = this.flags.lastndays; - this.outputFile = this.flags.outputfile || null; - const config = await getConfig("branch"); + const { flags } = await this.parse(DiagnoseAuditTrail); + this.debugMode = flags.debug || false; + this.excludeUsers = flags.excludeusers ? flags.excludeusers.split(',') : []; + this.lastNdays = flags.lastndays; + this.outputFile = flags.outputfile || null; + const config = await getConfig('branch'); // If manual mode and lastndays not sent as parameter, prompt user if (!isCI && !this.lastNdays) { const lastNdaysResponse = await prompts({ - type: "select", - name: "lastndays", - message: "Please select the number of days in the past from today you want to detect suspiscious setup activities", + type: 'select', + name: 'lastndays', + message: + 'Please select the number of days in the past from today you want to detect suspiscious setup activities', choices: [ { title: `1`, value: 1 }, { title: `2`, value: 2 }, @@ -207,63 +201,64 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co } this.allowedSectionsActions = { - "": ["createScratchOrg", "changedsenderemail", "deleteScratchOrg", "loginasgrantedtopartnerbt"], - "Certificate and Key Management": ["insertCertificate"], - "Custom App Licenses": ["addeduserpackagelicense", "granteduserpackagelicense"], - Currency: ["updateddatedexchrate"], - "Data Management": ["queueMembership"], - "Email Administration": ["dkimRotationSuccessful", "dkimRotationPreparationSuccessful"], - Holidays: ["holiday_insert"], - "Inbox mobile and legacy desktop apps": ["enableSIQUserNonEAC"], - Groups: ["groupMembership"], - "Manage Territories": ["tm2_userAddedToTerritory", "tm2_userRemovedFromTerritory"], - "Manage Users": [ - "activateduser", - "createduser", - "changedcommunitynickname", - "changedemail", - "changedfederationid", - "changedinteractionuseroffon", - "changedinteractionuseronoff", - "changedmarketinguseroffon", - "changedmarketinguseronoff", - "changedManager", - "changedprofileforuser", - "changedprofileforusercusttostd", - "changedprofileforuserstdtocust", - "changedroleforusertonone", - "changedroleforuser", - "changedroleforuserfromnone", - "changedpassword", - "changedUserEmailVerifiedStatusUnverified", - "changedUserEmailVerifiedStatusVerified", - "changedUserPhoneNumber", - "changedUserPhoneVerifiedStatusUnverified", - "deactivateduser", - "deleteAuthenticatorPairing", - "deleteTwoFactorInfo2", - "deleteTwoFactorTempCode", - "frozeuser", - "insertAuthenticatorPairing", - "insertTwoFactorInfo2", - "insertTwoFactorTempCode", - "lightningloginenroll", - "PermSetAssign", - "PermSetGroupAssign", - "PermSetGroupUnassign", - "PermSetLicenseAssign", - "PermSetUnassign", - "PermSetLicenseUnassign", - "registeredUserPhoneNumber", - "resetpassword", - "suOrgAdminLogin", - "suOrgAdminLogout", - "unfrozeuser", - "useremailchangesent", + '': ['createScratchOrg', 'changedsenderemail', 'deleteScratchOrg', 'loginasgrantedtopartnerbt'], + 'Certificate and Key Management': ['insertCertificate'], + 'Custom App Licenses': ['addeduserpackagelicense', 'granteduserpackagelicense'], + 'Customer Portal': ['createdcustomersuccessuser'], + Currency: ['updateddatedexchrate'], + 'Data Management': ['queueMembership'], + 'Email Administration': ['dkimRotationSuccessful', 'dkimRotationPreparationSuccessful'], + Holidays: ['holiday_insert'], + 'Inbox mobile and legacy desktop apps': ['enableSIQUserNonEAC'], + Groups: ['groupMembership'], + 'Manage Territories': ['tm2_userAddedToTerritory', 'tm2_userRemovedFromTerritory'], + 'Manage Users': [ + 'activateduser', + 'createduser', + 'changedcommunitynickname', + 'changedemail', + 'changedfederationid', + 'changedinteractionuseroffon', + 'changedinteractionuseronoff', + 'changedmarketinguseroffon', + 'changedmarketinguseronoff', + 'changedManager', + 'changedprofileforuser', + 'changedprofileforusercusttostd', + 'changedprofileforuserstdtocust', + 'changedroleforusertonone', + 'changedroleforuser', + 'changedroleforuserfromnone', + 'changedpassword', + 'changedUserEmailVerifiedStatusUnverified', + 'changedUserEmailVerifiedStatusVerified', + 'changedUserPhoneNumber', + 'changedUserPhoneVerifiedStatusUnverified', + 'deactivateduser', + 'deleteAuthenticatorPairing', + 'deleteTwoFactorInfo2', + 'deleteTwoFactorTempCode', + 'frozeuser', + 'insertAuthenticatorPairing', + 'insertTwoFactorInfo2', + 'insertTwoFactorTempCode', + 'lightningloginenroll', + 'PermSetAssign', + 'PermSetGroupAssign', + 'PermSetGroupUnassign', + 'PermSetLicenseAssign', + 'PermSetUnassign', + 'PermSetLicenseUnassign', + 'registeredUserPhoneNumber', + 'resetpassword', + 'suOrgAdminLogin', + 'suOrgAdminLogout', + 'unfrozeuser', + 'useremailchangesent', ], - "Mobile Administration": ["assigneduserstomobileconfig"], - "Reporting Snapshots": ["createdReportJob", "deletedReportJob"], - Sandboxes: ["DeleteSandbox"], + 'Mobile Administration': ['assigneduserstomobileconfig'], + 'Reporting Snapshots': ['createdReportJob', 'deletedReportJob'], + Sandboxes: ['DeleteSandbox'], }; // Append custom sections & actions considered as not suspect @@ -271,7 +266,7 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co this.allowedSectionsActions = Object.assign(this.allowedSectionsActions, config.monitoringAllowedSectionsActions); } - const conn = this.org.getConnection(); + const conn = flags['target-org'].getConnection(); uxLog(this, c.cyan(`Extracting Setup Audit Trail and detect suspect actions in ${conn.instanceUrl} ...`)); // Manage exclude users list @@ -288,8 +283,13 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co whereConstraint += `AND CreatedBy.Username NOT IN ('${this.excludeUsers.join("','")}') `; } - uxLog(this, c.cyan(`Excluded users are ${this.excludeUsers.join(",") || "None"}`)); - uxLog(this, c.cyan(`Use argument --excludeusers or .sfdx-hardis.yml property monitoringExcludeUsernames to exclude more users`)); + uxLog(this, c.cyan(`Excluded users are ${this.excludeUsers.join(',') || 'None'}`)); + uxLog( + this, + c.cyan( + `Use argument --excludeusers or .sfdx-hardis.yml property monitoringExcludeUsernames to exclude more users` + ) + ); // Fetch SetupAuditTrail records const auditTrailQuery = @@ -297,17 +297,17 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co `FROM SetupAuditTrail ` + whereConstraint + `ORDER BY CreatedDate DESC`; - uxLog(this, c.grey("Query: " + c.italic(auditTrailQuery))); + uxLog(this, c.grey('Query: ' + c.italic(auditTrailQuery))); const queryRes = await bulkQuery(auditTrailQuery, conn); - const suspectRecords = []; - let suspectUsers = []; - const suspectActions = []; - const severityIconLog = getSeverityIcon("log"); - const severityIconWarning = getSeverityIcon("warning"); + const suspectRecords: any[] = []; + let suspectUsers: any[] = []; + const suspectActions: any[] = []; + const severityIconLog = getSeverityIcon('log'); + const severityIconWarning = getSeverityIcon('warning'); this.auditTrailRecords = queryRes.records.map((record) => { - const section = record?.Section || ""; + const section = record?.Section || ''; record.Suspect = false; - record.severity = "log"; + record.severity = 'log'; record.severityIcon = severityIconLog; // Unallowed actions if ( @@ -316,10 +316,10 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co ) { record.Suspect = true; record.SuspectReason = `Manual config in unallowed section ${section} with action ${record.Action}`; - record.severity = "warning"; + record.severity = 'warning'; record.severityIcon = severityIconWarning; suspectRecords.push(record); - suspectUsers.push(record["CreatedBy.Username"] + " - " + record["CreatedBy.Name"]); + suspectUsers.push(record['CreatedBy.Username'] + ' - ' + record['CreatedBy.Name']); suspectActions.push(`${section} - ${record.Action}`); return record; } @@ -327,11 +327,11 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co }); let statusCode = 0; - let msg = "No suspect Setup Audit Trail records has been found"; - const suspectActionsWithCount = []; + let msg = 'No suspect Setup Audit Trail records has been found'; + const suspectActionsWithCount: any[] = []; if (suspectRecords.length > 0) { statusCode = 1; - uxLog(this, c.yellow("Suspect records list")); + uxLog(this, c.yellow('Suspect records list')); uxLog(this, JSON.stringify(suspectRecords, null, 2)); msg = `${suspectRecords.length} suspect Setup Audit Trail records has been found`; uxLog(this, c.yellow(msg)); @@ -345,50 +345,50 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co suspectActionsWithCount.push(`${suspectAction} (${suspectActionsSummary[suspectAction]})`); } suspectActionsWithCount.sort(); - uxLog(this, ""); - uxLog(this, c.yellow("Related users:")); + uxLog(this, ''); + uxLog(this, c.yellow('Related users:')); for (const user of suspectUsers) { uxLog(this, c.yellow(`- ${user}`)); } - uxLog(this, ""); - uxLog(this, c.yellow("Related actions:")); + uxLog(this, ''); + uxLog(this, c.yellow('Related actions:')); for (const action of suspectActionsWithCount) { uxLog(this, c.yellow(`- ${action}`)); } - uxLog(this, ""); + uxLog(this, ''); } else { uxLog(this, c.green(msg)); } // Generate output CSV file - this.outputFile = await generateReportPath("audit-trail", this.outputFile); + this.outputFile = await generateReportPath('audit-trail', this.outputFile); this.outputFilesRes = await generateCsvFile(this.auditTrailRecords, this.outputFile); // Manage notifications - const orgMarkdown = await getOrgMarkdown(this.org?.getConnection()?.instanceUrl); + const orgMarkdown = await getOrgMarkdown(flags['target-org']?.getConnection()?.instanceUrl); const notifButtons = await getNotificationButtons(); - let notifSeverity: NotifSeverity = "log"; + let notifSeverity: NotifSeverity = 'log'; let notifText = `No suspect Setup Audit Trail records has been found in ${orgMarkdown}`; - let notifAttachments = []; + let notifAttachments: any[] = []; if (suspectRecords.length > 0) { - notifSeverity = "warning"; + notifSeverity = 'warning'; notifText = `${suspectRecords.length} suspect Setup Audit Trail records have been found in ${orgMarkdown}`; let notifDetailText = ``; - notifDetailText += "*Related users*:\n"; + notifDetailText += '*Related users*:\n'; for (const user of suspectUsers) { notifDetailText += `• ${user}\n`; } - notifDetailText += "\n"; - notifDetailText += "*Related actions*:\n"; + notifDetailText += '\n'; + notifDetailText += '*Related actions*:\n'; for (const action of suspectActionsWithCount) { notifDetailText += `• ${action}\n`; } notifAttachments = [{ text: notifDetailText }]; } - globalThis.jsForceConn = this?.org?.getConnection(); // Required for some notifications providers like Email + globalThis.jsForceConn = flags['target-org']?.getConnection(); // Required for some notifications providers like Email NotifProvider.postNotifications({ - type: "AUDIT_TRAIL", + type: 'AUDIT_TRAIL', text: notifText, attachments: notifAttachments, buttons: notifButtons, @@ -401,7 +401,7 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co }, }); - if ((this.argv || []).includes("audittrail")) { + if ((this.argv || []).includes('audittrail')) { process.exitCode = statusCode; } diff --git a/src/commands/hardis/org/diagnose/instanceupgrade.ts b/src/commands/hardis/org/diagnose/instanceupgrade.ts index 9097e75af..27fa3dc39 100644 --- a/src/commands/hardis/org/diagnose/instanceupgrade.ts +++ b/src/commands/hardis/org/diagnose/instanceupgrade.ts @@ -1,63 +1,54 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import axios from "axios"; -import * as moment from "moment"; -import * as c from "chalk"; -import { uxLog } from "../../../../common/utils"; -import { soqlQuery } from "../../../../common/utils/apiUtils"; -import { NotifProvider, NotifSeverity } from "../../../../common/notifProvider"; -import { getNotificationButtons, getOrgMarkdown } from "../../../../common/utils/notifUtils"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class DiagnoseInstanceUpgrade extends SfdxCommand { - public static title = "Get Instance Upgrade date"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import axios from 'axios'; +import moment from 'moment'; +import c from 'chalk'; +import { uxLog } from '../../../../common/utils/index.js'; +import { soqlQuery } from '../../../../common/utils/apiUtils.js'; +import { NotifProvider, NotifSeverity } from '../../../../common/notifProvider/index.js'; +import { getNotificationButtons, getOrgMarkdown } from '../../../../common/utils/notifUtils.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class DiagnoseInstanceUpgrade extends SfCommand { + public static title = 'Get Instance Upgrade date'; public static description = `Get the date when the org instance will be upgraded (to Spring, Summer or Winter) `; - public static examples = ["$ sfdx hardis:org:diagnose:instanceupgrade"]; + public static examples = ['$ sf hardis:org:diagnose:instanceupgrade']; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; protected debugMode = false; /* jscpd:ignore-end */ public async run(): Promise { - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(DiagnoseInstanceUpgrade); + this.debugMode = flags.debug || false; // Get instance name - const orgQuery = "SELECT FIELDS(all) FROM Organization LIMIT 1"; - const orgQueryRes = await soqlQuery(orgQuery, this.org.getConnection()); + const orgQuery = 'SELECT FIELDS(all) FROM Organization LIMIT 1'; + const orgQueryRes = await soqlQuery(orgQuery, flags['target-org'].getConnection()); const orgInfo = orgQueryRes.records[0]; const instanceName = orgInfo.InstanceName; @@ -68,7 +59,11 @@ export default class DiagnoseInstanceUpgrade extends SfdxCommand { const maintenances = instanceInfo.Maintenances || []; orgInfo.maintenanceNextUpgrade = {}; for (const maintenance of maintenances) { - if (maintenance.isCore && maintenance.releaseType === "Major" && maintenance.serviceKeys.includes("coreService")) { + if ( + maintenance.isCore && + maintenance.releaseType === 'Major' && + maintenance.serviceKeys.includes('coreService') + ) { orgInfo.maintenanceNextUpgrade = maintenance; break; } @@ -76,30 +71,30 @@ export default class DiagnoseInstanceUpgrade extends SfdxCommand { // Get number of days before next major upgrade const nextUpgradeDate = moment(orgInfo?.maintenanceNextUpgrade?.plannedStartTime); - const nextMajorUpgradeDateStr = nextUpgradeDate.format(); + const nextMajorUpgradeDateStr = nextUpgradeDate.format("ll"); const today = moment(); - const daysBeforeUpgrade = nextUpgradeDate.diff(today, "days"); + const daysBeforeUpgrade = today.diff(nextUpgradeDate, 'days'); // Manage notifications - const orgMarkdown = await getOrgMarkdown(this.org?.getConnection()?.instanceUrl); + const orgMarkdown = await getOrgMarkdown(flags['target-org']?.getConnection()?.instanceUrl); const notifButtons = await getNotificationButtons(); - let notifSeverity: NotifSeverity = "log"; - const notifText = `Salesforce instance ${instanceName} of ${orgMarkdown} will be upgraded on ${nextMajorUpgradeDateStr} (${daysBeforeUpgrade} days) to ${orgInfo?.maintenanceNextUpgrade?.name}`; + let notifSeverity: NotifSeverity = 'log'; + const notifText = `Salesforce instance *${instanceName}* of ${orgMarkdown} will be upgraded on ${nextMajorUpgradeDateStr} (*${daysBeforeUpgrade} days*) to ${orgInfo?.maintenanceNextUpgrade?.name}`; // Change severity according to number of days if (daysBeforeUpgrade <= 15) { - notifSeverity = "warning"; + notifSeverity = 'warning'; uxLog(this, c.yellow(notifText)); } else if (daysBeforeUpgrade <= 30) { - notifSeverity = "info"; + notifSeverity = 'info'; uxLog(this, c.green(notifText)); } else { uxLog(this, c.green(notifText)); } - globalThis.jsForceConn = this?.org?.getConnection(); // Required for some notifications providers like Email + globalThis.jsForceConn = flags['target-org']?.getConnection(); // Required for some notifications providers like Email NotifProvider.postNotifications({ - type: "ORG_INFO", + type: 'ORG_INFO', text: notifText, attachments: [], buttons: notifButtons, diff --git a/src/commands/hardis/org/diagnose/legacyapi.ts b/src/commands/hardis/org/diagnose/legacyapi.ts index e820d6fc5..44fb2a8c3 100644 --- a/src/commands/hardis/org/diagnose/legacyapi.ts +++ b/src/commands/hardis/org/diagnose/legacyapi.ts @@ -1,27 +1,24 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as sortArray from "sort-array"; -import { uxLog } from "../../../../common/utils"; -import * as dns from "dns"; -import { getNotificationButtons, getOrgMarkdown, getSeverityIcon } from "../../../../common/utils/notifUtils"; -import { soqlQuery } from "../../../../common/utils/apiUtils"; -import { WebSocketClient } from "../../../../common/websocketClient"; -import { NotifProvider, NotifSeverity } from "../../../../common/notifProvider"; -import { generateCsvFile, generateReportPath } from "../../../../common/utils/filesUtils"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import sortArray from 'sort-array'; +import { uxLog } from '../../../../common/utils/index.js'; +import * as dns from 'dns'; +import { getNotificationButtons, getOrgMarkdown, getSeverityIcon } from '../../../../common/utils/notifUtils.js'; +import { soqlQuery } from '../../../../common/utils/apiUtils.js'; +import { WebSocketClient } from '../../../../common/websocketClient.js'; +import { NotifProvider, NotifSeverity } from '../../../../common/notifProvider/index.js'; +import { generateCsvFile, generateReportPath } from '../../../../common/utils/filesUtils.js'; +import { CONSTANTS } from '../../../../config/index.js'; const dnsPromises = dns.promises; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class LegacyApi extends SfdxCommand { - public static title = "Check for legacy API use"; +export default class LegacyApi extends SfCommand { + public static title = 'Check for legacy API use'; public static description = `Checks if an org uses retired or someday retired API version\n @@ -29,154 +26,165 @@ See article below [![Handle Salesforce API versions Deprecation like a pro](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-deprecated-api.jpg)](https://nicolas.vuillamy.fr/handle-salesforce-api-versions-deprecation-like-a-pro-335065f52238) -This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-deprecated-api-calls/) and can output Grafana, Slack and MsTeams Notifications. +This command is part of [sfdx-hardis Monitoring](${CONSTANTS.DOC_URL_ROOT}/salesforce-monitoring-deprecated-api-calls/) and can output Grafana, Slack and MsTeams Notifications. `; public static examples = [ - "$ sfdx hardis:org:diagnose:legacyapi", - "$ sfdx hardis:org:diagnose:legacyapi -u hardis@myclient.com", - "$ sfdx hardis:org:diagnose:legacyapi --outputfile 'c:/path/to/folder/legacyapi.csv'", - "$ sfdx hardis:org:diagnose:legacyapi -u hardis@myclient.com --outputfile ./tmp/legacyapi.csv", + '$ sf hardis:org:diagnose:legacyapi', + '$ sf hardis:org:diagnose:legacyapi -u hardis@myclient.com', + "$ sf hardis:org:diagnose:legacyapi --outputfile 'c:/path/to/folder/legacyapi.csv'", + '$ sf hardis:org:diagnose:legacyapi -u hardis@myclient.com --outputfile ./tmp/legacyapi.csv', ]; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - eventtype: flags.string({ - char: "e", - default: "ApiTotalUsage", - description: "Type of EventLogFile event to analyze", + public static flags: any = { + eventtype: Flags.string({ + char: 'e', + default: 'ApiTotalUsage', + description: 'Type of EventLogFile event to analyze', }), - limit: flags.number({ - char: "l", + limit: Flags.integer({ + char: 'l', default: 999, - description: "Number of latest EventLogFile events to analyze", + description: 'Number of latest EventLogFile events to analyze', }), - outputfile: flags.string({ - char: "o", - description: "Force the path and name of output report file. Must end with .csv", + outputfile: Flags.string({ + char: 'o', + description: 'Force the path and name of output report file. Must end with .csv', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; protected debugMode = false; - protected apexSCannerCodeUrl = "https://raw.githubusercontent.com/pozil/legacy-api-scanner/main/legacy-api-scanner.apex"; + protected apexSCannerCodeUrl = + 'https://raw.githubusercontent.com/pozil/legacy-api-scanner/main/legacy-api-scanner.apex'; protected legacyApiDescriptors = [ { - apiFamily: ["SOAP", "REST", "BULK_API"], + apiFamily: ['SOAP', 'REST', 'BULK_API'], minApiVersion: 1.0, maxApiVersion: 6.0, - severity: "ERROR", - deprecationRelease: "Summer 21 - retirement of 1 to 6", - errors: [], + severity: 'ERROR', + deprecationRelease: 'Summer 21 - retirement of 1 to 6', + errors: [] as any[], }, { - apiFamily: ["SOAP", "REST", "BULK_API"], + apiFamily: ['SOAP', 'REST', 'BULK_API'], minApiVersion: 7.0, maxApiVersion: 20.0, - severity: "ERROR", - deprecationRelease: "Summer 22 - retirement of 7 to 20", - errors: [], + severity: 'ERROR', + deprecationRelease: 'Summer 22 - retirement of 7 to 20', + errors: [] as any[], }, { - apiFamily: ["SOAP", "REST", "BULK_API"], + apiFamily: ['SOAP', 'REST', 'BULK_API'], minApiVersion: 21.0, maxApiVersion: 30.0, - severity: "WARNING", - deprecationRelease: "Summer 25 - retirement of 21 to 30", - errors: [], + severity: 'WARNING', + deprecationRelease: 'Summer 25 - retirement of 21 to 30', + errors: [] as any[], }, ]; - protected allErrors = []; - protected ipResultsSorted = []; + protected allErrors: any[] = []; + protected ipResultsSorted: any[] = []; protected outputFile; protected outputFilesRes: any = {}; /* jscpd:ignore-end */ public async run(): Promise { - this.debugMode = this.flags.debug || false; - return await this.runJsForce(); + const { flags } = await this.parse(LegacyApi); + this.debugMode = flags.debug || false; + return await this.runJsForce(flags); } // Refactoring of Philippe Ozil's apex script with JsForce queries - private async runJsForce() { - const eventType = this.flags.eventtype || "ApiTotalUsage"; - const limit = this.flags.limit || 999; - this.outputFile = this.flags.outputfile || null; + private async runJsForce(flags) { + const eventType = flags.eventtype || 'ApiTotalUsage'; + const limit = flags.limit || 999; + this.outputFile = flags.outputfile || null; - const limitConstraint = limit ? ` LIMIT ${limit}` : ""; - const conn = this.org.getConnection(); + const limitConstraint = limit ? ` LIMIT ${limit}` : ''; + const conn = flags['target-org'].getConnection(); // Get EventLogFile records with EventType = 'ApiTotalUsage' const logCountQuery = `SELECT COUNT() FROM EventLogFile WHERE EventType = '${eventType}'`; const logCountRes = await soqlQuery(logCountQuery, conn); if (logCountRes.totalSize === 0) { uxLog(this, c.green(`Found no EventLogFile entry of type ${eventType}.`)); - uxLog(this, c.green("This indicates that no legacy APIs were called during the log retention window.")); + uxLog(this, c.green('This indicates that no legacy APIs were called during the log retention window.')); } else { - uxLog(this, c.grey("Found " + c.bold(logCountRes.totalSize) + ` ${eventType} EventLogFile entries.`)); + uxLog(this, c.grey('Found ' + c.bold(logCountRes.totalSize) + ` ${eventType} EventLogFile entries.`)); } if (logCountRes.totalSize > limit) { - uxLog(this, c.yellow(`There are more than ${limit} results, you may consider to increase limit using --limit argument`)); + uxLog( + this, + c.yellow(`There are more than ${limit} results, you may consider to increase limit using --limit argument`) + ); } // Fetch EventLogFiles with ApiTotalUsage entries - const logCollectQuery = `SELECT LogFile FROM EventLogFile WHERE EventType = '${eventType}' ORDER BY CreatedDate DESC` + limitConstraint; - uxLog(this, c.grey("Query: " + c.italic(logCollectQuery))); + const logCollectQuery = + `SELECT LogFile FROM EventLogFile WHERE EventType = '${eventType}' ORDER BY CreatedDate DESC` + limitConstraint; + uxLog(this, c.grey('Query: ' + c.italic(logCollectQuery))); const eventLogRes: any = await soqlQuery(logCollectQuery, conn); // Collect legacy api calls from logs - uxLog(this, c.grey("Calling org API to get CSV content of each EventLogFile record, then parse and analyze it...")); + uxLog(this, c.grey('Calling org API to get CSV content of each EventLogFile record, then parse and analyze it...')); for (const eventLogFile of eventLogRes.records) { await this.collectDeprecatedApiCalls(eventLogFile.LogFile, conn); } - this.allErrors = [...this.legacyApiDescriptors[0].errors, ...this.legacyApiDescriptors[1].errors, ...this.legacyApiDescriptors[2].errors]; + this.allErrors = [ + ...this.legacyApiDescriptors[0].errors, + ...this.legacyApiDescriptors[1].errors, + ...this.legacyApiDescriptors[2].errors, + ]; // Display summary - uxLog(this, ""); - uxLog(this, c.cyan("Results:")); + uxLog(this, ''); + uxLog(this, c.cyan('Results:')); for (const descriptor of this.legacyApiDescriptors) { const colorMethod = - descriptor.severity === "ERROR" && descriptor.errors.length > 0 + descriptor.severity === 'ERROR' && descriptor.errors.length > 0 ? c.red - : descriptor.severity === "WARNING" && descriptor.errors.length > 0 + : descriptor.severity === 'WARNING' && descriptor.errors.length > 0 ? c.yellow : c.green; uxLog(this, colorMethod(`- ${descriptor.deprecationRelease} : ${c.bold(descriptor.errors.length)}`)); } - uxLog(this, ""); + uxLog(this, ''); // Build command result - let msg = "No deprecated API call has been found in ApiTotalUsage logs"; + let msg = 'No deprecated API call has been found in ApiTotalUsage logs'; let statusCode = 0; - if (this.legacyApiDescriptors.filter((descriptor) => descriptor.severity === "ERROR" && descriptor.errors.length > 0).length > 0) { - msg = "Found legacy API versions calls in logs"; + if ( + this.legacyApiDescriptors.filter((descriptor) => descriptor.severity === 'ERROR' && descriptor.errors.length > 0) + .length > 0 + ) { + msg = 'Found legacy API versions calls in logs'; statusCode = 1; uxLog(this, c.red(c.bold(msg))); - } else if (this.legacyApiDescriptors.filter((descriptor) => descriptor.severity === "WARNING" && descriptor.errors.length > 0).length > 0) { - msg = "Found deprecated API versions calls in logs that will not be supported anymore in the future"; + } else if ( + this.legacyApiDescriptors.filter( + (descriptor) => descriptor.severity === 'WARNING' && descriptor.errors.length > 0 + ).length > 0 + ) { + msg = 'Found deprecated API versions calls in logs that will not be supported anymore in the future'; statusCode = 0; uxLog(this, c.yellow(c.bold(msg))); } else { @@ -184,11 +192,11 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co } // Generate main CSV file - this.outputFile = await generateReportPath("legacy-api-calls", this.outputFile); + this.outputFile = await generateReportPath('legacy-api-calls', this.outputFile); this.outputFilesRes = await generateCsvFile(this.allErrors, this.outputFile); // Generate one summary file by severity - const outputFileIps = []; + const outputFileIps: any[] = []; for (const descriptor of this.legacyApiDescriptors) { const errors = descriptor.errors; if (errors.length > 0) { @@ -206,7 +214,7 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co } } - let notifDetailText = ""; + let notifDetailText = ''; for (const descriptor of this.legacyApiDescriptors) { if (descriptor.errors.length > 0) { notifDetailText += `• ${descriptor.severity}: API version calls found in logs: ${descriptor.errors.length} (${descriptor.deprecationRelease})\n`; @@ -219,18 +227,18 @@ See article to solve issue before it's too late: • FR: https://leblog.hardis-group.com/portfolio/versions-dapi-salesforce-decommissionnees-que-faire/`; // Build notifications - const orgMarkdown = await getOrgMarkdown(this.org?.getConnection()?.instanceUrl); + const orgMarkdown = await getOrgMarkdown(flags['target-org']?.getConnection()?.instanceUrl); const notifButtons = await getNotificationButtons(); - let notifSeverity: NotifSeverity = "log"; + let notifSeverity: NotifSeverity = 'log'; let notifText = `No deprecated Salesforce API versions are used in ${orgMarkdown}`; if (this.allErrors.length > 0) { - notifSeverity = "error"; + notifSeverity = 'error'; notifText = `${this.allErrors.length} deprecated Salesforce API versions are used in ${orgMarkdown}`; } // Post notifications - globalThis.jsForceConn = this?.org?.getConnection(); // Required for some notifications providers like Email + globalThis.jsForceConn = flags['target-org']?.getConnection(); // Required for some notifications providers like Email NotifProvider.postNotifications({ - type: "LEGACY_API", + type: 'LEGACY_API', text: notifText, attachments: [{ text: notifDetailText }], buttons: notifButtons, @@ -246,7 +254,7 @@ See article to solve issue before it's too late: }, }); - if ((this.argv || []).includes("legacyapi")) { + if ((this.argv || []).includes('legacyapi')) { process.exitCode = statusCode; } @@ -265,11 +273,11 @@ See article to solve issue before it's too late: uxLog(this, c.grey(`- Request info for ${logFileUrl} ...`)); const logEntries = await conn.request(logFileUrl); uxLog(this, c.grey(`-- Processing ${logEntries.length} returned entries...`)); - const severityIconError = getSeverityIcon("error"); - const severityIconWarning = getSeverityIcon("warning"); - const severityIconInfo = getSeverityIcon("info"); + const severityIconError = getSeverityIcon('error'); + const severityIconWarning = getSeverityIcon('warning'); + const severityIconInfo = getSeverityIcon('info'); for (const logEntry of logEntries) { - const apiVersion = logEntry.API_VERSION ? parseFloat(logEntry.API_VERSION) : parseFloat("999.0"); + const apiVersion = logEntry.API_VERSION ? parseFloat(logEntry.API_VERSION) : parseFloat('999.0'); // const apiType = logEntry.API_TYPE || null ; const apiFamily = logEntry.API_FAMILY || null; @@ -281,15 +289,15 @@ See article to solve issue before it's too late: ) { logEntry.SFDX_HARDIS_DEPRECATION_RELEASE = legacyApiDescriptor.deprecationRelease; logEntry.SFDX_HARDIS_SEVERITY = legacyApiDescriptor.severity; - if (legacyApiDescriptor.severity === "ERROR") { - logEntry.severity = "error"; + if (legacyApiDescriptor.severity === 'ERROR') { + logEntry.severity = 'error'; logEntry.severityIcon = severityIconError; - } else if (legacyApiDescriptor.severity === "WARNING") { - logEntry.severity = "warning"; + } else if (legacyApiDescriptor.severity === 'WARNING') { + logEntry.severity = 'warning'; logEntry.severityIcon = severityIconWarning; } else { // severity === 'INFO' - logEntry.severity = "info"; + logEntry.severity = 'info'; logEntry.severityIcon = severityIconInfo; } legacyApiDescriptor.errors.push(logEntry); @@ -310,7 +318,7 @@ See article to solve issue before it's too late: } } // Try to get hostname for ips - const ipResults = []; + const ipResults: any[] = []; for (const ip of Object.keys(ipList)) { const ipInfo = ipList[ip]; let hostname; @@ -318,19 +326,19 @@ See article to solve issue before it's too late: hostname = await dnsPromises.reverse(ip); // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (e) { - hostname = "unknown"; + hostname = 'unknown'; } const ipResult = { CLIENT_IP: ip, CLIENT_HOSTNAME: hostname, SFDX_HARDIS_COUNT: ipInfo.count }; ipResults.push(ipResult); } this.ipResultsSorted = sortArray(ipResults, { - by: ["SFDX_HARDIS_COUNT"], - order: ["desc"], + by: ['SFDX_HARDIS_COUNT'], + order: ['desc'], }); // Write output CSV with client api info - const outputFileIps = this.outputFile.endsWith(".csv") - ? this.outputFile.replace(".csv", ".api-clients-" + severity + ".csv") - : this.outputFile + "api-clients-" + severity + ".csv"; + const outputFileIps = this.outputFile.endsWith('.csv') + ? this.outputFile.replace('.csv', '.api-clients-' + severity + '.csv') + : this.outputFile + 'api-clients-' + severity + '.csv'; const outputFileIpsRes = await generateCsvFile(this.ipResultsSorted, outputFileIps); if (outputFileIpsRes.xlsxFile) { this.outputFilesRes.xlsxFile2 = outputFileIpsRes.xlsxFile; diff --git a/src/commands/hardis/org/diagnose/licenses.ts b/src/commands/hardis/org/diagnose/licenses.ts index b7c181df7..c041995da 100644 --- a/src/commands/hardis/org/diagnose/licenses.ts +++ b/src/commands/hardis/org/diagnose/licenses.ts @@ -1,57 +1,50 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { uxLog } from "../../../../common/utils"; -import { soqlQuery } from "../../../../common/utils/apiUtils"; -import { generateCsvFile, generateReportPath } from "../../../../common/utils/filesUtils"; -import { NotifProvider } from "../../../../common/notifProvider"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { uxLog } from '../../../../common/utils/index.js'; +import { soqlQuery } from '../../../../common/utils/apiUtils.js'; +import { generateCsvFile, generateReportPath } from '../../../../common/utils/filesUtils.js'; +import { NotifProvider } from '../../../../common/notifProvider/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class DiagnoseUnusedUsers extends SfdxCommand { - public static title = "List licenses subscribed and used in a Salesforce org"; +export default class DiagnoseUnusedUsers extends SfCommand { + public static title = 'List licenses subscribed and used in a Salesforce org'; public static description = `Mostly used for monitoring (Grafana) but you can also use it manually :)`; - public static examples = ["$ sfdx hardis:org:diagnose:licenses"]; + public static examples = ['$ sf hardis:org:diagnose:licenses']; //Comment default values to test the prompts - protected static flagsConfig = { - outputfile: flags.string({ - char: "o", - description: "Force the path and name of output report file. Must end with .csv", + public static flags: any = { + outputfile: Flags.string({ + char: 'o', + description: 'Force the path and name of output report file. Must end with .csv', }), - usedonly: flags.boolean({ - char: "u", + usedonly: Flags.boolean({ + char: 'u', default: false, - description: "Filter to have only used licenses", + description: 'Filter to have only used licenses', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; protected usedOnly = false; protected debugMode = false; @@ -63,16 +56,17 @@ export default class DiagnoseUnusedUsers extends SfdxCommand { /* jscpd:ignore-end */ public async run(): Promise { - this.usedOnly = this.flags.usedonly || false; - this.debugMode = this.flags.debug || false; - this.outputFile = this.flags.outputfile || null; + const { flags } = await this.parse(DiagnoseUnusedUsers); + this.usedOnly = flags.usedonly || false; + this.debugMode = flags.debug || false; + this.outputFile = flags.outputfile || null; // Retrieve the list of users who haven't logged in for a while - const conn = this.org.getConnection(); - uxLog(this, c.cyan(`Extracting Licenses from ${conn.instanceUrl} ...` + this.usedOnly ? "(used only)" : "")); + const conn = flags['target-org'].getConnection(); + uxLog(this, c.cyan(`Extracting Licenses from ${conn.instanceUrl} ...` + this.usedOnly ? '(used only)' : '')); const licensesByKey = {}; - const usedLicenses = []; + const usedLicenses: any[] = []; // Query User Licenses const userLicenseQuery = @@ -85,7 +79,7 @@ export default class DiagnoseUnusedUsers extends SfdxCommand { const userLicenseInfo = Object.assign({}, userLicense); delete userLicenseInfo.Id; delete userLicenseInfo.attributes; - userLicenseInfo.type = "UserLicense"; + userLicenseInfo.type = 'UserLicense'; licensesByKey[userLicenseInfo.MasterLabel] = userLicenseInfo.TotalLicenses; if (userLicenseInfo.UsedLicenses > 0) { usedLicenses.push(userLicenseInfo.MasterLabel); @@ -110,7 +104,7 @@ export default class DiagnoseUnusedUsers extends SfdxCommand { delete pslInfo.Id; delete pslInfo.attributes; delete pslInfo.PermissionSetLicenseKey; - pslInfo.type = "PermissionSetLicense"; + pslInfo.type = 'PermissionSetLicense'; licensesByKey[pslInfo.MasterLabel] = pslInfo.TotalLicenses; if (pslInfo.UsedLicenses > 0) { usedLicenses.push(pslInfo.MasterLabel); @@ -121,17 +115,17 @@ export default class DiagnoseUnusedUsers extends SfdxCommand { usedLicenses.sort(); console.table(this.licenses); - uxLog(this, c.cyan("Used licenses: " + usedLicenses.join(", "))); + uxLog(this, c.cyan('Used licenses: ' + usedLicenses.join(', '))); // Generate output CSV file - this.outputFile = await generateReportPath("licenses", this.outputFile); + this.outputFile = await generateReportPath('licenses', this.outputFile); this.outputFilesRes = await generateCsvFile(this.licenses, this.outputFile); - globalThis.jsForceConn = this?.org?.getConnection(); // Required for some notifications providers like Email + globalThis.jsForceConn = flags['target-org']?.getConnection(); // Required for some notifications providers like Email NotifProvider.postNotifications({ - type: "LICENSES", - text: "", - severity: "log", + type: 'LICENSES', + text: '', + severity: 'log', attachedFiles: this.outputFilesRes.xlsxFile ? [this.outputFilesRes.xlsxFile] : [], logElements: this.licenses, data: { diff --git a/src/commands/hardis/org/diagnose/releaseupdates.ts b/src/commands/hardis/org/diagnose/releaseupdates.ts new file mode 100644 index 000000000..7f948e350 --- /dev/null +++ b/src/commands/hardis/org/diagnose/releaseupdates.ts @@ -0,0 +1,140 @@ +/* jscpd:ignore-start */ +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { uxLog } from '../../../../common/utils/index.js'; +import { soqlQueryTooling } from '../../../../common/utils/apiUtils.js'; +import { NotifProvider, NotifSeverity } from '../../../../common/notifProvider/index.js'; +import { generateCsvFile, generateReportPath } from '../../../../common/utils/filesUtils.js'; +import { getNotificationButtons, getOrgMarkdown, getSeverityIcon } from '../../../../common/utils/notifUtils.js'; +import moment from 'moment'; +import columnify from 'columnify'; +import { CONSTANTS } from '../../../../config/index.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class DiagnoseReleaseUpdates extends SfCommand { + public static title = 'Check Release Updates of an org'; + + public static description = `Export Release Updates into a CSV file with selected criteria, and highlight Release Updates that should be checked. + +Before publishing **Breaking Changes** ❌, Salesforce announce them in the setup menu [**Release Updates**](https://help.salesforce.com/s/articleView?id=sf.release_updates.htm&type=5) + +⚠️ Some of them are very important, because if you don't make the related upgrades in time (ex: before Winter 25) , your production org can crash ! + +This command is part of [sfdx-hardis Monitoring](${CONSTANTS.DOC_URL_ROOT}/salesforce-monitoring-release-updates/) and can output Grafana, Slack and MsTeams Notifications. +`; + + public static examples = [ + '$ sf hardis:org:diagnose:releaseupdates', + ]; + + public static flags: any = { + outputfile: Flags.string({ + char: 'o', + description: 'Force the path and name of output report file. Must end with .csv', + }), + debug: Flags.boolean({ + char: 'd', + default: false, + description: messages.getMessage('debugMode'), + }), + websocket: Flags.string({ + description: messages.getMessage('websocket'), + }), + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', + }), + 'target-org': requiredOrgFlagWithDeprecations, + }; + + public static requiresProject = false; + + protected debugMode = false; + + protected releaseUpdatesRecords: any[] = []; + protected outputFile; + protected outputFilesRes: any = {}; + + /* jscpd:ignore-end */ + + public async run(): Promise { + const { flags } = await this.parse(DiagnoseReleaseUpdates); + this.debugMode = flags.debug || false; + this.outputFile = flags.outputfile || null; + const conn = flags['target-org'].getConnection(); + uxLog(this, c.cyan(`Extracting Release Updates and checks to perform in ${conn.instanceUrl} ...`)); + + // Fetch ReleaseUpdate records + const releaseUpdatesQuery = + `SELECT StepStage,Status,Category,Title,DueDate,Description,Release,ReleaseLabel,ReleaseDate,ApiVersion,DurableId,HasNewSteps,IsReleased,SupportsRevoke,DeveloperName ` + + `FROM ReleaseUpdate ` + + `WHERE StepStage IN ('Upcoming','OverDue') AND Status IN ('Invocable','Revocable','Nascent','Invoked','Info') AND DueDate >= LAST_N_DAYS:60 ` + + `ORDER BY DueDate ASC`; + const queryRes = await soqlQueryTooling(releaseUpdatesQuery, conn); + const severityIconWarning = getSeverityIcon('warning'); + const severityIconError = getSeverityIcon('error'); + this.releaseUpdatesRecords = queryRes.records.map((record) => { + delete record.attributes + record.severityIcon = record.StepStage === 'OverDue' ? severityIconError : severityIconWarning; + return record; + }); + + // Process result + if (this.releaseUpdatesRecords.length > 0) { + // Generate output CSV file + this.outputFile = await generateReportPath('release-updates', this.outputFile); + this.outputFilesRes = await generateCsvFile(this.releaseUpdatesRecords, this.outputFile); + + // Build notification + const orgMarkdown = await getOrgMarkdown(flags['target-org']?.getConnection()?.instanceUrl); + const notifButtons = await getNotificationButtons(); + const notifSeverity: NotifSeverity = 'warning'; + const notifText = `${this.releaseUpdatesRecords.length} Release Updates to check have been found in ${orgMarkdown}` + let notifDetailText = ''; + for (const releaseUpdate of this.releaseUpdatesRecords) { + notifDetailText += `• *${releaseUpdate.Title}* (${releaseUpdate.StepStage},${releaseUpdate.Status},${releaseUpdate.Category}), due for ${moment(releaseUpdate.DueDate).format("ll")}\n`; + } + const notifAttachments = [{ text: notifDetailText }]; + // Post notif + globalThis.jsForceConn = flags['target-org']?.getConnection(); // Required for some notifications providers like Email + NotifProvider.postNotifications({ + type: 'RELEASE_UPDATES', + text: notifText, + attachments: notifAttachments, + buttons: notifButtons, + severity: notifSeverity, + attachedFiles: this.outputFilesRes.xlsxFile ? [this.outputFilesRes.xlsxFile] : [], + logElements: this.releaseUpdatesRecords, + data: { metric: this.releaseUpdatesRecords.length }, + metrics: { + ReleaseUpdates: this.releaseUpdatesRecords.length, + }, + }); + + // Display output + const releaseUpdatesLight = this.releaseUpdatesRecords.map(releaseUpdate => { + return { + Title: releaseUpdate.Title, + StepStage: releaseUpdate.StepStage, + Status: releaseUpdate.Status, + Category: releaseUpdate.Category, + DueDate: moment(releaseUpdate.DueDate).format('ll') + } + }) + uxLog(this, c.yellow(notifText + "\n" + columnify(releaseUpdatesLight))); + } + else { + uxLog(this, c.green("No release updates has been found")); + } + + // Return an object to be displayed with --json + return { + status: this.releaseUpdatesRecords.length > 0 ? 1 : 0, + suspectRecords: this.releaseUpdatesRecords, + csvLogFile: this.outputFile, + }; + } +} diff --git a/src/commands/hardis/org/diagnose/unusedlicenses.ts b/src/commands/hardis/org/diagnose/unusedlicenses.ts index d18eb8ad6..8f7c38043 100644 --- a/src/commands/hardis/org/diagnose/unusedlicenses.ts +++ b/src/commands/hardis/org/diagnose/unusedlicenses.ts @@ -1,24 +1,21 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages, SfdxError } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { isCI, uxLog } from "../../../../common/utils"; -import { bulkQuery, bulkQueryChunksIn, bulkUpdate } from "../../../../common/utils/apiUtils"; -import { generateCsvFile, generateReportPath } from "../../../../common/utils/filesUtils"; -import { NotifProvider, NotifSeverity } from "../../../../common/notifProvider"; -import { getNotificationButtons, getOrgMarkdown, getSeverityIcon } from "../../../../common/utils/notifUtils"; -import { prompts } from "../../../../common/utils/prompts"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class DiagnoseUnusedLicenses extends SfdxCommand { - public static title = "Detect unused Permission Set Licenses (beta)"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages, SfError } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { isCI, uxLog } from '../../../../common/utils/index.js'; +import { bulkQuery, bulkQueryChunksIn, bulkUpdate } from '../../../../common/utils/apiUtils.js'; +import { generateCsvFile, generateReportPath } from '../../../../common/utils/filesUtils.js'; +import { NotifProvider, NotifSeverity } from '../../../../common/notifProvider/index.js'; +import { getNotificationButtons, getOrgMarkdown, getSeverityIcon } from '../../../../common/utils/notifUtils.js'; +import { prompts } from '../../../../common/utils/prompts.js'; +import { CONSTANTS } from '../../../../config/index.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class DiagnoseUnusedLicenses extends SfCommand { + public static title = 'Detect unused Permission Set Licenses (beta)'; public static description = `When you assign a Permission Set to a user, and that this Permission Set is related to a Permission Set License, a Permission Set License Assignment is automatically created for the user. @@ -30,66 +27,62 @@ This command detects such useless Permission Set Licenses Assignments and sugges Many thanks to [Vincent Finet](https://www.linkedin.com/in/vincentfinet/) for the inspiration during his great speaker session at [French Touch Dreamin '23](https://frenchtouchdreamin.com/), and his kind agreement for reusing such inspiration in this command :) -This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-unused-licenses/) and can output Grafana, Slack and MsTeams Notifications. +This command is part of [sfdx-hardis Monitoring](${CONSTANTS.DOC_URL_ROOT}/salesforce-monitoring-unused-licenses/) and can output Grafana, Slack and MsTeams Notifications. `; - public static examples = ["$ sfdx hardis:org:diagnose:unusedlicenses", "$ sfdx hardis:org:diagnose:unusedlicenses --fix"]; + public static examples = ['$ sf hardis:org:diagnose:unusedlicenses', '$ sf hardis:org:diagnose:unusedlicenses --fix']; - protected static flagsConfig = { - outputfile: flags.string({ - char: "o", - description: "Force the path and name of output report file. Must end with .csv", + public static flags: any = { + outputfile: Flags.string({ + char: 'o', + description: 'Force the path and name of output report file. Must end with .csv', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; + public static requiresProject = false; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; + protected static additionalPermissionSetsToAlwaysGet = ['Sales_User']; - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; + protected static permSetsPermSetLicenses = [{ permSet: 'Sales_User', permSetLicense: 'SalesUserPsl' }]; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + protected static profilesPermissionSetLicenses = [ + { profile: 'Salesforce API Only', permSetLicense: 'SalesforceAPIIntegrationPsl' }, + ]; - protected static additionalPermissionSetsToAlwaysGet = ["Sales_User"]; - - protected static permSetsPermSetLicenses = [{ permSet: "Sales_User", permSetLicense: "SalesUserPsl" }]; - - protected static profilesPermissionSetLicenses = [{ profile: "Salesforce API Only", permSetLicense: "SalesforceAPIIntegrationPsl" }]; - - protected static alwaysExcludeForActiveUsersPermissionSetLicenses = ["IdentityConnect"]; + protected static alwaysExcludeForActiveUsersPermissionSetLicenses = ['IdentityConnect']; protected debugMode = false; protected outputFile; protected outputFilesRes: any = {}; - protected permissionSetLicenseAssignmentsActive = []; - protected permissionSetLicenses = []; - protected unusedPermissionSetLicenseAssignments = []; - protected permissionSets = []; - protected permissionSetsGroupMembers = []; - protected permissionSetAssignments = []; - protected permissionSetGroupAssignments = []; - protected allPermissionSetAssignments = []; + protected permissionSetLicenseAssignmentsActive: any[] = []; + protected permissionSetLicenses: any[] = []; + protected unusedPermissionSetLicenseAssignments: any[] = []; + protected permissionSets: any[] = []; + protected permissionSetsGroupMembers: any[] = []; + protected permissionSetAssignments: any[] = []; + protected permissionSetGroupAssignments: any[] = []; + protected allPermissionSetAssignments: any[] = []; protected statusCode = 0; /* jscpd:ignore-end */ public async run(): Promise { - this.debugMode = this.flags.debug || false; - this.outputFile = this.flags.outputfile || null; + const { flags } = await this.parse(DiagnoseUnusedLicenses); + this.debugMode = flags.debug || false; + this.outputFile = flags.outputfile || null; - const conn = this.org.getConnection(); + const conn = flags['target-org'].getConnection(); // List Permission Set Licenses Assignments this.permissionSetLicenseAssignmentsActive = await this.listAllPermissionSetLicenseAssignments(conn); @@ -117,17 +110,20 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co this.allPermissionSetAssignments = this.permissionSetGroupAssignments.concat(this.permissionSetAssignments); // Browse Permission Sets License assignments - const severityIconWarning = getSeverityIcon("warning"); + const severityIconWarning = getSeverityIcon('warning'); for (const psla of this.permissionSetLicenseAssignmentsActive) { - const pslaUsername = psla["Assignee.Username"]; + const pslaUsername = psla['Assignee.Username']; // Find related Permission Set assignments const foundMatchingPsAssignments = this.allPermissionSetAssignments.filter((psa) => { - if (psa["Assignee.Username"] === pslaUsername) { + if (psa['Assignee.Username'] === pslaUsername) { if (psa.licenseIds.includes(psla.PermissionSetLicenseId)) { return true; } else if ( DiagnoseUnusedLicenses.permSetsPermSetLicenses.some((psPsl) => { - if (psa["PermissionSet.Name"] === psPsl.permSet && psla["PermissionSetLicense.DeveloperName"] === psPsl.permSetLicense) { + if ( + psa['PermissionSet.Name'] === psPsl.permSet && + psla['PermissionSetLicense.DeveloperName'] === psPsl.permSetLicense + ) { return true; } return false; @@ -142,17 +138,20 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co // Handle special cases of Profiles that assigns Permission set licenses when selected on a user const isProfileRelatedPSLA = DiagnoseUnusedLicenses.profilesPermissionSetLicenses.some((profilePsl) => { return ( - psla["Assignee.Profile.Name"].startsWith(profilePsl.profile) && psla["PermissionSetLicense.DeveloperName"] === profilePsl.permSetLicense + psla['Assignee.Profile.Name'].startsWith(profilePsl.profile) && + psla['PermissionSetLicense.DeveloperName'] === profilePsl.permSetLicense ); }); - const isExcluded = DiagnoseUnusedLicenses.alwaysExcludeForActiveUsersPermissionSetLicenses.includes(psla["PermissionSetLicense.DeveloperName"]); + const isExcluded = DiagnoseUnusedLicenses.alwaysExcludeForActiveUsersPermissionSetLicenses.includes( + psla['PermissionSetLicense.DeveloperName'] + ); if (foundMatchingPsAssignments.length === 0 && !isProfileRelatedPSLA && !isExcluded) { this.unusedPermissionSetLicenseAssignments.push({ Id: psla.Id, - PermissionsSetLicense: psla["PermissionSetLicense.MasterLabel"], - User: psla["Assignee.Username"], - Reason: "Related PS assignment not found", - severity: "warning", + PermissionsSetLicense: psla['PermissionSetLicense.MasterLabel'], + User: psla['Assignee.Username'], + Reason: 'Related PS assignment not found', + severity: 'warning', severityIcon: severityIconWarning, }); } @@ -173,7 +172,12 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co uxLog(this, c.red(msg)); for (const pslMasterLabel of Object.keys(summary).sort()) { const psl = this.getPermissionSetLicenseByMasterLabel(pslMasterLabel); - uxLog(this, c.red(`- ${pslMasterLabel}: ${summary[pslMasterLabel]} (${psl.UsedLicenses} used on ${psl.TotalLicenses} available)`)); + uxLog( + this, + c.red( + `- ${pslMasterLabel}: ${summary[pslMasterLabel]} (${psl.UsedLicenses} used on ${psl.TotalLicenses} available)` + ) + ); } } else { uxLog(this, c.green(msg)); @@ -181,17 +185,17 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co // Generate output CSV file if (this.unusedPermissionSetLicenseAssignments.length > 0) { - this.outputFile = await generateReportPath("unused-ps-license-assignments", this.outputFile); + this.outputFile = await generateReportPath('unused-ps-license-assignments', this.outputFile); this.outputFilesRes = await generateCsvFile(this.unusedPermissionSetLicenseAssignments, this.outputFile); } // Manage notifications - await this.manageNotifications(this.unusedPermissionSetLicenseAssignments, summary); + await this.manageNotifications(this.unusedPermissionSetLicenseAssignments, summary, flags); // Propose to delete await this.managePermissionSetLicenseAssignmentsDeletion(conn); - if ((this.argv || []).includes("unusedlicenses")) { + if ((this.argv || []).includes('unusedlicenses')) { process.exitCode = this.statusCode; } @@ -213,7 +217,7 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co FROM PermissionSetLicenseAssign WHERE Assignee.IsActive=true ORDER BY PermissionSetLicense.MasterLabel, Assignee.Username`, - conn, + conn ); return pslaQueryRes.records; } @@ -223,11 +227,14 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co .map((psla) => { return { Id: psla.PermissionSetLicenseId, - DeveloperName: psla["PermissionSetLicense.DeveloperName"], - MasterLabel: psla["PermissionSetLicense.MasterLabel"], + DeveloperName: psla['PermissionSetLicense.DeveloperName'], + MasterLabel: psla['PermissionSetLicense.MasterLabel'], }; }) - .filter((value, index, self) => index === self.findIndex((t) => t.Id === value.Id && t.MasterLabel === value.MasterLabel)); + .filter( + (value, index, self) => + index === self.findIndex((t) => t.Id === value.Id && t.MasterLabel === value.MasterLabel) + ); const psLicensesIds = relatedPermissionSetLicenses.map((psl) => psl.Id); if (relatedPermissionSetLicenses.length > 0) { uxLog(this, c.cyan(`Extracting related Permission Sets Licenses...`)); @@ -236,7 +243,7 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co FROM PermissionSetLicense WHERE Id in ({{IN}})`, conn, - psLicensesIds, + psLicensesIds ); return pslQueryRes.records; } @@ -250,14 +257,14 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co FROM PermissionSet WHERE LicenseId in ({{IN}})`, conn, - psLicensesIds, + psLicensesIds ); const psQueryAdditionalRes = await bulkQueryChunksIn( `SELECT Id,Label,Name,LicenseId FROM PermissionSet WHERE Name in ({{IN}})`, conn, - DiagnoseUnusedLicenses.additionalPermissionSetsToAlwaysGet, + DiagnoseUnusedLicenses.additionalPermissionSetsToAlwaysGet ); return psQueryRes.records.concat(psQueryAdditionalRes.records); } @@ -269,13 +276,15 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co FROM PermissionSetGroupComponent WHERE PermissionSetId in ({{IN}})`, conn, - permissionSetsIds, + permissionSetsIds ); return psgcQueryRes.records; } private async listRelatedPermissionSetAssignmentsToGroups(conn) { - const permissionSetsGroupIds = [...new Set(this.permissionSetsGroupMembers.map((psgc) => psgc.PermissionSetGroupId))]; + const permissionSetsGroupIds = [ + ...new Set(this.permissionSetsGroupMembers.map((psgc) => psgc.PermissionSetGroupId)), + ]; if (permissionSetsGroupIds.length > 0) { uxLog(this, c.cyan(`Extracting related Permission Set Group Assignments...`)); const psgaQueryRes = await bulkQueryChunksIn( @@ -283,15 +292,15 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co FROM PermissionSetAssignment WHERE PermissionSetGroupId in ({{IN}})`, conn, - permissionSetsGroupIds, + permissionSetsGroupIds ); // Add related licenses in licenseIds for each PS Assignment psgaQueryRes.records = psgaQueryRes.records.map((psga) => { psga.licenseIds = []; for (const psgm of this.permissionSetsGroupMembers) { if (psgm.PermissionSetGroupId === psga.PermissionSetGroupId) { - if (psgm["PermissionSet.LicenseId"]) { - psga.licenseIds.push(psgm["PermissionSet.LicenseId"]); + if (psgm['PermissionSet.LicenseId']) { + psga.licenseIds.push(psgm['PermissionSet.LicenseId']); } } } @@ -309,29 +318,29 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co FROM PermissionSetAssignment WHERE PermissionSetId in ({{IN}})`, conn, - permissionSetsIds, + permissionSetsIds ); // Add related license in licenseIds for each PS Assignment psaQueryRes.records = psaQueryRes.records.map((psa) => { psa.licenseIds = []; - if (psa["PermissionSet.LicenseId"]) { - psa.licenseIds.push(psa["PermissionSet.LicenseId"]); + if (psa['PermissionSet.LicenseId']) { + psa.licenseIds.push(psa['PermissionSet.LicenseId']); } return psa; }); return psaQueryRes.records; } - private async manageNotifications(unusedPermissionSetLicenseAssignments: any[], summary: any) { + private async manageNotifications(unusedPermissionSetLicenseAssignments: any[], summary: any, flags) { // Build notification - const orgMarkdown = await getOrgMarkdown(this.org?.getConnection()?.instanceUrl); + const orgMarkdown = await getOrgMarkdown(flags['target-org']?.getConnection()?.instanceUrl); const notifButtons = await getNotificationButtons(); - let notifSeverity: NotifSeverity = "log"; + let notifSeverity: NotifSeverity = 'log'; let notifText = `No unused Permission Set Licenses Assignments has been found in ${orgMarkdown}`; let notifDetailText = ``; - let attachments = []; + let attachments: any[] = []; if (unusedPermissionSetLicenseAssignments.length > 0) { - notifSeverity = "warning"; + notifSeverity = 'warning'; notifText = `${unusedPermissionSetLicenseAssignments.length} unused Permission Set Licenses Assignments have been found in ${orgMarkdown}`; for (const pslMasterLabel of Object.keys(summary).sort()) { const psl = this.getPermissionSetLicenseByMasterLabel(pslMasterLabel); @@ -340,9 +349,9 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co attachments = [{ text: notifDetailText }]; } // Send notifications - globalThis.jsForceConn = this?.org?.getConnection(); // Required for some notifications providers like Email + globalThis.jsForceConn = flags['target-org']?.getConnection(); // Required for some notifications providers like Email NotifProvider.postNotifications({ - type: "UNUSED_LICENSES", + type: 'UNUSED_LICENSES', text: notifText, attachments: attachments, buttons: notifButtons, @@ -360,25 +369,28 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co private async managePermissionSetLicenseAssignmentsDeletion(conn) { if (!isCI && this.unusedPermissionSetLicenseAssignments.length) { const confirmRes = await prompts({ - type: "select", - message: "Do you want to delete unused Permission Set License Assignments ?", + type: 'select', + message: 'Do you want to delete unused Permission Set License Assignments ?', choices: [ { title: `Yes, delete the ${this.unusedPermissionSetLicenseAssignments.length} useless Permission Set License Assignments !`, - value: "all", + value: 'all', }, - { title: "No" }, + { title: 'No' }, ], }); - if (confirmRes.value === "all") { + if (confirmRes.value === 'all') { const pslaToDelete = this.unusedPermissionSetLicenseAssignments.map((psla) => { return { Id: psla.Id }; }); - const deleteRes = await bulkUpdate("PermissionSetLicenseAssign", "delete", pslaToDelete, conn); - const deleteSuccessNb = deleteRes.successRecordsNb; - const deleteErrorNb = deleteRes.errorRecordsNb; + const deleteRes = await bulkUpdate('PermissionSetLicenseAssign', 'delete', pslaToDelete, conn); + const deleteSuccessNb = deleteRes.successfulResults.length; + const deleteErrorNb = deleteRes.failedResults.length; if (deleteErrorNb > 0) { - uxLog(this, c.yellow(`Warning: ${c.red(c.bold(deleteErrorNb))} assignments has not been deleted (bulk API errors)`)); + uxLog( + this, + c.yellow(`Warning: ${c.red(c.bold(deleteErrorNb))} assignments has not been deleted (bulk API errors)`) + ); this.statusCode = 1; } else { this.statusCode = 0; @@ -395,6 +407,6 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co if (pslList.length === 1) { return pslList[0]; } - throw new SfdxError(`Unable to find Permission Set License with MasterLabel ${masterLabel}`); + throw new SfError(`Unable to find Permission Set License with MasterLabel ${masterLabel}`); } } diff --git a/src/commands/hardis/org/diagnose/unusedusers.ts b/src/commands/hardis/org/diagnose/unusedusers.ts index 9d7e07ff8..221e4ced8 100644 --- a/src/commands/hardis/org/diagnose/unusedusers.ts +++ b/src/commands/hardis/org/diagnose/unusedusers.ts @@ -1,24 +1,21 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { isCI, uxLog } from "../../../../common/utils"; -import { bulkQuery } from "../../../../common/utils/apiUtils"; -import { generateCsvFile, generateReportPath } from "../../../../common/utils/filesUtils"; -import { NotifProvider, NotifSeverity } from "../../../../common/notifProvider"; -import { getNotificationButtons, getOrgMarkdown } from "../../../../common/utils/notifUtils"; -import { prompts } from "../../../../common/utils/prompts"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { isCI, uxLog } from '../../../../common/utils/index.js'; +import { bulkQuery } from '../../../../common/utils/apiUtils.js'; +import { generateCsvFile, generateReportPath } from '../../../../common/utils/filesUtils.js'; +import { NotifProvider, NotifSeverity } from '../../../../common/notifProvider/index.js'; +import { getNotificationButtons, getOrgMarkdown } from '../../../../common/utils/notifUtils.js'; +import { prompts } from '../../../../common/utils/prompts.js'; +import { CONSTANTS } from '../../../../config/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class DiagnoseUnusedUsers extends SfdxCommand { - public static title = "Detect unused Users in Salesforce"; +export default class DiagnoseUnusedUsers extends SfCommand { + public static title = 'Detect unused Users in Salesforce'; public static description = `Efficient user management is vital in Salesforce to ensure resources are optimized and costs are controlled. However, inactive or unused user accounts can often go unnoticed, leading to wasted licenses and potential security risks. This tool addresses this challenge by enabling administrators to identify users who haven't logged in within a specified period. @@ -34,86 +31,86 @@ Note: You can see the full list of available license identifiers in [Salesforce Use --returnactiveusers to revert the command and retrieve active users that has logged in during the period. -This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-inactive-users/) and can output Grafana, Slack and MsTeams Notifications. +This command is part of [sfdx-hardis Monitoring](${CONSTANTS.DOC_URL_ROOT}/salesforce-monitoring-inactive-users/) and can output Grafana, Slack and MsTeams Notifications. `; public static examples = [ - "$ sfdx hardis:org:diagnose:unusedusers", - "$ sfdx hardis:org:diagnose:unusedusers --days 365", - "$ sfdx hardis:org:diagnose:unusedusers --days 60 --licensetypes all-crm", - "$ sfdx hardis:org:diagnose:unusedusers --days 60 --licenseidentifiers SFDC,AUL,AUL1", - "$ sfdx hardis:org:diagnose:unusedusers --days 60 --licensetypes all-crm --returnactiveusers", + '$ sf hardis:org:diagnose:unusedusers', + '$ sf hardis:org:diagnose:unusedusers --days 365', + '$ sf hardis:org:diagnose:unusedusers --days 60 --licensetypes all-crm', + '$ sf hardis:org:diagnose:unusedusers --days 60 --licenseidentifiers SFDC,AUL,AUL1', + '$ sf hardis:org:diagnose:unusedusers --days 60 --licensetypes all-crm --returnactiveusers', ]; //Comment default values to test the prompts - protected static flagsConfig = { - outputfile: flags.string({ - char: "o", - description: "Force the path and name of output report file. Must end with .csv", + public static flags: any = { + outputfile: Flags.string({ + char: 'o', + description: 'Force the path and name of output report file. Must end with .csv', }), - days: flags.number({ - char: "t", - description: "Extracts the users that have been inactive for the amount of days specified. In CI, default is 180 days", + days: Flags.integer({ + char: 't', + description: + 'Extracts the users that have been inactive for the amount of days specified. In CI, default is 180 days', }), - licensetypes: flags.enum({ - char: "l", - options: ["all", "all-crm", "all-paying"], - description: "Type of licenses to check. If set, do not use licenseidentifiers option. In CI, default is all-crm", + licensetypes: Flags.string({ + char: 'l', + options: ['all', 'all-crm', 'all-paying'], + description: 'Type of licenses to check. If set, do not use licenseidentifiers option. In CI, default is all-crm', }), - licenseidentifiers: flags.string({ - char: "i", + licenseidentifiers: Flags.string({ + char: 'i', description: - "Comma-separated list of license identifiers, in case licensetypes is not used.. Identifiers available at https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_userlicense.htm", + 'Comma-separated list of license identifiers, in case licensetypes is not used.. Identifiers available at https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_userlicense.htm', }), - returnactiveusers: flags.boolean({ + returnactiveusers: Flags.boolean({ default: false, - description: "Inverts the command by returning the active users", + description: 'Inverts the command by returning the active users', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; protected licenseTypesCorrespondances = { - all: "all", - "all-crm": "SFDC,AUL,AUL1,AULL_IGHT", - "all-paying": "SFDC,AUL,AUL1,AULL_IGHT,PID_Customer_Community,PID_Customer_Community_Login,PID_Partner_Community,PID_Partner_Community_Login", + all: 'all', + 'all-crm': 'SFDC,AUL,AUL1,AULL_IGHT', + 'all-paying': + 'SFDC,AUL,AUL1,AULL_IGHT,PID_Customer_Community,PID_Customer_Community_Login,PID_Partner_Community,PID_Partner_Community_Login', }; protected returnActiveUsers = false; protected debugMode = false; protected outputFile; protected outputFilesRes: any = {}; - protected lastNdays: number; - protected licenseTypes: string; - protected licenseIdentifiers: string; - protected users = []; + protected lastNdays: number | null; + protected licenseTypes: string | null; + protected licenseIdentifiers: string | null; + protected users: any[] = []; protected statusCode = 0; /* jscpd:ignore-end */ public async run(): Promise { - this.debugMode = this.flags.debug || false; - this.returnActiveUsers = this.flags.returnactiveusers ?? false; - this.outputFile = this.flags.outputfile || null; - this.lastNdays = this.flags.days || null; - this.licenseIdentifiers = this.flags.licenseidentifiers || null; - this.licenseTypes = this.flags.licensetypes; + const { flags } = await this.parse(DiagnoseUnusedUsers); + this.debugMode = flags.debug || false; + this.returnActiveUsers = flags.returnactiveusers ?? false; + this.outputFile = flags.outputfile || null; + this.lastNdays = flags.days || null; + this.licenseIdentifiers = flags.licenseidentifiers || null; + this.licenseTypes = flags.licensetypes || null; // Calculate lastNdays to use await this.defineNumberOfInactiveDays(); @@ -121,20 +118,23 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co await this.defineLicenseIdentifiers(); // Retrieve the list of users who haven't logged in for a while - const conn = this.org.getConnection(); + const conn = flags['target-org'].getConnection(); uxLog( this, c.cyan( this.returnActiveUsers ? `Extracting active users on ${conn.instanceUrl} ...` - : `Extracting active users who haven't logged in for a while on ${conn.instanceUrl} ...`, - ), + : `Extracting active users who haven't logged in for a while on ${conn.instanceUrl} ...` + ) ); this.users = await this.listUsersFromLicenses(conn); // Generate output CSV file if (this.users.length > 0) { - this.outputFile = await generateReportPath(this.returnActiveUsers ? "active-users" : "unused-users", this.outputFile); + this.outputFile = await generateReportPath( + this.returnActiveUsers ? 'active-users' : 'unused-users', + this.outputFile + ); this.outputFilesRes = await generateCsvFile(this.users, this.outputFile); } @@ -144,7 +144,7 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co summary = `${this.users.length} users have logged in ${conn.instanceUrl} in the last ${this.lastNdays} days`; } else { // Inactive users mode - const userSummaryInfo = this.users.length == 1 ? "user has" : "users have"; + const userSummaryInfo = this.users.length == 1 ? 'user has' : 'users have'; summary = `No unused users have been found`; if (this.users.length === 0) { summary = `All users have logged in to ${conn.instanceUrl} within the last ${this.lastNdays} days!`; @@ -153,7 +153,7 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co summary = `${this.users.length} active ${userSummaryInfo} not logged in to ${conn.instanceUrl} in the last ${this.lastNdays} days!`; } - if ((this.argv || []).includes("unusedusers")) { + if ((this.argv || []).includes('unusedusers')) { process.exitCode = this.statusCode; } } @@ -182,21 +182,21 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co // Ask user if interactive mode if (!this.licenseTypes && !isCI) { const licenseTypesResponse = await prompts({ - type: "select", - name: "licensetypes", - message: "Please select the type of licenses you want to detect ", + type: 'select', + name: 'licensetypes', + message: 'Please select the type of licenses you want to detect ', choices: [ - { value: "all", title: "All licenses types" }, - { value: `all-crm`, title: "Salesforce Licenses" }, - { value: `all-paying`, title: "Salesforce Licences + Experience + Other paying" }, + { value: 'all', title: 'All licenses types' }, + { value: `all-crm`, title: 'Salesforce Licenses' }, + { value: `all-paying`, title: 'Salesforce Licences + Experience + Other paying' }, ], }); this.licenseTypes = licenseTypesResponse.licensetypes; } else if (!this.licenseTypes) { - this.licenseTypes = "all-crm"; + this.licenseTypes = 'all-crm'; } // Get licenseIdentifiers from licenseType - this.licenseIdentifiers = this.licenseTypesCorrespondances[this.licenseTypes]; + this.licenseIdentifiers = this.licenseTypesCorrespondances[this.licenseTypes || '']; } } @@ -205,9 +205,9 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co if (!isCI) { // If manual mode and days not sent as parameter, prompt user const lastNdaysResponse = await prompts({ - type: "select", - name: "days", - message: "Please select the period to detect users.", + type: 'select', + name: 'days', + message: 'Please select the period to detect users.', choices: [ { title: `1 day`, value: 1 }, { title: `2 days`, value: 2 }, @@ -231,16 +231,18 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co private async listUsersFromLicenses(conn) { let whereConstraint = this.returnActiveUsers ? // Active users - `WHERE IsActive = true AND (` + `(LastLoginDate >= LAST_N_DAYS:${this.lastNdays} AND LastLoginDate != NULL)` + `)` + `WHERE IsActive = true AND (` + + `(LastLoginDate >= LAST_N_DAYS:${this.lastNdays} AND LastLoginDate != NULL)` + + `)` : // Inactive users - `WHERE IsActive = true AND (` + - `(LastLoginDate < LAST_N_DAYS:${this.lastNdays} AND LastLoginDate != NULL) OR ` + - `(CreatedDate < LAST_N_DAYS:${this.lastNdays} AND LastLoginDate = NULL)` + // Check also for users never used - `)`; + `WHERE IsActive = true AND (` + + `(LastLoginDate < LAST_N_DAYS:${this.lastNdays} AND LastLoginDate != NULL) OR ` + + `(CreatedDate < LAST_N_DAYS:${this.lastNdays} AND LastLoginDate = NULL)` + // Check also for users never used + `)`; // Add License constraint only if necessary - if (this.licenseTypes !== "all") { - const licenseIdentifierValues = this.licenseIdentifiers.split(","); - const licenseIdentifierCondition = licenseIdentifierValues.map((value) => `'${value}'`).join(","); + if (this.licenseTypes !== 'all') { + const licenseIdentifierValues = (this.licenseIdentifiers || '').split(','); + const licenseIdentifierCondition = licenseIdentifierValues.map((value) => `'${value}'`).join(','); whereConstraint += ` AND Profile.UserLicense.LicenseDefinitionKey IN (${licenseIdentifierCondition})`; } // Build & call Bulk API Query @@ -254,26 +256,29 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co } private async manageNotifications(users: any[]) { + const { flags } = await this.parse(DiagnoseUnusedUsers); // Build notification - const orgMarkdown = await getOrgMarkdown(this.org?.getConnection()?.instanceUrl); + const orgMarkdown = await getOrgMarkdown(flags['target-org']?.getConnection()?.instanceUrl); const notifButtons = await getNotificationButtons(); - let notifSeverity: NotifSeverity = "log"; - let notifText = this.returnActiveUsers ? `No active user has logged in in ${orgMarkdown}` : `No inactive user has been found in ${orgMarkdown}`; + let notifSeverity: NotifSeverity = 'log'; + let notifText = this.returnActiveUsers + ? `No active user has logged in in ${orgMarkdown}` + : `No inactive user has been found in ${orgMarkdown}`; const notifDetailText = ``; - let attachments = []; + let attachments: any[] = []; if (users.length > 0) { - notifSeverity = this.returnActiveUsers ? "log" : "warning"; + notifSeverity = this.returnActiveUsers ? 'log' : 'warning'; notifText = this.returnActiveUsers - ? `${this.users.length} active users have logged in to ${orgMarkdown} within the last ${this.lastNdays} days.` - : `${this.users.length} active users have not logged in to ${orgMarkdown} within the last ${this.lastNdays} days.`; + ? `*${this.users.length}* active users have logged in to ${orgMarkdown} within the last ${this.lastNdays} days.` + : `*${this.users.length}* active users have not logged in to ${orgMarkdown} within the last ${this.lastNdays} days.`; attachments = [{ text: notifDetailText }]; } const metrics = this.returnActiveUsers ? { ActiveUsers: this.users.length } : { UnusedUsers: this.users.length }; /* jscpd:ignore-start */ // Send notifications - globalThis.jsForceConn = this?.org?.getConnection(); // Required for some notifications providers like Email + globalThis.jsForceConn = flags['target-org']?.getConnection(); // Required for some notifications providers like Email NotifProvider.postNotifications({ - type: this.returnActiveUsers ? "ACTIVE_USERS" : "UNUSED_USERS", + type: this.returnActiveUsers ? 'ACTIVE_USERS' : 'UNUSED_USERS', text: notifText, attachments: attachments, buttons: notifButtons, diff --git a/src/commands/hardis/org/files/export.ts b/src/commands/hardis/org/files/export.ts index 4151476c1..39226b70e 100644 --- a/src/commands/hardis/org/files/export.ts +++ b/src/commands/hardis/org/files/export.ts @@ -1,21 +1,22 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { uxLog } from "../../../../common/utils"; -import { FilesExporter, getFilesWorkspaceDetail, promptFilesExportConfiguration, selectFilesWorkspace } from "../../../../common/utils/filesUtils"; -import { prompts } from "../../../../common/utils/prompts"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class FilesExport extends SfdxCommand { - public static title = "Export files"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { uxLog } from '../../../../common/utils/index.js'; +import { + FilesExporter, + getFilesWorkspaceDetail, + promptFilesExportConfiguration, + selectFilesWorkspace, +} from '../../../../common/utils/filesUtils.js'; +import { prompts } from '../../../../common/utils/prompts.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class FilesExport extends SfCommand { + public static title = 'Export files'; public static description = `Export file attachments from a Salesforce org @@ -24,58 +25,54 @@ See article below [![How to mass download notes and attachments files from a Salesforce org](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-mass-download.jpg)](https://nicolas.vuillamy.fr/how-to-mass-download-notes-and-attachments-files-from-a-salesforce-org-83a028824afd) `; - public static examples = ["$ sfdx hardis:org:files:export"]; + public static examples = ['$ sf hardis:org:files:export']; - protected static flagsConfig = { - path: flags.string({ - char: "p", - description: "Path to the file export project", + public static flags: any = { + path: Flags.string({ + char: 'p', + description: 'Path to the file export project', }), - chunksize: flags.number({ - char: "c", - description: "Number of records to add in a chunk before it is processed", + chunksize: Flags.integer({ + char: 'c', + description: 'Number of records to add in a chunk before it is processed', default: 1000, }), - polltimeout: flags.number({ - char: "t", - description: "Timeout in MS for Bulk API calls", + polltimeout: Flags.integer({ + char: 't', + description: 'Timeout in MS for Bulk API calls', default: 300000, }), - startchunknumber: flags.number({ - char: "s", - description: "Chunk number to start from", + startchunknumber: Flags.integer({ + char: 's', + description: 'Chunk number to start from', default: 0, }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; /* jscpd:ignore-end */ public async run(): Promise { - let filesPath = this.flags.path || null; - const recordsChunkSize = this.flags.chunksize; - const pollTimeout = this.flags.polltimeout; - const startChunkNumber = this.flags.startchunknumber || 0; - //const debugMode = this.flags.debug || false; + const { flags } = await this.parse(FilesExport); + let filesPath = flags.path || null; + const recordsChunkSize = flags.chunksize; + const pollTimeout = flags.polltimeout; + const startChunkNumber = flags.startchunknumber || 0; + //const debugMode = flags.debug || false; const exportOptions: any = { pollTimeout: pollTimeout, @@ -85,12 +82,12 @@ See article below // Identify files workspace if not defined if (filesPath == null) { - filesPath = await selectFilesWorkspace({ selectFilesLabel: "Please select a files workspace to EXPORT" }); - const exportConfigInitial = await getFilesWorkspaceDetail(filesPath); + filesPath = await selectFilesWorkspace({ selectFilesLabel: 'Please select a files workspace to EXPORT' }); + const exportConfigInitial: any = (await getFilesWorkspaceDetail(filesPath || '')) || {}; // Request to use defaut config or to override it for this run const defaultConfigRes = await prompts({ - type: "confirm", - message: c.cyanBright("Do you want to use default configuration for " + exportConfigInitial.label + " ?"), + type: 'confirm', + message: c.cyanBright('Do you want to use default configuration for ' + exportConfigInitial.label + ' ?'), }); if (defaultConfigRes.value !== true) { const exportConfig = await promptFilesExportConfiguration(exportConfigInitial, true); @@ -100,10 +97,17 @@ See article below // Export files from org - const exportResult = await new FilesExporter(filesPath, this.org.getConnection(), exportOptions, this).processExport(); + const exportResult = await new FilesExporter( + filesPath || '', + flags['target-org'].getConnection(), + exportOptions, + this + ).processExport(); // Output message - const message = `Successfully exported files from project ${c.green(filesPath)} from org ${c.green(this.org.getUsername())}`; + const message = `Successfully exported files from project ${c.green(filesPath)} from org ${c.green( + flags['target-org'].getUsername() + )}`; uxLog(this, c.cyan(message)); return { outputString: message, exportResult: exportResult }; diff --git a/src/commands/hardis/org/files/import.ts b/src/commands/hardis/org/files/import.ts index 83a9f1f44..aefcec843 100644 --- a/src/commands/hardis/org/files/import.ts +++ b/src/commands/hardis/org/files/import.ts @@ -1,21 +1,17 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { isCI, uxLog } from "../../../../common/utils"; -import { FilesImporter, selectFilesWorkspace } from "../../../../common/utils/filesUtils"; -import { prompts } from "../../../../common/utils/prompts"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { isCI, uxLog } from '../../../../common/utils/index.js'; +import { FilesImporter, selectFilesWorkspace } from '../../../../common/utils/filesUtils.js'; +import { prompts } from '../../../../common/utils/prompts.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class FilesImport extends SfdxCommand { - public static title = "Import files"; +export default class FilesImport extends SfCommand { + public static title = 'Import files'; public static description = `Import file attachments into a Salesforce org @@ -24,56 +20,53 @@ See article below to see how to Export them. [![How to mass download notes and attachments files from a Salesforce org](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-mass-download.jpg)](https://nicolas.vuillamy.fr/how-to-mass-download-notes-and-attachments-files-from-a-salesforce-org-83a028824afd) `; - public static examples = ["$ sfdx hardis:org:files:import"]; + public static examples = ['$ sf hardis:org:files:import']; - protected static flagsConfig = { - path: flags.string({ - char: "p", - description: "Path to the file export project", + public static flags: any = { + path: Flags.string({ + char: 'p', + description: 'Path to the file export project', }), - overwrite: flags.boolean({ - char: "o", - description: "Override existing files (doubles the number of API calls)", + overwrite: Flags.boolean({ + char: 'o', + description: 'Override existing files (doubles the number of API calls)', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; protected handleOverwrite; /* jscpd:ignore-end */ public async run(): Promise { - let filesPath = this.flags.path || null; - this.handleOverwrite = this.flags?.overwrite === true; + const { flags } = await this.parse(FilesImport); + let filesPath = flags.path || null; + this.handleOverwrite = flags?.overwrite === true; // Identify files workspace if not defined if (filesPath == null) { - filesPath = await selectFilesWorkspace({ selectFilesLabel: "Please select a files workspace to IMPORT" }); + filesPath = await selectFilesWorkspace({ selectFilesLabel: 'Please select a files workspace to IMPORT' }); } if (!isCI) { const handleOverwriteRes = await prompts({ - type: "confirm", - name: "value", - message: "Do you want to overwrite the existing files with the same name ? (doubles the number of used API calls)", + type: 'confirm', + name: 'value', + message: + 'Do you want to overwrite the existing files with the same name ? (doubles the number of used API calls)', }); this.handleOverwrite = handleOverwriteRes.value; } @@ -81,10 +74,17 @@ See article below to see how to Export them. const importOptions: any = { handleOverwrite: this.handleOverwrite }; // Import files into org - const importResult = await new FilesImporter(filesPath, this.org.getConnection(), importOptions, this).processImport(); + const importResult = await new FilesImporter( + filesPath || '', + flags['target-org'].getConnection(), + importOptions, + this + ).processImport(); // Output message - const message = `Successfully imported files from project ${c.green(filesPath)} from org ${c.green(this.org.getUsername())}`; + const message = `Successfully imported files from project ${c.green(filesPath)} from org ${c.green( + flags['target-org'].getUsername() + )}`; uxLog(this, c.cyan(message)); return { outputString: message, importResult: importResult }; diff --git a/src/commands/hardis/org/fix/listviewmine.ts b/src/commands/hardis/org/fix/listviewmine.ts index b52f4cda8..cf8ec34b5 100644 --- a/src/commands/hardis/org/fix/listviewmine.ts +++ b/src/commands/hardis/org/fix/listviewmine.ts @@ -1,21 +1,17 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import { uxLog } from "../../../../common/utils"; -import { restoreListViewMine } from "../../../../common/utils/orgConfigUtils"; -import { getConfig } from "../../../../config"; -import * as c from "chalk"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { uxLog } from '../../../../common/utils/index.js'; +import { restoreListViewMine } from '../../../../common/utils/orgConfigUtils.js'; +import { getConfig } from '../../../../config/index.js'; +import c from 'chalk'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class FixListViewMine extends SfdxCommand { - public static title = "Fix listviews with "; +export default class FixListViewMine extends SfCommand { + public static title = 'Fix listviews with '; public static description = `Fix listviews whose scope Mine has been replaced by Everything @@ -69,38 +65,31 @@ ENV PUPPETEER_EXECUTABLE_PATH="$\\{CHROMIUM_PATH}" // remove \\ before { `; public static examples = [ - "$ sfdx hardis:org:fix:listviewmine", - "$ sfdx hardis:org:fix:listviewmine --listviews Opportunity:MySubscriptions,Account:MyActivePartners", + '$ sf hardis:org:fix:listviewmine', + '$ sf hardis:org:fix:listviewmine --listviews Opportunity:MySubscriptions,Account:MyActivePartners', ]; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - listviews: flags.string({ - char: "l", + public static flags: any = { + listviews: Flags.string({ + char: 'l', description: `Comma-separated list of listviews following format Object:ListViewName\nExample: Contact:MyContacts,Contact:MyActiveContacts,Opportunity:MYClosedOpportunities`, }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; protected debugMode = false; @@ -108,21 +97,24 @@ ENV PUPPETEER_EXECUTABLE_PATH="$\\{CHROMIUM_PATH}" // remove \\ before { /* jscpd:ignore-end */ public async run(): Promise { - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(FixListViewMine); + this.debugMode = flags.debug || false; - uxLog(this, c.cyan("Setting back listviews to Mine instead of Everything...")); + uxLog(this, c.cyan('Setting back listviews to Mine instead of Everything...')); // Identify listviews to process - if (this.flags.listviews) { + if (flags.listviews) { // Use input flag - this.listViewsStrings = this.flags.listviews.split(","); + this.listViewsStrings = flags.listviews.split(','); } else { // Use property listViewsToSetToMine from .sfdx-hardis.yml config file - const config = await getConfig("project"); + const config = await getConfig('project'); this.listViewsStrings = config.listViewsToSetToMine || []; } - const result = await restoreListViewMine(this.listViewsStrings, this.org.getConnection(), { debug: this.debugMode }); + const result = await restoreListViewMine(this.listViewsStrings, flags['target-org'].getConnection(), { + debug: this.debugMode, + }); return result; } } diff --git a/src/commands/hardis/org/generate/packagexmlfull.ts b/src/commands/hardis/org/generate/packagexmlfull.ts index 94ebc297c..35b4e5dab 100644 --- a/src/commands/hardis/org/generate/packagexmlfull.ts +++ b/src/commands/hardis/org/generate/packagexmlfull.ts @@ -1,57 +1,47 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as path from "path"; -import { isCI, uxLog } from "../../../../common/utils"; -import { getReportDirectory } from "../../../../config"; -import { buildOrgManifest } from "../../../../common/utils/deployUtils"; -import { promptOrgUsernameDefault } from "../../../../common/utils/orgUtils"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Connection, Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import * as path from 'path'; +import { isCI, uxLog } from '../../../../common/utils/index.js'; +import { getReportDirectory } from '../../../../config/index.js'; +import { buildOrgManifest } from '../../../../common/utils/deployUtils.js'; +import { promptOrgUsernameDefault } from '../../../../common/utils/orgUtils.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class GeneratePackageXmlFull extends SfCommand { + public static title = 'Generate Full Org package.xml'; -export default class GeneratePackageXmlFull extends SfdxCommand { - public static title = "Generate Full Org package.xml"; - - public static description = "Generates full org package.xml, including managed items"; + public static description = 'Generates full org package.xml, including managed items'; public static examples = [ - "$ sfdx hardis:org:generate:packagexmlfull", - "$ sfdx hardis:org:generate:packagexmlfull --outputfile /tmp/packagexmlfull.xml", - "$ sfdx hardis:org:generate:packagexmlfull --targetusername nico@example.com", + '$ sf hardis:org:generate:packagexmlfull', + '$ sf hardis:org:generate:packagexmlfull --outputfile /tmp/packagexmlfull.xml', + '$ sf hardis:org:generate:packagexmlfull --target-org nico@example.com', ]; - protected static flagsConfig = { - outputfile: flags.string({ - description: "Output package.xml file", + public static flags: any = { + outputfile: Flags.string({ + description: 'Output package.xml file', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; protected debugMode = false; protected outputFile; @@ -59,17 +49,18 @@ export default class GeneratePackageXmlFull extends SfdxCommand { /* jscpd:ignore-end */ public async run(): Promise { - this.outputFile = this.flags.outputfile || null; - this.debugMode = this.flags.debugMode || false; + const { flags } = await this.parse(GeneratePackageXmlFull); + this.outputFile = flags.outputfile || null; + this.debugMode = flags.debug || false; // Select org that will be used to export records - let conn = null; - let orgUsername = this.org.getUsername(); + let conn: Connection | null = null; + let orgUsername = flags['target-org'].getUsername(); if (!isCI) { const prevOrgUsername = orgUsername; - orgUsername = await promptOrgUsernameDefault(this, orgUsername, { devHub: false, setDefault: false }); + orgUsername = await promptOrgUsernameDefault(this, orgUsername || '', { devHub: false, setDefault: false }); if (prevOrgUsername === orgUsername) { - conn = this.org.getConnection(); + conn = flags['target-org'].getConnection(); } } uxLog(this, c.cyan(`Generating full package xml for ${orgUsername}`)); @@ -77,7 +68,7 @@ export default class GeneratePackageXmlFull extends SfdxCommand { // Calculate default output file if not provided as input if (this.outputFile == null) { const reportDir = await getReportDirectory(); - this.outputFile = path.join(reportDir, "org-package-xml-full.xml"); + this.outputFile = path.join(reportDir, 'org-package-xml-full.xml'); } await buildOrgManifest(orgUsername, this.outputFile, conn); diff --git a/src/commands/hardis/org/monitor/all.ts b/src/commands/hardis/org/monitor/all.ts index c94f26d15..344f82320 100644 --- a/src/commands/hardis/org/monitor/all.ts +++ b/src/commands/hardis/org/monitor/all.ts @@ -1,20 +1,16 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { execCommand, uxLog } from "../../../../common/utils"; -import { getConfig, getEnvVar } from "../../../../config"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { execCommand, uxLog } from '../../../../common/utils/index.js'; +import { CONSTANTS, getConfig, getEnvVar } from '../../../../config/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class MonitorAll extends SfdxCommand { - public static title = "Monitor org"; +export default class MonitorAll extends SfCommand { + public static title = 'Monitor org'; public static description = `Monitor org, generate reports and sends notifications @@ -35,49 +31,41 @@ Example in env var: MONITORING_DISABLE=METADATA_STATUS,MISSING_ATTRIBUTES,UNUSED_METADATAS \`\`\` -A [default list of monitoring commands](https://sfdx-hardis.cloudity.com/salesforce-monitoring-home/#monitoring-commands) is used, if you want to override it you can define property **monitoringCommands** in your .sfdx-hardis.yml file +A [default list of monitoring commands](${CONSTANTS.DOC_URL_ROOT}/salesforce-monitoring-home/#monitoring-commands) is used, if you want to override it you can define property **monitoringCommands** in your .sfdx-hardis.yml file Example: \`\`\`yaml monitoringCommands: - title: My Custom command - command: sfdx my:custom:command + command: sf my:custom:command - title: My Custom command 2 - command: sfdx my:other:custom:command + command: sf my:other:custom:command \`\`\` You can force the daily run of all commands by defining env var \`MONITORING_IGNORE_FREQUENCY=true\` `; - public static examples = ["$ sfdx hardis:org:monitor:all"]; + public static examples = ['$ sf hardis:org:monitor:all']; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; - - // List required plugins, their presence will be tested before running the command - protected static requiresSfdxPlugins = ["sfdx-essentials"]; + public static requiresProject = true; // Trigger notification(s) to MsTeams channel protected static triggerNotification = true; @@ -87,98 +75,116 @@ You can force the daily run of all commands by defining env var \`MONITORING_IGN /* jscpd:ignore-end */ public async run(): Promise { - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(MonitorAll); + this.debugMode = flags.debug || false; // Build target org full manifest - uxLog(this, c.cyan("Running monitoring scripts for org " + c.bold(this.org.getConnection().instanceUrl)) + " ..."); + uxLog( + this, + c.cyan('Running monitoring scripts for org ' + c.bold(flags['target-org'].getConnection().instanceUrl)) + ' ...' + ); const monitoringCommandsDefault = [ { - key: "AUDIT_TRAIL", - title: "Detect suspect setup actions in major org", - command: "sfdx hardis:org:diagnose:audittrail", - frequency: "daily", + key: 'AUDIT_TRAIL', + title: 'Detect suspect setup actions in major org', + command: 'sf hardis:org:diagnose:audittrail', + frequency: 'daily', + }, + { + key: 'LEGACY_API', + title: 'Detect calls to deprecated API versions', + command: 'sf hardis:org:diagnose:legacyapi', + frequency: 'daily', }, { - key: "LEGACY_API", - title: "Detect calls to deprecated API versions", - command: "sfdx hardis:org:diagnose:legacyapi", - frequency: "daily", + key: 'ORG_LIMITS', + title: 'Detect if org limits are close to be reached', + command: 'sf hardis:org:monitor:limits', + frequency: 'daily', }, { - key: "ORG_LIMITS", - title: "Detect if org limits are close to be reached", - command: "sfdx hardis:org:monitor:limits", - frequency: "daily", + key: 'LICENSES', + title: 'Extract licenses information', + command: 'sf hardis:org:diagnose:licenses', + frequency: 'weekly', }, { - key: "LICENSES", - title: "Extract licenses information", - command: "sfdx hardis:org:diagnose:licenses", - frequency: "weekly", + key: 'LINT_ACCESS', + title: 'Detect custom elements with no access rights defined in permission sets', + command: 'sf hardis:lint:access', + frequency: 'weekly', }, { - key: "LINT_ACCESS", - title: "Detect custom elements with no access rights defined in permission sets", - command: "sfdx hardis:lint:access", - frequency: "weekly", + key: 'UNUSED_LICENSES', + title: 'Detect permission set licenses that are assigned to users that do not need them', + command: 'sf hardis:org:diagnose:unusedlicenses', + frequency: 'weekly', }, { - key: "UNUSED_LICENSES", - title: "Detect permission set licenses that are assigned to users that do not need them", - command: "sfdx hardis:org:diagnose:unusedlicenses", - frequency: "weekly", + key: 'UNUSED_USERS', + title: 'Detect active users without recent logins', + command: 'sf hardis:org:diagnose:unusedusers', + frequency: 'weekly', }, { - key: "UNUSED_USERS", - title: "Detect active users without recent logins", - command: "sfdx hardis:org:diagnose:unusedusers", - frequency: "weekly", + key: 'ACTIVE_USERS', + title: 'Detect active users with recent logins', + command: 'sf hardis:org:diagnose:unusedusers --returnactiveusers', + frequency: 'weekly', }, { - key: "ACTIVE_USERS", - title: "Detect active users with recent logins", - command: "sfdx hardis:org:diagnose:unusedusers --returnactiveusers", - frequency: "weekly", + key: 'ORG_INFO', + title: 'Get org info + SF instance info + next major upgrade date', + command: 'sf hardis:org:diagnose:instanceupgrade', + frequency: 'weekly', }, { - key: "ORG_INFO", - title: "Get org info + SF instance info + next major upgrade date", - command: "sfdx hardis:org:diagnose:instanceupgrade", - frequency: "weekly", + key: 'RELEASE_UPDATES', + title: 'Gather warnings about incoming and overdue Release Updates', + command: 'sf hardis:org:diagnose:releaseupdates', + frequency: 'weekly', }, { - key: "UNUSED_METADATAS", - title: "Detect custom labels and custom permissions that are not in use", - command: "sfdx hardis:lint:unusedmetadatas", - frequency: "weekly", + key: 'UNUSED_METADATAS', + title: 'Detect custom labels and custom permissions that are not in use', + command: 'sf hardis:lint:unusedmetadatas', + frequency: 'weekly', }, { - key: "METADATA_STATUS", - title: "Detect inactive metadata", - command: "sfdx hardis:lint:metadatastatus", - frequency: "weekly", + key: 'METADATA_STATUS', + title: 'Detect inactive metadata', + command: 'sf hardis:lint:metadatastatus', + frequency: 'weekly', }, { - key: "MISSING_ATTRIBUTES", - title: "Detect missing description on custom field", - command: "sfdx hardis:lint:missingattributes", - frequency: "weekly", + key: 'MISSING_ATTRIBUTES', + title: 'Detect missing description on custom field', + command: 'sf hardis:lint:missingattributes', + frequency: 'weekly', }, ]; - const config = await getConfig("user"); + const config = await getConfig('user'); const commands = monitoringCommandsDefault.concat(config.monitoringCommands || []); - const monitoringDisable = config.monitoringDisable ?? (process.env?.MONITORING_DISABLE ? process.env.MONITORING_DISABLE.split(",") : []); + const monitoringDisable = + config.monitoringDisable ?? (process.env?.MONITORING_DISABLE ? process.env.MONITORING_DISABLE.split(',') : []); let success = true; - const commandsSummary = []; + const commandsSummary: any[] = []; for (const command of commands) { if (monitoringDisable.includes(command.key)) { uxLog(this, c.grey(`Skipped command ${c.bold(command.key)} according to custom configuration`)); continue; } - if (command?.frequency === "weekly" && new Date().getDay() !== 6 && getEnvVar("MONITORING_IGNORE_FREQUENCY") !== "true") { - uxLog(this, c.grey(`Skipped command ${c.bold(command.key)} as its frequency is defined as weekly and we are not Saturday`)); + if ( + command?.frequency === 'weekly' && + new Date().getDay() !== 6 && + getEnvVar('MONITORING_IGNORE_FREQUENCY') !== 'true' + ) { + uxLog( + this, + c.grey(`Skipped command ${c.bold(command.key)} as its frequency is defined as weekly and we are not Saturday`) + ); continue; } // Run command @@ -193,31 +199,36 @@ You can force the daily run of all commands by defining env var \`MONITORING_IGN } commandsSummary.push({ title: command.title, - status: execCommandResult.status === 0 ? "success" : "failure", + status: execCommandResult.status === 0 ? 'success' : 'failure', command: command.command, }); } catch (e) { // Handle unexpected failure success = false; - uxLog(this, c.yellow(`Command ${c.bold(command.title)} has failed !\n${e.message}`)); + uxLog(this, c.yellow(`Command ${c.bold(command.title)} has failed !\n${(e as Error).message}`)); commandsSummary.push({ title: command.title, - status: "error", + status: 'error', command: command.command, }); } } - uxLog(this, c.cyan("Summary of monitoring scripts")); + uxLog(this, c.cyan('Summary of monitoring scripts')); console.table(commandsSummary); - uxLog(this, c.cyan("You can check details in reports in Job Artifacts")); + uxLog(this, c.cyan('You can check details in reports in Job Artifacts')); - uxLog(this, c.yellow("To know more about sfdx-hardis monitoring, please check https://sfdx-hardis.cloudity.com/salesforce-monitoring-home/")); + uxLog( + this, + c.yellow( + `To know more about sfdx-hardis monitoring, please check ${CONSTANTS.DOC_URL_ROOT}/salesforce-monitoring-home/` + ) + ); // Exit code is 1 if monitoring detected stuff if (success === false) { process.exitCode = 1; } - return { outputString: "Monitoring processed on org " + this.org.getConnection().instanceUrl }; + return { outputString: 'Monitoring processed on org ' + flags['target-org'].getConnection().instanceUrl }; } } diff --git a/src/commands/hardis/org/monitor/backup.ts b/src/commands/hardis/org/monitor/backup.ts index e38c2f84c..c8950e041 100644 --- a/src/commands/hardis/org/monitor/backup.ts +++ b/src/commands/hardis/org/monitor/backup.ts @@ -1,29 +1,25 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; -import { buildOrgManifest } from "../../../../common/utils/deployUtils"; -import { execCommand, filterPackageXml, uxLog } from "../../../../common/utils"; -import { MetadataUtils } from "../../../../common/metadata-utils"; -import { CONSTANTS } from "../../../../config"; -import { NotifProvider, NotifSeverity } from "../../../../common/notifProvider"; -import { MessageAttachment } from "@slack/web-api"; -import { getNotificationButtons, getOrgMarkdown, getSeverityIcon } from "../../../../common/utils/notifUtils"; -import { generateCsvFile, generateReportPath } from "../../../../common/utils/filesUtils"; -import { parsePackageXmlFile, writePackageXmlFile } from "../../../../common/utils/xmlUtils"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class MonitorBackup extends SfdxCommand { - public static title = "Backup DX sources"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; +import { buildOrgManifest } from '../../../../common/utils/deployUtils.js'; +import { execCommand, filterPackageXml, uxLog } from '../../../../common/utils/index.js'; +import { MetadataUtils } from '../../../../common/metadata-utils/index.js'; +import { CONSTANTS } from '../../../../config/index.js'; +import { NotifProvider, NotifSeverity } from '../../../../common/notifProvider/index.js'; +import { MessageAttachment } from '@slack/web-api'; +import { getNotificationButtons, getOrgMarkdown, getSeverityIcon } from '../../../../common/utils/notifUtils.js'; +import { generateCsvFile, generateReportPath } from '../../../../common/utils/filesUtils.js'; +import { parsePackageXmlFile, writePackageXmlFile } from '../../../../common/utils/xmlUtils.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class MonitorBackup extends SfCommand { + public static title = 'Backup DX sources'; public static description = `Retrieve sfdx sources in the context of a monitoring backup @@ -35,46 +31,38 @@ You can remove more metadata types from backup, especially in case you have too - Environment variable MONITORING_BACKUP_SKIP_METADATA_TYPES (example: \`MONITORING_BACKUP_SKIP_METADATA_TYPES=CustomLabel,StaticResource,Translation\`): that will be applied to all monitoring branches. -This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-metadata-backup/) and can output Grafana, Slack and MsTeams Notifications. +This command is part of [sfdx-hardis Monitoring](${CONSTANTS.DOC_URL_ROOT}/salesforce-monitoring-metadata-backup/) and can output Grafana, Slack and MsTeams Notifications. `; - public static examples = ["$ sfdx hardis:org:monitor:backup"]; + public static examples = ['$ sf hardis:org:monitor:backup']; - protected static flagsConfig = { - outputfile: flags.string({ - char: "o", - description: "Force the path and name of output report file. Must end with .csv", + public static flags: any = { + outputfile: Flags.string({ + char: 'o', + description: 'Force the path and name of output report file. Must end with .csv', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; - - // List required plugins, their presence will be tested before running the command - protected static requiresSfdxPlugins = ["sfdx-essentials"]; + public static requiresProject = true; // Trigger notification(s) to MsTeams channel protected static triggerNotification = true; - protected diffFiles = []; - protected diffFilesSimplified = []; + protected diffFiles: any[] = []; + protected diffFilesSimplified: any[] = []; protected outputFile; protected outputFilesRes: any = {}; protected debugMode = false; @@ -82,20 +70,29 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co /* jscpd:ignore-end */ public async run(): Promise { - this.outputFile = this.flags.outputfile || null; - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(MonitorBackup); + this.outputFile = flags.outputfile || null; + this.debugMode = flags.debug || false; // Build target org full manifest - uxLog(this, c.cyan("Building full manifest for org " + c.bold(this.org.getConnection().instanceUrl)) + " ..."); - const packageXmlFullFile = "manifest/package-all-org-items.xml"; - await buildOrgManifest("", packageXmlFullFile, this.org.getConnection()); + uxLog( + this, + c.cyan('Building full manifest for org ' + c.bold(flags['target-org'].getConnection().instanceUrl)) + ' ...' + ); + const packageXmlFullFile = 'manifest/package-all-org-items.xml'; + await buildOrgManifest('', packageXmlFullFile, flags['target-org'].getConnection()); // Check if we have package-skip_items.xml - const packageXmlBackUpItemsFile = "manifest/package-backup-items.xml"; - const packageXmlSkipItemsFile = "manifest/package-skip-items.xml"; - let packageXmlToRemove = null; + const packageXmlBackUpItemsFile = 'manifest/package-backup-items.xml'; + const packageXmlSkipItemsFile = 'manifest/package-skip-items.xml'; + let packageXmlToRemove: string | null = null; if (fs.existsSync(packageXmlSkipItemsFile)) { - uxLog(this, c.grey(`${packageXmlSkipItemsFile} has been found and will be use to reduce the content of ${packageXmlFullFile} ...`)); + uxLog( + this, + c.grey( + `${packageXmlSkipItemsFile} has been found and will be use to reduce the content of ${packageXmlFullFile} ...` + ) + ); packageXmlToRemove = packageXmlSkipItemsFile; } @@ -105,25 +102,25 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co uxLog( this, c.grey( - `En var MONITORING_BACKUP_SKIP_METADATA_TYPES has been found and will also be used to reduce the content of ${packageXmlFullFile} ...`, - ), + `En var MONITORING_BACKUP_SKIP_METADATA_TYPES has been found and will also be used to reduce the content of ${packageXmlFullFile} ...` + ) ); let packageSkipItems = {}; - if (fs.existsSync(packageXmlToRemove)) { - packageSkipItems = await parsePackageXmlFile(packageXmlToRemove); + if (fs.existsSync(packageXmlToRemove || '')) { + packageSkipItems = await parsePackageXmlFile(packageXmlToRemove || ''); } - for (const metadataType of additionalSkipMetadataTypes.split(",")) { - packageSkipItems[metadataType] = ["*"]; + for (const metadataType of additionalSkipMetadataTypes.split(',')) { + packageSkipItems[metadataType] = ['*']; } - packageXmlToRemove = "manifest/package-skip-items-dynamic-do-not-update-manually.xml"; + packageXmlToRemove = 'manifest/package-skip-items-dynamic-do-not-update-manually.xml'; await writePackageXmlFile(packageXmlToRemove, packageSkipItems); } // List namespaces used in the org - const namespaces = []; + const namespaces: any[] = []; const installedPackages = await MetadataUtils.listInstalledPackages(null, this); for (const installedPackage of installedPackages) { - if (installedPackage?.SubscriberPackageNamespace !== "" && installedPackage?.SubscriberPackageNamespace != null) { + if (installedPackage?.SubscriberPackageNamespace !== '' && installedPackage?.SubscriberPackageNamespace != null) { namespaces.push(installedPackage.SubscriberPackageNamespace); } } @@ -140,34 +137,45 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co // Retrieve sfdx sources in local git repo uxLog(this, c.cyan(`Run the retrieve command for retrieving filtered metadatas ...`)); try { - await execCommand(`sfdx force:source:retrieve -x ${packageXmlBackUpItemsFile} -u ${this.org.getUsername()} --wait 120`, this, { - fail: true, - output: true, - debug: this.debugMode, - }); + await execCommand( + `sf project retrieve start -x ${packageXmlBackUpItemsFile} -o ${flags['target-org'].getUsername()} --ignore-conflicts --wait 120`, + this, + { + fail: true, + output: true, + debug: this.debugMode, + } + ); } catch (e) { - const failedPackageXmlContent = await fs.readFile(packageXmlBackUpItemsFile, "utf8"); - uxLog(this, c.yellow("BackUp package.xml that failed to be retrieved:\n" + c.grey(failedPackageXmlContent))); + const failedPackageXmlContent = await fs.readFile(packageXmlBackUpItemsFile, 'utf8'); + uxLog(this, c.yellow('BackUp package.xml that failed to be retrieved:\n' + c.grey(failedPackageXmlContent))); uxLog( this, c.red( c.bold( - "Crash during backup. You may exclude more metadata types by updating file manifest/package-skip-items.xml then commit and push it, or use variable NOTIFICATIONS_DISABLE", - ), - ), + 'Crash during backup. You may exclude more metadata types by updating file manifest/package-skip-items.xml then commit and push it, or use variable NOTIFICATIONS_DISABLE' + ) + ) + ); + uxLog( + this, + c.yellow( + c.bold( + `See troubleshooting doc at ${CONSTANTS.DOC_URL_ROOT}/salesforce-monitoring-config-home/#troubleshooting` + ) + ) ); - uxLog(this, c.yellow(c.bold("See troubleshooting doc at https://sfdx-hardis.cloudity.com/salesforce-monitoring-config-home/#troubleshooting"))); throw e; } // Write installed packages uxLog(this, c.cyan(`Write installed packages ...`)); - const installedPackagesLog = []; - const packageFolder = path.join(process.cwd(), "installedPackages"); + const installedPackagesLog: any[] = []; + const packageFolder = path.join(process.cwd(), 'installedPackages'); await fs.ensureDir(packageFolder); for (const installedPackage of installedPackages) { - const fileName = (installedPackage.SubscriberPackageName || installedPackage.SubscriberPackageId) + ".json"; - const fileNameNoSep = fileName.replace(/\//g, "_").replace(/:/g, "_"); // Handle case when package name contains slashes or colon + const fileName = (installedPackage.SubscriberPackageName || installedPackage.SubscriberPackageId) + '.json'; + const fileNameNoSep = fileName.replace(/\//g, '_').replace(/:/g, '_'); // Handle case when package name contains slashes or colon delete installedPackage.Id; // Not needed for diffs await fs.writeFile(path.join(packageFolder, fileNameNoSep), JSON.stringify(installedPackage, null, 2)); const installedPackageLog = { @@ -184,15 +192,17 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co // Write output file if (this.diffFiles.length > 0) { - const severityIconLog = getSeverityIcon("log"); - this.outputFile = await generateReportPath("backup-updated-files", this.outputFile); + const filesHumanUnformatted = MetadataUtils.getMetadataPrettyNames(this.diffFiles.map((diffFile) => diffFile.path), false); + const severityIconLog = getSeverityIcon('log'); + this.outputFile = await generateReportPath('backup-updated-files', this.outputFile); this.diffFilesSimplified = this.diffFiles.map((diffFile) => { return { - File: diffFile.path.replace("force-app/main/default/", ""), - ChangeType: diffFile.index === "?" ? "A" : diffFile.index, - WorkingDir: diffFile.working_dir === "?" ? "" : diffFile.working_dir, - PrevName: diffFile?.from || "", - severity: "log", + File: diffFile.path.replace('force-app/main/default/', ''), + ChangeType: diffFile.index === '?' ? 'A' : diffFile.index, + FileHuman: filesHumanUnformatted.get(diffFile.path) || diffFile.path.replace('force-app/main/default/', ''), + WorkingDir: diffFile.working_dir === '?' ? '' : diffFile.working_dir, + PrevName: diffFile?.from || '', + severity: 'log', severityIcon: severityIconLog, }; }); @@ -200,26 +210,27 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co } // Build notifications - const orgMarkdown = await getOrgMarkdown(this.org?.getConnection()?.instanceUrl); + const orgMarkdown = await getOrgMarkdown(flags['target-org']?.getConnection()?.instanceUrl); const notifButtons = await getNotificationButtons(); - let notifSeverity: NotifSeverity = "log"; + let notifSeverity: NotifSeverity = 'log'; let notifText = `No updates detected in ${orgMarkdown}`; let notifAttachments: MessageAttachment[] = []; if (this.diffFiles.length > 0) { - notifSeverity = "info"; + const filesHumanFormatted = MetadataUtils.getMetadataPrettyNames(this.diffFiles.map((diffFile) => diffFile.path), true); + notifSeverity = 'info'; notifText = `Updates detected in ${orgMarkdown}`; notifAttachments = [ { text: this.diffFiles .map((diffFile) => { - let flag = ""; - if (diffFile.index && diffFile.index !== " ") { - flag = ` (${diffFile.index === "?" ? "A" : diffFile.index})`; + let flag = ''; + if (diffFile.index && diffFile.index !== ' ') { + flag = ` (${diffFile.index === '?' ? 'A' : diffFile.index})`; } - const line = `• ${diffFile.path.replace("force-app/main/default/", "")}` + flag; + const line = `• ${filesHumanFormatted.get(diffFile.path)}` + flag; return line; }) - .join("\n"), + .join('\n'), }, ]; } else { @@ -227,14 +238,14 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co } // Post notifications - globalThis.jsForceConn = this?.org?.getConnection(); // Required for some notifications providers like Email + globalThis.jsForceConn = flags['target-org']?.getConnection(); // Required for some notifications providers like Email NotifProvider.postNotifications({ - type: "BACKUP", + type: 'BACKUP', text: notifText, buttons: notifButtons, attachments: notifAttachments, severity: notifSeverity, - sideImage: "backup", + sideImage: 'backup', attachedFiles: this.outputFilesRes.xlsxFile ? [this.outputFilesRes.xlsxFile] : [], logElements: this.diffFilesSimplified, data: { @@ -246,6 +257,7 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co }, }); - return { outputString: "BackUp processed on org " + this.org.getConnection().instanceUrl }; + return { outputString: 'BackUp processed on org ' + flags['target-org'].getConnection().instanceUrl }; } + } diff --git a/src/commands/hardis/org/monitor/limits.ts b/src/commands/hardis/org/monitor/limits.ts index 40af39601..155d19335 100644 --- a/src/commands/hardis/org/monitor/limits.ts +++ b/src/commands/hardis/org/monitor/limits.ts @@ -1,66 +1,57 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { execSfdxJson, uxLog } from "../../../../common/utils"; -import { getEnvVar } from "../../../../config"; -import { NotifProvider, NotifSeverity } from "../../../../common/notifProvider"; -import { MessageAttachment } from "@slack/web-api"; -import { getNotificationButtons, getOrgMarkdown, getSeverityIcon } from "../../../../common/utils/notifUtils"; -import { generateCsvFile, generateReportPath } from "../../../../common/utils/filesUtils"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class MonitorBackup extends SfdxCommand { - public static title = "Check org limits"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { execSfdxJson, uxLog } from '../../../../common/utils/index.js'; +import { CONSTANTS, getEnvVar } from '../../../../config/index.js'; +import { NotifProvider, NotifSeverity } from '../../../../common/notifProvider/index.js'; +import { MessageAttachment } from '@slack/web-api'; +import { getNotificationButtons, getOrgMarkdown, getSeverityIcon } from '../../../../common/utils/notifUtils.js'; +import { generateCsvFile, generateReportPath } from '../../../../common/utils/filesUtils.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class MonitorLimits extends SfCommand { + public static title = 'Check org limits'; public static description = `Check limits of a SF org and send notifications about limits are superior to 50%, 75% or 100%. -This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-org-limits/) and can output Grafana, Slack and MsTeams Notifications. +This command is part of [sfdx-hardis Monitoring](${CONSTANTS.DOC_URL_ROOT}/salesforce-monitoring-org-limits/) and can output Grafana, Slack and MsTeams Notifications. `; - public static examples = ["$ sfdx hardis:org:monitor:limits"]; + public static examples = ['$ sf hardis:org:monitor:limits']; - protected static flagsConfig = { - outputfile: flags.string({ - char: "o", - description: "Force the path and name of output report file. Must end with .csv", + public static flags: any = { + outputfile: Flags.string({ + char: 'o', + description: 'Force the path and name of output report file. Must end with .csv', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; // Trigger notification(s) to MsTeams channel protected static triggerNotification = true; - protected limitThresholdWarning = Number(getEnvVar("LIMIT_THRESHOLD_WARNING") || 50.0); - protected limitThresholdError = Number(getEnvVar("LIMIT_THRESHOLD_WARNING") || 75.0); + protected limitThresholdWarning = Number(getEnvVar('LIMIT_THRESHOLD_WARNING') || 50.0); + protected limitThresholdError = Number(getEnvVar('LIMIT_THRESHOLD_WARNING') || 75.0); - protected limitEntries = []; + protected limitEntries: any[] = []; protected outputFile; protected outputFilesRes: any = {}; protected debugMode = false; @@ -68,8 +59,9 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co /* jscpd:ignore-end */ public async run(): Promise { - this.outputFile = this.flags.outputfile || null; - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(MonitorLimits); + this.outputFile = flags.outputfile || null; + this.debugMode = flags.debug || false; // List org limits uxLog(this, c.cyan(`Run the org limits list command ...`)); @@ -88,59 +80,63 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co limit.percentUsed = 0.0; } limit.severity = - limit.percentUsed > this.limitThresholdError ? "error" : limit.percentUsed > this.limitThresholdWarning ? "warning" : "success"; + limit.percentUsed > this.limitThresholdError + ? 'error' + : limit.percentUsed > this.limitThresholdWarning + ? 'warning' + : 'success'; limit.severityIcon = getSeverityIcon(limit.severity); - limit.label = limit.name.replace(/([A-Z])/g, " $1"); + limit.label = limit.name.replace(/([A-Z])/g, ' $1'); return limit; }); console.table(this.limitEntries); - this.outputFile = await generateReportPath("org-limits", this.outputFile); + this.outputFile = await generateReportPath('org-limits', this.outputFile); this.outputFilesRes = await generateCsvFile(this.limitEntries, this.outputFile); - const limitsError = this.limitEntries.filter((limit) => limit.severity === "error"); + const limitsError = this.limitEntries.filter((limit) => limit.severity === 'error'); const numberLimitsError = limitsError.length; - const limitsWarning = this.limitEntries.filter((limit) => limit.severity === "warning"); + const limitsWarning = this.limitEntries.filter((limit) => limit.severity === 'warning'); const numberLimitsWarning = limitsWarning.length; // Build notifications - const orgMarkdown = await getOrgMarkdown(this.org?.getConnection()?.instanceUrl); + const orgMarkdown = await getOrgMarkdown(flags['target-org']?.getConnection()?.instanceUrl); const notifButtons = await getNotificationButtons(); - let notifSeverity: NotifSeverity = "log"; + let notifSeverity: NotifSeverity = 'log'; let notifText = `No limit issues detected in ${orgMarkdown}`; const notifAttachments: MessageAttachment[] = []; // Dangerous limits has been found if (numberLimitsError > 0) { - notifSeverity = "error"; + notifSeverity = 'error'; notifText = `Limit severe alerts have been detected in ${orgMarkdown} (error: ${numberLimitsError}, warning: ${numberLimitsWarning})`; const errorText = `*Error Limits*\n${limitsError .map((limit) => { - return `• ${limit.name}: ${limit.percentUsed}% used (${limit.used}/${limit.max})`; + return `• ${limit.name}: *${limit.percentUsed}%* used (${limit.used}/${limit.max})`; }) - .join("\n")}`; + .join('\n')}`; notifAttachments.push({ text: errorText, }); - uxLog(this, c.red(notifText + "\n" + errorText)); + uxLog(this, c.red(notifText + '\n' + errorText)); process.exitCode = 1; } // Warning limits detected else if (numberLimitsWarning > 0) { - notifSeverity = "warning"; + notifSeverity = 'warning'; notifText = `Limit warning alerts have been detected in ${orgMarkdown} (${numberLimitsWarning})`; const warningText = `*Warning Limits*\n${limitsWarning .map((limit) => { - return `• ${limit.name}: ${limit.percentUsed}% used (${limit.used}/${limit.max})`; + return `• ${limit.name}: *${limit.percentUsed}%* used (${limit.used}/${limit.max})`; }) - .join("\n")}`; + .join('\n')}`; notifAttachments.push({ text: warningText, }); - uxLog(this, c.yellow(notifText + "\n" + warningText)); + uxLog(this, c.yellow(notifText + '\n' + warningText)); } else { - uxLog(this, c.green("No limit issue has been found")); + uxLog(this, c.green('No limit issue has been found')); } const limitEntriesMap = {}; @@ -155,9 +151,9 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co } // Post notifications - globalThis.jsForceConn = this?.org?.getConnection(); // Required for some notifications providers like Email + globalThis.jsForceConn = flags['target-org']?.getConnection(); // Required for some notifications providers like Email NotifProvider.postNotifications({ - type: "ORG_LIMITS", + type: 'ORG_LIMITS', text: notifText, buttons: notifButtons, attachments: notifAttachments, @@ -168,6 +164,9 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co metrics: limitMetricsMap, }); - return { outputString: "Limits check on org " + this.org.getConnection().instanceUrl, limitEntries: this.limitEntries }; + return { + outputString: 'Limits check on org ' + flags['target-org'].getConnection().instanceUrl, + limitEntries: this.limitEntries, + }; } } diff --git a/src/commands/hardis/org/purge/apexlog.ts b/src/commands/hardis/org/purge/apexlog.ts index 21e10a428..c97a34db0 100644 --- a/src/commands/hardis/org/purge/apexlog.ts +++ b/src/commands/hardis/org/purge/apexlog.ts @@ -1,90 +1,87 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; -import { execCommand, uxLog } from "../../../../common/utils"; -import { prompts } from "../../../../common/utils/prompts"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; +import { execCommand, uxLog } from '../../../../common/utils/index.js'; +import { prompts } from '../../../../common/utils/prompts.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class PurgeApexLogs extends SfCommand { + public static title = 'Purge Apex Logs'; -export default class OrgPurgeFlow extends SfdxCommand { - public static title = "Purge Apex Logs"; + public static description = 'Purge apex logs in selected org'; - public static description = "Purge apex logs in selected org"; - - public static examples = [`$ sfdx hardis:org:purge:apexlog`, `$ sfdx hardis:org:purge:apexlog --targetusername nicolas.vuillamy@gmail.com`]; + public static examples = [ + `$ sf hardis:org:purge:apexlog`, + `$ sf hardis:org:purge:apexlog --target-org nicolas.vuillamy@gmail.com`, + ]; // public static args = [{name: 'file'}]; - protected static flagsConfig = { + public static flags: any = { // flag with a value (-n, --name=VALUE) - prompt: flags.boolean({ - char: "z", + prompt: Flags.boolean({ + char: 'z', default: true, allowNo: true, - description: messages.getMessage("prompt"), + description: messages.getMessage('prompt'), }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; /* jscpd:ignore-end */ public async run(): Promise { - const prompt = this.flags.prompt === false ? false : true; - const debugMode = this.flags.debug || false; + const { flags } = await this.parse(PurgeApexLogs); + const prompt = flags.prompt === false ? false : true; + const debugMode = flags.debug || false; // Build apex logs query - const tempDir = "./tmp"; + const tempDir = './tmp'; await fs.ensureDir(tempDir); - const apexLogsToDeleteCsv = path.join(tempDir, "ApexLogsToDelete_" + Math.random() + ".csv"); - const queryCommand = `sfdx force:data:soql:query -q "SELECT Id FROM ApexLog" -t -r "csv" > "${apexLogsToDeleteCsv}"`; + const apexLogsToDeleteCsv = path.join(tempDir, 'ApexLogsToDelete_' + Math.random() + '.csv'); + const queryCommand = `sf data query --query "SELECT Id FROM ApexLog LIMIT 50000" -t -r "csv" > "${apexLogsToDeleteCsv}"`; await execCommand(queryCommand, this, { output: true, debug: debugMode, fail: true, }); - const extractFile = (await fs.readFile(apexLogsToDeleteCsv, "utf8")).toString(); - const apexLogsNumber = extractFile.split("\n").filter((line) => line.length > 0).length; + const extractFile = (await fs.readFile(apexLogsToDeleteCsv, 'utf8')).toString(); + const apexLogsNumber = extractFile.split('\n').filter((line) => line.length > 0).length; if (apexLogsNumber === 0) { - uxLog(this, c.cyan(`There are no Apex Logs to delete in org ${c.green(this.org.getUsername())}`)); + uxLog(this, c.cyan(`There are no Apex Logs to delete in org ${c.green(flags['target-org'].getUsername())}`)); return {}; } // Prompt confirmation if (prompt) { const confirmRes = await prompts({ - type: "confirm", - name: "value", - message: `Do you want to delete ${c.bold(apexLogsNumber)} Apex Logs of org ${c.green(this.org.getUsername())} ?`, + type: 'confirm', + name: 'value', + message: `Do you want to delete ${c.bold(apexLogsNumber)} Apex Logs of org ${c.green( + flags['target-org'].getUsername() + )} ?`, }); if (confirmRes.value === false) { return {}; @@ -92,16 +89,21 @@ export default class OrgPurgeFlow extends SfdxCommand { } // Perform delete - const deleteCommand = `sfdx force:data:bulk:delete -s ApexLog -f ${apexLogsToDeleteCsv}`; + const deleteCommand = `sf data delete bulk --sobject ApexLog --file ${apexLogsToDeleteCsv}`; await execCommand(deleteCommand, this, { output: true, debug: debugMode, fail: true, }); - uxLog(this, c.green(`Successfully deleted ${c.bold(apexLogsNumber)} Apex Logs in org ${c.bold(this.org.getUsername())}`)); + uxLog( + this, + c.green( + `Successfully deleted ${c.bold(apexLogsNumber)} Apex Logs in org ${c.bold(flags['target-org'].getUsername())}` + ) + ); // Return an object to be displayed with --json - return { orgId: this.org.getOrgId() }; + return { orgId: flags['target-org'].getOrgId() }; } } diff --git a/src/commands/hardis/org/purge/flow.ts b/src/commands/hardis/org/purge/flow.ts index 5e0e6efb3..0255770dc 100644 --- a/src/commands/hardis/org/purge/flow.ts +++ b/src/commands/hardis/org/purge/flow.ts @@ -1,28 +1,24 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages, SfdxError } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as columnify from "columnify"; -import { execSfdxJson, isCI, uxLog } from "../../../../common/utils"; -import { prompts } from "../../../../common/utils/prompts"; -import { bulkDeleteTooling } from "../../../../common/utils/apiUtils"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages, SfError } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import columnify from 'columnify'; +import { execSfdxJson, extractRegexMatches, isCI, uxLog } from '../../../../common/utils/index.js'; +import { prompts } from '../../../../common/utils/prompts.js'; +import { bulkDelete, bulkDeleteTooling, bulkQuery } from '../../../../common/utils/apiUtils.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class OrgPurgeFlow extends SfCommand { + public static title = 'Purge Flow versions'; -export default class OrgPurgeFlow extends SfdxCommand { - public static title = "Purge Flow versions"; - - public static description = messages.getMessage("orgPurgeFlow"); + public static description = messages.getMessage('orgPurgeFlow'); public static examples = [ - `$ sfdx hardis:org:purge:flow --no-prompt`, - `$ sfdx hardis:org:purge:flow --targetusername nicolas.vuillamy@gmail.com + `$ sf hardis:org:purge:flow --no-prompt`, + `$ sf hardis:org:purge:flow --target-org nicolas.vuillamy@gmail.com Found 1 records: ID MASTERLABEL VERSIONNUMBER DESCRIPTION STATUS 30109000000kX7uAAE TestFlow 2 test flowwww Obsolete @@ -32,7 +28,7 @@ export default class OrgPurgeFlow extends SfdxCommand { ID MASTERLABEL VERSIONNUMBER DESCRIPTION STATUS 30109000000kX7uAAE TestFlow 2 test flowwww Obsolete `, - `$ sfdx hardis:org:purge:flow --targetusername nicolas.vuillamy@gmail.com --status "Obsolete,Draft,InvalidDraft --name TestFlow" + `$ sf hardis:org:purge:flow --target-org nicolas.vuillamy@gmail.com --status "Obsolete,Draft,InvalidDraft --name TestFlow" Found 4 records: ID MASTERLABEL VERSIONNUMBER DESCRIPTION STATUS 30109000000kX7uAAE TestFlow 2 test flowwww Obsolete @@ -46,197 +42,277 @@ export default class OrgPurgeFlow extends SfdxCommand { // public static args = [{name: 'file'}]; - protected static flagsConfig = { + public static flags: any = { // flag with a value (-n, --name=VALUE) - prompt: flags.boolean({ - char: "z", + prompt: Flags.boolean({ + char: 'z', default: true, allowNo: true, - description: messages.getMessage("prompt"), + description: messages.getMessage('prompt'), + }), + name: Flags.string({ + char: 'n', + description: messages.getMessage('nameFilter'), }), - name: flags.string({ - char: "n", - description: messages.getMessage("nameFilter"), + status: Flags.string({ + char: 's', + description: messages.getMessage('statusFilter'), }), - status: flags.string({ - char: "s", - description: messages.getMessage("statusFilter"), + "delete-flow-interviews": Flags.boolean({ + char: 'f', + default: false, + description: `If the presence of Flow interviews prevent to delete flows versions, delete them before retrying to delete flow versions`, }), - allowpurgefailure: flags.boolean({ - char: "f", + allowpurgefailure: Flags.boolean({ + char: 'f', default: true, allowNo: true, - description: messages.getMessage("allowPurgeFailure"), + description: messages.getMessage('allowPurgeFailure'), }), - instanceurl: flags.string({ - char: "r", - default: "https://login.salesforce.com", - description: messages.getMessage("instanceUrl"), + instanceurl: Flags.string({ + char: 'r', + default: 'https://login.salesforce.com', + description: messages.getMessage('instanceUrl'), }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; + protected debugMode = false; + protected statusFilter: string[] = []; + protected nameFilter: string | null = null; + protected username: string; + protected deleteFlowInterviews: boolean; + protected allowPurgeFailure: boolean; + protected flowRecordsRaw: any[]; + protected flowRecords: any[]; + protected deletedRecords: any[] = []; + protected deletedErrors: any[] = []; /* jscpd:ignore-end */ public async run(): Promise { - const prompt = this.flags.prompt === false ? false : true; - let nameFilter = this.flags.name || null; - const allowPurgeFailure = this.flags.allowpurgefailure === false ? false : true; - const debugMode = this.flags.debug || false; - const username = this.org.getUsername(); - - let statusFilter; - const manageableConstraint = "ManageableState IN ('deprecatedEditable','installedEditable','unmanaged')"; - if (this.flags.status) { - // Input parameter used - statusFilter = this.flags.status.split(","); - } else if (isCI) { - // Obsolete by default for CI - statusFilter = ["Obsolete"]; - } else { - // Query all flows definitions - const allFlowQueryCommand = - "sfdx force:data:soql:query " + - ` -q "SELECT Id,DeveloperName,MasterLabel,ManageableState FROM FlowDefinition WHERE ${manageableConstraint} ORDER BY DeveloperName"` + - ` --targetusername ${username}` + - " --usetoolingapi"; - const allFlowQueryRes = await execSfdxJson(allFlowQueryCommand, this, { - output: false, - debug: debugMode, - fail: true, - }); - const flowRecordsRaw = allFlowQueryRes?.result?.records || allFlowQueryRes.records || []; - const flowNamesUnique = [...new Set(flowRecordsRaw.map((flowRecord) => flowRecord.DeveloperName))]; - const flowNamesChoice = flowNamesUnique.map((flowName) => { - return { title: flowName, value: flowName }; - }); - flowNamesChoice.unshift({ title: "All flows", value: "all" }); + const { flags } = await this.parse(OrgPurgeFlow); + const prompt = flags.prompt === false ? false : true; + this.nameFilter = flags.name || null; + this.allowPurgeFailure = flags.allowpurgefailure === false ? false : true; + this.deleteFlowInterviews = flags["delete-flow-interviews"] || false; + this.debugMode = flags.debug || false; + this.username = flags['target-org'].getUsername(); - // Manually select status - const selectStatus = await prompts([ - { - type: "select", - name: "name", - message: "Please select the flow you want to clean", - choices: flowNamesChoice, - }, - { - type: "multiselect", - name: "status", - message: "Please select the status(es) you want to delete", - choices: [ - { title: `Draft`, value: "Draft" }, - { title: `Inactive`, value: "Inactive" }, - { title: `Obsolete`, value: "Obsolete" }, - ], - }, - ]); - nameFilter = selectStatus.name; - statusFilter = selectStatus.status; - } + // List flows to delete, prompt user if not in CI and not send as arguments + const manageableConstraint = await this.getFlowsScope(flags); // Check we don't delete active Flows - if (statusFilter.includes("Active")) { - throw new SfdxError("You can not delete active records"); + if (this.statusFilter.includes('Active')) { + throw new SfError('You can not delete active records'); } // Build query with name filter if sent - let query = `SELECT Id,MasterLabel,VersionNumber,Status,Description,Definition.DeveloperName FROM Flow WHERE ${manageableConstraint} AND Status IN ('${statusFilter.join( - "','", - )}')`; - if (nameFilter && nameFilter != "all") { - query += ` AND Definition.DeveloperName = '${nameFilter}'`; - } - query += " ORDER BY Definition.DeveloperName,VersionNumber"; - - const flowQueryCommand = "sfdx force:data:soql:query " + ` -q "${query}"` + ` --targetusername ${username}` + " --usetoolingapi"; - const flowQueryRes = await execSfdxJson(flowQueryCommand, this, { - output: false, - debug: debugMode, - fail: true, - }); - const recordsRaw = flowQueryRes?.result?.records || flowQueryRes.records || []; + await this.listFlowVersionsToDelete(manageableConstraint); // Check empty result - if (recordsRaw.length === 0) { - const outputString = `[sfdx-hardis] No matching Flow records found with query ${query}`; + if (this.flowRecordsRaw.length === 0) { + const outputString = `[sfdx-hardis] No matching Flow records found`; uxLog(this, c.yellow(outputString)); return { deleted: [], outputString }; } // Simplify results format & display them - const records = recordsRaw.map((record: any) => { - return { - Id: record.Id, - MasterLabel: record.MasterLabel, - VersionNumber: record.VersionNumber, - DefinitionDevName: record.Definition.DeveloperName, - Status: record.Status, - Description: record.Description, - }; - }); - - uxLog(this, `[sfdx-hardis] Found ${c.bold(records.length)} records:\n${c.yellow(columnify(records))}`); + this.formatFlowRecords(); // Confirm deletion if (prompt) { const confirmDelete = await prompts({ - type: "confirm", - name: "value", - message: c.cyanBright(`Do you confirm you want to delete these ${records.length} flow versions ?`), + type: 'confirm', + name: 'value', + message: c.cyanBright(`Do you confirm you want to delete these ${this.flowRecords.length} flow versions ?`), }); if (confirmDelete.value === false) { - uxLog(this, c.magenta("Action cancelled by user")); - return { outputString: "Action cancelled by user" }; + uxLog(this, c.magenta('Action cancelled by user')); + return { outputString: 'Action cancelled by user' }; } } // Perform deletion - const deleted = []; - const deleteErrors = []; - const conn = this.org.getConnection(); - const deleteResults = await bulkDeleteTooling("Flow", records, conn); + const conn = flags['target-org'].getConnection(); + await this.processDeleteFlowVersions(conn, true); + + const summary = + this.deletedRecords.length > 0 + ? `[sfdx-hardis] Deleted the following list of record(s):\n${columnify(this.deletedRecords)}` + : '[sfdx-hardis] No record(s) to delete'; + uxLog(this, c.green(summary)); + // Return an object to be displayed with --json + return { orgId: flags['target-org'].getOrgId(), outputString: summary }; + } + + private async processDeleteFlowVersions(conn: any, tryDeleteInterviews: boolean) { + const recordsIds = this.flowRecords.map((record) => record.Id); + const deleteResults = await bulkDeleteTooling('Flow', recordsIds, conn); for (const deleteRes of deleteResults.results) { if (deleteRes.success) { - deleted.push(deleteRes); + this.deletedRecords.push(deleteRes); } else { - this.ux.error(c.red(`[sfdx-hardis] Unable to perform deletion request: ${JSON.stringify(deleteRes)}`)); - deleteErrors.push(deleteRes); + uxLog(this, c.red(`[sfdx-hardis] Unable to perform deletion request: ${JSON.stringify(deleteRes)}`)); + this.deletedErrors.push(deleteRes); } } - if (deleteErrors.length > 0) { - const errMsg = `[sfdx-hardis] There have been errors while deleting ${deleteErrors.length} record(s): \n${JSON.stringify(deleteErrors)}`; - if (allowPurgeFailure) { + if (this.deletedErrors.length > 0 && (this.deleteFlowInterviews === true || !isCI) && tryDeleteInterviews === true) { + await this.manageDeleteFlowInterviews(conn); + } + + if (this.deletedErrors.length > 0) { + const errMsg = `[sfdx-hardis] There have been errors while deleting ${this.deletedErrors.length} record(s): \n${JSON.stringify(this.deletedErrors)}`; + if (this.allowPurgeFailure) { uxLog(this, c.yellow(errMsg)); } else { - throw new SfdxError(c.yellow(`There have been errors while deleting ${deleteErrors.length} record(s): \n${JSON.stringify(deleteErrors)}`)); + throw new SfError( + c.yellow( + `There have been errors while deleting ${this.deletedErrors.length} record(s): \n${JSON.stringify(this.deletedErrors)}` + ) + ); } } + } - const summary = - deleted.length > 0 ? `[sfdx-hardis] Deleted the following list of record(s):\n${columnify(deleted)}` : "[sfdx-hardis] No record(s) to delete"; - uxLog(this, c.green(summary)); - // Return an object to be displayed with --json - return { orgId: this.org.getOrgId(), outputString: summary }; + private async manageDeleteFlowInterviews(conn: any) { + // Gather flow interviews that prevent deleting flow versions + const flowInterviewsIds: string[] = []; + this.flowRecords = []; + const extractInterviewsRegex = /Flow Interview - ([a-zA-Z0-9]{15}|[a-zA-Z0-9]{18})/gm; + for (const deletedError of this.deletedErrors) { + this.flowRecords.push({ Id: deletedError.Id }); + const errorflowInterviewIds = await extractRegexMatches(extractInterviewsRegex, deletedError.error); + flowInterviewsIds.push(...[...new Set(errorflowInterviewIds)]); // make interview Ids unique + } + if (flowInterviewsIds.length === 0) { + return; + } + // Display flows & Prompt user if not in CI + await this.displayFlowInterviewToDelete(flowInterviewsIds, conn); + if (!isCI) { + const confirmDelete = await prompts({ + type: 'confirm', + name: 'value', + message: c.cyanBright(`Do you confirm you want to delete ${flowInterviewsIds.length} Flow Interviews ?`), + }); + if (confirmDelete.value === false) { + uxLog(this, c.magenta('Action cancelled by user')); + return { outputString: 'Action cancelled by user' }; + } + } + // Delete flow interviews + const deleteInterviewResults = await bulkDelete('FlowInterview', flowInterviewsIds, conn); + this.deletedRecords.push(deleteInterviewResults?.successfulResults || []) + this.deletedErrors = deleteInterviewResults?.failedResults || []; + // Try to delete flow versions again + uxLog(this, c.cyan(`Trying again to delete flow versions after deleting flow interviews...`)); + this.flowRecords = [...new Set(this.flowRecords)]; // Make list unique + await this.processDeleteFlowVersions(conn, false); + } + + private formatFlowRecords() { + this.flowRecords = this.flowRecordsRaw.map((record: any) => { + return { + Id: record.Id, + MasterLabel: record.MasterLabel, + VersionNumber: record.VersionNumber, + DefinitionDevName: record.Definition.DeveloperName, + Status: record.Status, + Description: record.Description, + }; + }); + uxLog(this, `[sfdx-hardis] Found ${c.bold(this.flowRecords.length)} records:\n${c.yellow(columnify(this.flowRecords))}`); + } + + private async listFlowVersionsToDelete(manageableConstraint: string) { + let query = `SELECT Id,MasterLabel,VersionNumber,Status,Description,Definition.DeveloperName FROM Flow WHERE ${manageableConstraint} AND Status IN ('${this.statusFilter.join( + "','" + )}')`; + if (this.nameFilter && this.nameFilter != 'all') { + query += ` AND Definition.DeveloperName = '${this.nameFilter}'`; + } + query += ' ORDER BY Definition.DeveloperName,VersionNumber'; + + const flowQueryCommand = 'sf data query ' + ` --query "${query}"` + ` --target-org ${this.username}` + ' --use-tooling-api'; + const flowQueryRes = await execSfdxJson(flowQueryCommand, this, { + output: false, + debug: this.debugMode, + fail: true, + }); + this.flowRecordsRaw = flowQueryRes?.result?.records || flowQueryRes.records || []; + } + + private async getFlowsScope(flags) { + const manageableConstraint = "ManageableState IN ('deprecatedEditable','installedEditable','unmanaged')"; + if (flags.status) { + // Input parameter used + this.statusFilter = flags.status.split(','); + } else if (isCI) { + // Obsolete by default for CI + this.statusFilter = ['Obsolete']; + } else { + // Query all flows definitions + const allFlowQueryCommand = 'sf data query ' + + ` --query "SELECT Id,DeveloperName,MasterLabel,ManageableState FROM FlowDefinition WHERE ${manageableConstraint} ORDER BY DeveloperName"` + + ` --target-org ${this.username}` + + ' --use-tooling-api'; + const allFlowQueryRes = await execSfdxJson(allFlowQueryCommand, this, { + output: false, + debug: this.debugMode, + fail: true, + }); + const flowRecordsRaw = allFlowQueryRes?.result?.records || allFlowQueryRes.records || []; + const flowNamesUnique = [...new Set(flowRecordsRaw.map((flowRecord) => flowRecord.DeveloperName))]; + const flowNamesChoice = flowNamesUnique.map((flowName) => { + return { title: flowName, value: flowName }; + }); + flowNamesChoice.unshift({ title: 'All flows', value: 'all' }); + + // Manually select status + const selectStatus = await prompts([ + { + type: 'select', + name: 'name', + message: 'Please select the flow you want to clean', + choices: flowNamesChoice, + }, + { + type: 'multiselect', + name: 'status', + message: 'Please select the status(es) you want to delete', + choices: [ + { title: `Draft`, value: 'Draft' }, + { title: `Inactive`, value: 'Inactive' }, + { title: `Obsolete`, value: 'Obsolete' }, + ], + }, + ]); + this.nameFilter = selectStatus.name; + this.statusFilter = selectStatus.status; + } + return manageableConstraint; + } + + private async displayFlowInterviewToDelete(flowVInterviewIds: string[], conn: any) { + const query = "SELECT Name,InterviewLabel,InterviewStatus,CreatedBy.Username,CreatedDate,LastModifiedDate " + + `FROM FlowInterview WHERE Id IN ('${flowVInterviewIds.join("','")}')` + + " ORDER BY Name"; + const flowsInterviewsToDelete = (await bulkQuery(query, conn)).records; + uxLog(this, c.yellow(`Flow interviews to be deleted would be the following:\n${columnify(flowsInterviewsToDelete)}`)); } } diff --git a/src/commands/hardis/org/retrieve/packageconfig.ts b/src/commands/hardis/org/retrieve/packageconfig.ts index ec52ea5ad..47f82e8d4 100644 --- a/src/commands/hardis/org/retrieve/packageconfig.ts +++ b/src/commands/hardis/org/retrieve/packageconfig.ts @@ -1,54 +1,46 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { MetadataUtils } from "../../../../common/metadata-utils"; -import { uxLog } from "../../../../common/utils"; -import { managePackageConfig, promptOrg } from "../../../../common/utils/orgUtils"; -import { prompts } from "../../../../common/utils/prompts"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { MetadataUtils } from '../../../../common/metadata-utils/index.js'; +import { uxLog } from '../../../../common/utils/index.js'; +import { managePackageConfig, promptOrg } from '../../../../common/utils/orgUtils.js'; +import { prompts } from '../../../../common/utils/prompts.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class RetrievePackageConfig extends SfCommand { + public static title = 'Retrieve package configuration from an org'; -export default class RetrievePackageConfig extends SfdxCommand { - public static title = "Retrieve package configuration from an org"; + public static description = 'Retrieve package configuration from an org'; - public static description = "Retrieve package configuration from an org"; + public static examples = ['$ sf hardis:org:retrieve:packageconfig', 'sf hardis:org:retrieve:packageconfig -u myOrg']; - public static examples = ["$ sfdx hardis:org:retrieve:packageconfig", "sfdx hardis:org:retrieve:packageconfig -u myOrg"]; - - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static supportsUsername = true; - protected static requiresUsername = false; - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; /* jscpd:ignore-end */ public async run(): Promise { - let targetUsername = this.flags.targetusername || null; + const { flags } = await this.parse(RetrievePackageConfig); + let targetUsername = flags['target-org'].getUsername() || null; // Prompt for organization if not sent if (targetUsername == null) { @@ -57,13 +49,13 @@ export default class RetrievePackageConfig extends SfdxCommand { } // Retrieve list of installed packages - const installedPackages = await MetadataUtils.listInstalledPackages(targetUsername, this); + const installedPackages = await MetadataUtils.listInstalledPackages(targetUsername || '', this); // Store list in config const updateConfigRes = await prompts({ - type: "confirm", - name: "value", - message: c.cyanBright("Do you want to update your project configuration with this list of packages ?"), + type: 'confirm', + name: 'value', + message: c.cyanBright('Do you want to update your project configuration with this list of packages ?'), }); if (updateConfigRes.value === true) { await managePackageConfig(installedPackages, installedPackages); @@ -71,6 +63,6 @@ export default class RetrievePackageConfig extends SfdxCommand { const message = `[sfdx-hardis] Successfully retrieved package config`; uxLog(this, c.green(message)); - return { orgId: this.org.getOrgId(), outputString: message }; + return { orgId: flags['target-org'].getOrgId(), outputString: message }; } } diff --git a/src/commands/hardis/org/retrieve/sources/analytics.ts b/src/commands/hardis/org/retrieve/sources/analytics.ts index 5c0be6e65..f257c1ba5 100644 --- a/src/commands/hardis/org/retrieve/sources/analytics.ts +++ b/src/commands/hardis/org/retrieve/sources/analytics.ts @@ -1,76 +1,74 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as path from "path"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import * as path from 'path'; // import * as path from "path"; -import { uxLog, isCI, createTempDir, execCommand } from "../../../../../common/utils"; +import { uxLog, isCI, createTempDir, execCommand } from '../../../../../common/utils/index.js'; -import { promptOrgUsernameDefault } from "../../../../../common/utils/orgUtils"; -import { buildOrgManifest } from "../../../../../common/utils/deployUtils"; -import { parsePackageXmlFile, writePackageXmlFile } from "../../../../../common/utils/xmlUtils"; +import { promptOrgUsernameDefault } from '../../../../../common/utils/orgUtils.js'; +import { buildOrgManifest } from '../../../../../common/utils/deployUtils.js'; +import { parsePackageXmlFile, writePackageXmlFile } from '../../../../../common/utils/xmlUtils.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class Retrofit extends SfdxCommand { - public static title = "Retrieve CRM Analytics configuration from an org"; +export default class RetrieveAnalytics extends SfCommand { + public static title = 'Retrieve CRM Analytics configuration from an org'; public static description = `Retrieve all CRM Analytics sources from an org, with workarounds for SFDX bugs`; - public static examples = ["$ sfdx hardis:org:retrieve:sources:analytics"]; + public static examples = ['$ sf hardis:org:retrieve:sources:analytics']; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), - }; - - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + 'target-org': requiredOrgFlagWithDeprecations, + }; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default + public static requiresProject = true; protected configInfo: any = {}; protected debugMode = false; /* jscpd:ignore-end */ - protected analyticsMetadataTypes = ["WaveApplication", "WaveDashboard", "WaveDataflow", "WaveDataset", "WaveLens", "WaveRecipe", "WaveXmd"]; + protected analyticsMetadataTypes = [ + 'WaveApplication', + 'WaveDashboard', + 'WaveDataflow', + 'WaveDataset', + 'WaveLens', + 'WaveRecipe', + 'WaveXmd', + ]; // Retrieves locally all items corresponding to CRM Analytics configuration public async run(): Promise { + const { flags } = await this.parse(RetrieveAnalytics); // Manage user selection for org if we are not in CI - let orgUsername = this.org.getUsername(); - if (!isCI && !this.flags.targetusername) { - orgUsername = await promptOrgUsernameDefault(this, orgUsername, { devHub: false, setDefault: false }); + let orgUsername = flags['target-org'].getUsername(); + if (!isCI && !flags['target-org']) { + orgUsername = await promptOrgUsernameDefault(this, orgUsername || '', { devHub: false, setDefault: false }); } // List all metadatas of target org const tmpDir = await createTempDir(); - const packageXmlAllFile = path.join(tmpDir, "packageXmlAll.xml"); - await buildOrgManifest(orgUsername, packageXmlAllFile, this.org.getConnection()); + const packageXmlAllFile = path.join(tmpDir, 'packageXmlAll.xml'); + await buildOrgManifest(orgUsername, packageXmlAllFile, flags['target-org'].getConnection()); uxLog(this, c.cyan(`Retrieved full package XML from org ${orgUsername}: ${packageXmlAllFile}`)); // Filter to keep only analytics metadatas const parsedPackageXmlAll = await parsePackageXmlFile(packageXmlAllFile); - const packageXmlAnalyticsFile = path.join(tmpDir, "packageXmlAnalytics.xml"); + const packageXmlAnalyticsFile = path.join(tmpDir, 'packageXmlAnalytics.xml'); const analyticsPackageXml = {}; for (const type of Object.keys(parsedPackageXmlAll)) { if (this.analyticsMetadataTypes.includes(type)) { @@ -78,10 +76,13 @@ export default class Retrofit extends SfdxCommand { } } await writePackageXmlFile(packageXmlAnalyticsFile, analyticsPackageXml); - uxLog(this, c.cyan(`Filtered and completed analytics metadatas in analytics package XML: ${packageXmlAnalyticsFile}`)); + uxLog( + this, + c.cyan(`Filtered and completed analytics metadatas in analytics package XML: ${packageXmlAnalyticsFile}`) + ); // Retrieve locally Analytics sources - const retrieveCommand = `sfdx force:source:retrieve -x "${packageXmlAnalyticsFile}" -u ${orgUsername}`; + const retrieveCommand = `sf project retrieve start -x "${packageXmlAnalyticsFile}" -o ${orgUsername}`; await execCommand(retrieveCommand, this, { fail: true, debug: this.debugMode, output: true }); uxLog(this, c.cyan(`Retrieved all analytics source items using package XML: ${packageXmlAnalyticsFile}`)); diff --git a/src/commands/hardis/org/retrieve/sources/dx.ts b/src/commands/hardis/org/retrieve/sources/dx.ts index df8eca0cf..884e3344a 100644 --- a/src/commands/hardis/org/retrieve/sources/dx.ts +++ b/src/commands/hardis/org/retrieve/sources/dx.ts @@ -1,105 +1,96 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages, SfdxError } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as child from "child_process"; -import * as fs from "fs-extra"; -import * as path from "path"; -import * as util from "util"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages, SfError } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import * as child from 'child_process'; +import fs from 'fs-extra'; +import * as path from 'path'; +import * as util from 'util'; const exec = util.promisify(child.exec); -import { MetadataUtils } from "../../../../../common/metadata-utils"; -import { createTempDir, uxLog } from "../../../../../common/utils"; -import { WebSocketClient } from "../../../../../common/websocketClient"; -import { setConfig } from "../../../../../config"; +import { MetadataUtils } from '../../../../../common/metadata-utils/index.js'; +import { uxLog } from '../../../../../common/utils/index.js'; +import { WebSocketClient } from '../../../../../common/websocketClient.js'; +import { setConfig } from '../../../../../config/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class DxSources extends SfCommand { + public static title = 'Retrieve sfdx sources from org'; -export default class DxSources extends SfdxCommand { - public static title = "Retrieve sfdx sources from org"; + public static description = messages.getMessage('retrieveDx'); - public static description = messages.getMessage("retrieveDx"); + public static examples = ['$ sf hardis:org:retrieve:sources:dx']; - public static examples = ["$ sfdx hardis:org:retrieve:sources:dx"]; - - protected static flagsConfig = { - folder: flags.string({ - char: "f", - default: ".", - description: messages.getMessage("folder"), + public static flags: any = { + folder: Flags.string({ + char: 'f', + default: '.', + description: messages.getMessage('folder'), }), - tempfolder: flags.string({ - char: "t", - default: "./tmp", - description: messages.getMessage("tempFolder"), + tempfolder: Flags.string({ + char: 't', + default: './tmp', + description: messages.getMessage('tempFolder'), }), - keepmetadatatypes: flags.string({ - char: "k", - description: "Comma separated list of metadatas types that will be the only ones to be retrieved", + keepmetadatatypes: Flags.string({ + char: 'k', + description: 'Comma separated list of metadatas types that will be the only ones to be retrieved', }), - filteredmetadatas: flags.string({ - char: "m", - description: messages.getMessage("filteredMetadatas"), + filteredmetadatas: Flags.string({ + char: 'm', + description: messages.getMessage('filteredMetadatas'), }), - shape: flags.boolean({ - char: "o", + shape: Flags.boolean({ + char: 'o', default: false, - description: messages.getMessage("createOrgShape"), + description: messages.getMessage('createOrgShape'), }), - instanceurl: flags.string({ - char: "r", - description: messages.getMessage("instanceUrl"), + instanceurl: Flags.string({ + char: 'r', + description: messages.getMessage('instanceUrl'), }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; - - // List required plugins, their presence will be tested before running the command - protected static requiresSfdxPlugins = ["sfdx-essentials"]; + public static requiresProject = false; /* jscpd:ignore-end */ public async run(): Promise { - const folder = path.resolve(this.flags.folder || "."); - const tempFolder = path.resolve(this.flags.tempfolder || "./tmp"); - const keepMetadataTypes = this.flags.keepmetadatatypes ? this.flags.keepmetadatatypes.split(",") : []; - const filteredMetadatas = this.flags.filteredmetadatas ? this.flags.filteredmetadatas.split(",") : MetadataUtils.listMetadatasNotManagedBySfdx(); - const shapeFlag = this.flags.shape || false; - const debug = this.flags.debug || false; + const { flags } = await this.parse(DxSources); + const folder = path.resolve(flags.folder || '.'); + const tempFolder = path.resolve(flags.tempfolder || './tmp'); + const keepMetadataTypes = flags.keepmetadatatypes ? flags.keepmetadatatypes.split(',') : []; + const filteredMetadatas = flags.filteredmetadatas + ? flags.filteredmetadatas.split(',') + : MetadataUtils.listMetadatasNotManagedBySfdx(); + const shapeFlag = flags.shape || false; + const debug = flags.debug || false; // Create working temp folders and define it as cwd const prevCwd = process.cwd(); await fs.ensureDir(tempFolder); await fs.emptyDir(tempFolder); process.chdir(tempFolder); - const metadataFolder = path.join(tempFolder, "mdapipkg"); + const metadataFolder = path.join(tempFolder, 'mdapipkg'); await fs.ensureDir(metadataFolder); await fs.emptyDir(metadataFolder); - const sfdxFolder = path.join(tempFolder, "sfdx-project"); + const sfdxFolder = path.join(tempFolder, 'sfdx-project'); await fs.ensureDir(sfdxFolder); await fs.emptyDir(sfdxFolder); @@ -108,13 +99,21 @@ export default class DxSources extends SfdxCommand { if (keepMetadataTypes) { retrieveOptions.keepMetadataTypes = keepMetadataTypes; } - const packageXml = path.resolve(path.join(tempFolder, "package.xml")); - await MetadataUtils.retrieveMetadatas(packageXml, metadataFolder, true, filteredMetadatas, retrieveOptions, this, debug); + const packageXml = path.resolve(path.join(tempFolder, 'package.xml')); + await MetadataUtils.retrieveMetadatas( + packageXml, + metadataFolder, + true, + filteredMetadatas, + retrieveOptions, + this, + debug + ); // Create sfdx project if (fs.readdirSync(sfdxFolder).length === 0) { - uxLog(this, c.cyan("Creating SFDX project...")); - const projectCreateCommand = 'sfdx force:project:create --projectname "sfdx-project"'; + uxLog(this, c.cyan('Creating SFDX project...')); + const projectCreateCommand = 'sf project generate --name "sfdx-project"'; uxLog(this, `[command] ${c.bold(c.grey(projectCreateCommand))}`); const createProjectRes = await exec(projectCreateCommand, { maxBuffer: 1024 * 2000 }); if (debug) { @@ -125,7 +124,8 @@ export default class DxSources extends SfdxCommand { // Converting metadatas to sfdx uxLog(this, c.cyan(`Converting metadatas into SFDX sources in ${c.green(sfdxFolder)}...`)); process.chdir(sfdxFolder); - const mdapiConvertCommand = `sfdx force:mdapi:convert --rootdir ${path.join(metadataFolder, "unpackaged")} ${debug ? "--verbose" : ""}`; + const mdapiConvertCommand = `sf project convert mdapi --root-dir ${path.join(metadataFolder, 'unpackaged')} ${debug ? '--verbose' : '' + }`; uxLog(this, `[command] ${c.bold(c.grey(mdapiConvertCommand))}`); try { const convertRes = await exec(mdapiConvertCommand, { @@ -135,16 +135,19 @@ export default class DxSources extends SfdxCommand { uxLog(this, convertRes.stdout + convertRes.stderr); } } catch (e) { - throw new SfdxError(JSON.stringify(e, null, 2)); + throw new SfError(JSON.stringify(e, null, 2)); } // Move sfdx sources in main folder uxLog(this, `[sfdx-hardis] Moving temp files to main folder ${c.green(path.resolve(folder))}...`); process.chdir(prevCwd); // Do not replace files if already defined - const filesToNotReplace = [".gitignore", ".forceignore", "sfdx-project.json", "README.md"]; + const filesToNotReplace = ['.gitignore', '.forceignore', 'sfdx-project.json', 'README.md']; for (const fileToNotReplace of filesToNotReplace) { - if (fs.existsSync(path.join(path.resolve(folder), fileToNotReplace)) && fs.existsSync(path.join(sfdxFolder, fileToNotReplace))) { + if ( + fs.existsSync(path.join(path.resolve(folder), fileToNotReplace)) && + fs.existsSync(path.join(sfdxFolder, fileToNotReplace)) + ) { await fs.remove(path.join(sfdxFolder, fileToNotReplace)); } } @@ -154,7 +157,7 @@ export default class DxSources extends SfdxCommand { // Manage org shape if requested if (shapeFlag === true) { // Copy package.xml - const packageXmlInConfig = path.resolve(folder) + "/manifest/package.xml"; // '/config/package.xml'; + const packageXmlInConfig = path.resolve(folder) + '/manifest/package.xml'; // '/config/package.xml'; if (!fs.existsSync(packageXmlInConfig)) { await fs.ensureDir(path.dirname(packageXmlInConfig)); uxLog(this, `[sfdx-hardis] Copying package.xml manifest ${c.green(packageXmlInConfig)}...`); @@ -162,30 +165,9 @@ export default class DxSources extends SfdxCommand { } // Store list of installed packages const installedPackages = await MetadataUtils.listInstalledPackages(null, this); - await setConfig("project", { + await setConfig('project', { installedPackages, }); - // Try to get org shape - const projectScratchDefFile = "./config/project-scratch-def.json"; - uxLog(this, `[sfdx-hardis] Getting org shape in ${c.green(path.resolve(projectScratchDefFile))}...`); - const shapeFile = path.join(await createTempDir(), "project-scratch-def.json"); - try { - await exec(`sfdx force:org:shape:create -f "${shapeFile} -u `); - const orgShape = await fs.readFile(shapeFile, "utf-8"); - const projectScratchDef = await fs.readFile(projectScratchDefFile, "utf-8"); - const newShape = Object.assign(projectScratchDef, orgShape); - await fs.writeFile(projectScratchDefFile, JSON.stringify(newShape, null, 2)); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - } catch (e) { - uxLog(this, c.yellow("[sfdx-hardis][ERROR] Unable to create org shape")); - uxLog(this, c.yellow("[sfdx-hardis] You need to manually update config/project-scratch-def.json")); - uxLog( - this, - c.yellow( - "[sfdx-hardis] See documentation at https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_scratch_orgs_def_file.htm", - ), - ); - } } // Remove temporary files @@ -198,11 +180,11 @@ export default class DxSources extends SfdxCommand { } // Trigger commands refresh on VsCode WebSocket Client - WebSocketClient.sendMessage({ event: "refreshCommands" }); + WebSocketClient.sendMessage({ event: 'refreshCommands' }); // Set bac initial cwd const message = `[sfdx-hardis] Successfully retrieved sfdx project in ${folder}`; uxLog(this, c.green(message)); - return { orgId: this.org.getOrgId(), outputString: message }; + return { orgId: flags['target-org'].getOrgId(), outputString: message }; } } diff --git a/src/commands/hardis/org/retrieve/sources/dx2.ts b/src/commands/hardis/org/retrieve/sources/dx2.ts index 5b62b67c0..08c5d6c0c 100644 --- a/src/commands/hardis/org/retrieve/sources/dx2.ts +++ b/src/commands/hardis/org/retrieve/sources/dx2.ts @@ -1,71 +1,62 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages, SfdxError } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages, SfError } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; -import { execCommand, uxLog } from "../../../../../common/utils"; -import { promptOrg } from "../../../../../common/utils/orgUtils"; -import { prompts } from "../../../../../common/utils/prompts"; -import { PACKAGE_ROOT_DIR } from "../../../../../settings"; +import { execCommand, uxLog } from '../../../../../common/utils/index.js'; +import { promptOrg } from '../../../../../common/utils/orgUtils.js'; +import { prompts } from '../../../../../common/utils/prompts.js'; +import { PACKAGE_ROOT_DIR } from '../../../../../settings.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class DxSources2 extends SfCommand { + public static title = 'Retrieve sfdx sources from org (2)'; -export default class DxSources2 extends SfdxCommand { - public static title = "Retrieve sfdx sources from org (2)"; + public static description = messages.getMessage('retrieveDx'); - public static description = messages.getMessage("retrieveDx"); + public static examples = ['$ sf hardis:org:retrieve:sources:dx2']; - public static examples = ["$ sfdx hardis:org:retrieve:sources:dx2"]; - - protected static flagsConfig = { - packagexml: flags.string({ - char: "x", - description: "Path to package.xml file", + public static flags: any = { + packagexml: Flags.string({ + char: 'x', + description: 'Path to package.xml file', }), - template: flags.string({ - char: "t", - description: "sfdx-hardis package.xml Template name. ex: wave", + template: Flags.string({ + char: 't', + description: 'sfdx-hardis package.xml Template name. ex: wave', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static supportsUsername = true; - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; protected debugMode = false; /* jscpd:ignore-end */ public async run(): Promise { - let packageXml = this.flags.packagexml || null; - let targetUsername = this.flags.targetusername || null; - const template = this.flags.template || null; - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(DxSources2); + let packageXml = flags.packagexml || null; + let targetUsername = flags['target-org'] || null; + const template = flags.template || null; + this.debugMode = flags.debug || false; // Prompt for organization if not sent if (targetUsername == null) { @@ -81,32 +72,34 @@ export default class DxSources2 extends SfdxCommand { // Prompt for package.xml if not sent if (packageXml === null) { const packageXmlRes = await prompts({ - message: c.cyanBright("Please input the path to the package.xml file to use force sfdx force:source:retrieve"), - type: "text", - name: "value", + message: c.cyanBright('Please input the path to the package.xml file to use force sf project retrieve start'), + type: 'text', + name: 'value', }); packageXml = packageXmlRes.value; } // Check package.xml file exists - if (!fs.existsSync(packageXml)) { - throw new SfdxError(c.red("Package.xml file not found at " + packageXml)); + if (!fs.existsSync(packageXml || '')) { + throw new SfError(c.red('Package.xml file not found at ' + packageXml)); } // Copy package.xml in /tmp if provided value is not within project - if (!path.resolve(packageXml).includes(path.resolve(process.cwd()))) { - const packageXmlTmp = path.join(process.cwd(), "tmp", "retrievePackage.xml"); + if (!path.resolve(packageXml || '').includes(path.resolve(process.cwd()))) { + const packageXmlTmp = path.join(process.cwd(), 'tmp', 'retrievePackage.xml'); await fs.ensureDir(path.dirname(packageXmlTmp)); - await fs.copy(packageXml, packageXmlTmp); + await fs.copy(packageXml || '', packageXmlTmp); uxLog(this, c.grey(`Copied ${packageXml} to ${packageXmlTmp}`)); packageXml = path.relative(process.cwd(), packageXmlTmp); } // Retrieve sources - const retrieveCommand = "sfdx force:source:retrieve" + ` -x "${packageXml}"` + ` --targetusername ${targetUsername}`; + const retrieveCommand = 'sf project retrieve start' + ` -x "${packageXml}"` + ` -o ${targetUsername}`; await execCommand(retrieveCommand, this, { fail: false, debug: this.debugMode, output: true }); // Set bac initial cwd - const message = `[sfdx-hardis] Successfully retrieved sfdx sources from ${c.bold(targetUsername)} using ${c.bold(packageXml)}`; + const message = `[sfdx-hardis] Successfully retrieved sfdx sources from ${c.bold(targetUsername)} using ${c.bold( + packageXml + )}`; uxLog(this, c.green(message)); return { outputString: message }; } diff --git a/src/commands/hardis/org/retrieve/sources/metadata.ts b/src/commands/hardis/org/retrieve/sources/metadata.ts index bf4f7f190..82d43ec63 100644 --- a/src/commands/hardis/org/retrieve/sources/metadata.ts +++ b/src/commands/hardis/org/retrieve/sources/metadata.ts @@ -1,78 +1,67 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as child from "child_process"; -import * as fs from "fs-extra"; -import * as path from "path"; -import { MetadataUtils } from "../../../../../common/metadata-utils"; -import { ensureGitRepository, execCommand, isMonitoringJob, uxLog } from "../../../../../common/utils"; -import LegacyApi from "../../diagnose/legacyapi"; -import OrgTestApex from "../../test/apex"; -import * as util from "util"; -import { PACKAGE_ROOT_DIR } from "../../../../../settings"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import * as child from 'child_process'; +import fs from 'fs-extra'; +import * as path from 'path'; +import { MetadataUtils } from '../../../../../common/metadata-utils/index.js'; +import { ensureGitRepository, execCommand, isMonitoringJob, uxLog } from '../../../../../common/utils/index.js'; +import LegacyApi from '../../diagnose/legacyapi.js'; +import OrgTestApex from '../../test/apex.js'; +import * as util from 'util'; +import { PACKAGE_ROOT_DIR } from '../../../../../settings.js'; +import { CONSTANTS } from '../../../../../config/index.js'; const exec = util.promisify(child.exec); -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class DxSources extends SfCommand { + public static title = 'Retrieve sfdx sources from org'; -export default class DxSources extends SfdxCommand { - public static title = "Retrieve sfdx sources from org"; - - public static description = messages.getMessage("retrieveDx"); + public static description = messages.getMessage('retrieveDx'); public static examples = [ - "$ sfdx hardis:org:retrieve:sources:metadata", - "$ SFDX_RETRIEVE_WAIT_MINUTES=200 sfdx hardis:org:retrieve:sources:metadata", + '$ sf hardis:org:retrieve:sources:metadata', + '$ SFDX_RETRIEVE_WAIT_MINUTES=200 sf hardis:org:retrieve:sources:metadata', ]; - protected static flagsConfig = { - folder: flags.string({ - char: "f", - default: ".", - description: messages.getMessage("folder"), + public static flags: any = { + folder: Flags.string({ + char: 'f', + default: '.', + description: messages.getMessage('folder'), }), - packagexml: flags.string({ - char: "p", - description: messages.getMessage("packageXml"), + packagexml: Flags.string({ + char: 'p', + description: messages.getMessage('packageXml'), }), - includemanaged: flags.boolean({ + includemanaged: Flags.boolean({ default: false, - description: "Include items from managed packages", + description: 'Include items from managed packages', }), - instanceurl: flags.string({ - char: "r", - description: messages.getMessage("instanceUrl"), + instanceurl: Flags.string({ + char: 'r', + description: messages.getMessage('instanceUrl'), }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; - - // List required plugins, their presence will be tested before running the command - protected static requiresSfdxPlugins = ["sfdx-essentials"]; + public static requiresProject = false; // Trigger notification(s) to MsTeams channel protected static triggerNotification = true; @@ -82,25 +71,34 @@ export default class DxSources extends SfdxCommand { /* jscpd:ignore-end */ public async run(): Promise { - const folder = path.resolve(this.flags.folder || "."); - const packageXml = path.resolve(this.flags.packagexml || "package.xml"); - const includeManaged = this.flags.includemanaged || false; - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(DxSources); + const folder = path.resolve(flags.folder || '.'); + const packageXml = path.resolve(flags.packagexml || 'package.xml'); + const includeManaged = flags.includemanaged || false; + this.debugMode = flags.debug || false; // Check required pre-requisites await ensureGitRepository({ init: true }); const isMonitoring = await isMonitoringJob(); // Retrieve metadatas - let message = ""; + let message = ''; try { const filterManagedItems = includeManaged === false; - await MetadataUtils.retrieveMetadatas(packageXml, folder, false, [], { filterManagedItems: filterManagedItems }, this, this.debugMode); + await MetadataUtils.retrieveMetadatas( + packageXml, + folder, + false, + [], + { filterManagedItems: filterManagedItems }, + this, + this.debugMode + ); // Copy to destination - await fs.copy(path.join(folder, "unpackaged"), path.resolve(folder)); + await fs.copy(path.join(folder, 'unpackaged'), path.resolve(folder)); // Remove temporary files - await fs.rm(path.join(folder, "unpackaged"), { recursive: true }); + await fs.rm(path.join(folder, 'unpackaged'), { recursive: true }); message = `[sfdx-hardis] Successfully retrieved metadatas in ${folder}`; uxLog(this, message); @@ -108,48 +106,51 @@ export default class DxSources extends SfdxCommand { if (!isMonitoring) { throw e; } - message = "[sfdx-hardis] Error retrieving metadatas"; + message = '[sfdx-hardis] Error retrieving metadatas'; } // Post actions for monitoring CI job if (isMonitoring) { try { - return await this.processPostActions(message); + return await this.processPostActions(message, flags); // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (e) { - uxLog(this, c.yellow("Post actions have failed !")); + uxLog(this, c.yellow('Post actions have failed !')); } - uxLog(this, c.yellow(c.bold("This version of sfdx-hardis monitoring is deprecated and will not be maintained anymore"))); - uxLog(this, c.yellow(c.bold("Switch to new sfdx-hardis monitoring that is enhanced !"))); - uxLog(this, c.yellow(c.bold("Info: https://sfdx-hardis.cloudity.com/salesforce-monitoring-home/"))); + uxLog( + this, + c.yellow(c.bold('This version of sfdx-hardis monitoring is deprecated and will not be maintained anymore')) + ); + uxLog(this, c.yellow(c.bold('Switch to new sfdx-hardis monitoring that is enhanced !'))); + uxLog(this, c.yellow(c.bold(`Info: ${CONSTANTS.DOC_URL_ROOT}/salesforce-monitoring-home/`))); } - return { orgId: this.org.getOrgId(), outputString: message }; + return { orgId: flags['target-org'].getOrgId(), outputString: message }; } - private async processPostActions(message) { - uxLog(this, c.cyan("Monitoring repo detected")); + private async processPostActions(message, flags) { + uxLog(this, c.cyan('Monitoring repo detected')); // Update default .gitlab-ci.yml within the monitoring repo - const localGitlabCiFile = path.join(process.cwd(), ".gitlab-ci.yml"); + const localGitlabCiFile = path.join(process.cwd(), '.gitlab-ci.yml'); if (fs.existsSync(localGitlabCiFile) && process.env?.AUTO_UPDATE_GITLAB_CI_YML) { - const localGitlabCiContent = await fs.readFile(localGitlabCiFile, "utf8"); - const latestGitlabCiFile = path.join(PACKAGE_ROOT_DIR, "defaults/monitoring/.gitlab-ci.yml"); - const latestGitlabCiContent = await fs.readFile(latestGitlabCiFile, "utf8"); + const localGitlabCiContent = await fs.readFile(localGitlabCiFile, 'utf8'); + const latestGitlabCiFile = path.join(PACKAGE_ROOT_DIR, 'defaults/monitoring/.gitlab-ci.yml'); + const latestGitlabCiContent = await fs.readFile(latestGitlabCiFile, 'utf8'); if (localGitlabCiContent !== latestGitlabCiContent) { await fs.writeFile(localGitlabCiFile, latestGitlabCiContent); - uxLog(this, c.cyan("Updated .gitlab-ci.yml file")); + uxLog(this, c.cyan('Updated .gitlab-ci.yml file')); } } // Also trace updates with sfdx sources, for better readability - uxLog(this, c.cyan("Convert into sfdx format...")); - if (fs.existsSync("metadatas")) { + uxLog(this, c.cyan('Convert into sfdx format...')); + if (fs.existsSync('metadatas')) { // Create sfdx project if not existing yet - if (!fs.existsSync("sfdx-project")) { - const createCommand = "sfdx force:project:create" + ` --projectname "sfdx-project"`; - uxLog(this, c.cyan("Creating sfdx-project...")); + if (!fs.existsSync('sfdx-project')) { + const createCommand = 'sf project generate' + ` --name "sfdx-project"`; + uxLog(this, c.cyan('Creating sfdx-project...')); await execCommand(createCommand, this, { output: true, fail: true, @@ -157,20 +158,18 @@ export default class DxSources extends SfdxCommand { }); } // Convert metadatas into sfdx sources - const mdapiConvertCommand = `sfdx force:mdapi:convert --rootdir "../metadatas"`; - uxLog(this, c.cyan("Converting metadata to source formation into sfdx-project...")); + const mdapiConvertCommand = `sf project convert mdapi --root-dir "../metadatas"`; + uxLog(this, c.cyan('Converting metadata to source formation into sfdx-project...')); uxLog(this, `[command] ${c.bold(c.grey(mdapiConvertCommand))}`); const prevCwd = process.cwd(); - process.chdir(path.join(process.cwd(), "./sfdx-project")); + process.chdir(path.join(process.cwd(), './sfdx-project')); try { const convertRes = await exec(mdapiConvertCommand, { maxBuffer: 10000 * 10000, }); - if (this.debug) { - uxLog(this, convertRes.stdout + convertRes.stderr); - } + uxLog(this, convertRes.stdout + convertRes.stderr); } catch (e) { - uxLog(this, c.yellow("Error while converting metadatas to sources:\n" + e.message)); + uxLog(this, c.yellow('Error while converting metadatas to sources:\n' + (e as Error).message)); } process.chdir(prevCwd); } @@ -180,21 +179,21 @@ export default class DxSources extends SfdxCommand { const prevExitCode = process.exitCode || 0; try { // Run test classes - uxLog(this, c.cyan("Running Apex tests...")); + uxLog(this, c.cyan('Running Apex tests...')); orgTestRes = await new OrgTestApex([], this.config)._run(); // Check usage of Legacy API versions - uxLog(this, c.cyan("Running Legacy API Use checks...")); + uxLog(this, c.cyan('Running Legacy API Use checks...')); legacyApiRes = await new LegacyApi([], this.config)._run(); // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (e) { - uxLog(this, c.yellow("Issues found when running Apex tests or Legacy API, please check messages")); + uxLog(this, c.yellow('Issues found when running Apex tests or Legacy API, please check messages')); } process.exitCode = prevExitCode; // Delete report files //const reportFiles = await glob("**/hardis-report/**", { cwd: process.cwd() }); //reportFiles.map(async (file) => await fs.remove(file)); - return { orgId: this.org.getOrgId(), outputString: message, orgTestRes, legacyApiRes }; + return { orgId: flags['target-org'].getOrgId(), outputString: message, orgTestRes, legacyApiRes }; } } diff --git a/src/commands/hardis/org/retrieve/sources/retrofit.ts b/src/commands/hardis/org/retrieve/sources/retrofit.ts index 63af3dfc4..73e13bb05 100644 --- a/src/commands/hardis/org/retrieve/sources/retrofit.ts +++ b/src/commands/hardis/org/retrieve/sources/retrofit.ts @@ -1,49 +1,52 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages, SfdxError } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import { getConfig } from "../../../../../config"; -import * as c from "chalk"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages, SfError } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { getConfig } from '../../../../../config/index.js'; +import c from 'chalk'; // import * as path from "path"; -import { ensureGitRepository, gitHasLocalUpdates, execCommand, git, uxLog, isCI } from "../../../../../common/utils"; -import { CleanOptions } from "simple-git"; -import CleanReferences from "../../../project/clean/references"; -import SaveTask from "../../../work/save"; -import CleanXml from "../../../project/clean/xml"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class Retrofit extends SfdxCommand { +import { + ensureGitRepository, + gitHasLocalUpdates, + execCommand, + git, + uxLog, + isCI, +} from '../../../../../common/utils/index.js'; +import { CleanOptions } from 'simple-git'; +import CleanReferences from '../../../project/clean/references.js'; +import SaveTask from '../../../work/save.js'; +import CleanXml from '../../../project/clean/xml.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class Retrofit extends SfCommand { public static DEFAULT_SOURCES_TO_RETROFIT = [ - "CompactLayout", - "CustomApplication", - "CustomField", - "CustomLabel", - "CustomLabels", - "CustomMetadata", - "CustomObject", - "CustomObjectTranslation", - "CustomTab", - "DuplicateRule", - "EmailTemplate", - "FlexiPage", - "GlobalValueSet", - "Layout", - "ListView", - "MatchingRules", - "PermissionSet", - "RecordType", - "StandardValueSet", - "Translations", - "ValidationRule", + 'CompactLayout', + 'CustomApplication', + 'CustomField', + 'CustomLabel', + 'CustomLabels', + 'CustomMetadata', + 'CustomObject', + 'CustomObjectTranslation', + 'CustomTab', + 'DuplicateRule', + 'EmailTemplate', + 'FlexiPage', + 'GlobalValueSet', + 'Layout', + 'ListView', + 'MatchingRules', + 'PermissionSet', + 'RecordType', + 'StandardValueSet', + 'Translations', + 'ValidationRule', ]; - public static title = "Retrofit changes from an org"; + public static title = 'Retrofit changes from an org'; public static description = `Retrieve changes from org link to a ref branch not present in sources @@ -58,7 +61,7 @@ export default class Retrofit extends SfdxCommand { - \`CI_SOURCES_TO_RETROFIT\`: env variable (can be defined in CI context) - \`sourcesToRetrofit\` property in \`.sfdx-hardis.yml\` - - Default list:\n\n - ${Retrofit.DEFAULT_SOURCES_TO_RETROFIT.join("\n - ")} + - Default list:\n\n - ${Retrofit.DEFAULT_SOURCES_TO_RETROFIT.join('\n - ')} You can also ignore some files even if they have been updated in production. To do that, define property **retrofitIgnoredFiles** in .sfdx-hardis.yml @@ -75,171 +78,170 @@ export default class Retrofit extends SfdxCommand { `; public static examples = [ - "$ sfdx hardis:org:retrieve:sources:retrofit", - "sfdx hardis:org:retrieve:sources:retrofit --productionbranch master --commit --commitmode updated", - "sfdx hardis:org:retrieve:sources:retrofit --productionbranch master --retrofitbranch preprod --commit --commitmode updated --push --pushmode mergerequest", + '$ sf hardis:org:retrieve:sources:retrofit', + 'sf hardis:org:retrieve:sources:retrofit --productionbranch master --commit --commitmode updated', + 'sf hardis:org:retrieve:sources:retrofit --productionbranch master --retrofitbranch preprod --commit --commitmode updated --push --pushmode mergerequest', ]; - protected static flagsConfig = { - commit: flags.boolean({ + public static flags: any = { + commit: Flags.boolean({ default: false, - description: "If true, a commit will be performed after the retrofit", + description: 'If true, a commit will be performed after the retrofit', }), - commitmode: flags.enum({ - default: "updated", - options: ["updated", "all"], - description: "Defines if we commit all retrieved updates, or all updates including creations", + commitmode: Flags.string({ + default: 'updated', + options: ['updated', 'all'], + description: 'Defines if we commit all retrieved updates, or all updates including creations', }), - push: flags.boolean({ + push: Flags.boolean({ default: false, - description: "If true, a push will be performed after the retrofit", + description: 'If true, a push will be performed after the retrofit', }), - pushmode: flags.enum({ - default: "default", - options: ["default", "mergerequest"], - description: "Defines if we send merge request options to git push arguments", + pushmode: Flags.string({ + default: 'default', + options: ['default', 'mergerequest'], + description: 'Defines if we send merge request options to git push arguments', }), - productionbranch: flags.string({ + productionbranch: Flags.string({ description: - "Name of the git branch corresponding to the org we want to perform the retrofit on.\nCan be defined in productionBranch property in .sfdx-hardis.yml", + 'Name of the git branch corresponding to the org we want to perform the retrofit on.\nCan be defined in productionBranch property in .sfdx-hardis.yml', }), - retrofittargetbranch: flags.string({ - description: "Name of branch the merge request will have as target\nCan be defined in retrofitBranch property in .sfdx-hardis.yml", + retrofittargetbranch: Flags.string({ + description: + 'Name of branch the merge request will have as target\nCan be defined in retrofitBranch property in .sfdx-hardis.yml', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), - }; - - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + 'target-org': requiredOrgFlagWithDeprecations, + }; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default + public static requiresProject = true; protected configInfo: any = {}; protected debugMode = false; protected commit = false; - protected commitMode = "updated"; + protected commitMode: string | boolean = 'updated'; protected push = false; - protected pushMode = "default"; - protected productionBranch: string; - protected retrofitTargetBranch: string; + protected pushMode = 'default'; + protected productionBranch: string | null; + protected retrofitTargetBranch: string | null; /* jscpd:ignore-end */ public async run(): Promise { - this.commit = this.flags.commit || false; - this.commitMode = this.flags.commitmode || false; - this.push = this.flags.push || false; - this.pushMode = this.flags.pushmode || "default"; - this.productionBranch = this.flags.productionbranch || null; - this.retrofitTargetBranch = this.flags.retrofittargetbranch || null; - this.debugMode = this.flags.debug || false; - this.configInfo = await getConfig("branch"); + const { flags } = await this.parse(Retrofit); + this.commit = flags.commit || false; + this.commitMode = flags.commitmode || false; + this.push = flags.push || false; + this.pushMode = flags.pushmode || 'default'; + this.productionBranch = flags.productionbranch || null; + this.retrofitTargetBranch = flags.retrofittargetbranch || null; + this.debugMode = flags.debug || false; + this.configInfo = await getConfig('branch'); // check git repo before processing await ensureGitRepository(); // set commit & merge request author await this.setDefaultGitConfig(); // checkout to retrofit branch, retrieve changes & push them if any - await this.processRetrofit(); + await this.processRetrofit(flags); - return { outputString: "Merge request created/updated" }; + return { outputString: 'Merge request created/updated' }; } - async processRetrofit() { - const config = await getConfig("branch"); - this.productionBranch = this.productionBranch || config.productionBranch || process.env.CI_COMMIT_REF_NAME || "master"; + async processRetrofit(flags) { + const config = await getConfig('branch'); + this.productionBranch = + this.productionBranch || config.productionBranch || process.env.CI_COMMIT_REF_NAME || 'master'; const retrofitWorkBranch = `retrofit/${this.productionBranch}`; - this.retrofitTargetBranch = this.retrofitTargetBranch || config.retrofitBranch || "retrofitTargetBranch MUST BE SET"; + this.retrofitTargetBranch = + this.retrofitTargetBranch || config.retrofitBranch || 'retrofitTargetBranch MUST BE SET'; - await git().fetch(["--prune"]); + await git().fetch(['--prune']); const branches = await git().branch(); if (branches.all.find((branch) => branch.includes(retrofitWorkBranch))) { // If manual command (not CI), force user to remove previous retrofit branches if (!isCI) { - throw new SfdxError(`You must delete local and remote branch ${c.yellow(retrofitWorkBranch)} before running this command`); + throw new SfError( + `You must delete local and remote branch ${c.yellow(retrofitWorkBranch)} before running this command` + ); } uxLog(this, c.cyan(`Checkout to existing branch ${retrofitWorkBranch}`)); - await git().checkout(retrofitWorkBranch, ["--force"]); + await git().checkout(retrofitWorkBranch, ['--force']); } else { uxLog(this, c.cyan(`Create a new branch ${retrofitWorkBranch} from ${this.productionBranch}`)); await git().checkoutBranch(retrofitWorkBranch, `origin/${this.productionBranch}`); } - const currentHash = await git().revparse(["HEAD"]); + const currentHash = await git().revparse(['HEAD']); uxLog(this, c.grey(`HEAD currently at ${currentHash}`)); // Retrieve sources from target org - const hasChangedSources = await this.retrieveSources(); + const hasChangedSources = await this.retrieveSources(flags); if (hasChangedSources) { // Commit and push if requested if (this.commit) { - await this.commitChanges(); + await this.commitChanges(flags); // Update package.xml files and clean if necessary - await SaveTask.run(["--targetbranch", this.retrofitTargetBranch, "--auto"]); + await SaveTask.run(['--targetbranch', this.retrofitTargetBranch || '', '--auto']); if (this.push) { await this.pushChanges(retrofitWorkBranch); } } } else { - uxLog(this, c.yellow("No changes to commit")); + uxLog(this, c.yellow('No changes to commit')); // Delete locally created branch if we are within CI process if (isCI) { - uxLog(this, c.yellow("Deleting local retrofit branch...")); + uxLog(this, c.yellow('Deleting local retrofit branch...')); await git().branch([`-D ${retrofitWorkBranch}`]); } } } // Commit all changes or only updated files - async commitChanges() { - if (this.commitMode === "updated") { - uxLog(this, c.cyan("Stage and commit only updated files... ")); - await git().add(["--update"]); - await this.doCommit(); - uxLog(this, c.cyan("Removing created files... ")); - await git().reset(["--hard"]); + async commitChanges(flags) { + if (this.commitMode === 'updated') { + uxLog(this, c.cyan('Stage and commit only updated files... ')); + await git().add(['--update']); + await this.doCommit(flags); + uxLog(this, c.cyan('Removing created files... ')); + await git().reset(['--hard']); await git().clean([CleanOptions.FORCE, CleanOptions.RECURSIVE]); } else { - uxLog(this, c.cyan("Stage and commit all files... ")); - await git().add(["--all"]); - await this.doCommit(); + uxLog(this, c.cyan('Stage and commit all files... ')); + await git().add(['--all']); + await this.doCommit(flags); } } - async doCommit() { - await git().commit(`[sfdx-hardis] Changes retrofited from ${this.org.getUsername()}`); + async doCommit(flags) { + await git().commit(`[sfdx-hardis] Changes retrofited from ${flags['target-org'].getUsername()}`); } // Push changes and add merge request options if requested async pushChanges(retrofitWorkBranch: string) { const origin = `https://root:${process.env.CI_TOKEN}@${process.env.CI_SERVER_HOST}/${process.env.CI_PROJECT_PATH}.git`; - const pushOptions = []; - if (this.pushMode === "mergerequest") { + const pushOptions: any[] = []; + if (this.pushMode === 'mergerequest') { const mrOptions = [ - "-o merge_request.create", + '-o merge_request.create', `-o merge_request.target ${this.retrofitTargetBranch}`, `-o merge_request.title='[sfdx-hardis][RETROFIT] Created by pipeline #${process.env.CI_PIPELINE_ID}'`, - "-o merge_request.merge_when_pipeline_succeeds", - "-o merge_request.remove_source_branch", + '-o merge_request.merge_when_pipeline_succeeds', + '-o merge_request.remove_source_branch', ]; pushOptions.push(...mrOptions); } - const pushResult = await execCommand(`git push ${origin} ${retrofitWorkBranch} ${pushOptions.join(" ")}`, this, { + const pushResult = await execCommand(`git push ${origin} ${retrofitWorkBranch} ${pushOptions.join(' ')}`, this, { fail: true, debug: this.debugMode, output: true, @@ -251,24 +253,26 @@ export default class Retrofit extends SfdxCommand { // Just do that in CI, because this config should already exist in local if (isCI) { // either use values from variables from CI or use predefined variables from gitlab - const USERNAME = process.env.CI_USER_NAME || process.env.GITLAB_USER_NAME; - const EMAIL = process.env.CI_USER_EMAIL || process.env.GITLAB_USER_EMAIL; - await git().addConfig("user.name", USERNAME, false, "local"); - await git().addConfig("user.email", EMAIL, false, "local"); + const USERNAME = process.env.CI_USER_NAME || process.env.GITLAB_USER_NAME || ''; + const EMAIL = process.env.CI_USER_EMAIL || process.env.GITLAB_USER_EMAIL || ''; + await git().addConfig('user.name', USERNAME, false, 'local'); + await git().addConfig('user.email', EMAIL, false, 'local'); } } - async retrieveSources() { - uxLog(this, c.cyan(`Retrieving sources from ${c.green(this.org.getUsername())} ...`)); + async retrieveSources(flags) { + uxLog(this, c.cyan(`Retrieving sources from ${c.green(flags['target-org'].getUsername())} ...`)); const RETROFIT_MDT: Array = process.env.CI_SOURCES_TO_RETROFIT || this.configInfo.sourcesToRetrofit || Retrofit.DEFAULT_SOURCES_TO_RETROFIT; - const retrieveCommand = `sfdx force:source:retrieve -m "${RETROFIT_MDT.join(",")}" -u ${this.org.getUsername()}`; + const retrieveCommand = `sf project retrieve start -m "${RETROFIT_MDT.join(',')}" -o ${flags[ + 'target-org' + ].getUsername()}`; await execCommand(retrieveCommand, this, { fail: true, debug: this.debugMode, output: true }); // Discard ignored changes await this.discardIgnoredChanges(); // Clean sources - await CleanReferences.run(["--type", "all"]); + await CleanReferences.run(['--type', 'all']); await CleanXml.run([]); // display current changes to commit @@ -277,13 +281,16 @@ export default class Retrofit extends SfdxCommand { // Discard ignored changes from retrofitIgnoredFiles async discardIgnoredChanges() { - const config = await getConfig("branch"); + const config = await getConfig('branch'); const ignoredFiles = config.retrofitIgnoredFiles || []; if (ignoredFiles.length > 0) { - uxLog(this, c.cyan(`Discarding ignored changes from .sfdx-hardis.yml ${c.bold("retrofitIgnoredFiles")} property...`)); + uxLog( + this, + c.cyan(`Discarding ignored changes from .sfdx-hardis.yml ${c.bold('retrofitIgnoredFiles')} property...`) + ); for (const ignoredFile of ignoredFiles) { // Reset file state - await git().checkout(["--", ignoredFile]); + await git().checkout(['--', ignoredFile]); } } } diff --git a/src/commands/hardis/org/select.ts b/src/commands/hardis/org/select.ts index 58f9ef0b6..51c6532fc 100644 --- a/src/commands/hardis/org/select.ts +++ b/src/commands/hardis/org/select.ts @@ -1,69 +1,64 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import { promptOrg } from "../../../common/utils/orgUtils"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { makeSureOrgIsConnected, promptOrg } from '../../../common/utils/orgUtils.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class OrgSelect extends SfCommand { + public static title = 'Select org'; -export default class OrgSelect extends SfdxCommand { - public static title = "Select org"; + public static description = messages.getMessage('selectOrg'); - public static description = messages.getMessage("selectOrg"); - - public static examples = ["$ sfdx hardis:org:select"]; + public static examples = ['$ sf hardis:org:select']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - devhub: flags.boolean({ - char: "h", + public static flags: any = { + devhub: Flags.boolean({ + char: 'h', default: false, - description: messages.getMessage("withDevHub"), + description: messages.getMessage('withDevHub'), }), - scratch: flags.boolean({ - char: "s", + scratch: Flags.boolean({ + char: 's', default: false, - description: "Select scratch org related to default DevHub", + description: 'Select scratch org related to default DevHub', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; protected debugMode = false; /* jscpd:ignore-end */ public async run(): Promise { - const devHub = this.flags.devhub || false; - const scratch = this.flags.scratch; - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(OrgSelect); + const devHub = flags.devhub || false; + const scratch = flags.scratch; + this.debugMode = flags.debug || false; + // Prompt user to select an org const org = await promptOrg(this, { devHub: devHub, setDefault: true, scratch: scratch }); + // If the org is not connected, ask the user to authenticate again + await makeSureOrgIsConnected(org.username); + // Return an object to be displayed with --json return { outputString: `Selected org ${org.username}` }; } diff --git a/src/commands/hardis/org/test/apex.ts b/src/commands/hardis/org/test/apex.ts index fa1a2dc85..6383fe55f 100644 --- a/src/commands/hardis/org/test/apex.ts +++ b/src/commands/hardis/org/test/apex.ts @@ -1,23 +1,19 @@ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; -import { execCommand, extractRegexMatchesMultipleGroups, uxLog } from "../../../../common/utils"; -import { getNotificationButtons, getOrgMarkdown } from "../../../../common/utils/notifUtils"; -import { getConfig, getReportDirectory } from "../../../../config"; -import { NotifProvider, NotifSeverity } from "../../../../common/notifProvider"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; +import { execCommand, extractRegexMatchesMultipleGroups, uxLog } from '../../../../common/utils/index.js'; +import { getNotificationButtons, getOrgMarkdown } from '../../../../common/utils/notifUtils.js'; +import { CONSTANTS, getConfig, getReportDirectory } from '../../../../config/index.js'; +import { NotifProvider, NotifSeverity } from '../../../../common/notifProvider/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class OrgTestApex extends SfdxCommand { - public static title = "Run apex tests"; +export default class OrgTestApex extends SfCommand { + public static title = 'Run apex tests'; public static description = `Run apex tests in Salesforce org @@ -28,37 +24,32 @@ If following configuration is defined, it will fail if apex coverage target is n You can override env var SFDX_TEST_WAIT_MINUTES to wait more than 60 minutes. -This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-apex-tests/) and can output Grafana, Slack and MsTeams Notifications. +This command is part of [sfdx-hardis Monitoring](${CONSTANTS.DOC_URL_ROOT}/salesforce-monitoring-apex-tests/) and can output Grafana, Slack and MsTeams Notifications. `; - public static examples = ["$ sfdx hardis:org:test:apex"]; + public static examples = ['$ sf hardis:org:test:apex']; - protected static flagsConfig = { - testlevel: flags.enum({ - char: "l", - default: "RunLocalTests", - options: ["NoTestRun", "RunSpecifiedTests", "RunLocalTests", "RunAllTestsInOrg"], - description: messages.getMessage("testLevel"), + public static flags: any = { + testlevel: Flags.string({ + char: 'l', + default: 'RunLocalTests', + options: ['NoTestRun', 'RunSpecifiedTests', 'RunLocalTests', 'RunAllTestsInOrg'], + description: messages.getMessage('testLevel'), }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default // protected static requiresProject = true; @@ -68,33 +59,33 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co protected statusMessage: string; protected coverageTarget = 75.0; protected coverageValue = 0.0; - protected failingTestClasses = []; - private notifSeverity: NotifSeverity = "log"; + protected failingTestClasses: any[] = []; + private notifSeverity: NotifSeverity = 'log'; private notifText: string; - private notifAttachments = []; - private notifAttachedFiles = []; - private orgMarkdown = ""; - private notifButtons = []; + private notifAttachments: any = []; + private notifAttachedFiles: any = []; + private orgMarkdown = ''; + private notifButtons: any[] = []; /* jscpd:ignore-start */ public async run(): Promise { - const check = this.flags.check || false; - const testlevel = this.flags.testlevel || "RunLocalTests"; - const debugMode = this.flags.debug || false; + const { flags } = await this.parse(OrgTestApex); + const testlevel = flags.testlevel || 'RunLocalTests'; + const debugMode = flags.debug || false; - this.configInfo = await getConfig("branch"); - this.orgMarkdown = await getOrgMarkdown(this.org?.getConnection()?.instanceUrl); + this.configInfo = await getConfig('branch'); + this.orgMarkdown = await getOrgMarkdown(flags['target-org']?.getConnection()?.instanceUrl); this.notifButtons = await getNotificationButtons(); /* jscpd:ignore-end */ - await this.runApexTests(testlevel, check, debugMode); + await this.runApexTests(testlevel, debugMode); // No Apex - if (this.testRunOutcome === "NoApex") { - this.notifSeverity = "log"; - this.statusMessage = "No Apex found in the org"; + if (this.testRunOutcome === 'NoApex') { + this.notifSeverity = 'log'; + this.statusMessage = 'No Apex found in the org'; this.notifText = `No Apex found in org ${this.orgMarkdown}`; } // Failed tests - else if (this.testRunOutcome === "Failed") { + else if (this.testRunOutcome === 'Failed') { await this.processApexTestsFailure(); } // Get test coverage (and fail if not reached) @@ -103,9 +94,9 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co uxLog(this, `Apex coverage: ${this.coverageValue}% (target: ${this.coverageTarget}%)`); - globalThis.jsForceConn = this?.org?.getConnection(); // Required for some notifications providers like Email + globalThis.jsForceConn = flags['target-org']?.getConnection(); // Required for some notifications providers like Email NotifProvider.postNotifications({ - type: "APEX_TESTS", + type: 'APEX_TESTS', text: this.notifText, attachments: this.notifAttachments, buttons: this.notifButtons, @@ -124,28 +115,27 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co }); // Handle output message & exit code - if (this.notifSeverity === "error") { + if (this.notifSeverity === 'error') { process.exitCode = 1; uxLog(this, c.red(this.statusMessage)); } else { uxLog(this, c.green(this.statusMessage)); } - return { orgId: this.org.getOrgId(), outputString: this.statusMessage, statusCode: process.exitCode }; + return { orgId: flags['target-org'].getOrgId(), outputString: this.statusMessage, statusCode: process.exitCode }; } - private async runApexTests(testlevel: any, check: any, debugMode: any) { + private async runApexTests(testlevel: any, debugMode: any) { // Run tests with SFDX commands const reportDir = await getReportDirectory(); const testCommand = - "sfdx force:apex:test:run" + - " --codecoverage" + - " --resultformat human" + - ` --outputdir ${reportDir}` + - ` --wait ${process.env.SFDX_TEST_WAIT_MINUTES || "60"}` + - ` --testlevel ${testlevel}` + - (check ? " --checkonly" : "") + - (debugMode ? " --verbose" : ""); + 'sf apex run test' + + ' --code-coverage' + + ' --result-format human' + + ` --output-dir ${reportDir}` + + ` --wait ${process.env.SFDX_TEST_WAIT_MINUTES || '60'}` + + ` --test-level ${testlevel}` + + (debugMode ? ' --verbose' : ''); try { const execCommandRes = await execCommand(testCommand, this, { output: true, @@ -153,37 +143,37 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co fail: true, }); // Parse outcome value from logs with Regex - this.testRunOutcome = /Outcome *(.*) */.exec(execCommandRes.stdout + execCommandRes.stderr)[1].trim(); + this.testRunOutcome = (/Outcome *(.*) */.exec(execCommandRes.stdout + execCommandRes.stderr) || '')[1].trim(); this.testRunOutputString = execCommandRes.stdout + execCommandRes.stderr; } catch (e) { // No Apex in the org if ( - e.message.includes("Toujours fournir une propriété classes, suites, tests ou testLevel") || - e.message.includes("Always provide a classes, suites, tests, or testLevel property") + (e as Error).message.includes('Toujours fournir une propriété classes, suites, tests ou testLevel') || + (e as Error).message.includes('Always provide a classes, suites, tests, or testLevel property') ) { - this.testRunOutcome = "NoApex"; + this.testRunOutcome = 'NoApex'; } else { // Failing Apex tests - this.testRunOutputString = e.message; - this.testRunOutcome = "Failed"; + this.testRunOutputString = (e as Error).message; + this.testRunOutcome = 'Failed'; } } } private async processApexTestsFailure() { - this.notifSeverity = "error"; + this.notifSeverity = 'error'; this.statusMessage = `Org apex tests failure (Outcome: ${this.testRunOutcome})`; this.notifText = `Org apex tests failure in org ${this.orgMarkdown} (Outcome: ${this.testRunOutcome})`; const reportDir = await getReportDirectory(); // Parse log from external file - const sfReportFile = path.join(reportDir, "/test-result.txt"); + const sfReportFile = path.join(reportDir, '/test-result.txt'); if (fs.existsSync(sfReportFile)) { this.notifAttachedFiles = [sfReportFile]; } // Parse failing test classes const failuresRegex = /(.*) Fail (.*)/gm; const regexMatches = await extractRegexMatchesMultipleGroups(failuresRegex, this.testRunOutputString); - uxLog(this, c.yellow("Failing tests:")); + uxLog(this, c.yellow('Failing tests:')); for (const match of regexMatches) { this.failingTestClasses.push({ name: match[1].trim(), error: match[2].trim() }); } @@ -191,81 +181,85 @@ This command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.co { text: this.failingTestClasses .map((failingTestClass) => { - return "• " + failingTestClass.name + " / " + failingTestClass.error; + return '• *' + failingTestClass.name + '*: ' + failingTestClass.error; }) - .join("\n"), + .join('\n'), }, ]; console.table(this.failingTestClasses); } private async checkOrgWideCoverage() { - const coverageOrgWide = parseFloat(/Org Wide Coverage *(.*)/.exec(this.testRunOutputString)[1].replace("%", "")); + const coverageOrgWide = parseFloat( + (/Org Wide Coverage *(.*)/.exec(this.testRunOutputString) || '')[1].replace('%', '') + ); const minCoverageOrgWide = parseFloat( process.env.APEX_TESTS_MIN_COVERAGE_ORG_WIDE || - process.env.APEX_TESTS_MIN_COVERAGE || - this.configInfo.apexTestsMinCoverageOrgWide || - this.configInfo.apexTestsMinCoverage || - 75.0, + process.env.APEX_TESTS_MIN_COVERAGE || + this.configInfo.apexTestsMinCoverageOrgWide || + this.configInfo.apexTestsMinCoverage || + 75.0 ); this.coverageTarget = minCoverageOrgWide; this.coverageValue = coverageOrgWide; // Do not test if tests failed - if (this.testRunOutcome !== "Passed") { + if (this.testRunOutcome !== 'Passed') { return; } // Developer tried to cheat in config ^^ if (minCoverageOrgWide < 75.0) { - this.notifSeverity = "error"; + this.notifSeverity = 'error'; this.statusMessage = `Don't try to cheat with configuration: Minimum org wide coverage must be 75% ;)`; this.notifText = this.statusMessage; } // Min coverage not reached else if (coverageOrgWide < minCoverageOrgWide) { - this.notifSeverity = "error"; - this.statusMessage = `Test run coverage (org wide) ${coverageOrgWide}% should be > to ${minCoverageOrgWide}%`; + this.notifSeverity = 'error'; + this.statusMessage = `Test run coverage (org wide) *${coverageOrgWide}%* should be > to ${minCoverageOrgWide}%`; this.notifText = `${this.statusMessage} in ${this.orgMarkdown}`; } // We are good ! else { - this.notifSeverity = "log"; - this.statusMessage = `Test run coverage (org wide) ${coverageOrgWide}% is > to ${minCoverageOrgWide}%`; + this.notifSeverity = 'log'; + this.statusMessage = `Test run coverage (org wide) *${coverageOrgWide}%* is > to ${minCoverageOrgWide}%`; this.notifText = `${this.statusMessage} in ${this.orgMarkdown}`; } } private async checkTestRunCoverage() { - if (this.testRunOutputString.includes("Test Run Coverage")) { + if (this.testRunOutputString.includes('Test Run Coverage')) { // const coverageTestRun = parseFloat(testRes.result.summary.testRunCoverage.replace('%', '')); - const coverageTestRun = parseFloat(/Test Run Coverage *(.*)/.exec(this.testRunOutputString)[1].replace("%", "")); + const coverageTestRun = parseFloat( + (/Test Run Coverage *(.*)/.exec(this.testRunOutputString) || '')[1].replace('%', '') + ); const minCoverageTestRun = parseFloat( process.env.APEX_TESTS_MIN_COVERAGE_TEST_RUN || - process.env.APEX_TESTS_MIN_COVERAGE || - this.configInfo.apexTestsMinCoverage || - this.coverageTarget, + process.env.APEX_TESTS_MIN_COVERAGE || + this.configInfo.apexTestsMinCoverage || + this.coverageTarget ); this.coverageTarget = minCoverageTestRun; this.coverageValue = coverageTestRun; // Do not test if tests failed - if (this.testRunOutcome !== "Passed") { + if (this.testRunOutcome !== 'Passed') { return; } // Developer tried to cheat in config ^^ if (minCoverageTestRun < 75.0) { - this.notifSeverity = "error"; + this.notifSeverity = 'error'; this.statusMessage = `Don't try to cheat with configuration: Minimum test run coverage must be 75% ;)`; this.notifText = this.statusMessage; } // Min coverage not reached else if (coverageTestRun < minCoverageTestRun) { - this.notifSeverity = "error"; - this.statusMessage = `Test run coverage ${coverageTestRun}% should be > to ${minCoverageTestRun}%`; + this.notifSeverity = 'error'; + this.statusMessage = `Test run coverage *${coverageTestRun}%* should be > to ${minCoverageTestRun}%`; this.notifText = `${this.statusMessage} in ${this.orgMarkdown}`; } // We are good ! else { - this.notifSeverity = "log"; - this.statusMessage = `Test run coverage ${coverageTestRun}% is > to ${minCoverageTestRun}%`; + this.notifSeverity = 'log'; + this.statusMessage = `Test run coverage *${coverageTestRun}%* is > to ${minCoverageTestRun}%`; this.notifText = `${this.statusMessage} in ${this.orgMarkdown}`; } } diff --git a/src/commands/hardis/org/user/activateinvalid.ts b/src/commands/hardis/org/user/activateinvalid.ts index 76e680b88..078cdab46 100644 --- a/src/commands/hardis/org/user/activateinvalid.ts +++ b/src/commands/hardis/org/user/activateinvalid.ts @@ -1,24 +1,20 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as columnify from "columnify"; -import * as sortArray from "sort-array"; -import { isCI, uxLog } from "../../../../common/utils"; -import { prompts } from "../../../../common/utils/prompts"; -import { bulkQuery, bulkUpdate, soqlQuery } from "../../../../common/utils/apiUtils"; -import { promptProfiles } from "../../../../common/utils/orgUtils"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class OrgUserActiveInvalid extends SfdxCommand { - public static title = "Reactivate sandbox invalid users"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import columnify from 'columnify'; +import sortArray from 'sort-array'; +import { isCI, uxLog } from '../../../../common/utils/index.js'; +import { prompts } from '../../../../common/utils/prompts.js'; +import { bulkQuery, bulkUpdate, soqlQuery } from '../../../../common/utils/apiUtils.js'; +import { promptProfiles } from '../../../../common/utils/orgUtils.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class OrgUserActiveInvalid extends SfCommand { + public static title = 'Reactivate sandbox invalid users'; public static description = `Update sandbox users so their email is valid @@ -30,52 +26,49 @@ See article below `; public static examples = [ - `$ sfdx hardis:org:user:activateinvalid`, - `$ sfdx hardis:org:user:activateinvalid --targetusername myuser@myorg.com`, - `$ sfdx hardis:org:user:activateinvalid --profiles 'System Administrator,MyCustomProfile' --targetusername myuser@myorg.com`, + `$ sf hardis:org:user:activateinvalid`, + `$ sf hardis:org:user:activateinvalid --target-org myuser@myorg.com`, + `$ sf hardis:org:user:activateinvalid --profiles 'System Administrator,MyCustomProfile' --target-org myuser@myorg.com`, ]; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - profiles: flags.string({ - char: "p", - description: "Comma-separated list of profiles names that you want to reactive users assigned to and with a .invalid email", + public static flags: any = { + profiles: Flags.string({ + char: 'p', + description: + 'Comma-separated list of profiles names that you want to reactive users assigned to and with a .invalid email', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; - protected profiles = []; + protected profiles: any[] = []; protected maxUsersDisplay = 100; protected debugMode = false; /* jscpd:ignore-end */ public async run(): Promise { - this.profiles = this.flags.profiles ? this.flags.profiles.split(",") : null; + const { flags } = await this.parse(OrgUserActiveInvalid); + this.profiles = flags.profiles ? flags.profiles.split(',') : []; const hasProfileConstraint = this.profiles !== null; - this.debugMode = this.flags.debug || false; + this.debugMode = flags.debug || false; - const conn = this.org.getConnection(); + const conn = flags['target-org'].getConnection(); // Query users that we want to freeze uxLog(this, c.cyan(`Querying User records with email ending with .invalid...`)); @@ -100,46 +93,46 @@ See article below // Request confirmation or selection from user if (!isCI && !hasProfileConstraint) { const confirmSelect = await prompts({ - type: "select", - name: "value", + type: 'select', + name: 'value', initial: true, message: c.cyanBright( - `Do you want to replace invalid mails by valid mails for all ${c.bold(usersToActivate.length)} found users in org ${c.green( - this.org.getUsername(), - )} ?`, + `Do you want to replace invalid mails by valid mails for all ${c.bold( + usersToActivate.length + )} found users in org ${c.green(flags['target-org'].getUsername())} ?` ), choices: [ - { title: `Yes, all ${c.bold(usersToActivate.length)} users`, value: "all" }, - { title: "No, i want to manually select by profile(s)", value: "selectProfiles" }, - { title: "No, i want to manually select user(s)", value: "select" }, + { title: `Yes, all ${c.bold(usersToActivate.length)} users`, value: 'all' }, + { title: 'No, i want to manually select by profile(s)', value: 'selectProfiles' }, + { title: 'No, i want to manually select user(s)', value: 'select' }, ], }); // Let users select profiles to reactivate users - if (confirmSelect.value === "selectProfiles") { - const selectedProfileIds = await promptProfiles(this.org.getConnection(), { + if (confirmSelect.value === 'selectProfiles') { + const selectedProfileIds = await promptProfiles(flags['target-org'].getConnection(), { multiselect: true, - returnField: "Id", - message: "Please select profiles that you want to reactivate users with .invalid emails", + returnField: 'Id', + message: 'Please select profiles that you want to reactivate users with .invalid emails', }); usersToActivateFinal = usersToActivateFinal.filter((user) => selectedProfileIds.includes(user.ProfileId)); } // Let users select users to reactivate - else if (confirmSelect.value === "select") { + else if (confirmSelect.value === 'select') { const usersSorted = sortArray(usersToActivate, { - by: ["Name", "Email"], - order: ["asc"], + by: ['Name', 'Email'], + order: ['asc'], }); const selectUsers = await prompts({ - type: "multiselect", - name: "value", - message: "Please select users that you want to remove the .invalid from emails", - choices: usersSorted.map((user) => { + type: 'multiselect', + name: 'value', + message: 'Please select users that you want to remove the .invalid from emails', + choices: usersSorted.map((user: any) => { return { title: `${user.Name} - ${user.Email}`, value: user }; }), }); usersToActivateFinal = selectUsers.value; - } else if (confirmSelect.value !== "all") { - const outputString = "Script cancelled by user"; + } else if (confirmSelect.value !== 'all') { + const outputString = 'Script cancelled by user'; uxLog(this, c.yellow(outputString)); return { outputString }; } @@ -147,25 +140,37 @@ See article below // Process invalid users reactivation const userToActivateUpdated = usersToActivateFinal.map((user) => { - const emailReplaced = user.Email.replace(".invalid", ""); + const emailReplaced = user.Email.replace('.invalid', ''); return { Id: user.Id, Email: emailReplaced }; }); - const bulkUpdateRes = await bulkUpdate("User", "update", userToActivateUpdated, conn); - - uxLog(this, "\n" + c.white(columnify(this.debugMode ? userToActivateUpdated : userToActivateUpdated.slice(0, this.maxUsersDisplay)))); - - const activateSuccessNb = bulkUpdateRes.successRecordsNb; - const activateErrorNb = bulkUpdateRes.errorRecordsNb; + const bulkUpdateRes = await bulkUpdate('User', 'update', userToActivateUpdated, conn); + + uxLog( + this, + '\n' + + c.white( + columnify(this.debugMode ? userToActivateUpdated : userToActivateUpdated.slice(0, this.maxUsersDisplay)) + ) + ); + + const activateSuccessNb = bulkUpdateRes.successfulResults.length; + const activateErrorNb = bulkUpdateRes.failedResults.length; if (activateErrorNb > 0) { - uxLog(this, c.yellow(`Warning: ${c.red(c.bold(activateErrorNb))} users has not been reactivated (bulk API errors)`)); + uxLog( + this, + c.yellow(`Warning: ${c.red(c.bold(activateErrorNb))} users has not been reactivated (bulk API errors)`) + ); } // Build results summary - uxLog(this, c.green(`${c.bold(activateSuccessNb)} users has been be reactivated by removing the .invalid of their email`)); + uxLog( + this, + c.green(`${c.bold(activateSuccessNb)} users has been be reactivated by removing the .invalid of their email`) + ); // Return an object to be displayed with --json return { - orgId: this.org.getOrgId(), + orgId: flags['target-org'].getOrgId(), activateSuccessNb: activateSuccessNb, activateErrorNb: activateErrorNb, outputString: `${activateSuccessNb} sandbox users has been be reactivated by removing the .invalid of their email`, diff --git a/src/commands/hardis/org/user/freeze.ts b/src/commands/hardis/org/user/freeze.ts index 1f60512ee..b60b74735 100644 --- a/src/commands/hardis/org/user/freeze.ts +++ b/src/commands/hardis/org/user/freeze.ts @@ -1,76 +1,67 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as columnify from "columnify"; -import { generateReports, isCI, uxLog } from "../../../../common/utils"; -import { promptProfiles } from "../../../../common/utils/orgUtils"; -//import { executeApex } from "../../../../common/utils/deployUtils"; -import { prompts } from "../../../../common/utils/prompts"; -import { soqlQuery, bulkQuery, bulkUpdate } from "../../../../common/utils/apiUtils"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import columnify from 'columnify'; +import { generateReports, isCI, uxLog } from '../../../../common/utils/index.js'; +import { promptProfiles } from '../../../../common/utils/orgUtils.js'; +//import { executeApex } from "../../../../common/utils/deployUtils.js"; +import { prompts } from '../../../../common/utils/prompts.js'; +import { soqlQuery, bulkQuery, bulkUpdate } from '../../../../common/utils/apiUtils.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class OrgFreezeUser extends SfCommand { + public static title = 'Freeze user logins'; -export default class OrgFreezeUser extends SfdxCommand { - public static title = "Freeze user logins"; - - public static description = messages.getMessage("orgfreezeUser"); + public static description = messages.getMessage('orgfreezeUser'); public static examples = [ - `$ sfdx hardis:org:user:freeze`, - `$ sfdx hardis:org:user:freeze --targetusername myuser@myorg.com`, - `$ sfdx hardis:org:user:freeze --includeprofiles 'Standard'`, - `$ sfdx hardis:org:user:freeze --excludeprofiles 'System Administrator,Some Other Profile'`, + `$ sf hardis:org:user:freeze`, + `$ sf hardis:org:user:freeze --target-org myuser@myorg.com`, + `$ sf hardis:org:user:freeze --includeprofiles 'Standard'`, + `$ sf hardis:org:user:freeze --excludeprofiles 'System Administrator,Some Other Profile'`, ]; // public static args = [{name: 'file'}]; - protected static flagsConfig = { + public static flags: any = { // flag with a value (-n, --name=VALUE) - name: flags.string({ - char: "n", - description: messages.getMessage("nameFilter"), + name: Flags.string({ + char: 'n', + description: messages.getMessage('nameFilter'), }), - includeprofiles: flags.string({ - char: "p", - description: "List of profiles that you want to freeze, separated by commas", + includeprofiles: Flags.string({ + char: 'p', + description: 'List of profiles that you want to freeze, separated by commas', }), - excludeprofiles: flags.string({ - char: "e", - description: "List of profiles that you want to NOT freeze, separated by commas", + excludeprofiles: Flags.string({ + char: 'e', + description: 'List of profiles that you want to NOT freeze, separated by commas', }), - maxuserdisplay: flags.number({ - char: "m", + maxuserdisplay: Flags.integer({ + char: 'm', default: 100, - description: "Maximum users to display in logs", + description: 'Maximum users to display in logs', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; protected maxUsersDisplay = 100; protected debugMode = false; @@ -78,26 +69,28 @@ export default class OrgFreezeUser extends SfdxCommand { /* jscpd:ignore-end */ public async run(): Promise { - const includeProfileNames = this.flags.includeprofiles ? this.flags.includeprofiles.split(",") : []; - const excludeProfileNames = this.flags.excludeprofiles ? this.flags.excludeprofiles.split(",") : []; - this.maxUsersDisplay = this.flags.maxuserdisplay || 100; - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(OrgFreezeUser); + const includeProfileNames = flags.includeprofiles ? flags.includeprofiles.split(',') : []; + const excludeProfileNames = flags.excludeprofiles ? flags.excludeprofiles.split(',') : []; + this.maxUsersDisplay = flags.maxuserdisplay || 100; + this.debugMode = flags.debug || false; - const conn = this.org.getConnection(); + const conn = flags['target-org'].getConnection(); // Select profiles that we want users to be frozen - let profileIds = []; - let profileNames = []; + let profileIds: any[] = []; + let profileNames: any[] = []; if (includeProfileNames.length === 0 && excludeProfileNames.length === 0) { // Manual user selection const profilesRes = await promptProfiles(conn, { multiselect: true, - message: "Please select profiles that you do you want to freeze users that are assigned to them ?", - returnField: "record", + message: 'Please select profiles that you do you want to freeze users that are assigned to them ?', + returnField: 'record', allowSelectMine: false, allowSelectMineErrorMessage: "If you freeze your own profile, you'll be unable to unfreeze it later :)", allowSelectAll: false, - allowSelectAllErrorMessage: "You can not select all profiles, keep at least one (usually System Administrator) so you can unfreeze later !", + allowSelectAllErrorMessage: + 'You can not select all profiles, keep at least one (usually System Administrator) so you can unfreeze later !', }); profileIds = profilesRes.map((profile) => profile.Id); profileNames = profilesRes.map((profile) => { @@ -105,7 +98,7 @@ export default class OrgFreezeUser extends SfdxCommand { }); } else if (includeProfileNames.length > 0) { // Use includeprofiles argument - const profilesConstraintIn = includeProfileNames.map((profileName) => `'${profileName}'`).join(","); + const profilesConstraintIn = includeProfileNames.map((profileName) => `'${profileName}'`).join(','); const profilesQuery = `SELECT Id,Name FROM Profile WHERE Name IN (${profilesConstraintIn})`; const profilesQueryRes = await soqlQuery(profilesQuery, conn); if (this.debugMode) { @@ -117,7 +110,7 @@ export default class OrgFreezeUser extends SfdxCommand { }); } else if (excludeProfileNames.length > 0) { // Use excludeprofiles argument - const profilesConstraintIn = excludeProfileNames.map((profileName) => `'${profileName}'`).join(","); + const profilesConstraintIn = excludeProfileNames.map((profileName) => `'${profileName}'`).join(','); const profilesQuery = `SELECT Id,Name FROM Profile WHERE Name NOT IN (${profilesConstraintIn})`; const profilesQueryRes = await soqlQuery(profilesQuery, conn); if (this.debugMode) { @@ -130,14 +123,14 @@ export default class OrgFreezeUser extends SfdxCommand { } // List profiles that must be frozen - const profileIdsStr = profileIds.map((profileId) => `'${profileId}'`).join(","); + const profileIdsStr = profileIds.map((profileId) => `'${profileId}'`).join(','); // Query users that we want to freeze uxLog(this, c.cyan(`Querying User records matching ${c.bold(profileIds.length)} profiles...`)); const userQuery = `SELECT Id,Name,Username,ProfileId FROM User WHERE ProfileId IN (${profileIdsStr}) and IsActive=true`; const userQueryRes = await bulkQuery(userQuery, conn); const usersToFreeze = userQueryRes.records; - const userIdsStr = usersToFreeze.map((user) => `'${user.Id}'`).join(","); + const userIdsStr = usersToFreeze.map((user) => `'${user.Id}'`).join(','); // Check empty result if (usersToFreeze.length === 0) { @@ -161,29 +154,35 @@ export default class OrgFreezeUser extends SfdxCommand { Profile: profileNames.filter((profile) => profile[0] === matchingUser.ProfileId)[1], }; }); - uxLog(this, "\n" + c.white(columnify(this.debugMode ? usersToFreezeDisplay : usersToFreezeDisplay.slice(0, this.maxUsersDisplay)))); + uxLog( + this, + '\n' + + c.white(columnify(this.debugMode ? usersToFreezeDisplay : usersToFreezeDisplay.slice(0, this.maxUsersDisplay))) + ); if (!this.debugMode === false && usersToFreezeDisplay.length > this.maxUsersDisplay) { uxLog(this, c.yellow(c.italic(`(list truncated to the first ${this.maxUsersDisplay} users)`))); } uxLog(this, c.cyan(`${c.bold(userLoginsToFreeze.length)} users can be frozen.`)); // Generate csv + xls of users about to be frozen - await generateReports(usersToFreezeDisplay, ["Username", "Name", "Profile"], this, { - logFileName: "users-to-freeze", - logLabel: "Extract of users to freeze", + await generateReports(usersToFreezeDisplay, ['Username', 'Name', 'Profile'], this, { + logFileName: 'users-to-freeze', + logLabel: 'Extract of users to freeze', }); // Request configuration from user if (!isCI) { const confirmfreeze = await prompts({ - type: "confirm", - name: "value", + type: 'confirm', + name: 'value', initial: true, message: c.cyanBright( - `Are you sure you want to freeze these ${c.bold(userLoginsToFreeze.length)} users in org ${c.green(this.org.getUsername())} (y/n)?`, + `Are you sure you want to freeze these ${c.bold(userLoginsToFreeze.length)} users in org ${c.green( + flags['target-org'].getUsername() + )} (y/n)?` ), }); if (confirmfreeze.value !== true) { - const outputString = "Script cancelled by user"; + const outputString = 'Script cancelled by user'; uxLog(this, c.yellow(outputString)); return { outputString }; } @@ -193,10 +192,10 @@ export default class OrgFreezeUser extends SfdxCommand { const userLoginsFrozen = userLoginsToFreeze.map((userLogin) => { return { Id: userLogin.Id, IsFrozen: true }; }); - const bulkUpdateRes = await bulkUpdate("UserLogin", "update", userLoginsFrozen, conn); + const bulkUpdateRes = await bulkUpdate('UserLogin', 'update', userLoginsFrozen, conn); - const freezeSuccessNb = bulkUpdateRes.successRecordsNb; - const freezeErrorsNb = bulkUpdateRes.errorRecordsNb; + const freezeSuccessNb = bulkUpdateRes.successfulResults.length; + const freezeErrorsNb = bulkUpdateRes.failedResults.length; if (freezeErrorsNb > 0) { uxLog(this, c.yellow(`Warning: ${c.red(c.bold(freezeErrorsNb))} users has not been frozen (bulk API errors)`)); } @@ -206,7 +205,7 @@ export default class OrgFreezeUser extends SfdxCommand { // Return an object to be displayed with --json return { - orgId: this.org.getOrgId(), + orgId: flags['target-org'].getOrgId(), freezeSuccess: freezeSuccessNb, freezeErrors: freezeErrorsNb, outputString: `${freezeSuccessNb} users has been be frozen`, diff --git a/src/commands/hardis/org/user/unfreeze.ts b/src/commands/hardis/org/user/unfreeze.ts index 170c941f3..21b6e5d34 100644 --- a/src/commands/hardis/org/user/unfreeze.ts +++ b/src/commands/hardis/org/user/unfreeze.ts @@ -1,76 +1,67 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as columnify from "columnify"; -import { generateReports, isCI, uxLog } from "../../../../common/utils"; -import { promptProfiles } from "../../../../common/utils/orgUtils"; -//import { executeApex } from "../../../../common/utils/deployUtils"; -import { prompts } from "../../../../common/utils/prompts"; -import { soqlQuery, bulkQuery, bulkUpdate } from "../../../../common/utils/apiUtils"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import columnify from 'columnify'; +import { generateReports, isCI, uxLog } from '../../../../common/utils/index.js'; +import { promptProfiles } from '../../../../common/utils/orgUtils.js'; +//import { executeApex } from "../../../../common/utils/deployUtils.js"; +import { prompts } from '../../../../common/utils/prompts.js'; +import { soqlQuery, bulkQuery, bulkUpdate } from '../../../../common/utils/apiUtils.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class OrgUnfreezeUser extends SfCommand { + public static title = 'Unfreeze user logins'; -export default class OrgUnfreezeUser extends SfdxCommand { - public static title = "Unfreeze user logins"; - - public static description = messages.getMessage("orgUnfreezeUser"); + public static description = messages.getMessage('orgUnfreezeUser'); public static examples = [ - `$ sfdx hardis:org:user:unfreeze`, - `$ sfdx hardis:org:user:unfreeze --targetusername myuser@myorg.com`, - `$ sfdx hardis:org:user:unfreeze --includeprofiles 'Standard'`, - `$ sfdx hardis:org:user:unfreeze --excludeprofiles 'System Administrator,Some Other Profile'`, + `$ sf hardis:org:user:unfreeze`, + `$ sf hardis:org:user:unfreeze --target-org myuser@myorg.com`, + `$ sf hardis:org:user:unfreeze --includeprofiles 'Standard'`, + `$ sf hardis:org:user:unfreeze --excludeprofiles 'System Administrator,Some Other Profile'`, ]; // public static args = [{name: 'file'}]; - protected static flagsConfig = { + public static flags: any = { // flag with a value (-n, --name=VALUE) - name: flags.string({ - char: "n", - description: messages.getMessage("nameFilter"), + name: Flags.string({ + char: 'n', + description: messages.getMessage('nameFilter'), }), - includeprofiles: flags.string({ - char: "p", - description: "List of profiles that you want to unfreeze, separated by commas", + includeprofiles: Flags.string({ + char: 'p', + description: 'List of profiles that you want to unfreeze, separated by commas', }), - excludeprofiles: flags.string({ - char: "e", - description: "List of profiles that you want to NOT unfreeze, separated by commas", + excludeprofiles: Flags.string({ + char: 'e', + description: 'List of profiles that you want to NOT unfreeze, separated by commas', }), - maxuserdisplay: flags.number({ - char: "m", + maxuserdisplay: Flags.integer({ + char: 'm', default: 100, - description: "Maximum users to display in logs", + description: 'Maximum users to display in logs', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; protected maxUsersDisplay = 100; protected debugMode = false; @@ -78,22 +69,23 @@ export default class OrgUnfreezeUser extends SfdxCommand { /* jscpd:ignore-end */ public async run(): Promise { - const includeProfileNames = this.flags.includeprofiles ? this.flags.includeprofiles.split(",") : []; - const excludeProfileNames = this.flags.excludeprofiles ? this.flags.excludeprofiles.split(",") : []; - this.maxUsersDisplay = this.flags.maxuserdisplay || 100; - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(OrgUnfreezeUser); + const includeProfileNames = flags.includeprofiles ? flags.includeprofiles.split(',') : []; + const excludeProfileNames = flags.excludeprofiles ? flags.excludeprofiles.split(',') : []; + this.maxUsersDisplay = flags.maxuserdisplay || 100; + this.debugMode = flags.debug || false; - const conn = this.org.getConnection(); + const conn = flags['target-org'].getConnection(); // Select profiles that we want users to be unfrozen - let profileIds = []; - let profileNames = []; + let profileIds: any[] = []; + let profileNames: any[] = []; if (includeProfileNames.length === 0 && excludeProfileNames.length === 0) { // Manual user selection const profilesRes = await promptProfiles(conn, { multiselect: true, - message: "Please select profiles that you do you want to unfreeze users that are assigned to them ?", - returnField: "record", + message: 'Please select profiles that you do you want to unfreeze users that are assigned to them ?', + returnField: 'record', }); profileIds = profilesRes.map((profile) => profile.Id); profileNames = profilesRes.map((profile) => { @@ -101,7 +93,7 @@ export default class OrgUnfreezeUser extends SfdxCommand { }); } else if (includeProfileNames.length > 0) { // Use includeprofiles argument - const profilesConstraintIn = includeProfileNames.map((profileName) => `'${profileName}'`).join(","); + const profilesConstraintIn = includeProfileNames.map((profileName) => `'${profileName}'`).join(','); const profilesQuery = `SELECT Id,Name FROM Profile WHERE Name IN (${profilesConstraintIn})`; const profilesQueryRes = await soqlQuery(profilesQuery, conn); if (this.debugMode) { @@ -113,7 +105,7 @@ export default class OrgUnfreezeUser extends SfdxCommand { }); } else if (excludeProfileNames.length > 0) { // Use excludeprofiles argument - const profilesConstraintIn = excludeProfileNames.map((profileName) => `'${profileName}'`).join(","); + const profilesConstraintIn = excludeProfileNames.map((profileName) => `'${profileName}'`).join(','); const profilesQuery = `SELECT Id,Name FROM Profile WHERE Name NOT IN (${profilesConstraintIn})`; const profilesQueryRes = await soqlQuery(profilesQuery, conn); if (this.debugMode) { @@ -126,14 +118,14 @@ export default class OrgUnfreezeUser extends SfdxCommand { } // List profiles that must be unfrozen - const profileIdsStr = profileIds.map((profileId) => `'${profileId}'`).join(","); + const profileIdsStr = profileIds.map((profileId) => `'${profileId}'`).join(','); // Query users that we want to unfreeze uxLog(this, c.cyan(`Querying User records matching ${c.bold(profileIds.length)} profiles...`)); const userQuery = `SELECT Id,Name,Username,ProfileId FROM User WHERE ProfileId IN (${profileIdsStr}) and IsActive=true`; const userQueryRes = await bulkQuery(userQuery, conn); const usersToUnfreeze = userQueryRes.records; - const userIdsStr = usersToUnfreeze.map((user) => `'${user.Id}'`).join(","); + const userIdsStr = usersToUnfreeze.map((user) => `'${user.Id}'`).join(','); // Check empty result if (usersToUnfreeze.length === 0) { @@ -157,29 +149,37 @@ export default class OrgUnfreezeUser extends SfdxCommand { Profile: profileNames.filter((profile) => profile[0] === matchingUser.ProfileId)[1], }; }); - uxLog(this, "\n" + c.white(columnify(this.debugMode ? usersToUnfreezeDisplay : usersToUnfreezeDisplay.slice(0, this.maxUsersDisplay)))); + uxLog( + this, + '\n' + + c.white( + columnify(this.debugMode ? usersToUnfreezeDisplay : usersToUnfreezeDisplay.slice(0, this.maxUsersDisplay)) + ) + ); if (!this.debugMode === false && usersToUnfreezeDisplay.length > this.maxUsersDisplay) { uxLog(this, c.yellow(c.italic(`(list truncated to the first ${this.maxUsersDisplay} users)`))); } uxLog(this, c.cyan(`${c.bold(userLoginsToUnfreeze.length)} users can be unfrozen.`)); // Generate csv + xls of users about to be unfrozen - await generateReports(usersToUnfreezeDisplay, ["Username", "Name", "Profile"], this, { - logFileName: "users-to-unfreeze", - logLabel: "Extract of users to unfreeze", + await generateReports(usersToUnfreezeDisplay, ['Username', 'Name', 'Profile'], this, { + logFileName: 'users-to-unfreeze', + logLabel: 'Extract of users to unfreeze', }); // Request configuration from user if (!isCI) { const confirmunfreeze = await prompts({ - type: "confirm", - name: "value", + type: 'confirm', + name: 'value', initial: true, message: c.cyanBright( - `Are you sure you want to unfreeze these ${c.bold(userLoginsToUnfreeze.length)} users in org ${c.green(this.org.getUsername())} (y/n)?`, + `Are you sure you want to unfreeze these ${c.bold(userLoginsToUnfreeze.length)} users in org ${c.green( + flags['target-org'].getUsername() + )} (y/n)?` ), }); if (confirmunfreeze.value !== true) { - const outputString = "Script cancelled by user"; + const outputString = 'Script cancelled by user'; uxLog(this, c.yellow(outputString)); return { outputString }; } @@ -189,12 +189,15 @@ export default class OrgUnfreezeUser extends SfdxCommand { const userLoginsFrozen = userLoginsToUnfreeze.map((userLogin) => { return { Id: userLogin.Id, IsFrozen: false }; }); - const bulkUpdateRes = await bulkUpdate("UserLogin", "update", userLoginsFrozen, conn); + const bulkUpdateRes = await bulkUpdate('UserLogin', 'update', userLoginsFrozen, conn); - const unfreezeSuccessNb = bulkUpdateRes.successRecordsNb; - const unfreezeErrorsNb = bulkUpdateRes.errorRecordsNb; + const unfreezeSuccessNb = bulkUpdateRes.successfulResults.length; + const unfreezeErrorsNb = bulkUpdateRes.failedResults.length; if (unfreezeErrorsNb > 0) { - uxLog(this, c.yellow(`Warning: ${c.red(c.bold(unfreezeErrorsNb))} users has not been unfrozen (bulk API errors)`)); + uxLog( + this, + c.yellow(`Warning: ${c.red(c.bold(unfreezeErrorsNb))} users has not been unfrozen (bulk API errors)`) + ); } // Build results summary @@ -202,7 +205,7 @@ export default class OrgUnfreezeUser extends SfdxCommand { // Return an object to be displayed with --json return { - orgId: this.org.getOrgId(), + orgId: flags['target-org'].getOrgId(), unfreezeSuccess: unfreezeSuccessNb, unfreezeErrors: unfreezeErrorsNb, outputString: `${unfreezeSuccessNb} users has been be unfrozen`, diff --git a/src/commands/hardis/package/create.ts b/src/commands/hardis/package/create.ts index 44daf1fc7..2137f97ba 100644 --- a/src/commands/hardis/package/create.ts +++ b/src/commands/hardis/package/create.ts @@ -1,82 +1,75 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { execSfdxJson, uxLog } from "../../../common/utils"; -import { prompts } from "../../../common/utils/prompts"; +import { SfCommand, Flags, requiredHubFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { execSfdxJson, uxLog } from '../../../common/utils/index.js'; +import { prompts } from '../../../common/utils/prompts.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class PackageCreate extends SfCommand { + public static title = 'Create a new package'; -export default class PackageCreate extends SfdxCommand { - public static title = "Create a new package"; + public static description = messages.getMessage('packageCreate'); - public static description = messages.getMessage("packageCreate"); - - public static examples = ["$ sfdx hardis:package:create"]; + public static examples = ['$ sf hardis:package:create']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-dev-hub': requiredHubFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; /* jscpd:ignore-end */ public async run(): Promise { - const debugMode = this.flags.debug || false; + const { flags } = await this.parse(PackageCreate); + const debugMode = flags.debug || false; // Request questions to user const packageResponse = await prompts([ { - type: "text", - name: "packageName", + type: 'text', + name: 'packageName', message: c.cyanBright(`Please input the name of the package (ex: MyPackage)`), }, { - type: "text", - name: "packagePath", + type: 'text', + name: 'packagePath', message: c.cyanBright(`Please input the path of the package (ex: sfdx-source/apex-mocks)`), }, { - type: "select", - name: "packageType", + type: 'select', + name: 'packageType', message: c.cyanBright(`Please select the type of the package`), choices: [ { - title: "Managed", - value: "Managed", - description: "Managed packages code is hidden in orgs where it is installed. Suited for AppExchanges packages", + title: 'Managed', + value: 'Managed', + description: + 'Managed packages code is hidden in orgs where it is installed. Suited for AppExchanges packages', }, { - title: "Unlocked", - value: "Unlocked", + title: 'Unlocked', + value: 'Unlocked', description: - "Unlocked packages code is readable and modifiable in orgs where it is installed. Use it for client project or shared tooling", + 'Unlocked packages code is readable and modifiable in orgs where it is installed. Use it for client project or shared tooling', }, ], }, @@ -84,16 +77,23 @@ export default class PackageCreate extends SfdxCommand { // Create package const packageCreateCommand = - "sfdx force:package:create" + + 'sf package create' + ` --name "${packageResponse.packageName}"` + - ` --packagetype ${packageResponse.packageType}` + + ` --package-type ${packageResponse.packageType}` + ` --path "${packageResponse.packagePath}"`; const packageCreateResult = await execSfdxJson(packageCreateCommand, this, { output: true, fail: true, debug: debugMode, }); - uxLog(this, c.cyan(`Created package Id: ${c.green(packageCreateResult.result.Id)} associated to DevHub ${c.green(this.hubOrg.getUsername())}`)); + uxLog( + this, + c.cyan( + `Created package Id: ${c.green(packageCreateResult.result.Id)} associated to DevHub ${c.green( + flags['target-dev-hub'].getUsername() + )}` + ) + ); // Return an object to be displayed with --json return { diff --git a/src/commands/hardis/package/install.ts b/src/commands/hardis/package/install.ts index 1d8fbdc0e..c4a6e0206 100644 --- a/src/commands/hardis/package/install.ts +++ b/src/commands/hardis/package/install.ts @@ -1,109 +1,100 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as axios1 from "axios"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import axios from 'axios'; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; // import * as packages from '../../../../defaults/packages.json' -import { MetadataUtils } from "../../../common/metadata-utils"; -import { isCI, uxLog } from "../../../common/utils"; -import { managePackageConfig } from "../../../common/utils/orgUtils"; -import { prompts } from "../../../common/utils/prompts"; -import { PACKAGE_ROOT_DIR } from "../../../settings"; +import { MetadataUtils } from '../../../common/metadata-utils/index.js'; +import { isCI, uxLog } from '../../../common/utils/index.js'; +import { managePackageConfig } from '../../../common/utils/orgUtils.js'; +import { prompts } from '../../../common/utils/prompts.js'; +import { PACKAGE_ROOT_DIR } from '../../../settings.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -const axios = axios1.default; - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class PackageVersionInstall extends SfdxCommand { - public static title = "Install packages in an org"; +export default class PackageVersionInstall extends SfCommand { + public static title = 'Install packages in an org'; public static description = `Install a package in an org using its id (starting with **04t**) Assisted menu to propose to update \`installedPackages\` property in \`.sfdx-hardis.yml\` `; - public static examples = ["$ sfdx hardis:package:install"]; + public static examples = ['$ sf hardis:package:install']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - package: flags.string({ - char: "p", - description: "Package Version Id to install (04t...)", + public static flags: any = { + package: Flags.string({ + char: 'p', + description: 'Package Version Id to install (04t...)', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - installationkey: flags.string({ - char: "k", - default: null, - description: messages.getMessage("packageInstallationKey"), + installationkey: Flags.string({ + char: 'k', + default: '', + description: messages.getMessage('packageInstallationKey'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; - /* jscpd:ignore-end */ - protected allPackagesFileName = path.join(PACKAGE_ROOT_DIR, "defaults/packages.json"); - protected sfdxProjectJsonFileName = path.join(process.cwd(), "sfdx-project.json"); + protected allPackagesFileName = path.join(PACKAGE_ROOT_DIR, 'defaults/packages.json'); + protected sfdxProjectJsonFileName = path.join(process.cwd(), 'sfdx-project.json'); public async run(): Promise { - const packagesRaw = await fs.readFile(this.allPackagesFileName, "utf8"); + const { flags } = await this.parse(PackageVersionInstall); + const packagesRaw = await fs.readFile(this.allPackagesFileName, 'utf8'); const packages = JSON.parse(packagesRaw); - const packageId = this.flags.package || null; - const packagesToInstall = []; + const packageId = flags.package || null; + const packagesToInstall: any[] = []; // If no package Id is sent, ask user what package he/she wants to install - if (!isCI && (packageId == null || !packageId.startsWith("04t"))) { + if (!isCI && (packageId == null || !packageId.startsWith('04t'))) { const allPackages = packages.map((pack) => ({ - title: `${c.yellow(pack.name)} - ${pack.repoUrl || "Bundle"}`, + title: `${c.yellow(pack.name)} - ${pack.repoUrl || 'Bundle'}`, value: pack, })); - allPackages.push({ title: "Other", value: "other" }); + allPackages.push({ title: 'Other', value: 'other' }); const packageResponse = await prompts({ - type: "select", - name: "value", - message: c.cyanBright(`Please select the package you want to install on org ${c.green(this.org.getUsername())}`), + type: 'select', + name: 'value', + message: c.cyanBright( + `Please select the package you want to install on org ${c.green(flags['target-org'].getUsername())}` + ), choices: allPackages, initial: 0, }); - if (packageResponse.value === "other") { + if (packageResponse.value === 'other') { const packageDtlResponse = await prompts([ { - type: "text", - name: "value", + type: 'text', + name: 'value', message: c.cyanBright( - "What is the id of the Package Version to install ? (starting with 04t)\nYou can find it using tooling api request " + - c.bold("Select Id,SubscriberPackage.Name,SubscriberPackageVersionId from InstalledSubscriberPackage"), + 'What is the id of the Package Version to install ? (starting with 04t)\nYou can find it using tooling api request ' + + c.bold('Select Id,SubscriberPackage.Name,SubscriberPackageVersionId from InstalledSubscriberPackage') ), }, { - type: "text", - name: "installationkey", - message: c.cyanBright("Enter the password for this package (leave empty if package is not protected by a password)"), + type: 'text', + name: 'installationkey', + message: c.cyanBright( + 'Enter the password for this package (leave empty if package is not protected by a password)' + ), }, ]); const pckg: { SubscriberPackageVersionId?: string; installationkey?: string } = { @@ -125,10 +116,10 @@ Assisted menu to propose to update \`installedPackages\` property in \`.sfdx-har } } else { const pckg: { SubscriberPackageVersionId: string; installationkey?: string } = { - SubscriberPackageVersionId: packageId, + SubscriberPackageVersionId: packageId || '', }; - if (this.flags.installationkey) { - pckg.installationkey = this.flags.installationkey; + if (flags.installationkey) { + pckg.installationkey = flags.installationkey; } packagesToInstall.push(pckg); } @@ -140,7 +131,7 @@ Assisted menu to propose to update \`installedPackages\` property in \`.sfdx-har const configResp = await axios.get(pckg.configUrl); const packageAliases = configResp.data.packageAliases || []; pckg.SubscriberPackageName = pckg.package; - if (pckg.package.includes("@")) { + if (pckg.package.includes('@')) { pckg.SubscriberPackageVersionId = packageAliases[pckg.package]; } else { // use last occurrence of package alias @@ -153,76 +144,19 @@ Assisted menu to propose to update \`installedPackages\` property in \`.sfdx-har } } return pckg; - }), + }) ); // Install packages - await MetadataUtils.installPackagesOnOrg(packagesToInstallCompleted, null, this, "install"); + await MetadataUtils.installPackagesOnOrg(packagesToInstallCompleted, null, this, 'install'); const installedPackages = await MetadataUtils.listInstalledPackages(null, this); - uxLog(this, c.italic(c.grey("New package list on org:\n" + JSON.stringify(installedPackages, null, 2)))); + uxLog(this, c.italic(c.grey('New package list on org:\n' + JSON.stringify(installedPackages, null, 2)))); if (!isCI) { // Manage package install config storage await managePackageConfig(installedPackages, packagesToInstallCompleted); } - /* disabled until sfdx multiple package deployment is working >_< - // Post install actions - if (!isCI && fs.existsSync(this.sfdxProjectJsonFileName)) { - - const postInstallResponse = await prompts([ - { - type: 'confirm', - name: 'retrieve', - message: c.cyanBright('Do you want to retrieve installed package sources in your local branch ?'), - initial: true - }, - { - type: 'confirm', - name: 'sfdxProject', - message: c.cyanBright('Do you want to update your sfdx-project.json ? (advice: yes)'), - initial: true - } - ]); - // Retrieve package sources if requested - if (postInstallResponse.retrieve === true) { - for (const pckg of packagesToInstallCompleted) { - const retrieveCommand = 'sfdx force:source:retrieve' + - ` -n ${pckg.key}` + - // ` -p ./force-app/main/default` + // let's try without it - ' -w 60'; - try { - await execCommand(retrieveCommand, this, { output: true, fail: true, debug: debugMode }); - } catch (e) { - // Ugly workaround but it's a sfdx bug... - uxLog(this, c.yellow(`Error while retrieving ${c.bold(pckg.key)} but it may have worked anyway`)); - if (fs.existsSync(path.join('.', pckg.key))) { - await fs.remove(path.join('.', pckg.key)); - } - } - } - } - // Update sfdx-project.json with new unlocked packages folder references, so it is taken in account with force:source:push and force:source:pull - if (postInstallResponse.sfdxProject === true) { - const sfdxProjectRaw = await fs.readFile(this.sfdxProjectJsonFileName, 'utf8'); - const sfdxProject = JSON.parse(sfdxProjectRaw); - let updated = false; - for (const installedPackage of installedPackages) { - const matchInstalled = packagesToInstallCompleted.filter(pckg => pckg.key === installedPackage.SubscriberPackageName); - const matchLocal = sfdxProject.packageDirectories.filter(packageDirectory => installedPackage.SubscriberPackageName === packageDirectory.path); - if (matchInstalled.length > 0 && matchLocal.length === 0) { - sfdxProject.packageDirectories.push({path: installedPackage.SubscriberPackageName}); - updated = true; - } - } - if (updated) { - await fs.writeFile(this.sfdxProjectJsonFileName, JSON.stringify(sfdxProject, null, 2)); - uxLog(this, c.cyan('[config] Updated sfdx-project.json to add new package folders')); - } - } - } - */ - // Return an object to be displayed with --json - return { outputString: "Installed package(s)" }; + return { outputString: 'Installed package(s)' }; } } diff --git a/src/commands/hardis/package/mergexml.ts b/src/commands/hardis/package/mergexml.ts index 7bedbb140..df5953602 100644 --- a/src/commands/hardis/package/mergexml.ts +++ b/src/commands/hardis/package/mergexml.ts @@ -1,85 +1,78 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import { glob } from "glob"; -import * as path from "path"; -import { execCommand, uxLog } from "../../../common/utils"; -import { prompts } from "../../../common/utils/prompts"; -import { WebSocketClient } from "../../../common/websocketClient"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class MergePackageXml extends SfdxCommand { - public static title = "Merge package.xml files"; - - public static description = "Select and merge package.xml files"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import { glob } from 'glob'; +import * as path from 'path'; +import { uxLog } from '../../../common/utils/index.js'; +import { prompts } from '../../../common/utils/prompts.js'; +import { WebSocketClient } from '../../../common/websocketClient.js'; +import { appendPackageXmlFilesContent } from '../../../common/utils/xmlUtils.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class MergePackageXml extends SfCommand { + public static title = 'Merge package.xml files'; + + public static description = 'Select and merge package.xml files'; public static examples = [ - "$ sfdx hardis:package:mergexml", - "$ sfdx hardis:package:mergexml --folder packages --pattern /**/*.xml --result myMergedPackage.xml", - '$ sfdx hardis:package:mergexml --packagexmls "config/mypackage1.xml,config/mypackage2.xml,config/mypackage3.xml" --result myMergedPackage.xml', + '$ sf hardis:package:mergexml', + '$ sf hardis:package:mergexml --folder packages --pattern /**/*.xml --result myMergedPackage.xml', + '$ sf hardis:package:mergexml --packagexmls "config/mypackage1.xml,config/mypackage2.xml,config/mypackage3.xml" --result myMergedPackage.xml', ]; - protected static flagsConfig = { - folder: flags.string({ - char: "f", - default: "manifest", - description: "Root folder", + public static flags: any = { + folder: Flags.string({ + char: 'f', + default: 'manifest', + description: 'Root folder', }), - packagexmls: flags.string({ - char: "p", - description: "Comma separated list of package.xml files to merge. Will be prompted to user if not provided", + packagexmls: Flags.string({ + char: 'p', + description: 'Comma separated list of package.xml files to merge. Will be prompted to user if not provided', }), - pattern: flags.string({ - char: "x", - default: "/**/*package*.xml", - description: "Name criteria to list package.xml files", + pattern: Flags.string({ + char: 'x', + default: '/**/*package*.xml', + description: 'Name criteria to list package.xml files', }), - result: flags.string({ - char: "r", - description: "Result package.xml file name", + result: Flags.string({ + char: 'r', + description: 'Result package.xml file name', }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + debug: Flags.boolean({ + default: false, + description: 'debug', }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + websocket: Flags.string({ + description: messages.getMessage('websocket'), + }), + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; - - // List required plugins, their presence will be tested before running the command - protected static requiresSfdxPlugins = ["sfdx-essentials"]; + public static requiresProject = false; protected folder: string; protected pattern: string; - protected packageXmlFiles = []; + protected packageXmlFiles: any[] = []; protected resultFileName: string; protected debugMode = false; public async run(): Promise { - this.folder = this.flags.folder || "./manifest"; - this.pattern = this.flags.pattern || "/**/*package*.xml"; - this.packageXmlFiles = this.flags.packagexmls ? this.flags.packagexmls.split(",") : []; - this.resultFileName = this.flags.result || path.join(this.folder, "package-merge.xml"); + const { flags } = await this.parse(MergePackageXml); + this.folder = flags.folder || './manifest'; + this.pattern = flags.pattern || '/**/*package*.xml'; + this.packageXmlFiles = flags.packagexmls ? flags.packagexmls.split(',') : []; + this.resultFileName = flags.result || path.join(this.folder, 'package-merge.xml'); await fs.ensureDir(path.dirname(this.resultFileName)); - this.debugMode = this.flags.debug || false; + this.debugMode = flags.debug || false; /* jscpd:ignore-end */ // If packagexmls are not provided, prompt user @@ -88,9 +81,9 @@ export default class MergePackageXml extends SfdxCommand { const findPackageXmlPattern = rootFolder + this.pattern; const matchingFiles = await glob(findPackageXmlPattern, { cwd: process.cwd() }); const filesSelectRes = await prompts({ - type: "multiselect", - name: "files", - message: "Please select the package.xml files you want to merge", + type: 'multiselect', + name: 'files', + message: 'Please select the package.xml files you want to merge', choices: matchingFiles.map((file) => { const relativeFile = path.relative(process.cwd(), file); return { title: relativeFile, value: relativeFile }; @@ -100,12 +93,7 @@ export default class MergePackageXml extends SfdxCommand { } // Process merge of package.xml files - const appendPackageXmlCommand = - "sfdx essentials:packagexml:append" + ` --packagexmls "${this.packageXmlFiles.join(",")}"` + ` --outputfile "${this.resultFileName}"`; - await execCommand(appendPackageXmlCommand, this, { - fail: true, - debug: this.debugMode, - }); + await appendPackageXmlFilesContent(this.packageXmlFiles, this.resultFileName); // Summary const msg = `Merged ${c.green(c.bold(this.packageXmlFiles.length))} files into ${c.green(this.resultFileName)}`; diff --git a/src/commands/hardis/package/version/create.ts b/src/commands/hardis/package/version/create.ts index 03a7c13f7..1121d6cc5 100644 --- a/src/commands/hardis/package/version/create.ts +++ b/src/commands/hardis/package/version/create.ts @@ -1,111 +1,105 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages, SfdxError } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { MetadataUtils } from "../../../../common/metadata-utils"; -import { execSfdxJson, isCI, uxLog } from "../../../../common/utils"; -import { prompts } from "../../../../common/utils/prompts"; -import { getConfig, setConfig } from "../../../../config"; +import { SfCommand, Flags, requiredHubFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages, SfError } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { MetadataUtils } from '../../../../common/metadata-utils/index.js'; +import { execSfdxJson, isCI, uxLog } from '../../../../common/utils/index.js'; +import { prompts } from '../../../../common/utils/prompts.js'; +import { getConfig, setConfig } from '../../../../config/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class PackageVersionCreate extends SfCommand { + public static title = 'Create a new version of a package'; -export default class PackageVersionCreate extends SfdxCommand { - public static title = "Create a new version of a package"; + public static description = messages.getMessage('packageVersionCreate'); - public static description = messages.getMessage("packageVersionCreate"); - - public static examples = ["$ sfdx hardis:package:version:create"]; + public static examples = ['$ sf hardis:package:version:create']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - package: flags.string({ - char: "p", - default: null, - description: "Package identifier that you want to use to generate a new package version", + package: Flags.string({ + char: 'p', + default: '', + description: 'Package identifier that you want to use to generate a new package version', }), - installkey: flags.string({ - char: "k", - default: null, - description: "Package installation key", + installkey: Flags.string({ + char: 'k', + default: '', + description: 'Package installation key', }), - deleteafter: flags.boolean({ + deleteafter: Flags.boolean({ default: false, - description: "Delete package version after creating it", + description: 'Delete package version after creating it', }), - install: flags.boolean({ - char: "i", + install: Flags.boolean({ + char: 'i', default: false, - description: "Install package version on default org after generation", + description: 'Install package version on default org after generation', }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-dev-hub': requiredHubFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; - protected package: string; + protected package: string | null; protected deleteAfter = false; protected install = false; - protected installKey = null; + protected installKey: string | null = null; protected promote = false; /* jscpd:ignore-end */ public async run(): Promise { - this.package = this.flags.package || null; - this.install = this.flags.install || false; - this.installKey = this.flags.installkey || null; - this.deleteAfter = this.flags.deleteafter || false; - this.promote = this.flags.promote || false; - const debugMode = this.flags.debug || false; - const config = await getConfig("project"); + const { flags } = await this.parse(PackageVersionCreate); + this.package = flags.package || null; + this.install = flags.install || false; + this.installKey = flags.installkey || null; + this.deleteAfter = flags.deleteafter || false; + //this.promote = flags.promote || false; + const debugMode = flags.debug || false; + const config = await getConfig('project'); // List project packages - const packageDirectories = this.project.getUniquePackageDirectories(); + const packageDirectories: any[] = this.project?.getUniquePackageDirectories() || []; // Ask user to select package and input install key if not sent as command arguments if (this.package == null) { if (isCI) { - throw new SfdxError("You need to send argument 'package'"); + throw new SfError("You need to send argument 'package'"); } const packageResponse = await prompts([ { - type: "select", - name: "packageSelected", - message: c.cyanBright(`Please select a package (this is not a drill, it will create an official new version !)`), + type: 'select', + name: 'packageSelected', + message: c.cyanBright( + `Please select a package (this is not a drill, it will create an official new version !)` + ), choices: packageDirectories.map((packageDirectory) => { return { - title: packageDirectory.package || packageDirectory.path, + title: packageDirectory?.package || packageDirectory?.path || packageDirectory?.fullPath || packageDirectory?.name, value: packageDirectory.name, }; }), }, { - type: "text", - name: "packageInstallationKey", + type: 'text', + name: 'packageInstallationKey', message: c.cyanBright(`Please input an installation password (or let empty)`), - initial: config.defaultPackageInstallationKey || "", + initial: config.defaultPackageInstallationKey || '', }, ]); this.package = packageResponse.packageSelected; @@ -113,21 +107,21 @@ export default class PackageVersionCreate extends SfdxCommand { } // Identify package directory const pckgDirectory = packageDirectories.filter( - (pckgDirectory) => pckgDirectory.name === this.package || pckgDirectory.package === this.package, + (pckgDirectory) => pckgDirectory.name === this.package || pckgDirectory.package === this.package )[0]; if (config.defaultPackageInstallationKey !== this.installKey && this.installKey != null) { - await setConfig("project", { + await setConfig('project', { defaultPackageInstallationKey: this.installKey, }); } // Create package version uxLog(this, c.cyan(`Generating new package version for ${c.green(pckgDirectory.package)}...`)); const createCommand = - "sfdx force:package:version:create" + + 'sf package version create' + ` --package "${pckgDirectory.package}"` + - (this.installKey ? ` --installationkey "${this.installKey}"` : " --installationkeybypass") + - " --codecoverage" + - " -w 60"; + (this.installKey ? ` --installation-key "${this.installKey}"` : ' --installation-key-bypass') + + ' --code-coverage' + + ' --wait 60'; const createResult = await execSfdxJson(createCommand, this, { fail: true, output: true, @@ -138,20 +132,23 @@ export default class PackageVersionCreate extends SfdxCommand { // If delete after is true, delete package version we just created if (this.deleteAfter) { // Delete package version - uxLog(this, c.cyan(`Delete new package version ${c.green(latestVersion)} of package ${c.green(pckgDirectory.package)}...`)); - const deleteVersionCommand = "sfdx force:package:version:delete --noprompt -p " + latestVersion; + uxLog( + this, + c.cyan(`Delete new package version ${c.green(latestVersion)} of package ${c.green(pckgDirectory.package)}...`) + ); + const deleteVersionCommand = 'sf package version delete --no-prompt --package ' + latestVersion; const deleteVersionResult = await execSfdxJson(deleteVersionCommand, this, { fail: true, output: true, debug: debugMode, }); if (!(deleteVersionResult.result.success === true)) { - throw new SfdxError(`Unable to delete package version ${latestVersion}`); + throw new SfError(`Unable to delete package version ${latestVersion}`); } } // Install package on org just after is has been generated else if (this.install) { - const packagesToInstall = []; + const packagesToInstall: any[] = []; const pckg: { SubscriberPackageVersionId?: string; installationkey?: string } = { SubscriberPackageVersionId: latestVersion, }; @@ -159,12 +156,12 @@ export default class PackageVersionCreate extends SfdxCommand { pckg.installationkey = this.installKey; } packagesToInstall.push(pckg); - await MetadataUtils.installPackagesOnOrg(packagesToInstall, null, this, "install"); + await MetadataUtils.installPackagesOnOrg(packagesToInstall, null, this, 'install'); } // Return an object to be displayed with --json return { - outputString: "Generated new package version", + outputString: 'Generated new package version', packageVersionId: latestVersion, }; } diff --git a/src/commands/hardis/package/version/list.ts b/src/commands/hardis/package/version/list.ts index 6cac3a375..51e929bf7 100644 --- a/src/commands/hardis/package/version/list.ts +++ b/src/commands/hardis/package/version/list.ts @@ -1,59 +1,51 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import { execCommand } from "../../../../common/utils"; +import { SfCommand, Flags, requiredHubFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { execCommand } from '../../../../common/utils/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class PackageVersionList extends SfCommand { + public static title = 'Create a new version of a package'; -export default class PackageVersionCreate extends SfdxCommand { - public static title = "Create a new version of a package"; + public static description = messages.getMessage('packageVersionList'); - public static description = messages.getMessage("packageVersionList"); - - public static examples = ["$ sfdx hardis:package:version:list"]; + public static examples = ['$ sf hardis:package:version:list']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-dev-hub': requiredHubFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; /* jscpd:ignore-end */ public async run(): Promise { - const debugMode = this.flags.debug || false; - const createCommand = "sfdx force:package:version:list"; + const { flags } = await this.parse(PackageVersionList); + const debugMode = flags.debug || false; + const createCommand = 'sf package version list'; await execCommand(createCommand, this, { fail: true, output: true, debug: debugMode, }); // Return an object to be displayed with --json - return { outputString: "Listed package versions" }; + return { outputString: 'Listed package versions' }; } } diff --git a/src/commands/hardis/package/version/promote.ts b/src/commands/hardis/package/version/promote.ts index c6b5776e6..b51887bfe 100644 --- a/src/commands/hardis/package/version/promote.ts +++ b/src/commands/hardis/package/version/promote.ts @@ -1,75 +1,70 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { execSfdxJson, uxLog } from "../../../../common/utils"; -import { prompts } from "../../../../common/utils/prompts"; +import { SfCommand, Flags, requiredHubFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { execSfdxJson, uxLog } from '../../../../common/utils/index.js'; +import { prompts } from '../../../../common/utils/prompts.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class PackageVersionPromote extends SfCommand { + public static title = 'Promote new versions of package(s)'; -export default class PackageVersionPromote extends SfdxCommand { - public static title = "Promote new versions of package(s)"; + public static description = 'Promote package(s) version(s): convert it from beta to released'; - public static description = "Promote package(s) version(s): convert it from beta to released"; - - public static examples = ["$ sfdx hardis:package:version:promote", "$ sfdx hardis:package:version:promote --auto"]; + public static examples = ['$ sf hardis:package:version:promote', '$ sf hardis:package:version:promote --auto']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - auto: flags.boolean({ - char: "d", + public static flags: any = { + auto: Flags.boolean({ + char: 'd', default: false, - description: "Auto-detect which versions of which packages need to be promoted", + description: 'Auto-detect which versions of which packages need to be promoted', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-dev-hub': requiredHubFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; /* jscpd:ignore-end */ public async run(): Promise { - const debugMode = this.flags.debug || false; - const auto = this.flags.auto || false; + const { flags } = await this.parse(PackageVersionPromote); + const debugMode = flags.debug || false; + const auto = flags.auto || false; // List project packages - const sfdxProjectJson = await this.project.retrieveSfdxProjectJson(false); - const packageAliases = sfdxProjectJson.get("packageAliases") || []; + const sfdxProjectJson: any = this.project?.getSfProjectJson(false) || {}; + const packageAliases = sfdxProjectJson.get('packageAliases') || []; const availablePackageAliases = {}; for (const packageAlias of Object.keys(packageAliases) .sort() - .filter((pckgAlias) => pckgAlias.includes("@"))) { - const packageName = packageAlias.split("@")[0]; + .filter((pckgAlias) => pckgAlias.includes('@'))) { + const packageName = packageAlias.split('@')[0]; availablePackageAliases[packageName] = packageAlias; } // Select packages to promote - const packagesToPromote = []; + const packagesToPromote: any[] = []; if (auto) { // Promote only packages not promoted yet - const packageListRes = await execSfdxJson("sfdx force:package:version:list --released", this, { output: true, fail: true }); + const packageListRes = await execSfdxJson('sf package version list --released', this, { + output: true, + fail: true, + }); const filteredPackagesToPromote = Object.values(availablePackageAliases).filter((packageAlias) => { return ( packageListRes.result.filter((releasedPackage) => { @@ -82,9 +77,11 @@ export default class PackageVersionPromote extends SfdxCommand { // Prompt user if not auto const packageResponse = await prompts([ { - type: "select", - name: "packageSelected", - message: c.cyanBright(`Please select a package (this is not a drill, it will create an official new version !)`), + type: 'select', + name: 'packageSelected', + message: c.cyanBright( + `Please select a package (this is not a drill, it will create an official new version !)` + ), choices: Object.values(availablePackageAliases).map((packageAlias) => { return { title: packageAlias, value: packageAlias }; }), @@ -94,13 +91,13 @@ export default class PackageVersionPromote extends SfdxCommand { packagesToPromote.push(packageResponse.packageSelected); } - const promotedPackageVersions = []; - const errorPromotedVersions = []; + const promotedPackageVersions: any[] = []; + const errorPromotedVersions: any[] = []; // Promote packages for (const packageToPromote of packagesToPromote) { uxLog(this, c.cyan(`Promoting version of package ${c.green(packageToPromote)}`)); - const promoteCommand = "sfdx force:package:version:promote" + ` --package "${packageToPromote}"` + " --noprompt"; + const promoteCommand = 'sf package version promote' + ` --package "${packageToPromote}"` + ' --no-prompt'; const promoteResult = await execSfdxJson(promoteCommand, this, { fail: false, output: false, @@ -111,18 +108,27 @@ export default class PackageVersionPromote extends SfdxCommand { this, c.cyan( `Promoted package version ${c.green(packageToPromote)} with id ${c.green( - promoteResult.result.id, - )}. It is now installable on production orgs`, - ), + promoteResult.result.id + )}. It is now installable on production orgs` + ) ); promotedPackageVersions.push({ package: packageToPromote, result: promoteResult }); } else { - uxLog(this, c.yellow(`Error promoting package version ${c.red(packageToPromote)} (probably already promoted so it can be ok)`)); + uxLog( + this, + c.yellow( + `Error promoting package version ${c.red(packageToPromote)} (probably already promoted so it can be ok)` + ) + ); errorPromotedVersions.push({ package: packageToPromote, result: promoteResult }); } } process.exitCode = errorPromotedVersions.length === 0 ? 0 : 1; // Return an object to be displayed with --json - return { outputString: "Promoted packages", promotedPackageVersions: promotedPackageVersions, errorPromotedVersions: errorPromotedVersions }; + return { + outputString: 'Promoted packages', + promotedPackageVersions: promotedPackageVersions, + errorPromotedVersions: errorPromotedVersions, + }; } } diff --git a/src/commands/hardis/packagexml/append.ts b/src/commands/hardis/packagexml/append.ts new file mode 100644 index 000000000..2788cecf9 --- /dev/null +++ b/src/commands/hardis/packagexml/append.ts @@ -0,0 +1,38 @@ +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { AnyJson } from "@salesforce/ts-types"; +import { appendPackageXmlFilesContent } from "../../../common/utils/xmlUtils.js"; + +export class PackageXmlAppend extends SfCommand { + public static readonly description = `Append one or multiple package.xml files into a single one`; + public static readonly examples = ["$ sf hardis packagexml append -p package1.xml,package2.xml -o package3.xml"]; + public static readonly flags: any = { + packagexmls: Flags.string({ + char: "p", + description: "package.xml files path (separated by commas)", + required: true + }), + outputfile: Flags.string({ + char: "o", + description: "package.xml output file", + required: true + }), + debug: Flags.boolean({ + default: false, + description: "debug", + }), + websocket: Flags.string({ + description: "websocket", + }), + }; + + protected packageXmlFiles: string[]; + protected outputFile: string; + + public async run(): Promise { + const { flags } = await this.parse(PackageXmlAppend); + this.packageXmlFiles = (flags.packagexmls || "").split(","); + this.outputFile = flags.outputfile; + await appendPackageXmlFilesContent(this.packageXmlFiles, this.outputFile); + return { outputPackageXmlFile: this.outputFile }; + } +} diff --git a/src/commands/hardis/packagexml/remove.ts b/src/commands/hardis/packagexml/remove.ts new file mode 100644 index 000000000..01ca57cde --- /dev/null +++ b/src/commands/hardis/packagexml/remove.ts @@ -0,0 +1,57 @@ +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { AnyJson } from "@salesforce/ts-types"; +import { removePackageXmlFilesContent } from "../../../common/utils/xmlUtils.js"; + +export class PackageXmlRemove extends SfCommand { + public static readonly description = `Removes the content of a package.xml file matching another package.xml file`; + public static readonly examples = ["$ sf hardis packagexml:remove -p package.xml -r destructiveChanges.xml -o my-reduced-package.xml"]; + public static readonly requiresProject = false; + public static readonly flags: any = { + packagexml: Flags.string({ + char: 'p', + description: 'package.xml file to reduce' + }), + removepackagexml: Flags.string({ + char: 'r', + description: 'package.xml file to use to filter input package.xml' + }), + removedonly: Flags.boolean({ + char: 'z', + description: 'Use this flag to generate a package.xml with only removed items', + default: false + }), + outputfile: Flags.string({ + char: 'o', + description: 'package.xml output file', + required: true + }), + debug: Flags.boolean({ + default: false, + description: "debug", + }), + websocket: Flags.string({ + description: "websocket", + }), + }; + + protected packageXmlFile: string; + protected removePackageXmlFile: string; + protected removedOnly = false; + protected outputFile: string; + + public async run(): Promise { + const { flags } = await this.parse(PackageXmlRemove); + this.packageXmlFile = flags.packagexml || 'package.xml'; + this.removePackageXmlFile = flags.removepackagexml || 'destructiveChanges.xml'; + this.removedOnly = flags.removedonly || false; + this.outputFile = flags.outputfile; + + await removePackageXmlFilesContent( + this.packageXmlFile, + this.removePackageXmlFile, + { logFlag: flags.debug, outputXmlFile: this.outputFile, removedOnly: this.removedOnly } + ); + + return { outputPackageXmlFile: this.outputFile }; + } +} diff --git a/src/commands/hardis/project/audit/apiversion.ts b/src/commands/hardis/project/audit/apiversion.ts index 0ea22e32b..5019ab1b1 100644 --- a/src/commands/hardis/project/audit/apiversion.ts +++ b/src/commands/hardis/project/audit/apiversion.ts @@ -1,78 +1,68 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages, SfdxError } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import { glob } from "glob"; -import * as sortArray from "sort-array"; -import { catchMatches, generateReports, uxLog } from "../../../../common/utils"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages, SfError } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import { glob } from 'glob'; +import sortArray from 'sort-array'; +import { catchMatches, generateReports, uxLog } from '../../../../common/utils/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class CallInCallOut extends SfCommand { + public static title = 'Audit Metadatas API Version'; -export default class CallInCallOut extends SfdxCommand { - public static title = "Audit Metadatas API Version"; + public static description = messages.getMessage('auditApiVersion'); - public static description = messages.getMessage("auditApiVersion"); - - public static examples = ["$ sfdx hardis:project:audit:apiversion"]; + public static examples = ['$ sf hardis:project:audit:apiversion']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - minimumapiversion: flags.number({ - char: "m", + public static flags: any = { + minimumapiversion: Flags.integer({ + char: 'm', default: 20.0, - description: messages.getMessage("minimumApiVersion"), + description: messages.getMessage('minimumApiVersion'), }), - failiferror: flags.boolean({ - char: "f", + failiferror: Flags.boolean({ + char: 'f', default: false, - description: messages.getMessage("failIfError"), + description: messages.getMessage('failIfError'), }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; /* jscpd:ignore-end */ protected matchResults: any[] = []; public async run(): Promise { - this.debug = this.flags.debug || false; - const minimumApiVersion = this.flags.minimumapiversion || false; - const failIfError = this.flags.failiferror || false; + const { flags } = await this.parse(CallInCallOut); + const minimumApiVersion = flags.minimumapiversion || false; + const failIfError = flags.failiferror || false; - const pattern = "**/*.xml"; + const pattern = '**/*.xml'; const catchers = [ { - type: "apiVersion", - subType: "", + type: 'apiVersion', + subType: '', regex: /(.*?)<\/apiVersion>/gims, - detail: [{ name: "apiVersion", regex: /(.*?)<\/apiVersion>/gims }], + detail: [{ name: 'apiVersion', regex: /(.*?)<\/apiVersion>/gims }], }, ]; const xmlFiles = await glob(pattern); @@ -81,7 +71,7 @@ export default class CallInCallOut extends SfdxCommand { /* jscpd:ignore-start */ // Loop in files for (const file of xmlFiles) { - const fileText = await fs.readFile(file, "utf8"); + const fileText = await fs.readFile(file, 'utf8'); // Loop on criteria to find matches in this file for (const catcher of catchers) { const catcherMatchResults = await catchMatches(catcher, file, fileText, this); @@ -95,16 +85,16 @@ export default class CallInCallOut extends SfdxCommand { return { type: item.type, fileName: item.fileName, - nameSpace: item.fileName.includes("__") ? item.fileName.split("__")[0] : "Custom", - apiVersion: parseFloat(item.detail["apiVersion"]), - valid: parseFloat(item.detail["apiVersion"]) > minimumApiVersion ? "yes" : "no", + nameSpace: item.fileName.includes('__') ? item.fileName.split('__')[0] : 'Custom', + apiVersion: parseFloat(item.detail['apiVersion']), + valid: parseFloat(item.detail['apiVersion']) > (minimumApiVersion || 100) ? 'yes' : 'no', }; }); // Sort array const resultSorted = sortArray(result, { - by: ["type", "subType", "fileName"], - order: ["asc", "asc", "asc"], + by: ['type', 'subType', 'fileName'], + order: ['asc', 'asc', 'asc'], }); // Display as table @@ -113,48 +103,50 @@ export default class CallInCallOut extends SfdxCommand { resultsLight.map((item: any) => { delete item.detail; return item; - }), + }) ); // Generate output files const columns = [ - { key: "type", header: "IN/OUT" }, - { key: "fileName", header: "Apex" }, - { key: "nameSpace", header: "Namespace" }, - { key: "apiVersion", header: "API Version" }, - { key: "valid", header: `Valid ( > ${minimumApiVersion} )` }, + { key: 'type', header: 'IN/OUT' }, + { key: 'fileName', header: 'Apex' }, + { key: 'nameSpace', header: 'Namespace' }, + { key: 'apiVersion', header: 'API Version' }, + { key: 'valid', header: `Valid ( > ${minimumApiVersion} )` }, ]; const reportFiles = await generateReports(resultSorted, columns, this); - const numberOfInvalid = result.filter((res: any) => res.valid === "no").length; + const numberOfInvalid = result.filter((res: any) => res.valid === 'no').length; const numberOfValid = result.length - numberOfInvalid; if (numberOfInvalid > 0) { uxLog( this, c.yellow( - `[sfdx-hardis] WARNING: Your sources contain ${c.bold(numberOfInvalid)} metadata files with API Version lesser than ${c.bold( - minimumApiVersion, - )}`, - ), + `[sfdx-hardis] WARNING: Your sources contain ${c.bold( + numberOfInvalid + )} metadata files with API Version lesser than ${c.bold(minimumApiVersion)}` + ) ); if (failIfError) { - throw new SfdxError(c.red(`[sfdx-hardis][ERROR] ${c.bold(numberOfInvalid)} metadata files with wrong API version detected`)); + throw new SfError( + c.red(`[sfdx-hardis][ERROR] ${c.bold(numberOfInvalid)} metadata files with wrong API version detected`) + ); } } else { uxLog( this, c.green( - `[sfdx-hardis] SUCCESS: Your sources contain ${c.bold(numberOfValid)} metadata files with API Version superior to ${c.bold( - minimumApiVersion, - )}`, - ), + `[sfdx-hardis] SUCCESS: Your sources contain ${c.bold( + numberOfValid + )} metadata files with API Version superior to ${c.bold(minimumApiVersion)}` + ) ); } // Return an object to be displayed with --json return { - outputString: "Processed apiVersion audit", + outputString: 'Processed apiVersion audit', result: resultSorted, reportFiles, }; diff --git a/src/commands/hardis/project/audit/callincallout.ts b/src/commands/hardis/project/audit/callincallout.ts index e30ae5906..6338ad5dd 100644 --- a/src/commands/hardis/project/audit/callincallout.ts +++ b/src/commands/hardis/project/audit/callincallout.ts @@ -1,80 +1,68 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as fs from "fs-extra"; -import { glob } from "glob"; -import * as sortArray from "sort-array"; -import { catchMatches, generateReports, uxLog } from "../../../../common/utils"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import fs from 'fs-extra'; +import { glob } from 'glob'; +import sortArray from 'sort-array'; +import { catchMatches, generateReports, uxLog } from '../../../../common/utils/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class CallInCallOut extends SfCommand { + public static title = 'Audit CallIns and CallOuts'; -export default class CallInCallOut extends SfdxCommand { - public static title = "Audit CallIns and CallOuts"; + public static description = messages.getMessage('auditCallInCallOut'); - public static description = messages.getMessage("auditCallInCallOut"); - - public static examples = ["$ sfdx hardis:project:audit:callouts"]; + public static examples = ['$ sf hardis:project:audit:callouts']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { + public static flags: any = { // flag with a value (-n, --name=VALUE) - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; /* jscpd:ignore-end */ protected matchResults: any[] = []; public async run(): Promise { - this.debug = this.flags.debug || false; - - const pattern = "**/*.{cls,trigger}"; + const pattern = '**/*.{cls,trigger}'; const catchers = [ { - type: "INBOUND", - subType: "SOAP", + type: 'INBOUND', + subType: 'SOAP', regex: /webservice static/gim, - detail: [{ name: "webServiceName", regex: /webservice static (.*?){/gims }], + detail: [{ name: 'webServiceName', regex: /webservice static (.*?){/gims }], }, { - type: "INBOUND", - subType: "REST", + type: 'INBOUND', + subType: 'REST', regex: /@RestResource/gim, - detail: [{ name: "restResource", regex: /@RestResource\((.*?)\)/gims }], + detail: [{ name: 'restResource', regex: /@RestResource\((.*?)\)/gims }], }, { - type: "OUTBOUND", - subType: "HTTP", + type: 'OUTBOUND', + subType: 'HTTP', regex: /new HttpRequest/gim, detail: [ - { name: "endPoint", regex: /setEndpoint\((.*?);/gims }, - { name: "action", regex: /<[A-Za-z0-9_-]*:(.*?)>/gims }, + { name: 'endPoint', regex: /setEndpoint\((.*?);/gims }, + { name: 'action', regex: /<[A-Za-z0-9_-]*:(.*?)>/gims }, ], }, ]; @@ -83,8 +71,8 @@ export default class CallInCallOut extends SfdxCommand { uxLog(this, `Browsing ${apexFiles.length} files`); // Loop in files for (const file of apexFiles) { - const fileText = await fs.readFile(file, "utf8"); - if (fileText.startsWith("hidden") || fileText.includes("@isTest")) { + const fileText = await fs.readFile(file, 'utf8'); + if (fileText.startsWith('hidden') || fileText.includes('@isTest')) { continue; } // Loop on criteria to find matches in this file @@ -100,31 +88,31 @@ export default class CallInCallOut extends SfdxCommand { type: item.type, subType: item.subType, fileName: item.fileName, - nameSpace: item.fileName.includes("__") ? item.fileName.split("__")[0] : "Custom", + nameSpace: item.fileName.includes('__') ? item.fileName.split('__')[0] : 'Custom', matches: item.matches, detail: Object.keys(item.detail) .map( (key: string) => key + - ": " + + ': ' + item.detail[key] .map( (extractedText: string) => extractedText - .replace(/(\r\n|\n|\r)/gm, "") // Remove new lines from result - .replace(/\s+/g, " "), // Replace multiple whitespaces by single whitespaces + .replace(/(\r\n|\n|\r)/gm, '') // Remove new lines from result + .replace(/\s+/g, ' ') // Replace multiple whitespaces by single whitespaces ) - .join(" | "), + .join(' | ') ) - .join(" || ") || "", + .join(' || ') || '', }; }); // Sort array const resultSorted = sortArray(result, { - by: ["type", "subType", "fileName", "matches"], - order: ["asc", "asc", "asc", "desc"], + by: ['type', 'subType', 'fileName', 'matches'], + order: ['asc', 'asc', 'asc', 'desc'], }); // Display as table @@ -133,23 +121,23 @@ export default class CallInCallOut extends SfdxCommand { resultsLight.map((item: any) => { delete item.detail; return item; - }), + }) ); // Generate output files const columns = [ - { key: "type", header: "IN/OUT" }, - { key: "subType", header: "Protocol" }, - { key: "fileName", header: "Apex" }, - { key: "nameSpace", header: "Namespace" }, - { key: "matches", header: "Number" }, - { key: "detail", header: "Detail" }, + { key: 'type', header: 'IN/OUT' }, + { key: 'subType', header: 'Protocol' }, + { key: 'fileName', header: 'Apex' }, + { key: 'nameSpace', header: 'Namespace' }, + { key: 'matches', header: 'Number' }, + { key: 'detail', header: 'Detail' }, ]; const reportFiles = await generateReports(resultSorted, columns, this); // Return an object to be displayed with --json return { - outputString: "Processed callIns and callOuts audit", + outputString: 'Processed callIns and callOuts audit', result: resultSorted, reportFiles, }; diff --git a/src/commands/hardis/project/audit/duplicatefiles.ts b/src/commands/hardis/project/audit/duplicatefiles.ts index 92d8ed1c2..f3ba8cd38 100644 --- a/src/commands/hardis/project/audit/duplicatefiles.ts +++ b/src/commands/hardis/project/audit/duplicatefiles.ts @@ -1,70 +1,67 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as readFilesRecursive from "fs-readdir-recursive"; -import * as path from "path"; -import { uxLog } from "../../../../common/utils"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import readFilesRecursive from 'fs-readdir-recursive'; +import * as path from 'path'; +import { uxLog } from '../../../../common/utils/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class AuditDuplicateFiles extends SfCommand { + public static title = 'Find duplicate sfdx files'; -export default class AuditDuplicateFiles extends SfdxCommand { - public static title = "Find duplicate sfdx files"; + public static description = 'Find duplicate files in sfdx folder (often from past @salesforce/cli bugs)'; - public static description = "Find duplicate files in sfdx folder (often from past @salesforce/cli bugs)"; + public static examples = ['$ sf hardis:project:audit:duplicatefiles']; - public static examples = ["$ sfdx hardis:project:audit:duplicatefiles"]; - - protected static flagsConfig = { - path: flags.string({ - char: "p", + public static flags: any = { + path: Flags.string({ + char: 'p', default: process.cwd(), - description: "Root path to check", + description: 'Root path to check', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - protected static requiresProject = false; + public static requiresProject = false; /* jscpd:ignore-end */ protected matchResults: any[] = []; public async run(): Promise { - const pathToBrowser = this.flags.path || process.cwd(); - this.debug = this.flags.debug || false; + const { flags } = await this.parse(AuditDuplicateFiles); + const pathToBrowser = flags.path || process.cwd(); // List all files const allFiles = readFilesRecursive(pathToBrowser) - .filter((file) => !file.includes("node_modules")) + .filter((file) => !file.includes('node_modules')) .map((file) => { return { fullPath: file, fileName: path.basename(file) }; }); - uxLog(this, c.cyan(`Checking for duplicate file names in ${c.bold(pathToBrowser)}. Files: ${c.bold(allFiles.length)}`)); + uxLog( + this, + c.cyan(`Checking for duplicate file names in ${c.bold(pathToBrowser)}. Files: ${c.bold(allFiles.length)}`) + ); // Find duplicates const duplicates = {}; for (const file of allFiles) { const doublingFiles = allFiles.filter( - (f) => f.fileName === file.fileName && f.fullPath !== file.fullPath && !this.checkDoublingAllowed(file, f), + (f) => f.fileName === file.fileName && f.fullPath !== file.fullPath && !this.checkDoublingAllowed(file, f) ); if (doublingFiles.length > 0) { const doublingFullPaths = duplicates[file.fileName] || []; diff --git a/src/commands/hardis/project/audit/remotesites.ts b/src/commands/hardis/project/audit/remotesites.ts index 9084d3635..73aa1b91b 100644 --- a/src/commands/hardis/project/audit/remotesites.ts +++ b/src/commands/hardis/project/audit/remotesites.ts @@ -1,72 +1,60 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as fs from "fs-extra"; -import { glob } from "glob"; -import * as psl from "psl"; -import * as sortArray from "sort-array"; -import * as url from "url"; -import { catchMatches, generateReports, uxLog } from "../../../../common/utils"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import fs from 'fs-extra'; +import { glob } from 'glob'; +import * as psl from 'psl'; +import sortArray from 'sort-array'; +import * as url from 'url'; +import { catchMatches, generateReports, uxLog } from '../../../../common/utils/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class RemoteSites extends SfCommand { + public static title = 'Audit Remote Sites'; -export default class RemoteSites extends SfdxCommand { - public static title = "Audit Remote Sites"; + public static description = messages.getMessage('auditRemoteSites'); - public static description = messages.getMessage("auditRemoteSites"); - - public static examples = ["$ sfdx hardis:project:audit:remotesites"]; + public static examples = ['$ sf hardis:project:audit:remotesites']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { + public static flags: any = { // flag with a value (-n, --name=VALUE) - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; /* jscpd:ignore-end */ protected matchResults: any[] = []; public async run(): Promise { - this.debug = this.flags.debug || false; - - const pattern = "**/*.{remoteSite-meta.xml,remoteSite}"; + const pattern = '**/*.{remoteSite-meta.xml,remoteSite}'; const catchers = [ { - type: "", - subType: "", + type: '', + subType: '', regex: /(.*?)<\/url>/gim, detail: [ - { name: "url", regex: /(.*?)<\/url>/gims }, - { name: "active", regex: /(.*?)<\/isActive>/gims }, + { name: 'url', regex: /(.*?)<\/url>/gims }, + { name: 'active', regex: /(.*?)<\/isActive>/gims }, { - name: "description", + name: 'description', regex: /(.*?)<\/description>/gimsu, }, ], @@ -77,7 +65,7 @@ export default class RemoteSites extends SfdxCommand { uxLog(this, `Browsing ${remoteSiteSettingsFiles.length} files`); // Loop in files for (const file of remoteSiteSettingsFiles) { - const fileText = await fs.readFile(file, "utf8"); + const fileText = await fs.readFile(file, 'utf8'); // Loop on criteria to find matches in this file for (const catcher of catchers) { const catcherMatchResults = await catchMatches(catcher, file, fileText, this); @@ -88,22 +76,22 @@ export default class RemoteSites extends SfdxCommand { // Format result const result: any[] = this.matchResults.map((item: any) => { return { - name: item.fileName.replace(".remoteSite-meta.xml", "").replace(".remoteSite", ""), + name: item.fileName.replace('.remoteSite-meta.xml', '').replace('.remoteSite', ''), fileName: item.fileName, - nameSpace: item.fileName.includes("__") ? item.fileName.split("__")[0] : "Custom", + nameSpace: item.fileName.includes('__') ? item.fileName.split('__')[0] : 'Custom', matches: item.matches, - url: item.detail?.url ? item.detail.url[0] : "", - active: item.detail?.active ? "yes" : "no", - description: item.detail?.description ? item.detail.description[0] : "", - protocol: item.detail.url[0].includes("https") ? "HTTPS" : "HTTP", - domain: psl.parse(new url.URL(item.detail.url[0]).hostname).domain, + url: item.detail?.url ? item.detail.url[0] : '', + active: item.detail?.active ? 'yes' : 'no', + description: item.detail?.description ? item.detail.description[0] : '', + protocol: item.detail.url[0].includes('https') ? 'HTTPS' : 'HTTP', + domain: (psl.parse(new url.URL(item.detail.url[0]).hostname) as any)?.domain || 'Domain not found', }; }); // Sort array const resultSorted = sortArray(result, { - by: ["protocol", "domain", "name", "active", "description"], - order: ["asc", "asc", "asc", "desc", "asc"], + by: ['protocol', 'domain', 'name', 'active', 'description'], + order: ['asc', 'asc', 'asc', 'desc', 'asc'], }); // Display as table @@ -114,23 +102,23 @@ export default class RemoteSites extends SfdxCommand { delete item.detail; delete item.matches; return item; - }), + }) ); // Export into csv & excel file const columns = [ - { key: "protocol", header: "Protocol" }, - { key: "domain", header: "Domain" }, - { key: "name", header: "Name" }, - { key: "url", header: "URL" }, - { key: "active", header: "Active" }, - { key: "description", header: "Description" }, + { key: 'protocol', header: 'Protocol' }, + { key: 'domain', header: 'Domain' }, + { key: 'name', header: 'Name' }, + { key: 'url', header: 'URL' }, + { key: 'active', header: 'Active' }, + { key: 'description', header: 'Description' }, ]; const reportFiles = await generateReports(resultSorted, columns, this); // Return an object to be displayed with --json return { - outputString: "Processed callIns and callOuts audit", + outputString: 'Processed callIns and callOuts audit', result: resultSorted, reportFiles, }; diff --git a/src/commands/hardis/project/clean/emptyitems.ts b/src/commands/hardis/project/clean/emptyitems.ts index 86d98f763..aba0bb376 100644 --- a/src/commands/hardis/project/clean/emptyitems.ts +++ b/src/commands/hardis/project/clean/emptyitems.ts @@ -1,71 +1,65 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import { glob } from "glob"; -import * as path from "path"; -import { uxLog } from "../../../../common/utils"; -import { parseXmlFile } from "../../../../common/utils/xmlUtils"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import { glob } from 'glob'; +import * as path from 'path'; +import { uxLog } from '../../../../common/utils/index.js'; +import { parseXmlFile } from '../../../../common/utils/xmlUtils.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class CleanEmptyItems extends SfCommand { + public static title = 'Clean retrieved empty items in dx sources'; -export default class CleanEmptyItems extends SfdxCommand { - public static title = "Clean retrieved empty items in dx sources"; + public static description = 'Remove unwanted empty items within sfdx project sources'; - public static description = "Remove unwanted empty items within sfdx project sources"; + public static examples = ['$ sf hardis:project:clean:emptyitems']; - public static examples = ["$ sfdx hardis:project:clean:emptyitems"]; - - protected static flagsConfig = { - folder: flags.string({ - char: "f", - default: "force-app", - description: "Root folder", + public static flags: any = { + folder: Flags.string({ + char: 'f', + default: 'force-app', + description: 'Root folder', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; protected folder: string; protected debugMode = false; public async run(): Promise { - this.folder = this.flags.folder || "./force-app"; - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(CleanEmptyItems); + this.folder = flags.folder || './force-app'; + this.debugMode = flags.debug || false; // Delete standard files when necessary uxLog(this, c.cyan(`Removing empty dx managed source files`)); /* jscpd:ignore-end */ const rootFolder = path.resolve(this.folder); const emptyConstraints = [ - { globPattern: `/**/*.globalValueSetTranslation-meta.xml`, tags: ["GlobalValueSetTranslation", "valueTranslation"] }, - { globPattern: `/**/*.standardValueSet-meta.xml`, tags: ["StandardValueSet", "standardValue"] }, - { globPattern: `/**/*.sharingRules-meta.xml`, tags: ["SharingRules", "sharingOwnerRules"] }, + { + globPattern: `/**/*.globalValueSetTranslation-meta.xml`, + tags: ['GlobalValueSetTranslation', 'valueTranslation'], + }, + { globPattern: `/**/*.standardValueSet-meta.xml`, tags: ['StandardValueSet', 'standardValue'] }, + { globPattern: `/**/*.sharingRules-meta.xml`, tags: ['SharingRules', 'sharingOwnerRules'] }, ]; let counter = 0; for (const emptyConstraint of emptyConstraints) { diff --git a/src/commands/hardis/project/clean/filter-xml-content.ts b/src/commands/hardis/project/clean/filter-xml-content.ts new file mode 100644 index 000000000..efc60e7c4 --- /dev/null +++ b/src/commands/hardis/project/clean/filter-xml-content.ts @@ -0,0 +1,193 @@ +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; +import * as util from 'util'; +import * as xml2js from 'xml2js'; +import { AnyJson } from '@salesforce/ts-types'; +import { uxLog } from '../../../../common/utils/index.js'; +import { writeXmlFile } from '../../../../common/utils/xmlUtils.js'; + +// The code of this method is awful... it's migrated from sfdx-essentials, written when async / await were not existing ^^ +export class FilterXmlContent extends SfCommand { + public static readonly description = `Filter content of metadatas (XML) in order to be able to deploy only part of them on an org (See [Example configuration](https://github.com/nvuillam/sfdx-essentials/blob/master/examples/filter-xml-content-config.json)) + +When you perform deployments from one org to another, the features activated in the target org may not fit the content of the sfdx/metadata files extracted from the source org. + +You may need to filter some elements in the XML files, for example in the Profiles + +This script requires a filter-config.json file`; + public static readonly examples = [ + 'sf hardis:project:clean:filter-xml-content -i "./mdapi_output"', + 'sf hardis:project:clean:filter-xml-content -i "retrieveUnpackaged"', + ]; + public static readonly requiresProject = true; + public static readonly flags: any = { + configfile: Flags.string({ + char: 'c', + description: 'Config JSON file path', + }), + inputfolder: Flags.string({ + char: 'i', + description: 'Input folder (default: "." )', + }), + outputfolder: Flags.string({ + char: 'o', + description: 'Output folder (default: parentFolder + _xml_content_filtered)', + }), + debug: Flags.boolean({ + default: false, + description: 'debug', + }), + websocket: Flags.string({ + description: 'websocket', + }), + }; + + // Input params properties + public configFile: string; + public inputFolder: string; + public outputFolder: string; + + // Internal properties + public smmryUpdatedFiles = {}; + public smmryResult = { filterResults: {} }; + + public async run(): Promise { + const { flags } = await this.parse(FilterXmlContent); + this.configFile = flags.configfile || './filter-config.json'; + this.inputFolder = flags.inputfolder || '.'; + this.outputFolder = + flags.outputfolder || + './' + path.dirname(this.inputFolder) + '/' + path.basename(this.inputFolder) + '_xml_content_filtered'; + uxLog( + this, + c.cyan( + `Initialize XML content filtering of ${this.inputFolder}, using ${c.bold(this.configFile)} , into ${this.outputFolder + }` + ) + ); + // Read json config file + const filterConfig = fs.readJsonSync(this.configFile); + if (flags.debug) { + uxLog(this, c.grey('Filtering config file content:\n' + JSON.stringify(filterConfig, null, 2))); + } + + // Create output folder/empty it if existing + if (fs.existsSync(this.outputFolder) && this.outputFolder !== this.inputFolder) { + uxLog(this, c.grey('Empty output folder ' + this.outputFolder)); + fs.emptyDirSync(this.outputFolder); + } else if (!fs.existsSync(this.outputFolder)) { + uxLog(this, c.grey('Create output folder ' + this.outputFolder)); + fs.mkdirSync(this.outputFolder); + } + + // Copy input folder to output folder + if (this.outputFolder !== this.inputFolder) { + uxLog(this, 'Copy in output folder ' + this.outputFolder); + fs.copySync(this.inputFolder, this.outputFolder); + } + + // Browse filters + filterConfig.filters.forEach((filter) => { + uxLog(this, c.grey(filter.name + ' (' + filter.description + ')...')); + // Browse filter folders + filter.folders.forEach((filterFolder) => { + // Browse folder files + if (!fs.existsSync(this.outputFolder + '/' + filterFolder)) { + return; + } + const folderFiles = fs.readdirSync(this.outputFolder + '/' + filterFolder); + folderFiles.forEach((file) => { + // Build file name + const fpath = file.replace(/\\/g, '/'); + const browsedFileExtension = fpath.substring(fpath.lastIndexOf('.') + 1); + filter.file_extensions.forEach((filterFileExt) => { + if (browsedFileExtension === filterFileExt) { + // Found a matching file, process it + const fullFilePath = this.outputFolder + '/' + filterFolder + '/' + fpath; + uxLog(this, c.grey('- ' + fullFilePath)); + this.filterXmlFromFile(filter, fullFilePath); + } + }); + }); + }); + }); + this.smmryResult.filterResults = this.smmryUpdatedFiles; + + // Display results as JSON + uxLog(this, c.grey('Filtering results:' + JSON.stringify(this.smmryResult))); + return {}; + } + + // Filter XML content of the file + public filterXmlFromFile(filter, file) { + const parser = new xml2js.Parser(); + const data = fs.readFileSync(file); + parser.parseString(data, (err2, fileXmlContent) => { + uxLog(this, 'Parsed XML \n' + util.inspect(fileXmlContent, false, null)); + Object.keys(fileXmlContent).forEach((eltKey) => { + fileXmlContent[eltKey] = this.filterElement(fileXmlContent[eltKey], filter, file); + }); + if (this.smmryUpdatedFiles[file] != null && this.smmryUpdatedFiles[file].updated === true) { + writeXmlFile(file, fileXmlContent); + uxLog(this, 'Updated ' + file); + } + }); + } + + public filterElement(elementValue, filter, file) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + // Object case + if (typeof elementValue === 'object') { + Object.keys(elementValue).forEach((eltKey) => { + let found = false; + // Browse filter exclude_list for elementValue + filter.exclude_list.forEach((excludeDef) => { + if (excludeDef.type_tag === eltKey) { + // Found matching type tag + found = true; + uxLog(this, '\nFound type: ' + eltKey); + uxLog(this, elementValue[eltKey]); + // Filter type values + const typeValues = elementValue[eltKey]; + const newTypeValues: any[] = []; + typeValues.forEach((typeItem) => { + // If identifier tag not found, do not filter and avoid crash + if ( + typeItem[excludeDef.identifier_tag] && + (excludeDef.values.includes(typeItem[excludeDef.identifier_tag]) || + excludeDef.values.includes(typeItem[excludeDef.identifier_tag][0])) + ) { + uxLog(this, '----- filtered ' + typeItem[excludeDef.identifier_tag]); + if (self.smmryUpdatedFiles[file] == null) { + self.smmryUpdatedFiles[file] = { updated: true, excluded: {} }; + } + if (self.smmryUpdatedFiles[file].excluded[excludeDef.type_tag] == null) { + self.smmryUpdatedFiles[file].excluded[excludeDef.type_tag] = []; + } + self.smmryUpdatedFiles[file].excluded[excludeDef.type_tag].push(typeItem[excludeDef.identifier_tag][0]); + } else { + uxLog(this, '--- kept ' + typeItem[excludeDef.identifier_tag]); + newTypeValues.push(typeItem); + } + }); + elementValue[eltKey] = newTypeValues; + } + }); + if (!found) { + elementValue[eltKey] = self.filterElement(elementValue[eltKey], filter, file); + } + }); + } else if (Array.isArray(elementValue)) { + const newElementValue: any[] = []; + elementValue.forEach((element) => { + element = self.filterElement(element, filter, file); + newElementValue.push(element); + }); + elementValue = newElementValue; + } + return elementValue; + } +} diff --git a/src/commands/hardis/project/clean/flowpositions.ts b/src/commands/hardis/project/clean/flowpositions.ts index 7fffc6c98..fa6d578e7 100644 --- a/src/commands/hardis/project/clean/flowpositions.ts +++ b/src/commands/hardis/project/clean/flowpositions.ts @@ -1,22 +1,18 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { glob } from "glob"; -import * as path from "path"; -import * as fs from "fs-extra"; -import { uxLog } from "../../../../common/utils"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { glob } from 'glob'; +import * as path from 'path'; +import fs from 'fs-extra'; +import { uxLog } from '../../../../common/utils/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class CleanListViews extends SfdxCommand { - public static title = "Clean Flow Positions"; +export default class CleanFlowPositions extends SfCommand { + public static title = 'Clean Flow Positions'; public static description = `Replace all positions in Auto-Layout Flows by 0 to simplify conflicts management @@ -47,42 +43,37 @@ autoCleanTypes: \`\`\` `; - public static examples = ["$ sfdx hardis:project:clean:flowpositions"]; + public static examples = ['$ sf hardis:project:clean:flowpositions']; - protected static flagsConfig = { - folder: flags.string({ - char: "f", - default: "force-app", - description: "Root folder", + public static flags: any = { + folder: Flags.string({ + char: 'f', + default: 'force-app', + description: 'Root folder', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; protected folder: string; protected debugMode = false; public async run(): Promise { - this.folder = this.flags.folder || "./force-app"; - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(CleanFlowPositions); + this.folder = flags.folder || './force-app'; + this.debugMode = flags.debug || false; // Delete standard files when necessary uxLog(this, c.cyan(`Setting flows as Auto Layout and remove positions...`)); @@ -92,10 +83,10 @@ autoCleanTypes: const matchingFlows = await glob(findManagedPattern, { cwd: process.cwd() }); let counter = 0; for (const flowMetadataFile of matchingFlows) { - const flowXml = await fs.readFile(flowMetadataFile, "utf8"); - if (flowXml.includes("AUTO_LAYOUT_CANVAS")) { - let updatedFlowXml = flowXml.replace(/([0-9]*)<\/locationX>/gm, "0"); - updatedFlowXml = updatedFlowXml.replace(/([0-9]*)<\/locationY>/gm, "0"); + const flowXml = await fs.readFile(flowMetadataFile, 'utf8'); + if (flowXml.includes('AUTO_LAYOUT_CANVAS')) { + let updatedFlowXml = flowXml.replace(/([0-9]*)<\/locationX>/gm, '0'); + updatedFlowXml = updatedFlowXml.replace(/([0-9]*)<\/locationY>/gm, '0'); if (updatedFlowXml !== flowXml) { await fs.writeFile(flowMetadataFile, updatedFlowXml); counter++; diff --git a/src/commands/hardis/project/clean/hiddenitems.ts b/src/commands/hardis/project/clean/hiddenitems.ts index f050deb88..ee2cdbf91 100644 --- a/src/commands/hardis/project/clean/hiddenitems.ts +++ b/src/commands/hardis/project/clean/hiddenitems.ts @@ -1,61 +1,52 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import { glob } from "glob"; -import * as path from "path"; -import { uxLog } from "../../../../common/utils"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import { glob } from 'glob'; +import * as path from 'path'; +import { uxLog } from '../../../../common/utils/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class CleanHiddenItems extends SfCommand { + public static title = 'Clean retrieved hidden items in dx sources'; -export default class CleanHiddenItems extends SfdxCommand { - public static title = "Clean retrieved hidden items in dx sources"; + public static description = 'Remove unwanted hidden items within sfdx project sources'; - public static description = "Remove unwanted hidden items within sfdx project sources"; + public static examples = ['$ sf hardis:project:clean:hiddenitems']; - public static examples = ["$ sfdx hardis:project:clean:hiddenitems"]; - - protected static flagsConfig = { - folder: flags.string({ - char: "f", - default: "force-app", - description: "Root folder", + public static flags: any = { + folder: Flags.string({ + char: 'f', + default: 'force-app', + description: 'Root folder', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; protected folder: string; protected debugMode = false; public async run(): Promise { - this.folder = this.flags.folder || "./force-app"; - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(CleanHiddenItems); + this.folder = flags.folder || './force-app'; + this.debugMode = flags.debug || false; // Delete standard files when necessary uxLog(this, c.cyan(`Removing hidden dx managed source files`)); @@ -68,11 +59,12 @@ export default class CleanHiddenItems extends SfdxCommand { if (!fs.existsSync(matchingCustomFile)) { continue; } - const fileContent = await fs.readFile(matchingCustomFile, "utf8"); - if (fileContent.startsWith("(hidden)")) { + const fileContent = await fs.readFile(matchingCustomFile, 'utf8'); + if (fileContent.startsWith('(hidden)')) { const componentFolder = path.dirname(matchingCustomFile); const folderSplit = componentFolder.split(path.sep); - const toRemove = folderSplit.includes("lwc") || folderSplit.includes("aura") ? componentFolder : matchingCustomFile; + const toRemove = + folderSplit.includes('lwc') || folderSplit.includes('aura') ? componentFolder : matchingCustomFile; await fs.remove(toRemove); uxLog(this, c.cyan(`Removed hidden item ${c.yellow(toRemove)}`)); counter++; diff --git a/src/commands/hardis/project/clean/listviews.ts b/src/commands/hardis/project/clean/listviews.ts index 15292b052..c77462e41 100644 --- a/src/commands/hardis/project/clean/listviews.ts +++ b/src/commands/hardis/project/clean/listviews.ts @@ -1,62 +1,53 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { glob } from "glob"; -import * as path from "path"; -import { uxLog } from "../../../../common/utils"; -import { parseXmlFile, writeXmlFile } from "../../../../common/utils/xmlUtils"; -import { getConfig, setConfig } from "../../../../config"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { glob } from 'glob'; +import * as path from 'path'; +import { uxLog } from '../../../../common/utils/index.js'; +import { parseXmlFile, writeXmlFile } from '../../../../common/utils/xmlUtils.js'; +import { getConfig, setConfig } from '../../../../config/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class CleanListViews extends SfCommand { + public static title = 'Replace Mine by Everything in ListViews'; -export default class CleanListViews extends SfdxCommand { - public static title = "Replace Mine by Everything in ListViews"; + public static description = 'Replace Mine by Everything in ListView, and log the replacements in sfdx-hardis.yml'; - public static description = "Replace Mine by Everything in ListView, and log the replacements in sfdx-hardis.yml"; + public static examples = ['$ sf hardis:project:clean:listviews']; - public static examples = ["$ sfdx hardis:project:clean:listviews"]; - - protected static flagsConfig = { - folder: flags.string({ - char: "f", - default: "force-app", - description: "Root folder", + public static flags: any = { + folder: Flags.string({ + char: 'f', + default: 'force-app', + description: 'Root folder', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; protected folder: string; protected debugMode = false; public async run(): Promise { - this.folder = this.flags.folder || "./force-app"; - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(CleanListViews); + this.folder = flags.folder || './force-app'; + this.debugMode = flags.debug || false; // Delete standard files when necessary uxLog(this, c.cyan(`Replacing 'Mine' by 'Everything' in ListViews for deployments to pass`)); @@ -65,20 +56,20 @@ export default class CleanListViews extends SfdxCommand { const findManagedPattern = rootFolder + `/**/*.listView-meta.xml`; const matchingListViews = await glob(findManagedPattern, { cwd: process.cwd() }); let counter = 0; - const config = await getConfig("project"); + const config = await getConfig('project'); let listViewsMine = config.listViewsToSetToMine || []; for (const listViewfile of matchingListViews) { const listViewXml = await parseXmlFile(listViewfile); - if (listViewXml.ListView?.filterScope[0] === "Mine") { - listViewXml.ListView.filterScope[0] = "Everything"; + if (listViewXml.ListView?.filterScope[0] === 'Mine') { + listViewXml.ListView.filterScope[0] = 'Everything'; uxLog(this, c.grey(`replaced Mine by Everything in ListView ${listViewXml}`)); await writeXmlFile(listViewfile, listViewXml); - listViewsMine.push(path.relative(process.cwd(), listViewfile).replace(/\\/g, "/")); + listViewsMine.push(path.relative(process.cwd(), listViewfile).replace(/\\/g, '/')); counter++; } } listViewsMine = [...new Set(listViewsMine)]; // Make unique - await setConfig("project", { listViewsToSetToMine: listViewsMine }); + await setConfig('project', { listViewsToSetToMine: listViewsMine }); // Summary const msg = `Replaced ${c.green(c.bold(counter))} Mine by Everything in ListViews`; diff --git a/src/commands/hardis/project/clean/manageditems.ts b/src/commands/hardis/project/clean/manageditems.ts index e9016d758..443c65901 100644 --- a/src/commands/hardis/project/clean/manageditems.ts +++ b/src/commands/hardis/project/clean/manageditems.ts @@ -1,71 +1,62 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages, SfdxError } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import { glob } from "glob"; -import * as path from "path"; -import { uxLog } from "../../../../common/utils"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages, SfError } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import { glob } from 'glob'; +import * as path from 'path'; +import { uxLog } from '../../../../common/utils/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class CleanManagedItems extends SfCommand { + public static title = 'Clean retrieved managed items in dx sources'; -export default class CleanManagedItems extends SfdxCommand { - public static title = "Clean retrieved managed items in dx sources"; + public static description = 'Remove unwanted managed items within sfdx project sources'; - public static description = "Remove unwanted managed items within sfdx project sources"; + public static examples = ['$ sf hardis:project:clean:manageditems --namespace crta']; - public static examples = ["$ sfdx hardis:project:clean:manageditems --namespace crta"]; - - protected static flagsConfig = { - namespace: flags.string({ - char: "n", - default: "", - description: "Namespace to remove", + public static flags: any = { + namespace: Flags.string({ + char: 'n', + default: '', + description: 'Namespace to remove', }), - folder: flags.string({ - char: "f", - default: "force-app", - description: "Root folder", + folder: Flags.string({ + char: 'f', + default: 'force-app', + description: 'Root folder', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; protected namespace: string; protected folder: string; protected debugMode = false; public async run(): Promise { - this.namespace = this.flags.namespace || ""; - this.folder = this.flags.folder || "./force-app"; - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(CleanManagedItems); + this.namespace = flags.namespace || ''; + this.folder = flags.folder || './force-app'; + this.debugMode = flags.debug || false; - if (this.namespace === "") { - throw new SfdxError("namespace argument is mandatory"); + if (this.namespace === '') { + throw new SfError('namespace argument is mandatory'); } // Delete standard files when necessary @@ -86,7 +77,7 @@ export default class CleanManagedItems extends SfdxCommand { } } // Keep .object-meta.xml item if there are local custom items defined on it - if (matchingCustomFile.endsWith(".object-meta.xml")) { + if (matchingCustomFile.endsWith('.object-meta.xml')) { const localItems = await this.folderContainsLocalItems(path.dirname(matchingCustomFile)); if (localItems) { continue; @@ -97,12 +88,12 @@ export default class CleanManagedItems extends SfdxCommand { } // Return an object to be displayed with --json - return { outputString: "Cleaned managed items from sfdx project" }; + return { outputString: 'Cleaned managed items from sfdx project' }; } private async folderContainsLocalItems(folder: string): Promise { // Do not remove managed folders when there are local custom items defined on it - const subFiles = await glob(folder + "/**/*", { cwd: process.cwd() }); + const subFiles = await glob(folder + '/**/*', { cwd: process.cwd() }); const standardItems = subFiles.filter((file) => { return !fs.lstatSync(file).isDirectory() && !path.basename(file).startsWith(`${this.namespace}__`); }); diff --git a/src/commands/hardis/project/clean/minimizeprofiles.ts b/src/commands/hardis/project/clean/minimizeprofiles.ts index cd7ebc59b..18440e569 100644 --- a/src/commands/hardis/project/clean/minimizeprofiles.ts +++ b/src/commands/hardis/project/clean/minimizeprofiles.ts @@ -1,23 +1,19 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { glob } from "glob"; -import * as path from "path"; -import { uxLog } from "../../../../common/utils"; -import { minimizeProfile } from "../../../../common/utils/profileUtils"; -import { getConfig } from "../../../../config"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class CleanMinimizeProfiles extends SfdxCommand { - public static title = "Clean profiles of Permission Set attributes"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { glob } from 'glob'; +import * as path from 'path'; +import { uxLog } from '../../../../common/utils/index.js'; +import { minimizeProfile } from '../../../../common/utils/profileUtils.js'; +import { getConfig } from '../../../../config/index.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class CleanMinimizeProfiles extends SfCommand { + public static title = 'Clean profiles of Permission Set attributes'; public static description = `Remove all profile attributes that exist on Permission Sets @@ -50,42 +46,37 @@ skipMinimizeProfiles \`\`\` `; - public static examples = ["$ sfdx hardis:project:clean:minimizeprofiles"]; + public static examples = ['$ sf hardis:project:clean:minimizeprofiles']; - protected static flagsConfig = { - folder: flags.string({ - char: "f", - default: "force-app", - description: "Root folder", + public static flags: any = { + folder: Flags.string({ + char: 'f', + default: 'force-app', + description: 'Root folder', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; protected folder: string; protected debugMode = false; public async run(): Promise { - this.folder = this.flags.folder || "./force-app"; - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(CleanMinimizeProfiles); + this.folder = flags.folder || './force-app'; + this.debugMode = flags.debug || false; // Delete standard files when necessary uxLog(this, c.cyan(`Removing profile attributes that exist on Permission Sets`)); @@ -93,11 +84,11 @@ skipMinimizeProfiles const rootFolder = path.resolve(this.folder); const findManagedPattern = rootFolder + `/**/*.profile-meta.xml`; const matchingProfileFiles = await glob(findManagedPattern, { cwd: process.cwd() }); - const config = await getConfig("branch"); + const config = await getConfig('branch'); const skipMinimizeProfiles = config.skipMinimizeProfiles || []; let counter = 0; for (const profileFile of matchingProfileFiles) { - const profileName = path.basename(profileFile).replace(".profile-meta.xml", ""); + const profileName = path.basename(profileFile).replace('.profile-meta.xml', ''); if (skipMinimizeProfiles.includes(profileName)) { uxLog(this, c.grey(`Skipped ${profileName} as found in skipMinimizeProfiles property`)); continue; @@ -110,7 +101,7 @@ skipMinimizeProfiles // Summary if (counter > 0) { - uxLog(this, c.yellow("Please make sure the attributes removed from Profiles are defined on Permission Sets")); + uxLog(this, c.yellow('Please make sure the attributes removed from Profiles are defined on Permission Sets')); globalThis.displayProfilesWarning = true; } const msg = `Cleaned ${c.green(c.bold(counter))} profiles from attributes existing on Permission Sets`; diff --git a/src/commands/hardis/project/clean/orgmissingitems.ts b/src/commands/hardis/project/clean/orgmissingitems.ts index 4cca15a71..65a1bced7 100644 --- a/src/commands/hardis/project/clean/orgmissingitems.ts +++ b/src/commands/hardis/project/clean/orgmissingitems.ts @@ -1,93 +1,94 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import { glob } from "glob"; -import { mergeObjectPropertyLists, uxLog } from "../../../../common/utils"; -import { buildOrgManifest } from "../../../../common/utils/deployUtils"; -import { promptOrg } from "../../../../common/utils/orgUtils"; -import { parsePackageXmlFile, parseXmlFile, writeXmlFile } from "../../../../common/utils/xmlUtils"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class OrgMissingItems extends SfdxCommand { - public static title = "Clean SFDX items using target org definition"; - - public static description = "Clean SFDX sources from items present neither in target org nor local package.xml"; - - public static examples = ["$ sfdx hardis:project:clean:orgmissingitems"]; - - protected static flagsConfig = { - folder: flags.string({ - char: "f", - default: "force-app", - description: "Root folder", +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import { glob } from 'glob'; +import { mergeObjectPropertyLists, uxLog } from '../../../../common/utils/index.js'; +import { buildOrgManifest } from '../../../../common/utils/deployUtils.js'; +import { promptOrg } from '../../../../common/utils/orgUtils.js'; +import { parsePackageXmlFile, parseXmlFile, writeXmlFile } from '../../../../common/utils/xmlUtils.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class OrgMissingItems extends SfCommand { + public static title = 'Clean SFDX items using target org definition'; + + public static description = 'Clean SFDX sources from items present neither in target org nor local package.xml'; + + public static examples = ['$ sf hardis:project:clean:orgmissingitems']; + + public static flags: any = { + folder: Flags.string({ + char: 'f', + default: 'force-app', + description: 'Root folder', }), - packagexmlfull: flags.string({ - char: "p", + packagexmlfull: Flags.string({ + char: 'p', description: - "Path to packagexml used for cleaning.\nMust contain also standard CustomObject and CustomField elements.\nIf not provided, it will be generated from a remote org", + 'Path to packagexml used for cleaning.\nMust contain also standard CustomObject and CustomField elements.\nIf not provided, it will be generated from a remote org', }), - packagexmltargetorg: flags.string({ - char: "t", - description: "Target org username or alias to build package.xml (sfdx must be authenticated).\nIf not provided, will be prompted to the user.", + packagexmltargetorg: Flags.string({ + char: 't', + description: + 'Target org username or alias to build package.xml (SF CLI must be authenticated).\nIf not provided, will be prompted to the user.', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; /* jscpd:ignore-end */ protected folder: string; - protected targetOrgUsernameAlias: string; - protected packageXmlFull: string; + protected targetOrgUsernameAlias: string | null; + protected packageXmlFull: string | null; protected debugMode = false; protected standardFields = [ - "Id", - "Name", - "Parent", - "IsActive", - "Alias", - "Owner", - "CreatedBy", - "CreatedDate", - "LastActivityDate", - "LastModifiedBy", - "LastModifiedDate", - "RecordType", + 'Id', + 'Name', + 'Parent', + 'IsActive', + 'Alias', + 'Owner', + 'CreatedBy', + 'CreatedDate', + 'LastActivityDate', + 'LastModifiedBy', + 'LastModifiedDate', + 'RecordType', ]; - protected standardSuffixes = ["Street", "City", "State", "PostalCode", "Country", "Latitude", "Longitude", "GeocodeAccuracy"]; + protected standardSuffixes = [ + 'Street', + 'City', + 'State', + 'PostalCode', + 'Country', + 'Latitude', + 'Longitude', + 'GeocodeAccuracy', + ]; public async run(): Promise { - this.folder = this.flags.folder || "./force-app"; - this.debugMode = this.flags.debug || false; - this.targetOrgUsernameAlias = this.flags.packagexmltargetorg || null; - this.packageXmlFull = this.flags.packagexmlfull || null; + const { flags } = await this.parse(OrgMissingItems); + this.folder = flags.folder || './force-app'; + this.debugMode = flags.debug || false; + this.targetOrgUsernameAlias = flags.packagexmltargetorg || null; + this.packageXmlFull = flags.packagexmlfull || null; if (this.packageXmlFull === null) { // Request user to select an org if not provided @@ -100,17 +101,17 @@ export default class OrgMissingItems extends SfdxCommand { let packageXmlContent = await parsePackageXmlFile(this.packageXmlFull); // Merge with local package.xml content - if (fs.existsSync("./manifest/package.xml")) { - const localPackageXmlContent = await parsePackageXmlFile("./manifest/package.xml"); + if (fs.existsSync('./manifest/package.xml')) { + const localPackageXmlContent = await parsePackageXmlFile('./manifest/package.xml'); packageXmlContent = mergeObjectPropertyLists(packageXmlContent, localPackageXmlContent, { sort: true }); } // Build destructiveChanges let destructiveChangesContent = {}; - if (fs.existsSync("./manifest/destructiveChanges.xml")) { - destructiveChangesContent = await parsePackageXmlFile("./manifest/destructiveChanges.xml"); + if (fs.existsSync('./manifest/destructiveChanges.xml')) { + destructiveChangesContent = await parsePackageXmlFile('./manifest/destructiveChanges.xml'); } // Build additional lists - const packageXmlAllFields = packageXmlContent["CustomField"].map((customField) => customField.split(".")[1]); + const packageXmlAllFields = packageXmlContent['CustomField'].map((customField) => customField.split('.')[1]); // const destructiveChangesAllFields = (destructiveChangesContent["CustomField"] || []).map(customField => customField.split('.')[1]); // Clean report types @@ -133,19 +134,19 @@ export default class OrgMissingItems extends SfdxCommand { // Filter columns referring to fields not in package.xml of target org + local package.xml section.columns = section.columns.filter((column) => { const object = column.table[0]; - const field = column.field[0].split(".")[0]; + const field = column.field[0].split('.')[0]; const objectField = `${object}.${field}`; - if ((destructiveChangesContent["CustomObject"] || []).includes(object)) { + if ((destructiveChangesContent['CustomObject'] || []).includes(object)) { return false; } - const objectFound = (packageXmlContent["CustomObject"] || []).includes(object); - const fieldFound = (packageXmlContent["CustomField"] || []).includes(objectField); + const objectFound = (packageXmlContent['CustomObject'] || []).includes(object); + const fieldFound = (packageXmlContent['CustomField'] || []).includes(objectField); const isStandardTechField = this.standardFields.includes(field); const isStandardSubField = this.standardSuffixes.filter((suffix) => field.endsWith(suffix)).length > 0; if ( (objectFound && (fieldFound || isStandardTechField || isStandardSubField)) || - (object.includes("__r") && (isStandardTechField || isStandardSubField)) || - (object.includes("__r") && packageXmlAllFields.includes(field)) + (object.includes('__r') && (isStandardTechField || isStandardSubField)) || + (object.includes('__r') && packageXmlAllFields.includes(field)) ) { return true; } else { diff --git a/src/commands/hardis/project/clean/references.ts b/src/commands/hardis/project/clean/references.ts index ef29c03b6..1e41ba983 100644 --- a/src/commands/hardis/project/clean/references.ts +++ b/src/commands/hardis/project/clean/references.ts @@ -1,160 +1,172 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; -import { glob } from "glob"; -import { createTempDir, execCommand, isCI, removeObjectPropertyLists, uxLog } from "../../../../common/utils"; -import { prompts } from "../../../../common/utils/prompts"; -import { parsePackageXmlFile, parseXmlFile, writePackageXmlFile, writeXmlFile } from "../../../../common/utils/xmlUtils"; -import { getConfig, setConfig } from "../../../../config"; -import { PACKAGE_ROOT_DIR } from "../../../../settings"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; +import { glob } from 'glob'; +import { createTempDir, execCommand, isCI, removeObjectPropertyLists, uxLog } from '../../../../common/utils/index.js'; +import { prompts } from '../../../../common/utils/prompts.js'; +import { + parsePackageXmlFile, + parseXmlFile, + writePackageXmlFile, + writeXmlFile, +} from '../../../../common/utils/xmlUtils.js'; +import { getConfig, setConfig } from '../../../../config/index.js'; +import { PACKAGE_ROOT_DIR } from '../../../../settings.js'; +import { FilterXmlContent } from './filter-xml-content.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class CleanReferences extends SfCommand { + public static title = 'Clean references in dx sources'; -export default class CleanReferences extends SfdxCommand { - public static title = "Clean references in dx sources"; - - public static description = "Remove unwanted references within sfdx project sources"; + public static description = 'Remove unwanted references within sfdx project sources'; public static examples = [ - "$ sfdx hardis:project:clean:references", - "$ sfdx hardis:project:clean:references --type all", - "$ sfdx hardis:project:clean:references --config ./cleaning/myconfig.json", - "$ sfdx hardis:project:clean:references --config ./somefolder/myDestructivePackage.xml", + '$ sf hardis:project:clean:references', + '$ sf hardis:project:clean:references --type all', + '$ sf hardis:project:clean:references --config ./cleaning/myconfig.json', + '$ sf hardis:project:clean:references --config ./somefolder/myDestructivePackage.xml', ]; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - type: flags.string({ - char: "t", - description: "Cleaning type", - options: ["all", "caseentitlement", "dashboards", "datadotcom", "destructivechanges", "localfields", "productrequest", "entitlement"], + public static flags: any = { + type: Flags.string({ + char: 't', + description: 'Cleaning type', + options: [ + 'all', + 'caseentitlement', + 'dashboards', + 'datadotcom', + 'destructivechanges', + 'localfields', + 'productrequest', + 'entitlement', + 'flowPositions', + 'sensitiveMetadatas', + 'minimizeProfiles' + ], }), - config: flags.string({ - char: "c", - description: "Path to a JSON config file or a destructiveChanges.xml file", + config: Flags.string({ + char: 'c', + description: 'Path to a JSON config file or a destructiveChanges.xml file', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = true; /* jscpd:ignore-end */ - // List required plugins, their presence will be tested before running the command - protected static requiresSfdxPlugins = ["sfdx-essentials"]; - protected debugMode = false; - protected cleaningTypes = []; + protected cleaningTypes: any[] = []; protected allCleaningTypes = [ { - value: "checkPermissions", - title: "Check custom items are existing it at least one Permission Set", - command: "sfdx hardis:lint:access", + value: 'checkPermissions', + title: 'Check custom items are existing it at least one Permission Set', + command: 'sf hardis:lint:access', }, { - value: "dashboards", - title: "Dashboards: Remove reference to hardcoded users", + value: 'dashboards', + title: 'Dashboards: Remove reference to hardcoded users', }, { - value: "destructivechanges", - title: "DestructiveChanges.xml: Remove source files mentioned in destructiveChanges.xml", + value: 'destructivechanges', + title: 'DestructiveChanges.xml: Remove source files mentioned in destructiveChanges.xml', }, { - value: "flowPositions", + value: 'flowPositions', title: `Flows: Replace all positions in AutoLayout Flows by 0 to simplify conflicts management`, - command: "sfdx hardis:project:clean:flowpositions", + command: 'sf hardis:project:clean:flowpositions', + }, + { + value: 'sensitiveMetadatas', + title: `Remove sensitive metadata content from sources (ex: Certificates)`, + command: 'sf hardis:project:clean:sensitive-metadatas', }, { - value: "listViewsMine", + value: 'listViewsMine', title: `ListViews: Convert scope "Everything" into scope "Mine" on ListViews`, - command: "sfdx hardis:project:clean:listviews", + command: 'sf hardis:project:clean:listviews', }, { - value: "minimizeProfiles", - title: "Profiles: Remove profile attributes that exists on permission sets", - command: "sfdx hardis:project:clean:minimizeprofiles", + value: 'minimizeProfiles', + title: 'Profiles: Remove profile attributes that exists on permission sets', + command: 'sf hardis:project:clean:minimizeprofiles', }, { - value: "caseentitlement", - title: "References to Entitlement Management items", + value: 'caseentitlement', + title: 'References to Entitlement Management items', }, { - value: "datadotcom", - title: "References to Data.com items. https://help.salesforce.com/articleView?id=000320795&type=1&mode=1", + value: 'datadotcom', + title: 'References to Data.com items. https://help.salesforce.com/articleView?id=000320795&type=1&mode=1', }, { - value: "entitlement", - title: "References to Entitlement object", + value: 'entitlement', + title: 'References to Entitlement object', }, { - value: "localfields", - title: "References to Local Fields items. https://help.salesforce.com/articleView?id=sf.admin_local_name_fields.htm&type=5", + value: 'localfields', + title: + 'References to Local Fields items. https://help.salesforce.com/articleView?id=sf.admin_local_name_fields.htm&type=5', }, { - value: "productrequest", - title: "References to ProductRequest object", + value: 'productrequest', + title: 'References to ProductRequest object', }, { - value: "systemDebug", - title: "Remove System.debug from sources", - command: "sfdx hardis:project:clean:systemdebug", + value: 'systemDebug', + title: 'Remove System.debug from sources', + command: 'sf hardis:project:clean:systemdebug', }, { - value: "v60", - title: "Make metadata compliant with v60", + value: 'v60', + title: 'Make metadata compliant with v60', }, ]; - protected configFile: string; + protected configFile: string | null; protected deleteItems: any = {}; public async run(): Promise { - this.debugMode = this.flags.debug || false; - this.cleaningTypes = this.flags.type ? [this.flags.type] : []; - this.configFile = this.flags.config || null; - const config = await getConfig("project"); + const { flags } = await this.parse(CleanReferences); + this.debugMode = flags.debug || false; + this.cleaningTypes = flags.type ? [flags.type] : []; + this.configFile = flags.config || null; + const config = await getConfig('project'); // Config file sent by user if (this.configFile != null) { this.cleaningTypes = [this.configFile.trim()]; } else { // Read list of cleanings to perform in references - if (this.cleaningTypes.length > 0 && this.cleaningTypes[0] === "all") { + if (this.cleaningTypes.length > 0 && this.cleaningTypes[0] === 'all') { this.cleaningTypes = config.autoCleanTypes || []; } // Prompt user cleanings to perform if (!isCI && this.cleaningTypes.length === 0) { const typesResponse = await prompts({ - type: "multiselect", - name: "value", - message: c.cyanBright("What references do you want to clean from your SFDX project sources ?"), + type: 'multiselect', + name: 'value', + message: c.cyanBright('What references do you want to clean from your SFDX project sources ?'), choices: this.allCleaningTypes, }); this.cleaningTypes = typesResponse.value; @@ -164,16 +176,18 @@ export default class CleanReferences extends SfdxCommand { // Prompt user to save choice in configuration const autoCleanTypes = config.autoCleanTypes || []; const toAdd = this.cleaningTypes.filter((type) => !autoCleanTypes.includes(type)); - if (toAdd.length > 0 && !isCI && this.flags.type !== "all") { + if (toAdd.length > 0 && !isCI && flags.type !== 'all') { const saveResponse = await prompts({ - type: "confirm", - name: "value", + type: 'confirm', + name: 'value', default: true, - message: c.cyanBright("Do you want to save this action in your project configuration, so it is executed at each Work Save ?"), + message: c.cyanBright( + 'Do you want to save this action in your project configuration, so it is executed at each Work Save ?' + ), }); if (saveResponse.value === true) { autoCleanTypes.push(...this.cleaningTypes); - await setConfig("project", { + await setConfig('project', { autoCleanTypes: [...new Set(autoCleanTypes)], }); } @@ -181,11 +195,13 @@ export default class CleanReferences extends SfdxCommand { // Process cleaning for (const cleaningType of this.cleaningTypes) { - const cleaningTypeObj = this.allCleaningTypes.filter((cleaningTypeObj) => cleaningTypeObj.value === cleaningType)[0]; + const cleaningTypeObj = this.allCleaningTypes.filter( + (cleaningTypeObj) => cleaningTypeObj.value === cleaningType + )[0]; if (cleaningTypeObj?.command) { let command = cleaningTypeObj?.command; - if (this.argv.indexOf("--websocket") > -1) { - command += ` --websocket ${this.argv[this.argv.indexOf("--websocket") + 1]}`; + if (this.argv.indexOf('--websocket') > -1) { + command += ` --websocket ${this.argv[this.argv.indexOf('--websocket') + 1]}`; } uxLog(this, c.cyan(`Run cleaning command ${c.bold(cleaningType)} (${cleaningTypeObj.title}) ...`)); // Command based cleaning @@ -198,23 +214,19 @@ export default class CleanReferences extends SfdxCommand { // Template based cleaning uxLog(this, c.cyan(`Apply cleaning of references to ${c.bold(cleaningType)} (${cleaningTypeObj.title})...`)); const filterConfigFile = await this.getFilterConfigFile(cleaningType); - const cleanCommand = - "sfdx essentials:metadata:filter-xml-content" + - ` -c ${filterConfigFile}` + - ` --inputfolder ./force-app/main/default` + - ` --outputfolder ./force-app/main/default` + - " --noinsight"; - await execCommand(cleanCommand, this, { - fail: true, - output: false, - debug: this.debugMode, - }); + const packageDirectories = this.project?.getPackageDirectories() || []; + for (const packageDirectory of packageDirectories) { + await FilterXmlContent.run( + ['-c', filterConfigFile, '--inputfolder', packageDirectory.path, '--outputfolder', packageDirectory.path], + this.config + ); + } } } // Clean package.xml file from deleted items uxLog(this, c.grey(`Cleaning package.xml files...`)); - const patternPackageXml = "**/manifest/**/package*.xml"; + const patternPackageXml = '**/manifest/**/package*.xml'; const packageXmlFiles = await glob(patternPackageXml, { cwd: process.cwd(), }); @@ -224,7 +236,7 @@ export default class CleanReferences extends SfdxCommand { const newPackageXmlContent = removeObjectPropertyLists(packageXmlContent, this.deleteItems); if (packageXmlContentStr !== JSON.stringify(newPackageXmlContent)) { await writePackageXmlFile(packageXmlFile, newPackageXmlContent); - uxLog(this, c.grey("-- cleaned elements from " + packageXmlFile)); + uxLog(this, c.grey('-- cleaned elements from ' + packageXmlFile)); } } @@ -233,41 +245,48 @@ export default class CleanReferences extends SfdxCommand { await Promise.all( Object.keys(this.deleteItems).map(async (type) => { await this.manageDeleteRelatedFiles(type); - }), + }) ); uxLog(this, c.green(`Cleaning complete`)); // Return an object to be displayed with --json - return { outputString: "Cleaned references from sfdx project" }; + return { outputString: 'Cleaned references from sfdx project' }; } private async getFilterConfigFile(cleaningType) { - const templateFile = path.join(path.join(PACKAGE_ROOT_DIR, "defaults/clean", "template.txt")); + const templateFile = path.join(path.join(PACKAGE_ROOT_DIR, 'defaults/clean', 'template.txt')); // Read and complete cleaning template - let templateContent = await fs.readFile(templateFile, "utf8"); - if (cleaningType === "destructivechanges" || cleaningType.endsWith(".xml")) { + let templateContent = await fs.readFile(templateFile, 'utf8'); + if (cleaningType === 'destructivechanges' || cleaningType.endsWith('.xml')) { // destructive changes file - const destructiveChangesFile = cleaningType.endsWith(".xml") ? cleaningType : "./manifest/destructiveChanges.xml"; + const destructiveChangesFile = cleaningType.endsWith('.xml') ? cleaningType : './manifest/destructiveChanges.xml'; const destructiveChanges = await parseXmlFile(destructiveChangesFile); for (const type of destructiveChanges.Package.types || []) { const members = type.members; - templateContent = templateContent.replace(new RegExp(`{{ ${type.name[0]} }}`, "g"), JSON.stringify(members, null, 2)); + templateContent = templateContent.replace( + new RegExp(`{{ ${type.name[0]} }}`, 'g'), + JSON.stringify(members, null, 2) + ); this.deleteItems[type.name[0]] = (this.deleteItems[type.name[0]] || []).concat(members); } } else { // Predefined destructive items file - const filterConfigFileConfigPath = cleaningType.endsWith(".json") + const filterConfigFileConfigPath = cleaningType.endsWith('.json') ? cleaningType - : path.join(path.join(PACKAGE_ROOT_DIR, "defaults/clean", cleaningType + ".json")); - const filterConfigFileConfig = JSON.parse(await fs.readFile(filterConfigFileConfigPath, "utf8")); + : path.join(path.join(PACKAGE_ROOT_DIR, 'defaults/clean', cleaningType + '.json')); + const filterConfigFileConfig = JSON.parse(await fs.readFile(filterConfigFileConfigPath, 'utf8')); for (const type of Object.keys(filterConfigFileConfig.items)) { - templateContent = templateContent.replace(new RegExp(`{{ ${type} }}`, "g"), JSON.stringify(filterConfigFileConfig.items[type], null, 2)); + templateContent = templateContent.replace( + new RegExp(`{{ ${type} }}`, 'g'), + JSON.stringify(filterConfigFileConfig.items[type], null, 2) + ); this.deleteItems[type] = (this.deleteItems[type] || []).concat(filterConfigFileConfig.items[type]); } } // Create temporary file - templateContent = templateContent.replace(/{{ .* }}/gm, "[]"); - const tmpCleanFileName = cleaningType.endsWith(".xml") || cleaningType.endsWith(".json") ? path.basename(cleaningType) : cleaningType; + templateContent = templateContent.replace(/{{ .* }}/gm, '[]'); + const tmpCleanFileName = + cleaningType.endsWith('.xml') || cleaningType.endsWith('.json') ? path.basename(cleaningType) : cleaningType; const filterConfigFile = path.join(await createTempDir(), `clean_${tmpCleanFileName}.json`); await fs.writeFile(filterConfigFile, templateContent); return filterConfigFile; @@ -275,7 +294,7 @@ export default class CleanReferences extends SfdxCommand { private async manageDeleteRelatedFiles(type) { // Custom fields - if (type === "CustomField") { + if (type === 'CustomField') { for (const field of this.deleteItems[type] || []) { await this.manageDeleteCustomFieldRelatedFiles(field); } @@ -284,7 +303,7 @@ export default class CleanReferences extends SfdxCommand { private async manageDeleteCustomFieldRelatedFiles(field: string) { // Remove custom field and customTranslation - const [obj, fld] = field.split("."); + const [obj, fld] = field.split('.'); const patternField = `force-app/**/objects/${obj}/fields/${fld}.field-meta.xml`; const patternTranslation = `force-app/**/objectTranslations/${obj}-*/${fld}.fieldTranslation-meta.xml`; for (const pattern of [patternField, patternTranslation]) { diff --git a/src/commands/hardis/project/clean/retrievefolders.ts b/src/commands/hardis/project/clean/retrievefolders.ts index 0cc3f186e..9191d41be 100644 --- a/src/commands/hardis/project/clean/retrievefolders.ts +++ b/src/commands/hardis/project/clean/retrievefolders.ts @@ -1,75 +1,65 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; -import { execCommand, uxLog } from "../../../../common/utils"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; +import { execCommand, uxLog } from '../../../../common/utils/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class CleanRetrieveFolders extends SfCommand { + public static title = 'Retrieve dashboards, documents and report folders in DX sources'; -export default class CleanRetrieveFolders extends SfdxCommand { - public static title = "Retrieve dashboards, documents and report folders in DX sources"; + public static description = 'Retrieve dashboards, documents and report folders in DX sources. Use -u ORGALIAS'; - public static description = "Retrieve dashboards, documents and report folders in DX sources. Use -u ORGALIAS"; + public static examples = ['$ sf hardis:project:clean:retrievefolders']; - public static examples = ["$ sfdx hardis:project:clean:retrievefolders"]; - - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), - }; - - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + 'target-org': requiredOrgFlagWithDeprecations, + }; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default + public static requiresProject = true; /* jscpd:ignore-end */ protected debugMode = false; protected deleteItems: any = {}; public async run(): Promise { - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(CleanRetrieveFolders); + this.debugMode = flags.debug || false; // Delete standard files when necessary uxLog(this, c.cyan(`Retrieve dashboards, documents and report folders in DX sources`)); - const rootSourcesFolder = path.join(process.cwd() + "/force-app/main/default"); + const rootSourcesFolder = path.join(process.cwd() + '/force-app/main/default'); const folderTypes = [ - { sourceType: "dashboards", mdType: "Dashboard" }, - { sourceType: "documents", mdType: "Document" }, - { sourceType: "email", mdType: "EmailTemplate" }, - { sourceType: "reports", mdType: "Report" }, + { sourceType: 'dashboards', mdType: 'Dashboard' }, + { sourceType: 'documents', mdType: 'Document' }, + { sourceType: 'email', mdType: 'EmailTemplate' }, + { sourceType: 'reports', mdType: 'Report' }, ]; // Iterate on types, and for each sub folder found, retrieve its SFDX source from org for (const folderType of folderTypes) { - const folderDir = rootSourcesFolder + "/" + folderType.sourceType; + const folderDir = rootSourcesFolder + '/' + folderType.sourceType; await this.manageRetrieveFolder(folderDir, folderType); } // Return an object to be displayed with --json - return { outputString: "Retrieved folders" }; + return { outputString: 'Retrieved folders' }; } private async manageRetrieveFolder(folderDir, folderType) { @@ -78,10 +68,10 @@ export default class CleanRetrieveFolders extends SfdxCommand { } const folderDirContent = await fs.readdir(folderDir); for (const subFolder of folderDirContent) { - const subFolderFull = folderDir + "/" + subFolder; + const subFolderFull = folderDir + '/' + subFolder; if (fs.lstatSync(subFolderFull).isDirectory()) { // Retrieve sub folder DX source - await execCommand(`sfdx force:source:retrieve -m ${folderType.mdType}:${subFolder}`, this, { + await execCommand(`sf project retrieve start -m ${folderType.mdType}:${subFolder}`, this, { fail: true, output: true, debug: this.debugMode, diff --git a/src/commands/hardis/project/clean/sensitive-metadatas.ts b/src/commands/hardis/project/clean/sensitive-metadatas.ts new file mode 100644 index 000000000..8805b6a66 --- /dev/null +++ b/src/commands/hardis/project/clean/sensitive-metadatas.ts @@ -0,0 +1,93 @@ +/* jscpd:ignore-start */ +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { glob } from 'glob'; +import * as path from 'path'; +import fs from 'fs-extra'; +import { uxLog } from '../../../../common/utils/index.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class CleanSensitiveMetadatas extends SfCommand { + public static title = 'Clean Sensitive Metadatas'; + + public static description = `Sensitive data like credentials and certificates are not supposed to be stored in Git, to avoid security breaches. + +This command detects the related metadata and replaces their sensitive content by "HIDDEN_BY_SFDX_HARDIS" + +Can be automated at each **hardis:work:save** if **sensitiveMetadatas** is added in .sfdx-hardis.yml **autoCleanTypes** property + +Example in config/.sfdx-hardis.yml: + +\`\`\`yaml +autoCleanTypes: + - destructivechanges + - sensitiveMetadatas +\`\`\` +`; + + public static examples = ['$ sf hardis:project:clean:sensitive-metadatas']; + + public static flags: any = { + folder: Flags.string({ + char: 'f', + default: 'force-app', + description: 'Root folder', + }), + debug: Flags.boolean({ + char: 'd', + default: false, + description: messages.getMessage('debugMode'), + }), + websocket: Flags.string({ + description: messages.getMessage('websocket'), + }), + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', + }), + }; + + // Set this to true if your command requires a project workspace; 'requiresProject' is false by default + public static requiresProject = true; + + protected folder: string; + protected debugMode = false; + + public async run(): Promise { + const { flags } = await this.parse(CleanSensitiveMetadatas); + this.folder = flags.folder || './force-app'; + this.debugMode = flags.debug || false; + + // Delete standard files when necessary + uxLog(this, c.cyan(`Looking for certificates...`)); + /* jscpd:ignore-end */ + const rootFolder = path.resolve(this.folder); + const findManagedPattern = rootFolder + `/**/*.crt`; + const matchingCerts = await glob(findManagedPattern, { cwd: process.cwd() }); + let counter = 0; + for (const cert of matchingCerts) { + let certText = await fs.readFile(cert, 'utf8'); + if (certText.includes('BEGIN CERTIFICATE')) { + certText = `CERTIFICATE HIDDEN BY SFDX-HARDIS. + +Certificates are not supposed to be stored in Git Repositories, please: + +- Make sure they are never overwritten thanks to package-no-overwrite.Xml +- Manually upload them in target orgs when necessary +` + await fs.writeFile(cert, certText); + counter++; + uxLog(this, c.grey(`Replaced certificate content of ${cert}`)); + } + } + + // Summary + const msg = `Updated ${c.green(c.bold(counter))} certificates to hide their content`; + uxLog(this, c.cyan(msg)); + // Return an object to be displayed with --json + return { outputString: msg }; + } +} diff --git a/src/commands/hardis/project/clean/standarditems.ts b/src/commands/hardis/project/clean/standarditems.ts index c5ef64185..91a9f390d 100644 --- a/src/commands/hardis/project/clean/standarditems.ts +++ b/src/commands/hardis/project/clean/standarditems.ts @@ -1,73 +1,64 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import { glob } from "glob"; -import * as path from "path"; -import { uxLog } from "../../../../common/utils"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import { glob } from 'glob'; +import * as path from 'path'; +import { uxLog } from '../../../../common/utils/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class CleanStandardItems extends SfCommand { + public static title = 'Clean retrieved standard items in dx sources'; -export default class CleanStandardItems extends SfdxCommand { - public static title = "Clean retrieved standard items in dx sources"; + public static description = 'Remove unwanted standard items within sfdx project sources'; - public static description = "Remove unwanted standard items within sfdx project sources"; + public static examples = ['$ sf hardis:project:clean:standarditems']; - public static examples = ["$ sfdx hardis:project:clean:standarditems"]; - - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; protected debugMode = false; protected deleteItems: any = {}; public async run(): Promise { - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(CleanStandardItems); + this.debugMode = flags.debug || false; // Delete standard files when necessary uxLog(this, c.cyan(`Removing unwanted standard dx source files...`)); /* jscpd:ignore-end */ - const sourceRootFolder = path.join(process.cwd() + "/force-app/main/default"); - const objectsFolder = path.join(sourceRootFolder + "/objects"); + const sourceRootFolder = path.join(process.cwd() + '/force-app/main/default'); + const objectsFolder = path.join(sourceRootFolder + '/objects'); const objectsFolderContent = await fs.readdir(objectsFolder); for (const objectDirName of objectsFolderContent) { - const objectDir = objectsFolder + "/" + objectDirName; + const objectDir = objectsFolder + '/' + objectDirName; // Process only standard objects - if (fs.lstatSync(objectDir).isDirectory() && !objectDir.includes("__")) { + if (fs.lstatSync(objectDir).isDirectory() && !objectDir.includes('__')) { const findCustomFieldsPattern = `${objectDir}/fields/*__*`; const matchingCustomFiles = await glob(findCustomFieldsPattern, { cwd: process.cwd() }); if (matchingCustomFiles.length === 0) { // Remove the whole folder await fs.remove(objectDir); uxLog(this, c.cyan(`Removed folder ${c.yellow(objectDir)}`)); - const sharingRuleFile = path.join(sourceRootFolder, "sharingRules", objectDirName + ".sharingRules-meta.xml"); + const sharingRuleFile = path.join(sourceRootFolder, 'sharingRules', objectDirName + '.sharingRules-meta.xml'); if (fs.existsSync(sharingRuleFile)) { // Remove sharingRule if existing await fs.remove(sharingRuleFile); @@ -78,7 +69,7 @@ export default class CleanStandardItems extends SfdxCommand { const findAllFieldsPattern = `${objectDir}/fields/*.field-meta.xml`; const matchingAllFields = await glob(findAllFieldsPattern, { cwd: process.cwd() }); for (const field of matchingAllFields) { - if (!field.includes("__")) { + if (!field.includes('__')) { await fs.remove(field); uxLog(this, c.cyan(` - removed standard field ${c.yellow(field)}`)); } @@ -90,6 +81,6 @@ export default class CleanStandardItems extends SfdxCommand { } // Return an object to be displayed with --json - return { outputString: "Cleaned standard items from sfdx project" }; + return { outputString: 'Cleaned standard items from sfdx project' }; } } diff --git a/src/commands/hardis/project/clean/systemdebug.ts b/src/commands/hardis/project/clean/systemdebug.ts index 854e2d25a..a7843f43a 100644 --- a/src/commands/hardis/project/clean/systemdebug.ts +++ b/src/commands/hardis/project/clean/systemdebug.ts @@ -1,61 +1,52 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { glob } from "glob"; -import * as path from "path"; -import { uxLog } from "../../../../common/utils"; -import * as fs from "fs-extra"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { glob } from 'glob'; +import * as path from 'path'; +import { uxLog } from '../../../../common/utils/index.js'; +import fs from 'fs-extra'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class CleanSystemDebug extends SfCommand { + public static title = 'Clean System debug'; -export default class CleanSystemDebug extends SfdxCommand { - public static title = "Clean System debug"; + public static description = 'Clean System.debug() lines in APEX Code (classes and triggers)'; - public static description = "Clean System.debug() lines in APEX Code (classes and triggers)"; + public static examples = ['$ sf hardis:project:clean:systemdebug']; - public static examples = ["$ sfdx hardis:project:clean:systemdebug"]; - - protected static flagsConfig = { - folder: flags.string({ - char: "f", - default: "force-app", - description: "Root folder", + public static flags: any = { + folder: Flags.string({ + char: 'f', + default: 'force-app', + description: 'Root folder', }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), - delete: flags.boolean({ - char: "d", + delete: Flags.boolean({ + char: 'd', default: false, - description: "Delete lines with System.debug", + description: 'Delete lines with System.debug', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; protected folder: string; protected del = false; public async run(): Promise { - this.folder = this.flags.folder || "./force-app"; - this.del = this.flags.delete || false; + const { flags } = await this.parse(CleanSystemDebug); + this.folder = flags.folder || './force-app'; + this.del = flags.delete || false; // Delete standard files when necessary uxLog(this, c.cyan(`Comment or delete System.debug line in apex classes and triggers`)); @@ -65,14 +56,16 @@ export default class CleanSystemDebug extends SfdxCommand { const matchingFiles = await glob(findManagedPattern, { cwd: process.cwd() }); let countFiles = 0; for (const apexFile of matchingFiles) { - const fileText = await fs.readFile(apexFile, "utf8"); - const fileLines = fileText.split("\n"); + const fileText = await fs.readFile(apexFile, 'utf8'); + const fileLines = fileText.split('\n'); let counter = 0; let writeF = false; for (const line of fileLines) { - if ((line.includes("System.debug") || line.includes("system.debug")) && !line.includes("NOPMD")) { - if (!this.del && line.trim().substring(0, 2) != "//") { - fileLines[counter] = line.replace("System.debug", "// System.debug").replace("system.debug", "// system.debug"); + if ((line.includes('System.debug') || line.includes('system.debug')) && !line.includes('NOPMD')) { + if (!this.del && line.trim().substring(0, 2) != '//') { + fileLines[counter] = line + .replace('System.debug', '// System.debug') + .replace('system.debug', '// system.debug'); writeF = true; } else if (this.del) { delete fileLines[counter]; @@ -82,8 +75,8 @@ export default class CleanSystemDebug extends SfdxCommand { counter++; } if (writeF) { - const joinLines = fileLines.join("\n"); - await fs.writeFile(apexFile, joinLines, "utf8"); + const joinLines = fileLines.join('\n'); + await fs.writeFile(apexFile, joinLines, 'utf8'); countFiles++; } } diff --git a/src/commands/hardis/project/clean/xml.ts b/src/commands/hardis/project/clean/xml.ts index 61bf85900..d1e80cd2d 100644 --- a/src/commands/hardis/project/clean/xml.ts +++ b/src/commands/hardis/project/clean/xml.ts @@ -1,28 +1,24 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages, SfdxError } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import { glob } from "glob"; -import * as path from "path"; -import * as sortArray from "sort-array"; -import * as xmldom from "@xmldom/xmldom"; -import * as xpath from "xpath"; -import { isCI, uxLog } from "../../../../common/utils"; -import { prompts } from "../../../../common/utils/prompts"; -import { writeXmlFileFormatted } from "../../../../common/utils/xmlUtils"; -import { getConfig, setConfig } from "../../../../config"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class CleanXml extends SfdxCommand { - public static title = "Clean retrieved empty items in dx sources"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages, SfError } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import { glob } from 'glob'; +import * as path from 'path'; +import sortArray from 'sort-array'; +import * as xmldom from '@xmldom/xmldom'; +import * as xpath from 'xpath'; +import { isCI, uxLog } from '../../../../common/utils/index.js'; +import { prompts } from '../../../../common/utils/prompts.js'; +import { writeXmlFileFormatted } from '../../../../common/utils/xmlUtils.js'; +import { getConfig, setConfig } from '../../../../config/index.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class CleanXml extends SfCommand { + public static title = 'Clean retrieved empty items in dx sources'; public static description = `Remove XML elements using Glob patterns and XPath expressions @@ -39,65 +35,61 @@ Note: If globpattern and xpath are not sent, elements defined in property **clea `; public static examples = [ - "$ sfdx hardis:project:clean:xml", - `$ sfdx hardis:project:clean:xml --globpattern "/**/*.flexipage-meta.xml" --xpath "//ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')]"`, + '$ sf hardis:project:clean:xml', + `$ sf hardis:project:clean:xml --globpattern "/**/*.flexipage-meta.xml" --xpath "//ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')]"`, ]; - protected static flagsConfig = { - folder: flags.string({ - char: "f", - default: "force-app", - description: "Root folder", + public static flags: any = { + folder: Flags.string({ + char: 'f', + default: 'force-app', + description: 'Root folder', }), - globpattern: flags.string({ - char: "p", - description: "Glob pattern to find files to clean. Ex: /**/*.flexipage-meta.xml", - dependsOn: ["xpath"], + globpattern: Flags.string({ + char: 'p', + description: 'Glob pattern to find files to clean. Ex: /**/*.flexipage-meta.xml', + dependsOn: ['xpath'], }), - xpath: flags.string({ - char: "x", - description: "XPath to use to detect the elements to remove. Ex: //ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')]", - dependsOn: ["globpattern"], + xpath: Flags.string({ + char: 'x', + description: + "XPath to use to detect the elements to remove. Ex: //ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')]", + dependsOn: ['globpattern'], }), - namespace: flags.string({ - char: "n", - default: "http://soap.sforce.com/2006/04/metadata", - description: "XML Namespace to use", + namespace: Flags.string({ + char: 'n', + default: 'http://soap.sforce.com/2006/04/metadata', + description: 'XML Namespace to use', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; protected folder: string; - protected globPattern: string; + protected globPattern: string | undefined; protected namespace: string; - protected xpath: string; + protected xpath: string | undefined; protected debugMode = false; public async run(): Promise { - this.folder = this.flags.folder || "./force-app"; - this.globPattern = this.flags.globpattern; - this.xpath = this.flags.xpath; - this.namespace = this.flags.namespace || "http://soap.sforce.com/2006/04/metadata"; - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(CleanXml); + this.folder = flags.folder || './force-app'; + this.globPattern = flags.globpattern; + this.xpath = flags.xpath; + this.namespace = flags.namespace || 'http://soap.sforce.com/2006/04/metadata'; + this.debugMode = flags.debug || false; // Delete standard files when necessary uxLog(this, c.cyan(`Clean XML elements matching patterns`)); @@ -113,12 +105,12 @@ Note: If globpattern and xpath are not sent, elements defined in property **clea // Iterate on matching files for (const xmlFile of matchingXmlFiles) { let updated = false; - const xml = await fs.readFile(xmlFile, "utf8"); + const xml = await fs.readFile(xmlFile, 'utf8'); const doc = new xmldom.DOMParser().parseFromString(xml); // Iterate on xpaths for (const xpathItem of cleanXmlPattern.xpaths) { const nodes = xpathSelect(xpathItem, doc); - for (const node of nodes) { + for (const node of nodes as Node[]) { await this.removeXPath(xpathItem, doc, node); uxLog(this, c.grey(`Removed xpath ${xpathItem} from ${xmlFile}`)); updated = true; @@ -146,7 +138,7 @@ Note: If globpattern and xpath are not sent, elements defined in property **clea public async buildCleanXmlPatterns() { // Input parameters if (this.globPattern && this.xpath) { - uxLog(this, c.cyan("Using configuration from input arguments...")); + uxLog(this, c.cyan('Using configuration from input arguments...')); return [ { globPattern: this.globPattern, @@ -155,8 +147,11 @@ Note: If globpattern and xpath are not sent, elements defined in property **clea ]; } // Stored config - uxLog(this, c.cyan(`Using configuration from property ${c.bold("cleanXmlPatterns")} in .sfdx-hardis.yml config file...`)); - const config = await getConfig("branch"); + uxLog( + this, + c.cyan(`Using configuration from property ${c.bold('cleanXmlPatterns')} in .sfdx-hardis.yml config file...`) + ); + const config = await getConfig('branch'); return config.cleanXmlPatterns || []; } @@ -169,16 +164,16 @@ Note: If globpattern and xpath are not sent, elements defined in property **clea } public findRemoveParentNodeName(xpathItem: string) { - const splits = xpathItem.split("//ns:").filter((str) => str !== ""); + const splits = xpathItem.split('//ns:').filter((str) => str !== ''); if (splits[0]) { return splits[0]; } - throw new SfdxError(`[sfdx-hardis] xpath should start with //ns:PARENT-TAG-NAME//ns:`); + throw new SfError(`[sfdx-hardis] xpath should start with //ns:PARENT-TAG-NAME//ns:`); } public findParentNode(node: any, parentNodeName: string) { if (node == null) { - throw new SfdxError(`[sfdx-hardis] Parent node named ${parentNodeName} not found`); + throw new SfError(`[sfdx-hardis] Parent node named ${parentNodeName} not found`); } if (node.localName === parentNodeName) { return node; @@ -189,16 +184,20 @@ Note: If globpattern and xpath are not sent, elements defined in property **clea // Propose user to perform such cleaning at each future hardis:work:save command public async manageAddToPermanentConfig(globPattern: string, xpath: string) { if (!isCI) { - const config = await getConfig("project"); + const config = await getConfig('project'); let cleanXmlPatterns = config.cleanXmlPatterns || []; - const alreadyDefined = cleanXmlPatterns.filter((item: any) => item.globPattern === globPattern && item.xpaths.includes(xpath)); + const alreadyDefined = cleanXmlPatterns.filter( + (item: any) => item.globPattern === globPattern && item.xpaths.includes(xpath) + ); if (alreadyDefined.length > 0) { return; } // prompt user const addConfigRes = await prompts({ - type: "confirm", - message: c.cyanBright(`Do you want to ALWAYS apply removal of xpath ${xpath} from files of pattern ${globPattern} ?`), + type: 'confirm', + message: c.cyanBright( + `Do you want to ALWAYS apply removal of xpath ${xpath} from files of pattern ${globPattern} ?` + ), }); if (addConfigRes.value === true) { let updated = false; @@ -220,10 +219,10 @@ Note: If globpattern and xpath are not sent, elements defined in property **clea } // Update config with sorted new value cleanXmlPatterns = sortArray(cleanXmlPatterns, { - by: ["globPattern"], - order: ["asc"], + by: ['globPattern'], + order: ['asc'], }); - await setConfig("project", { cleanXmlPatterns: cleanXmlPatterns }); + await setConfig('project', { cleanXmlPatterns: cleanXmlPatterns }); } } } diff --git a/src/commands/hardis/project/configure/auth.ts b/src/commands/hardis/project/configure/auth.ts index 38c799674..37b71615c 100644 --- a/src/commands/hardis/project/configure/auth.ts +++ b/src/commands/hardis/project/configure/auth.ts @@ -1,133 +1,126 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { execSfdxJson, generateSSLCertificate, promptInstanceUrl, uxLog } from "../../../../common/utils"; -import { getOrgAliasUsername, promptOrg } from "../../../../common/utils/orgUtils"; -import { prompts } from "../../../../common/utils/prompts"; -import { checkConfig, getConfig, setConfig, setInConfigFile } from "../../../../config"; -import { WebSocketClient } from "../../../../common/websocketClient"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class ConfigureAuth extends SfdxCommand { - public static title = "Configure authentication"; - - public static description = "Configure authentication from git branch to target org"; - - public static examples = ["$ sfdx hardis:project:configure:auth"]; +import { + SfCommand, + Flags, + optionalOrgFlagWithDeprecations, + optionalHubFlagWithDeprecations, +} from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { execSfdxJson, generateSSLCertificate, promptInstanceUrl, uxLog } from '../../../../common/utils/index.js'; +import { getOrgAliasUsername, promptOrg } from '../../../../common/utils/orgUtils.js'; +import { prompts } from '../../../../common/utils/prompts.js'; +import { checkConfig, getConfig, setConfig, setInConfigFile } from '../../../../config/index.js'; +import { WebSocketClient } from '../../../../common/websocketClient.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class ConfigureAuth extends SfCommand { + public static title = 'Configure authentication'; + + public static description = 'Configure authentication from git branch to target org'; + + public static examples = ['$ sf hardis:project:configure:auth']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - devhub: flags.boolean({ - char: "b", + public static flags: any = { + devhub: Flags.boolean({ + char: 'b', default: false, - description: "Configure project DevHub", + description: 'Configure project DevHub', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': optionalOrgFlagWithDeprecations, + 'target-dev-hub': optionalHubFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static supportsUsername = true; - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static supportsDevhubUsername = true; - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; - protected static requiresDependencies = ["openssl"]; + protected static requiresDependencies = ['openssl']; /* jscpd:ignore-end */ public async run(): Promise { - const devHub = this.flags.devhub || false; + const { flags } = await this.parse(ConfigureAuth); + const devHub = flags.devhub || false; // Ask user to login to org - const prevUserName = devHub ? this.hubOrg?.getUsername() : this.org?.getUsername(); - /*uxLog(this, c.cyan("Please select org login into the org you want to configure the SFDX Authentication")); - await this.config.runHook("auth", { - checkAuth: true, - Command: this, - alias: "CONFIGURE_CI", - devHub, - }); */ + const prevUserName = devHub ? flags['target-dev-hub']?.getUsername() : flags['target-org']?.getUsername(); await promptOrg(this, { setDefault: true, devHub: devHub, - promptMessage: "Please select org login into the org you want to configure the SFDX Authentication", + promptMessage: 'Please select or login into the org you want to configure the SF CLI Authentication', }); await checkConfig(this); // Check if the user has changed. If yes, ask to run the command again - const configGetRes = await execSfdxJson("sfdx config:get " + (devHub ? "defaultdevhubusername" : "defaultusername"), this, { + const configGetRes = await execSfdxJson('sf config get ' + (devHub ? 'target-dev-hub' : 'target-org'), this, { output: false, fail: false, }); - let newUsername = configGetRes?.result[0]?.value || ""; + let newUsername = configGetRes?.result[0]?.value || ''; newUsername = (await getOrgAliasUsername(newUsername)) || newUsername; if (prevUserName !== newUsername) { // Restart command so the org is selected as default org (will help to select profiles) - const infoMsg = "Default org changed. Please restart the same command if VsCode does not do that automatically for you :)"; + const infoMsg = + 'Default org changed. Please restart the same command if VsCode does not do that automatically for you :)'; uxLog(this, c.yellow(infoMsg)); - const currentCommand = "sfdx " + this.id + " " + this.argv.join(" "); + const currentCommand = 'sf ' + this.id + ' ' + this.argv.join(' '); WebSocketClient.sendMessage({ - event: "runSfdxHardisCommand", + event: 'runSfdxHardisCommand', sfdxHardisCommand: currentCommand, }); return { outputString: infoMsg }; } - const config = await getConfig("project"); + const config = await getConfig('project'); // Get branch name to configure if not Dev Hub - let branchName = ""; - let instanceUrl = "https://login.salesforce.com"; + let branchName = ''; + let instanceUrl = 'https://login.salesforce.com'; if (!devHub) { const branchResponse = await prompts({ - type: "text", - name: "value", - message: c.cyanBright("What is the name of the git branch you want to configure ? Examples: developpement,recette,production"), + type: 'text', + name: 'value', + message: c.cyanBright( + 'What is the name of the git branch you want to configure ? Examples: developpement,recette,production' + ), }); - branchName = branchResponse.value.replace(/\s/g, "-"); + branchName = branchResponse.value.replace(/\s/g, '-'); /* if (["main", "master"].includes(branchName)) { - throw new SfdxError("You can not use main or master as deployment branch name. Maybe you want to use production ?"); + throw new SfError("You can not use main or master as deployment branch name. Maybe you want to use production ?"); } */ - instanceUrl = await promptInstanceUrl(["login", "test"], `${branchName} related org`, { - instanceUrl: devHub ? this.hubOrg.getConnection().instanceUrl : this.org.getConnection().instanceUrl, + instanceUrl = await promptInstanceUrl(['login', 'test'], `${branchName} related org`, { + instanceUrl: devHub + ? flags['target-dev-hub']?.getConnection()?.instanceUrl || "" + : flags['target-org']?.getConnection()?.instanceUrl || "", }); } // Request username const usernameResponse = await prompts({ - type: "text", - name: "value", - initial: (devHub ? this.hubOrg.getUsername() : this.org.getUsername()) || "", + type: 'text', + name: 'value', + initial: (devHub ? flags['target-dev-hub']?.getUsername() || "" : flags['target-org'].getUsername() || "") || '', message: c.cyanBright( - `What is the Salesforce username that will be ${ - devHub ? "used as Dev Hub" : "used for deployments by CI server" - } ? Example: admin.sfdx@myclient.com`, + `What is the Salesforce username that will be ${devHub ? 'used as Dev Hub' : 'used for deployments by CI server' + } ? Example: admin.sfdx@myclient.com` ), }); if (devHub) { - await setConfig("project", { + await setConfig('project', { devHubUsername: usernameResponse.value, }); } else { @@ -138,19 +131,19 @@ export default class ConfigureAuth extends SfdxCommand { targetUsername: usernameResponse.value, instanceUrl, }, - `./config/branches/.sfdx-hardis.${branchName}.yml`, + `./config/branches/.sfdx-hardis.${branchName}.yml` ); } // Generate SSL certificate (requires openssl to be installed on computer) - const certFolder = devHub ? "./config/.jwt" : "./config/branches/.jwt"; + const certFolder = devHub ? './config/.jwt' : './config/branches/.jwt'; const certName = devHub ? config.devHubAlias : branchName; - const orgConn = devHub ? this.hubOrg?.getConnection() : this.org?.getConnection(); + const orgConn = devHub ? flags['target-dev-hub']?.getConnection() : flags['target-org']?.getConnection(); const sslGenOptions = { - targetUsername: devHub ? this.hubOrg?.getUsername() : this.org?.getUsername(), + targetUsername: devHub ? flags['target-dev-hub']?.getUsername() : flags['target-org']?.getUsername(), }; await generateSSLCertificate(certName, certFolder, this, orgConn, sslGenOptions); // Return an object to be displayed with --json - return { outputString: "Configured branch for authentication" }; + return { outputString: 'Configured branch for authentication' }; } } diff --git a/src/commands/hardis/project/convert/profilestopermsets.ts b/src/commands/hardis/project/convert/profilestopermsets.ts index 9bcb6d0a9..3cebd2ed9 100644 --- a/src/commands/hardis/project/convert/profilestopermsets.ts +++ b/src/commands/hardis/project/convert/profilestopermsets.ts @@ -1,73 +1,66 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; -import { execCommand, uxLog } from "../../../../common/utils"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; +import { execCommand, uxLog } from '../../../../common/utils/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class ConvertProfilesToPermSets extends SfCommand { + public static title = 'Convert Profiles into Permission Sets'; -export default class ConvertProfilesToPermSets extends SfdxCommand { - public static title = "Convert Profiles into Permission Sets"; + public static description = 'Creates permission sets from existing profiles, with id PS_PROFILENAME'; - public static description = "Creates permission sets from existing profiles, with id PS_PROFILENAME"; + public static examples = ['$ sf hardis:project:convert:profilestopermsets']; - public static examples = ["$ sfdx hardis:project:convert:profilestopermsets"]; - - protected static flagsConfig = { - except: flags.array({ - char: "e", + public static flags: any = { + except: Flags.string({ + char: 'e', default: [], - description: "List of filters", + description: 'List of filters', + multiple: true, }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), - }; - - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + }; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default + public static requiresProject = true; /* jscpd:ignore-end */ // List required plugins, their presence will be tested before running the command - protected static requiresSfdxPlugins = ["shane-sfdx-plugins"]; + protected static requiresSfdxPlugins = ['shane-sfdx-plugins']; public async run(): Promise { - const except = this.flags.except || []; + const { flags } = await this.parse(ConvertProfilesToPermSets); + const except = flags.except || []; - uxLog(this, c.cyan("This command will convert profiles into permission sets")); + uxLog(this, c.cyan('This command will convert profiles into permission sets')); - const sourceRootFolder = path.join(process.cwd() + "/force-app/main/default"); - const profilesFolder = path.join(sourceRootFolder, "profiles"); + const sourceRootFolder = path.join(process.cwd() + '/force-app/main/default'); + const profilesFolder = path.join(sourceRootFolder, 'profiles'); const objectsFolderContent = await fs.readdir(profilesFolder); for (const profileFile of objectsFolderContent) { - if (profileFile.includes(".profile-meta.xml")) { - const profileName = path.basename(profileFile).replace(".profile-meta.xml", ""); + if (profileFile.includes('.profile-meta.xml')) { + const profileName = path.basename(profileFile).replace('.profile-meta.xml', ''); if (except.filter((str) => profileName.toLowerCase().includes(str)).length > 0) { continue; } - const psName = "PS_" + profileName.split(" ").join("_"); + const psName = 'PS_' + profileName.split(' ').join('_'); uxLog(this, c.cyan(`Generating Permission set ${c.green(psName)} from profile ${c.green(profileName)}`)); - const convertCommand = "sfdx shane:profile:convert" + ` -p "${profileName}"` + ` -n "${psName}"` + " -e"; + const convertCommand = 'sf shane:profile:convert' + ` -p "${profileName}"` + ` -n "${psName}"` + ' -e'; await execCommand(convertCommand, this, { fail: true, output: true }); } } diff --git a/src/commands/hardis/project/create.ts b/src/commands/hardis/project/create.ts index 894c8c43c..12a00977b 100644 --- a/src/commands/hardis/project/create.ts +++ b/src/commands/hardis/project/create.ts @@ -1,84 +1,75 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import { ensureGitRepository, execCommand, uxLog } from "../../../common/utils"; -import { prompts } from "../../../common/utils/prompts"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; -import { getConfig, setConfig } from "../../../config"; -import { WebSocketClient } from "../../../common/websocketClient"; -import { isSfdxProject } from "../../../common/utils/projectUtils"; -import { PACKAGE_ROOT_DIR } from "../../../settings"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class ProjectCreate extends SfdxCommand { - public static title = "Login"; - - public static description = "Create a new SFDX Project"; - - public static examples = ["$ sfdx hardis:project:create"]; - - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { ensureGitRepository, execCommand, uxLog } from '../../../common/utils/index.js'; +import { prompts } from '../../../common/utils/prompts.js'; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; +import { CONSTANTS, getConfig, setConfig } from '../../../config/index.js'; +import { WebSocketClient } from '../../../common/websocketClient.js'; +import { isSfdxProject } from '../../../common/utils/projectUtils.js'; +import { PACKAGE_ROOT_DIR } from '../../../settings.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class ProjectCreate extends SfCommand { + public static title = 'Login'; + + public static description = 'Create a new SFDX Project'; + + public static examples = ['$ sf hardis:project:create']; + + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; protected debugMode = false; /* jscpd:ignore-end */ public async run(): Promise { - this.debugMode = this.flags.debugMode || false; + const { flags } = await this.parse(ProjectCreate); + this.debugMode = flags.debug || false; // Check git repo await ensureGitRepository({ clone: true }); const devHubPrompt = await prompts({ - name: "orgType", - type: "select", - message: "To perform implementation, will your project use scratch org or source tracked sandboxes only ?", + name: 'orgType', + type: 'select', + message: 'To perform implementation, will your project use scratch org or source tracked sandboxes only ?', choices: [ { - title: "Scratch orgs only", - value: "scratch", + title: 'Scratch orgs only', + value: 'scratch', }, { - title: "Source tracked sandboxes only", - value: "sandbox", + title: 'Source tracked sandboxes only', + value: 'sandbox', }, { - title: "Source tracked sandboxes and scratch orgs", - value: "sandboxAndScratch", + title: 'Source tracked sandboxes and scratch orgs', + value: 'sandboxAndScratch', }, ], }); - if (["scratch", "sandboxAndScratch"].includes(devHubPrompt.orgType)) { + if (['scratch', 'sandboxAndScratch'].includes(devHubPrompt.orgType)) { // Connect to DevHub - await this.config.runHook("auth", { + await this.config.runHook('auth', { checkAuth: true, Command: this, devHub: true, @@ -86,21 +77,21 @@ export default class ProjectCreate extends SfdxCommand { }); } // Project name - let config = await getConfig("project"); + let config = await getConfig('project'); let projectName = config.projectName; if (projectName == null) { // User prompts const projectRes = await prompts({ - type: "text", - name: "projectName", - message: "What is the name of your project ? (example: MyClient)", + type: 'text', + name: 'projectName', + message: 'What is the name of your project ? (example: MyClient)', }); - projectName = projectRes.projectName.toLowerCase().replace(" ", "_"); + projectName = projectRes.projectName.toLowerCase().replace(' ', '_'); } // Create sfdx project only if not existing if (!isSfdxProject()) { - const createCommand = "sfdx force:project:create" + ` --projectname "${projectName}"` + " --manifest"; + const createCommand = 'sf project generate' + ` --name "${projectName}"` + ' --manifest'; await execCommand(createCommand, this, { output: true, fail: true, @@ -114,36 +105,44 @@ export default class ProjectCreate extends SfdxCommand { await fs.rm(path.join(process.cwd(), projectName), { recursive: true }); } // Copy default project files - uxLog(this, "Copying default files..."); - await fs.copy(path.join(PACKAGE_ROOT_DIR, "defaults/ci", "."), process.cwd(), { overwrite: false }); + uxLog(this, 'Copying default files...'); + await fs.copy(path.join(PACKAGE_ROOT_DIR, 'defaults/ci', '.'), process.cwd(), { overwrite: false }); - config = await getConfig("project"); + config = await getConfig('project'); if (config.developmentBranch == null) { // User prompts const devBranchRes = await prompts({ - type: "text", - name: "devBranch", + type: 'text', + name: 'devBranch', message: - "What is the name of your default development branch ? (Examples: if you manage RUN and BUILD, it can be integration. If you manage RUN only, it can be preprod)", - initial: "integration", + 'What is the name of your default development branch ? (Examples: if you manage RUN and BUILD, it can be integration. If you manage RUN only, it can be preprod)', + initial: 'integration', }); - await setConfig("project", { developmentBranch: devBranchRes.devBranch }); + await setConfig('project', { developmentBranch: devBranchRes.devBranch }); } - await setConfig("project", { autoCleanTypes: ["destructivechanges"] }); - + // Initialize autoCleanTypes + const defaultAutoCleanTypes = [ + 'destructivechanges', + 'flowPositions', + 'minimizeProfiles']; + await setConfig('project', { + autoCleanTypes: defaultAutoCleanTypes + }); + uxLog(this, c.yellow(`autoCleanTypes ${defaultAutoCleanTypes.join(",")} has been activated on the new project.`)); + uxLog(this, c.bold(c.yellow(`If you install CI/CD on an existing org with many rights in Profiles, you might remove "minimizeProfiles" from .sfdx-hardis.yml autoCleanTypes property `))); // Message instructions uxLog( this, c.cyan( - "SFDX Project has been created. You can continue the steps in documentation at https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-home/", - ), + `SFDX Project has been created. You can continue the steps in documentation at ${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-home/` + ) ); // Trigger commands refresh on VsCode WebSocket Client - WebSocketClient.sendMessage({ event: "refreshCommands" }); + WebSocketClient.sendMessage({ event: 'refreshCommands' }); // Return an object to be displayed with --json - return { outputString: "Created SFDX Project" }; + return { outputString: 'Created SFDX Project' }; } } diff --git a/src/commands/hardis/project/deploy/quick.ts b/src/commands/hardis/project/deploy/quick.ts new file mode 100644 index 000000000..5c71b7a2e --- /dev/null +++ b/src/commands/hardis/project/deploy/quick.ts @@ -0,0 +1,116 @@ +/* jscpd:ignore-start */ +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { AnyJson } from "@salesforce/ts-types"; +import { wrapSfdxCoreCommand } from "../../../../common/utils/wrapUtils.js"; +import { checkDeploymentOrgCoverage, executePrePostCommands, extractOrgCoverageFromLog } from '../../../../common/utils/deployUtils.js'; +import { GitProvider } from '../../../../common/gitProvider/index.js'; + +export default class ProjectDeployStart extends SfCommand { + public static description = `sfdx-hardis wrapper for **sf project deploy quick** that displays tips to solve deployment errors. + +[![Assisted solving of Salesforce deployments errors](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-deployment-errors.jpg)](https://nicolas.vuillamy.fr/assisted-solving-of-salesforce-deployments-errors-47f3666a9ed0) + +[See documentation of Salesforce command](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_project_commands_unified.htm#cli_reference_project_deploy_quick_unified) + +### Deployment pre or post commands + +You can define command lines to run before or after a deployment, with parameters: + +- **id**: Unique Id for the command +- **label**: Human readable label for the command +- **skipIfError**: If defined to "true", the post-command won't be run if there is a deployment failure +- **context**: Defines the context where the command will be run. Can be **all** (default), **check-deployment-only** or **process-deployment-only** +- **runOnlyOnceByOrg**: If set to true, the command will be run only one time per org. A record of SfdxHardisTrace__c is stored to make that possible (it needs to be existing in target org) + +If the commands are not the same depending on the target org, you can define them into **config/branches/.sfdx-hardis-BRANCHNAME.yml** instead of root **config/.sfdx-hardis.yml** + +Example: + +\`\`\`yaml +commandsPreDeploy: + - id: knowledgeUnassign + label: Remove KnowledgeUser right to the user who has it + command: sf data update record --sobject User --where "UserPermissionsKnowledgeUser='true'" --values "UserPermissionsKnowledgeUser='false'" --json + - id: knowledgeAssign + label: Assign Knowledge user to the deployment user + command: sf data update record --sobject User --where "Username='deploy.github@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + +commandsPostDeploy: + - id: knowledgeUnassign + label: Remove KnowledgeUser right to the user who has it + command: sf data update record --sobject User --where "UserPermissionsKnowledgeUser='true'" --values "UserPermissionsKnowledgeUser='false'" --json + - id: knowledgeAssign + label: Assign Knowledge user to desired username + command: sf data update record --sobject User --where "Username='admin-yser@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + - id: someActionToRunJustOneTime + label: And to run only if deployment is success + command: sf sfdmu:run ... + skipIfError: true + context: process-deployment-only + runOnlyOnceByOrg: true +\`\`\` +`; + + public static flags: any = { + "api-version": Flags.integer({ + char: "a", + description: "api-version", + }), + async: Flags.boolean({ + description: "async", + exclusive: ["wait"], + }), + "target-org": Flags.requiredOrg(), + tests: Flags.string({ + description: "tests", + }), + "--job-id": Flags.string({ + char: "i", + description: "job-id", + }), + "--use-most-recent": Flags.boolean({ + char: "r", + description: "use-most-recent", + }), + wait: Flags.integer({ + char: "w", + default: 33, + min: 1, + description: "wait", + exclusive: ["async"], + }), + debug: Flags.boolean({ + default: false, + description: "debug", + }), + }; + + public static requiresProject = true; + + public async run(): Promise { + const { flags } = await this.parse(ProjectDeployStart); + const conn = flags["target-org"].getConnection(); + // Run pre deployment commands if defined + await executePrePostCommands('commandsPreDeploy', { success: true, checkOnly: false, conn: conn }); + const result = await wrapSfdxCoreCommand("sf project deploy start", this.argv, this, flags.debug); + // Check org coverage if requested + if (flags['coverage-formatters'] && result.stdout) { + const orgCoveragePercent = await extractOrgCoverageFromLog(result.stdout + result.stderr || ''); + const checkOnly = false; + if (orgCoveragePercent) { + try { + await checkDeploymentOrgCoverage(Number(orgCoveragePercent), { check: checkOnly }); + } catch (errCoverage) { + await GitProvider.managePostPullRequestComment(); + throw errCoverage; + } + } + } + // Run post deployment commands if defined + await executePrePostCommands('commandsPostDeploy', { success: process.exitCode === 0, checkOnly: false, conn: conn }); + await GitProvider.managePostPullRequestComment(); + return result; + } +} + +/* jscpd:ignore-end */ \ No newline at end of file diff --git a/src/commands/hardis/project/deploy/simulate.ts b/src/commands/hardis/project/deploy/simulate.ts new file mode 100644 index 000000000..c7de88b64 --- /dev/null +++ b/src/commands/hardis/project/deploy/simulate.ts @@ -0,0 +1,75 @@ +/* jscpd:ignore-start */ +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { promptOrgUsernameDefault } from '../../../../common/utils/orgUtils.js'; +import { wrapSfdxCoreCommand } from '../../../../common/utils/wrapUtils.js'; + + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class DeploySimulate extends SfCommand { + public static title = 'Simulate the deployment of a metadata in an org prompted to the user\nUsed by VsCode Extension'; + + public static description = `Simulate the deployment of a metadata in an org prompted to the user + +For example, helps to solve the issue in a Permission Set without having to run a CI/CD job. + +Used by VsCode Extension`; + + public static examples = [ + '$ sf hardis:project:deploy:simulate --source-dir force-app/defaut/main/permissionset/PS_Admin.permissionset-meta.xml', + ]; + + // public static args = [{name: 'file'}]; + + public static flags: any = { + "source-dir": Flags.string({ + char: "d", + description: "Source file or directory to simulate the deployment", + multiple: true, + required: true + }), + debug: Flags.boolean({ + char: 'd', + default: false, + description: messages.getMessage('debugMode'), + }), + websocket: Flags.string({ + description: messages.getMessage('websocket'), + }), + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', + }), + 'target-org': requiredOrgFlagWithDeprecations, + }; + + // Set this to true if your command requires a project workspace; 'requiresProject' is false by default + public static requiresProject = true; + /* jscpd:ignore-end */ + + protected debugMode = false; + + public async run(): Promise { + const { flags } = await this.parse(DeploySimulate); + const sourceDirOrFile = flags["source-dir"]; + this.debugMode = flags.debug || false; + + // Prompt target org to user + const orgUsername = await promptOrgUsernameDefault(this, + flags['target-org'].getUsername(), + { devHub: false, setDefault: false, message: `Do you want to use org ${flags['target-org'].getConnection().instanceUrl} to simulate deployment of metadata ${sourceDirOrFile} ?`, quickOrgList: true }); + + // Build command + const simulateDeployCommand = "sf project deploy start" + + ` --source-dir "${sourceDirOrFile}"` + + ` --target-org ${orgUsername}` + + ` --ignore-conflicts` + + ` --dry-run`; + + // Simulate deployment + const result = await wrapSfdxCoreCommand(simulateDeployCommand, [], this, flags.debug); + return result; + } +} diff --git a/src/commands/hardis/project/deploy/smart.ts b/src/commands/hardis/project/deploy/smart.ts new file mode 100644 index 000000000..83260b75a --- /dev/null +++ b/src/commands/hardis/project/deploy/smart.ts @@ -0,0 +1,764 @@ +/* jscpd:ignore-start */ +/* +To test locally, you can call the command like that: + +Gitlab: CI=true CI_SFDX_HARDIS_GITLAB_TOKEN=XXX CI_PROJECT_ID=YYY CI_JOB_TOKEN=xxx NODE_OPTIONS=--inspect-brk sf hardis:project:deploy:smart --target-org nicolas.vuillamy@cloudity.com.demointeg + +Azure: CI=true SYSTEM_ACCESSTOKEN=XXX SYSTEM_COLLECTIONURI=https://dev.azure.com/MyAzureCollection/ BUILD_REPOSITORY_ID=XXX CI_JOB_TOKEN=xxx NODE_OPTIONS=--inspect-brk sf hardis:project:deploy:smart --target-org nicolas.vuillamy@cloudity.com.muuuurf + +- Before, you need to make a sf alias set myBranch=myUsername +- You can find CI_PROJECT_ID with https://gitlab.com/api/v4/projects?search=YOUR-REPO-NAME + +*/ + +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; +import { MetadataUtils } from '../../../../common/metadata-utils/index.js'; +import { + createTempDir, + getCurrentGitBranch, + getLatestGitCommit, + isCI, + uxLog, +} from '../../../../common/utils/index.js'; +import { CONSTANTS, getConfig } from '../../../../config/index.js'; +import { smartDeploy, removePackageXmlContent } from '../../../../common/utils/deployUtils.js'; +import { isProductionOrg, promptOrgUsernameDefault } from '../../../../common/utils/orgUtils.js'; +import { getApexTestClasses } from '../../../../common/utils/classUtils.js'; +import { listMajorOrgs, restoreListViewMine } from '../../../../common/utils/orgConfigUtils.js'; +import { NotifProvider, UtilsNotifs } from '../../../../common/notifProvider/index.js'; +import { GitProvider } from '../../../../common/gitProvider/index.js'; +import { callSfdxGitDelta, computeCommitsSummary, getGitDeltaScope } from '../../../../common/utils/gitUtils.js'; +import { getBranchMarkdown, getNotificationButtons, getOrgMarkdown } from '../../../../common/utils/notifUtils.js'; +import { MessageAttachment } from '@slack/web-api'; +import { TicketProvider } from '../../../../common/ticketProvider/index.js'; +import { parsePackageXmlFile } from '../../../../common/utils/xmlUtils.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class SmartDeploy extends SfCommand { + public static title = 'Smart Deploy sfdx sources to org'; + + public static aliases = [ + "hardis:project:deploy:sources:dx" + ] + + public static description = `Smart deploy of SFDX sources to target org, with many useful options. + +In case of errors, [tips to fix them](${CONSTANTS.DOC_URL_ROOT}/deployTips/) will be included within the error messages. + +### Quick Deploy + +In case Pull Request comments are configured on the project, Quick Deploy will try to be used (equivalent to button Quick Deploy) + +If you do not want to use QuickDeploy, define variable \`SFDX_HARDIS_QUICK_DEPLOY=false\` + +- [GitHub Pull Requests comments config](${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-integration-github/) +- [Gitlab Merge requests notes config](${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-integration-gitlab/) +- [Azure Pull Requests comments config](${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-integration-azure/) + +### Delta deployments + +To activate delta deployments, define property \`useDeltaDeployment: true\` in \`config/.sfdx-hardis.yml\`. + +This will activate delta deployments only between minor and major branches (major to major remains full deployment mode) + +If you want to force the delta deployment into major orgs (ex: preprod to prod), this is not recommended but you can use env variable ALWAYS_ENABLE_DELTA_DEPLOYMENT=true + +### Smart Deployments Tests + +Not all metadata updates can break test classes, use Smart Deployment Tests to skip running test classes if ALL the following conditions are met: + +- Delta deployment is activated and applicable to the source and target branches +- Delta deployed metadatas are all matching the list of **NOT_IMPACTING_METADATA_TYPES** (see below) +- Target org is not a production org + +Activate Smart Deployment tests with: + +- env variable \`USE_SMART_DEPLOYMENT_TESTS=true\` +- .sfdx-hardis.yml config property \`useSmartDeploymentTests: true\` + +Defaut list for **NOT_IMPACTING_METADATA_TYPES** (can be overridden with comma-separated list on env var NOT_IMPACTING_METADATA_TYPES) + +- Audience +- AuraDefinitionBundle +- Bot +- BotVersion +- ContentAsset +- CustomObjectTranslation +- CustomSite +- CustomTab +- Dashboard +- ExperienceBundle +- Flexipage +- GlobalValueSetTranslation +- Layout +- LightningComponentBundle +- NavigationMenu +- ReportType +- Report +- SiteDotCom +- StandardValueSetTranslation +- StaticResource +- Translations + +Note: if you want to disable Smart test classes for a PR, add **nosmart** in the text of the latest commit. + +### Dynamic deployment items / Overwrite management + +If necessary,you can define the following files (that supports wildcards *): + +- \`manifest/package-no-overwrite.xml\`: Every element defined in this file will be deployed only if it is not existing yet in the target org (can be useful with ListView for example, if the client wants to update them directly in production org) +- \`manifest/packageXmlOnChange.xml\`: Every element defined in this file will not be deployed if it already has a similar definition in target org (can be useful for SharingRules for example) + +See [Overwrite management documentation](${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-config-overwrite/) + +### Deployment plan + +If you need to deploy in multiple steps, you can define a property \`deploymentPlan\` in \`.sfdx-hardis.yml\`. + +- If a file \`manifest/package.xml\` is found, it will be placed with order 0 in the deployment plan + +- If a file \`manifest/destructiveChanges.xml\` is found, it will be executed as --postdestructivechanges + +- If env var \`SFDX_HARDIS_DEPLOY_IGNORE_SPLIT_PACKAGES\` is defined as \`false\` , split of package.xml will be applied + +Example: + +\`\`\`yaml +deploymentPlan: + packages: + - label: Deploy Flow-Workflow + packageXmlFile: manifest/splits/packageXmlFlowWorkflow.xml + order: 6 + - label: Deploy SharingRules - Case + packageXmlFile: manifest/splits/packageXmlSharingRulesCase.xml + order: 30 + waitAfter: 30 +\`\`\` + +### Packages installation + +You can define a list of package to install during deployments using property \`installedPackages\` + +- If \`INSTALL_PACKAGES_DURING_CHECK_DEPLOY\` is defined as \`true\` (or \`installPackagesDuringCheckDeploy: true\` in \`.sfdx-hardis.yml\`), packages will be installed even if the command is called with \`--check\` mode +- You can automatically update this property by listing all packages installed on an org using command \`sf hardis:org:retrieve:packageconfig\` + +Example: + +\`\`\`yaml +installedPackages: + - Id: 0A35r0000009EtECAU + SubscriberPackageId: 033i0000000LVMYAA4 + SubscriberPackageName: Marketing Cloud + SubscriberPackageNamespace: et4ae5 + SubscriberPackageVersionId: 04t6S000000l11iQAA + SubscriberPackageVersionName: Marketing Cloud + SubscriberPackageVersionNumber: 236.0.0.2 + installOnScratchOrgs: true // true or false depending you want to install this package when creating a new scratch org + installDuringDeployments: true // set as true to install package during a deployment using sf hardis:project:deploy:smart + installationkey: xxxxxxxxxxxxxxxxxxxx // if the package has a password, write it in this property + - Id: 0A35r0000009F9CCAU + SubscriberPackageId: 033b0000000Pf2AAAS + SubscriberPackageName: Declarative Lookup Rollup Summaries Tool + SubscriberPackageNamespace: dlrs + SubscriberPackageVersionId: 04t5p000001BmLvAAK + SubscriberPackageVersionName: Release + SubscriberPackageVersionNumber: 2.15.0.9 + installOnScratchOrgs: true + installDuringDeployments: true +\`\`\` + +### Deployment pre or post commands + +You can define command lines to run before or after a deployment, with parameters: + +- **id**: Unique Id for the command +- **label**: Human readable label for the command +- **skipIfError**: If defined to "true", the post-command won't be run if there is a deployment failure +- **context**: Defines the context where the command will be run. Can be **all** (default), **check-deployment-only** or **process-deployment-only** +- **runOnlyOnceByOrg**: If set to true, the command will be run only one time per org. A record of SfdxHardisTrace__c is stored to make that possible (it needs to be existing in target org) + +If the commands are not the same depending on the target org, you can define them into **config/branches/.sfdx-hardis-BRANCHNAME.yml** instead of root **config/.sfdx-hardis.yml** + +Example: + +\`\`\`yaml +commandsPreDeploy: + - id: knowledgeUnassign + label: Remove KnowledgeUser right to the user who has it + command: sf data update record --sobject User --where "UserPermissionsKnowledgeUser='true'" --values "UserPermissionsKnowledgeUser='false'" --json + - id: knowledgeAssign + label: Assign Knowledge user to the deployment user + command: sf data update record --sobject User --where "Username='deploy.github@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + +commandsPostDeploy: + - id: knowledgeUnassign + label: Remove KnowledgeUser right to the user who has it + command: sf data update record --sobject User --where "UserPermissionsKnowledgeUser='true'" --values "UserPermissionsKnowledgeUser='false'" --json + - id: knowledgeAssign + label: Assign Knowledge user to desired username + command: sf data update record --sobject User --where "Username='admin-yser@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + - id: someActionToRunJustOneTime + label: And to run only if deployment is success + command: sf sfdmu:run ... + skipIfError: true + context: process-deployment-only + runOnlyOnceByOrg: true +\`\`\` + +### Automated fixes post deployments + +#### List view with scope Mine + +If you defined a property **listViewsToSetToMine** in your .sfdx-hardis.yml, related ListViews will be set to Mine ( see command <${CONSTANTS.DOC_URL_ROOT}/hardis/org/fix/listviewmine/> ) + +Example: + +\`\`\`yaml +listViewsToSetToMine: + - "Operation__c:MyCurrentOperations" + - "Operation__c:MyFinalizedOperations" + - "Opportunity:Default_Opportunity_Pipeline" + - "Opportunity:MyCurrentSubscriptions" + - "Opportunity:MySubscriptions" + - "Account:MyActivePartners" +\`\`\` + +Troubleshooting: if you need to fix ListViews with mine from an alpine-linux based docker image, use this workaround in your dockerfile: + +\`\`\`dockerfile +# Do not use puppeteer embedded chromium +RUN apk add --update --no-cache chromium +ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD="true" +ENV CHROMIUM_PATH="/usr/bin/chromium-browser" +ENV PUPPETEER_EXECUTABLE_PATH="$\\{CHROMIUM_PATH}" // remove \\ before { +\`\`\` + +If you need to increase the deployment waiting time (sf project deploy start --wait arg), you can define env variable SFDX_DEPLOY_WAIT_MINUTES + +If you need notifications to be sent using the current Pull Request and not the one just merged ([see use case](https://github.com/hardisgroupcom/sfdx-hardis/issues/637#issuecomment-2230798904)), define env variable SFDX_HARDIS_DEPLOY_BEFORE_MERGE=true +`; + + public static examples = [ + '$ sf hardis:project:deploy:smart', + '$ sf hardis:project:deploy:smart --check', + '$ sf hardis:project:deploy:smart --check --testlevel RunRepositoryTests', + "$ sf hardis:project:deploy:smart --check --testlevel RunRepositoryTests --runtests '^(?!FLI|MyPrefix).*'", + '$ sf hardis:project:deploy:smart --check --testlevel RunRepositoryTestsExceptSeeAllData', + '$ sf hardis:project:deploy:smart', + '$ FORCE_TARGET_BRANCH=preprod NODE_OPTIONS=--inspect-brk sf hardis:project:deploy:smart --check --websocket localhost:2702 --skipauth --target-org nicolas.vuillamy@myclient.com.preprod' + ]; + + + public static flags: any = { + check: Flags.boolean({ + char: 'c', + default: false, + description: messages.getMessage('checkOnly'), + }), + testlevel: Flags.string({ + char: 'l', + options: [ + 'NoTestRun', + 'RunSpecifiedTests', + 'RunRepositoryTests', + 'RunRepositoryTestsExceptSeeAllData', + 'RunLocalTests', + 'RunAllTestsInOrg', + ], + description: messages.getMessage('testLevelExtended'), + }), + runtests: Flags.string({ + char: 'r', + description: `If testlevel=RunSpecifiedTests, please provide a list of classes. +If testlevel=RunRepositoryTests, can contain a regular expression to keep only class names matching it. If not set, will run all test classes found in the repo.`, + }), + packagexml: Flags.string({ + char: 'p', + description: 'Path to package.xml containing what you want to deploy in target org', + }), + delta: Flags.boolean({ + default: false, + description: 'Applies sfdx-git-delta to package.xml before other deployment processes', + }), + debug: Flags.boolean({ + char: 'd', + default: false, + description: messages.getMessage('debugMode'), + }), + websocket: Flags.string({ + description: messages.getMessage('websocket'), + }), + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', + }), + 'target-org': requiredOrgFlagWithDeprecations, + }; + + // Set this to true if your command requires a project workspace; 'requiresProject' is false by default + public static requiresProject = true; + + protected checkOnly = false; + protected configInfo: any = {}; + protected testLevel; + protected testClasses; + protected smartDeployOptions: any; + protected packageXmlFile: string; + protected delta = false; + protected debugMode = false; + + /* jscpd:ignore-end */ + + public async run(): Promise { + const { flags } = await this.parse(SmartDeploy); + this.configInfo = await getConfig('branch'); + this.checkOnly = flags.check || false; + const deltaFromArgs = flags.delta || false; + const packageXml = flags.packagexml || null; + this.debugMode = flags.debug || false; + const currentGitBranch = await getCurrentGitBranch(); + // Get target org + let targetUsername = flags['target-org'].getUsername(); + if (!isCI) { + uxLog(this, c.yellow("Just to be sure, please select the org you want to use for this command :)")) + targetUsername = await promptOrgUsernameDefault(this, targetUsername, { devHub: false, setDefault: false, scratch: false }); + } + + await this.initTestLevelAndTestClasses(flags); + + await this.handlePackages(targetUsername); + + // Compute commitsSummary and store it in globalThis.pullRequestData.commitsSummary + if (this.checkOnly) { + try { + const pullRequestInfo = await GitProvider.getPullRequestInfo(); + const commitsSummary = await computeCommitsSummary(true, pullRequestInfo); + const prDataCommitsSummary = { commitsSummary: commitsSummary.markdown }; + globalThis.pullRequestData = Object.assign(globalThis.pullRequestData || {}, prDataCommitsSummary); + } catch (e3) { + uxLog(this, c.yellow('Unable to compute git summary:\n' + e3)); + } + } + + // Get package.xml & destructiveChanges.xml + this.initPackageXmlAndDestructiveChanges(packageXml, targetUsername, flags); + + // Compute and apply delta if required + await this.handleDeltaDeployment(deltaFromArgs, targetUsername, currentGitBranch); + + // Process deployment (or deployment check) + const { messages, quickDeploy, deployXmlCount } = await smartDeploy( + this.packageXmlFile, + this.checkOnly, + this.testLevel, + this.debugMode, + this, + this.smartDeployOptions + ); + + const deployExecuted = !this.checkOnly && deployXmlCount > 0 ? true : false; + + // Set ListViews to scope Mine if defined in .sfdx-hardis.yml + if (this.configInfo.listViewsToSetToMine && deployExecuted) { + await restoreListViewMine(this.configInfo.listViewsToSetToMine, flags['target-org'].getConnection(), { + debug: this.debugMode, + }); + } + + // Send notification of deployment success + if (deployExecuted) { + await this.handleNotifications(flags, targetUsername, quickDeploy); + } + // Return result + return { orgId: flags['target-org'].getOrgId(), outputString: messages.join('\n') }; + } + + private async handleNotifications(flags, targetUsername: any, quickDeploy: any) { + const pullRequestInfo = await GitProvider.getPullRequestInfo(); + const attachments: MessageAttachment[] = []; + try { + // Build notification attachments & handle ticketing systems comments + const commitsSummary = await this.collectNotifAttachments(attachments, pullRequestInfo); + await TicketProvider.postDeploymentActions( + commitsSummary.tickets, + flags['target-org']?.getConnection()?.instanceUrl || targetUsername || '', + pullRequestInfo + ); + } catch (e4: any) { + uxLog( + this, + c.yellow('Unable to handle commit info on TicketProvider post deployment actions:\n' + e4.message) + + '\n' + + c.gray(e4.stack) + ); + } + + const orgMarkdown = await getOrgMarkdown( + flags['target-org']?.getConnection()?.instanceUrl || targetUsername || '' + ); + const branchMarkdown = await getBranchMarkdown(); + let notifMessage = `Deployment has been successfully processed from branch ${branchMarkdown} to org ${orgMarkdown}`; + notifMessage += quickDeploy + ? ' (🚀 quick deployment)' + : this.delta + ? ' (🌙 delta deployment)' + : ' (🌕 full deployment)'; + + const notifButtons = await getNotificationButtons(); + if (pullRequestInfo) { + if (this.debugMode) { + uxLog(this, c.gray('PR info:\n' + JSON.stringify(pullRequestInfo))); + } + const prUrl = pullRequestInfo.web_url || pullRequestInfo.html_url || pullRequestInfo.url; + const prAuthor = pullRequestInfo?.authorName || pullRequestInfo?.author?.login || pullRequestInfo?.author?.name || null; + notifMessage += `\nRelated: <${prUrl}|${pullRequestInfo.title}>` + (prAuthor ? ` by ${prAuthor}` : ''); + const prButtonText = 'View Pull Request'; + notifButtons.push({ text: prButtonText, url: prUrl }); + } else { + uxLog(this, c.yellow("WARNING: Unable to get Pull Request info, notif won't have a button URL")); + } + globalThis.jsForceConn = flags['target-org']?.getConnection(); // Required for some notifications providers like Email + NotifProvider.postNotifications({ + type: 'DEPLOYMENT', + text: notifMessage, + buttons: notifButtons, + severity: 'success', + attachments: attachments, + logElements: [], + data: { metric: 0 }, // Todo: if delta used, count the number of items deployed + metrics: { + DeployedItems: 0, + }, + }); + } + + private async handleDeltaDeployment(deltaFromArgs: any, targetUsername: string, currentGitBranch: string | null) { + this.delta = false; + if ((deltaFromArgs === true || + process.env.USE_DELTA_DEPLOYMENT === 'true' || + this.configInfo.useDeltaDeployment === true) && + (await this.isDeltaAllowed()) === true) { + this.delta = true; + this.smartDeployOptions.delta = true; + // Define delta deployment depending on context + let fromCommit = 'HEAD'; + let toCommit = 'HEAD^'; + if (this.checkOnly) { + // In deployment check context + const prInfo = await GitProvider.getPullRequestInfo(); + const deltaScope = await getGitDeltaScope( + prInfo?.sourceBranch || currentGitBranch, + prInfo?.targetBranch || process.env.FORCE_TARGET_BRANCH + ); + fromCommit = deltaScope.fromCommit; + toCommit = deltaScope?.toCommit?.hash || ''; + } + // call delta + uxLog(this, c.cyan('[DeltaDeployment] Generating git delta package.xml and destructiveChanges.xml ...')); + const tmpDir = await createTempDir(); + await callSfdxGitDelta(fromCommit, toCommit, tmpDir, { debug: this.debugMode }); + + // Update package.xml + const packageXmlFileDeltaDeploy = path.join(tmpDir, 'package', 'packageDelta.xml'); + await fs.copy(this.packageXmlFile, packageXmlFileDeltaDeploy); + this.packageXmlFile = packageXmlFileDeltaDeploy; + const diffPackageXml = path.join(tmpDir, 'package', 'package.xml'); + await removePackageXmlContent(this.packageXmlFile, diffPackageXml, true, { + debugMode: this.debugMode, + keepEmptyTypes: false, + }); + + const deltaContent = await fs.readFile(this.packageXmlFile, 'utf8'); + uxLog(this, c.cyan('[DeltaDeployment] Final Delta package.xml to deploy:\n' + c.green(deltaContent))); + + const smartDeploymentTestsAllowed = await this.isSmartDeploymentTestsAllowed() + if (smartDeploymentTestsAllowed) { + uxLog(this, c.cyan("[SmartDeploymentTests] Smart Deployment tests activated: analyzing delta package content...")); + const deltaPackageContent = await parsePackageXmlFile(this.packageXmlFile); + const metadataTypesInDelta = Object.keys(deltaPackageContent); + const impactingMetadataTypesInDelta: string[] = [] + for (const metadataTypeInDelta of metadataTypesInDelta) { + if (!CONSTANTS.NOT_IMPACTING_METADATA_TYPES.includes(metadataTypeInDelta)) { + impactingMetadataTypesInDelta.push(metadataTypeInDelta); + } + } + if (impactingMetadataTypesInDelta.length === 0 && !(await isProductionOrg(targetUsername, {}))) { + uxLog(this, c.green("[SmartDeploymentTests] No Impacting metadata in delta package.xml: Skip test classes as the deployed items seem safe :)")); + this.testLevel = "NoTestRun"; + this.testClasses = ""; + } + else { + if (impactingMetadataTypesInDelta.length > 0) { + uxLog(this, c.yellow(`[SmartDeploymentTests] Impacting metadata in delta package.xml (${impactingMetadataTypesInDelta.join(",")}): do not skip test classes.`)); + } else { + uxLog(this, c.yellow("[SmartDeploymentTests] Production org as deployment target: do not skip test classes")); + } + } + + } + + // Update destructiveChanges.xml + if (this.smartDeployOptions.postDestructiveChanges) { + const destructiveXmlFileDeploy = path.join(tmpDir, 'destructiveChanges', 'destructiveChangesDelta.xml'); + await fs.copy(this.smartDeployOptions.postDestructiveChanges, destructiveXmlFileDeploy); + const diffDestructiveChangesXml = path.join(tmpDir, 'destructiveChanges', 'destructiveChanges.xml'); + await removePackageXmlContent(destructiveXmlFileDeploy, diffDestructiveChangesXml, true, { + debugMode: this.debugMode, + keepEmptyTypes: false, + }); + this.smartDeployOptions.postDestructiveChanges = destructiveXmlFileDeploy; + const deltaContentDelete = await fs.readFile(destructiveXmlFileDeploy, 'utf8'); + uxLog(this, c.cyan('[DeltaDeployment] Final Delta destructiveChanges.xml to delete:\n' + c.yellow(deltaContentDelete))); + } + } + } + + private initPackageXmlAndDestructiveChanges(packageXml: any, targetUsername: any, flags) { + this.packageXmlFile = + packageXml || + process.env.PACKAGE_XML_TO_DEPLOY || + this.configInfo.packageXmlToDeploy || + fs.existsSync('./manifest/package.xml') + ? './manifest/package.xml' + : './config/package.xml'; + this.smartDeployOptions = { + targetUsername: targetUsername, + conn: flags['target-org']?.getConnection(), + testClasses: this.testClasses, + }; + // Get destructiveChanges.xml and add it in options if existing + const postDestructiveChanges = process.env.PACKAGE_XML_TO_DELETE || + this.configInfo.packageXmlToDelete || + fs.existsSync('./manifest/destructiveChanges.xml') + ? './manifest/destructiveChanges.xml' + : './config/destructiveChanges.xml'; + if (fs.existsSync(postDestructiveChanges)) { + this.smartDeployOptions.postDestructiveChanges = postDestructiveChanges; + } + + // Get preDestructiveChanges.xml and add it in options if existing + const preDestructiveChanges = process.env.PACKAGE_XML_TO_DELETE_PRE_DEPLOY || + this.configInfo.packageXmlToDeletePreDeploy || + fs.existsSync('./manifest/preDestructiveChanges.xml') + ? './manifest/preDestructiveChanges.xml' + : './config/preDestructiveChanges.xml'; + if (fs.existsSync(preDestructiveChanges)) { + this.smartDeployOptions.preDestructiveChanges = preDestructiveChanges; + } + } + + private async handlePackages(targetUsername: any) { + const packages = this.configInfo.installedPackages || []; + const missingPackages: any[] = []; + const installPackages = this.checkOnly === false || + process.env.INSTALL_PACKAGES_DURING_CHECK_DEPLOY === 'true' || + this.configInfo.installPackagesDuringCheckDeploy === true; + if (packages.length > 0 && installPackages) { + // Install packages only if we are in real deployment mode + await MetadataUtils.installPackagesOnOrg(packages, targetUsername, this, 'deploy'); + } else if (packages.length > 0 && this.checkOnly === true) { + // If check mode, warn if there are missing packages + const alreadyInstalled = await MetadataUtils.listInstalledPackages(targetUsername, this); + for (const package1 of packages) { + if (alreadyInstalled.filter( + (installedPackage: any) => package1.SubscriberPackageVersionId === installedPackage.SubscriberPackageVersionId + ).length === 0 && + package1.installDuringDeployments === true) { + missingPackages.push(package1); + } + } + } + + // Display missing packages message + if (missingPackages.length > 0) { + for (const package1 of missingPackages) { + uxLog( + this, + c.yellow( + `You may need to install package ${c.bold(package1.SubscriberPackageName)} ${c.bold( + package1.SubscriberPackageVersionId + )} in target org to validate the deployment check` + ) + ); + } + uxLog(this, ''); + uxLog( + this, + c.yellow( + c.italic( + `If you want deployment checks to automatically install packages, please define ${c.bold( + 'INSTALL_PACKAGES_DURING_CHECK_DEPLOY=true' + )} in ENV vars, or property ${c.bold('installPackagesDuringCheckDeploy: true')} in .sfdx-hardis.yml` + ) + ) + ); + } + } + + private async initTestLevelAndTestClasses(flags) { + const givenTestlevel = flags.testlevel || this.configInfo.testLevel || 'RunLocalTests'; + this.testClasses = flags.runtests || this.configInfo.runtests || ''; + + // Auto-detect all APEX test classes within project in order to run "dynamic" RunSpecifiedTests deployment + if (['RunRepositoryTests', 'RunRepositoryTestsExceptSeeAllData'].includes(givenTestlevel)) { + const testClassList = await getApexTestClasses( + this.testClasses, + givenTestlevel === 'RunRepositoryTestsExceptSeeAllData' + ); + if (Array.isArray(testClassList) && testClassList.length) { + flags.testlevel = 'RunSpecifiedTests'; + this.testClasses = testClassList.join(" "); + } else { + // Default back to RunLocalTests in case if repository has zero tests + flags.testlevel = 'RunLocalTests'; + this.testClasses = ''; + } + } + + this.testLevel = flags.testlevel || this.configInfo.testLevel || 'RunLocalTests'; + + // Test classes are only valid for RunSpecifiedTests + if (this.testLevel != 'RunSpecifiedTests') { + this.testClasses = ''; + } + } + + private async collectNotifAttachments(attachments: MessageAttachment[], pullRequestInfo: any) { + const commitsSummary = await computeCommitsSummary(false, pullRequestInfo); + // Tickets attachment + if (commitsSummary.tickets.length > 0) { + attachments.push({ + text: `*Tickets*\n${commitsSummary.tickets + .map((ticket) => { + if (ticket.foundOnServer) { + return '• ' + UtilsNotifs.markdownLink(ticket.url, ticket.id) + ' ' + ticket.subject; + } else { + return '• ' + UtilsNotifs.markdownLink(ticket.url, ticket.id); + } + }) + .join('\n')}`, + }); + } + // Manual actions attachment + if (commitsSummary.manualActions.length > 0) { + attachments.push({ + text: `*Manual actions*\n${commitsSummary.manualActions + .map((manualAction) => { + return '• ' + manualAction; + }) + .join('\n')}`, + }); + } + // Commits attachment + if (commitsSummary.logResults.length > 0) { + attachments.push({ + text: `*Commits*\n${commitsSummary.logResults + .map((logResult) => { + return '• ' + logResult.message + ', by ' + logResult.author_name; + }) + .join('\n')}`, + }); + } + return commitsSummary; + } + + async isDeltaAllowed() { + if (process.env?.DISABLE_DELTA_DEPLOYMENT === 'true') { + uxLog( + this, + c.yellow(`[DeltaDeployment] Delta deployment has been explicitly disabled with variable DISABLE_DELTA_DEPLOYMENT=true`) + ); + return false; + } + const latestCommit = await getLatestGitCommit(); + if (latestCommit && this.isNoDelta(latestCommit)) { + uxLog(this, c.yellow(c.bold((`[DeltaDeployment] Latest commit contains string "nodelta" so disable delta for this time :)`)))); + return false; + } + if (this.checkOnly === false && !(process.env?.USE_DELTA_DEPLOYMENT_AFTER_MERGE === 'true')) { + uxLog( + this, + c.yellow( + "[DeltaDeployment] We'll try to deploy using Quick Deployment feature. If not available, it's safer to use full deployment for a merge job." + ) + ); + uxLog( + this, + c.yellow( + '[DeltaDeployment] If you want to use delta deployment anyway, define env variable USE_DELTA_DEPLOYMENT_AFTER_MERGE=true' + ) + ); + return false; + } + if (process.env?.ALWAYS_ENABLE_DELTA_DEPLOYMENT === 'true') { + uxLog( + this, + c.yellow(`[DeltaDeployment] Delta deployment has been explicitly enabled with variable ALWAYS_ENABLE_DELTA_DEPLOYMENT=true`) + ); + uxLog( + this, + c.yellow( + `[DeltaDeployment] It is recommended to use delta deployments for merges between major branches, use this config at your own responsibility` + ) + ); + return true; + } + let currentBranch = await getCurrentGitBranch(); + let parentBranch = process.env.FORCE_TARGET_BRANCH || null; + const prInfo = await GitProvider.getPullRequestInfo(); + if (prInfo) { + currentBranch = prInfo.sourceBranch; + parentBranch = prInfo.targetBranch; + } + const majorOrgs = await listMajorOrgs(); + uxLog(this, c.grey('Major orgs with auth configured:\n' + JSON.stringify(majorOrgs, null, 2))); + const currentBranchIsMajor = majorOrgs.some((majorOrg) => majorOrg.branchName === currentBranch); + const parentBranchIsMajor = majorOrgs.some((majorOrg) => majorOrg.branchName === parentBranch); + if (currentBranchIsMajor && (parentBranchIsMajor === true || parentBranch == null)) { + uxLog( + this, + c.yellow( + `This is not safe to use delta between major branches (${c.bold(currentBranch)} to ${c.bold( + parentBranch + )}): using full deployment mode` + ) + ); + return false; + } + uxLog( + this, + c.cyan( + `[DeltaDeployment] Delta allowed between minor branch (${currentBranch}) and major branch (${parentBranch}): using delta deployment mode` + ) + ); + return true; + } + + isNoDelta(latestCommit) { + return latestCommit?.body?.trim().includes('nodelta') || latestCommit?.message?.trim().includes('nodelta') || + latestCommit?.body?.trim().includes('no delta') || latestCommit?.message?.trim().includes('no delta') + } + + async isSmartDeploymentTestsAllowed() { + if (process.env?.USE_SMART_DEPLOYMENT_TESTS === 'true' || this.configInfo?.useSmartDeploymentTests === true) { + const latestCommit = await getLatestGitCommit(); + if (latestCommit && this.isNoSmartDeploymentTests(latestCommit)) { + uxLog(this, c.yellow(c.bold((`[SmartDeploymentTests] Latest commit contains string "nosmart" so disable smartDeploymentTests for this time :)`)))); + return false; + } + return true; + } + return false; + } + + isNoSmartDeploymentTests(latestCommit) { + return latestCommit?.body?.trim().includes('nosmart') || latestCommit?.message?.trim().includes('nosmart') || + latestCommit?.body?.trim().includes('no smart') || latestCommit?.message?.trim().includes('no smart') + } +} diff --git a/src/commands/hardis/project/deploy/sources/dx.ts b/src/commands/hardis/project/deploy/sources/dx.ts deleted file mode 100644 index 7bc6ae6f0..000000000 --- a/src/commands/hardis/project/deploy/sources/dx.ts +++ /dev/null @@ -1,576 +0,0 @@ -/* jscpd:ignore-start */ -/* -To test locally, you can call the command like that: - -Gitlab: CI=true CI_SFDX_HARDIS_GITLAB_TOKEN=XXX CI_PROJECT_ID=YYY CI_JOB_TOKEN=xxx NODE_OPTIONS=--inspect-brk sfdx hardis:project:deploy:sources:dx --targetusername nicolas.vuillamy@cloudity.com.demointeg - -Azure: CI=true SYSTEM_ACCESSTOKEN=XXX SYSTEM_COLLECTIONURI=https://dev.azure.com/MyAzureCollection/ BUILD_REPOSITORY_ID=XXX CI_JOB_TOKEN=xxx NODE_OPTIONS=--inspect-brk sfdx hardis:project:deploy:sources:dx --targetusername nicolas.vuillamy@cloudity.com.muuuurf - -- Before, you need to make a sfdx alias:set myBranch=myUsername -- You can find CI_PROJECT_ID with https://gitlab.com/api/v4/projects?search=YOUR-REPO-NAME - -*/ - -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; -import { MetadataUtils } from "../../../../../common/metadata-utils"; -import { createTempDir, getCurrentGitBranch, getLatestGitCommit, isCI, uxLog } from "../../../../../common/utils"; -import { getConfig } from "../../../../../config"; -import { forceSourceDeploy, removePackageXmlContent } from "../../../../../common/utils/deployUtils"; -import { promptOrg } from "../../../../../common/utils/orgUtils"; -import { getApexTestClasses } from "../../../../../common/utils/classUtils"; -import { listMajorOrgs, restoreListViewMine } from "../../../../../common/utils/orgConfigUtils"; -import { NotifProvider, UtilsNotifs } from "../../../../../common/notifProvider"; -import { GitProvider } from "../../../../../common/gitProvider"; -import { callSfdxGitDelta, computeCommitsSummary, getGitDeltaScope } from "../../../../../common/utils/gitUtils"; -import { getBranchMarkdown, getNotificationButtons, getOrgMarkdown } from "../../../../../common/utils/notifUtils"; -import { MessageAttachment } from "@slack/web-api"; -import { TicketProvider } from "../../../../../common/ticketProvider"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class DxSources extends SfdxCommand { - public static title = "Deploy sfdx sources to org"; - - public static description = `Deploy SFDX source to org, following deploymentPlan in .sfdx-hardis.yml - -In case of errors, [tips to fix them](https://sfdx-hardis.cloudity.com/deployTips/) will be included within the error messages. - -### Quick Deploy - -In case Pull Request comments are configured on the project, Quick Deploy will try to be used (equivalent to button Quick Deploy) - -If you do not want to use QuickDeploy, define variable \`SFDX_HARDIS_QUICK_DEPLOY=false\` - -- [GitHub Pull Requests comments config](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-integration-github/) -- [Gitlab Merge requests notes config](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-integration-gitlab/) -- [Azure Pull Requests comments config](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-integration-azure/) - -### Delta deployments - -To activate delta deployments, define property \`useDeltaDeployment: true\` in \`config/.sfdx-hardis.yml\`. - -This will activate delta deployments only between minor and major branches (major to major remains full deployment mode) - -If you want to force the delta deployment into major orgs (ex: preprod to prod), this is not recommended but you can use env variable ALWAYS_ENABLE_DELTA_DEPLOYMENT=true - -### Dynamic deployment items / Overwrite management - -If necessary,you can define the following files (that supports wildcards *): - -- \`manifest/package-no-overwrite.xml\`: Every element defined in this file will be deployed only if it is not existing yet in the target org (can be useful with ListView for example, if the client wants to update them directly in production org) -- \`manifest/packageXmlOnChange.xml\`: Every element defined in this file will not be deployed if it already has a similar definition in target org (can be useful for SharingRules for example) - -See [Overwrite management documentation](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-config-overwrite/) - -### Deployment plan - -If you need to deploy in multiple steps, you can define a property \`deploymentPlan\` in \`.sfdx-hardis.yml\`. - -- If a file \`manifest/package.xml\` is found, it will be placed with order 0 in the deployment plan - -- If a file \`manifest/destructiveChanges.xml\` is found, it will be executed as --postdestructivechanges - -- If env var \`SFDX_HARDIS_DEPLOY_IGNORE_SPLIT_PACKAGES\` is defined as \`false\` , split of package.xml will be applied - -Example: - -\`\`\`yaml -deploymentPlan: - packages: - - label: Deploy Flow-Workflow - packageXmlFile: manifest/splits/packageXmlFlowWorkflow.xml - order: 6 - - label: Deploy SharingRules - Case - packageXmlFile: manifest/splits/packageXmlSharingRulesCase.xml - order: 30 - waitAfter: 30 -\`\`\` - -### Packages installation - -You can define a list of package to install during deployments using property \`installedPackages\` - -- If \`INSTALL_PACKAGES_DURING_CHECK_DEPLOY\` is defined as \`true\` (or \`installPackagesDuringCheckDeploy: true\` in \`.sfdx-hardis.yml\`), packages will be installed even if the command is called with \`--check\` mode -- You can automatically update this property by listing all packages installed on an org using command \`sfdx hardis:org:retrieve:packageconfig\` - -Example: - -\`\`\`yaml -installedPackages: - - Id: 0A35r0000009EtECAU - SubscriberPackageId: 033i0000000LVMYAA4 - SubscriberPackageName: Marketing Cloud - SubscriberPackageNamespace: et4ae5 - SubscriberPackageVersionId: 04t6S000000l11iQAA - SubscriberPackageVersionName: Marketing Cloud - SubscriberPackageVersionNumber: 236.0.0.2 - installOnScratchOrgs: true // true or false depending you want to install this package when creating a new scratch org - installDuringDeployments: true // set as true to install package during a deployment using sfdx hardis:project:deploy:sources:dx - installationkey: xxxxxxxxxxxxxxxxxxxx // if the package has a password, write it in this property - - Id: 0A35r0000009F9CCAU - SubscriberPackageId: 033b0000000Pf2AAAS - SubscriberPackageName: Declarative Lookup Rollup Summaries Tool - SubscriberPackageNamespace: dlrs - SubscriberPackageVersionId: 04t5p000001BmLvAAK - SubscriberPackageVersionName: Release - SubscriberPackageVersionNumber: 2.15.0.9 - installOnScratchOrgs: true - installDuringDeployments: true -\`\`\` - -### Deployment pre or post commands - -You can define command lines to run before or after a deployment - -If the commands are not the same depending on the target org, you can define them into **config/branches/.sfdx-hardis-BRANCHNAME.yml** instead of root **config/.sfdx-hardis.yml** - -Example: - -\`\`\`yaml -commandsPreDeploy: - - id: knowledgeUnassign - label: Remove KnowledgeUser right to the user who has it - command: sf data update record --sobject User --where "UserPermissionsKnowledgeUser='true'" --values "UserPermissionsKnowledgeUser='false'" --json - - id: knowledgeAssign - label: Assign Knowledge user to the deployment user - command: sf data update record --sobject User --where "Username='deploy.github@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json -commandsPostDeploy: - - id: knowledgeUnassign - label: Remove KnowledgeUser right to the user who has it - command: sf data update record --sobject User --where "UserPermissionsKnowledgeUser='true'" --values "UserPermissionsKnowledgeUser='false'" --json - - id: knowledgeAssign - label: Assign Knowledge user to desired username - command: sf data update record --sobject User --where "Username='admin-yser@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json -\`\`\` - -### Automated fixes post deployments - -#### List view with scope Mine - -If you defined a property **listViewsToSetToMine** in your .sfdx-hardis.yml, related ListViews will be set to Mine ( see command ) - -Example: - -\`\`\`yaml -listViewsToSetToMine: - - "Operation__c:MyCurrentOperations" - - "Operation__c:MyFinalizedOperations" - - "Opportunity:Default_Opportunity_Pipeline" - - "Opportunity:MyCurrentSubscriptions" - - "Opportunity:MySubscriptions" - - "Account:MyActivePartners" -\`\`\` - -Troubleshooting: if you need to fix ListViews with mine from an alpine-linux based docker image, use this workaround in your dockerfile: - -\`\`\`dockerfile -# Do not use puppeteer embedded chromium -RUN apk add --update --no-cache chromium -ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD="true" -ENV CHROMIUM_PATH="/usr/bin/chromium-browser" -ENV PUPPETEER_EXECUTABLE_PATH="$\\{CHROMIUM_PATH}" // remove \\ before { -\`\`\` - -If you need to increase the deployment waiting time (force:source:deploy --wait arg), you can define env variable SFDX_DEPLOY_WAIT_MINUTES - -If you need notifications to be sent using the current Pull Request and not the one just merged ([see use case](https://github.com/hardisgroupcom/sfdx-hardis/issues/637#issuecomment-2230798904)), define env variable SFDX_HARDIS_DEPLOY_BEFORE_MERGE=true -`; - - public static examples = [ - "$ sfdx hardis:project:deploy:sources:dx", - "$ sfdx hardis:project:deploy:sources:dx --check", - "$ sfdx hardis:project:deploy:sources:dx --check --testlevel RunRepositoryTests", - "$ sfdx hardis:project:deploy:sources:dx --check --testlevel RunRepositoryTests --runtests '^(?!FLI|MyPrefix).*'", - "$ sfdx hardis:project:deploy:sources:dx --check --testlevel RunRepositoryTestsExceptSeeAllData", - ]; - - protected static flagsConfig = { - check: flags.boolean({ - char: "c", - default: false, - description: messages.getMessage("checkOnly"), - }), - testlevel: flags.enum({ - char: "l", - options: ["NoTestRun", "RunSpecifiedTests", "RunRepositoryTests", "RunRepositoryTestsExceptSeeAllData", "RunLocalTests", "RunAllTestsInOrg"], - description: messages.getMessage("testLevelExtended"), - }), - runtests: flags.string({ - char: "r", - description: `If testlevel=RunSpecifiedTests, please provide a list of classes. -If testlevel=RunRepositoryTests, can contain a regular expression to keep only class names matching it. If not set, will run all test classes found in the repo.`, - }), - packagexml: flags.string({ - char: "p", - description: "Path to package.xml containing what you want to deploy in target org", - }), - delta: flags.boolean({ - default: false, - description: "Applies sfdx-git-delta to package.xml before other deployment processes", - }), - debug: flags.boolean({ - char: "d", - default: false, - description: messages.getMessage("debugMode"), - }), - websocket: flags.string({ - description: messages.getMessage("websocket"), - }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", - }), - }; - - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; - - protected checkOnly = false; - protected configInfo: any = {}; - protected debugMode = false; - - /* jscpd:ignore-end */ - - public async run(): Promise { - this.configInfo = await getConfig("branch"); - this.checkOnly = this.flags.check || false; - const deltaFromArgs = this.flags.delta || false; - - const givenTestlevel = this.flags.testlevel || this.configInfo.testLevel || "RunLocalTests"; - let testClasses = this.flags.runtests || this.configInfo.runtests || ""; - - // Auto-detect all APEX test classes within project in order to run "dynamic" RunSpecifiedTests deployment - if (["RunRepositoryTests", "RunRepositoryTestsExceptSeeAllData"].includes(givenTestlevel)) { - const testClassList = await getApexTestClasses(testClasses, givenTestlevel === "RunRepositoryTestsExceptSeeAllData"); - if (Array.isArray(testClassList) && testClassList.length) { - this.flags.testlevel = "RunSpecifiedTests"; - testClasses = testClassList.join(); - } else { - // Default back to RunLocalTests in case if repository has zero tests - this.flags.testlevel = "RunLocalTests"; - testClasses = ""; - } - } - - const testlevel = this.flags.testlevel || this.configInfo.testLevel || "RunLocalTests"; - - // Test classes are only valid for RunSpecifiedTests - if (testlevel != "RunSpecifiedTests") { - testClasses = ""; - } - - const packageXml = this.flags.packagexml || null; - this.debugMode = this.flags.debug || false; - const currentGitBranch = await getCurrentGitBranch(); - - // Get target org - let targetUsername = this.org.getUsername(); - if (!isCI) { - const targetOrg = await promptOrg(this, { devHub: false, setDefault: false, scratch: false }); - targetUsername = targetOrg.username; - } - - // Install packages - const packages = this.configInfo.installedPackages || []; - const missingPackages = []; - const installPackages = - this.checkOnly === false || - process.env.INSTALL_PACKAGES_DURING_CHECK_DEPLOY === "true" || - this.configInfo.installPackagesDuringCheckDeploy === true; - if (packages.length > 0 && installPackages) { - // Install packages only if we are in real deployment mode - await MetadataUtils.installPackagesOnOrg(packages, targetUsername, this, "deploy"); - } else if (packages.length > 0 && this.checkOnly === true) { - // If check mode, warn if there are missing packages - const alreadyInstalled = await MetadataUtils.listInstalledPackages(targetUsername, this); - for (const package1 of packages) { - if ( - alreadyInstalled.filter((installedPackage: any) => package1.SubscriberPackageVersionId === installedPackage.SubscriberPackageVersionId) - .length === 0 && - package1.installDuringDeployments === true - ) { - missingPackages.push(package1); - } - } - } - - // Display missing packages message - if (missingPackages.length > 0) { - for (const package1 of missingPackages) { - uxLog( - this, - c.yellow( - `You may need to install package ${c.bold(package1.SubscriberPackageName)} ${c.bold( - package1.SubscriberPackageVersionId, - )} in target org to validate the deployment check`, - ), - ); - } - uxLog(this, ""); - uxLog( - this, - c.yellow( - c.italic( - `If you want deployment checks to automatically install packages, please define ${c.bold( - "INSTALL_PACKAGES_DURING_CHECK_DEPLOY=true", - )} in ENV vars, or property ${c.bold("installPackagesDuringCheckDeploy: true")} in .sfdx-hardis.yml`, - ), - ), - ); - } - - // Compute commitsSummary and store it in globalThis.pullRequestData.commitsSummary - if (this.checkOnly) { - try { - const pullRequestInfo = await GitProvider.getPullRequestInfo(); - const commitsSummary = await computeCommitsSummary(true, pullRequestInfo); - const prDataCommitsSummary = { commitsSummary: commitsSummary.markdown }; - globalThis.pullRequestData = Object.assign(globalThis.pullRequestData || {}, prDataCommitsSummary); - } catch (e3) { - uxLog(this, c.yellow("Unable to compute git summary:\n" + e3)); - } - } - - // Get package.xml - let packageXmlFile = - packageXml || process.env.PACKAGE_XML_TO_DEPLOY || this.configInfo.packageXmlToDeploy || fs.existsSync("./manifest/package.xml") - ? "./manifest/package.xml" - : "./config/package.xml"; - const forceSourceDeployOptions: any = { - targetUsername: targetUsername, - conn: this.org?.getConnection(), - testClasses: testClasses, - }; - // Get destructiveChanges.xml and add it in options if existing - const postDestructiveChanges = - process.env.PACKAGE_XML_TO_DELETE || this.configInfo.packageXmlToDelete || fs.existsSync("./manifest/destructiveChanges.xml") - ? "./manifest/destructiveChanges.xml" - : "./config/destructiveChanges.xml"; - if (fs.existsSync(postDestructiveChanges)) { - forceSourceDeployOptions.postDestructiveChanges = postDestructiveChanges; - } - - // Get preDestructiveChanges.xml and add it in options if existing - const preDestructiveChanges = - process.env.PACKAGE_XML_TO_DELETE_PRE_DEPLOY || - this.configInfo.packageXmlToDeletePreDeploy || - fs.existsSync("./manifest/preDestructiveChanges.xml") - ? "./manifest/preDestructiveChanges.xml" - : "./config/preDestructiveChanges.xml"; - if (fs.existsSync(preDestructiveChanges)) { - forceSourceDeployOptions.preDestructiveChanges = preDestructiveChanges; - } - - // Compute and apply delta if required - let delta = false; - if ( - (deltaFromArgs === true || process.env.USE_DELTA_DEPLOYMENT === "true" || this.configInfo.useDeltaDeployment === true) && - (await this.isDeltaAllowed()) === true - ) { - delta = true; - forceSourceDeployOptions.delta = true; - // Define delta deployment depending on context - let fromCommit = "HEAD"; - let toCommit = "HEAD^"; - if (this.checkOnly) { - // In deployment check context - const prInfo = await GitProvider.getPullRequestInfo(); - const deltaScope = await getGitDeltaScope(prInfo?.sourceBranch || currentGitBranch, prInfo?.targetBranch || process.env.FORCE_TARGET_BRANCH); - fromCommit = deltaScope.fromCommit; - toCommit = deltaScope.toCommit.hash; - } - // call delta - uxLog(this, c.cyan("Generating git delta package.xml and destructiveChanges.xml ...")); - const tmpDir = await createTempDir(); - await callSfdxGitDelta(fromCommit, toCommit, tmpDir, { debug: this.debugMode }); - - // Update package.xml - const packageXmlFileDeltaDeploy = path.join(tmpDir, "package", "packageDelta.xml"); - await fs.copy(packageXmlFile, packageXmlFileDeltaDeploy); - packageXmlFile = packageXmlFileDeltaDeploy; - const diffPackageXml = path.join(tmpDir, "package", "package.xml"); - await removePackageXmlContent(packageXmlFile, diffPackageXml, true, { debugMode: this.debugMode, keepEmptyTypes: false }); - - const deltaContent = await fs.readFile(packageXmlFile, "utf8"); - uxLog(this, c.cyan("Final Delta package.xml to deploy:\n" + c.green(deltaContent))); - - // Update destructiveChanges.xml - if (forceSourceDeployOptions.postDestructiveChanges) { - const destructiveXmlFileDeploy = path.join(tmpDir, "destructiveChanges", "destructiveChangesDelta.xml"); - await fs.copy(forceSourceDeployOptions.postDestructiveChanges, destructiveXmlFileDeploy); - const diffDestructiveChangesXml = path.join(tmpDir, "destructiveChanges", "destructiveChanges.xml"); - await removePackageXmlContent(destructiveXmlFileDeploy, diffDestructiveChangesXml, true, { - debugMode: this.debugMode, - keepEmptyTypes: false, - }); - forceSourceDeployOptions.postDestructiveChanges = destructiveXmlFileDeploy; - const deltaContentDelete = await fs.readFile(destructiveXmlFileDeploy, "utf8"); - uxLog(this, c.cyan("Final Delta destructiveChanges.xml to delete:\n" + c.yellow(deltaContentDelete))); - } - } - - // Process deployment (or deployment check) - const { messages, quickDeploy, deployXmlCount } = await forceSourceDeploy( - packageXmlFile, - this.checkOnly, - testlevel, - this.debugMode, - this, - forceSourceDeployOptions, - ); - - const deployExecuted = !this.checkOnly && deployXmlCount > 0 ? true : false; - - // Set ListViews to scope Mine if defined in .sfdx-hardis.yml - if (this.configInfo.listViewsToSetToMine && deployExecuted) { - await restoreListViewMine(this.configInfo.listViewsToSetToMine, this.org.getConnection(), { debug: this.debugMode }); - } - - // Send notification of deployment success - if (deployExecuted) { - const pullRequestInfo = await GitProvider.getPullRequestInfo(); - const attachments: MessageAttachment[] = []; - try { - // Build notification attachments & handle ticketing systems comments - const commitsSummary = await this.collectNotifAttachments(attachments, pullRequestInfo); - await TicketProvider.postDeploymentActions(commitsSummary.tickets, this.org?.getConnection()?.instanceUrl || targetUsername, pullRequestInfo); - } catch (e4) { - uxLog(this, c.yellow("Unable to handle commit info on TicketProvider post deployment actions:\n" + e4.message) + "\n" + c.gray(e4.stack)); - } - - const orgMarkdown = await getOrgMarkdown(this.org?.getConnection()?.instanceUrl || targetUsername); - const branchMarkdown = await getBranchMarkdown(); - let notifMessage = `Deployment has been successfully processed from branch ${branchMarkdown} to org ${orgMarkdown}`; - notifMessage += quickDeploy ? " (🚀 quick deployment)" : delta ? " (🌙 delta deployment)" : " (🌕 full deployment)"; - - const notifButtons = await getNotificationButtons(); - if (pullRequestInfo) { - if (this.debugMode) { - uxLog(this, c.gray("PR info:\n" + JSON.stringify(pullRequestInfo))); - } - const prUrl = pullRequestInfo.web_url || pullRequestInfo.html_url || pullRequestInfo.url; - const prAuthor = pullRequestInfo?.authorName || pullRequestInfo?.author?.login || pullRequestInfo?.author?.name || null; - notifMessage += `\nRelated: <${prUrl}|${pullRequestInfo.title}>` + (prAuthor ? ` by ${prAuthor}` : ""); - const prButtonText = "View Pull Request"; - notifButtons.push({ text: prButtonText, url: prUrl }); - } else { - uxLog(this, c.yellow("WARNING: Unable to get Pull Request info, notif won't have a button URL")); - } - globalThis.jsForceConn = this?.org?.getConnection(); // Required for some notifications providers like Email - NotifProvider.postNotifications({ - type: "DEPLOYMENT", - text: notifMessage, - buttons: notifButtons, - severity: "success", - attachments: attachments, - logElements: [], - data: { metric: 0 }, // Todo: if delta used, count the number of items deployed - metrics: { - DeployedItems: 0, // Todo: if delta used, count the number of items deployed - }, - }); - } - return { orgId: this.org.getOrgId(), outputString: messages.join("\n") }; - } - - private async collectNotifAttachments(attachments: MessageAttachment[], pullRequestInfo: any) { - const commitsSummary = await computeCommitsSummary(false, pullRequestInfo); - // Tickets attachment - if (commitsSummary.tickets.length > 0) { - attachments.push({ - text: `*Tickets*\n${commitsSummary.tickets - .map((ticket) => { - if (ticket.foundOnServer) { - return "• " + UtilsNotifs.markdownLink(ticket.url, ticket.id) + " " + ticket.subject; - } else { - return "• " + UtilsNotifs.markdownLink(ticket.url, ticket.id); - } - }) - .join("\n")}`, - }); - } - // Manual actions attachment - if (commitsSummary.manualActions.length > 0) { - attachments.push({ - text: `*Manual actions*\n${commitsSummary.manualActions - .map((manualAction) => { - return "• " + manualAction; - }) - .join("\n")}`, - }); - } - // Commits attachment - if (commitsSummary.logResults.length > 0) { - attachments.push({ - text: `*Commits*\n${commitsSummary.logResults - .map((logResult) => { - return "• " + logResult.message + ", by " + logResult.author_name; - }) - .join("\n")}`, - }); - } - return commitsSummary; - } - - async isDeltaAllowed() { - if (process.env?.DISABLE_DELTA_DEPLOYMENT === "true") { - uxLog(this, c.yellow(`Delta deployment has been explicitly disabled with variable DISABLE_DELTA_DEPLOYMENT=true`)); - return false; - } - const latestCommit = await getLatestGitCommit(); - if (latestCommit && (latestCommit?.body?.includes("nodelta") || latestCommit?.message?.includes("nodelta"))) { - uxLog(this, c.yellow(`Latest commit contains string "nodelta" so disable delta for this time :)`)); - return false; - } - if (this.checkOnly === false && !(process.env?.USE_DELTA_DEPLOYMENT_AFTER_MERGE === "true")) { - uxLog( - this, - c.yellow("We'll try to deploy using Quick Deployment feature. If not available, it's safer to use full deployment for a merge job."), - ); - uxLog(this, c.yellow("If you want to use delta deployment anyway, define env variable USE_DELTA_DEPLOYMENT_AFTER_MERGE=true")); - return false; - } - if (process.env?.ALWAYS_ENABLE_DELTA_DEPLOYMENT === "true") { - uxLog(this, c.yellow(`Delta deployment has been explicitly enabled with variable ALWAYS_ENABLE_DELTA_DEPLOYMENT=true`)); - uxLog( - this, - c.yellow(`It is recommended to use delta deployments for merges between major branches, use this config at your own responsibility`), - ); - return true; - } - let currentBranch = await getCurrentGitBranch(); - let parentBranch = process.env.FORCE_TARGET_BRANCH || null; - const prInfo = await GitProvider.getPullRequestInfo(); - if (prInfo) { - currentBranch = prInfo.sourceBranch; - parentBranch = prInfo.targetBranch; - } - const majorOrgs = await listMajorOrgs(); - uxLog(this, c.grey("Major orgs with auth configured:\n" + JSON.stringify(majorOrgs, null, 2))); - const currentBranchIsMajor = majorOrgs.some((majorOrg) => majorOrg.branchName === currentBranch); - const parentBranchIsMajor = majorOrgs.some((majorOrg) => majorOrg.branchName === parentBranch); - if (currentBranchIsMajor && (parentBranchIsMajor === true || parentBranch == null)) { - uxLog( - this, - c.yellow( - `This is not safe to use delta between major branches (${c.bold(currentBranch)} to ${c.bold(parentBranch)}): using full deployment mode`, - ), - ); - return false; - } - uxLog(this, c.cyan(`Delta allowed between minor branch (${currentBranch}) and major branch (${parentBranch}): using delta deployment mode`)); - return true; - } -} diff --git a/src/commands/hardis/project/deploy/sources/metadata.ts b/src/commands/hardis/project/deploy/sources/metadata.ts index ca616bd29..4877de595 100644 --- a/src/commands/hardis/project/deploy/sources/metadata.ts +++ b/src/commands/hardis/project/deploy/sources/metadata.ts @@ -1,101 +1,103 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; -import { MetadataUtils } from "../../../../../common/metadata-utils"; -import { createTempDir, execCommand, uxLog } from "../../../../../common/utils"; -import { deployDestructiveChanges, deployMetadatas } from "../../../../../common/utils/deployUtils"; -import { getConfig } from "../../../../../config"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class DxSources extends SfdxCommand { - public static title = "Deploy metadata sources to org"; - - public static description = messages.getMessage("deployMetadatas"); - - public static examples = ["$ sfdx hardis:project:deploy:sources:metadata"]; - - protected static flagsConfig = { - check: flags.boolean({ - char: "c", +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; +import { MetadataUtils } from '../../../../../common/metadata-utils/index.js'; +import { createTempDir, execCommand, uxLog } from '../../../../../common/utils/index.js'; +import { deployDestructiveChanges, deployMetadatas } from '../../../../../common/utils/deployUtils.js'; +import { getConfig } from '../../../../../config/index.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class DxSources extends SfCommand { + public static title = 'Deploy metadata sources to org'; + + public static description = messages.getMessage('deployMetadatas'); + + public static examples = ['$ sf hardis:project:deploy:sources:metadata']; + + public static flags: any = { + check: Flags.boolean({ + char: 'c', default: false, - description: messages.getMessage("checkOnly"), + description: messages.getMessage('checkOnly'), }), - deploydir: flags.string({ - char: "x", - default: ".", - description: "Deploy directory", + deploydir: Flags.string({ + char: 'x', + default: '.', + description: 'Deploy directory', }), - packagexml: flags.string({ - char: "p", - description: "Path to package.xml file to deploy", + packagexml: Flags.string({ + char: 'p', + description: 'Path to package.xml file to deploy', }), - filter: flags.boolean({ - char: "f", + filter: Flags.boolean({ + char: 'f', default: false, - description: "Filter metadatas before deploying", + description: 'Filter metadatas before deploying', }), - destructivepackagexml: flags.string({ - char: "k", - description: "Path to destructiveChanges.xml file to deploy", + destructivepackagexml: Flags.string({ + char: 'k', + description: 'Path to destructiveChanges.xml file to deploy', }), - testlevel: flags.enum({ - char: "l", - default: "RunLocalTests", - options: ["NoTestRun", "RunSpecifiedTests", "RunLocalTests", "RunAllTestsInOrg"], - description: messages.getMessage("testLevel"), + testlevel: Flags.string({ + char: 'l', + default: 'RunLocalTests', + options: ['NoTestRun', 'RunSpecifiedTests', 'RunLocalTests', 'RunAllTestsInOrg'], + description: messages.getMessage('testLevel'), }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; // List required plugins, their presence will be tested before running the command - protected static requiresSfdxPlugins = ["sfdx-essentials"]; + protected static requiresSfdxPlugins = ['sfdx-essentials']; protected configInfo: any = {}; - protected deployDir: any = "."; + protected deployDir: any = '.'; /* jscpd:ignore-end */ public async run(): Promise { - const check = this.flags.check || false; - const packageXml = this.flags.packagexml || null; - const filter = this.flags.filter || false; - const destructivePackageXml = this.flags.destructivepackagexml || null; - const testlevel = this.flags.testlevel || "RunLocalTests"; - const debugMode = this.flags.debug || false; - this.deployDir = this.flags.deploydir || "."; - this.configInfo = await getConfig("branch"); + const { flags } = await this.parse(DxSources); + uxLog(this, c.red('This command is deprecated and will be removed in January 2025')); + uxLog(this, c.red('Nobody used Metadata format anymore :)')); + uxLog( + this, + c.red('If you think it should be kept and maintained, please post an issue on sfdx-hardis GitHub repository') + ); + + const check = flags.check || false; + const packageXml = flags.packagexml || null; + const filter = flags.filter || false; + const destructivePackageXml = flags.destructivepackagexml || null; + const testlevel = flags.testlevel || 'RunLocalTests'; + const debugMode = flags.debug || false; + this.deployDir = flags.deploydir || '.'; + this.configInfo = await getConfig('branch'); // Install packages const packages = this.configInfo.installedPackages || []; if (packages.length > 0 && !check) { - await MetadataUtils.installPackagesOnOrg(packages, null, this, "deploy"); + await MetadataUtils.installPackagesOnOrg(packages, null, this, 'deploy'); } const destructiveProcessed = false; @@ -103,18 +105,26 @@ export default class DxSources extends SfdxCommand { // Deploy sources const packageXmlFile = - packageXml || process.env.PACKAGE_XML_TO_DEPLOY || this.configInfo.packageXmlToDeploy || fs.existsSync("./manifest/package.xml") - ? "./manifest/package.xml" - : fs.existsSync("./package.xml") - ? "./package.xml" - : fs.existsSync(path.join(this.deployDir, "package.xml")) - ? path.join(this.deployDir, "package.xml") - : "./config/package.xml"; + packageXml || + process.env.PACKAGE_XML_TO_DEPLOY || + this.configInfo.packageXmlToDeploy || + fs.existsSync('./manifest/package.xml') + ? './manifest/package.xml' + : fs.existsSync('./package.xml') + ? './package.xml' + : fs.existsSync(path.join(this.deployDir, 'package.xml')) + ? path.join(this.deployDir, 'package.xml') + : './config/package.xml'; if (fs.existsSync(packageXmlFile)) { // Filter if necessary if (filter) { const tmpDir = await createTempDir(); - const filterCommand = "sfdx essentials:metadata:filter-from-packagexml" + ` -i ${this.deployDir}` + ` -p ${packageXmlFile}` + ` -o ${tmpDir}`; + // sfdx-essentials still here but deprecated and will be removed + const filterCommand = + 'sfdx essentials:metadata:filter-from-packagexml' + + ` -i ${this.deployDir}` + + ` -p ${packageXmlFile}` + + ` -o ${tmpDir}`; this.deployDir = tmpDir; await execCommand(filterCommand, this, { output: true, @@ -127,46 +137,45 @@ export default class DxSources extends SfdxCommand { deployDir: this.deployDir, testlevel, check, - soap: true, debug: debugMode, tryOnce: true, }); - let message = ""; + let message = ''; if (deployRes.status === 0) { deployProcessed = true; - message = "[sfdx-hardis] Successfully deployed sfdx project sources to Salesforce org"; + message = '[sfdx-hardis] Successfully deployed sfdx project sources to Salesforce org'; uxLog(this, c.green(message)); } else { - message = "[sfdx-hardis] Unable to deploy sfdx project sources to Salesforce org"; + message = '[sfdx-hardis] Unable to deploy sfdx project sources to Salesforce org'; uxLog(this, c.red(deployRes.errorMessage)); } } else { - uxLog(this, "No package.xml found so no deployment has been performed"); + uxLog(this, 'No package.xml found so no deployment has been performed'); } // Deploy destructive changes const packageDeletedXmlFile = destructivePackageXml || - process.env.PACKAGE_XML_TO_DELETE || - this.configInfo.packageXmlToDelete || - fs.existsSync("./manifest/destructiveChanges.xml") - ? "./manifest/destructiveChanges.xml" - : fs.existsSync("./destructiveChanges.xml") - ? "./destructiveChanges.xml" - : fs.existsSync(path.join(this.deployDir, "destructiveChanges.xml")) - ? path.join(this.deployDir, "destructiveChanges.xml") - : "./config/destructiveChanges.xml"; + process.env.PACKAGE_XML_TO_DELETE || + this.configInfo.packageXmlToDelete || + fs.existsSync('./manifest/destructiveChanges.xml') + ? './manifest/destructiveChanges.xml' + : fs.existsSync('./destructiveChanges.xml') + ? './destructiveChanges.xml' + : fs.existsSync(path.join(this.deployDir, 'destructiveChanges.xml')) + ? path.join(this.deployDir, 'destructiveChanges.xml') + : './config/destructiveChanges.xml'; if (fs.existsSync(packageDeletedXmlFile)) { await deployDestructiveChanges(packageDeletedXmlFile, { debug: debugMode, check }, this); } else { - uxLog(this, "No destructivePackage.Xml found so no destructive deployment has been performed"); + uxLog(this, 'No destructivePackage.Xml found so no destructive deployment has been performed'); } return { - orgId: this.org.getOrgId(), + orgId: flags['target-org'].getOrgId(), deployProcessed, destructiveProcessed, - outputString: "", + outputString: '', }; } } diff --git a/src/commands/hardis/project/deploy/start.ts b/src/commands/hardis/project/deploy/start.ts new file mode 100644 index 000000000..a8cdaac5f --- /dev/null +++ b/src/commands/hardis/project/deploy/start.ts @@ -0,0 +1,170 @@ +/* jscpd:ignore-start */ +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { AnyJson } from "@salesforce/ts-types"; +import { wrapSfdxCoreCommand } from "../../../../common/utils/wrapUtils.js"; +import { checkDeploymentOrgCoverage, executePrePostCommands, extractOrgCoverageFromLog } from '../../../../common/utils/deployUtils.js'; +import { GitProvider } from '../../../../common/gitProvider/index.js'; + +export default class ProjectDeployStart extends SfCommand { + public static description = `sfdx-hardis wrapper for **sf project deploy start** that displays tips to solve deployment errors. + +[![Assisted solving of Salesforce deployments errors](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-deployment-errors.jpg)](https://nicolas.vuillamy.fr/assisted-solving-of-salesforce-deployments-errors-47f3666a9ed0) + +[See documentation of Salesforce command](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_project_commands_unified.htm#cli_reference_project_deploy_start_unified) + +### Deployment pre or post commands + +You can define command lines to run before or after a deployment, with parameters: + +- **id**: Unique Id for the command +- **label**: Human readable label for the command +- **skipIfError**: If defined to "true", the post-command won't be run if there is a deployment failure +- **context**: Defines the context where the command will be run. Can be **all** (default), **check-deployment-only** or **process-deployment-only** +- **runOnlyOnceByOrg**: If set to true, the command will be run only one time per org. A record of SfdxHardisTrace__c is stored to make that possible (it needs to be existing in target org) + +If the commands are not the same depending on the target org, you can define them into **config/branches/.sfdx-hardis-BRANCHNAME.yml** instead of root **config/.sfdx-hardis.yml** + +Example: + +\`\`\`yaml +commandsPreDeploy: + - id: knowledgeUnassign + label: Remove KnowledgeUser right to the user who has it + command: sf data update record --sobject User --where "UserPermissionsKnowledgeUser='true'" --values "UserPermissionsKnowledgeUser='false'" --json + - id: knowledgeAssign + label: Assign Knowledge user to the deployment user + command: sf data update record --sobject User --where "Username='deploy.github@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + +commandsPostDeploy: + - id: knowledgeUnassign + label: Remove KnowledgeUser right to the user who has it + command: sf data update record --sobject User --where "UserPermissionsKnowledgeUser='true'" --values "UserPermissionsKnowledgeUser='false'" --json + - id: knowledgeAssign + label: Assign Knowledge user to desired username + command: sf data update record --sobject User --where "Username='admin-yser@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + - id: someActionToRunJustOneTime + label: And to run only if deployment is success + command: sf sfdmu:run ... + skipIfError: true + context: process-deployment-only + runOnlyOnceByOrg: true +\`\`\` +`; + + public static flags: any = { + "api-version": Flags.integer({ + char: "a", + description: "api-version", + }), + async: Flags.boolean({ + description: "async", + exclusive: ["wait"], + }), + "dry-run": Flags.boolean({ + description: "dry-run", + default: false, + }), + "ignore-conflicts": Flags.boolean({ + char: "c", + description: "ignore-conflicts", + default: false, + }), + "ignore-errors": Flags.boolean({ + char: "r", + description: "ignore-errors", + default: false, + }), + "ignore-warnings": Flags.boolean({ + char: "g", + description: "ignore-warnings", + default: false, + }), + manifest: Flags.string({ + char: "x", + description: "manifest", + }), + metadata: Flags.string({ + char: "m", + description: "metadata", + multiple: true, + }), + "metadata-dir": Flags.string({ + description: "metadata-dir", + }), + "single-package": Flags.boolean({ + dependsOn: ["metadata-dir"], + description: "single-package", + }), + "source-dir": Flags.string({ + char: "d", + description: "source-dir", + multiple: true, + }), + "target-org": Flags.requiredOrg(), + tests: Flags.string({ + description: "tests", + }), + "test-level": Flags.string({ + description: "test-level", + }), + wait: Flags.integer({ + char: "w", + default: 33, + min: 1, + description: "wait", + exclusive: ["async"], + }), + "purge-on-delete": Flags.boolean({ + description: "purge-on-delete", + }), + "pre-destructive-changes": Flags.string({ + dependsOn: ["manifest"], + description: "pre-destructive-changes", + }), + "post-destructive-changes": Flags.string({ + dependsOn: ["manifest"], + description: "post-destructive-changes", + }), + "coverage-formatters": Flags.string({ + description: "coverage-formatters", + }), + junit: Flags.boolean({ + description: "junit", + }), + "results-dir": Flags.string({ + description: "results-dir", + }), + debug: Flags.boolean({ + default: false, + description: "debug", + }), + }; + + public static requiresProject = true; + + public async run(): Promise { + const { flags } = await this.parse(ProjectDeployStart); + const conn = flags["target-org"].getConnection(); + const checkOnly = flags["dry-run"] === true; + // Run pre deployment commands if defined + await executePrePostCommands('commandsPreDeploy', { success: true, checkOnly: checkOnly, conn: conn }); + const result = await wrapSfdxCoreCommand("sf project deploy start", this.argv, this, flags.debug); + // Check org coverage if requested + if (flags['coverage-formatters'] && result.stdout) { + const orgCoveragePercent = await extractOrgCoverageFromLog(result.stdout + result.stderr || ''); + if (orgCoveragePercent) { + try { + await checkDeploymentOrgCoverage(Number(orgCoveragePercent), { check: checkOnly }); + } catch (errCoverage) { + await GitProvider.managePostPullRequestComment(); + throw errCoverage; + } + } + } + // Run post deployment commands if defined + await executePrePostCommands('commandsPostDeploy', { success: process.exitCode === 0, checkOnly: checkOnly, conn: conn }); + await GitProvider.managePostPullRequestComment(); + return result; + } +} +/* jscpd:ignore-end */ \ No newline at end of file diff --git a/src/commands/hardis/project/deploy/validate.ts b/src/commands/hardis/project/deploy/validate.ts new file mode 100644 index 000000000..277d8d503 --- /dev/null +++ b/src/commands/hardis/project/deploy/validate.ts @@ -0,0 +1,169 @@ +/* jscpd:ignore-start */ +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { AnyJson } from "@salesforce/ts-types"; +import { wrapSfdxCoreCommand } from "../../../../common/utils/wrapUtils.js"; +import { checkDeploymentOrgCoverage, executePrePostCommands, extractOrgCoverageFromLog } from '../../../../common/utils/deployUtils.js'; +import { GitProvider } from '../../../../common/gitProvider/index.js'; + +export default class ProjectDeployValidate extends SfCommand { + public static description = `sfdx-hardis wrapper for **sf project deploy validate** that displays tips to solve deployment errors. + +[![Assisted solving of Salesforce deployments errors](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-deployment-errors.jpg)](https://nicolas.vuillamy.fr/assisted-solving-of-salesforce-deployments-errors-47f3666a9ed0) + +[See documentation of Salesforce command](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_project_commands_unified.htm#cli_reference_project_deploy_validate_unified) + +### Deployment pre or post commands + +You can define command lines to run before or after a deployment, with parameters: + +- **id**: Unique Id for the command +- **label**: Human readable label for the command +- **skipIfError**: If defined to "true", the post-command won't be run if there is a deployment failure +- **context**: Defines the context where the command will be run. Can be **all** (default), **check-deployment-only** or **process-deployment-only** +- **runOnlyOnceByOrg**: If set to true, the command will be run only one time per org. A record of SfdxHardisTrace__c is stored to make that possible (it needs to be existing in target org) + +If the commands are not the same depending on the target org, you can define them into **config/branches/.sfdx-hardis-BRANCHNAME.yml** instead of root **config/.sfdx-hardis.yml** + +Example: + +\`\`\`yaml +commandsPreDeploy: + - id: knowledgeUnassign + label: Remove KnowledgeUser right to the user who has it + command: sf data update record --sobject User --where "UserPermissionsKnowledgeUser='true'" --values "UserPermissionsKnowledgeUser='false'" --json + - id: knowledgeAssign + label: Assign Knowledge user to the deployment user + command: sf data update record --sobject User --where "Username='deploy.github@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + +commandsPostDeploy: + - id: knowledgeUnassign + label: Remove KnowledgeUser right to the user who has it + command: sf data update record --sobject User --where "UserPermissionsKnowledgeUser='true'" --values "UserPermissionsKnowledgeUser='false'" --json + - id: knowledgeAssign + label: Assign Knowledge user to desired username + command: sf data update record --sobject User --where "Username='admin-yser@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + - id: someActionToRunJustOneTime + label: And to run only if deployment is success + command: sf sfdmu:run ... + skipIfError: true + context: process-deployment-only + runOnlyOnceByOrg: true +\`\`\` +`; + public static flags: any = { + "api-version": Flags.integer({ + char: "a", + description: "api-version", + }), + async: Flags.boolean({ + description: "async", + exclusive: ["wait"], + }), + "dry-run": Flags.boolean({ + description: "dry-run", + default: false, + }), + "ignore-conflicts": Flags.boolean({ + char: "c", + description: "ignore-conflicts", + default: false, + }), + "ignore-errors": Flags.boolean({ + char: "r", + description: "ignore-errors", + default: false, + }), + "ignore-warnings": Flags.boolean({ + char: "g", + description: "ignore-warnings", + default: false, + }), + manifest: Flags.string({ + char: "x", + description: "manifest", + }), + metadata: Flags.string({ + char: "m", + description: "metadata", + multiple: true, + }), + "metadata-dir": Flags.string({ + description: "metadata-dir", + }), + "single-package": Flags.boolean({ + dependsOn: ["metadata-dir"], + description: "single-package", + }), + "source-dir": Flags.string({ + char: "d", + description: "source-dir", + multiple: true, + }), + "target-org": Flags.requiredOrg(), + tests: Flags.string({ + description: "tests", + }), + "test-level": Flags.string({ + description: "test-level", + }), + wait: Flags.integer({ + char: "w", + default: 33, + min: 1, + description: "wait", + exclusive: ["async"], + }), + "purge-on-delete": Flags.boolean({ + description: "purge-on-delete", + }), + "pre-destructive-changes": Flags.string({ + dependsOn: ["manifest"], + description: "pre-destructive-changes", + }), + "post-destructive-changes": Flags.string({ + dependsOn: ["manifest"], + description: "post-destructive-changes", + }), + "coverage-formatters": Flags.string({ + description: "coverage-formatters", + }), + junit: Flags.boolean({ + description: "junit", + }), + "results-dir": Flags.string({ + description: "results-dir", + }), + debug: Flags.boolean({ + default: false, + description: "debug", + }), + }; + + public static requiresProject = true; + + public async run(): Promise { + const { flags } = await this.parse(ProjectDeployValidate); + const conn = flags["target-org"].getConnection(); + // Run pre deployment commands if defined + await executePrePostCommands('commandsPreDeploy', { success: true, checkOnly: true, conn: conn }); + const result = await wrapSfdxCoreCommand("sf project deploy start", this.argv, this, flags.debug); + // Check org coverage if requested + if (flags['coverage-formatters'] && result.stdout) { + const orgCoveragePercent = await extractOrgCoverageFromLog(result.stdout + result.stderr || ''); + const checkOnly = true; + if (orgCoveragePercent) { + try { + await checkDeploymentOrgCoverage(Number(orgCoveragePercent), { check: checkOnly }); + } catch (errCoverage) { + await GitProvider.managePostPullRequestComment(); + throw errCoverage; + } + } + } + // Run post deployment commands if defined + await executePrePostCommands('commandsPostDeploy', { success: process.exitCode === 0, checkOnly: true, conn: conn }); + await GitProvider.managePostPullRequestComment(); + return result; + } +} +/* jscpd:ignore-end */ \ No newline at end of file diff --git a/src/commands/hardis/project/fix/profiletabs.ts b/src/commands/hardis/project/fix/profiletabs.ts index 5f218d0c6..0dc84f4c6 100644 --- a/src/commands/hardis/project/fix/profiletabs.ts +++ b/src/commands/hardis/project/fix/profiletabs.ts @@ -1,69 +1,59 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { glob } from "glob"; -import * as sortArray from "sort-array"; -import { uxLog } from "../../../../common/utils"; -import { soqlQueryTooling } from "../../../../common/utils/apiUtils"; -import { prompts } from "../../../../common/utils/prompts"; -import { parseXmlFile, writeXmlFile } from "../../../../common/utils/xmlUtils"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class FixV53Flexipages extends SfdxCommand { - public static title = "Fix profiles to add tabs that are not retrieved by SF CLI"; - - public static description = `Interactive prompts to add tab visibilities that are not retrieved by force:source:pull`; - - public static examples = ["$ sfdx hardis:project:fix:profiletabs"]; - - protected static flagsConfig = { - path: flags.string({ - char: "p", +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { glob } from 'glob'; +import sortArray from 'sort-array'; +import { uxLog } from '../../../../common/utils/index.js'; +import { soqlQueryTooling } from '../../../../common/utils/apiUtils.js'; +import { prompts } from '../../../../common/utils/prompts.js'; +import { parseXmlFile, writeXmlFile } from '../../../../common/utils/xmlUtils.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class FixV53Flexipages extends SfCommand { + public static title = 'Fix profiles to add tabs that are not retrieved by SF CLI'; + + public static description = `Interactive prompts to add tab visibilities that are not retrieved by project retrieve start`; + + public static examples = ['$ sf hardis:project:fix:profiletabs']; + + public static flags: any = { + path: Flags.string({ + char: 'p', default: process.cwd(), - description: "Root folder", + description: 'Root folder', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), - }; - - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + 'target-org': requiredOrgFlagWithDeprecations, + }; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default + public static requiresProject = true; protected pathToBrowse: string; protected debugMode = false; public async run(): Promise { - this.pathToBrowse = this.flags.path || process.cwd(); - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(FixV53Flexipages); + this.pathToBrowse = flags.path || process.cwd(); + this.debugMode = flags.debug || false; /* jscpd:ignore-end */ // List available tabs in org - const tabsRequest = "SELECT Label,DurableId,Name,SobjectName FROM TabDefinition ORDER BY Label"; - const tabsResult = await soqlQueryTooling(tabsRequest, this.org.getConnection()); + const tabsRequest = 'SELECT Label,DurableId,Name,SobjectName FROM TabDefinition ORDER BY Label'; + const tabsResult = await soqlQueryTooling(tabsRequest, (flags['target-org'] as any).getConnection()); const choices = tabsResult.records.map((tab) => { return { title: `${tab.Label} (${tab.Name} on SObject ${tab.SobjectName})}`, @@ -74,23 +64,23 @@ export default class FixV53Flexipages extends SfdxCommand { // Prompt tabs to add to Profiles const promptTabsToAdd = await prompts([ { - type: "multiselect", - name: "tabs", - message: "Please select the tabs you want to display or hide in Profile(s)", + type: 'multiselect', + name: 'tabs', + message: 'Please select the tabs you want to display or hide in Profile(s)', choices: choices, }, { - type: "select", - name: "visibility", - message: "Please select the flag you want the tabs to be applied on profiles you will select", + type: 'select', + name: 'visibility', + message: 'Please select the flag you want the tabs to be applied on profiles you will select', choices: [ { - title: "Visible (DefaultOn)", - value: "DefaultOn", + title: 'Visible (DefaultOn)', + value: 'DefaultOn', }, { - title: "Hidden", - value: "Hidden", + title: 'Hidden', + value: 'Hidden', }, ], }, @@ -103,12 +93,16 @@ export default class FixV53Flexipages extends SfdxCommand { const globPattern = this.pathToBrowse + `/**/*.profile-meta.xml`; const profileSourceFiles = await glob(globPattern, { cwd: this.pathToBrowse }); const promptProfilesToUpdate = await prompts({ - type: "multiselect", - name: "profiles", - message: "Please select the profiles you want to update to apply tabs [" + tabsToUpdate.join(", ") + "] with visibility " + visibility, + type: 'multiselect', + name: 'profiles', + message: + 'Please select the profiles you want to update to apply tabs [' + + tabsToUpdate.join(', ') + + '] with visibility ' + + visibility, choices: profileSourceFiles.map((profileFile) => { return { - title: profileFile.replace(/\\/g, "/").split("/").pop().replace(".profile-meta.xml", ""), + title: (profileFile.replace(/\\/g, '/').split('/').pop() || '').replace('.profile-meta.xml', ''), value: profileFile, }; }), @@ -117,7 +111,7 @@ export default class FixV53Flexipages extends SfdxCommand { // Apply updates on Profiles for (const profileFile of promptProfilesToUpdate.profiles) { const profile = await parseXmlFile(profileFile); - let tabVisibilities = profile.Profile["tabVisibilities"] || []; + let tabVisibilities = profile.Profile['tabVisibilities'] || []; for (const tabName of tabsToUpdate) { // Update existing tabVisibility if (tabVisibilities.filter((tabVisibility) => tabVisibility.tab[0] === tabName).length > 0) { @@ -144,14 +138,14 @@ export default class FixV53Flexipages extends SfdxCommand { }; }), { - by: ["key"], - order: ["asc"], - }, - ).map((sorted) => sorted.value); - profile.Profile["tabVisibilities"] = sortedTabVisibility; + by: ['key'], + order: ['asc'], + } + ).map((sorted: any) => sorted.value); + profile.Profile['tabVisibilities'] = sortedTabVisibility; // Update Profile XML File await writeXmlFile(profileFile, profile); - uxLog(this, c.grey("Updated " + profileFile)); + uxLog(this, c.grey('Updated ' + profileFile)); } // Summary diff --git a/src/commands/hardis/project/fix/v53flexipages.ts b/src/commands/hardis/project/fix/v53flexipages.ts index 1eb54a5e2..6e7cf0494 100644 --- a/src/commands/hardis/project/fix/v53flexipages.ts +++ b/src/commands/hardis/project/fix/v53flexipages.ts @@ -1,62 +1,53 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import { glob } from "glob"; -import { uxLog } from "../../../../common/utils"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import { glob } from 'glob'; +import { uxLog } from '../../../../common/utils/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class FixV53Flexipages extends SfdxCommand { - public static title = "Fix flexipages for v53"; +export default class FixV53Flexipages extends SfCommand { + public static title = 'Fix flexipages for v53'; public static description = `Fix flexipages for apiVersion v53 (Winter22). Note: Update api version to 53.0 in package.xml and sfdx-project.json`; - public static examples = ["$ sfdx hardis:project:fix:v53flexipages"]; + public static examples = ['$ sf hardis:project:fix:v53flexipages']; - protected static flagsConfig = { - path: flags.string({ - char: "p", + public static flags: any = { + path: Flags.string({ + char: 'p', default: process.cwd(), - description: "Root folder", + description: 'Root folder', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; protected pathToBrowse: string; protected debugMode = false; public async run(): Promise { - this.pathToBrowse = this.flags.path || process.cwd(); - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(FixV53Flexipages); + this.pathToBrowse = flags.path || process.cwd(); + this.debugMode = flags.debug || false; // Delete standard files when necessary uxLog(this, c.cyan(`Adding identifiers to componentInstance in flexipages`)); @@ -65,28 +56,28 @@ Note: Update api version to 53.0 in package.xml and sfdx-project.json`; const globPattern = this.pathToBrowse + `/**/*.flexipage-meta.xml`; let counter = 0; - const flexipages = []; + const flexipages: any[] = []; const flexipageSourceFiles = await glob(globPattern, { cwd: this.pathToBrowse }); uxLog(this, c.grey(`Found ${flexipageSourceFiles.length} flexipages`)); const regexAndReplacements = [ { regex: /(.*<\/componentName>\n.*<\/componentInstance>)/gim, - replace: "", + replace: '', replaceWith: `\n SFDX_HARDIS_REPLACEMENT_ID`, }, { regex: /(.*<\/componentName>\n.*)/gim, - replace: "", + replace: '', replaceWith: `\n SFDX_HARDIS_REPLACEMENT_ID`, }, { regex: /(.*<\/fieldItem>\n.*<\/fieldInstance>)/gim, - replace: "", + replace: '', replaceWith: `\n SFDX_HARDIS_REPLACEMENT_ID`, }, ]; for (const flexiFile of flexipageSourceFiles) { - let flexipageRawXml = await fs.readFile(flexiFile, "utf8"); + let flexipageRawXml = await fs.readFile(flexiFile, 'utf8'); let found = false; for (const replaceParams of regexAndReplacements) { const regex = replaceParams.regex; @@ -100,8 +91,8 @@ Note: Update api version to 53.0 in package.xml and sfdx-project.json`; // Iterate thru the regex matches m.forEach((match, groupIndex) => { console.log(`Found match, group ${groupIndex}: ${match}`); - const newId = "sfdxHardisId" + counter; - const replaceWith = replaceParams.replaceWith.replace("SFDX_HARDIS_REPLACEMENT_ID", newId); + const newId = 'sfdxHardisId' + counter; + const replaceWith = replaceParams.replaceWith.replace('SFDX_HARDIS_REPLACEMENT_ID', newId); const replacementWithIdentifier = match.replace(replaceParams.replace, replaceWith); flexipageRawXml = flexipageRawXml.replace(match, replacementWithIdentifier); if (!flexipages.includes(flexiFile)) { @@ -112,7 +103,7 @@ Note: Update api version to 53.0 in package.xml and sfdx-project.json`; } if (found) { await fs.writeFile(flexiFile, flexipageRawXml); - uxLog(this, c.grey("Updated " + flexiFile)); + uxLog(this, c.grey('Updated ' + flexiFile)); } } } diff --git a/src/commands/hardis/project/generate/gitdelta.ts b/src/commands/hardis/project/generate/gitdelta.ts index 1a58e28a6..703ba570f 100644 --- a/src/commands/hardis/project/generate/gitdelta.ts +++ b/src/commands/hardis/project/generate/gitdelta.ts @@ -1,68 +1,66 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as path from "path"; -import { createTempDir, ensureGitRepository, git, gitCheckOutRemote, selectGitBranch, uxLog } from "../../../../common/utils"; -import { callSfdxGitDelta } from "../../../../common/utils/gitUtils"; -import { prompts } from "../../../../common/utils/prompts"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class GenerateGitDelta extends SfdxCommand { - public static title = "Generate Git Delta"; - - public static description = "Generate package.xml git delta between 2 commits"; - - public static examples = ["$ sfdx hardis:project:generate:gitdelta"]; - - protected static flagsConfig = { - branch: flags.string({ - description: "Git branch to use to generate delta", +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import * as path from 'path'; +import { + createTempDir, + ensureGitRepository, + git, + gitCheckOutRemote, + selectGitBranch, + uxLog, +} from '../../../../common/utils/index.js'; +import { callSfdxGitDelta } from '../../../../common/utils/gitUtils.js'; +import { prompts } from '../../../../common/utils/prompts.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class GenerateGitDelta extends SfCommand { + public static title = 'Generate Git Delta'; + + public static description = 'Generate package.xml git delta between 2 commits'; + + public static examples = ['$ sf hardis:project:generate:gitdelta']; + + public static flags: any = { + branch: Flags.string({ + description: 'Git branch to use to generate delta', }), - fromcommit: flags.string({ - description: "Hash of commit to start from", + fromcommit: Flags.string({ + description: 'Hash of commit to start from', }), - tocommit: flags.string({ - description: "Hash of commit to stop at", + tocommit: Flags.string({ + description: 'Hash of commit to stop at', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; protected debugMode = false; /* jscpd:ignore-end */ public async run(): Promise { - let gitBranch = this.flags.branch || null; - let fromCommit = this.flags.fromcommit || null; - let toCommit = this.flags.fromcommit || null; - this.debugMode = this.flags.debugMode || false; + const { flags } = await this.parse(GenerateGitDelta); + let gitBranch = flags.branch || null; + let fromCommit = flags.fromcommit || null; + let toCommit = flags.fromcommit || null; + this.debugMode = flags.debug || false; // Check git repo await ensureGitRepository(); @@ -74,8 +72,11 @@ export default class GenerateGitDelta extends SfdxCommand { } // List branch commits - const branchCommits = await git().log(["--first-parent"]); - const branchCommitsChoices = branchCommits.all.map((commit) => { + const branchCommits = await git().log(['--first-parent']); + let pos = 0; + const branchCommitsChoices = branchCommits.all.map((commit: any) => { + commit.pos = pos; + pos++; return { title: commit.message, description: `${commit.author_name} on ${new Date(commit.date).toLocaleString()}`, @@ -84,48 +85,61 @@ export default class GenerateGitDelta extends SfdxCommand { }); // Prompt fromCommit + let selectedFirstCommitLabel = ""; + let selectedFirstCommitPos = 0; if (fromCommit === null) { const headItem = { - title: "HEAD", + title: 'HEAD', description: `Current git HEAD`, - value: { hash: "HEAD" }, + value: { hash: 'HEAD' }, }; const commitFromResp = await prompts({ - type: "select", - name: "value", - message: "Please select the commit that you want to start from", + type: 'select', + name: 'value', + message: 'Please select the commit that you want to start from', choices: [headItem, ...branchCommitsChoices], }); fromCommit = commitFromResp.value.hash; + selectedFirstCommitLabel = commitFromResp.value.message; + selectedFirstCommitPos = commitFromResp.value.pos; } // Prompt toCommit if (toCommit === null) { const currentItem = { - title: "current", + title: 'current', description: `Local files not committed yet`, - value: { hash: "*" }, + value: { hash: '*' }, + }; + const singleCommitChoice = { + title: 'Single commit', + description: `Only for ${selectedFirstCommitLabel}`, + value: branchCommitsChoices[selectedFirstCommitPos + 1].value }; const commitToResp = await prompts({ - type: "select", - name: "value", - message: "Please select the commit hash that you want to go to", - choices: [currentItem, ...branchCommitsChoices], + type: 'select', + name: 'value', + message: 'Please select the commit hash that you want to go to', + choices: [currentItem, singleCommitChoice, ...branchCommitsChoices], }); toCommit = commitToResp.value.hash; } - // Generate package.xml & destructiveChanges.xml using sfdx git delta + // Generate package.xml & destructiveChanges.xml using sfdx-git-delta const tmpDir = await createTempDir(); - await callSfdxGitDelta(fromCommit, toCommit, tmpDir, { debug: this.debugMode }); + await callSfdxGitDelta(fromCommit || '', toCommit || '', tmpDir, { debug: this.debugMode }); - const diffPackageXml = path.join(tmpDir, "package", "package.xml"); - const diffDestructiveChangesXml = path.join(tmpDir, "destructiveChanges", "destructiveChanges.xml"); + const diffPackageXml = path.join(tmpDir, 'package', 'package.xml'); + const diffDestructiveChangesXml = path.join(tmpDir, 'destructiveChanges', 'destructiveChanges.xml'); uxLog(this, c.cyan(`Generated diff package.xml at ${c.green(diffPackageXml)}`)); uxLog(this, c.cyan(`Generated diff destructiveChanges.xml at ${c.green(diffDestructiveChangesXml)}`)); // Return an object to be displayed with --json - return { outputString: "Generated package.xml", diffPackageXml: diffPackageXml, diffDestructiveChangesXml: diffDestructiveChangesXml }; + return { + outputString: 'Generated package.xml', + diffPackageXml: diffPackageXml, + diffDestructiveChangesXml: diffDestructiveChangesXml, + }; } } diff --git a/src/commands/hardis/project/lint.ts b/src/commands/hardis/project/lint.ts index 49a786b67..403b101f5 100644 --- a/src/commands/hardis/project/lint.ts +++ b/src/commands/hardis/project/lint.ts @@ -1,54 +1,43 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages, SfdxError } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import { isCI, uxLog } from "../../../common/utils"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import { MegaLinterRunner } from "mega-linter-runner/lib"; +import { SfCommand, Flags, optionalOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages, SfError } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { isCI, uxLog } from '../../../common/utils/index.js'; +import c from 'chalk'; +import fs from 'fs-extra'; +import { MegaLinterRunner } from 'mega-linter-runner/lib/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class Lint extends SfCommand { + public static title = 'Lint'; -export default class ProjectCreate extends SfdxCommand { - public static title = "Lint"; + public static description = 'Apply syntactic analysis (linters) on the repository sources, using Mega-Linter'; - public static description = "Apply syntactic analysis (linters) on the repository sources, using Mega-Linter"; + public static examples = ['$ sf hardis:project:lint', '$ sf hardis:project:lint --fix']; - public static examples = ["$ sfdx hardis:project:lint", "$ sfdx hardis:project:lint --fix"]; - - protected static flagsConfig = { - fix: flags.boolean({ - char: "f", + public static flags: any = { + fix: Flags.boolean({ + char: 'f', default: false, - description: "Apply linters fixes", + description: 'Apply linters fixes', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': optionalOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - protected static supportsUsername = true; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; protected fix = false; protected debugMode = false; @@ -56,29 +45,33 @@ export default class ProjectCreate extends SfdxCommand { /* jscpd:ignore-end */ public async run(): Promise { - this.fix = this.flags.fix || false; - this.debugMode = this.flags.debugMode || false; + const { flags } = await this.parse(Lint); + this.fix = flags.fix || false; + this.debugMode = flags.debug || false; // Check if Mega-Linter is configured - if (!fs.existsSync(".mega-linter.yml")) { + if (!fs.existsSync('.mega-linter.yml')) { if (isCI) { - throw new SfdxError( + throw new SfError( c.red( - "[sfdx-hardis] You must run sfdx hardis:project:lint locally to install Mega-Linter configuration before being able to run it from CI", - ), + '[sfdx-hardis] You must run sf hardis:project:lint locally to install Mega-Linter configuration before being able to run it from CI' + ) ); } else { // Configure Mega-Linter (yeoman generator) - uxLog(this, c.cyan("Mega-Linter needs to be configured. Please select Salesforce flavor in the following wizard")); + uxLog( + this, + c.cyan('Mega-Linter needs to be configured. Please select Salesforce flavor in the following wizard') + ); const megaLinter = new MegaLinterRunner(); const installRes = megaLinter.run({ install: true }); - console.assert(installRes.status === 0, "Mega-Linter configuration incomplete"); + console.assert(installRes.status === 0, 'Mega-Linter configuration incomplete'); } } // Run MegaLinter const megaLinter = new MegaLinterRunner(); - const megaLinterOptions = { flavor: "salesforce", fix: this.fix }; + const megaLinterOptions = { flavor: 'salesforce', fix: this.fix }; const res = await megaLinter.run(megaLinterOptions); process.exitCode = res.status; @@ -89,6 +82,6 @@ export default class ProjectCreate extends SfdxCommand { } // Return an object to be displayed with --json - return { outputString: "Linted project sources", linterStatusCode: res.status }; + return { outputString: 'Linted project sources', linterStatusCode: res.status }; } } diff --git a/src/commands/hardis/project/metadata/findduplicates.ts b/src/commands/hardis/project/metadata/findduplicates.ts index 7a676e2ae..b787074ac 100644 --- a/src/commands/hardis/project/metadata/findduplicates.ts +++ b/src/commands/hardis/project/metadata/findduplicates.ts @@ -1,36 +1,36 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Logger, LoggerLevel, Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import { uxLog } from "../../../../common/utils"; -import { parseXmlFile } from "../../../../common/utils/xmlUtils"; -import { getConfig } from "../../../../config"; -import { glob } from "glob"; -import { basename } from "path"; -import * as c from "chalk"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Logger, LoggerLevel, Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { uxLog } from '../../../../common/utils/index.js'; +import { parseXmlFile } from '../../../../common/utils/xmlUtils.js'; +import { getConfig } from '../../../../config/index.js'; +import { glob } from 'glob'; +import { basename } from 'path'; +import c from 'chalk'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -function getCommonPermissionPatterns(rootTagName: "Profile" | "PermissionSet") { - return [`${rootTagName}.fieldPermissions.field`, `${rootTagName}.objectPermissions.object`, `${rootTagName}.classAccesses.apexClass`]; +function getCommonPermissionPatterns(rootTagName: 'Profile' | 'PermissionSet') { + return [ + `${rootTagName}.fieldPermissions.field`, + `${rootTagName}.objectPermissions.object`, + `${rootTagName}.classAccesses.apexClass`, + ]; } -export default class Find extends SfdxCommand { +export default class Find extends SfCommand { protected static metadataDuplicateFindKeys = { - layout: ["Layout.layoutSections.layoutColumns.layoutItems.field", "Layout.quickActionListItems.quickActionName"], - profile: getCommonPermissionPatterns("Profile"), - labels: ["CustomLabels.labels.fullName"], - permissionset: getCommonPermissionPatterns("PermissionSet"), + layout: ['Layout.layoutSections.layoutColumns.layoutItems.field', 'Layout.quickActionListItems.quickActionName'], + profile: getCommonPermissionPatterns('Profile'), + labels: ['CustomLabels.labels.fullName'], + permissionset: getCommonPermissionPatterns('PermissionSet'), }; - public static title = "XML duplicate values finder"; + public static title = 'XML duplicate values finder'; public static description = `find duplicate values in XML file(s). Find duplicate values in XML file(s). Keys to be checked can be configured in \`config/sfdx-hardis.yml\` using property metadataDuplicateFindKeys. @@ -59,13 +59,13 @@ ${Find.metadataDuplicateFindKeys} `, ` -$ sfdx hardis:project:metadata:findduplicates --file layout.layout-meta.xml +$ sf hardis:project:metadata:findduplicates --file layout.layout-meta.xml [sfdx-hardis] Duplicate values in layout.layout-meta.xml - Key : Layout.layoutSections.layoutColumns.layoutItems.field - Values : Name `, ` -$ sfdx hardis:project.metadata:findduplicates -f "force-app/main/default/**/*.xml" +$ sf hardis:project.metadata:findduplicates -f "force-app/main/default/**/*.xml" [sfdx-hardis] hardis:project:metadata:findduplicates execution time 0:00:00.397 [sfdx-hardis] Duplicate values in layout1.layout-meta.xml - Key : Layout.layoutSections.layoutColumns.layoutItems.field @@ -80,26 +80,28 @@ $ sfdx hardis:project.metadata:findduplicates -f "force-app/main/default/**/*.xm protected configInfo: any; protected logLevel: LoggerLevel; - protected static flagsConfig = { - files: flags.array({ - char: "f", - description: "XML metadata files path", + public static flags: any = { + files: Flags.string({ + char: 'f', + description: 'XML metadata files path', + multiple: true, }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; public async run(): Promise { + const { flags } = await this.parse(Find); uxLog(this, c.cyan(`Start finding duplicate values in XML metadata files.`)); await this.initConfig(); - const filesWithDuplicates = await this.findDuplicates(); + const filesWithDuplicates = await this.findDuplicates(flags); uxLog(this, c.cyan(`Done finding duplicate values in XML metadata files.`)); if (filesWithDuplicates.length > 0) { process.exitCode = 1; @@ -108,24 +110,24 @@ $ sfdx hardis:project.metadata:findduplicates -f "force-app/main/default/**/*.xm } async initConfig() { - this.configInfo = await getConfig("user"); + this.configInfo = await getConfig('user'); if (this.configInfo.metadataDuplicateFindKeys) { Find.metadataDuplicateFindKeys = this.configInfo.metadataDuplicateFindKeys; } - // Gets the root sfdx logger level + // Gets the root SF CLI logger level this.logLevel = (await Logger.root()).getLevel(); } - async findDuplicates() { + async findDuplicates(flags) { // Collect input parameters - const inputFiles = []; + const inputFiles: any[] = []; - if (this.flags.files) { - const files = await glob("./" + this.flags.files, { cwd: process.cwd() }); + if (flags.files) { + const files = await glob('./' + flags.files, { cwd: process.cwd() }); inputFiles.push(...files); } - const foundFilesWithDuplicates = []; + const foundFilesWithDuplicates: any[] = []; for (const inputFile of inputFiles) { // Extract given metadata type based on filename using type-meta.xml // For example PersonAccount.layout-meta.xml returns layout and Admin.profile-meta.xml returns profile @@ -145,7 +147,7 @@ $ sfdx hardis:project.metadata:findduplicates -f "force-app/main/default/**/*.xm const file = await parseXmlFile(inputFile); uniqueKeys.forEach((key) => { // Traverse the file down to the key based on the fragments separated by . (dots), abort if not found - const allProps = key.split("."); + const allProps = key.split('.'); const valuesFound = this.traverseDown(file, allProps[0], allProps, []); // https://stackoverflow.com/a/840808 @@ -160,8 +162,8 @@ $ sfdx hardis:project.metadata:findduplicates -f "force-app/main/default/**/*.xm this, c.red(`Duplicate values in ${basename(inputFile)} - Key : ${key} - - Values : ${duplicates.join(", ")} -`), + - Values : ${duplicates.join(', ')} +`) ); } }); @@ -173,7 +175,12 @@ $ sfdx hardis:project.metadata:findduplicates -f "force-app/main/default/**/*.xm * Traverse down a XML tree, allProps containing all the properties to be traversed, currentProp being updated as we * descend. */ - traverseDown(parent: Record | Array, currentProp: string, allProps: Array, results: Array) { + traverseDown( + parent: Record | Array, + currentProp: string, + allProps: Array, + results: Array + ) { const nextProp = allProps[allProps.indexOf(currentProp) + 1]; // If we're at the end of property path (A.B.C -> parent = A.B, currentProp = C, nextProp = undefined) we add the diff --git a/src/commands/hardis/scratch/create.ts b/src/commands/hardis/scratch/create.ts index 685aa33ac..4ed22888e 100644 --- a/src/commands/hardis/scratch/create.ts +++ b/src/commands/hardis/scratch/create.ts @@ -1,15 +1,23 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { AuthInfo, Messages, SfdxError } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { assert } from "console"; -import * as fs from "fs-extra"; -import * as moment from "moment"; -import * as os from "os"; -import * as path from "path"; -import { clearCache } from "../../../common/cache"; -import { elapseEnd, elapseStart, execCommand, execSfdxJson, getCurrentGitBranch, isCI, uxLog } from "../../../common/utils"; +import { SfCommand, Flags, requiredHubFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { AuthInfo, Messages, SfError } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { assert } from 'console'; +import fs from 'fs-extra'; +import moment from 'moment'; +import * as os from 'os'; +import * as path from 'path'; +import { clearCache } from '../../../common/cache/index.js'; +import { + elapseEnd, + elapseStart, + execCommand, + execSfdxJson, + getCurrentGitBranch, + isCI, + uxLog, +} from '../../../common/utils/index.js'; import { initApexScripts, initOrgData, @@ -17,21 +25,17 @@ import { initPermissionSetAssignments, installPackages, promptUserEmail, -} from "../../../common/utils/orgUtils"; -import { addScratchOrgToPool, fetchScratchOrg } from "../../../common/utils/poolUtils"; -import { prompts } from "../../../common/utils/prompts"; -import { WebSocketClient } from "../../../common/websocketClient"; -import { getConfig, setConfig } from "../../../config"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +} from '../../../common/utils/orgUtils.js'; +import { addScratchOrgToPool, fetchScratchOrg } from '../../../common/utils/poolUtils.js'; +import { prompts } from '../../../common/utils/prompts.js'; +import { WebSocketClient } from '../../../common/websocketClient.js'; +import { getConfig, setConfig } from '../../../config/index.js'; -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -export default class ScratchCreate extends SfdxCommand { - public static title = "Create and initialize scratch org"; +export default class ScratchCreate extends SfCommand { + public static title = 'Create and initialize scratch org'; public static description = `Create and initialize a scratch org or a source-tracked sandbox (config can be defined using \`config/.sfdx-hardis.yml\`): @@ -46,45 +50,40 @@ export default class ScratchCreate extends SfdxCommand { - Use property \`dataPackages\` `; - public static examples = ["$ sfdx hardis:scratch:create"]; + public static examples = ['$ sf hardis:scratch:create']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - forcenew: flags.boolean({ - char: "n", + public static flags: any = { + forcenew: Flags.boolean({ + char: 'n', default: false, - description: messages.getMessage("forceNewScratch"), + description: messages.getMessage('forceNewScratch'), }), - pool: flags.boolean({ - char: "d", + pool: Flags.boolean({ + char: 'd', default: false, - description: "Creates the scratch org for a scratch org pool", + description: 'Creates the scratch org for a scratch org pool', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-dev-hub': requiredHubFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; // List required plugins, their presence will be tested before running the command - protected static requiresSfdxPlugins = ["sfdmu", "texei-sfdx-plugin"]; + protected static requiresSfdxPlugins = ['sfdmu', 'texei-sfdx-plugin']; protected forceNew = false; @@ -103,43 +102,56 @@ export default class ScratchCreate extends SfdxCommand { protected scratchOrgInfo: any; protected scratchOrgUsername: string; protected scratchOrgPassword: string; - protected scratchOrgSfdxAuthUrl: string; + protected scratchOrgSfdxAuthUrl: string | null; protected authFileJson: any; protected projectName: string; protected scratchOrgFromPool: any; public async run(): Promise { - this.pool = this.flags.pool || false; - this.debugMode = this.flags.debug || false; - this.forceNew = this.flags.forcenew || false; + const { flags } = await this.parse(ScratchCreate); + this.pool = flags.pool || false; + this.debugMode = flags.debug || false; + this.forceNew = flags.forcenew || false; elapseStart(`Create and initialize scratch org`); await this.initConfig(); - await this.createScratchOrg(); + await this.createScratchOrg(flags); try { await this.updateScratchOrgUser(); await installPackages(this.configInfo.installedPackages || [], this.scratchOrgAlias); if (this.pool === false) { - await initOrgMetadatas(this.configInfo, this.scratchOrgUsername, this.scratchOrgAlias, this.projectScratchDef, this.debugMode, { - scratch: true, - }); + await initOrgMetadatas( + this.configInfo, + this.scratchOrgUsername, + this.scratchOrgAlias, + this.projectScratchDef, + this.debugMode, + { + scratch: true, + } + ); await initPermissionSetAssignments(this.configInfo.initPermissionSets || [], this.scratchOrgUsername); await initApexScripts(this.configInfo.scratchOrgInitApexScripts || [], this.scratchOrgUsername); - await initOrgData(path.join(".", "scripts", "data", "ScratchInit"), this.scratchOrgUsername); + await initOrgData(path.join('.', 'scripts', 'data', 'ScratchInit'), this.scratchOrgUsername); } } catch (e) { elapseEnd(`Create and initialize scratch org`); - uxLog(this, c.grey("Error: " + e.message + "\n" + e.stack)); + uxLog(this, c.grey('Error: ' + (e as Error).message + '\n' + (e as Error).stack)); if (isCI && this.scratchOrgFromPool) { this.scratchOrgFromPool.failures = this.scratchOrgFromPool.failures || []; this.scratchOrgFromPool.failures.push(JSON.stringify(e, null, 2)); - uxLog(this, "[pool] " + c.yellow("Put back scratch org in the scratch orgs pool. ") + c.grey({ result: this.scratchOrgFromPool })); - await addScratchOrgToPool(this.scratchOrgFromPool, { position: "first" }); + uxLog( + this, + '[pool] ' + + c.yellow('Put back scratch org in the scratch orgs pool. ') + + c.grey({ result: this.scratchOrgFromPool }) + ); + await addScratchOrgToPool(this.scratchOrgFromPool, { position: 'first' }); } else if (isCI && this.scratchOrgUsername) { - await execCommand(`sfdx force:org:delete --noprompt --targetusername ${this.scratchOrgUsername}`, this, { + await execCommand(`sf org delete scratch --no-prompt --target-org ${this.scratchOrgUsername}`, this, { fail: false, output: true, }); - uxLog(this, c.red("Deleted scratch org as we are in CI and its creation has failed")); + uxLog(this, c.red('Deleted scratch org as we are in CI and its creation has failed')); } throw e; } @@ -148,7 +160,11 @@ export default class ScratchCreate extends SfdxCommand { if (this.scratchOrgPassword) { uxLog( this, - c.cyan(`You can connect to your scratch using username ${c.green(this.scratchOrgUsername)} and password ${c.green(this.scratchOrgPassword)}`), + c.cyan( + `You can connect to your scratch using username ${c.green(this.scratchOrgUsername)} and password ${c.green( + this.scratchOrgPassword + )}` + ) ); } elapseEnd(`Create and initialize scratch org`); @@ -161,32 +177,41 @@ export default class ScratchCreate extends SfdxCommand { scratchOrgPassword: this.scratchOrgPassword, scratchOrgSfdxAuthUrl: this.scratchOrgSfdxAuthUrl, authFileJson: this.authFileJson, - outputString: "Created and initialized scratch org", + outputString: 'Created and initialized scratch org', }; } // Initialize configuration from .sfdx-hardis.yml + .gitbranch.sfdx-hardis.yml + .username.sfdx-hardis.yml public async initConfig() { - this.configInfo = await getConfig("user"); - this.gitBranch = await getCurrentGitBranch({ formatted: true }); - const newScratchName = os.userInfo().username + "-" + this.gitBranch.split("/").pop().slice(0, 15) + "_" + moment().format("YYYYMMDD_hhmm"); + this.configInfo = await getConfig('user'); + this.gitBranch = (await getCurrentGitBranch({ formatted: true })) || ''; + const newScratchName = + os.userInfo().username + + '-' + + (this.gitBranch.split('/').pop() || '').slice(0, 15) + + '_' + + moment().format('YYYYMMDD_hhmm'); this.scratchOrgAlias = - process.env.SCRATCH_ORG_ALIAS || (!this.forceNew && this.pool === false ? this.configInfo.scratchOrgAlias : null) || newScratchName; - if (isCI && !this.scratchOrgAlias.startsWith("CI-")) { - this.scratchOrgAlias = "CI-" + this.scratchOrgAlias; + process.env.SCRATCH_ORG_ALIAS || + (!this.forceNew && this.pool === false ? this.configInfo.scratchOrgAlias : null) || + newScratchName; + if (isCI && !this.scratchOrgAlias.startsWith('CI-')) { + this.scratchOrgAlias = 'CI-' + this.scratchOrgAlias; } if (this.pool === true) { - this.scratchOrgAlias = "PO-" + Math.random().toString(36).substr(2, 2) + this.scratchOrgAlias; + this.scratchOrgAlias = 'PO-' + Math.random().toString(36).substr(2, 2) + this.scratchOrgAlias; } // Verify that the user wants to resume scratch org creation if (!isCI && this.scratchOrgAlias !== newScratchName && this.pool === false) { const checkRes = await prompts({ - type: "confirm", - name: "value", + type: 'confirm', + name: 'value', message: c.cyanBright( - `You are about to reuse scratch org ${c.green(this.scratchOrgAlias)}. Are you sure that's what you want to do ?\n${c.grey( - "(if not, run again hardis:work:new or use hardis:scratch:create --forcenew)", - )}`, + `You are about to reuse scratch org ${c.green( + this.scratchOrgAlias + )}. Are you sure that's what you want to do ?\n${c.grey( + '(if not, run again hardis:work:new or use hardis:scratch:create --forcenew)' + )}` ), default: false, }); @@ -210,29 +235,29 @@ export default class ScratchCreate extends SfdxCommand { // If not found, prompt user email and store it in user config file if (this.userEmail == null) { if (this.pool === true) { - throw new SfdxError(c.red("You need to define userEmail property in .sfdx-hardis.yml")); + throw new SfError(c.red('You need to define userEmail property in .sfdx-hardis.yml')); } this.userEmail = await promptUserEmail(); } } // Create a new scratch org or reuse existing one - public async createScratchOrg() { + public async createScratchOrg(flags) { // Build project-scratch-def-branch-user.json - uxLog(this, c.cyan("Building custom project-scratch-def.json...")); - this.projectScratchDef = JSON.parse(fs.readFileSync("./config/project-scratch-def.json", "utf-8")); + uxLog(this, c.cyan('Building custom project-scratch-def.json...')); + this.projectScratchDef = JSON.parse(fs.readFileSync('./config/project-scratch-def.json', 'utf-8')); this.projectScratchDef.orgName = this.scratchOrgAlias; this.projectScratchDef.adminEmail = this.userEmail; - this.projectScratchDef.username = `${this.userEmail.split("@")[0]}@hardis-scratch-${this.scratchOrgAlias}.com`; + this.projectScratchDef.username = `${this.userEmail.split('@')[0]}@hardis-scratch-${this.scratchOrgAlias}.com`; const projectScratchDefLocal = `./config/user/project-scratch-def-${this.scratchOrgAlias}.json`; await fs.ensureDir(path.dirname(projectScratchDefLocal)); await fs.writeFile(projectScratchDefLocal, JSON.stringify(this.projectScratchDef, null, 2)); // Check current scratch org - const orgListResult = await execSfdxJson("sfdx force:org:list", this); - const hubOrgUsername = this.hubOrg.getUsername(); + const orgListResult = await execSfdxJson('sf org list', this); + const hubOrgUsername = flags['target-dev-hub'].getUsername(); const matchingScratchOrgs = orgListResult?.result?.scratchOrgs?.filter((org: any) => { - return org.alias === this.scratchOrgAlias && org.status === "Active" && org.devHubUsername === hubOrgUsername; + return org.alias === this.scratchOrgAlias && org.status === 'Active' && org.devHubUsername === hubOrgUsername; }) || []; // Reuse existing scratch org if (matchingScratchOrgs?.length > 0 && !this.forceNew && this.pool === false) { @@ -243,82 +268,96 @@ export default class ScratchCreate extends SfdxCommand { } // Try to fetch a scratch org from the pool if (this.pool === false && this.configInfo.poolConfig) { - this.scratchOrgFromPool = await fetchScratchOrg({ devHubConn: this.hubOrg.getConnection(), devHubUsername: this.hubOrg.getUsername() }); + this.scratchOrgFromPool = await fetchScratchOrg({ + devHubConn: flags['target-dev-hub'].getConnection(), + devHubUsername: flags['target-dev-hub'].getUsername(), + }); if (this.scratchOrgFromPool) { this.scratchOrgAlias = this.scratchOrgFromPool.scratchOrgAlias; this.scratchOrgInfo = this.scratchOrgFromPool.scratchOrgInfo; this.scratchOrgUsername = this.scratchOrgFromPool.scratchOrgUsername; this.scratchOrgPassword = this.scratchOrgFromPool.scratchOrgPassword; - await setConfig("user", { scratchOrgAlias: this.scratchOrgAlias }); - uxLog(this, "[pool] " + c.cyan(`Fetched org ${c.green(this.scratchOrgAlias)} from pool with user ${c.green(this.scratchOrgUsername)}`)); + await setConfig('user', { scratchOrgAlias: this.scratchOrgAlias }); + uxLog( + this, + '[pool] ' + + c.cyan( + `Fetched org ${c.green(this.scratchOrgAlias)} from pool with user ${c.green(this.scratchOrgUsername)}` + ) + ); if (!isCI) { - uxLog(this, c.cyan("Now opening org...") + " " + c.yellow("(The org is not ready to work in until this script is completed !)")); - await execSfdxJson("sf org open", this, { + uxLog( + this, + c.cyan('Now opening org...') + + ' ' + + c.yellow('(The org is not ready to work in until this script is completed !)') + ); + await execSfdxJson('sf org open', this, { fail: true, output: false, debug: this.debugMode, }); // Trigger a status refresh on VsCode WebSocket Client - WebSocketClient.sendMessage({ event: "refreshStatus" }); + WebSocketClient.sendMessage({ event: 'refreshStatus' }); } return; } } // Fix @salesforce/cli bug: remove shape.zip if found - const tmpShapeFolder = path.join(os.tmpdir(), "shape"); + const tmpShapeFolder = path.join(os.tmpdir(), 'shape'); if (fs.existsSync(tmpShapeFolder) && this.pool === false) { await fs.remove(tmpShapeFolder); - uxLog(this, c.grey("Deleted " + tmpShapeFolder)); + uxLog(this, c.grey('Deleted ' + tmpShapeFolder)); } // Create new scratch org - uxLog(this, c.cyan("Creating new scratch org...")); - const waitTime = process.env.SCRATCH_ORG_WAIT || "15"; + uxLog(this, c.cyan('Creating new scratch org...')); + const waitTime = process.env.SCRATCH_ORG_WAIT || '15'; const createCommand = - "sfdx force:org:create --setdefaultusername " + - `--definitionfile ${projectScratchDefLocal} ` + - `--setalias ${this.scratchOrgAlias} ` + + 'sf org create scratch --set-default ' + + `--definition-file ${projectScratchDefLocal} ` + + `--alias ${this.scratchOrgAlias} ` + `--wait ${waitTime} ` + - `--targetdevhubusername ${this.devHubAlias} ` + - `-d ${this.scratchOrgDuration}`; + `--target-org ${this.devHubAlias} ` + + `--duration-days ${this.scratchOrgDuration}`; const createResult = await execSfdxJson(createCommand, this, { fail: false, output: false, debug: this.debugMode, }); - await clearCache("force:org:list"); + await clearCache('sf org list'); assert(createResult.status === 0 && createResult.result, this.buildScratchCreateErrorMessage(createResult)); this.scratchOrgInfo = createResult.result; this.scratchOrgUsername = this.scratchOrgInfo.username; - await setConfig("user", { + await setConfig('user', { scratchOrgAlias: this.scratchOrgAlias, scratchOrgUsername: this.scratchOrgUsername, }); // Generate password - const passwordCommand = `sfdx force:user:password:generate --targetusername ${this.scratchOrgUsername}`; + const passwordCommand = `sf org generate password --target-org ${this.scratchOrgUsername}`; const passwordResult = await execSfdxJson(passwordCommand, this, { fail: true, output: false, debug: this.debugMode, }); this.scratchOrgPassword = passwordResult.result.password; - await setConfig("user", { + await setConfig('user', { scratchOrgPassword: this.scratchOrgPassword, }); // Trigger a status refresh on VsCode WebSocket Client - WebSocketClient.sendMessage({ event: "refreshStatus" }); + WebSocketClient.sendMessage({ event: 'refreshStatus' }); if (isCI || this.pool === true) { // Try to store sfdxAuthUrl for scratch org reuse during CI - const displayOrgCommand = `sfdx force:org:display -u ${this.scratchOrgAlias} --verbose`; + const displayOrgCommand = `sf org display -o ${this.scratchOrgAlias} --verbose`; const displayResult = await execSfdxJson(displayOrgCommand, this, { fail: true, output: false, debug: this.debugMode, }); if (displayResult.result.sfdxAuthUrl) { - await setConfig("user", { + await setConfig('user', { scratchOrgSfdxAuthUrl: displayResult.result.sfdxAuthUrl, }); this.scratchOrgSfdxAuthUrl = displayResult.result.sfdxAuthUrl; @@ -328,7 +367,7 @@ export default class ScratchCreate extends SfdxCommand { const authInfo = await AuthInfo.create({ username: displayResult.result.username }); this.scratchOrgSfdxAuthUrl = authInfo.getSfdxAuthUrl(); displayResult.result.sfdxAuthUrl = this.scratchOrgSfdxAuthUrl; - await setConfig("user", { + await setConfig('user', { scratchOrgSfdxAuthUrl: this.scratchOrgSfdxAuthUrl, }); // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -336,20 +375,20 @@ export default class ScratchCreate extends SfdxCommand { uxLog( this, c.yellow( - `Unable to fetch sfdxAuthUrl for ${displayResult.result.username}. Only Scratch Orgs created from DevHub using authenticated using auth:sfdxurl or auth:web will have access token and enabled for autoLogin\nYou may need to define SFDX_AUTH_URL_DEV_HUB or SFDX_AUTH_URL_devHubAlias in your CI job running sfdx hardis:scratch:pool:refresh`, - ), + `Unable to fetch sfdxAuthUrl for ${displayResult.result.username}. Only Scratch Orgs created from DevHub using authenticated using sf org login sfdx-url or sf org login web will have access token and enabled for autoLogin\nYou may need to define SFDX_AUTH_URL_DEV_HUB or SFDX_AUTH_URL_devHubAlias in your CI job running sf hardis:scratch:pool:refresh` + ) ); this.scratchOrgSfdxAuthUrl = null; } } if (this.pool) { - await setConfig("user", { + await setConfig('user', { authFileJson: displayResult, }); this.authFileJson = displayResult; } // Display org URL - const openRes = await execSfdxJson("sf org open --url-only", this, { + const openRes = await execSfdxJson('sf org open --url-only', this, { fail: true, output: false, debug: this.debugMode, @@ -357,60 +396,75 @@ export default class ScratchCreate extends SfdxCommand { uxLog(this, c.cyan(`Open scratch org with url: ${c.green(openRes?.result?.url)}`)); } else { // Open scratch org for user if not in CI - await execSfdxJson("sf org open", this, { + await execSfdxJson('sf org open', this, { fail: true, output: false, debug: this.debugMode, }); } - uxLog(this, c.cyan(`Created scratch org ${c.green(this.scratchOrgAlias)} with user ${c.green(this.scratchOrgUsername)}`)); + uxLog( + this, + c.cyan(`Created scratch org ${c.green(this.scratchOrgAlias)} with user ${c.green(this.scratchOrgUsername)}`) + ); } public buildScratchCreateErrorMessage(createResult) { if (createResult.status === 0 && createResult.result) { - return c.green("Scratch create OK"); - } else if (createResult.status === 1 && createResult.errorMessage.includes("Socket timeout occurred while listening for results")) { + return c.green('Scratch create OK'); + } else if ( + createResult.status === 1 && + createResult.errorMessage.includes('Socket timeout occurred while listening for results') + ) { return c.red( `[sfdx-hardis] Error creating scratch org. ${c.bold( - "This is probably a Salesforce error, try again manually or launch again CI job", - )}\n${JSON.stringify(createResult, null, 2)}`, + 'This is probably a Salesforce error, try again manually or launch again CI job' + )}\n${JSON.stringify(createResult, null, 2)}` ); - } else if (createResult.status === 1 && createResult.errorMessage.includes("LIMIT_EXCEEDED")) { + } else if (createResult.status === 1 && createResult.errorMessage.includes('LIMIT_EXCEEDED')) { return c.red( `[sfdx-hardis] Error creating scratch org. ${c.bold( - 'It seems you have no more scratch orgs available, go delete some in "Active Scratch Orgs" tab in the Dev Hub org', - )}\n${JSON.stringify(createResult, null, 2)}`, + 'It seems you have no more scratch orgs available, go delete some in "Active Scratch Orgs" tab in the Dev Hub org' + )}\n${JSON.stringify(createResult, null, 2)}` ); } return c.red( - `[sfdx-hardis] Error creating scratch org. Maybe try ${c.yellow(c.bold("sfdx hardis:scratch:create --forcenew"))} ?\n${JSON.stringify( - createResult, - null, - 2, - )}`, + `[sfdx-hardis] Error creating scratch org. Maybe try ${c.yellow( + c.bold('sf hardis:scratch:create --forcenew') + )} ?\n${JSON.stringify(createResult, null, 2)}` ); } // Update scratch org user public async updateScratchOrgUser() { - const config = await getConfig("user"); + const config = await getConfig('user'); // Update scratch org main user - uxLog(this, c.cyan("Update / fix scratch org user " + this.scratchOrgUsername)); - const userQueryCommand = `sfdx force:data:record:get -s User -w "Username=${this.scratchOrgUsername}" -u ${this.scratchOrgAlias}`; - const userQueryRes = await execSfdxJson(userQueryCommand, this, { fail: true, output: false, debug: this.debugMode }); + uxLog(this, c.cyan('Update / fix scratch org user ' + this.scratchOrgUsername)); + const userQueryCommand = `sf data get record --sobject User --where "Username=${this.scratchOrgUsername}" --target-org ${this.scratchOrgAlias}`; + const userQueryRes = await execSfdxJson(userQueryCommand, this, { + fail: true, + output: false, + debug: this.debugMode, + }); let updatedUserValues = `LastName='SFDX-HARDIS' FirstName='Scratch Org'`; if (config.userEmail !== userQueryRes.result.CountryCode) { updatedUserValues += ` Email='${config.userEmail}'`; } // Fix country value is State & Country picklist activated - if ((this.projectScratchDef.features || []).includes("StateAndCountryPicklist") && userQueryRes.result.CountryCode == null) { - updatedUserValues += ` CountryCode='${config.defaultCountryCode || "FR"}' Country='${config.defaultCountry || "France"}'`; + if ( + (this.projectScratchDef.features || []).includes('StateAndCountryPicklist') && + userQueryRes.result.CountryCode == null + ) { + updatedUserValues += ` CountryCode='${config.defaultCountryCode || 'FR'}' Country='${config.defaultCountry || 'France' + }'`; } - if ((this.projectScratchDef.features || []).includes("MarketingUser") && userQueryRes.result.UserPermissionsMarketingUser === false) { + if ( + (this.projectScratchDef.features || []).includes('MarketingUser') && + userQueryRes.result.UserPermissionsMarketingUser === false + ) { // Make sure MarketingUser is checked on scratch org user if it is supposed to be - updatedUserValues += " UserPermissionsMarketingUser=true"; + updatedUserValues += ' UserPermissionsMarketingUser=true'; } - const userUpdateCommand = `sfdx force:data:record:update -s User -i ${userQueryRes.result.Id} -v "${updatedUserValues}" -u ${this.scratchOrgAlias}`; + const userUpdateCommand = `sf data update record --sobject User --record-id ${userQueryRes.result.Id} --values "${updatedUserValues}" --target-org ${this.scratchOrgAlias}`; await execSfdxJson(userUpdateCommand, this, { fail: false, output: true, debug: this.debugMode }); } } diff --git a/src/commands/hardis/scratch/delete.ts b/src/commands/hardis/scratch/delete.ts index 2690efcfb..6bc2d5824 100644 --- a/src/commands/hardis/scratch/delete.ts +++ b/src/commands/hardis/scratch/delete.ts @@ -1,89 +1,88 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import { execCommand, execSfdxJson, uxLog } from "../../../common/utils"; -import { prompts } from "../../../common/utils/prompts"; -import * as c from "chalk"; -import * as sortArray from "sort-array"; +import { SfCommand, Flags, requiredHubFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { execCommand, execSfdxJson, uxLog } from '../../../common/utils/index.js'; +import { prompts } from '../../../common/utils/prompts.js'; +import c from 'chalk'; +import sortArray from 'sort-array'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class ScratchDelete extends SfCommand { + public static title = 'Delete scratch orgs(s)'; -export default class ScratchDelete extends SfdxCommand { - public static title = "Delete scratch orgs(s)"; + public static description = 'Assisted menu to delete scratch orgs associated to a DevHub'; - public static description = "Assisted menu to delete scratch orgs associated to a DevHub"; - - public static examples = ["$ sfdx hardis:scratch:delete"]; + public static examples = ['$ sf hardis:scratch:delete']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-dev-hub': requiredHubFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = true; - /* jscpd:ignore-end */ public async run(): Promise { - const debugMode = this.flags.debug || false; + const { flags } = await this.parse(ScratchDelete); + const debugMode = flags.debug || false; // List all scratch orgs referenced on local computer - const orgListRequest = "sfdx force:org:list"; - const hubOrgUsername = this.hubOrg.getUsername(); + const orgListRequest = 'sf org list'; + const hubOrgUsername = flags['target-dev-hub'].getUsername(); const orgListResult = await execSfdxJson(orgListRequest, this, { fail: true, output: false, debug: debugMode }); const scratchOrgsSorted = sortArray(orgListResult?.result?.scratchOrgs || [], { - by: ["username", "alias", "instanceUrl"], - order: ["asc", "asc", "asc"], + by: ['username', 'alias', 'instanceUrl'], + order: ['asc', 'asc', 'asc'], }); const scratchOrgChoices = scratchOrgsSorted - .filter((scratchInfo) => { + .filter((scratchInfo: any) => { return scratchInfo.devHubUsername === hubOrgUsername; }) - .map((scratchInfo) => { + .map((scratchInfo: any) => { return { title: scratchInfo.username, - description: `${scratchInfo.instanceUrl}, last used on ${new Date(scratchInfo.lastUsed).toLocaleDateString()}`, + description: `${scratchInfo.instanceUrl}, last used on ${new Date( + scratchInfo.lastUsed + ).toLocaleDateString()}`, value: scratchInfo, }; }); // Request user which scratch he/she wants to delete const scratchToDeleteRes = await prompts({ - type: "multiselect", - name: "value", - message: c.cyanBright("Please select the list of scratch orgs you want to delete"), + type: 'multiselect', + name: 'value', + message: c.cyanBright('Please select the list of scratch orgs you want to delete'), choices: scratchOrgChoices, }); // Delete scratch orgs for (const scratchOrgToDelete of scratchToDeleteRes.value) { - const deleteCommand = `sfdx force:org:delete --noprompt --targetusername ${scratchOrgToDelete.username}`; + const deleteCommand = `sf org delete scratch --no-prompt --target-org ${scratchOrgToDelete.username}`; await execCommand(deleteCommand, this, { fail: false, debug: debugMode, output: true }); - uxLog(this, c.cyan(`Scratch org ${c.green(scratchOrgToDelete.username)} at ${scratchOrgToDelete.instanceUrl} has been deleted`)); + uxLog( + this, + c.cyan( + `Scratch org ${c.green(scratchOrgToDelete.username)} at ${scratchOrgToDelete.instanceUrl} has been deleted` + ) + ); } // Return an object to be displayed with --json - return { outputString: "Deleted scratch orgs" }; + return { outputString: 'Deleted scratch orgs' }; } } diff --git a/src/commands/hardis/scratch/pool/create.ts b/src/commands/hardis/scratch/pool/create.ts index 119fb8a9e..88dea1187 100644 --- a/src/commands/hardis/scratch/pool/create.ts +++ b/src/commands/hardis/scratch/pool/create.ts @@ -1,23 +1,19 @@ /* jscpd:ignore-start */ -import * as c from "chalk"; -import { flags, SfdxCommand } from "@salesforce/command"; -import { AuthInfo, Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import { getConfig, setConfig } from "../../../../config"; -import { prompts } from "../../../../common/utils/prompts"; -import { uxLog } from "../../../../common/utils"; -import { instantiateProvider, listKeyValueProviders } from "../../../../common/utils/poolUtils"; -import { KeyValueProviderInterface } from "../../../../common/utils/keyValueUtils"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class ScratchPoolCreate extends SfdxCommand { - public static title = "Create and configure scratch org pool"; +import c from 'chalk'; +import { SfCommand, Flags, requiredHubFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { AuthInfo, Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { getConfig, setConfig } from '../../../../config/index.js'; +import { prompts } from '../../../../common/utils/prompts.js'; +import { uxLog } from '../../../../common/utils/index.js'; +import { instantiateProvider, listKeyValueProviders } from '../../../../common/utils/poolUtils.js'; +import { KeyValueProviderInterface } from '../../../../common/utils/keyValueUtils.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class ScratchPoolCreate extends SfCommand { + public static title = 'Create and configure scratch org pool'; public static description = `Select a data storage service and configure information to build a scratch org pool @@ -28,43 +24,39 @@ export default class ScratchPoolCreate extends SfdxCommand { - Call the following lines in the CI job: \`\`\`shell - sfdx hardis:auth:login --devhub - sfdx hardis:scratch:pool:refresh + sf hardis:auth:login --devhub + sf hardis:scratch:pool:refresh \`\`\` `; - public static examples = ["$ sfdx hardis:scratch:pool:configure"]; + public static examples = ['$ sf hardis:scratch:pool:configure']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-dev-hub': requiredHubFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; /* jscpd:ignore-end */ public async run(): Promise { // Get pool configuration - const config = await getConfig("project"); + const { flags } = await this.parse(ScratchPoolCreate); + const config = await getConfig('project'); const poolConfig = config.poolConfig || {}; // Tell user if he/she's about to overwrite existing configuration @@ -73,27 +65,27 @@ export default class ScratchPoolCreate extends SfdxCommand { this, c.yellow( `There is already an existing scratch org pool configuration: ${JSON.stringify(config.poolConfig)}. -If you really want to replace it, please remove poolConfig property from .sfdx-hardis.yml and run again this command`, - ), +If you really want to replace it, please remove poolConfig property from .sfdx-hardis.yml and run again this command` + ) ); - return { outputString: "Scratch org pool configuration already existing" }; + return { outputString: 'Scratch org pool configuration already existing' }; } const allProviders = await listKeyValueProviders(); const response = await prompts([ { - type: "select", - name: "storageService", - message: c.cyanBright("What storage service do you want to use for your scratch orgs pool ?"), + type: 'select', + name: 'storageService', + message: c.cyanBright('What storage service do you want to use for your scratch orgs pool ?'), initial: 0, choices: allProviders.map((provider: KeyValueProviderInterface) => { return { title: provider.name, description: provider.description, value: provider.name }; }), }, { - type: "number", - name: "maxScratchOrgsNumber", - message: c.cyanBright("What is the maximum number of scratch orgs in the pool ?"), + type: 'number', + name: 'maxScratchOrgsNumber', + message: c.cyanBright('What is the maximum number of scratch orgs in the pool ?'), initial: poolConfig.maxScratchOrgsNumber || 5, }, ]); @@ -101,28 +93,36 @@ If you really want to replace it, please remove poolConfig property from .sfdx-h // Store updated config poolConfig.maxScratchOrgsNumber = response.maxScratchOrgsNumber; poolConfig.storageService = response.storageService; - await setConfig("project", { poolConfig: poolConfig }); + await setConfig('project', { poolConfig: poolConfig }); // Request additional setup to the user const provider = await instantiateProvider(response.storageService); - await provider.userSetup({ devHubConn: this.hubOrg.getConnection(), devHubUsername: this.hubOrg.getUsername() }); + await provider.userSetup({ + devHubConn: flags['target-dev-hub'].getConnection(), + devHubUsername: flags['target-dev-hub'].getUsername(), + }); - const authInfo = await AuthInfo.create({ username: this.hubOrg.getUsername() }); + const authInfo = await AuthInfo.create({ username: flags['target-dev-hub'].getUsername() }); const sfdxAuthUrl = authInfo.getSfdxAuthUrl(); if (sfdxAuthUrl) { - uxLog(this, c.cyan(`You need to define CI masked variable ${c.green("SFDX_AUTH_URL_DEV_HUB")} = ${c.green(sfdxAuthUrl)}`)); + uxLog( + this, + c.cyan(`You need to define CI masked variable ${c.green('SFDX_AUTH_URL_DEV_HUB')} = ${c.green(sfdxAuthUrl)}`) + ); } else { uxLog( this, c.yellow( `You'll probably need to define CI masked variable ${c.green( - "SFDX_AUTH_URL_DEV_HUB", - )} with content of sfdxAuthUrl that you can retrieve with ${c.white("sfdx force:org:display -u YOURDEVHUBUSERNAME --verbose --json")}`, - ), + 'SFDX_AUTH_URL_DEV_HUB' + )} with content of sfdxAuthUrl that you can retrieve with ${c.white( + 'sf org display -o YOURDEVHUBUSERNAME --verbose --json' + )}` + ) ); } // Return an object to be displayed with --json - return { outputString: "Configured scratch orgs pool" }; + return { outputString: 'Configured scratch orgs pool' }; } } diff --git a/src/commands/hardis/scratch/pool/localauth.ts b/src/commands/hardis/scratch/pool/localauth.ts index b57bc877a..f5a307e21 100644 --- a/src/commands/hardis/scratch/pool/localauth.ts +++ b/src/commands/hardis/scratch/pool/localauth.ts @@ -1,58 +1,49 @@ /* jscpd:ignore-start */ -import * as c from "chalk"; -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import { getConfig } from "../../../../config"; -import { uxLog } from "../../../../common/utils"; -import { instantiateProvider } from "../../../../common/utils/poolUtils"; -import { KeyValueProviderInterface } from "../../../../common/utils/keyValueUtils"; +import c from 'chalk'; +import { SfCommand, Flags, requiredHubFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { getConfig } from '../../../../config/index.js'; +import { uxLog } from '../../../../common/utils/index.js'; +import { instantiateProvider } from '../../../../common/utils/poolUtils.js'; +import { KeyValueProviderInterface } from '../../../../common/utils/keyValueUtils.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class ScratchPoolLocalAuth extends SfdxCommand { - public static title = "Authenticate locally to scratch org pool"; +export default class ScratchPoolLocalAuth extends SfCommand { + public static title = 'Authenticate locally to scratch org pool'; public static description = - "Calls the related storage service to request api keys and secrets that allows a local user to fetch a scratch org from scratch org pool"; + 'Calls the related storage service to request api keys and secrets that allows a local user to fetch a scratch org from scratch org pool'; - public static examples = ["$ sfdx hardis:scratch:pool:localauth"]; + public static examples = ['$ sf hardis:scratch:pool:localauth']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-dev-hub': requiredHubFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; /* jscpd:ignore-end */ public async run(): Promise { // Get pool configuration - const config = await getConfig("project"); + const config = await getConfig('project'); const poolConfig = config.poolConfig || {}; // Tell user if he/she's about to overwrite existing configuration @@ -60,10 +51,10 @@ export default class ScratchPoolLocalAuth extends SfdxCommand { uxLog( this, c.yellow( - `There is not scratch orgs pool configured on this project. Please see with your tech lead about using command hardis:scratch:pool:configure`, - ), + `There is not scratch orgs pool configured on this project. Please see with your tech lead about using command hardis:scratch:pool:configure` + ) ); - return { outputString: "Scratch org pool configuration to create" }; + return { outputString: 'Scratch org pool configuration to create' }; } // Request additional setup to the user @@ -71,6 +62,6 @@ export default class ScratchPoolLocalAuth extends SfdxCommand { await provider.userAuthenticate(); // Return an object to be displayed with --json - return { outputString: "Locally authenticated with scratch org pool" }; + return { outputString: 'Locally authenticated with scratch org pool' }; } } diff --git a/src/commands/hardis/scratch/pool/refresh.ts b/src/commands/hardis/scratch/pool/refresh.ts index d9d05f2be..e1327c0dc 100644 --- a/src/commands/hardis/scratch/pool/refresh.ts +++ b/src/commands/hardis/scratch/pool/refresh.ts @@ -1,154 +1,156 @@ /* jscpd:ignore-start */ -import { spawn } from "child_process"; -import * as c from "chalk"; +import { spawn } from 'child_process'; +import c from 'chalk'; -import * as which from "which"; -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import { addScratchOrgToPool, getPoolStorage, setPoolStorage } from "../../../../common/utils/poolUtils"; -import { getConfig } from "../../../../config"; -import { execCommand, stripAnsi, uxLog } from "../../../../common/utils"; -import * as moment from "moment"; -import { authenticateWithSfdxUrlStore } from "../../../../common/utils/orgUtils"; +import which from 'which'; +import { SfCommand, Flags, requiredHubFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { addScratchOrgToPool, getPoolStorage, setPoolStorage } from '../../../../common/utils/poolUtils.js'; +import { getConfig } from '../../../../config/index.js'; +import { execCommand, stripAnsi, uxLog } from '../../../../common/utils/index.js'; +import moment from 'moment'; +import { authenticateWithSfdxUrlStore } from '../../../../common/utils/orgUtils.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class ScratchPoolRefresh extends SfCommand { + public static title = 'Refresh scratch org pool'; -export default class ScratchPoolRefresh extends SfdxCommand { - public static title = "Refresh scratch org pool"; + public static description = 'Create enough scratch orgs to fill the pool'; - public static description = "Create enough scratch orgs to fill the pool"; - - public static examples = ["$ sfdx hardis:scratch:pool:refresh"]; + public static examples = ['$ sf hardis:scratch:pool:refresh']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-dev-hub': requiredHubFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; private debugMode = false; public async run(): Promise { - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(ScratchPoolRefresh); + this.debugMode = flags.debug || false; // Check pool configuration is defined on project - const config = await getConfig("project"); + const config = await getConfig('project'); if (config.poolConfig == null) { - uxLog(this, c.yellow("Configuration file must contain a poolConfig property") + "\n" + c.grey(JSON.stringify(config, null, 2))); - return { outputString: "Configuration file must contain a poolConfig property" }; + uxLog( + this, + c.yellow('Configuration file must contain a poolConfig property') + + '\n' + + c.grey(JSON.stringify(config, null, 2)) + ); + return { outputString: 'Configuration file must contain a poolConfig property' }; } const maxScratchOrgsNumber = config.poolConfig.maxScratchOrgsNumber || 5; const maxScratchOrgsNumberToCreateOnce = config.poolConfig.maxScratchOrgsNumberToCreateOnce || 10; - uxLog(this, c.grey("Pool config: " + JSON.stringify(config.poolConfig))); + uxLog(this, c.grey('Pool config: ' + JSON.stringify(config.poolConfig))); // Get pool storage - const poolStorage = await getPoolStorage({ devHubConn: this.hubOrg.getConnection(), devHubUsername: this.hubOrg.getUsername() }); + const poolStorage = await getPoolStorage({ + devHubConn: flags['target-dev-hub'].getConnection(), + devHubUsername: flags['target-dev-hub'].getUsername(), + }); let scratchOrgs = poolStorage.scratchOrgs || []; /* jscpd:ignore-end */ // Clean expired orgs const minScratchOrgRemainingDays = config.poolConfig.minScratchOrgRemainingDays || 25; - const scratchOrgsToDelete = []; + const scratchOrgsToDelete: any[] = []; scratchOrgs = scratchOrgs.filter((scratchOrg) => { const expiration = moment(scratchOrg?.authFileJson?.result?.expirationDate); const today = moment(); - const daysBeforeExpiration = expiration.diff(today, "days"); + const daysBeforeExpiration = expiration.diff(today, 'days'); if (daysBeforeExpiration < minScratchOrgRemainingDays) { scratchOrg.daysBeforeExpiration = daysBeforeExpiration; scratchOrgsToDelete.push(scratchOrg); uxLog( this, c.grey( - `Scratch org ${scratchOrg?.authFileJson?.result?.instanceUrl} will be deleted as it has only ${daysBeforeExpiration} remaining days (expiration on ${scratchOrg?.authFileJson?.result?.expirationDate})`, - ), + `Scratch org ${scratchOrg?.authFileJson?.result?.instanceUrl} will be deleted as it has only ${daysBeforeExpiration} remaining days (expiration on ${scratchOrg?.authFileJson?.result?.expirationDate})` + ) ); return false; } uxLog( this, c.grey( - `Scratch org ${scratchOrg?.authFileJson?.result?.instanceUrl} will be kept as it still has ${daysBeforeExpiration} remaining days (expiration on ${scratchOrg?.authFileJson?.result?.expirationDate})`, - ), + `Scratch org ${scratchOrg?.authFileJson?.result?.instanceUrl} will be kept as it still has ${daysBeforeExpiration} remaining days (expiration on ${scratchOrg?.authFileJson?.result?.expirationDate})` + ) ); return true; }); // Delete expired orgs and update pool if found if (scratchOrgsToDelete.length > 0) { poolStorage.scratchOrgs = scratchOrgs; - await setPoolStorage(poolStorage, { devHubConn: this.hubOrg.getConnection(), devHubUsername: this.hubOrg.getUsername() }); + await setPoolStorage(poolStorage, { + devHubConn: flags['target-dev-hub'].getConnection(), + devHubUsername: flags['target-dev-hub'].getUsername(), + }); for (const scratchOrgToDelete of scratchOrgsToDelete) { // Authenticate to scratch org to delete await authenticateWithSfdxUrlStore(scratchOrgToDelete); // Delete scratch org - const deleteCommand = `sfdx force:org:delete --noprompt --targetusername ${scratchOrgToDelete.scratchOrgUsername}`; + const deleteCommand = `sf org delete scratch --no-prompt --target-org ${scratchOrgToDelete.scratchOrgUsername}`; await execCommand(deleteCommand, this, { fail: false, debug: this.debugMode, output: true }); uxLog( this, c.cyan( - `Scratch org ${c.green(scratchOrgToDelete.scratchOrgUsername)} at ${ - scratchOrgToDelete?.authFileJson?.result?.instanceUrl - } has been deleted because only ${scratchOrgToDelete.daysBeforeExpiration} days were remaining.`, - ), + `Scratch org ${c.green(scratchOrgToDelete.scratchOrgUsername)} at ${scratchOrgToDelete?.authFileJson?.result?.instanceUrl + } has been deleted because only ${scratchOrgToDelete.daysBeforeExpiration} days were remaining.` + ) ); } } // Create new scratch orgs const numberOfOrgsToCreate = Math.min(maxScratchOrgsNumber - scratchOrgs.length, maxScratchOrgsNumberToCreateOnce); - uxLog(this, c.cyan("Creating " + numberOfOrgsToCreate + " scratch orgs...")); + uxLog(this, c.cyan('Creating ' + numberOfOrgsToCreate + ' scratch orgs...')); let numberCreated = 0; let numberfailed = 0; - const subProcesses = []; + const subProcesses: any[] = []; for (let i = 0; i < numberOfOrgsToCreate; i++) { // eslint-disable-next-line no-async-promise-executor const spawnPromise = new Promise(async (resolve) => { // Run scratch:create command asynchronously - const commandArgs = ["hardis:scratch:create", "--pool", "--json"]; - const sfdxPath = await which("sfdx"); - const child = spawn(sfdxPath || "sfdx", commandArgs, { cwd: process.cwd(), env: process.env }); - uxLog(this, "[pool] " + c.grey(`hardis:scratch:create (${i}) started`)); + const commandArgs = ['hardis:scratch:create', '--pool', '--json']; + const sfdxPath = await which('sf'); + const child = spawn(sfdxPath || 'sf', commandArgs, { cwd: process.cwd(), env: process.env }); + uxLog(this, '[pool] ' + c.grey(`hardis:scratch:create (${i}) started`)); // handle errors - child.on("error", (err) => { + child.on('error', (err) => { resolve({ code: 1, result: { error: err } }); }); // Store data - let stdout = ""; - child.stdout.on("data", (data) => { + let stdout = ''; + child.stdout.on('data', (data) => { stdout += data.toString(); if (this.debugMode === true) { uxLog(this, data.toString()); } }); // Handle end of command - child.on("close", async (code) => { + child.on('close', async (code) => { const colorFunc = code === 0 ? c.green : c.red; - uxLog(this, "[pool] " + colorFunc(`hardis:scratch:create (${i}) exited with code ${c.bold(code)}`)); + uxLog(this, '[pool] ' + colorFunc(`hardis:scratch:create (${i}) exited with code ${c.bold(code)}`)); if (code !== 0) { uxLog(this, `Return code is not 0 (${i}): ` + c.grey(stdout)); numberfailed++; @@ -174,12 +176,21 @@ export default class ScratchPoolRefresh extends SfdxCommand { // Await parallel scratch org creations are completed const createResults = await Promise.all(subProcesses); if (this.debugMode) { - uxLog(this, c.grey("Create results: \n" + JSON.stringify(createResults, null, 2))); + uxLog(this, c.grey('Create results: \n' + JSON.stringify(createResults, null, 2))); } const colorFunc = numberCreated === numberOfOrgsToCreate ? c.green : numberCreated === 0 ? c.red : c.yellow; - uxLog(this, "[pool] " + colorFunc(`Created ${c.bold(numberCreated)} scratch orgs (${c.bold(numberfailed)} creations(s) failed)`)); + uxLog( + this, + '[pool] ' + + colorFunc(`Created ${c.bold(numberCreated)} scratch orgs (${c.bold(numberfailed)} creations(s) failed)`) + ); // Return an object to be displayed with --json - return { outputString: "Refreshed scratch orgs pool", createResults: createResults, numberCreated: numberCreated, numberFailed: numberfailed }; + return { + outputString: 'Refreshed scratch orgs pool', + createResults: createResults, + numberCreated: numberCreated, + numberFailed: numberfailed, + }; } } diff --git a/src/commands/hardis/scratch/pool/reset.ts b/src/commands/hardis/scratch/pool/reset.ts index 3d11bbf56..b176bc8ae 100644 --- a/src/commands/hardis/scratch/pool/reset.ts +++ b/src/commands/hardis/scratch/pool/reset.ts @@ -1,69 +1,69 @@ /* jscpd:ignore-start */ -import * as c from "chalk"; -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import { getPoolStorage, setPoolStorage } from "../../../../common/utils/poolUtils"; -import { getConfig } from "../../../../config"; -import { execCommand, uxLog } from "../../../../common/utils"; -import { authenticateWithSfdxUrlStore } from "../../../../common/utils/orgUtils"; +import c from 'chalk'; +import { SfCommand, Flags, requiredHubFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { getPoolStorage, setPoolStorage } from '../../../../common/utils/poolUtils.js'; +import { getConfig } from '../../../../config/index.js'; +import { execCommand, uxLog } from '../../../../common/utils/index.js'; +import { authenticateWithSfdxUrlStore } from '../../../../common/utils/orgUtils.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class ScratchPoolReset extends SfCommand { + public static title = 'Reset scratch org pool'; -export default class ScratchPoolReset extends SfdxCommand { - public static title = "Reset scratch org pool"; + public static description = 'Reset scratch org pool (delete all scratches in the pool)'; - public static description = "Reset scratch org pool (delete all scratches in the pool)"; - - public static examples = ["$ sfdx hardis:scratch:pool:refresh"]; + public static examples = ['$ sf hardis:scratch:pool:refresh']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-dev-hub': requiredHubFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; /* jscpd:ignore-end */ private debugMode = false; public async run(): Promise { - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(ScratchPoolReset); + this.debugMode = flags.debug || false; // Check pool configuration is defined on project - const config = await getConfig("project"); + const config = await getConfig('project'); if (config.poolConfig == null) { - uxLog(this, c.yellow("Configuration file must contain a poolConfig property") + "\n" + c.grey(JSON.stringify(config, null, 2))); - return { outputString: "Configuration file must contain a poolConfig property" }; + uxLog( + this, + c.yellow('Configuration file must contain a poolConfig property') + + '\n' + + c.grey(JSON.stringify(config, null, 2)) + ); + return { outputString: 'Configuration file must contain a poolConfig property' }; } - uxLog(this, c.cyan(`Reseting scratch org pool on org ${c.green(this.hubOrg.getUsername())}...`)); - uxLog(this, c.grey("Pool config: " + JSON.stringify(config.poolConfig))); + uxLog(this, c.cyan(`Reseting scratch org pool on org ${c.green(flags['target-dev-hub'].getUsername())}...`)); + uxLog(this, c.grey('Pool config: ' + JSON.stringify(config.poolConfig))); // Get pool storage - const poolStorage = await getPoolStorage({ devHubConn: this.hubOrg.getConnection(), devHubUsername: this.hubOrg.getUsername() }); + const poolStorage = await getPoolStorage({ + devHubConn: flags['target-dev-hub'].getConnection(), + devHubUsername: flags['target-dev-hub'].getUsername(), + }); let scratchOrgs = poolStorage.scratchOrgs || []; // Delete existing scratch orgs @@ -71,24 +71,26 @@ export default class ScratchPoolReset extends SfdxCommand { const scratchOrgsToDelete = [...scratchOrgs]; scratchOrgs = []; poolStorage.scratchOrgs = scratchOrgs; - await setPoolStorage(poolStorage, { devHubConn: this.hubOrg.getConnection(), devHubUsername: this.hubOrg.getUsername() }); + await setPoolStorage(poolStorage, { + devHubConn: flags['target-dev-hub'].getConnection(), + devHubUsername: flags['target-dev-hub'].getUsername(), + }); for (const scratchOrgToDelete of scratchOrgsToDelete) { // Authenticate to scratch org to delete await authenticateWithSfdxUrlStore(scratchOrgToDelete); // Delete scratch org - const deleteCommand = `sfdx force:org:delete --noprompt --targetusername ${scratchOrgToDelete.scratchOrgUsername}`; + const deleteCommand = `sf org delete scratch --no-prompt --target-org ${scratchOrgToDelete.scratchOrgUsername}`; await execCommand(deleteCommand, this, { fail: false, debug: this.debugMode, output: true }); uxLog( this, c.cyan( - `Scratch org ${c.green(scratchOrgToDelete.scratchOrgUsername)} at ${ - scratchOrgToDelete?.authFileJson?.result?.instanceUrl - } has been deleted`, - ), + `Scratch org ${c.green(scratchOrgToDelete.scratchOrgUsername)} at ${scratchOrgToDelete?.authFileJson?.result?.instanceUrl + } has been deleted` + ) ); } /* jscpd:ignore-end */ - return { outputString: "Reset scratch orgs pool" }; + return { outputString: 'Reset scratch orgs pool' }; } } diff --git a/src/commands/hardis/scratch/pool/view.ts b/src/commands/hardis/scratch/pool/view.ts index af347f0d5..c900f1659 100644 --- a/src/commands/hardis/scratch/pool/view.ts +++ b/src/commands/hardis/scratch/pool/view.ts @@ -1,73 +1,68 @@ /* jscpd:ignore-start */ -import * as c from "chalk"; -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import { getConfig } from "../../../../config"; -import { uxLog } from "../../../../common/utils"; -import { getPoolStorage } from "../../../../common/utils/poolUtils"; +import c from 'chalk'; +import { SfCommand, Flags, requiredHubFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { getConfig } from '../../../../config/index.js'; +import { uxLog } from '../../../../common/utils/index.js'; +import { getPoolStorage } from '../../../../common/utils/poolUtils.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class ScratchPoolView extends SfCommand { + public static title = 'View scratch org pool info'; -export default class ScratchPoolView extends SfdxCommand { - public static title = "View scratch org pool info"; + public static description = 'Displays all stored content of project scratch org pool if defined'; - public static description = "Displays all stored content of project scratch org pool if defined"; - - public static examples = ["$ sfdx hardis:scratch:pool:view"]; + public static examples = ['$ sf hardis:scratch:pool:view']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-dev-hub': requiredHubFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; /* jscpd:ignore-end */ public async run(): Promise { + const { flags } = await this.parse(ScratchPoolView); // Get pool configuration - const config = await getConfig("project"); + const config = await getConfig('project'); const poolConfig = config.poolConfig || {}; - uxLog(this, "Pool config: " + c.grey(JSON.stringify(poolConfig, null, 2))); + uxLog(this, 'Pool config: ' + c.grey(JSON.stringify(poolConfig, null, 2))); // Missing scratch orgs pool configuration if (!poolConfig.storageService) { uxLog( this, c.yellow( - `There is not scratch orgs pool configured on this project. Please see with your tech lead about using command hardis:scratch:pool:configure`, - ), + `There is not scratch orgs pool configured on this project. Please see with your tech lead about using command hardis:scratch:pool:configure` + ) ); - return { status: 1, outputString: "Scratch org pool configuration to create" }; + return { status: 1, outputString: 'Scratch org pool configuration to create' }; } // Query pool storage - const poolStorage = await getPoolStorage({ devHubConn: this?.hubOrg?.getConnection(), devHubUsername: this?.hubOrg?.getUsername() }); - uxLog(this, "Pool storage: " + c.grey(JSON.stringify(poolStorage, null, 2))); + const poolStorage = await getPoolStorage({ + devHubConn: flags['target-dev-hub']?.getConnection(), + devHubUsername: flags['target-dev-hub']?.getUsername(), + }); + uxLog(this, 'Pool storage: ' + c.grey(JSON.stringify(poolStorage, null, 2))); const scratchOrgs = poolStorage.scratchOrgs || []; const availableNumber = scratchOrgs.length; @@ -78,7 +73,7 @@ export default class ScratchPoolView extends SfdxCommand { // Return an object to be displayed with --json return { status: 0, - outputString: "Viewed scratch org pool", + outputString: 'Viewed scratch org pool', poolStorage: poolStorage, availableScratchOrgs: availableNumber, maxScratchOrgs: poolConfig.maxScratchOrgsNumber, diff --git a/src/commands/hardis/scratch/pull.ts b/src/commands/hardis/scratch/pull.ts index c295f2904..9e9b2047a 100644 --- a/src/commands/hardis/scratch/pull.ts +++ b/src/commands/hardis/scratch/pull.ts @@ -1,18 +1,15 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import { forceSourcePull } from "../../../common/utils/deployUtils"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { forceSourcePull } from '../../../common/utils/deployUtils.js'; +import { uxLog } from '../../../common/utils/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class SourcePull extends SfdxCommand { - public static title = "Scratch PULL"; +export default class SourcePull extends SfCommand { + public static title = 'Scratch PULL'; public static description = `This commands pulls the updates you performed in your scratch or sandbox org, into your local files @@ -20,9 +17,10 @@ Then, you probably want to stage and commit the files containing the updates you -- Calls sfdx force:source:pull under the hood +- Calls \`sf project retrieve start\` under the hood - If there are errors, proposes to automatically add erroneous item in \`.forceignore\`, then pull again -- If you want to always retrieve sources like CustomApplication that are not always detected as updates by force:source:pull , you can define property **autoRetrieveWhenPull** in .sfdx-hardis.yml +- If you don't see your updated items in the results, you can manually retrieve [using SF Extension **Org Browser** or **Salesforce CLI**](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-publish-task/#retrieve-metadatas) +- If you want to always retrieve sources like CustomApplication that are not always detected as updates by project:retrieve:start , you can define property **autoRetrieveWhenPull** in .sfdx-hardis.yml Example: \`\`\`yaml @@ -33,41 +31,39 @@ autoRetrieveWhenPull: \`\`\` `; - public static examples = ["$ sfdx hardis:scratch:pull"]; + public static examples = ['$ sf hardis:scratch:pull']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; /* jscpd:ignore-end */ public async run(): Promise { - const debugMode = this.flags.debug || false; - const targetUsername = this.org.getUsername(); + const { flags } = await this.parse(SourcePull); + const debugMode = flags.debug || false; + const targetUsername = flags['target-org'].getUsername() || ''; await forceSourcePull(targetUsername, debugMode); + uxLog(this, `If you don't see your updated items in the results, check the following documentation: https://sfdx-hardis.cloudity.com/salesforce-ci-cd-publish-task/#retrieve-metadatas`); + // Return an object to be displayed with --json - return { outputString: "Pulled scratch org updates" }; + return { outputString: 'Pulled scratch org / source-tracked sandbox updates' }; } } diff --git a/src/commands/hardis/scratch/push.ts b/src/commands/hardis/scratch/push.ts index cc2f4d718..eca4d708f 100644 --- a/src/commands/hardis/scratch/push.ts +++ b/src/commands/hardis/scratch/push.ts @@ -1,57 +1,51 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import { forceSourcePush } from "../../../common/utils/deployUtils"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { forceSourcePush } from '../../../common/utils/deployUtils.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class SourcePush extends SfdxCommand { - public static title = "Scratch PUSH"; +export default class SourcePush extends SfCommand { + public static title = 'Scratch PUSH'; public static description = `Push local files to scratch org -Calls \`sfdx force:source:push\` under the hood +Calls \`sf project deploy start\` under the hood `; - public static examples = ["$ sfdx hardis:scratch:push"]; + public static examples = ['$ sf hardis:scratch:push']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - // protected static requiresDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; /* jscpd:ignore-end */ public async run(): Promise { - const debugMode = this.flags.debug || false; - await forceSourcePush(this.org.getUsername(), this, debugMode, { conn: this.org.getConnection() }); + const { flags } = await this.parse(SourcePush); + const debugMode = flags.debug || false; + await forceSourcePush(flags['target-org'].getUsername() || '', this, debugMode, { + conn: flags['target-org'].getConnection(), + }); // Return an object to be displayed with --json - return { outputString: "Pushed local git branch in scratch org" }; + return { outputString: 'Pushed local git branch in scratch org' }; } } diff --git a/src/commands/hardis/source/deploy.ts b/src/commands/hardis/source/deploy.ts index 69f05f442..949c7a5ab 100644 --- a/src/commands/hardis/source/deploy.ts +++ b/src/commands/hardis/source/deploy.ts @@ -1,12 +1,19 @@ -import { flags, FlagsConfig, SfdxCommand } from "@salesforce/command"; -import { Duration } from "@salesforce/kit"; -import { AnyJson } from "@salesforce/ts-types"; -import { GitProvider } from "../../../common/gitProvider"; -import { checkDeploymentOrgCoverage, executePrePostCommands, extractOrgCoverageFromLog } from "../../../common/utils/deployUtils"; -import { wrapSfdxCoreCommand } from "../../../common/utils/wrapUtils"; +/* jscpd:ignore-start */ +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { GitProvider } from '../../../common/gitProvider/index.js'; +import { + checkDeploymentOrgCoverage, + executePrePostCommands, + extractOrgCoverageFromLog, +} from '../../../common/utils/deployUtils.js'; +import { wrapSfdxCoreCommand } from '../../../common/utils/wrapUtils.js'; +import { uxLog } from '../../../common/utils/index.js'; +import { CONSTANTS } from '../../../config/index.js'; // Wrapper for sfdx force:source:deploy -export class Deploy extends SfdxCommand { +export class Deploy extends SfCommand { public static readonly description = `sfdx-hardis wrapper for sfdx force:source:deploy that displays tips to solve deployment errors. Additional to the base command wrapper: If using **--checkonly**, add options **--checkcoverage** and **--coverageformatters json-summary** to check that org coverage is > 75% (or value defined in .sfdx-hardis.yml property **apexTestsMinCoverageOrgWide**) @@ -15,16 +22,22 @@ Additional to the base command wrapper: If using **--checkonly**, add options ** You can also have deployment results as pull request comments, on: -- GitHub (see [GitHub Pull Requests comments config](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-integration-github/)) -- Gitlab (see [Gitlab integration configuration](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-integration-gitlab/)) -- Azure DevOps (see [Azure integration configuration](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-integration-azure/)) +- GitHub (see [GitHub Pull Requests comments config](${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-integration-github/)) +- Gitlab (see [Gitlab integration configuration](${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-integration-gitlab/)) +- Azure DevOps (see [Azure integration configuration](${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-integration-azure/)) [![Assisted solving of Salesforce deployments errors](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-deployment-errors.jpg)](https://nicolas.vuillamy.fr/assisted-solving-of-salesforce-deployments-errors-47f3666a9ed0) ### Deployment pre or post commands -You can define command lines to run before or after a deployment +You can define command lines to run before or after a deployment, with parameters: + +- **id**: Unique Id for the command +- **label**: Human readable label for the command +- **skipIfError**: If defined to "true", the post-command won't be run if there is a deployment failure +- **context**: Defines the context where the command will be run. Can be **all** (default), **check-deployment-only** or **process-deployment-only** +- **runOnlyOnceByOrg**: If set to true, the command will be run only one time per org. A record of SfdxHardisTrace__c is stored to make that possible (it needs to be existing in target org) If the commands are not the same depending on the target org, you can define them into **config/branches/.sfdx-hardis-BRANCHNAME.yml** instead of root **config/.sfdx-hardis.yml** @@ -38,6 +51,7 @@ commandsPreDeploy: - id: knowledgeAssign label: Assign Knowledge user to the deployment user command: sf data update record --sobject User --where "Username='deploy.github@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + commandsPostDeploy: - id: knowledgeUnassign label: Remove KnowledgeUser right to the user who has it @@ -45,6 +59,12 @@ commandsPostDeploy: - id: knowledgeAssign label: Assign Knowledge user to desired username command: sf data update record --sobject User --where "Username='admin-yser@myclient.com'" --values "UserPermissionsKnowledgeUser='true'" --json + - id: someActionToRunJustOneTime + label: And to run only if deployment is success + command: sf sfdmu:run ... + skipIfError: true + context: process-deployment-only + runOnlyOnceByOrg: true \`\`\` Notes: @@ -54,114 +74,137 @@ Notes: [See documentation of Salesforce command](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_force_source.htm#cli_reference_force_source_deploy) `; public static readonly examples = [ - "$ sfdx hardis:source:deploy -x manifest/package.xml --wait 60 --ignorewarnings --testlevel RunLocalTests --postdestructivechanges ./manifest/destructiveChanges.xml --targetusername nicolas.vuillamy@cloudity.com.sfdxhardis --checkonly --checkcoverage --verbose --coverageformatters json-summary", + '$ sf hardis:source:deploy -x manifest/package.xml --wait 60 --ignorewarnings --testlevel RunLocalTests --postdestructivechanges ./manifest/destructiveChanges.xml --target-org nicolas.vuillamy@cloudity.com.sfdxhardis --checkonly --checkcoverage --verbose --coverageformatters json-summary', ]; public static readonly requiresProject = true; - public static readonly requiresUsername = true; - public static readonly flagsConfig: FlagsConfig = { - checkonly: flags.boolean({ - char: "c", - description: "checkonly", + public static readonly flags: any = { + checkonly: Flags.boolean({ + char: 'c', + description: 'checkonly', }), - soapdeploy: flags.boolean({ + soapdeploy: Flags.boolean({ default: false, - description: "soapDeploy", - }), - wait: flags.minutes({ - char: "w", - default: Duration.minutes(60), - min: Duration.minutes(0), // wait=0 means deploy is asynchronous - description: "wait", - }), - testlevel: flags.enum({ - char: "l", - description: "testlevel", - options: ["NoTestRun", "RunSpecifiedTests", "RunLocalTests", "RunAllTestsInOrg"], - default: "NoTestRun", - }), - runtests: flags.array({ - char: "r", - description: "runTests", + description: 'soapDeploy', + }), + wait: Flags.integer({ + char: 'w', + default: 60, + min: 0, // wait=0 means deploy is asynchronous + description: 'wait', + }), + testlevel: Flags.string({ + char: 'l', + description: 'testlevel', + options: ['NoTestRun', 'RunSpecifiedTests', 'RunLocalTests', 'RunAllTestsInOrg'], + default: 'NoTestRun', + }), + runtests: Flags.string({ + char: 'r', + description: 'runTests', default: [], - }), - ignoreerrors: flags.boolean({ - char: "o", - description: "ignoreErrors", - }), - ignorewarnings: flags.boolean({ - char: "g", - description: "ignoreWarnings", - }), - validateddeployrequestid: flags.id({ - char: "q", - description: "validateDeployRequestId", - exclusive: ["manifest", "metadata", "sourcepath", "checkonly", "testlevel", "runtests", "ignoreerrors", "ignorewarnings"], - }), - verbose: flags.builtin({ - description: "verbose", - }), - metadata: flags.array({ - char: "m", - description: "metadata", - exclusive: ["manifest", "sourcepath"], - }), - sourcepath: flags.array({ - char: "p", - description: "sourcePath", - exclusive: ["manifest", "metadata"], - }), - manifest: flags.filepath({ - char: "x", - description: "flagsLong.manifest", - exclusive: ["metadata", "sourcepath"], - }), - predestructivechanges: flags.filepath({ - description: "predestructivechanges", - dependsOn: ["manifest"], - }), - postdestructivechanges: flags.filepath({ - description: "postdestructivechanges", - dependsOn: ["manifest"], - }), - tracksource: flags.boolean({ - char: "t", - description: "tracksource", - exclusive: ["checkonly", "validateddeployrequestid"], - }), - forceoverwrite: flags.boolean({ - char: "f", - description: "forceoverwrite", - dependsOn: ["tracksource"], - }), - resultsdir: flags.directory({ - description: "resultsdir", - }), - coverageformatters: flags.array({ - description: "coverageformatters", - }), - junit: flags.boolean({ description: "junit" }), - checkcoverage: flags.boolean({ description: "Check Apex org coverage" }), - debug: flags.boolean({ + multiple: true, + }), + ignoreerrors: Flags.boolean({ + char: 'o', + description: 'ignoreErrors', + }), + ignorewarnings: Flags.boolean({ + char: 'g', + description: 'ignoreWarnings', + }), + validateddeployrequestid: Flags.string({ + char: 'q', + description: 'validateDeployRequestId', + exclusive: [ + 'manifest', + 'metadata', + 'sourcepath', + 'checkonly', + 'testlevel', + 'runtests', + 'ignoreerrors', + 'ignorewarnings', + ], + }), + verbose: Flags.boolean({ + description: 'verbose', + }), + metadata: Flags.string({ + char: 'm', + description: 'metadata', + exclusive: ['manifest', 'sourcepath'], + multiple: true, + }), + sourcepath: Flags.string({ + char: 'p', + description: 'sourcePath', + exclusive: ['manifest', 'metadata'], + multiple: true, + }), + manifest: Flags.file({ + char: 'x', + description: 'flagsLong.manifest', + exclusive: ['metadata', 'sourcepath'], + }), + predestructivechanges: Flags.file({ + description: 'predestructivechanges', + dependsOn: ['manifest'], + }), + postdestructivechanges: Flags.file({ + description: 'postdestructivechanges', + dependsOn: ['manifest'], + }), + tracksource: Flags.boolean({ + char: 't', + description: 'tracksource', + exclusive: ['checkonly', 'validateddeployrequestid'], + }), + forceoverwrite: Flags.boolean({ + char: 'f', + description: 'forceoverwrite', + dependsOn: ['tracksource'], + }), + resultsdir: Flags.directory({ + description: 'resultsdir', + }), + coverageformatters: Flags.string({ + description: 'coverageformatters', + multiple: true, + }), + junit: Flags.boolean({ description: 'junit' }), + checkcoverage: Flags.boolean({ description: 'Check Apex org coverage' }), + debug: Flags.boolean({ default: false, - description: "debug", + description: 'debug', }), - websocket: flags.string({ - description: "websocket", + websocket: Flags.string({ + description: 'websocket', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - protected xorFlags = ["manifest", "metadata", "sourcepath", "validateddeployrequestid"]; + protected xorFlags = ['manifest', 'metadata', 'sourcepath', 'validateddeployrequestid']; public async run(): Promise { + const { flags } = await this.parse(Deploy); + uxLog(this, c.red('This command will be removed by Salesforce in November 2024.')); + uxLog(this, c.red('Please migrate to command sf hardis project deploy start')); + uxLog( + this, + c.red( + 'See https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_mig_deploy_retrieve.htm' + ) + ); // Run pre deployment commands if defined - await executePrePostCommands("commandsPreDeploy", true); - const result = await wrapSfdxCoreCommand("sfdx force:source:deploy", this.argv, this, this.flags.debug); + const conn = flags["target-org"].getConnection(); + await executePrePostCommands('commandsPreDeploy', { success: true, checkOnly: flags.checkonly, conn: conn }); + const result = await wrapSfdxCoreCommand('sfdx force:source:deploy', this.argv, this, flags.debug); // Check org coverage if requested - if (this.flags.checkcoverage && result.stdout) { - const orgCoveragePercent = await extractOrgCoverageFromLog(result.stdout + result.stderr || ""); - const checkOnly = this.flags.checkonly || false; + const checkOnly = flags.checkonly || false; + if (flags.checkcoverage && result.stdout) { + const orgCoveragePercent = await extractOrgCoverageFromLog(result.stdout + result.stderr || ''); if (orgCoveragePercent) { try { - await checkDeploymentOrgCoverage(orgCoveragePercent, { check: checkOnly }); + await checkDeploymentOrgCoverage(Number(orgCoveragePercent), { check: checkOnly }); } catch (errCoverage) { await GitProvider.managePostPullRequestComment(); throw errCoverage; @@ -169,8 +212,9 @@ Notes: } } // Run post deployment commands if defined - await executePrePostCommands("commandsPostDeploy", process.exitCode === 0); + await executePrePostCommands('commandsPostDeploy', { success: process.exitCode === 0, checkOnly: checkOnly, conn: conn }); await GitProvider.managePostPullRequestComment(); return result; } } +/* jscpd:ignore-end */ \ No newline at end of file diff --git a/src/commands/hardis/source/push.ts b/src/commands/hardis/source/push.ts index b215a9be5..88fc721d0 100644 --- a/src/commands/hardis/source/push.ts +++ b/src/commands/hardis/source/push.ts @@ -1,45 +1,58 @@ -import { FlagsConfig, flags, SfdxCommand } from "@salesforce/command"; -import { Duration } from "@salesforce/kit"; -import { AnyJson } from "@salesforce/ts-types"; -import { wrapSfdxCoreCommand } from "../../../common/utils/wrapUtils"; +/* jscpd:ignore-start */ +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import c from 'chalk'; +import { AnyJson } from '@salesforce/ts-types'; +import { wrapSfdxCoreCommand } from '../../../common/utils/wrapUtils.js'; +import { uxLog } from '../../../common/utils/index.js'; -export default class Push extends SfdxCommand { +export default class Push extends SfCommand { public static readonly description = `sfdx-hardis wrapper for sfdx force:source:push that displays tips to solve deployment errors. [![Assisted solving of Salesforce deployments errors](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-deployment-errors.jpg)](https://nicolas.vuillamy.fr/assisted-solving-of-salesforce-deployments-errors-47f3666a9ed0) [See documentation of Salesforce command](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_force_source.htm#cli_reference_force_source_push) `; - protected static readonly flagsConfig: FlagsConfig = { - forceoverwrite: flags.boolean({ - char: "f", - description: "forceoverwrite", + public static readonly flags: any = { + forceoverwrite: Flags.boolean({ + char: 'f', + description: 'forceoverwrite', }), - wait: flags.minutes({ - char: "w", - default: Duration.minutes(60), - min: Duration.minutes(1), - description: "wait", + wait: Flags.integer({ + char: 'w', + default: 60, + min: 1, + description: 'wait', }), - ignorewarnings: flags.boolean({ - char: "g", - description: "ignorewarnings", + ignorewarnings: Flags.boolean({ + char: 'g', + description: 'ignorewarnings', }), - quiet: flags.builtin({ - description: "quiet", + quiet: Flags.boolean({ + description: 'quiet', }), - debug: flags.boolean({ + debug: Flags.boolean({ default: false, - description: "debug", + description: 'debug', }), - websocket: flags.string({ - description: "websocket", + websocket: Flags.string({ + description: 'websocket', }), + 'target-org': requiredOrgFlagWithDeprecations, }; - protected static requiresUsername = true; - protected static requiresProject = true; + + public static requiresProject = true; public async run(): Promise { - return await wrapSfdxCoreCommand("sfdx force:source:push", this.argv, this, this.flags.debug); + const { flags } = await this.parse(Push); + uxLog(this, c.red('This command will be removed by Salesforce in November 2024.')); + uxLog(this, c.red('Please migrate to command sf hardis project deploy start')); + uxLog( + this, + c.red( + 'See https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_mig_deploy_retrieve.htm' + ) + ); + return await wrapSfdxCoreCommand('sfdx force:source:push', this.argv, this, flags.debug); } } +/* jscpd:ignore-end */ \ No newline at end of file diff --git a/src/commands/hardis/source/retrieve.ts b/src/commands/hardis/source/retrieve.ts index 7070ad61e..5a0a69ceb 100644 --- a/src/commands/hardis/source/retrieve.ts +++ b/src/commands/hardis/source/retrieve.ts @@ -1,12 +1,13 @@ -import { flags, FlagsConfig, SfdxCommand } from "@salesforce/command"; -import { SfdxError } from "@salesforce/core"; -import * as c from "chalk"; -import { MetadataUtils } from "../../../common/metadata-utils"; -import { isCI } from "../../../common/utils"; -import { promptOrgUsernameDefault } from "../../../common/utils/orgUtils"; -import { wrapSfdxCoreCommand } from "../../../common/utils/wrapUtils"; +/* jscpd:ignore-start */ +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { SfError } from '@salesforce/core'; +import c from 'chalk'; +import { MetadataUtils } from '../../../common/metadata-utils/index.js'; +import { isCI, uxLog } from '../../../common/utils/index.js'; +import { promptOrgUsernameDefault } from '../../../common/utils/orgUtils.js'; +import { wrapSfdxCoreCommand } from '../../../common/utils/wrapUtils.js'; -export class SourceRetrieve extends SfdxCommand { +export class SourceRetrieve extends SfCommand { public static readonly description = `sfdx-hardis wrapper for sfdx force:source:retrieve - If no retrieve constraint is sent, as assisted menu will request the list of metadatas to retrieve @@ -16,83 +17,94 @@ export class SourceRetrieve extends SfdxCommand { `; public static readonly examples = []; public static readonly requiresProject = true; - public static readonly requiresUsername = true; - public static readonly flagsConfig: FlagsConfig = { - apiversion: flags.builtin({ + public static readonly flags: any = { + apiversion: Flags.orgApiVersion({ /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */ // @ts-ignore force char override for backward compat - char: "a", + char: 'a', }), - sourcepath: flags.array({ - char: "p", - description: "sourcePath", - longDescription: "sourcePath", - exclusive: ["manifest", "metadata"], + sourcepath: Flags.string({ + char: 'p', + description: 'sourcePath', + longDescription: 'sourcePath', + exclusive: ['manifest', 'metadata'], + multiple: true, }), - wait: flags.minutes({ - char: "w", - description: "wait", - longDescription: "wait", + wait: Flags.integer({ + char: 'w', + description: 'wait', }), - manifest: flags.filepath({ - char: "x", - description: "manifest", - longDescription: "manifest", - exclusive: ["metadata", "sourcepath"], + manifest: Flags.directory({ + char: 'x', + description: 'manifest', + exclusive: ['metadata', 'sourcepath'], }), - metadata: flags.array({ - char: "m", - description: "metadata", - longDescription: "metadata", - exclusive: ["manifest", "sourcepath"], + metadata: Flags.string({ + char: 'm', + description: 'metadata', + longDescription: 'metadata', + exclusive: ['manifest', 'sourcepath'], + multiple: true, }), - packagenames: flags.array({ - char: "n", - description: "packagenames", + packagenames: Flags.string({ + char: 'n', + description: 'packagenames', + multiple: true, }), - tracksource: flags.boolean({ - char: "t", - description: "tracksource", + tracksource: Flags.boolean({ + char: 't', + description: 'tracksource', }), - forceoverwrite: flags.boolean({ - char: "f", - description: "forceoverwrite", - dependsOn: ["tracksource"], + forceoverwrite: Flags.boolean({ + char: 'f', + description: 'forceoverwrite', + dependsOn: ['tracksource'], }), - verbose: flags.builtin({ - description: "verbose", + verbose: Flags.boolean({ + description: 'verbose', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: "debugMode", + description: 'debugMode', }), - websocket: flags.string({ - description: "websocket", + websocket: Flags.string({ + description: 'websocket', }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-org': requiredOrgFlagWithDeprecations, }; public async run(): Promise { + const { flags } = await this.parse(SourceRetrieve); + uxLog(this, c.red('This command will be removed by Salesforce in November 2024.')); + uxLog(this, c.red('Please migrate to command sf hardis project retrieve start')); + uxLog( + this, + c.red( + 'See https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_mig_deploy_retrieve.htm' + ) + ); const args = this.argv; // Manage user selection for metadatas - if (!isCI && !this.flags.sourcepath && !this.flags.manifest && !this.flags.metadata && !this.flags.packagenames) { + if (!isCI && !flags.sourcepath && !flags.manifest && !flags.metadata && !flags.packagenames) { const metadatas = await MetadataUtils.promptMetadataTypes(); - const metadataArg = metadatas.map((metadataType: any) => metadataType.xmlName).join(","); - args.push(...["-m", `"${metadataArg}"`]); + const metadataArg = metadatas.map((metadataType: any) => metadataType.xmlName).join(','); + args.push(...['-m', `"${metadataArg}"`]); } // Manage user selection for org - if (!isCI && !this.flags.targetusername) { - let orgUsername = this.org.getUsername(); + if (!isCI && !flags['target-org']) { + let orgUsername = (flags['target-org'] as any).getUsername(); orgUsername = await promptOrgUsernameDefault(this, orgUsername, { devHub: false, setDefault: false }); if (orgUsername) { - args.push(...["--targetusername", `"${orgUsername}"`]); + args.push(...['--target-org', `"${orgUsername}"`]); } else { - throw new SfdxError(c.yellow("For technical reasons, run again this command and select your org in the list :)")); + throw new SfError(c.yellow('For technical reasons, run again this command and select your org in the list :)')); } } - return await wrapSfdxCoreCommand("sfdx force:source:retrieve", args, this, this.flags.debug); + return await wrapSfdxCoreCommand('sfdx force:source:retrieve', args, this, flags.debug); } } +/* jscpd:ignore-end */ \ No newline at end of file diff --git a/src/commands/hardis/work/new.ts b/src/commands/hardis/work/new.ts index 32ff4a6f2..774571854 100644 --- a/src/commands/hardis/work/new.ts +++ b/src/commands/hardis/work/new.ts @@ -1,39 +1,44 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages, SfdxError } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as path from "path"; -import { MetadataUtils } from "../../../common/metadata-utils"; -import { checkGitClean, ensureGitBranch, execCommand, execSfdxJson, git, gitCheckOutRemote, uxLog } from "../../../common/utils"; -import { selectTargetBranch } from "../../../common/utils/gitUtils"; +import { SfCommand, Flags, optionalHubFlagWithDeprecations, optionalOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages, SfError } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import * as path from 'path'; +import { MetadataUtils } from '../../../common/metadata-utils/index.js'; +import { + checkGitClean, + ensureGitBranch, + execCommand, + execSfdxJson, + git, + gitCheckOutRemote, + uxLog, +} from '../../../common/utils/index.js'; +import { selectTargetBranch } from '../../../common/utils/gitUtils.js'; import { initApexScripts, initOrgData, initOrgMetadatas, initPermissionSetAssignments, installPackages, + makeSureOrgIsConnected, promptOrg, -} from "../../../common/utils/orgUtils"; -import { prompts } from "../../../common/utils/prompts"; -import { WebSocketClient } from "../../../common/websocketClient"; -import { getConfig, setConfig } from "../../../config"; -import SandboxCreate from "../org/create"; -import ScratchCreate from "../scratch/create"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +} from '../../../common/utils/orgUtils.js'; +import { prompts } from '../../../common/utils/prompts.js'; +import { WebSocketClient } from '../../../common/websocketClient.js'; +import { CONSTANTS, getConfig, setConfig } from '../../../config/index.js'; +import SandboxCreate from '../org/create.js'; +import ScratchCreate from '../scratch/create.js'; -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -export default class NewTask extends SfdxCommand { - public static title = "New work task"; +export default class NewTask extends SfCommand { + public static title = 'New work task'; public static description = `Assisted menu to start working on a Salesforce task. -Advanced instructions in [Create New Task documentation](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-create-new-task/) +Advanced instructions in [Create New Task documentation](${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-create-new-task/) At the end of the command, it will allow you to work on either a scratch org or a sandbox, depending on your choices. @@ -54,33 +59,28 @@ Under the hood, it can: - Use property \`dataPackages\` `; - public static examples = ["$ sfdx hardis:work:task:new"]; + public static examples = ['$ sf hardis:work:task:new']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), + 'target-dev-hub': optionalHubFlagWithDeprecations, + 'target-org': optionalOrgFlagWithDeprecations }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - protected static supportsDevhubUsername = true; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + public static requiresProject = true; protected targetBranch: string; protected debugMode = false; @@ -88,82 +88,96 @@ Under the hood, it can: /* jscpd:ignore-end */ public async run(): Promise { - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(NewTask); + this.debugMode = flags.debug || false; - uxLog(this, c.cyan("This tool will assist you to create a new task (dev or config) with Hardis CI/CD")); + uxLog(this, c.cyan('This tool will assist you to create a new task (dev or config) with Hardis CI/CD')); uxLog(this, c.cyan("When you don't know what to answer, you can let the default value and push ENTER")); // Make sure the git status is clean, to not delete uncommitted updates await checkGitClean({ allowStash: true }); - const config = await getConfig("project"); + const config = await getConfig('project'); this.targetBranch = await selectTargetBranch(); const defaultBranchPrefixChoices = [ { - title: "🏗️ Feature", - value: "features", + title: '🏗️ Feature', + value: 'features', description: "New feature, evolution of an existing feature... If you don't know, just select Feature", }, - { title: "🛠️ Debug", value: "fixes", description: "A bug has been identified and you are the right person to solve it !" }, + { + title: '🛠️ Debug', + value: 'fixes', + description: 'A bug has been identified and you are the right person to solve it !', + }, ]; const branchPrefixChoices = config.branchPrefixChoices || defaultBranchPrefixChoices; // Select project if multiple projects are defined in availableProjects .sfdx-hardis.yml property - let projectBranchPart = ""; + let projectBranchPart = ''; const availableProjects = config.availableProjects || []; if (availableProjects.length > 1) { const projectResponse = await prompts({ - type: "select", - name: "project", - message: c.cyanBright("Please select the project your task is for"), + type: 'select', + name: 'project', + message: c.cyanBright('Please select the project your task is for'), choices: availableProjects.map((project: string) => { return { title: project, value: project }; }), }); - projectBranchPart = projectResponse.project + "/"; + projectBranchPart = projectResponse.project + '/'; } // Request info to build branch name. ex features/config/MYTASK const response = await prompts([ { - type: "select", - name: "branch", - message: c.cyanBright("What is the type of the task you want to do ?"), + type: 'select', + name: 'branch', + message: c.cyanBright('What is the type of the task you want to do ?'), initial: 0, choices: branchPrefixChoices, }, { - type: "select", - name: "sources", - message: c.cyanBright("What type(s) of Salesforce updates will you have to perform for this task ?"), + type: 'select', + name: 'sources', + message: c.cyanBright('What type(s) of Salesforce updates will you have to perform for this task ?'), initial: 0, choices: [ - { title: "🥸 Configuration", value: "config", description: "You will update anything in the setup except apex code :)" }, - { title: "🤓 Development", value: "dev", description: "You are a developer who will do magic with Apex or Javascript !" }, { - title: "🥸🤓 Configuration + Development", - value: "dev", - description: "Like the unicorn you are, you will update configuration but also write code :)", + title: '🥸 Configuration', + value: 'config', + description: 'You will update anything in the setup except apex code :)', + }, + { + title: '🤓 Development', + value: 'dev', + description: 'You are a developer who will do magic with Apex or Javascript !', + }, + { + title: '🥸🤓 Configuration + Development', + value: 'dev', + description: 'Like the unicorn you are, you will update configuration but also write code :)', }, ], }, { - type: "text", - name: "taskName", + type: 'text', + name: 'taskName', message: c.cyanBright( - "What is the name of your new task ? (examples: JIRA123-webservice-get-account, T1000-flow-process-opportunity...). Please avoid accents or special characters", + 'What is the name of your new task ? (examples: JIRA123-webservice-get-account, T1000-flow-process-opportunity...). Please avoid accents or special characters' ), }, ]); // Checkout development main branch - const branchName = `${projectBranchPart}${response.branch || "features"}/${response.sources || "dev"}/${response.taskName.replace( - /[^a-zA-Z0-9 -]|\s/g, - "-", - )}`; - uxLog(this, c.cyan(`Checking out the most recent version of branch ${c.bold(this.targetBranch)} from git server...`)); + const branchName = `${projectBranchPart}${response.branch || 'features'}/${response.sources || 'dev' + }/${response.taskName.replace(/[^a-zA-Z0-9 -]|\s/g, '-')}`; + uxLog( + this, + c.cyan(`Checking out the most recent version of branch ${c.bold(this.targetBranch)} from git server...`) + ); await gitCheckOutRemote(this.targetBranch); // Pull latest version of target branch await git().pull(); @@ -173,48 +187,58 @@ Under the hood, it can: // Update config if necessary if (config.developmentBranch !== this.targetBranch && (config.availableTargetBranches || null) == null) { const updateDefaultBranchRes = await prompts({ - type: "confirm", - name: "value", - message: c.cyanBright(`Do you want to update your default target git branch to ${c.green(this.targetBranch)} ?`), + type: 'confirm', + name: 'value', + message: c.cyanBright( + `Do you want to update your default target git branch to ${c.green(this.targetBranch)} ?` + ), default: false, }); if (updateDefaultBranchRes.value === true) { - await setConfig("user", { developmentBranch: this.targetBranch }); + await setConfig('user', { developmentBranch: this.targetBranch }); } } // Update local user config files to store the target of the just created branch - const currentUserConfig = await getConfig("user"); + const currentUserConfig = await getConfig('user'); const localStorageBranchTargets = currentUserConfig.localStorageBranchTargets || {}; localStorageBranchTargets[branchName] = this.targetBranch; - await setConfig("user", { localStorageBranchTargets: localStorageBranchTargets }); + await setConfig('user', { localStorageBranchTargets: localStorageBranchTargets }); // Get allowed work org types from config if possible const allowedOrgTypes = config?.allowedOrgTypes || []; let selectedOrgType = allowedOrgTypes.length == 1 ? allowedOrgTypes[0] : null; // If necessary, Prompt if you want to use a scratch org or a tracked sandbox org, or no org - const orgTypeChoices = []; - if (allowedOrgTypes.includes("sandbox") || allowedOrgTypes.length === 0) { + const orgTypeChoices: any[] = []; + if (allowedOrgTypes.includes('sandbox') || allowedOrgTypes.length === 0) { + orgTypeChoices.push({ + title: '🌎 Sandbox org with source tracking', + value: 'sandbox', + description: + "Release manager told me that I can work on Sandboxes on my project so let's use fresh dedicated one", + }); + } + if (allowedOrgTypes.includes('scratch') || allowedOrgTypes.length === 0) { orgTypeChoices.push({ - title: "🌎 Sandbox org with source tracking", - value: "sandbox", - description: "Release manager told me that I can work on Sandboxes on my project so let's use fresh dedicated one", + title: '🪐 Scratch org', + value: 'scratch', + description: 'Scratch orgs are configured on my project so I want to create or reuse one', }); } - if (allowedOrgTypes.includes("scratch") || allowedOrgTypes.length === 0) { + if (flags['target-org'] && flags['target-org']?.getConnection()) { orgTypeChoices.push({ - title: "🪐 Scratch org", - value: "scratch", - description: "Scratch orgs are configured on my project so I want to create or reuse one", + title: `😎 Current org ${flags['target-org']?.getConnection().instanceUrl.replace("https://", "")}`, + value: 'currentOrg', + description: `Your default org with username ${flags['target-org']?.getUsername()} is already the org you want to use to work :)`, }); } orgTypeChoices.push({ title: "🤠 I'm hardcore, I don't need an org !", - value: "noOrg", - description: "You just want to play with XML and sfdx-hardis configuration, and you know what you are doing !", + value: 'noOrg', + description: 'You just want to play with XML and sfdx-hardis configuration, and you know what you are doing !', }); const orgTypeResponse = await prompts({ - type: "select", - name: "value", + type: 'select', + name: 'value', message: c.cyanBright(`Do you want to use a scratch org or a tracked sandbox org ?`), initial: 0, choices: orgTypeChoices, @@ -222,30 +246,30 @@ Under the hood, it can: selectedOrgType = orgTypeResponse.value; // Select or create org that user will work in - if (selectedOrgType === "scratch") { + if (selectedOrgType === 'scratch') { // scratch org - await this.selectOrCreateScratchOrg(branchName); - } else if (selectedOrgType === "sandbox") { + await this.selectOrCreateScratchOrg(branchName, flags); + } else if (selectedOrgType === 'sandbox' || selectedOrgType === 'currentOrg') { // source tracked sandbox - await this.selectOrCreateSandbox(branchName, config); + await this.selectOrCreateSandbox(branchName, config, flags, selectedOrgType); } else { uxLog(this, c.yellow(`No org selected... I hope you know what you are doing, don't break anything :)`)); } uxLog(this, c.cyan(`You are now ready to work in branch ${c.green(branchName)} :)`)); // Return an object to be displayed with --json - return { outputString: "Created new task" }; + return { outputString: 'Created new task' }; } // Select/Create scratch org - async selectOrCreateScratchOrg(branchName) { - const hubOrgUsername = this?.hubOrg?.getUsername(); - const scratchOrgList = await MetadataUtils.listLocalOrgs("scratch", { devHubUsername: hubOrgUsername }); + async selectOrCreateScratchOrg(branchName, flags) { + const hubOrgUsername = flags['target-dev-hub'].getUsername(); + const scratchOrgList = await MetadataUtils.listLocalOrgs('scratch', { devHubUsername: hubOrgUsername }); const currentOrg = await MetadataUtils.getCurrentOrg(); const baseChoices = [ { - title: c.yellow("🆕 Create new scratch org"), - value: "newScratchOrg", + title: c.yellow('🆕 Create new scratch org'), + value: 'newScratchOrg', description: "This will generate a new scratch org, and in a few minutes you'll be ready to work", }, ]; @@ -257,8 +281,8 @@ Under the hood, it can: }); } const scratchResponse = await prompts({ - type: "select", - name: "value", + type: 'select', + name: 'value', message: c.cyanBright(`Please select a scratch org to use for your branch ${c.green(branchName)}`), initial: 0, choices: [ @@ -272,135 +296,89 @@ Under the hood, it can: }), ], }); - if (scratchResponse.value === "newScratchOrg") { - await setConfig("user", { + if (scratchResponse.value === 'newScratchOrg') { + await setConfig('user', { scratchOrgAlias: null, scratchOrgUsername: null, }); // Check if DevHub is connected - await this.config.runHook("auth", { + await this.config.runHook('auth', { Command: this, devHub: true, scratch: false, }); - this.assignHubOrg(); // Create scratch org const config = await getConfig(); - const createResult = await ScratchCreate.run(["--forcenew", "--targetdevhubusername", config.devHubAlias]); + const createResult = await ScratchCreate.run(['--forcenew', '--targetdevhubusername', config.devHubAlias]); if (createResult == null) { - throw new SfdxError("Unable to create scratch org"); + throw new SfError('Unable to create scratch org'); } } else { // Set selected org as default org - await execCommand(`sfdx config:set defaultusername=${scratchResponse.value.username}`, this, { + await execCommand(`sf config set target-org=${scratchResponse.value.username}`, this, { output: true, fail: true, }); uxLog( this, - c.cyan(`Selected and opening scratch org ${c.green(scratchResponse.value.instanceUrl)} with user ${c.green(scratchResponse.value.username)}`), + c.cyan( + `Selected and opening scratch org ${c.green(scratchResponse.value.instanceUrl)} with user ${c.green( + scratchResponse.value.username + )}` + ) ); // Open selected org - await execSfdxJson("sf org open", this, { + await execSfdxJson('sf org open', this, { fail: true, output: false, debug: this.debugMode, }); // Trigger a status refresh on VsCode WebSocket Client - WebSocketClient.sendMessage({ event: "refreshStatus" }); + WebSocketClient.sendMessage({ event: 'refreshStatus' }); } } // Select or create sandbox - async selectOrCreateSandbox(branchName, config) { - const hubOrgUsername = this?.hubOrg?.getUsername(); - const sandboxOrgList = await MetadataUtils.listLocalOrgs("devSandbox", { devHubUsername: hubOrgUsername }); - const sandboxResponse = await prompts({ - type: "select", - name: "value", - message: c.cyanBright( - `Please select a sandbox org to use for your branch ${c.green( - branchName, - )} (if you want to avoid conflicts, you should often refresh your sandbox)`, - ), - initial: 0, - choices: [ - ...[ - { - title: c.yellow("🌐 Connect to a sandbox not appearing in this list"), - description: "Login in web browser to your source-tracked sandbox", - value: "connectSandbox", - }, - /* { - title: c.yellow("Create new sandbox from another sandbox or production org (ALPHA -> UNSTABLE, DO NOT USE YET)"), - value: "newSandbox", - }, */ - ], - ...sandboxOrgList.map((sandboxOrg: any) => { - return { - title: `☁️ Use sandbox org ${c.yellow(sandboxOrg.username || sandboxOrg.alias)}`, - description: sandboxOrg.instanceUrl, - value: sandboxOrg, - }; - }), - ], - }); - // Remove scratch org info in user config - await setConfig("user", { - scratchOrgAlias: null, - scratchOrgUsername: null, - }); - let orgUsername = ""; + async selectOrCreateSandbox(branchName, config, flags, selectedOrgType: "sandbox" | "currentOrg") { let openOrg = false; - // Connect to a sandbox - if (sandboxResponse.value === "connectSandbox") { - const slctdOrg = await promptOrg(this, { setDefault: true, devSandbox: true }); - orgUsername = slctdOrg.username; - } - // Create a new sandbox ( NOT WORKING YET, DO NOT USE) - else if (sandboxResponse.value === "newSandbox") { - const createResult = await SandboxCreate.run(); - if (createResult == null) { - throw new SfdxError("Unable to create sandbox org"); - } - orgUsername = createResult.username; + let orgUsername = ""; + if (selectedOrgType === "currentOrg") { + openOrg = true; + orgUsername = flags['target-org'].getUsername(); + await makeSureOrgIsConnected(orgUsername); } - // Selected sandbox from list else { - await execCommand(`sfdx config:set defaultusername=${sandboxResponse.value.username}`, this, { - output: true, - fail: true, - }); - orgUsername = sandboxResponse.value.username; - openOrg = true; + const promptRes = await this.promptSandbox(flags, branchName); + orgUsername = promptRes.orgUsername; + openOrg = promptRes.openOrg; } // Initialize / Update existing sandbox if required const initSandboxResponse = await prompts({ - type: "select", - name: "value", + type: 'select', + name: 'value', message: c.cyanBright( - `Do you want to update the sandbox according to git branch "${this.targetBranch}" current state ? (packages,SOURCES,permission set assignments,apex scripts,initial data)`, + `Do you want to update the sandbox according to git branch "${this.targetBranch}" current state ? (packages,SOURCES,permission set assignments,apex scripts,initial data)` ), choices: [ { - title: "🧑‍🤝‍🧑 No, continue working on my current sandbox state", - value: "no", - description: "Use if you are multiple users in the same SB, or have have uncommitted changes in your sandbox", + title: '🧑‍🤝‍🧑 No, continue working on my current sandbox state', + value: 'no', + description: 'Use if you are multiple users in the same SB, or have have uncommitted changes in your sandbox', }, { - title: "☢️ Yes, please try to update my sandbox !", - value: "init", + title: '☢️ Yes, please try to update my sandbox !', + value: 'init', description: `Integrate new updates from the parent branch "${this.targetBranch}" before working on your new task. WARNING: Will overwrite uncommitted changes in your org !`, }, ], }); - let initSandbox = initSandboxResponse.value === "init"; + let initSandbox = initSandboxResponse.value === 'init'; // Ask the user if he's really sure of what he's doing ! if (initSandbox) { const promptConfirm = await prompts({ - type: "confirm", + type: 'confirm', message: c.cyanBright( - `Are you really sure you want to update the dev sandbox with the state of git branch ${this.targetBranch} ? This will overwrite setup updates that you or other users have not committed yet`, + `Are you really sure you want to update the dev sandbox with the state of git branch ${this.targetBranch} ? This will overwrite setup updates that you or other users have not committed yet` ), }); initSandbox = promptConfirm.value === true; @@ -414,37 +392,53 @@ Under the hood, it can: } try { // Continue initialization even if push did not work... it could work and be not such a problem :) - uxLog(this, c.cyan("Resetting local sfdx tracking...")); - await execCommand(`sfdx force:source:tracking:clear --noprompt -u ${orgUsername}`, this, { fail: false, output: true }); + uxLog(this, c.cyan('Resetting local SF Cli tracking...')); + await execCommand(`sf project delete tracking --no-prompt -o ${orgUsername}`, this, { + fail: false, + output: true, + }); await initOrgMetadatas(config, orgUsername, orgUsername, {}, this.debugMode, { scratch: false }); } catch (e1) { initSourcesErr = e1; } await initPermissionSetAssignments(config.initPermissionSets || [], orgUsername); await initApexScripts(config.scratchOrgInitApexScripts || [], orgUsername); - await initOrgData(path.join(".", "scripts", "data", "ScratchInit"), orgUsername); + await initOrgData(path.join('.', 'scripts', 'data', 'ScratchInit'), orgUsername); } catch (e) { initSandboxErr = e; } if (initSandboxErr) { - uxLog(this, c.grey("Error(s) while initializing sandbox: " + initSandboxErr.message + "\n" + initSandboxErr.stack)); - uxLog(this, c.yellow("Your sandbox may not be completely initialized from git. You can send the error above to your release manager")); + uxLog( + this, + c.grey('Error(s) while initializing sandbox: ' + initSandboxErr.message + '\n' + initSandboxErr.stack) + ); + uxLog( + this, + c.yellow( + 'Your sandbox may not be completely initialized from git. You can send the error above to your release manager' + ) + ); } if (initSourcesErr) { - uxLog(this, c.grey("Error(s) while pushing sources to sandbox: " + initSourcesErr.message + "\n" + initSourcesErr.stack)); + uxLog( + this, + c.grey('Error(s) while pushing sources to sandbox: ' + initSourcesErr.message + '\n' + initSourcesErr.stack) + ); uxLog( this, c.yellow(`If you really want your sandbox to be up to date with branch ${c.bold(this.targetBranch)}, you may: - - ${c.bold("Fix the errors")} (probably by manually updating the target sandbox in setup), then run new task again and select again the same sandbox - - ${c.bold("Refresh your sandbox")} (ask your release manager if you don't know how) + - ${c.bold( + 'Fix the errors' + )} (probably by manually updating the target sandbox in setup), then run new task again and select again the same sandbox + - ${c.bold('Refresh your sandbox')} (ask your release manager if you don't know how) Else, you can start working now (but beware of conflicts ^^):) - `), + `) ); } } // Open of if not already open if (openOrg === true) { - await execSfdxJson("sf org open", this, { + await execSfdxJson('sf org open', this, { fail: true, output: false, debug: this.debugMode, @@ -452,6 +446,78 @@ Under the hood, it can: } // Trigger a status refresh on VsCode WebSocket Client - WebSocketClient.sendMessage({ event: "refreshStatus" }); + WebSocketClient.sendMessage({ event: 'refreshStatus' }); + } + + private async promptSandbox(flags: any, branchName: any) { + const hubOrgUsername = flags['target-dev-hub']?.getUsername(); + const sandboxOrgList = await MetadataUtils.listLocalOrgs('devSandbox', { devHubUsername: hubOrgUsername }); + const sandboxResponse = await prompts({ + type: 'select', + name: 'value', + message: c.cyanBright( + `Please select a sandbox org to use for your branch ${c.green( + branchName + )} (if you want to avoid conflicts, you should often refresh your sandbox)` + ), + initial: 0, + choices: [ + ...[ + { + title: c.yellow('🌐 Connect to a sandbox not appearing in this list'), + description: 'Login in web browser to your source-tracked sandbox', + value: 'connectSandbox', + }, + /* { + title: c.yellow("Create new sandbox from another sandbox or production org (ALPHA -> UNSTABLE, DO NOT USE YET)"), + value: "newSandbox", + }, */ + ], + ...sandboxOrgList.map((sandboxOrg: any) => { + return { + title: `☁️ Use sandbox org ${c.yellow(sandboxOrg.username || sandboxOrg.alias)}`, + description: sandboxOrg.instanceUrl, + value: sandboxOrg, + }; + }), + ], + }); + // Remove scratch org info in user config if necessary + const config = await getConfig("user"); + if (config.scratchOrgAlias || config.scratchOrgUsername) { + await setConfig('user', { + scratchOrgAlias: null, + scratchOrgUsername: null, + }); + } + + // Connect to a sandbox + let orgUsername = ''; + let openOrg = false; + if (sandboxResponse.value === 'connectSandbox') { + const slctdOrg = await promptOrg(this, { setDefault: true, devSandbox: true }); + orgUsername = slctdOrg.username; + } + + // Create a new sandbox ( NOT WORKING YET, DO NOT USE) + else if (sandboxResponse.value === 'newSandbox') { + const createResult = await SandboxCreate.run(); + if (createResult == null) { + throw new SfError('Unable to create sandbox org'); + } + orgUsername = (createResult as any).username; + } + + // Selected sandbox from list + else { + await makeSureOrgIsConnected(sandboxResponse.value); + await execCommand(`sf config set target-org=${sandboxResponse.value.username}`, this, { + output: true, + fail: true, + }); + orgUsername = sandboxResponse.value.username; + openOrg = true; + } + return { orgUsername, openOrg }; } } diff --git a/src/commands/hardis/work/refresh.ts b/src/commands/hardis/work/refresh.ts index 37e3d704d..cba8e8677 100644 --- a/src/commands/hardis/work/refresh.ts +++ b/src/commands/hardis/work/refresh.ts @@ -1,56 +1,45 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages, SfdxError } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { execCommand, getCurrentGitBranch, git, uxLog } from "../../../common/utils"; -import { forceSourcePull, forceSourcePush } from "../../../common/utils/deployUtils"; -import { prompts } from "../../../common/utils/prompts"; -import { getConfig } from "../../../config"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages, SfError } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { execCommand, getCurrentGitBranch, git, uxLog } from '../../../common/utils/index.js'; +import { forceSourcePull, forceSourcePush } from '../../../common/utils/deployUtils.js'; +import { prompts } from '../../../common/utils/prompts.js'; +import { getConfig } from '../../../config/index.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class RefreshTask extends SfCommand { + public static title = 'Refresh work task'; -export default class RefreshTask extends SfdxCommand { - public static title = "Refresh work task"; + public static description = messages.getMessage('refreshWorkTask'); - public static description = messages.getMessage("refreshWorkTask"); - - public static examples = ["$ sfdx hardis:work:refresh"]; + public static examples = ['$ sf hardis:work:refresh']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - nopull: flags.boolean({ - char: "n", + public static flags: any = { + nopull: Flags.boolean({ + char: 'n', default: false, - description: "No scratch pull before save (careful if you use that!)", + description: 'No scratch pull before save (careful if you use that!)', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), - }; - - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + 'target-org': requiredOrgFlagWithDeprecations, + }; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default + public static requiresProject = true; protected debugMode = false; protected noPull = false; @@ -58,26 +47,32 @@ export default class RefreshTask extends SfdxCommand { /* jscpd:ignore-end */ public async run(): Promise { - const config = await getConfig("project"); - if (config.get("EXPERIMENTAL", "") !== "true") { - const msg = "This command is not stable enough to be used. Use EXPERIMENTAL=true to use it anyway"; + const { flags } = await this.parse(RefreshTask); + const config = await getConfig('project'); + if (config.get('EXPERIMENTAL', '') !== 'true') { + const msg = 'This command is not stable enough to be used. Use EXPERIMENTAL=true to use it anyway'; uxLog(this, c.yellow(msg)); return { outputString: msg }; } - this.noPull = this.flags.nopull || false; - uxLog(this, c.cyan("This command will refresh your git branch and your org with the content of another git branch")); + this.noPull = flags.nopull || false; + uxLog( + this, + c.cyan('This command will refresh your git branch and your org with the content of another git branch') + ); // Verify that the user saved his/her work before merging another branch const savePromptRes = await prompts({ - type: "select", - message: c.cyanBright(`This is a SENSITIVE OPERATION. Did you run ${c.green("hardis:work:save")} BEFORE running this command ?`), - name: "value", + type: 'select', + message: c.cyanBright( + `This is a SENSITIVE OPERATION. Did you run ${c.green('hardis:work:save')} BEFORE running this command ?` + ), + name: 'value', choices: [ { - title: "Yes I did save my current updates before merging updates from others !", + title: 'Yes I did save my current updates before merging updates from others !', value: true, }, - { title: "No, I did not, I will do that right now", value: false }, + { title: 'No, I did not, I will do that right now', value: false }, ], }); if (savePromptRes.value !== true) { @@ -85,7 +80,7 @@ export default class RefreshTask extends SfdxCommand { } // Select branch to merge const localBranch = await getCurrentGitBranch(); - const branchSummary = await git().branch(["-r"]); + const branchSummary = await git().branch(['-r']); const branchChoices = [ { title: `${config.developmentBranch} (recommended)`, @@ -93,66 +88,69 @@ export default class RefreshTask extends SfdxCommand { }, ]; for (const branchName of Object.keys(branchSummary.branches)) { - const branchNameLocal = branchName.replace("origin/", ""); + const branchNameLocal = branchName.replace('origin/', ''); if (branchNameLocal !== config.developmentBranch) { branchChoices.push({ title: branchNameLocal, value: branchNameLocal }); } } const branchRes = await prompts({ - type: "select", + type: 'select', message: `Please select the branch that you want to merge in your current branch ${c.green(localBranch)}`, - name: "value", + name: 'value', choices: branchChoices, }); this.mergeBranch = branchRes.value; // Run refresh of local branch try { - return await this.runRefresh(localBranch); + return await this.runRefresh(localBranch, flags); } catch (e) { - uxLog(this, c.yellow("There has been a merge conflict or a technical error, please contact a Developer for help !")); + uxLog( + this, + c.yellow('There has been a merge conflict or a technical error, please contact a Developer for help !') + ); throw e; } } - private async runRefresh(localBranch): Promise { - this.debugMode = this.flags.debug || false; + private async runRefresh(localBranch, flags): Promise { + this.debugMode = flags.debug || false; uxLog( this, c.cyan( `sfdx-hardis will refresh your local branch ${c.green(localBranch)} and your local scratch org ${c.green( - this.org.getUsername(), - )} with the latest state of ${c.green(this.mergeBranch)}`, - ), + flags['target-org'].getUsername() + )} with the latest state of ${c.green(this.mergeBranch)}` + ) ); if (localBranch === this.mergeBranch) { - throw new SfdxError("[sfdx-hardis] You can not refresh from the same branch"); + throw new SfError('[sfdx-hardis] You can not refresh from the same branch'); } // Pull from scratch org if (this.noPull) { uxLog(this, c.cyan(`Skipped pull from scratch org`)); } else { - uxLog(this, c.cyan(`Pulling sources from scratch org ${this.org.getUsername()}...`)); - await forceSourcePull(this.org.getUsername(), this.debugMode); + uxLog(this, c.cyan(`Pulling sources from scratch org ${flags['target-org'].getUsername()}...`)); + await forceSourcePull(flags['target-org'].getUsername(), this.debugMode); } // Stash uxLog( this, c.cyan( - `Stashing your uncommitted updates in ${c.green(localBranch)} before merging ${c.green(this.mergeBranch)} into your local branch ${c.green( - localBranch, - )}...`, - ), + `Stashing your uncommitted updates in ${c.green(localBranch)} before merging ${c.green( + this.mergeBranch + )} into your local branch ${c.green(localBranch)}...` + ) ); - const stashResult = await git({ output: true }).stash(["save", `[sfdx-hardis] Stash of ${localBranch}`]); - const stashed = stashResult.includes("Saved working directory"); + const stashResult = await git({ output: true }).stash(['save', `[sfdx-hardis] Stash of ${localBranch}`]); + const stashed = stashResult.includes('Saved working directory'); // Pull most recent version of development branch uxLog(this, c.cyan(`Pulling most recent version of remote branch ${c.green(this.mergeBranch)}...`)); await git({ output: true }).fetch(); - await git({ output: true }).checkout(this.mergeBranch); + await git({ output: true }).checkout(this.mergeBranch || ''); const pullRes = await git({ output: true }).pull(); // Go back to current work branch await git({ output: true }).checkout(localBranch); @@ -162,21 +160,22 @@ export default class RefreshTask extends SfdxCommand { output: true, }) ).stdout; - const localRef = (await execCommand(`git merge-base ${this.mergeBranch} ${localBranch}`, this, { output: true })).stdout; + const localRef = (await execCommand(`git merge-base ${this.mergeBranch} ${localBranch}`, this, { output: true })) + .stdout; // Merge into current branch if necessary if (pullRes.summary.changes > 0 || mergeRef !== localRef) { // Create new commit from merge uxLog(this, c.cyan(`Creating a merge commit of ${c.green(this.mergeBranch)} within ${c.green(localBranch)}...`)); - let mergeSummary = await git({ output: true }).merge([this.mergeBranch]); + let mergeSummary = await git({ output: true }).merge([this.mergeBranch || '']); while (mergeSummary.failed) { const mergeResult = await prompts({ - type: "select", - name: "value", + type: 'select', + name: 'value', message: c.cyanBright( - "There are merge conflicts, please solve them, then select YES here. Otherwise, exit the script and call a developer for help :)", + 'There are merge conflicts, please solve them, then select YES here. Otherwise, exit the script and call a developer for help :)' ), choices: [ - { value: true, title: "If finished to merge conflicts" }, + { value: true, title: 'If finished to merge conflicts' }, { value: false, title: "I can't merge conflicts, I give up for now", @@ -184,24 +183,29 @@ export default class RefreshTask extends SfdxCommand { ], }); if (mergeResult.value === false) { - uxLog(this, "Refresh script stopped by user"); + uxLog(this, 'Refresh script stopped by user'); process.exit(0); } - mergeSummary = await git({ output: true }).merge(["--continue"]); + mergeSummary = await git({ output: true }).merge(['--continue']); } } else { - uxLog(this, c.cyan(`Local branch ${c.green(localBranch)} is already up to date with ${c.green(this.mergeBranch)}`)); + uxLog( + this, + c.cyan(`Local branch ${c.green(localBranch)} is already up to date with ${c.green(this.mergeBranch)}`) + ); } // Restoring stash if (stashed) { uxLog(this, c.cyan(`Restoring stash into your local branch ${c.green(localBranch)}...`)); - await git({ output: true }).stash(["pop"]); + await git({ output: true }).stash(['pop']); } // Push new branch state to scratch org - await forceSourcePush(this.org.getUsername(), this, this.debugMode, { conn: this.org.getConnection() }); + await forceSourcePush(flags['target-org'].getUsername(), this, this.debugMode, { + conn: flags['target-org'].getConnection(), + }); // Return an object to be displayed with --json - return { outputString: "Refreshed the task & org" }; + return { outputString: 'Refreshed the task & org' }; } } diff --git a/src/commands/hardis/work/resetselection.ts b/src/commands/hardis/work/resetselection.ts index 7d688a04f..77f40f101 100644 --- a/src/commands/hardis/work/resetselection.ts +++ b/src/commands/hardis/work/resetselection.ts @@ -1,78 +1,70 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages, SfdxError } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import { execCommand, getCurrentGitBranch, git, uxLog } from "../../../common/utils"; -import { selectTargetBranch } from "../../../common/utils/gitUtils"; -import { setConfig } from "../../../config"; -import { prompts } from "../../../common/utils/prompts"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages, SfError } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import { execCommand, getCurrentGitBranch, git, uxLog } from '../../../common/utils/index.js'; +import { selectTargetBranch } from '../../../common/utils/gitUtils.js'; +import { setConfig } from '../../../config/index.js'; +import { prompts } from '../../../common/utils/prompts.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class RebuildSelection extends SfdxCommand { - public static title = "Select again"; +export default class RebuildSelection extends SfCommand { + public static title = 'Select again'; public static description = `Resets the selection that we want to add in the merge request Calls a soft git reset behind the hood `; - public static examples = ["$ sfdx hardis:work:resetsave"]; + public static examples = ['$ sf hardis:work:resetsave']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - debug: flags.boolean({ - char: "d", + public static flags: any = { + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), - }; - - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + 'target-org': requiredOrgFlagWithDeprecations, + }; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default + public static requiresProject = true; protected debugMode = false; /* jscpd:ignore-end */ public async run(): Promise { - this.debugMode = this.flags.debug || false; + const { flags } = await this.parse(RebuildSelection); + this.debugMode = flags.debug || false; - const targetBranch = await selectTargetBranch({ message: "Please select the target branch of your current or future merge request" }); + const targetBranch = await selectTargetBranch({ + message: 'Please select the target branch of your current or future merge request', + }); uxLog(this, c.cyan(`This script will rebuild selection that you will want to merge into ${c.green(targetBranch)}`)); const currentGitBranch = await getCurrentGitBranch(); if (currentGitBranch === targetBranch) { - throw new SfdxError(c.red("[sfdx-hardis] You can not revert commits of a protected branch !")); + throw new SfError(c.red('[sfdx-hardis] You can not revert commits of a protected branch !')); } // Ask user to confirm const confirm = await prompts({ - type: "confirm", + type: 'confirm', message: `This command will git reset (soft) your branch ${currentGitBranch}. You will need to select and commit again your files. Are you sure ?`, }); if (confirm.value === false) { - throw new SfdxError(c.red("[sfdx-hardis] Cancelled by user")); + throw new SfError(c.red('[sfdx-hardis] Cancelled by user')); } // List all commits since the branch creation @@ -80,20 +72,20 @@ Calls a soft git reset behind the hood const commitstoReset = logResult.all; const commitsToResetNumber = commitstoReset.length; // Reset commits - await git({ output: true }).reset(["--soft", `HEAD~${commitsToResetNumber}`]); - await setConfig("user", { canForcePush: true }); + await git({ output: true }).reset(['--soft', `HEAD~${commitsToResetNumber}`]); + await setConfig('user', { canForcePush: true }); // unstage files - await execCommand("git reset", this, { + await execCommand('git reset', this, { output: true, fail: true, debug: this.debugMode, }); // await git({output:true}).reset(); does not work, let's use direct command - await git({ output: true }).checkout(["--", "manifest/package.xml"]); - await git({ output: true }).checkout(["--", "manifest/destructiveChanges.xml"]); + await git({ output: true }).checkout(['--', 'manifest/package.xml']); + await git({ output: true }).checkout(['--', 'manifest/destructiveChanges.xml']); await git({ output: true }).status(); - uxLog(this, c.cyan("The following items are not available for selection")); - uxLog(this, c.cyan("Selection has been reset")); + uxLog(this, c.cyan('The following items are not available for selection')); + uxLog(this, c.cyan('Selection has been reset')); // Return an object to be displayed with --json - return { outputString: "Reset selection pocessed" }; + return { outputString: 'Reset selection pocessed' }; } } diff --git a/src/commands/hardis/work/save.ts b/src/commands/hardis/work/save.ts index 443ca74be..ee72d1b32 100644 --- a/src/commands/hardis/work/save.ts +++ b/src/commands/hardis/work/save.ts @@ -1,40 +1,49 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as open from "open"; -import * as path from "path"; -import { createTempDir, execCommand, getCurrentGitBranch, git, gitHasLocalUpdates, normalizeFileStatusPath, uxLog } from "../../../common/utils"; -import { exportData } from "../../../common/utils/dataUtils"; -import { forceSourcePull } from "../../../common/utils/deployUtils"; -import { callSfdxGitDelta, getGitDeltaScope, selectTargetBranch } from "../../../common/utils/gitUtils"; -import { prompts } from "../../../common/utils/prompts"; -import { parseXmlFile, writeXmlFile } from "../../../common/utils/xmlUtils"; -import { WebSocketClient } from "../../../common/websocketClient"; -import { CONSTANTS, getConfig, setConfig } from "../../../config"; -import CleanReferences from "../project/clean/references"; -import CleanXml from "../project/clean/xml"; - -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); - -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); - -export default class SaveTask extends SfdxCommand { - public static title = "Save work task"; +import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import c from 'chalk'; +import fs from 'fs-extra'; +import open from 'open'; +import * as path from 'path'; +import { + createTempDir, + execCommand, + getCurrentGitBranch, + git, + gitHasLocalUpdates, + normalizeFileStatusPath, + uxLog, +} from '../../../common/utils/index.js'; +import { exportData } from '../../../common/utils/dataUtils.js'; +import { forceSourcePull } from '../../../common/utils/deployUtils.js'; +import { callSfdxGitDelta, getGitDeltaScope, selectTargetBranch } from '../../../common/utils/gitUtils.js'; +import { prompts } from '../../../common/utils/prompts.js'; +import { + appendPackageXmlFilesContent, + parseXmlFile, + removePackageXmlFilesContent, + writeXmlFile, +} from '../../../common/utils/xmlUtils.js'; +import { WebSocketClient } from '../../../common/websocketClient.js'; +import { CONSTANTS, getConfig, setConfig } from '../../../config/index.js'; +import CleanReferences from '../project/clean/references.js'; +import CleanXml from '../project/clean/xml.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); + +export default class SaveTask extends SfCommand { + public static title = 'Save work task'; public static description = `When a work task is completed, guide user to create a merge request -Advanced instructions in [Publish a task](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-publish-task/) +Advanced instructions in [Publish a task](${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-publish-task/) - Generate package-xml diff using sfdx-git-delta - Automatically update \`manifest/package.xml\` and \`manifest/destructiveChanges.xml\` according to the committed updates - Automatically Clean XML files using \`.sfdx-hardis.yml\` properties - - \`autocleantypes\`: List of auto-performed sources cleanings, available on command [hardis:project:clean:references](https://sfdx-hardis.cloudity.com/hardis/project/clean/references/) + - \`autocleantypes\`: List of auto-performed sources cleanings, available on command [hardis:project:clean:references](${CONSTANTS.DOC_URL_ROOT}/hardis/project/clean/references/) - \`autoRemoveUserPermissions\`: List of userPermission to automatically remove from profile metadatas Example: @@ -61,57 +70,50 @@ autoRemoveUserPermissions: - Push commit to server `; - public static examples = ["$ sfdx hardis:work:task:save", "$ sfdx hardis:work:task:save --nopull --nogit --noclean"]; + public static examples = ['$ sf hardis:work:task:save', '$ sf hardis:work:task:save --nopull --nogit --noclean']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - nopull: flags.boolean({ - char: "n", + public static flags: any = { + nopull: Flags.boolean({ + char: 'n', default: false, - description: "No scratch pull before save", + description: 'No scratch pull before save', }), - nogit: flags.boolean({ - char: "g", + nogit: Flags.boolean({ + char: 'g', default: false, - description: "No automated git operations", + description: 'No automated git operations', }), - noclean: flags.boolean({ - char: "c", + noclean: Flags.boolean({ + char: 'c', default: false, - description: "No cleaning of local sources", + description: 'No cleaning of local sources', }), - auto: flags.boolean({ + auto: Flags.boolean({ default: false, - description: "No user prompts (when called from CI for example)", + description: 'No user prompts (when called from CI for example)', }), - targetbranch: flags.string({ - description: "Name of the Merge Request target branch. Will be guessed or prompted if not provided.", + targetbranch: Flags.string({ + description: 'Name of the Merge Request target branch. Will be guessed or prompted if not provided.', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), - }; - - // Comment this out if your command does not require an org username - protected static requiresUsername = true; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = true; + 'target-org': requiredOrgFlagWithDeprecations, + }; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default + public static requiresProject = true; // List required plugins, their presence will be tested before running the command - protected static requiresSfdxPlugins = ["sfdx-essentials", "sfdx-git-delta"]; + protected static requiresSfdxPlugins = ['sfdx-git-delta']; protected debugMode = false; protected noPull = false; @@ -120,43 +122,50 @@ autoRemoveUserPermissions: protected auto = false; protected gitUrl: string; protected currentBranch: string; - protected targetBranch: string; + protected targetBranch: string | null; /* jscpd:ignore-end */ public async run(): Promise { - this.noPull = this.flags.nopull || false; - this.noGit = this.flags.nogit || false; - this.noClean = this.flags.noclean || false; - this.auto = this.flags.auto || false; - this.targetBranch = this.flags.targetbranch || null; - this.debugMode = this.flags.debug || false; - const localBranch = await getCurrentGitBranch(); + const { flags } = await this.parse(SaveTask); + this.noPull = flags.nopull || false; + this.noGit = flags.nogit || false; + this.noClean = flags.noclean || false; + this.auto = flags.auto || false; + this.targetBranch = flags.targetbranch || null; + this.debugMode = flags.debug || false; + const localBranch = (await getCurrentGitBranch()) || ''; // Define current and target branches - this.gitUrl = await git().listRemote(["--get-url"]); - this.currentBranch = await getCurrentGitBranch(); + this.gitUrl = await git().listRemote(['--get-url']); + this.currentBranch = (await getCurrentGitBranch()) || ''; if (this.targetBranch == null) { - const userConfig = await getConfig("user"); + const userConfig = await getConfig('user'); if (userConfig?.localStorageBranchTargets && userConfig?.localStorageBranchTargets[localBranch]) { this.targetBranch = userConfig?.localStorageBranchTargets[localBranch]; } } if (this.targetBranch == null) { - this.targetBranch = await selectTargetBranch({ message: "Please select the target branch of your Merge Request" }); + this.targetBranch = await selectTargetBranch({ + message: 'Please select the target branch of your Merge Request', + }); } // User log info uxLog( this, - c.cyan(`This script will prepare the merge request from your local branch ${c.green(localBranch)} to remote ${c.green(this.targetBranch)}`), + c.cyan( + `This script will prepare the merge request from your local branch ${c.green(localBranch)} to remote ${c.green( + this.targetBranch + )}` + ) ); // Make sure git is clean before starting operations await this.cleanGitStatus(); // Make sure commit is ready before starting operations - const orgPullStateRes = await this.ensureCommitIsReady(); + const orgPullStateRes = await this.ensureCommitIsReady(flags); if (orgPullStateRes && orgPullStateRes.outputString) { return orgPullStateRes; } - // Update package.xml files using sfdx git delta + // Update package.xml files using sfdx-git-delta const gitStatusWithConfig = await this.upgradePackageXmlFilesWithDelta(); // Apply cleaning on sources await this.applyCleaningOnSources(); @@ -167,10 +176,14 @@ autoRemoveUserPermissions: await this.manageCommitPush(gitStatusWithConfig, gitStatusAfterDeployPlan); // Merge request - uxLog(this, c.cyan(`If your work is ${c.bold("completed")}, you can create a ${c.bold("merge request")}:`)); + uxLog(this, c.cyan(`If your work is ${c.bold('completed')}, you can create a ${c.bold('merge request')}:`)); uxLog( this, - c.cyan(`- click on the link in the upper text, below ${c.italic("To create a merge request for " + this.currentBranch + ", visit")}`), + c.cyan( + `- click on the link in the upper text, below ${c.italic( + 'To create a merge request for ' + this.currentBranch + ', visit' + )}` + ) ); uxLog(this, c.cyan(`- or manually create the merge request on repository UI: ${c.green(this.gitUrl)}`)); // const remote = await git().listRemote(); @@ -180,11 +193,11 @@ autoRemoveUserPermissions: this, c.cyan( c.bold( - `${c.yellow("When your Merge Request will have been merged:")} - - ${c.yellow("DO NOT REUSE THE SAME BRANCH")} - - Use New task menu (sfdx hardis:work:new), even if you work in the same sandbox or scratch org :)`, - ), - ), + `${c.yellow('When your Merge Request will have been merged:')} + - ${c.yellow('DO NOT REUSE THE SAME BRANCH')} + - Use New task menu (sf hardis:work:new), even if you work in the same sandbox or scratch org :)` + ) + ) ); uxLog( this, @@ -192,19 +205,19 @@ autoRemoveUserPermissions: `If you are working with a ticketing system like JIRA, try to add the FULL URL of the tickets in the MR/PR description - Good example: https://sfdx-hardis.atlassian.net/browse/CLOUDITY-4 - Less good example but will work anyway on most cases: CLOUDITY-4 -`, - ), +` + ) ); uxLog( this, c.cyan( `Merge request documentation is available here -> ${c.bold( - "https://sfdx-hardis.cloudity.com/salesforce-ci-cd-publish-task/#create-merge-request", - )}`, - ), + `${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-publish-task/#create-merge-request` + )}` + ) ); // Return an object to be displayed with --json - return { outputString: "Saved the task" }; + return { outputString: 'Saved the task' }; } // Clean git status @@ -217,77 +230,85 @@ autoRemoveUserPermissions: let gitStatusInit = await git().status(); // Cancel merge if ongoing merge if (gitStatusInit.conflicted.length > 0) { - await git({ output: true }).merge(["--abort"]); + await git({ output: true }).merge(['--abort']); gitStatusInit = await git().status(); } // Unstage files if (gitStatusInit.staged.length > 0) { - await execCommand("git reset", this, { output: true, fail: true }); + await execCommand('git reset', this, { output: true, fail: true }); } } - private async ensureCommitIsReady() { - // Manage force:source:pull from scratch org + private async ensureCommitIsReady(flags) { + // Manage project deploy start from scratch org if (this.noPull || this.auto) { // Skip pull - uxLog(this, c.cyan(`Skipped force:source:pull from scratch org`)); + uxLog(this, c.cyan(`Skipped sf project:retrieve:start from scratch org`)); return; } // Request user if commit is ready const commitReadyRes = await prompts({ - type: "select", - name: "value", - message: c.cyanBright("Have you already committed the updated metadata you want to deploy ?"), + type: 'select', + name: 'value', + message: c.cyanBright('Have you already committed the updated metadata you want to deploy ?'), choices: [ { - title: "😎 Yes, my commit(s) is ready ! I staged my files then created one or multiple commits !", - value: "commitReady", + title: '😎 Yes, my commit(s) is ready ! I staged my files then created one or multiple commits !', + value: 'commitReady', description: "You have already pulled updates from your org (or locally updated the files if you're a nerd) then staged your files and created a commit", }, { - title: "😐 No, please pull my latest updates from my org so I can commit my metadatas", - value: "pleasePull", - description: "Pull latest updates from org so then you can stage files and create your commit", + title: '😐 No, please pull my latest updates from my org so I can commit my metadatas', + value: 'pleasePull', + description: 'Pull latest updates from org so then you can stage files and create your commit', }, { - title: "😱 What is a commit ? What does mean pull ? Help !", - value: "help", - description: "Don't panic, just click on the link that will appear in the console (CTRL + Click) and then you will know :)", + title: '😱 What is a commit ? What does mean pull ? Help !', + value: 'help', + description: + "Don't panic, just click on the link that will appear in the console (CTRL + Click) and then you will know :)", }, ], }); - if (commitReadyRes.value === "pleasePull") { - // Process force:source:pull - uxLog(this, c.cyan(`Pulling sources from scratch org ${this.org.getUsername()}...`)); - await forceSourcePull(this.org.getUsername(), this.debugMode); - uxLog(this, c.cyan(`Sources has been pulled from ${this.org.getUsername()}, now you can stage and commit your updates !`)); - return { outputString: "Pull performed" }; - } else if (commitReadyRes.value === "help") { + if (commitReadyRes.value === 'pleasePull') { + // Process sf project retrieve start + uxLog(this, c.cyan(`Pulling sources from scratch org ${flags['target-org'].getUsername()}...`)); + await forceSourcePull(flags['target-org'].getUsername(), this.debugMode); + uxLog( + this, + c.cyan( + `Sources has been pulled from ${flags[ + 'target-org' + ].getUsername()}, now you can stage and commit your updates !` + ) + ); + return { outputString: 'Pull performed' }; + } else if (commitReadyRes.value === 'help') { // Show pull commit stage help - const commitHelpUrl = "https://sfdx-hardis.cloudity.com/hardis/scratch/pull/"; + const commitHelpUrl = `${CONSTANTS.DOC_URL_ROOT}/hardis/scratch/pull/`; uxLog(this, c.cyan(`Opening help at ${commitHelpUrl} ...`)); await open(commitHelpUrl, { wait: true }); - return { outputString: "Help displayed at " }; + return { outputString: 'Help displayed at ' }; } // Extract data from org const dataSources = [ { - label: "Email templates", - dataPath: "./scripts/data/EmailTemplate", + label: 'Email templates', + dataPath: './scripts/data/EmailTemplate', }, ]; for (const dataSource of dataSources) { if (fs.existsSync(dataSource.dataPath)) { const exportDataRes = await prompts({ - type: "confirm", - name: "value", + type: 'confirm', + name: 'value', message: c.cyan(`Did you update ${c.green(dataSource.label)} and want to export related data ?`), }); if (exportDataRes.value === true) { await exportData(dataSource.dataPath, this, { - sourceUsername: this.org.getUsername(), + sourceUsername: flags['target-org'].getUsername(), }); } } @@ -296,24 +317,28 @@ autoRemoveUserPermissions: private async upgradePackageXmlFilesWithDelta() { // Retrieving info about current branch latest commit and master branch latest commit - const gitDeltaScope = await getGitDeltaScope(this.currentBranch, this.targetBranch); + const gitDeltaScope = await getGitDeltaScope(this.currentBranch, this.targetBranch || ''); // Build package.xml delta between most recent commit and developpement - const localPackageXml = path.join("manifest", "package.xml"); - const toCommitMessage = gitDeltaScope.toCommit ? gitDeltaScope.toCommit.message : ""; + const localPackageXml = path.join('manifest', 'package.xml'); + const toCommitMessage = gitDeltaScope.toCommit ? gitDeltaScope.toCommit.message : ''; uxLog( this, - c.cyan(`Calculating package.xml diff from [${c.green(this.targetBranch)}] to [${c.green(this.currentBranch)} - ${c.green(toCommitMessage)}]`), + c.cyan( + `Calculating package.xml diff from [${c.green(this.targetBranch)}] to [${c.green( + this.currentBranch + )} - ${c.green(toCommitMessage)}]` + ) ); const tmpDir = await createTempDir(); const packageXmlResult = await callSfdxGitDelta( gitDeltaScope.fromCommit, gitDeltaScope.toCommit ? gitDeltaScope.toCommit.hash : gitDeltaScope.fromCommit, - tmpDir, + tmpDir ); if (packageXmlResult.status === 0) { // Upgrade local destructivePackage.xml - const localDestructiveChangesXml = path.join("manifest", "destructiveChanges.xml"); + const localDestructiveChangesXml = path.join('manifest', 'destructiveChanges.xml'); if (!fs.existsSync(localDestructiveChangesXml)) { // Create default destructiveChanges.xml if not defined const blankDestructiveChanges = ` @@ -323,50 +348,46 @@ autoRemoveUserPermissions: `; await fs.writeFile(localDestructiveChangesXml, blankDestructiveChanges); } - const diffDestructivePackageXml = path.join(tmpDir, "destructiveChanges", "destructiveChanges.xml"); - const destructivePackageXmlDiffStr = await fs.readFile(diffDestructivePackageXml, "utf8"); + const diffDestructivePackageXml = path.join(tmpDir, 'destructiveChanges', 'destructiveChanges.xml'); + const destructivePackageXmlDiffStr = await fs.readFile(diffDestructivePackageXml, 'utf8'); uxLog( this, c.bold(c.cyan(`destructiveChanges.xml diff to be merged within ${c.green(localDestructiveChangesXml)}:\n`)) + - c.red(destructivePackageXmlDiffStr), + c.red(destructivePackageXmlDiffStr) + ); + await appendPackageXmlFilesContent( + [localDestructiveChangesXml, diffDestructivePackageXml], + localDestructiveChangesXml ); - const appendDestructivePackageXmlCommand = - "sfdx essentials:packagexml:append" + - ` --packagexmls ${localDestructiveChangesXml},${diffDestructivePackageXml}` + - ` --outputfile ${localDestructiveChangesXml}`; - await execCommand(appendDestructivePackageXmlCommand, this, { - fail: true, - debug: this.debugMode, - }); if ((await gitHasLocalUpdates()) && !this.noGit) { await git().add(localDestructiveChangesXml); } // Upgrade local package.xml - const diffPackageXml = path.join(tmpDir, "package", "package.xml"); - const packageXmlDiffStr = await fs.readFile(diffPackageXml, "utf8"); - uxLog(this, c.bold(c.cyan(`package.xml diff to be merged within ${c.green(localPackageXml)}:\n`)) + c.green(packageXmlDiffStr)); - const appendPackageXmlCommand = - "sfdx essentials:packagexml:append" + ` --packagexmls ${localPackageXml},${diffPackageXml}` + ` --outputfile ${localPackageXml}`; - await execCommand(appendPackageXmlCommand, this, { - fail: true, - debug: this.debugMode, - }); - const removePackageXmlCommand = - "sfdx essentials:packagexml:remove" + - ` --packagexml ${localPackageXml}` + - ` --removepackagexml ${localDestructiveChangesXml}` + - ` --outputfile ${localPackageXml}`; - await execCommand(removePackageXmlCommand, this, { - fail: true, - debug: this.debugMode, + const diffPackageXml = path.join(tmpDir, 'package', 'package.xml'); + const packageXmlDiffStr = await fs.readFile(diffPackageXml, 'utf8'); + uxLog( + this, + c.bold(c.cyan(`package.xml diff to be merged within ${c.green(localPackageXml)}:\n`)) + + c.green(packageXmlDiffStr) + ); + await appendPackageXmlFilesContent([localPackageXml, diffPackageXml], localPackageXml); + await removePackageXmlFilesContent(localPackageXml, localDestructiveChangesXml, { + outputXmlFile: localPackageXml, }); if ((await gitHasLocalUpdates()) && !this.noGit) { await git().add(localPackageXml); } } else { uxLog(this, `[error] ${c.grey(JSON.stringify(packageXmlResult))}`); - uxLog(this, c.red(`Unable to build git diff.${c.yellow(c.bold("Please update package.xml and destructiveChanges.xml manually"))}`)); + uxLog( + this, + c.red( + `Unable to build git diff.${c.yellow( + c.bold('Please update package.xml and destructiveChanges.xml manually') + )}` + ) + ); } // Commit updates @@ -374,9 +395,16 @@ autoRemoveUserPermissions: if (gitStatusWithConfig.staged.length > 0 && !this.noGit) { uxLog(this, `Committing files in local git branch ${c.green(this.currentBranch)}...`); try { - await git({ output: true }).commit("[sfdx-hardis] Update package content"); + await git({ output: true }).commit('[sfdx-hardis] Update package content'); } catch (e) { - uxLog(this, c.yellow(`There may be an issue while committing files but it can be ok to ignore it\n${c.grey(e.message)}`)); + uxLog( + this, + c.yellow( + `There may be an issue while committing files but it can be ok to ignore it\n${c.grey( + (e as Error).message + )}` + ) + ); gitStatusWithConfig = await git().status(); } } @@ -385,19 +413,22 @@ autoRemoveUserPermissions: // Apply automated cleaning to avoid to have to do it manually private async applyCleaningOnSources() { - const config = await getConfig("branch"); + const config = await getConfig('branch'); if (!this.noClean) { const gitStatusFilesBeforeClean = (await git().status()).files.map((file) => file.path); uxLog(this, JSON.stringify(gitStatusFilesBeforeClean, null, 2)); // References cleaning - uxLog(this, c.cyan("Cleaning sfdx project from obsolete references...")); + uxLog(this, c.cyan('Cleaning sfdx project from obsolete references...')); // User defined cleaning - await CleanReferences.run(["--type", "all"]); + await CleanReferences.run(['--type', 'all']); if (globalThis?.displayProfilesWarning === true) { - uxLog(this, c.yellow(c.bold("Please make sure the attributes removed from Profiles are defined on Permission Sets :)"))); + uxLog( + this, + c.yellow(c.bold('Please make sure the attributes removed from Profiles are defined on Permission Sets :)')) + ); } - uxLog(this, c.cyan("Cleaning sfdx project using patterns and xpaths defined in cleanXmlPatterns...")); + uxLog(this, c.cyan('Cleaning sfdx project using patterns and xpaths defined in cleanXmlPatterns...')); await CleanXml.run([]); // Manage git after cleaning const gitStatusAfterClean = await git().status(); @@ -406,13 +437,20 @@ autoRemoveUserPermissions: .filter((file) => !gitStatusFilesBeforeClean.includes(file.path)) .map((file) => normalizeFileStatusPath(file.path, config)); if (cleanedFiles.length > 0) { - uxLog(this, c.cyan(`Cleaned the following list of files:\n${cleanedFiles.join("\n")}`)); + uxLog(this, c.cyan(`Cleaned the following list of files:\n${cleanedFiles.join('\n')}`)); if (!this.noGit) { try { await git().add(cleanedFiles); - await git({ output: true }).commit("[sfdx-hardis] Clean sfdx project"); + await git({ output: true }).commit('[sfdx-hardis] Clean sfdx project'); } catch (e) { - uxLog(this, c.yellow(`There may be an issue while adding cleaned files but it can be ok to ignore it\n${c.grey(e.message)}`)); + uxLog( + this, + c.yellow( + `There may be an issue while adding cleaned files but it can be ok to ignore it\n${c.grey( + (e as Error).message + )}` + ) + ); } } } @@ -422,19 +460,19 @@ autoRemoveUserPermissions: private async buildDeploymentPlan() { // Build deployment plan splits let splitConfig = await this.getSeparateDeploymentsConfig(); - const localPackageXml = path.join("manifest", "package.xml"); + const localPackageXml = path.join('manifest', 'package.xml'); const packageXml = await parseXmlFile(localPackageXml); for (const type of packageXml.Package.types || []) { const typeName = type.name[0]; splitConfig = splitConfig.map((split) => { - if (split.types.includes(typeName) && type.members[0] !== "*") { + if (split.types.includes(typeName) && type.members[0] !== '*') { split.content[typeName] = type.members; } return split; }); } // Generate deployment plan items - const config = await getConfig("project"); + const config = await getConfig('project'); const deploymentPlan = config?.deploymentPlan || {}; let packages = deploymentPlan?.packages || []; const blankPackageXml = packageXml; @@ -443,7 +481,7 @@ autoRemoveUserPermissions: if (Object.keys(split.content).length > 0) { // data case if (split.data) { - const label = `Import ${split.types.join("-")} records`; + const label = `Import ${split.types.join('-')} records`; packages = this.addToPlan(packages, { label: label, dataPath: split.data, @@ -462,7 +500,7 @@ autoRemoveUserPermissions: }); } await writeXmlFile(split.file, splitPackageXml); - const label = `Deploy ${split.types.join("-")}`; + const label = `Deploy ${split.types.join('-')}`; packages = this.addToPlan(packages, { label: label, packageXmlFile: split.file, @@ -505,17 +543,24 @@ autoRemoveUserPermissions: } // Update deployment plan in config deploymentPlan.packages = packages.sort((a, b) => (a.order > b.order ? 1 : -1)); - await setConfig("project", { deploymentPlan: deploymentPlan }); + await setConfig('project', { deploymentPlan: deploymentPlan }); if (!this.noGit) { - await git({ output: true }).add(["./config"]); - await git({ output: true }).add(["./manifest"]); + await git({ output: true }).add(['./config']); + await git({ output: true }).add(['./manifest']); } let gitStatusAfterDeployPlan = await git().status(); if (gitStatusAfterDeployPlan.staged.length > 0 && !this.noGit) { try { - await git({ output: true }).commit("[sfdx-hardis] Update deployment plan"); + await git({ output: true }).commit('[sfdx-hardis] Update deployment plan'); } catch (e) { - uxLog(this, c.yellow(`There may be an issue while committing files but it can be ok to ignore it\n${c.grey(e.message)}`)); + uxLog( + this, + c.yellow( + `There may be an issue while committing files but it can be ok to ignore it\n${c.grey( + (e as Error).message + )}` + ) + ); gitStatusAfterDeployPlan = await git().status(); } } @@ -533,21 +578,25 @@ autoRemoveUserPermissions: !this.auto ) { const pushResponse = await prompts({ - type: "confirm", - name: "push", + type: 'confirm', + name: 'push', default: true, - message: c.cyanBright(`Do you want to push your commit(s) on git server ? (git push in remote git branch ${c.green(this.currentBranch)})`), + message: c.cyanBright( + `Do you want to push your commit(s) on git server ? (git push in remote git branch ${c.green( + this.currentBranch + )})` + ), }); if (pushResponse.push === true) { uxLog(this, c.cyan(`Pushing new commit(s) in remote git branch ${c.green(`origin/${this.currentBranch}`)}...`)); - const configUSer = await getConfig("user"); + const configUSer = await getConfig('user'); let pushResult: any; if (configUSer.canForcePush === true) { // Force push if hardis:work:resetselection has been called before - pushResult = await git({ output: true }).push(["-u", "origin", this.currentBranch, "--force"]); - await setConfig("user", { canForcePush: false }); + pushResult = await git({ output: true }).push(['-u', 'origin', this.currentBranch, '--force']); + await setConfig('user', { canForcePush: false }); } else { - pushResult = await git({ output: true }).push(["-u", "origin", this.currentBranch]); + pushResult = await git({ output: true }).push(['-u', 'origin', this.currentBranch]); } // Update merge request info if (pushResult && pushResult.remoteMessages) { @@ -562,8 +611,8 @@ autoRemoveUserPermissions: mergeRequestsStored.push(this.updateMergeRequestInfo({ branch: this.currentBranch }, pushResult)); } // Update user config file & send Websocket event - await setConfig("user", { mergeRequests: mergeRequestsStored.filter((mr: any) => mr !== null) }); - WebSocketClient.sendMessage({ event: "refreshStatus" }); + await setConfig('user', { mergeRequests: mergeRequestsStored.filter((mr: any) => mr !== null) }); + WebSocketClient.sendMessage({ event: 'refreshStatus' }); } } } @@ -583,7 +632,10 @@ autoRemoveUserPermissions: } else { delete mergeRequestStored.urlCreate; } - if (mergeRequestInfo?.remoteMessages?.all[0] && mergeRequestInfo?.remoteMessages?.all[0].includes("View merge request")) { + if ( + mergeRequestInfo?.remoteMessages?.all[0] && + mergeRequestInfo?.remoteMessages?.all[0].includes('View merge request') + ) { mergeRequestStored.url = mergeRequestInfo?.remoteMessages?.all[1]; } else { delete mergeRequestStored.url; @@ -592,7 +644,7 @@ autoRemoveUserPermissions: } private async getSeparateDeploymentsConfig() { - const config = await getConfig("project"); + const config = await getConfig('project'); if (config.separateDeploymentsConfig || config.separateDeploymentsConfig === false) { return config.separateDeploymentConfig || []; } @@ -613,10 +665,10 @@ autoRemoveUserPermissions: content: {}, }, */ { - types: ["SharingRules", "SharingOwnerRule"], - files: "manifest/splits/packageXmlSharingRules{{name}}.xml", + types: ['SharingRules', 'SharingOwnerRule'], + files: 'manifest/splits/packageXmlSharingRules{{name}}.xml', filePos: 30, - mainType: "SharingRules", + mainType: 'SharingRules', waitAfter: 30, content: {}, }, diff --git a/src/commands/hardis/work/ws.ts b/src/commands/hardis/work/ws.ts index c024f3c11..bacb39b26 100644 --- a/src/commands/hardis/work/ws.ts +++ b/src/commands/hardis/work/ws.ts @@ -1,64 +1,55 @@ /* jscpd:ignore-start */ -import { flags, SfdxCommand } from "@salesforce/command"; -import { Messages } from "@salesforce/core"; -import { AnyJson } from "@salesforce/ts-types"; -import { WebSocketClient } from "../../../common/websocketClient"; +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; +import { AnyJson } from '@salesforce/ts-types'; +import { WebSocketClient } from '../../../common/websocketClient.js'; -// Initialize Messages with the current plugin directory -Messages.importMessagesDirectory(__dirname); +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'org'); -// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, -// or any library that is using the messages framework can also be loaded this way. -const messages = Messages.loadMessages("sfdx-hardis", "org"); +export default class WebSocketAction extends SfCommand { + public static title = 'WebSocket operations'; -export default class WebSocketAction extends SfdxCommand { - public static title = "WebSocket operations"; + public static description = 'Technical calls to WebSocket functions'; - public static description = "Technical calls to WebSocket functions"; - - public static examples = ["$ sfdx hardis:work:ws --event refreshStatus"]; + public static examples = ['$ sf hardis:work:ws --event refreshStatus']; // public static args = [{name: 'file'}]; - protected static flagsConfig = { - event: flags.string({ - char: "e", - description: "WebSocket event", + public static flags: any = { + event: Flags.string({ + char: 'e', + description: 'WebSocket event', }), - debug: flags.boolean({ - char: "d", + debug: Flags.boolean({ + char: 'd', default: false, - description: messages.getMessage("debugMode"), + description: messages.getMessage('debugMode'), }), - websocket: flags.string({ - description: messages.getMessage("websocket"), + websocket: Flags.string({ + description: messages.getMessage('websocket'), }), - skipauth: flags.boolean({ - description: "Skip authentication check when a default username is required", + skipauth: Flags.boolean({ + description: 'Skip authentication check when a default username is required', }), }; - // Comment this out if your command does not require an org username - protected static requiresUsername = false; - - // Comment this out if your command does not support a hub org username - protected static requiresDevhubUsername = false; - // Set this to true if your command requires a project workspace; 'requiresProject' is false by default - protected static requiresProject = false; + public static requiresProject = false; protected debugMode = false; - protected event = ""; + protected event = ''; /* jscpd:ignore-end */ public async run(): Promise { - this.event = this.flags.event || ""; + const { flags } = await this.parse(WebSocketAction); + this.event = flags.event || ''; if (WebSocketClient.isAlive()) { - if (this.event === "refreshStatus") { - WebSocketClient.sendMessage({ event: "refreshStatus" }); - } else if (this.event === "refreshPlugins") { - WebSocketClient.sendMessage({ event: "refreshPlugins" }); + if (this.event === 'refreshStatus') { + WebSocketClient.sendMessage({ event: 'refreshStatus' }); + } else if (this.event === 'refreshPlugins') { + WebSocketClient.sendMessage({ event: 'refreshPlugins' }); } } diff --git a/src/commands/hello/world.ts b/src/commands/hello/world.ts new file mode 100644 index 000000000..112495c85 --- /dev/null +++ b/src/commands/hello/world.ts @@ -0,0 +1,35 @@ +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('sfdx-hardis', 'hello.world'); + +export type HelloWorldResult = { + name: string; + time: string; +}; + +export default class World 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 flags: any = { + name: Flags.string({ + char: 'n', + summary: messages.getMessage('flags.name.summary'), + description: messages.getMessage('flags.name.description'), + default: 'World', + }), + }; + + public async run(): Promise { + const { flags } = await this.parse(World); + const time = new Date().toDateString(); + this.log(messages.getMessage('info.hello', [flags.name, time])); + return { + name: flags.name, + time, + }; + } +} diff --git a/src/common/aiProvider/aiProviderRoot.ts b/src/common/aiProvider/aiProviderRoot.ts index 5e1f27d20..5b88b7bdf 100644 --- a/src/common/aiProvider/aiProviderRoot.ts +++ b/src/common/aiProvider/aiProviderRoot.ts @@ -1,20 +1,20 @@ -import { SfdxError } from "@salesforce/core"; -import { AiResponse } from "."; -import { getEnvVar } from "../../config"; +import { SfError } from "@salesforce/core"; +import { AiResponse } from "./index.js"; +import { getEnvVar } from "../../config/index.js"; export abstract class AiProviderRoot { protected token: string; public getLabel(): string { - throw new SfdxError("getLabel should be implemented on this call"); + throw new SfError("getLabel should be implemented on this call"); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async promptAi(prompt: string): Promise { - throw new SfdxError("promptAi should be implemented on this call"); + public async promptAi(prompt: string): Promise { + throw new SfError("promptAi should be implemented on this call"); } - // Get user defined maximum number of calls during an sfdx hardis command + // Get user defined maximum number of calls during an sfdx-hardis command getAiMaxCallsNumber() { return parseInt(getEnvVar("AI_MAXIMUM_CALL_NUMBER") || "10"); } diff --git a/src/common/aiProvider/index.ts b/src/common/aiProvider/index.ts index f292b668b..a222672b5 100644 --- a/src/common/aiProvider/index.ts +++ b/src/common/aiProvider/index.ts @@ -1,22 +1,26 @@ -import { UtilsAi } from "./utils"; -import { AiProviderRoot } from "./aiProviderRoot"; -import { OpenApiProvider } from "./openapiProvider"; +import { UtilsAi } from "./utils.js"; +import { AiProviderRoot } from "./aiProviderRoot.js"; +import { OpenAiProvider } from "./openaiProvider.js"; +import { SfError } from "@salesforce/core"; export abstract class AiProvider { static isAiAvailable(): boolean { return this.getInstance() != null; } - static getInstance(): AiProviderRoot { + static getInstance(): AiProviderRoot | null { // OpenAi if (UtilsAi.isOpenApiAvailable()) { - return new OpenApiProvider(); + return new OpenAiProvider(); } return null; } - static async promptAi(prompt: string): Promise { + static async promptAi(prompt: string): Promise { const aiInstance = this.getInstance(); + if (!aiInstance) { + throw new SfError("aiInstance should be set"); + } return await aiInstance.promptAi(prompt); } } diff --git a/src/common/aiProvider/openapiProvider.ts b/src/common/aiProvider/openaiProvider.ts similarity index 75% rename from src/common/aiProvider/openapiProvider.ts rename to src/common/aiProvider/openaiProvider.ts index f48a64524..a4d9498f4 100644 --- a/src/common/aiProvider/openapiProvider.ts +++ b/src/common/aiProvider/openaiProvider.ts @@ -1,10 +1,10 @@ import OpenAI from "openai"; -import { AiResponse } from "."; -import { AiProviderRoot } from "./aiProviderRoot"; -import * as c from "chalk"; -import { uxLog } from "../utils"; +import { AiResponse } from "./index.js"; +import { AiProviderRoot } from "./aiProviderRoot.js"; +import c from "chalk"; +import { uxLog } from "../utils/index.js"; -export class OpenApiProvider extends AiProviderRoot { +export class OpenAiProvider extends AiProviderRoot { protected openai: OpenAI; constructor() { @@ -13,10 +13,10 @@ export class OpenApiProvider extends AiProviderRoot { } public getLabel(): string { - return "OpenApi connector"; + return "OpenAi connector"; } - public async promptAi(promptText: string): Promise { + public async promptAi(promptText: string): Promise { if (!this.checkMaxAiCallsNumber()) { const maxCalls = this.getAiMaxCallsNumber(); uxLog(this, c.grey(`[OpenAi] Already performed maximum ${maxCalls} calls. Increase it by defining OPENAI_MAXIMUM_CALL_NUMBER`)); @@ -35,7 +35,7 @@ export class OpenApiProvider extends AiProviderRoot { }; if (completion?.choices?.length > 0) { aiResponse.success = true; - aiResponse.promptResponse = completion.choices[0].message.content; + aiResponse.promptResponse = completion.choices[0].message.content ?? undefined; } return aiResponse; } diff --git a/src/common/aiProvider/utils.ts b/src/common/aiProvider/utils.ts index e3b93806a..cb3020352 100644 --- a/src/common/aiProvider/utils.ts +++ b/src/common/aiProvider/utils.ts @@ -1,4 +1,4 @@ -import { getEnvVar } from "../../config"; +import { getEnvVar } from "../../config/index.js"; export class UtilsAi { public static isOpenApiAvailable() { diff --git a/src/common/cache/index.ts b/src/common/cache/index.ts index c19f9ec2c..a5ddb7338 100644 --- a/src/common/cache/index.ts +++ b/src/common/cache/index.ts @@ -1,8 +1,8 @@ -import * as fs from "fs-extra"; -import * as os from "os"; -import * as path from "path"; +import fs from 'fs-extra'; +import * as os from 'os'; +import * as path from 'path'; -const cacheFileName = path.join(os.homedir(), ".sfdx", ".sfdx-hardis-cache.json"); +const cacheFileName = path.join(os.homedir(), '.sfdx', '.sfdx-hardis-cache.json'); let MEMORY_CACHE: any = null; const readCache = async (): Promise => { @@ -46,7 +46,7 @@ export const setCache = async (key: string, val: any): Promise => { }; // Clear cache property, or all cache if property is empty -export const clearCache = async (key: string = null): Promise => { +export const clearCache = async (key: string | null = null): Promise => { await readCache(); if (key) { delete MEMORY_CACHE[key]; diff --git a/src/common/cryptoUtils.ts b/src/common/cryptoUtils.ts index 508eba608..da548e0db 100644 --- a/src/common/cryptoUtils.ts +++ b/src/common/cryptoUtils.ts @@ -1,43 +1,41 @@ -"use strict"; - -import * as crypto from "crypto"; -import * as fs from "fs-extra"; +import * as crypto from 'crypto'; +import fs from 'fs-extra'; //const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY; // Must be 256 bits (32 characters) const IV_LENGTH = 16; // For AES, this is always 16 export async function encryptFile(filePath) { - const fileContent = await fs.readFile(filePath, "utf8"); + const fileContent = await fs.readFile(filePath, 'utf8'); const encryptedFileContent = encrypt(fileContent); await fs.writeFile(filePath, encryptedFileContent.text); return encryptedFileContent.encryptionKey; } export async function decryptFile(filePath, targetFile, encryptionKey) { - const fileContent = await fs.readFile(filePath, "utf8"); + const fileContent = await fs.readFile(filePath, 'utf8'); const decryptedFileContent = decrypt(fileContent, encryptionKey); await fs.writeFile(targetFile, decryptedFileContent); } export function encrypt(text) { const iv = crypto.randomBytes(IV_LENGTH); - const encryptionKey = crypto.randomBytes(16).toString("hex"); - const cipher = crypto.createCipheriv("aes-256-cbc", Buffer.from(encryptionKey), iv); + const encryptionKey = crypto.randomBytes(16).toString('hex'); + const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(encryptionKey), iv); let encrypted = cipher.update(text); encrypted = Buffer.concat([encrypted, cipher.final()]); return { - text: iv.toString("hex") + ":" + encrypted.toString("hex"), + text: iv.toString('hex') + ':' + encrypted.toString('hex'), encryptionKey: encryptionKey, }; } export function decrypt(text, encryptionKey) { - const textParts = text.split(":"); - const iv = Buffer.from(textParts.shift(), "hex"); - const encryptedText = Buffer.from(textParts.join(":"), "hex"); - const decipher = crypto.createDecipheriv("aes-256-cbc", Buffer.from(encryptionKey), iv); + const textParts = text.split(':'); + const iv = Buffer.from(textParts.shift(), 'hex'); + const encryptedText = Buffer.from(textParts.join(':'), 'hex'); + const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(encryptionKey), iv); let decrypted = decipher.update(encryptedText); decrypted = Buffer.concat([decrypted, decipher.final()]); diff --git a/src/common/gitProvider/azureDevops.ts b/src/common/gitProvider/azureDevops.ts index 49e0b6544..904d18a4f 100644 --- a/src/common/gitProvider/azureDevops.ts +++ b/src/common/gitProvider/azureDevops.ts @@ -1,24 +1,22 @@ -import { GitProviderRoot } from "./gitProviderRoot"; +import { GitProviderRoot } from "./gitProviderRoot.js"; import * as azdev from "azure-devops-node-api"; -import * as c from "chalk"; -import { getCurrentGitBranch, git, uxLog } from "../utils"; -import { PullRequestMessageRequest, PullRequestMessageResult } from "."; -import { - CommentThreadStatus, - GitPullRequestCommentThread, - PullRequestAsyncStatus, - PullRequestStatus, -} from "azure-devops-node-api/interfaces/GitInterfaces"; +import c from "chalk"; +import { getCurrentGitBranch, git, uxLog } from "../utils/index.js"; +import { PullRequestMessageRequest, PullRequestMessageResult } from "./index.js"; +import { CommentThreadStatus, GitPullRequestCommentThread, PullRequestAsyncStatus, PullRequestStatus } from "azure-devops-node-api/interfaces/GitInterfaces.js"; +import { CONSTANTS } from "../../config/index.js"; export class AzureDevopsProvider extends GitProviderRoot { private azureApi: InstanceType; + public serverUrl: string; + public token: string; constructor() { super(); // Azure server url must be provided in SYSTEM_COLLECTIONURI. ex: https:/dev.azure.com/mycompany - this.serverUrl = process.env.SYSTEM_COLLECTIONURI; + this.serverUrl = process.env.SYSTEM_COLLECTIONURI || ""; // a Personal Access Token must be defined - this.token = process.env.CI_SFDX_HARDIS_AZURE_TOKEN || process.env.SYSTEM_ACCESSTOKEN; + this.token = process.env.CI_SFDX_HARDIS_AZURE_TOKEN || process.env.SYSTEM_ACCESSTOKEN || ""; const authHandler = azdev.getHandlerFromToken(this.token); this.azureApi = new azdev.WebApi(this.serverUrl, authHandler); } @@ -28,11 +26,10 @@ export class AzureDevopsProvider extends GitProviderRoot { } // Returns current job URL - public async getCurrentJobUrl(): Promise { + public async getCurrentJobUrl(): Promise { if (process.env.SYSTEM_COLLECTIONURI && process.env.SYSTEM_TEAMPROJECT && process.env.BUILD_BUILDID) { - const jobUrl = `${process.env.SYSTEM_COLLECTIONURI}${encodeURIComponent(process.env.SYSTEM_TEAMPROJECT)}/_build/results?buildId=${ - process.env.BUILD_BUILDID - }`; + const jobUrl = `${process.env.SYSTEM_COLLECTIONURI}${encodeURIComponent(process.env.SYSTEM_TEAMPROJECT)}/_build/results?buildId=${process.env.BUILD_BUILDID + }`; return jobUrl; } uxLog( @@ -46,7 +43,7 @@ export class AzureDevopsProvider extends GitProviderRoot { } // Returns current job URL - public async getCurrentBranchUrl(): Promise { + public async getCurrentBranchUrl(): Promise { if ( process.env.SYSTEM_COLLECTIONURI && process.env.SYSTEM_TEAMPROJECT && @@ -84,7 +81,7 @@ ${this.getPipelineVariablesConfig()} const pullRequest = await azureGitApi.getPullRequestById(pullRequestId); if (pullRequest && pullRequest.targetRefName) { // Add references to work items in PR result - const pullRequestWorkItemRefs = await azureGitApi.getPullRequestWorkItemRefs(repositoryId, pullRequestId); + const pullRequestWorkItemRefs = await azureGitApi.getPullRequestWorkItemRefs(repositoryId || "", pullRequestId); if (!pullRequest.workItemRefs) { pullRequest.workItemRefs = pullRequestWorkItemRefs; } @@ -96,7 +93,7 @@ ${this.getPipelineVariablesConfig()} } // Case when we find PR from a commit const sha = await git().revparse(["HEAD"]); - const latestPullRequestsOnBranch = await azureGitApi.getPullRequests(repositoryId, { + const latestPullRequestsOnBranch = await azureGitApi.getPullRequests(repositoryId || "", { targetRefName: `refs/heads/${currentGitBranch}`, status: PullRequestStatus.Completed, }); @@ -106,7 +103,7 @@ ${this.getPipelineVariablesConfig()} if (latestMergedPullRequestOnBranch.length > 0) { const pullRequest = latestMergedPullRequestOnBranch[0]; // Add references to work items in PR result - const pullRequestWorkItemRefs = await azureGitApi.getPullRequestWorkItemRefs(repositoryId, pullRequest.pullRequestId); + const pullRequestWorkItemRefs = await azureGitApi.getPullRequestWorkItemRefs(repositoryId || "", pullRequest.pullRequestId || 0); if (!pullRequest.workItemRefs) { pullRequest.workItemRefs = pullRequestWorkItemRefs; } @@ -116,7 +113,7 @@ ${this.getPipelineVariablesConfig()} return null; } - public async getBranchDeploymentCheckId(gitBranch: string): Promise { + public async getBranchDeploymentCheckId(gitBranch: string): Promise { let deploymentCheckId = null; // Get Azure Git API const azureGitApi = await this.azureApi.getGitApi(); @@ -136,7 +133,7 @@ ${this.getPipelineVariablesConfig()} deploymentCheckId = await this.getDeploymentIdFromPullRequest( azureGitApi, repositoryId, - latestPullRequestId, + latestPullRequestId || 0, deploymentCheckId, latestPullRequest, ); @@ -144,7 +141,7 @@ ${this.getPipelineVariablesConfig()} return deploymentCheckId; } - public async getPullRequestDeploymentCheckId(): Promise { + public async getPullRequestDeploymentCheckId(): Promise { const pullRequestInfo = await this.getPullRequestInfo(); if (pullRequestInfo) { const azureGitApi = await this.azureApi.getGitApi(); @@ -206,8 +203,8 @@ ${this.getPipelineVariablesConfig()} } const pullRequestId = Number(pullRequestIdStr); const azureJobName = process.env.SYSTEM_JOB_DISPLAY_NAME; - const SYSTEM_COLLECTIONURI = process.env.SYSTEM_COLLECTIONURI.replace(/ /g, "%20"); - const SYSTEM_TEAMPROJECT = process.env.SYSTEM_TEAMPROJECT.replace(/ /g, "%20"); + const SYSTEM_COLLECTIONURI = (process.env.SYSTEM_COLLECTIONURI || "").replace(/ /g, "%20"); + const SYSTEM_TEAMPROJECT = (process.env.SYSTEM_TEAMPROJECT || "").replace(/ /g, "%20"); const azureBuildUri = `${SYSTEM_COLLECTIONURI}${encodeURIComponent(SYSTEM_TEAMPROJECT)}/_build/results?buildId=${buildId}&view=logs&j=${jobId}`; // Build thread message const messageKey = prMessage.messageKey + "-" + azureJobName + "-" + pullRequestId; @@ -217,7 +214,7 @@ ${prMessage.message}
-_Powered by [sfdx-hardis](https://sfdx-hardis.cloudity.com) from job [${azureJobName}](${azureBuildUri})_ +_Powered by [sfdx-hardis](${CONSTANTS.DOC_URL_ROOT}) from job [${azureJobName}](${azureBuildUri})_ `; // Add deployment id if present @@ -229,9 +226,9 @@ _Powered by [sfdx-hardis](https://sfdx-hardis.cloudity.com) from job [${azureJob // Check for existing threads from a previous run uxLog(this, c.grey("[Azure integration] Listing Threads of Pull Request...")); const existingThreads = await azureGitApi.getThreads(repositoryId, pullRequestId); - let existingThreadId: number = null; - let existingThreadComment: GitPullRequestCommentThread = null; - let existingThreadCommentId: number = null; + let existingThreadId: number | null = null; + let existingThreadComment: GitPullRequestCommentThread | null = null; + let existingThreadCommentId: number | null | undefined = null; for (const existingThread of existingThreads) { if (existingThread.isDeleted) { continue; @@ -239,8 +236,8 @@ _Powered by [sfdx-hardis](https://sfdx-hardis.cloudity.com) from job [${azureJob for (const comment of existingThread?.comments || []) { if ((comment?.content || "").includes(``)) { existingThreadComment = existingThread; - existingThreadCommentId = existingThread.comments[0].id; - existingThreadId = existingThread.id; + existingThreadCommentId = (existingThread.comments || [])[0].id; + existingThreadId = existingThread.id || null; break; } } @@ -253,7 +250,7 @@ _Powered by [sfdx-hardis](https://sfdx-hardis.cloudity.com) from job [${azureJob if (existingThreadId) { // Delete previous comment uxLog(this, c.grey("[Azure integration] Deleting previous comment and closing previous thread...")); - await azureGitApi.deleteComment(repositoryId, pullRequestId, existingThreadId, existingThreadCommentId); + await azureGitApi.deleteComment(repositoryId, pullRequestId, existingThreadId, existingThreadCommentId || 0); existingThreadComment = await azureGitApi.getPullRequestThread(repositoryId, pullRequestId, existingThreadId); // Update existing thread existingThreadComment = { @@ -271,7 +268,7 @@ _Powered by [sfdx-hardis](https://sfdx-hardis.cloudity.com) from job [${azureJob }; const azureEditThreadResult = await azureGitApi.createThread(newThreadComment, repositoryId, pullRequestId); const prResult: PullRequestMessageResult = { - posted: azureEditThreadResult.id > 0, + posted: (azureEditThreadResult.id || -1) > 0, providerResult: azureEditThreadResult, }; uxLog(this, c.grey(`[Azure integration] Posted Pull Request Thread ${azureEditThreadResult.id}`)); @@ -291,8 +288,8 @@ _Powered by [sfdx-hardis](https://sfdx-hardis.cloudity.com) from job [${azureJob const prInfo: any = Object.assign({}, prData); prInfo.sourceBranch = (prData.sourceRefName || "").replace("refs/heads/", ""); prInfo.targetBranch = (prData.targetRefName || "").replace("refs/heads/", ""); - prInfo.web_url = `${process.env.SYSTEM_COLLECTIONURI}${encodeURIComponent(process.env.SYSTEM_TEAMPROJECT)}/_git/${encodeURIComponent( - process.env.BUILD_REPOSITORYNAME, + prInfo.web_url = `${process.env.SYSTEM_COLLECTIONURI}${encodeURIComponent(process.env.SYSTEM_TEAMPROJECT || "")}/_git/${encodeURIComponent( + process.env.BUILD_REPOSITORYNAME || "", )}/pullrequest/${prData.pullRequestId}`; prInfo.authorName = prData?.createdBy?.displayName || ""; return prInfo; @@ -320,4 +317,16 @@ _Powered by [sfdx-hardis](https://sfdx-hardis.cloudity.com) from job [${azureJob BUILD_SOURCEBRANCHNAME: $(Build.SourceBranchName) BUILD_BUILD_ID: $(Build.BuildId)`; } + + // Do not make crash the whole process in case there is an issue with integration + public async tryPostPullRequestMessage(prMessage: PullRequestMessageRequest): Promise { + let prResult: PullRequestMessageResult | null = null; + try { + prResult = await this.postPullRequestMessage(prMessage); + } catch (e) { + uxLog(this, c.yellow(`[GitProvider] Error while trying to post pull request message.\n${(e as Error).message}\n${(e as Error).stack}`)); + prResult = { posted: false, providerResult: { error: e } }; + } + return prResult; + } } diff --git a/src/common/gitProvider/bitbucket.ts b/src/common/gitProvider/bitbucket.ts index 5a019c8e5..627563c0b 100644 --- a/src/common/gitProvider/bitbucket.ts +++ b/src/common/gitProvider/bitbucket.ts @@ -1,24 +1,28 @@ -import { GitProviderRoot } from "./gitProviderRoot"; -import * as c from "chalk"; -import { PullRequestMessageRequest, PullRequestMessageResult } from "."; -import { git, uxLog } from "../utils"; -import { Bitbucket, Schema } from "bitbucket"; +import { GitProviderRoot } from './gitProviderRoot.js'; +import c from 'chalk'; +import { PullRequestMessageRequest, PullRequestMessageResult } from './index.js'; +import { git, uxLog } from '../utils/index.js'; +import bbPkg, { Schema } from 'bitbucket'; +import { CONSTANTS } from '../../config/index.js'; +const { Bitbucket } = bbPkg; export class BitbucketProvider extends GitProviderRoot { private bitbucket: InstanceType; + public serverUrl: string = ''; + public token: string; constructor() { super(); - const token = process.env.CI_SFDX_HARDIS_BITBUCKET_TOKEN; - const clientOptions = { auth: { token: token } }; + this.token = process.env.CI_SFDX_HARDIS_BITBUCKET_TOKEN || ''; + const clientOptions = { auth: { token: this.token } }; this.bitbucket = new Bitbucket(clientOptions); } public getLabel(): string { - return "sfdx-hardis Bitbucket connector"; + return 'sfdx-hardis Bitbucket connector'; } - public async getCurrentJobUrl(): Promise { + public async getCurrentJobUrl(): Promise { if (process.env.BITBUCKET_WORKSPACE && process.env.BITBUCKET_REPO_SLUG && process.env.BITBUCKET_BUILD_NUMBER) { const jobUrl = `https://bitbucket.org/${process.env.BITBUCKET_WORKSPACE}/${process.env.BITBUCKET_REPO_SLUG}/pipelines/results/${process.env.BITBUCKET_BUILD_NUMBER}`; return jobUrl; @@ -28,13 +32,13 @@ export class BitbucketProvider extends GitProviderRoot { c.yellow(`[Bitbucket Integration] You need the following variables to be accessible to sfdx-hardis to build current job url: - BITBUCKET_WORKSPACE - BITBUCKET_REPO_SLUG - - BITBUCKET_BUILD_NUMBER`), + - BITBUCKET_BUILD_NUMBER`) ); return null; } - public async getCurrentBranchUrl(): Promise { + public async getCurrentBranchUrl(): Promise { if (process.env.BITBUCKET_WORKSPACE && process.env.BITBUCKET_REPO_SLUG && process.env.BITBUCKET_BRANCH) { const currentBranchUrl = `https://bitbucket.org/${process.env.BITBUCKET_WORKSPACE}/${process.env.BITBUCKET_REPO_SLUG}/branch/${process.env.BITBUCKET_BRANCH}`; return currentBranchUrl; @@ -44,7 +48,7 @@ export class BitbucketProvider extends GitProviderRoot { c.yellow(`[Bitbucket Integration] You need the following variables to be accessible to sfdx-hardis to build current job url: - BITBUCKET_WORKSPACE - BITBUCKET_REPO_SLUG - - BITBUCKET_BRANCH`), + - BITBUCKET_BRANCH`) ); return null; @@ -61,8 +65,8 @@ export class BitbucketProvider extends GitProviderRoot { const pullRequestId = Number(pullRequestIdStr); const pullRequest = await this.bitbucket.repositories.getPullRequest({ pull_request_id: pullRequestId, - repo_slug: repoSlug, - workspace: workspace, + repo_slug: repoSlug || '', + workspace: workspace || '', }); if (pullRequest?.data.destination) { @@ -75,17 +79,17 @@ export class BitbucketProvider extends GitProviderRoot { } // Case when we find PR from a commit - const sha = await git().revparse(["HEAD"]); + const sha = await git().revparse(['HEAD']); const latestPullRequestsOnBranch = await this.bitbucket.repositories.listPullrequestsForCommit({ // cspell:disable-line commit: sha, - repo_slug: repoSlug, - workspace: workspace, + repo_slug: repoSlug || '', + workspace: workspace || '', }); const latestMergedPullRequestOnBranch = latestPullRequestsOnBranch?.data?.values?.filter( - (pr) => pr.state === "MERGED" && pr.merge_commit?.hash === sha, + (pr) => pr.state === 'MERGED' && pr.merge_commit?.hash === sha ); - if (latestMergedPullRequestOnBranch?.length > 0) { + if (latestMergedPullRequestOnBranch?.length && latestMergedPullRequestOnBranch?.length > 0) { const pullRequest = latestMergedPullRequestOnBranch[0]; // Add cross git provider properties used by sfdx-hardis return this.completePullRequestInfo(pullRequest); @@ -95,32 +99,47 @@ export class BitbucketProvider extends GitProviderRoot { return null; } - public async getBranchDeploymentCheckId(gitBranch: string): Promise { + public async getBranchDeploymentCheckId(gitBranch: string): Promise { let deploymentCheckId = null; const repoSlug = process.env.BITBUCKET_REPO_SLUG || null; const workspace = process.env.BITBUCKET_WORKSPACE || null; const latestMergedPullRequestsOnBranch = await this.bitbucket.repositories.listPullRequests({ - repo_slug: repoSlug, - workspace: workspace, - state: "MERGED", + repo_slug: repoSlug || '', + workspace: workspace || '', + state: 'MERGED', q: `destination.branch.name = "${gitBranch}"`, - sort: "-updated_on", + sort: '-updated_on', }); - if (latestMergedPullRequestsOnBranch?.data?.values?.length > 0) { + if ( + latestMergedPullRequestsOnBranch?.data?.values?.length && + latestMergedPullRequestsOnBranch?.data?.values?.length > 0 + ) { const latestPullRequest = latestMergedPullRequestsOnBranch?.data?.values[0]; const latestPullRequestId = latestPullRequest.id; - deploymentCheckId = await this.getDeploymentIdFromPullRequest(latestPullRequestId, repoSlug, workspace, deploymentCheckId, latestPullRequest); + deploymentCheckId = await this.getDeploymentIdFromPullRequest( + latestPullRequestId || 0, + repoSlug || '', + workspace || '', + deploymentCheckId, + latestPullRequest + ); } return deploymentCheckId; } - public async getPullRequestDeploymentCheckId(): Promise { + public async getPullRequestDeploymentCheckId(): Promise { const pullRequestInfo = await this.getPullRequestInfo(); if (pullRequestInfo) { const repoSlug = process.env.BITBUCKET_REPO_SLUG || null; const workspace = process.env.BITBUCKET_WORKSPACE || null; - return await this.getDeploymentIdFromPullRequest(pullRequestInfo.id, repoSlug, workspace, null, pullRequestInfo); + return await this.getDeploymentIdFromPullRequest( + pullRequestInfo.id, + repoSlug || '', + workspace || '', + null, + pullRequestInfo + ); } return null; } @@ -130,7 +149,7 @@ export class BitbucketProvider extends GitProviderRoot { repoSlug: string, workspace: string, deploymentCheckId: any, - latestPullRequest: Schema.Pullrequest, + latestPullRequest: Schema.Pullrequest ) { const comments = await this.bitbucket.repositories.listPullRequestComments({ pull_request_id: latestPullRequestId, @@ -139,13 +158,15 @@ export class BitbucketProvider extends GitProviderRoot { }); for (const comment of comments?.data?.values || []) { - if ((comment?.content?.raw || "").includes(`/gm.exec(comment?.content?.raw); + if ((comment?.content?.raw || '').includes(`/gm.exec(comment?.content?.raw || ''); if (matches) { deploymentCheckId = matches[1]; uxLog( this, - c.gray(`[Bitbucket Integration] Found deployment id ${deploymentCheckId} on PR #${latestPullRequestId} ${latestPullRequest.title}`), + c.gray( + `[Bitbucket Integration] Found deployment id ${deploymentCheckId} on PR #${latestPullRequestId} ${latestPullRequest.title}` + ) ); break; } @@ -160,19 +181,19 @@ export class BitbucketProvider extends GitProviderRoot { const workspace = process.env.BITBUCKET_WORKSPACE || null; if (repoSlug == null || pullRequestIdStr == null) { - uxLog(this, c.grey("[Bitbucket integration] No repo and pull request, so no note posted...")); - return { posted: false, providerResult: { info: "No related pull request" } }; + uxLog(this, c.grey('[Bitbucket integration] No repo and pull request, so no note posted...')); + return { posted: false, providerResult: { info: 'No related pull request' } }; } const pullRequestId = Number(pullRequestIdStr); const bitbucketBuildNumber = process.env.BITBUCKET_BUILD_NUMBER || null; const bitbucketJobUrl = await this.getCurrentJobUrl(); const messageKey = `${prMessage.messageKey}-${bitbucketBuildNumber}-${pullRequestId}`; - let messageBody = `**${prMessage.title || ""}** + let messageBody = `**${prMessage.title || ''}** ${prMessage.message} - \n_Powered by [sfdx-hardis](https://sfdx-hardis.cloudity.com) from job [${bitbucketBuildNumber}](${bitbucketJobUrl})_ + \n_Powered by [sfdx-hardis](${CONSTANTS.DOC_URL_ROOT}) from job [${bitbucketBuildNumber}](${bitbucketJobUrl})_ \n `; @@ -188,25 +209,28 @@ export class BitbucketProvider extends GitProviderRoot { }; // Check for existing comment from a previous run - uxLog(this, c.grey("[Bitbucket integration] Listing comments of Pull Request...")); + uxLog(this, c.grey('[Bitbucket integration] Listing comments of Pull Request...')); const existingComments = await this.bitbucket.repositories.listPullRequestComments({ pull_request_id: pullRequestId, repo_slug: repoSlug, - workspace: workspace, + workspace: workspace || '', }); - let existingCommentId = null; + let existingCommentId: number | null = null; for (const existingComment of existingComments?.data?.values || []) { - if (existingComment?.content.raw?.includes(``)) { - existingCommentId = existingComment.id; + if ( + existingComment?.content?.raw && + existingComment?.content.raw?.includes(``) + ) { + existingCommentId = existingComment.id || null; } } // Create or update MR comment if (existingCommentId) { // Update existing comment - uxLog(this, c.grey("[Bitbucket integration] Updating Pull Request Comment on Bitbucket...")); + uxLog(this, c.grey('[Bitbucket integration] Updating Pull Request Comment on Bitbucket...')); const pullRequestComment = await this.bitbucket.repositories.updatePullRequestComment({ - workspace: workspace, + workspace: workspace || '', repo_slug: repoSlug, pull_request_id: pullRequestId, comment_id: existingCommentId, @@ -214,23 +238,23 @@ export class BitbucketProvider extends GitProviderRoot { }); const prResult: PullRequestMessageResult = { - posted: pullRequestComment?.data?.id > 0, + posted: (pullRequestComment?.data?.id || -1) > 0, providerResult: pullRequestComment, }; return prResult; } else { // Create new comment if no existing comment was found - uxLog(this, c.grey("[Bitbucket integration] Adding Pull Request Comment on Bitbucket...")); + uxLog(this, c.grey('[Bitbucket integration] Adding Pull Request Comment on Bitbucket...')); const pullRequestComment = await this.bitbucket.repositories.createPullRequestComment({ - workspace: workspace, + workspace: workspace || '', repo_slug: repoSlug, pull_request_id: pullRequestId, _body: commentBody, }); const prResult: PullRequestMessageResult = { - posted: pullRequestComment?.data?.id > 0, + posted: (pullRequestComment?.data?.id || -1) > 0, providerResult: pullRequestComment, }; return prResult; @@ -239,8 +263,8 @@ export class BitbucketProvider extends GitProviderRoot { private completePullRequestInfo(prData: Schema.Pullrequest) { const prInfo: any = Object.assign({}, prData); - prInfo.sourceBranch = prData?.source?.branch?.name || ""; - prInfo.targetBranch = prData?.destination?.branch?.name || ""; + prInfo.sourceBranch = prData?.source?.branch?.name || ''; + prInfo.targetBranch = prData?.destination?.branch?.name || ''; return prInfo; } } diff --git a/src/common/gitProvider/gitProviderRoot.ts b/src/common/gitProvider/gitProviderRoot.ts index 663a355ce..66793519a 100644 --- a/src/common/gitProvider/gitProviderRoot.ts +++ b/src/common/gitProvider/gitProviderRoot.ts @@ -1,32 +1,33 @@ -import { SfdxError } from "@salesforce/core"; -import * as c from "chalk"; -import { PullRequestMessageRequest, PullRequestMessageResult } from "."; -import { uxLog } from "../utils"; +import { SfError } from "@salesforce/core"; +import c from "chalk"; +import { PullRequestMessageRequest, PullRequestMessageResult } from "./index.js"; +import { uxLog } from "../utils/index.js"; export abstract class GitProviderRoot { - protected serverUrl: string; - protected token: string; + public serverUrl: string | null; + public token: string; + public getLabel(): string { - throw new SfdxError("getLabel should be implemented on this call"); + throw new SfError("getLabel should be implemented on this call"); } - public async getBranchDeploymentCheckId(gitBranch: string): Promise { + public async getBranchDeploymentCheckId(gitBranch: string): Promise { uxLog(this, `Method getBranchDeploymentCheckId(${gitBranch}) is not implemented yet on ${this.getLabel()}`); return null; } - public async getPullRequestDeploymentCheckId(): Promise { + public async getPullRequestDeploymentCheckId(): Promise { uxLog(this, `Method getPullRequestDeploymentCheckId() is not implemented yet on ${this.getLabel()}`); return null; } - public async getCurrentJobUrl(): Promise { + public async getCurrentJobUrl(): Promise { uxLog(this, `Method getCurrentJobUrl is not implemented yet on ${this.getLabel()}`); return null; } - public async getCurrentBranchUrl(): Promise { + public async getCurrentBranchUrl(): Promise { uxLog(this, `Method getCurrentBranchUrl is not implemented yet on ${this.getLabel()}`); return null; } @@ -40,16 +41,17 @@ export abstract class GitProviderRoot { uxLog(this, c.yellow("Method postPullRequestMessage is not yet implemented on " + this.getLabel() + " to post " + JSON.stringify(prMessage))); return { posted: false, providerResult: { error: "Not implemented in sfdx-hardis" } }; } - + /* jscpd:ignore-start */ // Do not make crash the whole process in case there is an issue with integration public async tryPostPullRequestMessage(prMessage: PullRequestMessageRequest): Promise { - let prResult: PullRequestMessageResult = null; + let prResult: PullRequestMessageResult | null = null; try { prResult = await this.postPullRequestMessage(prMessage); } catch (e) { - uxLog(this, c.yellow(`[GitProvider] Error while trying to post pull request message.\n${e.message}\n${e.stack}`)); + uxLog(this, c.yellow(`[GitProvider] Error while trying to post pull request message.\n${(e as Error).message}\n${(e as Error).stack}`)); prResult = { posted: false, providerResult: { error: e } }; } return prResult; } + /* jscpd:ignore-end */ } diff --git a/src/common/gitProvider/github.ts b/src/common/gitProvider/github.ts index f0a8beae1..06c18d36b 100644 --- a/src/common/gitProvider/github.ts +++ b/src/common/gitProvider/github.ts @@ -1,20 +1,22 @@ import * as github from "@actions/github"; -import * as c from "chalk"; -import { GitProviderRoot } from "./gitProviderRoot"; -import { getCurrentGitBranch, git, uxLog } from "../utils"; -import { PullRequestMessageRequest, PullRequestMessageResult } from "."; -import { GitHub } from "@actions/github/lib/utils"; +import c from "chalk"; +import { GitProviderRoot } from "./gitProviderRoot.js"; +import { getCurrentGitBranch, git, uxLog } from "../utils/index.js"; +import { PullRequestMessageRequest, PullRequestMessageResult } from "./index.js"; +import { GitHub } from "@actions/github/lib/utils.js"; +import { CONSTANTS } from "../../config/index.js"; export class GithubProvider extends GitProviderRoot { private octokit: InstanceType; - private repoOwner: string; - private repoName: string; + private repoOwner: string | null; + private repoName: string | null; + public serverUrl: string | null; constructor() { super(); const tokenName = process.env.CI_SFDX_HARDIS_GITHUB_TOKEN ? "CI_SFDX_HARDIS_GITHUB_TOKEN" : process.env.PAT ? "PAT" : "GITHUB_TOKEN"; const token = process.env[tokenName]; - this.octokit = github.getOctokit(token); + this.octokit = github.getOctokit(token || ""); this.repoOwner = github?.context?.repo?.owner || null; this.repoName = github?.context?.repo?.repo || null; this.serverUrl = github?.context?.serverUrl || null; @@ -24,14 +26,14 @@ export class GithubProvider extends GitProviderRoot { return "sfdx-hardis GitHub connector"; } - public async getBranchDeploymentCheckId(gitBranch: string): Promise { + public async getBranchDeploymentCheckId(gitBranch: string): Promise { let deploymentCheckId = null; const repoOwner = github?.context?.repo?.owner || null; const repoName = github?.context?.repo?.repo || null; uxLog(this, c.grey("[GitHub integration] Listing previously closed Pull Requests")); const latestPullRequestsOnBranch = await this.octokit.rest.pulls.list({ - owner: repoOwner, - repo: repoName, + owner: repoOwner || "", + repo: repoName || "", state: "closed", direction: "desc", per_page: 10, @@ -40,17 +42,17 @@ export class GithubProvider extends GitProviderRoot { if (latestPullRequestsOnBranch.data.length > 0) { const latestPullRequest = latestPullRequestsOnBranch.data[0]; const latestPullRequestId = latestPullRequest.number; - deploymentCheckId = await this.getDeploymentIdFromPullRequest(latestPullRequestId, repoOwner, repoName, deploymentCheckId, latestPullRequest); + deploymentCheckId = await this.getDeploymentIdFromPullRequest(latestPullRequestId, repoOwner || "", repoName || "", deploymentCheckId, latestPullRequest); } return deploymentCheckId; } - public async getPullRequestDeploymentCheckId(): Promise { + public async getPullRequestDeploymentCheckId(): Promise { const pullRequestInfo = await this.getPullRequestInfo(); if (pullRequestInfo) { const repoOwner = github?.context?.repo?.owner || null; const repoName = github?.context?.repo?.repo || null; - return await this.getDeploymentIdFromPullRequest(pullRequestInfo.number, repoOwner, repoName, null, pullRequestInfo); + return await this.getDeploymentIdFromPullRequest(pullRequestInfo.number, repoOwner || "", repoName || "", null, pullRequestInfo); } return null; } @@ -69,8 +71,8 @@ export class GithubProvider extends GitProviderRoot { issue_number: latestPullRequestId, }); for (const existingComment of existingComments.data) { - if (existingComment.body.includes("/gm.exec(existingComment.body); + if ((existingComment.body || "").includes("/gm.exec(existingComment.body || ""); if (matches) { deploymentCheckId = matches[1]; uxLog(this, c.gray(`Found deployment id ${deploymentCheckId} on PR #${latestPullRequestId} ${latestPullRequest.title}`)); @@ -82,13 +84,13 @@ export class GithubProvider extends GitProviderRoot { } // Returns current job URL - public async getCurrentJobUrl(): Promise { + public async getCurrentJobUrl(): Promise { try { const runId = github?.context?.runId; if (this.repoOwner && this.repoName && this.serverUrl && runId) { return `${this.serverUrl}/${this.repoOwner}/${this.repoName}/actions/runs/${runId}`; } - } catch (err) { + } catch (err: any) { uxLog(this, c.yellow("[GitHub integration]" + err.message)); } if (process.env.GITHUB_JOB_URL) { @@ -98,13 +100,13 @@ export class GithubProvider extends GitProviderRoot { } // Returns current job URL - public async getCurrentBranchUrl(): Promise { + public async getCurrentBranchUrl(): Promise { try { const branch = github?.context?.ref || null; if (this.repoOwner && this.repoName && this.serverUrl && branch) { return `${this.serverUrl}/${this.repoOwner}/${this.repoName}/tree/${branch}`; } - } catch (err) { + } catch (err: any) { uxLog(this, c.yellow("[GitHub integration]" + err.message)); } return null; @@ -117,7 +119,7 @@ export class GithubProvider extends GitProviderRoot { if (prNumber !== null && this.repoOwner !== null && prNumber !== null) { const pullRequest = await this.octokit.rest.pulls.get({ owner: this.repoOwner, - repo: this.repoName, + repo: this.repoName || "", pull_number: prNumber, }); // Add cross git provider properties used by sfdx-hardis @@ -165,7 +167,7 @@ export class GithubProvider extends GitProviderRoot { }, ); } catch (error) { - uxLog(this, c.yellow(`[GitHub Integration] Error while calling GraphQL Api to list PR on commit ${sha}\n${error.message}`)); + uxLog(this, c.yellow(`[GitHub Integration] Error while calling GraphQL Api to list PR on commit ${sha}\n${(error as any).message}`)); } if (graphQlRes?.repository?.commit?.associatedPullRequests?.edges?.length > 0) { const currentGitBranch = await getCurrentGitBranch(); @@ -198,7 +200,7 @@ export class GithubProvider extends GitProviderRoot { ${prMessage.message} -_Powered by [sfdx-hardis](https://sfdx-hardis.cloudity.com) from job [${githubWorkflowName}](${githubJobUrl})_ +_Powered by [sfdx-hardis](${CONSTANTS.DOC_URL_ROOT}) from job [${githubWorkflowName}](${githubJobUrl})_ `; // Add deployment id if present @@ -208,11 +210,11 @@ _Powered by [sfdx-hardis](https://sfdx-hardis.cloudity.com) from job [${githubWo // Check for existing note from a previous run uxLog(this, c.grey("[GitHub integration] Listing comments of Pull Request...")); const existingComments = await this.octokit.rest.issues.listComments({ - owner: this.repoOwner, + owner: this.repoOwner || "", repo: this.repoName, issue_number: pullRequestId, }); - let existingCommentId = null; + let existingCommentId: number | null = null; for (const existingComment of existingComments.data) { if (existingComment?.body?.includes(``)) { existingCommentId = existingComment.id; @@ -224,7 +226,7 @@ _Powered by [sfdx-hardis](https://sfdx-hardis.cloudity.com) from job [${githubWo // Update existing note uxLog(this, c.grey("[GitHub integration] Updating Pull Request Comment on GitHub...")); const githubCommentEditResult = await this.octokit.rest.issues.updateComment({ - owner: this.repoOwner, + owner: this.repoOwner || "", repo: this.repoName, issue_number: pullRequestId, comment_id: existingCommentId, @@ -239,7 +241,7 @@ _Powered by [sfdx-hardis](https://sfdx-hardis.cloudity.com) from job [${githubWo // Create new note if no existing not was found uxLog(this, c.grey("[GitHub integration] Adding Pull Request Comment on GitHub...")); const githubCommentCreateResult = await this.octokit.rest.issues.createComment({ - owner: this.repoOwner, + owner: this.repoOwner || "", repo: this.repoName, issue_number: pullRequestId, body: messageBody, diff --git a/src/common/gitProvider/gitlab.ts b/src/common/gitProvider/gitlab.ts index 414888f55..a2f09c5e7 100644 --- a/src/common/gitProvider/gitlab.ts +++ b/src/common/gitProvider/gitlab.ts @@ -1,18 +1,21 @@ import { Gitlab } from "@gitbeaker/node"; -import * as c from "chalk"; -import { PullRequestMessageRequest, PullRequestMessageResult } from "."; -import { getCurrentGitBranch, git, uxLog } from "../utils"; -import { GitProviderRoot } from "./gitProviderRoot"; +import c from "chalk"; +import { PullRequestMessageRequest, PullRequestMessageResult } from "./index.js"; +import { getCurrentGitBranch, git, uxLog } from "../utils/index.js"; +import { GitProviderRoot } from "./gitProviderRoot.js"; +import { CONSTANTS } from "../../config/index.js"; export class GitlabProvider extends GitProviderRoot { private gitlabApi: InstanceType; + public serverUrl: string; + public token: string; constructor() { super(); // Gitlab URL is always provided by default CI variables - this.serverUrl = process.env.CI_SERVER_URL; + this.serverUrl = process.env.CI_SERVER_URL || ""; // It's better to have a project token defined in a CI_SFDX_HARDIS_GITLAB_TOKEN variable, to have the rights to act on Pull Requests - this.token = process.env.CI_SFDX_HARDIS_GITLAB_TOKEN || process.env.ACCESS_TOKEN; + this.token = process.env.CI_SFDX_HARDIS_GITLAB_TOKEN || process.env.ACCESS_TOKEN || ""; this.gitlabApi = new Gitlab({ host: this.serverUrl, token: this.token, @@ -25,7 +28,7 @@ export class GitlabProvider extends GitProviderRoot { } // Returns current job URL - public async getCurrentJobUrl(): Promise { + public async getCurrentJobUrl(): Promise { if (process.env.CI_JOB_URL) { return process.env.CI_JOB_URL; } @@ -33,7 +36,7 @@ export class GitlabProvider extends GitProviderRoot { } // Returns current job URL - public async getCurrentBranchUrl(): Promise { + public async getCurrentBranchUrl(): Promise { if (process.env.CI_PROJECT_URL && process.env.CI_COMMIT_REF_NAME) return `${process.env.CI_PROJECT_URL}/-/tree/${process.env.CI_COMMIT_REF_NAME}`; return null; } @@ -45,7 +48,7 @@ export class GitlabProvider extends GitProviderRoot { const mrNumber = process.env.CI_MERGE_REQUEST_IID || null; if (mrNumber !== null) { const mergeRequests = await this.gitlabApi.MergeRequests.all({ - projectId: projectId, + projectId: projectId || "", iids: [parseInt(mrNumber)], }); if (mergeRequests.length > 0) { @@ -55,7 +58,7 @@ export class GitlabProvider extends GitProviderRoot { // Case when we find MR from a commit const sha = await git().revparse(["HEAD"]); const latestMergeRequestsOnBranch = await this.gitlabApi.MergeRequests.all({ - projectId: projectId, + projectId: projectId || "", state: "merged", sort: "desc", sha: sha, @@ -71,11 +74,11 @@ export class GitlabProvider extends GitProviderRoot { return null; } - public async getBranchDeploymentCheckId(gitBranch: string): Promise { + public async getBranchDeploymentCheckId(gitBranch: string): Promise { let deploymentCheckId = null; const projectId = process.env.CI_PROJECT_ID || null; const latestMergeRequestsOnBranch = await this.gitlabApi.MergeRequests.all({ - projectId: projectId, + projectId: projectId || "", state: "merged", sort: "desc", targetBranch: gitBranch, @@ -83,16 +86,16 @@ export class GitlabProvider extends GitProviderRoot { if (latestMergeRequestsOnBranch.length > 0) { const latestMergeRequest = latestMergeRequestsOnBranch[0]; const latestMergeRequestId = latestMergeRequest.iid; - deploymentCheckId = await this.getDeploymentIdFromPullRequest(projectId, latestMergeRequestId, deploymentCheckId, latestMergeRequest); + deploymentCheckId = await this.getDeploymentIdFromPullRequest(projectId || "", latestMergeRequestId, deploymentCheckId, latestMergeRequest); } return deploymentCheckId; } - public async getPullRequestDeploymentCheckId(): Promise { + public async getPullRequestDeploymentCheckId(): Promise { const pullRequestInfo = await this.getPullRequestInfo(); if (pullRequestInfo) { const projectId = process.env.CI_PROJECT_ID || null; - return await this.getDeploymentIdFromPullRequest(projectId, pullRequestInfo.iid, null, pullRequestInfo); + return await this.getDeploymentIdFromPullRequest(projectId || "", pullRequestInfo.iid, null, pullRequestInfo); } return null; } @@ -129,7 +132,7 @@ export class GitlabProvider extends GitProviderRoot { ${prMessage.message} -_Powered by [sfdx-hardis](https://sfdx-hardis.cloudity.com) from job [${gitlabCiJobName}](${gitlabCIJobUrl})_ +_Powered by [sfdx-hardis](${CONSTANTS.DOC_URL_ROOT}) from job [${gitlabCiJobName}](${gitlabCIJobUrl})_ `; // Add deployment id if present @@ -139,7 +142,7 @@ _Powered by [sfdx-hardis](https://sfdx-hardis.cloudity.com) from job [${gitlabCi // Check for existing note from a previous run uxLog(this, c.grey("[Gitlab integration] Listing Notes of Merge Request...")); const existingNotes = await this.gitlabApi.MergeRequestNotes.all(projectId, mergeRequestId); - let existingNoteId = null; + let existingNoteId: number | null = null; for (const existingNote of existingNotes) { if (existingNote.body.includes(``)) { existingNoteId = existingNote.id; diff --git a/src/common/gitProvider/index.ts b/src/common/gitProvider/index.ts index 1a2d176e9..62d328cbe 100644 --- a/src/common/gitProvider/index.ts +++ b/src/common/gitProvider/index.ts @@ -1,16 +1,16 @@ -import * as c from "chalk"; -import { getCurrentGitBranch, isCI, uxLog } from "../utils"; -import { AzureDevopsProvider } from "./azureDevops"; -import { GithubProvider } from "./github"; -import { GitlabProvider } from "./gitlab"; -import { GitProviderRoot } from "./gitProviderRoot"; -import { BitbucketProvider } from "./bitbucket"; +import c from "chalk"; +import { getCurrentGitBranch, isCI, uxLog } from "../utils/index.js"; +import { AzureDevopsProvider } from "./azureDevops.js"; +import { GithubProvider } from "./github.js"; +import { GitlabProvider } from "./gitlab.js"; +import { GitProviderRoot } from "./gitProviderRoot.js"; +import { BitbucketProvider } from "./bitbucket.js"; import Debug from "debug"; -import { getEnvVar } from "../../config"; +import { CONSTANTS, getEnvVar } from "../../config/index.js"; const debug = Debug("sfdxhardis"); export abstract class GitProvider { - static getInstance(): GitProviderRoot { + static getInstance(): GitProviderRoot | null { // Azure if (process.env.SYSTEM_ACCESSTOKEN) { const serverUrl = process.env.SYSTEM_COLLECTIONURI || null; @@ -75,7 +75,7 @@ export abstract class GitProvider { uxLog(this, c.yellow("[Git Provider] WARNING: No git provider found to post pull request comment. Maybe you should configure it ?")); uxLog( this, - c.yellow("[Git Provider] See documentation: https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-integrations-home/#git-providers"), + c.yellow(`[Git Provider] See documentation: ${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-integrations-home/#git-providers`), ); return; } @@ -109,7 +109,7 @@ export abstract class GitProvider { } } - static async getDeploymentCheckId(): Promise { + static async getDeploymentCheckId(): Promise { const gitProvider = GitProvider.getInstance(); if (gitProvider == null) { return null; @@ -120,15 +120,15 @@ export abstract class GitProvider { return gitProvider.getPullRequestDeploymentCheckId(); } // Classic way: get deployment check Id from latest merged Pull Request - const currentGitBranch = await getCurrentGitBranch(); + const currentGitBranch = await getCurrentGitBranch() || ""; return gitProvider.getBranchDeploymentCheckId(currentGitBranch); } catch (e) { - uxLog(this, c.yellow(`Error while trying to retrieve deployment check id:\n${e.message}`)); + uxLog(this, c.yellow(`Error while trying to retrieve deployment check id:\n${(e as Error).message}`)); return null; } } - static async getCurrentBranchUrl(): Promise { + static async getCurrentBranchUrl(): Promise { const gitProvider = GitProvider.getInstance(); if (gitProvider == null) { return null; @@ -136,7 +136,7 @@ export abstract class GitProvider { return gitProvider.getCurrentBranchUrl(); } - static async getJobUrl(): Promise { + static async getJobUrl(): Promise { const gitProvider = GitProvider.getInstance(); if (gitProvider == null) { return null; @@ -155,9 +155,9 @@ export abstract class GitProvider { prInfo = await gitProvider.getPullRequestInfo(); debug("[GitProvider][PR Info] " + JSON.stringify(prInfo, null, 2)); } catch (e) { - uxLog(this, c.yellow("[GitProvider] Unable to get Pull Request info: " + e.message)); + uxLog(this, c.yellow("[GitProvider] Unable to get Pull Request info: " + (e as Error).message)); uxLog(this, c.yellow(`[GitProvider] Maybe you misconfigured your ${gitProvider.getLabel()} ?`)); - uxLog(this, c.yellow(`[GitProvider] See https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-integrations-home/#git-providers`)); + uxLog(this, c.yellow(`[GitProvider] See ${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-integrations-home/#git-providers`)); prInfo = null; } return prInfo; diff --git a/src/common/gitProvider/utilsMarkdown.ts b/src/common/gitProvider/utilsMarkdown.ts index 76a2c4544..1bb84803b 100644 --- a/src/common/gitProvider/utilsMarkdown.ts +++ b/src/common/gitProvider/utilsMarkdown.ts @@ -1,14 +1,14 @@ export function deployErrorsToMarkdown(errorsAndTips: Array) { let md = "## Deployment errors\n\n"; for (const err of errorsAndTips) { - const errorMessage = err.error.message.trim().includes("Error ") - ? err.error.message - .trim() - .replace("Error ", "") - .replace(" ", "
") - .trim() - .replace(/(.*)/gm, `$1 `) - : err.error.message.trim(); + const errorMessage = (err as any)?.error?.message?.trim().includes("Error ") + ? (err as any)?.error?.message + .trim() + .replace("Error ", "") + .replace(" ", "
") + .trim() + .replace(/(.*)/gm, `$1 `) + : (err as any)?.error?.message?.trim() || "WE SHOULD NOT GO THERE: PLEASE DECLARE AN ISSUE"; // sfdx-hardis tip if (err.tip) { const aiText = err?.tipFromAi?.promptResponse @@ -18,7 +18,7 @@ export function deployErrorsToMarkdown(errorsAndTips: Array) { : ""; md += `
🛠️ ${errorMessage} -_${err.tip.label}_ +_[**${err.tip.label}**](${err.tip.docUrl})_ ${err.tip.message.replace(/:\n-/gm, `:\n\n-`)} ${aiText} diff --git a/src/common/keyValueProviders/keyValueXyz.ts b/src/common/keyValueProviders/keyValueXyz.ts deleted file mode 100644 index 639e62244..000000000 --- a/src/common/keyValueProviders/keyValueXyz.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { SfdxError } from "@salesforce/core"; -import axios from "axios"; -import * as c from "chalk"; -import { getConfig, setConfig } from "../../config"; -import { uxLog } from "../utils"; -import { KeyValueProviderInterface } from "../utils/keyValueUtils"; -import { prompts } from "../utils/prompts"; - -export class KeyValueXyzProvider implements KeyValueProviderInterface { - name = "keyvalue.xyz"; - description = "keyvalue.xyz external service (api token, no auth). Seems down for now."; - keyValueUrl = null; - - async initialize() { - await this.manageKeyValueXyzAuth(null); - return this.keyValueUrl !== null; - } - - async getValue(key: string | null = null) { - await this.manageKeyValueXyzAuth(key); - const response = await axios({ - method: "get", - url: this.keyValueUrl, - responseType: "json", - }); - return response.status === 200 ? response.data || {} : null; - } - - async setValue(key: string | null = null, value: any) { - await this.manageKeyValueXyzAuth(key); - await axios({ - method: "post", - url: this.keyValueUrl, - responseType: "json", - data: value, - }); - return true; - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async updateActiveScratchOrg(_scratchOrg: any, _keyValues: any) { - return null; - } - - async manageKeyValueXyzAuth(key: string | null = null) { - if (this.keyValueUrl == null) { - const config = await getConfig("user"); - const apiKey = config.keyValueXyzApiKey || process.env.KEY_VALUE_XYZ_API_KEY; - if (apiKey === null) { - throw new SfdxError(c.red("You need to define a keyvalue.xyz apiKey in config.keyValueXyzApiKey or env var KEY_VALUE_XYZ_API_KEY")); - } - if (key === null) { - const projectName = config.projectName || "default"; - key = `pool_${projectName}`; - } - this.keyValueUrl = `https://api.keyvalue.xyz/${apiKey}/${key}`; - uxLog(this, c.grey("keyvalue.xyz url: " + this.keyValueUrl)); - } - } - - async userSetup() { - const config = await getConfig("user"); - const projectName = config.projectName || "default"; - const keyValueUrl = `https://api.keyvalue.xyz/new/pool_${projectName}`; - const resp = await axios({ - method: "post", - url: keyValueUrl, - responseType: "json", - }); - const keyValueXyzApiKey = resp.data; - await setConfig("user", { keyValueXyzApiKey: keyValueXyzApiKey }); - uxLog(this, c.cyan("Created new keyvalue.xyz API key and stored in local untracked config")); - uxLog(this, c.yellow(`In CI config, set protected variable ${c.bold(c.green("KEY_VALUE_XYZ_API_KEY = " + keyValueXyzApiKey))}`)); - return true; - } - - async userAuthenticate() { - const config = await getConfig("user"); - const response = await prompts([ - { - type: "text", - name: "keyValueXyzApiKey", - message: c.cyanBright("Please input keyvalue.xyz API KEY (ask the value to your tech lead or look in CI variable KEY_VALUE_XYZ_API_KEY )"), - initial: config.keyValueXyzApiKey || null, - }, - ]); - await setConfig("user", { keyValueXyzApiKey: response.keyValueXyzApiKey }); - return true; - } -} diff --git a/src/common/keyValueProviders/kvdbIo.ts b/src/common/keyValueProviders/kvdbIo.ts deleted file mode 100644 index 3456aa534..000000000 --- a/src/common/keyValueProviders/kvdbIo.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { SfdxError } from "@salesforce/core"; -import axios from "axios"; -import * as c from "chalk"; -import * as crypto from "crypto"; -import { getConfig, setConfig } from "../../config"; -import { uxLog } from "../utils"; -import { KeyValueProviderInterface } from "../utils/keyValueUtils"; -import { setPoolStorage } from "../utils/poolUtils"; -import { prompts } from "../utils/prompts"; - -export class KvdbIoProvider implements KeyValueProviderInterface { - name = "kvdb.io"; - description = "kvdb.io external service (api token, auth with Bearer). Requires paid plan, or renewing config every 2 weeks"; - kvdbIoUrl = null; - kvdbIoSecretKey = null; - - async initialize() { - await this.manageKvdbIoAuth(null); - return this.kvdbIoUrl !== null && this.kvdbIoSecretKey !== null; - } - - async getValue(key: string | null = null) { - await this.manageKvdbIoAuth(key); - const response = await axios({ - method: "get", - url: this.kvdbIoUrl, - responseType: "json", - headers: { - Authorization: "Bearer " + this.kvdbIoSecretKey, - }, - }); - return response.status === 200 ? response.data || {} : null; - } - - async setValue(key: string | null = null, value: any) { - await this.manageKvdbIoAuth(key); - const resp = await axios({ - method: "post", - url: this.kvdbIoUrl, - responseType: "json", - data: JSON.stringify(value), - headers: { - Authorization: "Bearer " + this.kvdbIoSecretKey, - }, - }); - return resp.status === 200; - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async updateActiveScratchOrg(_scratchOrg: any, _keyValues: any) { - return null; - } - - async manageKvdbIoAuth(key: string | null = null) { - if (this.kvdbIoUrl == null) { - const config = await getConfig("user"); - const kvdbIoBucketId = config.kvdbIoBucketId || process.env.KVDB_IO_BUCKET_ID; - if (kvdbIoBucketId == null) { - throw new SfdxError(c.red("You need to define an kvdb.io apiKey in config.kvdbIoBucketId or CI env var KVDB_IO_BUCKET_ID")); - } - const kvdbIoSecretKey = config.kvdbIoSecretKey || process.env.KVDB_IO_SECRET_KEY; - if (kvdbIoSecretKey == null) { - throw new SfdxError(c.red("You need to define an kvdb.io secretKey in config.kvdbIoSecretKey or CI env var KVDB_IO_SECRET_KEY")); - } - if (key == null) { - const projectName = config.projectName || "default"; - key = `pool_${projectName}`; - } - this.kvdbIoUrl = `https://kvdb.io/${kvdbIoBucketId}/${key}`; - this.kvdbIoSecretKey = kvdbIoSecretKey; - uxLog(this, c.grey("kvdb.io url: " + this.kvdbIoUrl)); - } - } - - async userSetup() { - const config = await getConfig("user"); - const projectName = config.projectName || "default"; - const randomSecretKey = crypto.randomBytes(48).toString("hex"); - const kvdbIoUrl = `https://kvdb.io/`; - const resp = await axios({ - method: "post", - url: kvdbIoUrl, - responseType: "json", - data: { - email: `${projectName}@hardis-scratch-org-pool.com`, - secret_key: randomSecretKey, - }, - }); - const kvdbIoBucketId = resp.data; - await setConfig("user", { kvdbIoSecretKey: randomSecretKey, kvdbIoBucketId: kvdbIoBucketId }); - await setPoolStorage({}); - uxLog(this, c.cyan("Created new kvdb.io bucket and stored in local untracked config")); - uxLog( - this, - c.yellow( - `In future CI config, set protected variables ${c.bold(c.green("KVDB_IO_SECRET_KEY = " + randomSecretKey))} and ${c.bold( - c.green("KVDB_IO_BUCKET_ID = " + kvdbIoBucketId), - )}`, - ), - ); - return true; - } - - async userAuthenticate() { - const config = await getConfig("user"); - const response = await prompts([ - { - type: "text", - name: "kvdbIoBucketId", - message: c.cyanBright("Please input kvdb.io BUCKET ID (ask the value to your tech lead or look in CI variable KVDB_IO_BUCKET_ID )"), - initial: config.kvdbIoSecretKey || null, - }, - { - type: "text", - name: "kvdbIoSecretKey", - message: c.cyanBright("Please input kvdb.io BUCKET SECRET KEY (ask the value to your tech lead or look in CI variable KVDB_IO_SECRET_KEY )"), - initial: config.kvdbIoSecretKey || null, - }, - ]); - await setConfig("user", { kvdbIoBucketId: response.kvdbIoBucketId, kvdbIoSecretKey: response.kvdbIoSecretKey }); - return true; - } -} diff --git a/src/common/keyValueProviders/localtest.ts b/src/common/keyValueProviders/localtest.ts index 50c8343e8..5649fa636 100644 --- a/src/common/keyValueProviders/localtest.ts +++ b/src/common/keyValueProviders/localtest.ts @@ -1,15 +1,15 @@ -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; -import * as os from "os"; -import { getConfig } from "../../config"; -import { uxLog } from "../utils"; -import { KeyValueProviderInterface } from "../utils/keyValueUtils"; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; +import * as os from 'os'; +import { getConfig } from '../../config/index.js'; +import { uxLog } from '../utils/index.js'; +import { KeyValueProviderInterface } from '../utils/keyValueUtils.js'; export class LocalTestProvider implements KeyValueProviderInterface { - name = "localtest"; - description = "Writes in a local file (just for tests, can not work in CI)"; - poolStorageLocalFileName = null; + name = 'localtest'; + description = 'Writes in a local file (just for tests, can not work in CI)'; + poolStorageLocalFileName: string | null = null; async initialize() { await this.managePoolStorageLocalFileName(); @@ -18,15 +18,15 @@ export class LocalTestProvider implements KeyValueProviderInterface { async getValue(key: string | null = null) { await this.managePoolStorageLocalFileName(key); - if (fs.existsSync(this.poolStorageLocalFileName)) { - return fs.readJsonSync(this.poolStorageLocalFileName); + if (fs.existsSync(this.poolStorageLocalFileName || '')) { + return fs.readJsonSync(this.poolStorageLocalFileName || ''); } return {}; } async setValue(key: string | null = null, value: any) { await this.managePoolStorageLocalFileName(key); - await fs.writeFile(this.poolStorageLocalFileName, JSON.stringify(value, null, 2), "utf8"); + await fs.writeFile(this.poolStorageLocalFileName || '', JSON.stringify(value, null, 2), 'utf8'); return true; } @@ -38,12 +38,12 @@ export class LocalTestProvider implements KeyValueProviderInterface { async managePoolStorageLocalFileName(key: string | null = null) { if (this.poolStorageLocalFileName == null) { if (key === null) { - const config = await getConfig("user"); - const projectName = config.projectName || "default"; + const config = await getConfig('user'); + const projectName = config.projectName || 'default'; key = `pool_${projectName}`; } this.poolStorageLocalFileName = path.join(os.homedir(), `poolStorage_${key}.json`); - uxLog(this, c.grey("Local test storage file: " + this.poolStorageLocalFileName)); + uxLog(this, c.grey('Local test storage file: ' + this.poolStorageLocalFileName)); } } diff --git a/src/common/keyValueProviders/redis.ts b/src/common/keyValueProviders/redis.ts deleted file mode 100644 index 9b67f6594..000000000 --- a/src/common/keyValueProviders/redis.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { SfdxError } from "@salesforce/core"; -import * as Keyv from "keyv"; -import * as c from "chalk"; -import { getConfig, setConfig } from "../../config"; -import { uxLog } from "../utils"; -import { KeyValueProviderInterface } from "../utils/keyValueUtils"; -import { setPoolStorage } from "../utils/poolUtils"; -import { prompts } from "../utils/prompts"; - -export class RedisProvider implements KeyValueProviderInterface { - name = "redis"; - description = "redis external service (redis secure db authentication)"; - keyv = null; - redisKey = null; - authError = false; - - async initialize() { - await this.manageRedisAuth("init"); - const connectionOk = this.keyv !== null; - await this.disconnectRedis(); - return connectionOk; - } - - async getValue(key: string | null = null) { - await this.manageRedisAuth(key); - const value = await this.keyv.get(this.redisKey); - await this.disconnectRedis(); - return value; - } - - async setValue(key: string | null = null, value: any) { - await this.manageRedisAuth(key); - await this.keyv.set(this.redisKey, value); - await this.disconnectRedis(); - return true; - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async updateActiveScratchOrg(_scratchOrg: any, _keyValues: any) { - return null; - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async manageRedisAuth(key: string | null = null) { - if (this.keyv == null) { - const config = await getConfig("user"); - const redisAuthUrl = config.redisAuthUrl || process.env.REDIS_AUTH_URL; - if (redisAuthUrl == null) { - throw new SfdxError(c.red("You need to define an redis auth URL config.redisAuthUrl or CI env var REDIS_AUTH_URL")); - } - if (this.redisKey == null) { - const projectName = config.projectName || "default"; - this.redisKey = `pool${projectName}`; - } - this.keyv = new Keyv(redisAuthUrl, { disable_resubscribing: true, autoResubscribe: false, maxRetriesPerRequest: 10 }); - this.keyv.on("error", (err) => { - uxLog(this, "[pool]" + c.red("Redis connection Error :" + err)); - }); - uxLog(this, c.grey("[pool] Requested redis connection")); - } - } - - async disconnectRedis() { - if (this.keyv?.opts?.store?.redis) { - // Kill redis connection - this.keyv.opts.store.redis.disconnect(); - this.keyv = null; - } - } - - async userSetup() { - const config = await getConfig("user"); - const projectName = config.projectName || "default"; - - uxLog(this, c.cyan(`You need a redis account. You can create one for free at ${c.bold("https://redis.com/try-free/")}`)); - uxLog( - this, - c.cyan("Create a database that you can name scratchPool, then build auth URL by appending default user password and public endpoint"), - ); - const response = await prompts([ - { - type: "text", - name: "redisAuthUrl", - message: c.cyanBright("Please enter authentication URL for Redis remote database"), - initial: config.redisAuthUrl || null, - }, - ]); - const redisAuthUrl = response.redisAuthUrl; - await setConfig("user", { redisAuthUrl: redisAuthUrl }); - await setPoolStorage({}); - uxLog(this, c.cyan(`Initialized scratch org pool storage for ${projectName} on Redis`)); - uxLog(this, c.yellow(`In CI config, set protected variable ${c.bold(c.green("REDIS_AUTH_URL = " + redisAuthUrl))}}`)); - return true; - } - - async userAuthenticate() { - const config = await getConfig("user"); - const response = await prompts([ - { - type: "text", - name: "redisAuthUrl", - message: c.cyanBright("Please enter authentication URL for Redis remote database"), - initial: config.redisAuthUrl || null, - }, - ]); - const redisAuthUrl = response.redisAuthUrl; - await setConfig("user", { redisAuthUrl: redisAuthUrl }); - return true; - } -} diff --git a/src/common/keyValueProviders/salesforce.ts b/src/common/keyValueProviders/salesforce.ts index fd5c0388b..ef4f8492e 100644 --- a/src/common/keyValueProviders/salesforce.ts +++ b/src/common/keyValueProviders/salesforce.ts @@ -1,21 +1,21 @@ import { Connection } from "@salesforce/core"; -import * as c from "chalk"; +import c from "chalk"; import * as he from "he"; import * as path from "path"; -import { getConfig } from "../../config"; -import { PACKAGE_ROOT_DIR } from "../../settings"; -import { uxLog } from "../utils"; -import { soqlQuery } from "../utils/apiUtils"; -import { deployMetadatas } from "../utils/deployUtils"; -import { KeyValueProviderInterface } from "../utils/keyValueUtils"; -import { setPoolStorage } from "../utils/poolUtils"; +import { getConfig } from "../../config/index.js"; +import { PACKAGE_ROOT_DIR } from "../../settings.js"; +import { uxLog } from "../utils/index.js"; +import { soqlQuery } from "../utils/apiUtils.js"; +import { deployMetadatas } from "../utils/deployUtils.js"; +import { KeyValueProviderInterface } from "../utils/keyValueUtils.js"; +import { setPoolStorage } from "../utils/poolUtils.js"; export class SalesforceProvider implements KeyValueProviderInterface { name = "salesforce"; description = "Use a custom object on a Salesforce org (usually DevHub) to store scratch org pool tech info"; - conn: Connection = null; - recordName = null; + conn: Connection | null = null; + recordName: string | null = null; async initialize(options) { await this.manageSfdcOrgAuth(options); @@ -24,6 +24,9 @@ export class SalesforceProvider implements KeyValueProviderInterface { // eslint-disable-next-line @typescript-eslint/no-unused-vars async getValue(_key: string | null = null) { + if (!this.conn) { + return "ERROR"; + } await this.manageSfdcOrgAuth(); // Single record upsert const queryRes = await soqlQuery( @@ -40,6 +43,9 @@ export class SalesforceProvider implements KeyValueProviderInterface { // eslint-disable-next-line @typescript-eslint/no-unused-vars async setValue(_key: string | null = null, value: any) { await this.manageSfdcOrgAuth(); + if (!this.conn) { + return false; + } // Single record upsert const queryRes = await soqlQuery( `SELECT Id,Name,ValueText__c FROM SfdxHardisKeyValueStore__c WHERE Name='${this.recordName}' LIMIT 1`, @@ -63,6 +69,9 @@ export class SalesforceProvider implements KeyValueProviderInterface { } async updateActiveScratchOrg(scratchOrg: any, keyValues: any) { + if (!this.conn) { + return; + } const orgId = scratchOrg?.scratchOrgInfo?.orgId ? scratchOrg.scratchOrgInfo.orgId.slice(0, 15) : scratchOrg.Id.slice(0, 15); const activeScratchOrg: any = await this.conn.sobject("ActiveScratchOrg").findOne({ ScratchOrg: orgId }, { Id: true, Description: true }); keyValues.Id = activeScratchOrg.Id; @@ -88,7 +97,6 @@ export class SalesforceProvider implements KeyValueProviderInterface { try { await deployMetadatas({ deployDir: path.join(path.join(PACKAGE_ROOT_DIR, "defaults/utils/sfdxHardisKeyValueStore", ".")), - soap: true, targetUsername: options.devHubConn.options.authInfo.fields.username, }); } catch (e) { diff --git a/src/common/metadata-utils/index.ts b/src/common/metadata-utils/index.ts index 4d7e4fabc..660217d68 100644 --- a/src/common/metadata-utils/index.ts +++ b/src/common/metadata-utils/index.ts @@ -1,20 +1,29 @@ -import { SfdxError } from "@salesforce/core"; -import * as c from "chalk"; -import * as extractZip from "extract-zip"; -import * as fs from "fs-extra"; -import * as path from "path"; -import * as sortArray from "sort-array"; -import { elapseEnd, elapseStart, execCommand, execSfdxJson, filterPackageXml, git, isGitRepo, uxLog } from "../../common/utils"; -import { CONSTANTS } from "../../config"; -import { PACKAGE_ROOT_DIR } from "../../settings"; -import { getCache, setCache } from "../cache"; -import { buildOrgManifest } from "../utils/deployUtils"; -import { listMajorOrgs } from "../utils/orgConfigUtils"; -import { isSfdxProject } from "../utils/projectUtils"; -import { prompts } from "../utils/prompts"; -import { parsePackageXmlFile } from "../utils/xmlUtils"; -import { listMetadataTypes } from "./metadataList"; -import { FileStatusResult } from "simple-git"; +import { SfError } from '@salesforce/core'; +import c from 'chalk'; +import extractZip from 'extract-zip'; +import fs from 'fs-extra'; +import * as path from 'path'; +import sortArray from 'sort-array'; +import { + elapseEnd, + elapseStart, + execCommand, + execSfdxJson, + filterPackageXml, + git, + isGitRepo, + uxLog, +} from '../../common/utils/index.js'; +import { CONSTANTS } from '../../config/index.js'; +import { PACKAGE_ROOT_DIR } from '../../settings.js'; +import { getCache, setCache } from '../cache/index.js'; +import { buildOrgManifest } from '../utils/deployUtils.js'; +import { listMajorOrgs } from '../utils/orgConfigUtils.js'; +import { isSfdxProject } from '../utils/projectUtils.js'; +import { prompts } from '../utils/prompts.js'; +import { parsePackageXmlFile } from '../utils/xmlUtils.js'; +import { listMetadataTypes } from './metadataList.js'; +import { FileStatusResult } from 'simple-git'; class MetadataUtils { // Describe packageXml <=> metadata folder correspondance @@ -25,244 +34,244 @@ class MetadataUtils { const metadataTypesDescription = { // Metadatas to use for copy ApexClass: { - folder: "classes", - nameSuffixList: [".cls", ".cls-meta.xml"], - sfdxNameSuffixList: [".cls", "-meta.xml"], - permissionSetTypeName: "classAccesses", - permissionSetMemberName: "apexClass", + folder: 'classes', + nameSuffixList: ['.cls', '.cls-meta.xml'], + sfdxNameSuffixList: ['.cls', '-meta.xml'], + permissionSetTypeName: 'classAccesses', + permissionSetMemberName: 'apexClass', }, ApexComponent: { - folder: "components", - nameSuffixList: [".component", ".component-meta.xml"], - sfdxNameSuffixList: [".component", ".component-meta.xml"], + folder: 'components', + nameSuffixList: ['.component', '.component-meta.xml'], + sfdxNameSuffixList: ['.component', '.component-meta.xml'], }, ApexPage: { - folder: "pages", - nameSuffixList: [".page", ".page-meta.xml"], - sfdxNameSuffixList: [".page", "-meta.xml"], - permissionSetTypeName: "pageAccesses", - permissionSetMemberName: "apexPage", + folder: 'pages', + nameSuffixList: ['.page', '.page-meta.xml'], + sfdxNameSuffixList: ['.page', '-meta.xml'], + permissionSetTypeName: 'pageAccesses', + permissionSetMemberName: 'apexPage', }, ApexTrigger: { - folder: "triggers", - nameSuffixList: [".trigger", ".trigger-meta.xml"], - sfdxNameSuffixList: [".trigger", "-meta.xml"], + folder: 'triggers', + nameSuffixList: ['.trigger', '.trigger-meta.xml'], + sfdxNameSuffixList: ['.trigger', '-meta.xml'], }, ApprovalProcess: { - folder: "approvalProcesses", - nameSuffixList: [".approvalProcess"], - sfdxNameSuffixList: [".approvalProcess-meta.xml"], + folder: 'approvalProcesses', + nameSuffixList: ['.approvalProcess'], + sfdxNameSuffixList: ['.approvalProcess-meta.xml'], }, AuraDefinitionBundle: { - folder: "aura", - nameSuffixList: [""], - sfdxNameSuffixList: [""], + folder: 'aura', + nameSuffixList: [''], + sfdxNameSuffixList: [''], }, AuthProvider: { - folder: "authproviders", - nameSuffixList: [".authprovider"], - sfdxNameSuffixList: [".authprovider-meta.xml"], + folder: 'authproviders', + nameSuffixList: ['.authprovider'], + sfdxNameSuffixList: ['.authprovider-meta.xml'], }, LightningComponentBundle: { - folder: "lwc", - nameSuffixList: [""], - sfdxNameSuffixList: [""], + folder: 'lwc', + nameSuffixList: [''], + sfdxNameSuffixList: [''], }, ContentAsset: { - folder: "contentassets", - nameSuffixList: [".asset", ".asset-meta.xml"], - sfdxNameSuffixList: [".asset", ".asset-meta.xml"], + folder: 'contentassets', + nameSuffixList: ['.asset', '.asset-meta.xml'], + sfdxNameSuffixList: ['.asset', '.asset-meta.xml'], }, CustomApplication: { - folder: "applications", - nameSuffixList: [".app"], - sfdxNameSuffixList: [".app-meta.xml"], - permissionSetTypeName: "applicationVisibilities", - permissionSetMemberName: "application", + folder: 'applications', + nameSuffixList: ['.app'], + sfdxNameSuffixList: ['.app-meta.xml'], + permissionSetTypeName: 'applicationVisibilities', + permissionSetMemberName: 'application', }, CustomLabel: { - folder: "labels", - nameSuffixList: [".labels"], - sfdxNameSuffixList: [".labels-meta.xml"], + folder: 'labels', + nameSuffixList: ['.labels'], + sfdxNameSuffixList: ['.labels-meta.xml'], }, CustomMetadata: { - folder: "customMetadata", - nameSuffixList: [".md"], - sfdxNameSuffixList: [".md-meta.xml"], + folder: 'customMetadata', + nameSuffixList: ['.md'], + sfdxNameSuffixList: ['.md-meta.xml'], }, CustomMetadataType: { virtual: true, - permissionSetTypeName: "customMetadataTypeAccesses", - permissionSetMemberName: "name", + permissionSetTypeName: 'customMetadataTypeAccesses', + permissionSetMemberName: 'name', }, CustomSettings: { virtual: true, - permissionSetTypeName: "customSettingAccesses", - permissionSetMemberName: "name", + permissionSetTypeName: 'customSettingAccesses', + permissionSetMemberName: 'name', }, CustomSite: { - folder: "sites", - nameSuffixList: [".site"], - sfdxNameSuffixList: [".site-meta.xml"], + folder: 'sites', + nameSuffixList: ['.site'], + sfdxNameSuffixList: ['.site-meta.xml'], }, CustomObjectTranslation: { - folder: "objectTranslations", - nameSuffixList: [".objectTranslation"], + folder: 'objectTranslations', + nameSuffixList: ['.objectTranslation'], }, // We use Translations to define the list of objectTranslations to filter & copy CustomPermission: { - folder: "customPermissions", - nameSuffixList: [".customPermission"], - sfdxNameSuffixList: [".customPermission-meta.xml"], + folder: 'customPermissions', + nameSuffixList: ['.customPermission'], + sfdxNameSuffixList: ['.customPermission-meta.xml'], }, CustomPlatformEvent: { virtual: true, - permissionSetTypeName: "objectPermissions", - permissionSetMemberName: "object", + permissionSetTypeName: 'objectPermissions', + permissionSetMemberName: 'object', }, CustomTab: { - folder: "tabs", - nameSuffixList: [".tab"], - sfdxNameSuffixList: [".tab-meta.xml"], - permissionSetTypeName: "tabSettings", - permissionSetMemberName: "tab", + folder: 'tabs', + nameSuffixList: ['.tab'], + sfdxNameSuffixList: ['.tab-meta.xml'], + permissionSetTypeName: 'tabSettings', + permissionSetMemberName: 'tab', }, Document: { - folder: "documents", - nameSuffixList: ["", "-meta.xml"], - sfdxNameSuffixList: [".documentFolder-meta.xml", ".document-meta.xml", ".png"], + folder: 'documents', + nameSuffixList: ['', '-meta.xml'], + sfdxNameSuffixList: ['.documentFolder-meta.xml', '.document-meta.xml', '.png'], metasInSubFolders: true, }, EmailTemplate: { - folder: "email", - nameSuffixList: ["", ".email", ".email-meta.xml"], - sfdxNameSuffixList: [".email", ".email-meta.xml"], + folder: 'email', + nameSuffixList: ['', '.email', '.email-meta.xml'], + sfdxNameSuffixList: ['.email', '.email-meta.xml'], metasInSubFolders: true, }, EscalationRules: { - folder: "escalationRules", - nameSuffixList: [".escalationRules"], - sfdxNameSuffixList: [".escalationRules-meta.xml"], + folder: 'escalationRules', + nameSuffixList: ['.escalationRules'], + sfdxNameSuffixList: ['.escalationRules-meta.xml'], }, FlexiPage: { - folder: "flexipages", - nameSuffixList: [".flexipage"], - sfdxNameSuffixList: [".flexipage-meta.xml"], + folder: 'flexipages', + nameSuffixList: ['.flexipage'], + sfdxNameSuffixList: ['.flexipage-meta.xml'], }, Flow: { - folder: "flows", - nameSuffixList: [".flow"], - sfdxNameSuffixList: [".flow-meta.xml"], + folder: 'flows', + nameSuffixList: ['.flow'], + sfdxNameSuffixList: ['.flow-meta.xml'], }, GlobalValueSet: { - folder: "globalValueSets", - nameSuffixList: [".globalValueSet"], - sfdxNameSuffixList: [".globalValueSet-meta.xml"], + folder: 'globalValueSets', + nameSuffixList: ['.globalValueSet'], + sfdxNameSuffixList: ['.globalValueSet-meta.xml'], }, GlobalValueSetTranslation: { - folder: "globalValueSetTranslations", - nameSuffixList: [".globalValueSetTranslation"], - sfdxNameSuffixList: [".globalValueSetTranslation-meta.xml"], + folder: 'globalValueSetTranslations', + nameSuffixList: ['.globalValueSetTranslation'], + sfdxNameSuffixList: ['.globalValueSetTranslation-meta.xml'], }, HomePageLayout: { - folder: "homePageLayouts", - nameSuffixList: [".homePageLayout"], - sfdxNameSuffixList: [".homePageLayout-meta.xml"], + folder: 'homePageLayouts', + nameSuffixList: ['.homePageLayout'], + sfdxNameSuffixList: ['.homePageLayout-meta.xml'], }, Layout: { - folder: "layouts", - nameSuffixList: [".layout"], - sfdxNameSuffixList: [".layout-meta.xml"], + folder: 'layouts', + nameSuffixList: ['.layout'], + sfdxNameSuffixList: ['.layout-meta.xml'], }, NamedCredential: { - folder: "namedCredentials", - nameSuffixList: [".namedCredential"], - sfdxNameSuffixList: [".namedCredential-meta.xml"], + folder: 'namedCredentials', + nameSuffixList: ['.namedCredential'], + sfdxNameSuffixList: ['.namedCredential-meta.xml'], }, Network: { - folder: "networks", - nameSuffixList: [".network"], - sfdxNameSuffixList: [".network-meta.xml"], + folder: 'networks', + nameSuffixList: ['.network'], + sfdxNameSuffixList: ['.network-meta.xml'], }, NetworkBranding: { - folder: "networkBranding", - nameSuffixList: ["", ".networkBranding", ".networkBranding-meta.xml"], - sfdxNameSuffixList: [".networkBranding-meta.xml", ".networkBranding"], + folder: 'networkBranding', + nameSuffixList: ['', '.networkBranding', '.networkBranding-meta.xml'], + sfdxNameSuffixList: ['.networkBranding-meta.xml', '.networkBranding'], }, NotificationTypeConfig: { - folder: "notificationtypes", - nameSuffixList: [".notiftype"], - sfdxNameSuffixList: [".notiftype-meta.xml"], + folder: 'notificationtypes', + nameSuffixList: ['.notiftype'], + sfdxNameSuffixList: ['.notiftype-meta.xml'], }, PermissionSet: { - folder: "permissionsets", - nameSuffixList: [".permissionset"], - sfdxNameSuffixList: [".permissionset-meta.xml"], + folder: 'permissionsets', + nameSuffixList: ['.permissionset'], + sfdxNameSuffixList: ['.permissionset-meta.xml'], }, PlatformCachePartition: { - folder: "cachePartitions", - nameSuffixList: [".cachePartition"], - sfdxNameSuffixList: [".cachePartition-meta.xml"], + folder: 'cachePartitions', + nameSuffixList: ['.cachePartition'], + sfdxNameSuffixList: ['.cachePartition-meta.xml'], }, Profile: { - folder: "profiles", - nameSuffixList: [".profile"], - sfdxNameSuffixList: [".profile-meta.xml"], + folder: 'profiles', + nameSuffixList: ['.profile'], + sfdxNameSuffixList: ['.profile-meta.xml'], }, Queue: { - folder: "queues", - nameSuffixList: [".queue"], - sfdxNameSuffixList: [".queue-meta.xml"], + folder: 'queues', + nameSuffixList: ['.queue'], + sfdxNameSuffixList: ['.queue-meta.xml'], }, QuickAction: { - folder: "quickActions", - nameSuffixList: [".quickAction"], - sfdxNameSuffixList: [".quickAction-meta.xml"], + folder: 'quickActions', + nameSuffixList: ['.quickAction'], + sfdxNameSuffixList: ['.quickAction-meta.xml'], }, RemoteSiteSetting: { - folder: "remoteSiteSettings", - nameSuffixList: [".remoteSite"], - sfdxNameSuffixList: [".remoteSite-meta.xml"], + folder: 'remoteSiteSettings', + nameSuffixList: ['.remoteSite'], + sfdxNameSuffixList: ['.remoteSite-meta.xml'], }, Report: { - folder: "reports", - nameSuffixList: ["", "-meta.xml"], - sfdxNameSuffixList: [".reportFolder-meta.xml"], + folder: 'reports', + nameSuffixList: ['', '-meta.xml'], + sfdxNameSuffixList: ['.reportFolder-meta.xml'], }, Role: { - folder: "roles", - nameSuffixList: [".role"], - sfdxNameSuffixList: [".role-meta.xml"], + folder: 'roles', + nameSuffixList: ['.role'], + sfdxNameSuffixList: ['.role-meta.xml'], }, Settings: { - folder: "settings", - nameSuffixList: [".settings"], - sfdxNameSuffixList: [".settings-meta.xml"], + folder: 'settings', + nameSuffixList: ['.settings'], + sfdxNameSuffixList: ['.settings-meta.xml'], }, SiteDotCom: { - folder: "siteDotComSites", - nameSuffixList: [".site", ".site-meta.xml"], - sfdxNameSuffixList: [".site", ".site-meta.xml"], + folder: 'siteDotComSites', + nameSuffixList: ['.site', '.site-meta.xml'], + sfdxNameSuffixList: ['.site', '.site-meta.xml'], }, StandardValueSet: { - folder: "standardValueSets", - nameSuffixList: [".standardValueSet"], - sfdxNameSuffixList: [".standardValueSet-meta.xml"], + folder: 'standardValueSets', + nameSuffixList: ['.standardValueSet'], + sfdxNameSuffixList: ['.standardValueSet-meta.xml'], }, StandardValueSetTranslation: { - folder: "standardValueSetTranslations", - nameSuffixList: [".standardValueSetTranslation"], - sfdxNameSuffixList: [".standardValueSetTranslation-meta.xml"], + folder: 'standardValueSetTranslations', + nameSuffixList: ['.standardValueSetTranslation'], + sfdxNameSuffixList: ['.standardValueSetTranslation-meta.xml'], }, StaticResource: { - folder: "staticresources", - nameSuffixList: [".resource", ".resource-meta.xml"], - sfdxNameSuffixList: [".resource-meta.xml", ".json", ".txt", ".bin", ".js", ".mp3", ".gif"], + folder: 'staticresources', + nameSuffixList: ['.resource', '.resource-meta.xml'], + sfdxNameSuffixList: ['.resource-meta.xml', '.json', '.txt', '.bin', '.js', '.mp3', '.gif'], }, // 'Translations': { folder: 'translations', nameSuffixList: ['.translation'] }, processed apart, as they need to be filtered Workflow: { - folder: "workflows", - nameSuffixList: [".workflow"], - sfdxNameSuffixList: [".workflow-meta.xml"], + folder: 'workflows', + nameSuffixList: ['.workflow'], + sfdxNameSuffixList: ['.workflow-meta.xml'], }, // Metadatas to use for building objects folder ( SObjects ) @@ -270,25 +279,25 @@ class MetadataUtils { CompactLayout: { sobjectRelated: true }, CustomField: { sobjectRelated: true, - permissionSetTypeName: "fieldPermissions", - permissionSetMemberName: "field", + permissionSetTypeName: 'fieldPermissions', + permissionSetMemberName: 'field', }, CustomObject: { sobjectRelated: true, - permissionSetTypeName: "objectPermissions", - permissionSetMemberName: "object", + permissionSetTypeName: 'objectPermissions', + permissionSetMemberName: 'object', }, FieldSet: { sobjectRelated: true }, ListView: { sobjectRelated: true }, RecordType: { sobjectRelated: true, - permissionSetTypeName: "recordTypeVisibilities", - permissionSetMemberName: "recordType", + permissionSetTypeName: 'recordTypeVisibilities', + permissionSetMemberName: 'recordType', }, UserPermission: { sobjectRelated: false, - permissionSetTypeName: "userPermissions", - permissionSetMemberName: "name", + permissionSetTypeName: 'userPermissions', + permissionSetMemberName: 'name', }, ValidationRule: { sobjectRelated: true }, WebLink: { sobjectRelated: true }, @@ -296,9 +305,9 @@ class MetadataUtils { // Special case: Translations, used for object copy and for filtering Translations: { translationRelated: true, - folder: "translations", - nameSuffixList: [".translation"], - sfdxNameSuffixList: [".translation-meta.xml"], + folder: 'translations', + nameSuffixList: ['.translation'], + sfdxNameSuffixList: ['.translation-meta.xml'], }, }; @@ -309,67 +318,67 @@ class MetadataUtils { public static describeObjectProperties() { const objectFilteringProperties = [ { - objectXmlPropName: "businessProcesses", - packageXmlPropName: "BusinessProcess", - nameProperty: "fullName", - translationNameProperty: "name", - sfdxNameSuffixList: [".businessProcess-meta.xml"], + objectXmlPropName: 'businessProcesses', + packageXmlPropName: 'BusinessProcess', + nameProperty: 'fullName', + translationNameProperty: 'name', + sfdxNameSuffixList: ['.businessProcess-meta.xml'], }, { - objectXmlPropName: "compactLayouts", - packageXmlPropName: "CompactLayout", - nameProperty: "fullName", - translationNameProperty: "layout", - sfdxNameSuffixList: [".compactLayout-meta.xml"], + objectXmlPropName: 'compactLayouts', + packageXmlPropName: 'CompactLayout', + nameProperty: 'fullName', + translationNameProperty: 'layout', + sfdxNameSuffixList: ['.compactLayout-meta.xml'], }, { - objectXmlPropName: "fields", - packageXmlPropName: "CustomField", - nameProperty: "fullName", - translationNameProperty: "name", - sfdxNameSuffixList: [".field-meta.xml"], + objectXmlPropName: 'fields', + packageXmlPropName: 'CustomField', + nameProperty: 'fullName', + translationNameProperty: 'name', + sfdxNameSuffixList: ['.field-meta.xml'], }, { - objectXmlPropName: "listViews", - packageXmlPropName: "ListView", - nameProperty: "fullName", - translationNameProperty: "name", - sfdxNameSuffixList: [".listView-meta.xml"], + objectXmlPropName: 'listViews', + packageXmlPropName: 'ListView', + nameProperty: 'fullName', + translationNameProperty: 'name', + sfdxNameSuffixList: ['.listView-meta.xml'], }, { - objectXmlPropName: "layouts", - packageXmlPropName: "Layout", - nameProperty: "fullName", - translationNameProperty: "layout", - sfdxNameSuffixList: [".layout-meta.xml"], + objectXmlPropName: 'layouts', + packageXmlPropName: 'Layout', + nameProperty: 'fullName', + translationNameProperty: 'layout', + sfdxNameSuffixList: ['.layout-meta.xml'], }, { - objectXmlPropName: "recordTypes", - packageXmlPropName: "RecordType", - nameProperty: "fullName", - translationNameProperty: "name", - sfdxNameSuffixList: [".recordType-meta.xml"], + objectXmlPropName: 'recordTypes', + packageXmlPropName: 'RecordType', + nameProperty: 'fullName', + translationNameProperty: 'name', + sfdxNameSuffixList: ['.recordType-meta.xml'], }, { - objectXmlPropName: "webLinks", - packageXmlPropName: "WebLink", - nameProperty: "fullName", - translationNameProperty: "name", - sfdxNameSuffixList: [".webLink-meta.xml"], + objectXmlPropName: 'webLinks', + packageXmlPropName: 'WebLink', + nameProperty: 'fullName', + translationNameProperty: 'name', + sfdxNameSuffixList: ['.webLink-meta.xml'], }, { - objectXmlPropName: "validationRules", - packageXmlPropName: "ValidationRule", - nameProperty: "fullName", - translationNameProperty: "name", - sfdxNameSuffixList: [".validationRule-meta.xml"], + objectXmlPropName: 'validationRules', + packageXmlPropName: 'ValidationRule', + nameProperty: 'fullName', + translationNameProperty: 'name', + sfdxNameSuffixList: ['.validationRule-meta.xml'], }, { - objectXmlPropName: "fieldSets", - packageXmlPropName: "FieldSet", - nameProperty: "fullName", - translationNameProperty: "name", - sfdxNameSuffixList: [".fieldSet-meta.xml"], + objectXmlPropName: 'fieldSets', + packageXmlPropName: 'FieldSet', + nameProperty: 'fullName', + translationNameProperty: 'name', + sfdxNameSuffixList: ['.fieldSet-meta.xml'], }, ]; return objectFilteringProperties; @@ -377,25 +386,25 @@ class MetadataUtils { public static listMetadatasNotManagedBySfdx() { return [ - "ApexEmailNotifications", - "AppMenu", - "AppointmentSchedulingPolicy", - "Audience", - "BlacklistedConsumer", - "ConnectedApp", - "CustomIndex", - "ForecastingType", - "IframeWhiteListUrlSettings", - "ManagedContentType", - "NotificationTypeConfig", - "Settings", - "TopicsForObjects", + 'ApexEmailNotifications', + 'AppMenu', + 'AppointmentSchedulingPolicy', + 'Audience', + 'BlacklistedConsumer', + 'ConnectedApp', + 'CustomIndex', + 'ForecastingType', + 'IframeWhiteListUrlSettings', + 'ManagedContentType', + 'NotificationTypeConfig', + 'Settings', + 'TopicsForObjects', ]; } // Get default org that is currently selected for user public static async getCurrentOrg() { - const displayOrgCommand = "sfdx force:org:display"; + const displayOrgCommand = 'sf org display'; const displayResult = await execSfdxJson(displayOrgCommand, this, { fail: false, output: false, @@ -407,29 +416,31 @@ class MetadataUtils { } // List local orgs for user - public static async listLocalOrgs(type = "any", options: any = {}) { - let orgListResult = await getCache("force:org:list", null); + public static async listLocalOrgs(type = 'any', options: any = {}) { + const quickListParams = options?.quickOrgList === true ? ' --skip-connection-status' : ''; + const orgListCommand = `sf org list${quickListParams}`; + let orgListResult = await getCache(orgListCommand, null); if (orgListResult == null) { - orgListResult = await execSfdxJson("sfdx force:org:list", this); - await setCache("force:org:list", orgListResult); + orgListResult = await execSfdxJson(orgListCommand, this); + await setCache(orgListCommand, orgListResult); } // All orgs - if (type === "any") { + if (type === 'any') { return orgListResult?.result || []; } // Sandbox - else if (type === "sandbox") { + else if (type === 'sandbox') { return ( orgListResult?.result?.nonScratchOrgs?.filter((org: any) => { - return org.loginUrl.includes("--") || org.loginUrl.includes("test.salesforce.com"); + return org.loginUrl.includes('--') || org.loginUrl.includes('test.salesforce.com'); }) || [] ); } // Sandbox - else if (type === "devSandbox") { + else if (type === 'devSandbox') { const allSandboxes = orgListResult?.result?.nonScratchOrgs?.filter((org: any) => { - return org.loginUrl.includes("--") || org.loginUrl.includes("test.salesforce.com"); + return org.loginUrl.includes('--') || org.loginUrl.includes('test.salesforce.com'); }) || []; const majorOrgs = await listMajorOrgs(); const devSandboxes = allSandboxes.filter((org: any) => { @@ -437,17 +448,20 @@ class MetadataUtils { majorOrgs.filter( (majorOrg) => majorOrg.targetUsername === org.username || - (majorOrg.instanceUrl === org.instanceUrl && !majorOrg.instanceUrl.includes("test.salesforce.com")), + (majorOrg.instanceUrl === org.instanceUrl && !majorOrg.instanceUrl.includes('test.salesforce.com')) ).length === 0 ); }); return devSandboxes; } // scratch - else if (type === "scratch") { + else if (type === 'scratch') { return ( orgListResult?.result?.scratchOrgs?.filter((org: any) => { - return org.status === "Active" && (options.devHubUsername && org.devHubUsername !== options.devHubUsername ? false : true); + return ( + org.status === 'Active' && + (options.devHubUsername && org.devHubUsername !== options.devHubUsername ? false : true) + ); }) || [] ); } @@ -455,10 +469,10 @@ class MetadataUtils { } // List installed packages on a org - public static async listInstalledPackages(orgAlias: string = null, commandThis: any): Promise { - let listCommand = "sfdx force:package:installed:list"; + public static async listInstalledPackages(orgAlias: string | null = null, commandThis: any): Promise { + let listCommand = 'sf package installed list'; if (orgAlias != null) { - listCommand += ` -u ${orgAlias}`; + listCommand += ` --target-org ${orgAlias}`; } try { const alreadyInstalled = await execSfdxJson(listCommand, commandThis, { @@ -467,40 +481,60 @@ class MetadataUtils { }); return alreadyInstalled?.result || []; } catch (e) { - uxLog(this, c.yellow(`Unable to list installed packages: This is probably a @salesforce/cli bug !\n${e.message}\n${e.stack}`)); + uxLog( + this, + c.yellow( + `Unable to list installed packages: This is probably a @salesforce/cli bug !\n${(e as Error).message}\n${(e as Error).stack + }` + ) + ); globalThis.workaroundCliPackages = true; return []; } } // Install package on existing org - public static async installPackagesOnOrg(packages: any[], orgAlias: string = null, commandThis: any = null, context = "none") { + public static async installPackagesOnOrg( + packages: any[], + orgAlias: string | null = null, + commandThis: any = null, + context = 'none' + ) { const alreadyInstalled = await MetadataUtils.listInstalledPackages(orgAlias, this); if (globalThis?.workaroundCliPackages === true) { uxLog( commandThis, c.yellow(`Skip packages installation because of a @salesforce/cli bug. Until it is solved, please install packages manually in target org if necessary. -Issue tracking: https://github.com/forcedotcom/cli/issues/2426`), +Issue tracking: https://github.com/forcedotcom/cli/issues/2426`) ); return; } for (const package1 of packages) { if ( - alreadyInstalled.filter((installedPackage: any) => package1.SubscriberPackageVersionId === installedPackage.SubscriberPackageVersionId) - .length === 0 + alreadyInstalled.filter( + (installedPackage: any) => package1.SubscriberPackageVersionId === installedPackage.SubscriberPackageVersionId + ).length === 0 ) { - if (context === "scratch" && package1.installOnScratchOrgs === false) { + if (context === 'scratch' && package1.installOnScratchOrgs === false) { uxLog( commandThis, - c.cyan(`Skip installation of ${c.green(package1.SubscriberPackageName)} as it is configured to not be installed on scratch orgs`), + c.cyan( + `Skip installation of ${c.green( + package1.SubscriberPackageName + )} as it is configured to not be installed on scratch orgs` + ) ); continue; } - if (context === "deploy" && package1.installDuringDeployments === false) { + if (context === 'deploy' && package1.installDuringDeployments === false) { uxLog( commandThis, - c.cyan(`Skip installation of ${c.green(package1.SubscriberPackageName)} as it is configured to not be installed on scratch orgs`), + c.cyan( + `Skip installation of ${c.green( + package1.SubscriberPackageName + )} as it is configured to not be installed on scratch orgs` + ) ); continue; } @@ -508,35 +542,54 @@ Issue tracking: https://github.com/forcedotcom/cli/issues/2426`), commandThis, c.cyan( `Installing package ${c.green( - `${c.bold(package1.SubscriberPackageName || "")} - ${c.bold(package1.SubscriberPackageVersionName || "")}`, - )}...`, - ), + `${c.bold(package1.SubscriberPackageName || '')} - ${c.bold(package1.SubscriberPackageVersionName || '')}` + )}...` + ) ); if (package1.SubscriberPackageVersionId == null) { - throw new SfdxError( - c.red(`[sfdx-hardis] You must define ${c.bold("SubscriberPackageVersionId")} in .sfdx-hardis.yml (in installedPackages property)`), + throw new SfError( + c.red( + `[sfdx-hardis] You must define ${c.bold( + 'SubscriberPackageVersionId' + )} in .sfdx-hardis.yml (in installedPackages property)` + ) ); } - const securityType = package1.SecurityType || "AdminsOnly"; + const securityType = package1.SecurityType || 'AdminsOnly'; let packageInstallCommand = - "sfdx force:package:install" + + 'sf package install' + ` --package ${package1.SubscriberPackageVersionId}` + - " --noprompt" + - ` --securitytype ${securityType}` + - " -w 60" + - " --json " + - (package1.installationkey != null && package1.installationkey != "" ? ` --installationkey ${package1.installationkey}` : ""); + ' --no-prompt' + + ` --security-type ${securityType}` + + ' --wait 60' + + ' --json ' + + (package1.installationkey != null && package1.installationkey != '' + ? ` --installationkey ${package1.installationkey}` + : ''); if (orgAlias != null) { packageInstallCommand += ` -u ${orgAlias}`; } elapseStart(`Install package ${package1.SubscriberPackageName}`); try { - await execCommand(packageInstallCommand, this, { + await execCommand(packageInstallCommand, null, { fail: true, output: true, }); - } catch (ex) { - const ignoredErrors = ["Une version plus récente de ce package est installée.", "A newer version of this package is currently installed."]; + } catch (ex: any) { + if (ex.message.includes('Installation key not valid')) { + uxLog( + this, + c.yellow( + `${c.bold('Package requiring password')}: Please manually install package ${package1.SubscriberPackageName + } in target org using its password, and define 'installDuringDeployments: false' in its .sfdx-hardis.yml reference` + ) + ); + throw ex; + } + const ignoredErrors = [ + 'Une version plus récente de ce package est installée.', + 'A newer version of this package is currently installed.', + ]; // If ex.message contains at least one of the ignoredError, don't rethrow exception if (!ignoredErrors.some((msg) => ex.message && ex.message.includes(msg))) { throw ex; @@ -544,16 +597,23 @@ Issue tracking: https://github.com/forcedotcom/cli/issues/2426`), uxLog( this, c.yellow( - `${c.bold("This is not a real error")}: A newer version of ${ - package1.SubscriberPackageName - } has been found. You may update installedPackages property in .sfdx-hardis.yml`, - ), + `${c.bold('This is not a real error')}: A newer version of ${package1.SubscriberPackageName + } has been found. You may update installedPackages property in .sfdx-hardis.yml` + ) + ); + uxLog( + this, + c.yellow( + `You can do that using command ${c.bold('sf hardis:org:retrieve:packageconfig')} in a minor git branch` + ) ); - uxLog(this, c.yellow(`You can do that using command ${c.bold("sfdx hardis:org:retrieve:packageconfig")} in a minor git branch`)); } elapseEnd(`Install package ${package1.SubscriberPackageName}`); } else { - uxLog(commandThis, c.cyan(`Skip installation of ${c.green(package1.SubscriberPackageName)} as it is already installed`)); + uxLog( + commandThis, + c.cyan(`Skip installation of ${c.green(package1.SubscriberPackageName)} as it is already installed`) + ); } } } @@ -566,37 +626,40 @@ Issue tracking: https://github.com/forcedotcom/cli/issues/2426`), filteredMetadatas: string[], options: any = {}, commandThis: any, - debug: boolean, + debug: boolean ) { // Create output folder if not existing await fs.ensureDir(metadataFolder); // Build package.xml for all org - await buildOrgManifest(commandThis.org.getUsername(), "package-full.xml"); - await fs.copyFile("package-full.xml", "package.xml"); + await buildOrgManifest(commandThis.org.getUsername(), 'package-full.xml'); + await fs.copyFile('package-full.xml', 'package.xml'); // Filter managed items if requested if (options.filterManagedItems) { - uxLog(commandThis, c.cyan("Filtering managed items from package.Xml manifest...")); + uxLog(commandThis, c.cyan('Filtering managed items from package.Xml manifest...')); // List installed packages & collect managed namespaces - let namespaces = []; + let namespaces: any[] = []; if (isSfdxProject()) { // Use sfdx command if possible const installedPackages = await this.listInstalledPackages(null, commandThis); for (const installedPackage of installedPackages) { - if (installedPackage?.SubscriberPackageNamespace !== "" && installedPackage?.SubscriberPackageNamespace != null) { + if ( + installedPackage?.SubscriberPackageNamespace !== '' && + installedPackage?.SubscriberPackageNamespace != null + ) { namespaces.push(installedPackage.SubscriberPackageNamespace); } } } else { // Get namespace list from package.xml - const packageXmlContent = await parsePackageXmlFile("package-full.xml"); - namespaces = packageXmlContent["InstalledPackage"] || []; + const packageXmlContent = await parsePackageXmlFile('package-full.xml'); + namespaces = packageXmlContent['InstalledPackage'] || []; } // Filter package XML to remove identified metadatas - const packageXmlToRemove = fs.existsSync("./remove-items-package.xml") - ? path.resolve("./remove-items-package.xml") - : path.resolve(PACKAGE_ROOT_DIR + "/defaults/remove-items-package.xml"); + const packageXmlToRemove = fs.existsSync('./remove-items-package.xml') + ? path.resolve('./remove-items-package.xml') + : path.resolve(PACKAGE_ROOT_DIR + '/defaults/remove-items-package.xml'); const removeStandard = options.removeStandard === false ? false : true; const filterNamespaceRes = await filterPackageXml(packageXml, packageXml, { removeNamespaces: namespaces, @@ -607,9 +670,9 @@ Issue tracking: https://github.com/forcedotcom/cli/issues/2426`), uxLog(commandThis, filterNamespaceRes.message); } // Filter package.xml only using locally defined remove-items-package.xml - else if (fs.existsSync("./remove-items-package.xml")) { + else if (fs.existsSync('./remove-items-package.xml')) { const filterNamespaceRes = await filterPackageXml(packageXml, packageXml, { - removeFromPackageXmlFile: path.resolve("./remove-items-package.xml"), + removeFromPackageXmlFile: path.resolve('./remove-items-package.xml'), updateApiVersion: CONSTANTS.API_VERSION, }); uxLog(commandThis, filterNamespaceRes.message); @@ -633,11 +696,11 @@ Issue tracking: https://github.com/forcedotcom/cli/issues/2426`), if (fs.readdirSync(metadataFolder).length === 0 || checkEmpty === false) { uxLog(commandThis, c.cyan(`Retrieving metadatas in ${c.green(metadataFolder)}...`)); const retrieveCommand = - "sfdx force:mdapi:retrieve" + - ` --retrievetargetdir ${metadataFolder}` + - ` --unpackaged ${packageXml}` + - ` --wait ${process.env.SFDX_RETRIEVE_WAIT_MINUTES || "60"}` + - (debug ? " --verbose" : ""); + 'sf project retrieve start' + + ` --target-metadata-dir ${metadataFolder}` + + ` --manifest ${packageXml}` + + ` --wait ${process.env.SFDX_RETRIEVE_WAIT_MINUTES || '60'}` + + (debug ? ' --verbose' : ''); const retrieveRes = await execSfdxJson(retrieveCommand, this, { output: false, fail: true, @@ -647,23 +710,23 @@ Issue tracking: https://github.com/forcedotcom/cli/issues/2426`), uxLog(commandThis, retrieveRes); } // Unzip metadatas - uxLog(commandThis, c.cyan("Unzipping metadatas...")); - await extractZip(path.join(metadataFolder, "unpackaged.zip"), { + uxLog(commandThis, c.cyan('Unzipping metadatas...')); + await extractZip(path.join(metadataFolder, 'unpackaged.zip'), { dir: metadataFolder, }); - await fs.unlink(path.join(metadataFolder, "unpackaged.zip")); + await fs.unlink(path.join(metadataFolder, 'unpackaged.zip')); } } // Prompt user to select a list of metadata types public static async promptMetadataTypes() { - const metadataTypes = sortArray(listMetadataTypes(), { by: ["xmlName"], order: ["asc"] }); + const metadataTypes = sortArray(listMetadataTypes(), { by: ['xmlName'], order: ['asc'] }); const metadataResp = await prompts({ - type: "multiselect", - message: c.cyanBright("Please select metadata types"), + type: 'multiselect', + message: c.cyanBright('Please select metadata types'), choices: metadataTypes.map((metadataType: any) => { return { - title: c.cyan(`${metadataType.xmlName || "no xml name"} (${metadataType.directoryName || "no dir name"})`), + title: c.cyan(`${metadataType.xmlName || 'no xml name'} (${metadataType.directoryName || 'no dir name'})`), value: metadataType, }; }), @@ -676,10 +739,37 @@ Issue tracking: https://github.com/forcedotcom/cli/issues/2426`), if (!isGitRepo()) { return []; } - const files = (await git().status(["--porcelain"])).files; + const files = (await git().status(['--porcelain'])).files; const filesSorted = files.sort((a, b) => (a.path > b.path ? 1 : -1)); return filesSorted; } + + public static getMetadataPrettyNames(metadataFilePaths: string[], bold = false): Map { + const metadataList = listMetadataTypes(); + const metadataFilePathsHuman = new Map(); + for (const fileRaw of metadataFilePaths) { + const file = fileRaw.replace(/\\/g, '/').replace('force-app/main/default/', ''); + let fileHuman = "" + file; + for (const metadataDesc of metadataList) { + if (file.includes(metadataDesc.directoryName || "THEREISNOT")) { + const endOfPath = file.split(metadataDesc.directoryName + "/")[1]; + const suffix = metadataDesc.suffix ?? "THEREISNOT"; + let metadataName = endOfPath.includes("." + suffix + "-meta.xml") ? + endOfPath.replace("." + suffix + "-meta.xml", "") : + endOfPath.includes("." + suffix) ? + endOfPath.replace("." + suffix, "") : + endOfPath; + if (bold) { + metadataName = "*" + metadataName + "*"; + } + fileHuman = metadataDesc.xmlName + " " + metadataName; + continue; + } + } + metadataFilePathsHuman.set(fileRaw, fileHuman); + } + return metadataFilePathsHuman; + } } export { MetadataUtils }; diff --git a/src/common/metadata-utils/metadataList.ts b/src/common/metadata-utils/metadataList.ts index 48ef63d42..70505fc0c 100644 --- a/src/common/metadata-utils/metadataList.ts +++ b/src/common/metadata-utils/metadataList.ts @@ -1,1322 +1,2213 @@ export function listMetadataTypes() { - // v54 + // v62 return [ { - directoryName: "installedPackages", - inFolder: false, - metaFile: false, - suffix: "installedPackage", - xmlName: "InstalledPackage", + "directoryName": "conversationMessageDefinitions", + "inFolder": false, + "metaFile": false, + "suffix": "conversationMessageDefinition", + "xmlName": "ConversationMessageDefinition" }, { - childXmlNames: "CustomLabel", - directoryName: "labels", - inFolder: false, - metaFile: false, - suffix: "labels", - xmlName: "CustomLabels", + "directoryName": "genAiPromptTemplateActivations", + "inFolder": false, + "metaFile": false, + "suffix": "genAiPromptTemplateActivation", + "xmlName": "GenAiPromptTemplateActv" }, { - directoryName: "staticresources", - inFolder: false, - metaFile: true, - suffix: "resource", - xmlName: "StaticResource", + "directoryName": "genAiPromptTemplates", + "inFolder": false, + "metaFile": false, + "suffix": "genAiPromptTemplate", + "xmlName": "GenAiPromptTemplate" }, { - directoryName: "scontrols", - inFolder: false, - metaFile: true, - suffix: "scf", - xmlName: "Scontrol", + "directoryName": "dw", + "inFolder": false, + "metaFile": false, + "suffix": "dwl", + "xmlName": "DataWeaveResource" }, { - directoryName: "certs", - inFolder: false, - metaFile: true, - suffix: "crt", - xmlName: "Certificate", + "directoryName": "useraccesspolicies", + "inFolder": false, + "metaFile": false, + "suffix": "useraccesspolicy", + "xmlName": "UserAccessPolicy" }, { - directoryName: "messageChannels", - inFolder: false, - metaFile: false, - suffix: "messageChannel", - xmlName: "LightningMessageChannel", + "directoryName": "webstoretemplate", + "inFolder": false, + "metaFile": false, + "suffix": "webstoretemplate", + "xmlName": "WebStoreTemplate" }, { - directoryName: "aura", - inFolder: false, - metaFile: false, - xmlName: "AuraDefinitionBundle", + "directoryName": "messagingChannels", + "inFolder": false, + "metaFile": false, + "suffix": "messagingChannel", + "xmlName": "MessagingChannel" }, { - directoryName: "lwc", - inFolder: false, - metaFile: false, - xmlName: "LightningComponentBundle", + "directoryName": "recordAlertCategories", + "inFolder": false, + "metaFile": false, + "suffix": "recordAlertCategory", + "xmlName": "RecordAlertCategory" }, { - directoryName: "components", - inFolder: false, - metaFile: true, - suffix: "component", - xmlName: "ApexComponent", + "directoryName": "aiApplicationConfigs", + "inFolder": false, + "metaFile": false, + "suffix": "aiapplicationconfig", + "xmlName": "AIApplicationConfig" }, { - directoryName: "pages", - inFolder: false, - metaFile: true, - suffix: "page", - xmlName: "ApexPage", + "directoryName": "aiApplications", + "inFolder": false, + "metaFile": false, + "suffix": "ai", + "xmlName": "AIApplication" }, { - directoryName: "queues", - inFolder: false, - metaFile: false, - suffix: "queue", - xmlName: "Queue", + "directoryName": "mlDataDefinitions", + "inFolder": false, + "metaFile": false, + "suffix": "mlDataDefinition", + "xmlName": "MLDataDefinition" }, { - directoryName: "CaseSubjectParticles", - inFolder: false, - metaFile: false, - suffix: "CaseSubjectParticle", - xmlName: "CaseSubjectParticle", + "directoryName": "mlPredictions", + "inFolder": false, + "metaFile": false, + "suffix": "mlPrediction", + "xmlName": "MLPredictionDefinition" }, { - directoryName: "dataSources", - inFolder: false, - metaFile: false, - suffix: "dataSource", - xmlName: "ExternalDataSource", + "directoryName": "eventRelays", + "inFolder": false, + "metaFile": false, + "suffix": "eventRelay", + "xmlName": "EventRelayConfig" }, { - directoryName: "namedCredentials", - inFolder: false, - metaFile: false, - suffix: "namedCredential", - xmlName: "NamedCredential", + "directoryName": "omniSupervisorConfigs", + "inFolder": false, + "metaFile": false, + "suffix": "omniSupervisorConfig", + "xmlName": "OmniSupervisorConfig" }, { - directoryName: "externalServiceRegistrations", - inFolder: false, - metaFile: false, - suffix: "externalServiceRegistration", - xmlName: "ExternalServiceRegistration", + "directoryName": "uiObjectRelationConfigs", + "inFolder": false, + "metaFile": false, + "suffix": "uiObjectRelationConfig", + "xmlName": "UIObjectRelationConfig" }, { - directoryName: "roles", - inFolder: false, - metaFile: false, - suffix: "role", - xmlName: "Role", + "directoryName": "timelineObjectDefinitions", + "inFolder": false, + "metaFile": false, + "suffix": "timelineObjectDefinition", + "xmlName": "TimelineObjectDefinition" }, { - directoryName: "groups", - inFolder: false, - metaFile: false, - suffix: "group", - xmlName: "Group", + "directoryName": "slackapps", + "inFolder": false, + "metaFile": true, + "suffix": "slackapp", + "xmlName": "SlackApp" }, { - directoryName: "globalValueSets", - inFolder: false, - metaFile: false, - suffix: "globalValueSet", - xmlName: "GlobalValueSet", + "directoryName": "viewdefinitions", + "inFolder": false, + "metaFile": true, + "suffix": "view", + "xmlName": "ViewDefinition" }, { - directoryName: "standardValueSets", - inFolder: false, - metaFile: false, - suffix: "standardValueSet", - xmlName: "StandardValueSet", + "directoryName": "ActionLauncherItemDef", + "inFolder": false, + "metaFile": false, + "suffix": "actionLauncherItemDef", + "xmlName": "ActionLauncherItemDef" }, { - directoryName: "customPermissions", - inFolder: false, - metaFile: false, - suffix: "customPermission", - xmlName: "CustomPermission", + "directoryName": "loyaltyProgramSetups", + "inFolder": false, + "metaFile": false, + "suffix": "loyaltyProgramSetup", + "xmlName": "LoyaltyProgramSetup" }, { - childXmlNames: [ - "CustomField", - "Index", - "BusinessProcess", - "RecordType", - "CompactLayout", - "WebLink", - "ValidationRule", - "SharingReason", - "ListView", - "FieldSet", - ], - directoryName: "objects", - inFolder: false, - metaFile: false, - suffix: "object", - xmlName: "CustomObject", + "directoryName": "documentCategory", + "inFolder": false, + "metaFile": false, + "suffix": "documentCategory", + "xmlName": "DocumentCategory" }, { - directoryName: "businessProcesses", - inFolder: false, - metaFile: false, - suffix: "businessProcess", - xmlName: "BusinessProcess", + "directoryName": "documentCategoryDocumentTypes", + "inFolder": false, + "metaFile": false, + "suffix": "documentCategoryDocumentType", + "xmlName": "DocumentCategoryDocumentType" }, { - directoryName: "compactLayouts", - inFolder: false, - metaFile: false, - suffix: "compactLayout", - xmlName: "CompactLayout", + "directoryName": "AssessmentQuestions", + "inFolder": false, + "metaFile": false, + "suffix": "aq", + "xmlName": "AssessmentQuestion" }, { - directoryName: "fields", - inFolder: false, - metaFile: false, - suffix: "field", - xmlName: "CustomField", + "directoryName": "AssessmentQuestionSets", + "inFolder": false, + "metaFile": false, + "suffix": "aqs", + "xmlName": "AssessmentQuestionSet" }, { - directoryName: "fieldSets", - inFolder: false, - metaFile: false, - suffix: "fieldSet", - xmlName: "FieldSet", + "directoryName": "decisionTables", + "inFolder": false, + "metaFile": false, + "suffix": "decisionTable", + "xmlName": "DecisionTable" }, { - directoryName: "listViews", - inFolder: false, - metaFile: false, - suffix: "listView", - xmlName: "ListView", + "directoryName": "decisionTableDatasetLinks", + "inFolder": false, + "metaFile": false, + "suffix": "decisionTableDatasetLink", + "xmlName": "DecisionTableDatasetLink" }, { - directoryName: "recordTypes", - inFolder: false, - metaFile: false, - suffix: "recordType", - xmlName: "RecordType", + "directoryName": "forecastingFilters", + "inFolder": false, + "metaFile": false, + "suffix": "forecastingFilter", + "xmlName": "ForecastingFilter" }, { - directoryName: "sharingReasons", - inFolder: false, - metaFile: false, - suffix: "sharingReason", - xmlName: "SharingReason", + "directoryName": "forecastingFilterConditions", + "inFolder": false, + "metaFile": false, + "suffix": "forecastingFilterCondition", + "xmlName": "ForecastingFilterCondition" }, { - directoryName: "validationRules", - inFolder: false, - metaFile: false, - suffix: "validationRule", - xmlName: "ValidationRule", + "directoryName": "forecastingSourceDefinitions", + "inFolder": false, + "metaFile": false, + "suffix": "forecastingSourceDefinition", + "xmlName": "ForecastingSourceDefinition" }, { - directoryName: "webLinks", - inFolder: false, - metaFile: false, - suffix: "webLink", - xmlName: "WebLink", + "directoryName": "forecastingTypes", + "inFolder": false, + "metaFile": false, + "suffix": "forecastingType", + "xmlName": "ForecastingType" }, { - directoryName: "reportTypes", - inFolder: false, - metaFile: false, - suffix: "reportType", - xmlName: "ReportType", + "directoryName": "forecastingTypeSources", + "inFolder": false, + "metaFile": false, + "suffix": "forecastingTypeSource", + "xmlName": "ForecastingTypeSource" }, { - directoryName: "reports", - inFolder: true, - metaFile: false, - suffix: "report", - xmlName: "Report", + "directoryName": "decisionMatrixDefinition", + "inFolder": false, + "metaFile": false, + "suffix": "decisionMatrixDefinition", + "xmlName": "DecisionMatrixDefinition" }, { - directoryName: "dashboards", - inFolder: true, - metaFile: false, - suffix: "dashboard", - xmlName: "Dashboard", + "directoryName": "expressionSetDefinition", + "inFolder": false, + "metaFile": false, + "suffix": "expressionSetDefinition", + "xmlName": "ExpressionSetDefinition" }, { - directoryName: "analyticSnapshots", - inFolder: false, - metaFile: false, - suffix: "snapshot", - xmlName: "AnalyticSnapshot", + "directoryName": "explainabilityActionDefinition", + "inFolder": false, + "metaFile": false, + "suffix": "explainabilityActionDefinition", + "xmlName": "ExplainabilityActionDefinition" }, { - directoryName: "feedFilters", - inFolder: false, - metaFile: false, - suffix: "feedFilter", - xmlName: "CustomFeedFilter", + "directoryName": "explainabilityActionVersion", + "inFolder": false, + "metaFile": false, + "suffix": "explainabilityActionVersion", + "xmlName": "ExplainabilityActionVersion" }, { - directoryName: "layouts", - inFolder: false, - metaFile: false, - suffix: "layout", - xmlName: "Layout", + "directoryName": "applicationSubtypeDefinition", + "inFolder": false, + "metaFile": false, + "suffix": "applicationSubtypeDefinition", + "xmlName": "ApplicationSubtypeDefinition" }, { - directoryName: "documents", - inFolder: true, - metaFile: true, - suffix: "document", - xmlName: "Document", + "directoryName": "businessProcessTypeDefinition", + "inFolder": false, + "metaFile": false, + "suffix": "businessProcessTypeDefinition", + "xmlName": "BusinessProcessTypeDefinition" }, { - directoryName: "weblinks", - inFolder: false, - metaFile: false, - suffix: "weblink", - xmlName: "CustomPageWebLink", + "directoryName": "actionPlanTemplates", + "inFolder": false, + "metaFile": false, + "suffix": "apt", + "xmlName": "ActionPlanTemplate" }, { - directoryName: "letterhead", - inFolder: false, - metaFile: false, - suffix: "letter", - xmlName: "Letterhead", + "directoryName": "documentTypes", + "inFolder": false, + "metaFile": false, + "suffix": "documentType", + "xmlName": "DocumentType" }, { - directoryName: "email", - inFolder: true, - metaFile: true, - suffix: "email", - xmlName: "EmailTemplate", + "directoryName": "recommendationStrategies", + "inFolder": false, + "metaFile": false, + "suffix": "recommendationStrategy", + "xmlName": "RecommendationStrategy" }, { - directoryName: "quickActions", - inFolder: false, - metaFile: false, - suffix: "quickAction", - xmlName: "QuickAction", + "directoryName": "recordActionDeployments", + "inFolder": false, + "metaFile": false, + "suffix": "deployment", + "xmlName": "RecordActionDeployment" }, { - directoryName: "flexipages", - inFolder: false, - metaFile: false, - suffix: "flexipage", - xmlName: "FlexiPage", + "directoryName": "relationshipGraphDefinitions", + "inFolder": false, + "metaFile": false, + "suffix": "relationshipGraphDefinition", + "xmlName": "RelationshipGraphDefinition" }, { - directoryName: "tabs", - inFolder: false, - metaFile: false, - suffix: "tab", - xmlName: "CustomTab", + "directoryName": "omniDataTransforms", + "inFolder": false, + "metaFile": false, + "suffix": "rpt", + "xmlName": "OmniDataTransform" }, { - directoryName: "customApplicationComponents", - inFolder: false, - metaFile: false, - suffix: "customApplicationComponent", - xmlName: "CustomApplicationComponent", + "directoryName": "OmniInteractionConfig", + "inFolder": false, + "metaFile": false, + "suffix": "omniInteractionConfig", + "xmlName": "OmniInteractionConfig" }, { - directoryName: "applications", - inFolder: false, - metaFile: false, - suffix: "app", - xmlName: "CustomApplication", + "directoryName": "omniIntegrationProcedures", + "inFolder": false, + "metaFile": false, + "suffix": "oip", + "xmlName": "OmniIntegrationProcedure" }, { - directoryName: "portals", - inFolder: false, - metaFile: false, - suffix: "portal", - xmlName: "Portal", + "directoryName": "omniScripts", + "inFolder": false, + "metaFile": false, + "suffix": "os", + "xmlName": "OmniScript" }, { - directoryName: "customMetadata", - inFolder: false, - metaFile: false, - suffix: "md", - xmlName: "CustomMetadata", + "directoryName": "omniUiCard", + "inFolder": false, + "metaFile": false, + "suffix": "ouc", + "xmlName": "OmniUiCard" }, { - directoryName: "flows", - inFolder: false, - metaFile: false, - suffix: "flow", - xmlName: "Flow", + "directoryName": "customindex", + "inFolder": false, + "metaFile": false, + "suffix": "indx", + "xmlName": "CustomIndex " }, { - directoryName: "flowDefinitions", - inFolder: false, - metaFile: false, - suffix: "flowDefinition", - xmlName: "FlowDefinition", + "directoryName": "batchCalcJobDefinitions", + "inFolder": false, + "metaFile": false, + "suffix": "batchCalcJobDefinition", + "xmlName": "BatchCalcJobDefinition" }, { - childXmlNames: [ - "WorkflowFieldUpdate", - "WorkflowKnowledgePublish", - "WorkflowTask", - "WorkflowAlert", - "WorkflowSend", - "WorkflowOutboundMessage", - "WorkflowFlowAction", - "WorkflowRule", - ], - directoryName: "workflows", - inFolder: false, - metaFile: false, - suffix: "workflow", - xmlName: "Workflow", + "directoryName": "batchProcessJobDefinitions", + "inFolder": false, + "metaFile": false, + "suffix": "batchProcessJobDefinition", + "xmlName": "BatchProcessJobDefinition" + }, + { + "directoryName": "installedPackages", + "inFolder": false, + "metaFile": false, + "suffix": "installedPackage", + "xmlName": "InstalledPackage" + }, + { + "directoryName": "staticresources", + "inFolder": false, + "metaFile": true, + "suffix": "resource", + "xmlName": "StaticResource" + }, + { + "directoryName": "scontrols", + "inFolder": false, + "metaFile": true, + "suffix": "scf", + "xmlName": "Scontrol" + }, + { + "directoryName": "certs", + "inFolder": false, + "metaFile": true, + "suffix": "crt", + "xmlName": "Certificate" + }, + { + "directoryName": "messageChannels", + "inFolder": false, + "metaFile": false, + "suffix": "messageChannel", + "xmlName": "LightningMessageChannel" + }, + { + "directoryName": "aura", + "inFolder": false, + "metaFile": false, + "xmlName": "AuraDefinitionBundle" + }, + { + "directoryName": "lwc", + "inFolder": false, + "metaFile": false, + "xmlName": "LightningComponentBundle" + }, + { + "directoryName": "components", + "inFolder": false, + "metaFile": true, + "suffix": "component", + "xmlName": "ApexComponent" + }, + { + "directoryName": "pages", + "inFolder": false, + "metaFile": true, + "suffix": "page", + "xmlName": "ApexPage" }, { - childXmlNames: "AssignmentRule", - directoryName: "assignmentRules", - inFolder: false, - metaFile: false, - suffix: "assignmentRules", - xmlName: "AssignmentRules", + "directoryName": "queues", + "inFolder": false, + "metaFile": false, + "suffix": "queue", + "xmlName": "Queue" }, { - childXmlNames: "AutoResponseRule", - directoryName: "autoResponseRules", - inFolder: false, - metaFile: false, - suffix: "autoResponseRules", - xmlName: "AutoResponseRules", + "directoryName": "CaseSubjectParticles", + "inFolder": false, + "metaFile": false, + "suffix": "CaseSubjectParticle", + "xmlName": "CaseSubjectParticle" }, { - childXmlNames: "EscalationRule", - directoryName: "escalationRules", - inFolder: false, - metaFile: false, - suffix: "escalationRules", - xmlName: "EscalationRules", + "directoryName": "dataSources", + "inFolder": false, + "metaFile": false, + "suffix": "dataSource", + "xmlName": "ExternalDataSource" }, { - directoryName: "postTemplates", - inFolder: false, - metaFile: false, - suffix: "postTemplate", - xmlName: "PostTemplate", + "directoryName": "namedCredentials", + "inFolder": false, + "metaFile": false, + "suffix": "namedCredential", + "xmlName": "NamedCredential" }, { - directoryName: "approvalProcesses", - inFolder: false, - metaFile: false, - suffix: "approvalProcess", - xmlName: "ApprovalProcess", + "directoryName": "externalCredentials", + "inFolder": false, + "metaFile": false, + "suffix": "externalCredential", + "xmlName": "ExternalCredential" }, { - directoryName: "homePageComponents", - inFolder: false, - metaFile: false, - suffix: "homePageComponent", - xmlName: "HomePageComponent", + "directoryName": "externalServiceRegistrations", + "inFolder": false, + "metaFile": false, + "suffix": "externalServiceRegistration", + "xmlName": "ExternalServiceRegistration" }, { - directoryName: "homePageLayouts", - inFolder: false, - metaFile: false, - suffix: "homePageLayout", - xmlName: "HomePageLayout", + "directoryName": "roles", + "inFolder": false, + "metaFile": false, + "suffix": "role", + "xmlName": "Role" }, { - directoryName: "objectTranslations", - inFolder: false, - metaFile: false, - suffix: "objectTranslation", - xmlName: "CustomObjectTranslation", + "directoryName": "groups", + "inFolder": false, + "metaFile": false, + "suffix": "group", + "xmlName": "Group" }, { - directoryName: "translations", - inFolder: false, - metaFile: false, - suffix: "translation", - xmlName: "Translations", + "directoryName": "globalValueSets", + "inFolder": false, + "metaFile": false, + "suffix": "globalValueSet", + "xmlName": "GlobalValueSet" }, { - directoryName: "globalValueSetTranslations", - inFolder: false, - metaFile: false, - suffix: "globalValueSetTranslation", - xmlName: "GlobalValueSetTranslation", + "directoryName": "standardValueSets", + "inFolder": false, + "metaFile": false, + "suffix": "standardValueSet", + "xmlName": "StandardValueSet" }, { - directoryName: "standardValueSetTranslations", - inFolder: false, - metaFile: false, - suffix: "standardValueSetTranslation", - xmlName: "StandardValueSetTranslation", + "directoryName": "customPermissions", + "inFolder": false, + "metaFile": false, + "suffix": "customPermission", + "xmlName": "CustomPermission" }, { - directoryName: "classes", - inFolder: false, - metaFile: true, - suffix: "cls", - xmlName: "ApexClass", + "childXmlNames": [ + "CustomField", + "Index", + "BusinessProcess", + "RecordType", + "CompactLayout", + "WebLink", + "ValidationRule", + "SharingReason", + "ListView", + "FieldSet" + ], + "directoryName": "objects", + "inFolder": false, + "metaFile": false, + "suffix": "object", + "xmlName": "CustomObject" }, { - directoryName: "triggers", - inFolder: false, - metaFile: true, - suffix: "trigger", - xmlName: "ApexTrigger", + "directoryName": "businessProcesses", + "inFolder": false, + "metaFile": false, + "suffix": "businessProcess", + "xmlName": "BusinessProcess" }, { - directoryName: "testSuites", - inFolder: false, - metaFile: false, - suffix: "testSuite", - xmlName: "ApexTestSuite", + "directoryName": "compactLayouts", + "inFolder": false, + "metaFile": false, + "suffix": "compactLayout", + "xmlName": "CompactLayout" }, { - directoryName: "profiles", - inFolder: false, - metaFile: false, - suffix: "profile", - xmlName: "Profile", + "directoryName": "fields", + "inFolder": false, + "metaFile": false, + "suffix": "field", + "xmlName": "CustomField" }, { - directoryName: "permissionsets", - inFolder: false, - metaFile: false, - suffix: "permissionset", - xmlName: "PermissionSet", + "directoryName": "fieldSets", + "inFolder": false, + "metaFile": false, + "suffix": "fieldSet", + "xmlName": "FieldSet" }, { - directoryName: "mutingpermissionsets", - inFolder: false, - metaFile: false, - suffix: "mutingpermissionset", - xmlName: "MutingPermissionSet", + "directoryName": "indexes", + "inFolder": false, + "metaFile": false, + "suffix": "index", + "xmlName": "Index" }, { - directoryName: "permissionsetgroups", - inFolder: false, - metaFile: false, - suffix: "permissionsetgroup", - xmlName: "PermissionSetGroup", + "directoryName": "listViews", + "inFolder": false, + "metaFile": false, + "suffix": "listView", + "xmlName": "ListView" }, { - directoryName: "profilePasswordPolicies", - inFolder: false, - metaFile: false, - suffix: "profilePasswordPolicy", - xmlName: "ProfilePasswordPolicy", + "directoryName": "recordTypes", + "inFolder": false, + "metaFile": false, + "suffix": "recordType", + "xmlName": "RecordType" }, { - directoryName: "profileSessionSettings", - inFolder: false, - metaFile: false, - suffix: "profileSessionSetting", - xmlName: "ProfileSessionSetting", + "directoryName": "sharingReasons", + "inFolder": false, + "metaFile": false, + "suffix": "sharingReason", + "xmlName": "SharingReason" }, { - directoryName: "myDomainDiscoverableLogins", - inFolder: false, - metaFile: false, - suffix: "myDomainDiscoverableLogin", - xmlName: "MyDomainDiscoverableLogin", + "directoryName": "validationRules", + "inFolder": false, + "metaFile": false, + "suffix": "validationRule", + "xmlName": "ValidationRule" }, { - directoryName: "oauthcustomscopes", - inFolder: false, - metaFile: false, - suffix: "oauthcustomscope", - xmlName: "OauthCustomScope", + "directoryName": "webLinks", + "inFolder": false, + "metaFile": false, + "suffix": "webLink", + "xmlName": "WebLink" }, { - directoryName: "datacategorygroups", - inFolder: false, - metaFile: false, - suffix: "datacategorygroup", - xmlName: "DataCategoryGroup", + "directoryName": "reportTypes", + "inFolder": false, + "metaFile": false, + "suffix": "reportType", + "xmlName": "ReportType" }, { - directoryName: "remoteSiteSettings", - inFolder: false, - metaFile: false, - suffix: "remoteSite", - xmlName: "RemoteSiteSetting", + "directoryName": "reports", + "inFolder": true, + "metaFile": false, + "suffix": "report", + "xmlName": "Report" }, { - directoryName: "cspTrustedSites", - inFolder: false, - metaFile: false, - suffix: "cspTrustedSite", - xmlName: "CspTrustedSite", + "directoryName": "dashboards", + "inFolder": true, + "metaFile": false, + "suffix": "dashboard", + "xmlName": "Dashboard" }, { - directoryName: "redirectWhitelistUrls", - inFolder: false, - metaFile: false, - suffix: "redirectWhitelistUrl", - xmlName: "RedirectWhitelistUrl", + "directoryName": "analyticSnapshots", + "inFolder": false, + "metaFile": false, + "suffix": "snapshot", + "xmlName": "AnalyticSnapshot" }, { - childXmlNames: "MatchingRule", - directoryName: "matchingRules", - inFolder: false, - metaFile: false, - suffix: "matchingRule", - xmlName: "MatchingRules", + "directoryName": "feedFilters", + "inFolder": false, + "metaFile": false, + "suffix": "feedFilter", + "xmlName": "CustomFeedFilter" }, { - directoryName: "duplicateRules", - inFolder: false, - metaFile: false, - suffix: "duplicateRule", - xmlName: "DuplicateRule", + "directoryName": "layouts", + "inFolder": false, + "metaFile": false, + "suffix": "layout", + "xmlName": "Layout" }, { - directoryName: "cleanDataServices", - inFolder: false, - metaFile: false, - suffix: "cleanDataService", - xmlName: "CleanDataService", + "directoryName": "documents", + "inFolder": true, + "metaFile": true, + "suffix": "document", + "xmlName": "Document" }, { - directoryName: "skills", - inFolder: false, - metaFile: false, - suffix: "skill", - xmlName: "Skill", + "directoryName": "weblinks", + "inFolder": false, + "metaFile": false, + "suffix": "weblink", + "xmlName": "CustomPageWebLink" }, { - directoryName: "serviceChannels", - inFolder: false, - metaFile: false, - suffix: "serviceChannel", - xmlName: "ServiceChannel", + "directoryName": "letterhead", + "inFolder": false, + "metaFile": false, + "suffix": "letter", + "xmlName": "Letterhead" }, { - directoryName: "queueRoutingConfigs", - inFolder: false, - metaFile: false, - suffix: "queueRoutingConfig", - xmlName: "QueueRoutingConfig", + "directoryName": "email", + "inFolder": true, + "metaFile": true, + "suffix": "email", + "xmlName": "EmailTemplate" }, { - directoryName: "servicePresenceStatuses", - inFolder: false, - metaFile: false, - suffix: "servicePresenceStatus", - xmlName: "ServicePresenceStatus", + "directoryName": "quickActions", + "inFolder": false, + "metaFile": false, + "suffix": "quickAction", + "xmlName": "QuickAction" }, { - directoryName: "presenceDeclineReasons", - inFolder: false, - metaFile: false, - suffix: "presenceDeclineReason", - xmlName: "PresenceDeclineReason", + "directoryName": "flexipages", + "inFolder": false, + "metaFile": false, + "suffix": "flexipage", + "xmlName": "FlexiPage" }, { - directoryName: "presenceUserConfigs", - inFolder: false, - metaFile: false, - suffix: "presenceUserConfig", - xmlName: "PresenceUserConfig", + "directoryName": "tabs", + "inFolder": false, + "metaFile": false, + "suffix": "tab", + "xmlName": "CustomTab" }, { - directoryName: "workSkillRoutings", - inFolder: false, - metaFile: false, - suffix: "workSkillRouting", - xmlName: "WorkSkillRouting", + "directoryName": "customApplicationComponents", + "inFolder": false, + "metaFile": false, + "suffix": "customApplicationComponent", + "xmlName": "CustomApplicationComponent" }, { - directoryName: "authproviders", - inFolder: false, - metaFile: false, - suffix: "authprovider", - xmlName: "AuthProvider", + "directoryName": "applications", + "inFolder": false, + "metaFile": false, + "suffix": "app", + "xmlName": "CustomApplication" }, { - directoryName: "eclair", - inFolder: false, - metaFile: true, - suffix: "geodata", - xmlName: "EclairGeoData", + "directoryName": "portals", + "inFolder": false, + "metaFile": false, + "suffix": "portal", + "xmlName": "Portal" }, { - directoryName: "channelLayouts", - inFolder: false, - metaFile: false, - suffix: "channelLayout", - xmlName: "ChannelLayout", + "directoryName": "customMetadata", + "inFolder": false, + "metaFile": false, + "suffix": "md", + "xmlName": "CustomMetadata" }, { - directoryName: "contentassets", - inFolder: false, - metaFile: true, - suffix: "asset", - xmlName: "ContentAsset", + "directoryName": "flows", + "inFolder": false, + "metaFile": false, + "suffix": "flow", + "xmlName": "Flow" }, { - directoryName: "sites", - inFolder: false, - metaFile: false, - suffix: "site", - xmlName: "CustomSite", + "directoryName": "flowtests", + "inFolder": false, + "metaFile": false, + "suffix": "flowtest", + "xmlName": "FlowTest" }, { - childXmlNames: ["SharingOwnerRule", "SharingCriteriaRule", "SharingGuestRule"], - directoryName: "sharingRules", - inFolder: false, - metaFile: false, - suffix: "sharingRules", - xmlName: "SharingRules", + "directoryName": "flowDefinitions", + "inFolder": false, + "metaFile": false, + "suffix": "flowDefinition", + "xmlName": "FlowDefinition" }, { - directoryName: "sharingSets", - inFolder: false, - metaFile: false, - suffix: "sharingSet", - xmlName: "SharingSet", + "directoryName": "postTemplates", + "inFolder": false, + "metaFile": false, + "suffix": "postTemplate", + "xmlName": "PostTemplate" }, { - directoryName: "iframeWhiteListUrlSettings", - inFolder: false, - metaFile: false, - suffix: "iframeWhiteListUrlSettings", - xmlName: "IframeWhiteListUrlSettings", + "directoryName": "approvalProcesses", + "inFolder": false, + "metaFile": false, + "suffix": "approvalProcess", + "xmlName": "ApprovalProcess" }, { - directoryName: "communities", - inFolder: false, - metaFile: false, - suffix: "community", - xmlName: "Community", + "directoryName": "homePageComponents", + "inFolder": false, + "metaFile": false, + "suffix": "homePageComponent", + "xmlName": "HomePageComponent" }, { - directoryName: "ChatterExtensions", - inFolder: false, - metaFile: false, - suffix: "ChatterExtension", - xmlName: "ChatterExtension", + "directoryName": "homePageLayouts", + "inFolder": false, + "metaFile": false, + "suffix": "homePageLayout", + "xmlName": "HomePageLayout" }, { - directoryName: "platformEventChannels", - inFolder: false, - metaFile: false, - suffix: "platformEventChannel", - xmlName: "PlatformEventChannel", + "directoryName": "classes", + "inFolder": false, + "metaFile": true, + "suffix": "cls", + "xmlName": "ApexClass" }, { - directoryName: "platformEventChannelMembers", - inFolder: false, - metaFile: false, - suffix: "platformEventChannelMember", - xmlName: "PlatformEventChannelMember", + "directoryName": "triggers", + "inFolder": false, + "metaFile": true, + "suffix": "trigger", + "xmlName": "ApexTrigger" }, { - directoryName: "PlatformEventSubscriberConfigs", - inFolder: false, - metaFile: false, - suffix: "platformEventSubscriberConfig", - xmlName: "PlatformEventSubscriberConfig", + "directoryName": "testSuites", + "inFolder": false, + "metaFile": false, + "suffix": "testSuite", + "xmlName": "ApexTestSuite" }, { - directoryName: "callCenters", - inFolder: false, - metaFile: false, - suffix: "callCenter", - xmlName: "CallCenter", + "directoryName": "permissionsets", + "inFolder": false, + "metaFile": false, + "suffix": "permissionset", + "xmlName": "PermissionSet" }, { - directoryName: "milestoneTypes", - inFolder: false, - metaFile: false, - suffix: "milestoneType", - xmlName: "MilestoneType", + "directoryName": "mutingpermissionsets", + "inFolder": false, + "metaFile": false, + "suffix": "mutingpermissionset", + "xmlName": "MutingPermissionSet" }, { - directoryName: "entitlementProcesses", - inFolder: false, - metaFile: false, - suffix: "entitlementProcess", - xmlName: "EntitlementProcess", + "directoryName": "permissionsetgroups", + "inFolder": false, + "metaFile": false, + "suffix": "permissionsetgroup", + "xmlName": "PermissionSetGroup" }, { - directoryName: "entitlementTemplates", - inFolder: false, - metaFile: false, - suffix: "entitlementTemplate", - xmlName: "EntitlementTemplate", + "directoryName": "profilePasswordPolicies", + "inFolder": false, + "metaFile": false, + "suffix": "profilePasswordPolicy", + "xmlName": "ProfilePasswordPolicy" }, { - directoryName: "timeSheetTemplates", - inFolder: false, - metaFile: false, - suffix: "timeSheetTemplate", - xmlName: "TimeSheetTemplate", + "directoryName": "profileSessionSettings", + "inFolder": false, + "metaFile": false, + "suffix": "profileSessionSetting", + "xmlName": "ProfileSessionSetting" }, { - directoryName: "appointmentSchedulingPolicies", - inFolder: false, - metaFile: false, - suffix: "policy", - xmlName: "AppointmentSchedulingPolicy", + "directoryName": "myDomainDiscoverableLogins", + "inFolder": false, + "metaFile": false, + "suffix": "myDomainDiscoverableLogin", + "xmlName": "MyDomainDiscoverableLogin" }, { - directoryName: "Canvases", - inFolder: false, - metaFile: false, - suffix: "Canvas", - xmlName: "CanvasMetadata", + "directoryName": "oauthcustomscopes", + "inFolder": false, + "metaFile": false, + "suffix": "oauthcustomscope", + "xmlName": "OauthCustomScope" }, { - directoryName: "MobileApplicationDetails", - inFolder: false, - metaFile: false, - suffix: "MobileApplicationDetail", - xmlName: "MobileApplicationDetail", + "directoryName": "datacategorygroups", + "inFolder": false, + "metaFile": false, + "suffix": "datacategorygroup", + "xmlName": "DataCategoryGroup" }, { - directoryName: "notificationtypes", - inFolder: false, - metaFile: false, - suffix: "notiftype", - xmlName: "CustomNotificationType", + "directoryName": "remoteSiteSettings", + "inFolder": false, + "metaFile": false, + "suffix": "remoteSite", + "xmlName": "RemoteSiteSetting" }, { - directoryName: "connectedApps", - inFolder: false, - metaFile: false, - suffix: "connectedApp", - xmlName: "ConnectedApp", + "directoryName": "cspTrustedSites", + "inFolder": false, + "metaFile": false, + "suffix": "cspTrustedSite", + "xmlName": "CspTrustedSite" }, { - directoryName: "appMenus", - inFolder: false, - metaFile: false, - suffix: "appMenu", - xmlName: "AppMenu", + "directoryName": "redirectWhitelistUrls", + "inFolder": false, + "metaFile": false, + "suffix": "redirectWhitelistUrl", + "xmlName": "RedirectWhitelistUrl" }, { - directoryName: "notificationTypeConfig", - inFolder: false, - metaFile: false, - suffix: "config", - xmlName: "NotificationTypeConfig", + "directoryName": "duplicateRules", + "inFolder": false, + "metaFile": false, + "suffix": "duplicateRule", + "xmlName": "DuplicateRule" }, { - directoryName: "delegateGroups", - inFolder: false, - metaFile: false, - suffix: "delegateGroup", - xmlName: "DelegateGroup", + "directoryName": "cleanDataServices", + "inFolder": false, + "metaFile": false, + "suffix": "cleanDataService", + "xmlName": "CleanDataService" }, { - directoryName: "siteDotComSites", - inFolder: false, - metaFile: true, - suffix: "site", - xmlName: "SiteDotCom", + "directoryName": "skills", + "inFolder": false, + "metaFile": false, + "suffix": "skill", + "xmlName": "Skill" }, { - directoryName: "experiences", - inFolder: false, - metaFile: true, - suffix: "site", - xmlName: "ExperienceBundle", + "directoryName": "serviceChannels", + "inFolder": false, + "metaFile": false, + "suffix": "serviceChannel", + "xmlName": "ServiceChannel" }, { - directoryName: "networks", - inFolder: false, - metaFile: false, - suffix: "network", - xmlName: "Network", + "directoryName": "queueRoutingConfigs", + "inFolder": false, + "metaFile": false, + "suffix": "queueRoutingConfig", + "xmlName": "QueueRoutingConfig" }, { - directoryName: "networkBranding", - inFolder: false, - metaFile: true, - suffix: "networkBranding", - xmlName: "NetworkBranding", + "directoryName": "servicePresenceStatuses", + "inFolder": false, + "metaFile": false, + "suffix": "servicePresenceStatus", + "xmlName": "ServicePresenceStatus" }, { - directoryName: "brandingSets", - inFolder: false, - metaFile: false, - suffix: "brandingSet", - xmlName: "BrandingSet", + "directoryName": "presenceDeclineReasons", + "inFolder": false, + "metaFile": false, + "suffix": "presenceDeclineReason", + "xmlName": "PresenceDeclineReason" }, { - directoryName: "communityThemeDefinitions", - inFolder: false, - metaFile: false, - suffix: "communityThemeDefinition", - xmlName: "CommunityThemeDefinition", + "directoryName": "presenceUserConfigs", + "inFolder": false, + "metaFile": false, + "suffix": "presenceUserConfig", + "xmlName": "PresenceUserConfig" }, { - directoryName: "communityTemplateDefinitions", - inFolder: false, - metaFile: false, - suffix: "communityTemplateDefinition", - xmlName: "CommunityTemplateDefinition", + "directoryName": "workSkillRoutings", + "inFolder": false, + "metaFile": false, + "suffix": "workSkillRouting", + "xmlName": "WorkSkillRouting" }, { - directoryName: "navigationMenus", - inFolder: false, - metaFile: false, - suffix: "navigationMenu", - xmlName: "NavigationMenu", + "directoryName": "authproviders", + "inFolder": false, + "metaFile": false, + "suffix": "authprovider", + "xmlName": "AuthProvider" }, { - directoryName: "audience", - inFolder: false, - metaFile: false, - suffix: "audience", - xmlName: "Audience", + "directoryName": "eclair", + "inFolder": false, + "metaFile": true, + "suffix": "geodata", + "xmlName": "EclairGeoData" }, { - directoryName: "flowCategories", - inFolder: false, - metaFile: false, - suffix: "flowCategory", - xmlName: "FlowCategory", + "directoryName": "channelLayouts", + "inFolder": false, + "metaFile": false, + "suffix": "channelLayout", + "xmlName": "ChannelLayout" }, { - directoryName: "lightningBolts", - inFolder: false, - metaFile: false, - suffix: "lightningBolt", - xmlName: "LightningBolt", + "directoryName": "contentassets", + "inFolder": false, + "metaFile": true, + "suffix": "asset", + "xmlName": "ContentAsset" }, { - directoryName: "lightningExperienceThemes", - inFolder: false, - metaFile: false, - suffix: "lightningExperienceTheme", - xmlName: "LightningExperienceTheme", + "directoryName": "sites", + "inFolder": false, + "metaFile": false, + "suffix": "site", + "xmlName": "CustomSite" }, { - directoryName: "lightningOnboardingConfigs", - inFolder: false, - metaFile: false, - suffix: "lightningOnboardingConfig", - xmlName: "LightningOnboardingConfig", + "childXmlNames": [ + "SharingOwnerRule", + "SharingCriteriaRule", + "SharingGuestRule" + ], + "directoryName": "sharingRules", + "inFolder": false, + "metaFile": false, + "suffix": "sharingRules", + "xmlName": "SharingRules" }, { - directoryName: "customHelpMenuSections", - inFolder: false, - metaFile: false, - suffix: "customHelpMenuSection", - xmlName: "CustomHelpMenuSection", + "directoryName": "sharingSets", + "inFolder": false, + "metaFile": false, + "suffix": "sharingSet", + "xmlName": "SharingSet" }, { - directoryName: "prompts", - inFolder: false, - metaFile: false, - suffix: "prompt", - xmlName: "Prompt", + "directoryName": "iframeWhiteListUrlSettings", + "inFolder": false, + "metaFile": false, + "suffix": "iframeWhiteListUrlSettings", + "xmlName": "IframeWhiteListUrlSettings" }, { - childXmlNames: "ManagedTopic", - directoryName: "managedTopics", - inFolder: false, - metaFile: false, - suffix: "managedTopics", - xmlName: "ManagedTopics", + "directoryName": "communities", + "inFolder": false, + "metaFile": false, + "suffix": "community", + "xmlName": "Community" }, { - directoryName: "moderation", - inFolder: false, - metaFile: false, - suffix: "keywords", - xmlName: "KeywordList", + "directoryName": "ChatterExtensions", + "inFolder": false, + "metaFile": false, + "suffix": "ChatterExtension", + "xmlName": "ChatterExtension" }, { - directoryName: "userCriteria", - inFolder: false, - metaFile: false, - suffix: "userCriteria", - xmlName: "UserCriteria", + "directoryName": "platformEventChannels", + "inFolder": false, + "metaFile": false, + "suffix": "platformEventChannel", + "xmlName": "PlatformEventChannel" }, { - directoryName: "moderation", - inFolder: false, - metaFile: false, - suffix: "rule", - xmlName: "ModerationRule", + "directoryName": "platformEventChannelMembers", + "inFolder": false, + "metaFile": false, + "suffix": "platformEventChannelMember", + "xmlName": "PlatformEventChannelMember" }, { - directoryName: "cmsConnectSource", - inFolder: false, - metaFile: false, - suffix: "cmsConnectSource", - xmlName: "CMSConnectSource", + "directoryName": "PlatformEventSubscriberConfigs", + "inFolder": false, + "metaFile": false, + "suffix": "platformEventSubscriberConfig", + "xmlName": "PlatformEventSubscriberConfig" }, { - directoryName: "managedContentTypes", - inFolder: false, - metaFile: false, - suffix: "managedContentType", - xmlName: "ManagedContentType", + "directoryName": "callCenters", + "inFolder": false, + "metaFile": false, + "suffix": "callCenter", + "xmlName": "CallCenter" }, { - directoryName: "territory2Types", - inFolder: false, - metaFile: false, - suffix: "territory2Type", - xmlName: "Territory2Type", + "directoryName": "milestoneTypes", + "inFolder": false, + "metaFile": false, + "suffix": "milestoneType", + "xmlName": "MilestoneType" }, { - childXmlNames: ["Territory2Rule", "Territory2"], - directoryName: "territory2Models", - inFolder: false, - metaFile: false, - suffix: "territory2Model", - xmlName: "Territory2Model", + "directoryName": "entitlementProcesses", + "inFolder": false, + "metaFile": false, + "suffix": "entitlementProcess", + "xmlName": "EntitlementProcess" }, { - directoryName: "rules", - inFolder: false, - metaFile: false, - suffix: "territory2Rule", - xmlName: "Territory2Rule", + "directoryName": "entitlementTemplates", + "inFolder": false, + "metaFile": false, + "suffix": "entitlementTemplate", + "xmlName": "EntitlementTemplate" }, { - directoryName: "territories", - inFolder: false, - metaFile: false, - suffix: "territory2", - xmlName: "Territory2", + "directoryName": "timeSheetTemplates", + "inFolder": false, + "metaFile": false, + "suffix": "timeSheetTemplate", + "xmlName": "TimeSheetTemplate" }, { - directoryName: "campaignInfluenceModels", - inFolder: false, - metaFile: false, - suffix: "campaignInfluenceModel", - xmlName: "CampaignInfluenceModel", + "directoryName": "appointmentSchedulingPolicies", + "inFolder": false, + "metaFile": false, + "suffix": "policy", + "xmlName": "AppointmentSchedulingPolicy" }, { - directoryName: "samlssoconfigs", - inFolder: false, - metaFile: false, - suffix: "samlssoconfig", - xmlName: "SamlSsoConfig", + "directoryName": "Canvases", + "inFolder": false, + "metaFile": false, + "suffix": "Canvas", + "xmlName": "CanvasMetadata" }, { - directoryName: "corsWhitelistOrigins", - inFolder: false, - metaFile: false, - suffix: "corsWhitelistOrigin", - xmlName: "CorsWhitelistOrigin", + "directoryName": "MobileApplicationDetails", + "inFolder": false, + "metaFile": false, + "suffix": "MobileApplicationDetail", + "xmlName": "MobileApplicationDetail" }, { - directoryName: "actionLinkGroupTemplates", - inFolder: false, - metaFile: false, - suffix: "actionLinkGroupTemplate", - xmlName: "ActionLinkGroupTemplate", + "directoryName": "notificationtypes", + "inFolder": false, + "metaFile": false, + "suffix": "notiftype", + "xmlName": "CustomNotificationType" }, { - directoryName: "transactionSecurityPolicies", - inFolder: false, - metaFile: false, - suffix: "transactionSecurityPolicy", - xmlName: "TransactionSecurityPolicy", + "directoryName": "connectedApps", + "inFolder": false, + "metaFile": false, + "suffix": "connectedApp", + "xmlName": "ConnectedApp" }, { - directoryName: "liveChatDeployments", - inFolder: false, - metaFile: false, - suffix: "liveChatDeployment", - xmlName: "LiveChatDeployment", + "directoryName": "appMenus", + "inFolder": false, + "metaFile": false, + "suffix": "appMenu", + "xmlName": "AppMenu" }, { - directoryName: "liveChatButtons", - inFolder: false, - metaFile: false, - suffix: "liveChatButton", - xmlName: "LiveChatButton", + "directoryName": "notificationTypeConfig", + "inFolder": false, + "metaFile": false, + "suffix": "config", + "xmlName": "NotificationTypeConfig" }, { - directoryName: "liveChatAgentConfigs", - inFolder: false, - metaFile: false, - suffix: "liveChatAgentConfig", - xmlName: "LiveChatAgentConfig", + "directoryName": "delegateGroups", + "inFolder": false, + "metaFile": false, + "suffix": "delegateGroup", + "xmlName": "DelegateGroup" }, { - directoryName: "synonymDictionaries", - inFolder: false, - metaFile: false, - suffix: "synonymDictionary", - xmlName: "SynonymDictionary", + "directoryName": "siteDotComSites", + "inFolder": false, + "metaFile": true, + "suffix": "site", + "xmlName": "SiteDotCom" }, { - directoryName: "pathAssistants", - inFolder: false, - metaFile: false, - suffix: "pathAssistant", - xmlName: "PathAssistant", + "directoryName": "experiences", + "inFolder": false, + "metaFile": true, + "suffix": "site", + "xmlName": "ExperienceBundle" }, { - directoryName: "animationRules", - inFolder: false, - metaFile: false, - suffix: "animationRule", - xmlName: "AnimationRule", + "directoryName": "digitalExperienceConfigs", + "inFolder": false, + "metaFile": true, + "suffix": "digitalExperienceConfig", + "xmlName": "DigitalExperienceConfig" }, { - directoryName: "LeadConvertSettings", - inFolder: false, - metaFile: false, - suffix: "LeadConvertSetting", - xmlName: "LeadConvertSettings", + "directoryName": "digitalExperiences", + "inFolder": false, + "metaFile": true, + "suffix": "digitalExperience", + "xmlName": "DigitalExperienceBundle" }, { - directoryName: "liveChatSensitiveDataRule", - inFolder: false, - metaFile: false, - suffix: "liveChatSensitiveDataRule", - xmlName: "LiveChatSensitiveDataRule", + "directoryName": "networks", + "inFolder": false, + "metaFile": false, + "suffix": "network", + "xmlName": "Network" }, { - directoryName: "cachePartitions", - inFolder: false, - metaFile: false, - suffix: "cachePartition", - xmlName: "PlatformCachePartition", + "directoryName": "networkBranding", + "inFolder": false, + "metaFile": true, + "suffix": "networkBranding", + "xmlName": "NetworkBranding" }, { - directoryName: "topicsForObjects", - inFolder: false, - metaFile: false, - suffix: "topicsForObjects", - xmlName: "TopicsForObjects", + "directoryName": "brandingSets", + "inFolder": false, + "metaFile": false, + "suffix": "brandingSet", + "xmlName": "BrandingSet" }, { - directoryName: "recommendationStrategies", - inFolder: false, - metaFile: false, - suffix: "recommendationStrategy", - xmlName: "RecommendationStrategy", + "directoryName": "communityThemeDefinitions", + "inFolder": false, + "metaFile": false, + "suffix": "communityThemeDefinition", + "xmlName": "CommunityThemeDefinition" }, { - directoryName: "emailservices", - inFolder: false, - metaFile: false, - suffix: "xml", - xmlName: "EmailServicesFunction", + "directoryName": "communityTemplateDefinitions", + "inFolder": false, + "metaFile": false, + "suffix": "communityTemplateDefinition", + "xmlName": "CommunityTemplateDefinition" }, { - directoryName: "recordActionDeployments", - inFolder: false, - metaFile: false, - suffix: "deployment", - xmlName: "RecordActionDeployment", + "directoryName": "navigationMenus", + "inFolder": false, + "metaFile": false, + "suffix": "navigationMenu", + "xmlName": "NavigationMenu" }, { - directoryName: "EmbeddedServiceConfig", - inFolder: false, - metaFile: false, - suffix: "EmbeddedServiceConfig", - xmlName: "EmbeddedServiceConfig", + "directoryName": "audience", + "inFolder": false, + "metaFile": false, + "suffix": "audience", + "xmlName": "Audience" }, { - directoryName: "EmbeddedServiceLiveAgent", - inFolder: false, - metaFile: false, - suffix: "EmbeddedServiceLiveAgent", - xmlName: "EmbeddedServiceLiveAgent", + "directoryName": "flowCategories", + "inFolder": false, + "metaFile": false, + "suffix": "flowCategory", + "xmlName": "FlowCategory" }, { - directoryName: "EmbeddedServiceBranding", - inFolder: false, - metaFile: false, - suffix: "EmbeddedServiceBranding", - xmlName: "EmbeddedServiceBranding", + "directoryName": "lightningBolts", + "inFolder": false, + "metaFile": false, + "suffix": "lightningBolt", + "xmlName": "LightningBolt" }, { - directoryName: "EmbeddedServiceFlowConfig", - inFolder: false, - metaFile: false, - suffix: "EmbeddedServiceFlowConfig", - xmlName: "EmbeddedServiceFlowConfig", + "directoryName": "lightningExperienceThemes", + "inFolder": false, + "metaFile": false, + "suffix": "lightningExperienceTheme", + "xmlName": "LightningExperienceTheme" }, { - directoryName: "EmbeddedServiceMenuSettings", - inFolder: false, - metaFile: false, - suffix: "EmbeddedServiceMenuSettings", - xmlName: "EmbeddedServiceMenuSettings", + "directoryName": "lightningOnboardingConfigs", + "inFolder": false, + "metaFile": false, + "suffix": "lightningOnboardingConfig", + "xmlName": "LightningOnboardingConfig" }, { - directoryName: "settings", - inFolder: false, - metaFile: false, - suffix: "settings", - xmlName: "Settings", + "directoryName": "customHelpMenuSections", + "inFolder": false, + "metaFile": false, + "suffix": "customHelpMenuSection", + "xmlName": "CustomHelpMenuSection" }, { - directoryName: "mlDomains", - inFolder: false, - metaFile: false, - suffix: "mlDomain", - xmlName: "MlDomain", + "directoryName": "prompts", + "inFolder": false, + "metaFile": false, + "suffix": "prompt", + "xmlName": "Prompt" }, { - directoryName: "discovery", - inFolder: false, - metaFile: true, - content: [ + "childXmlNames": "ManagedTopic", + "directoryName": "managedTopics", + "inFolder": false, + "metaFile": false, + "suffix": "managedTopics", + "xmlName": "ManagedTopics" + }, + { + "directoryName": "userCriteria", + "inFolder": false, + "metaFile": false, + "suffix": "userCriteria", + "xmlName": "UserCriteria" + }, + { + "directoryName": "moderation", + "inFolder": false, + "metaFile": false, + "xmlName": "VirtualModeration", + "content": [ { - suffix: "model", - xmlName: "DiscoveryAIModel", + "suffix": "keywords", + "xmlName": "KeywordList" }, { - suffix: "goal", - xmlName: "DiscoveryGoal", - }, + "suffix": "rule", + "xmlName": "ModerationRule" + } + ] + }, + { + "directoryName": "cmsConnectSource", + "inFolder": false, + "metaFile": false, + "suffix": "cmsConnectSource", + "xmlName": "CMSConnectSource" + }, + { + "directoryName": "managedContentTypes", + "inFolder": false, + "metaFile": false, + "suffix": "managedContentType", + "xmlName": "ManagedContentType" + }, + { + "directoryName": "territory2Types", + "inFolder": false, + "metaFile": false, + "suffix": "territory2Type", + "xmlName": "Territory2Type" + }, + { + "childXmlNames": [ + "Territory2Rule", + "Territory2" ], + "directoryName": "territory2Models", + "inFolder": false, + "metaFile": false, + "suffix": "territory2Model", + "xmlName": "Territory2Model" + }, + { + "directoryName": "rules", + "inFolder": false, + "metaFile": false, + "suffix": "territory2Rule", + "xmlName": "Territory2Rule" + }, + { + "directoryName": "territories", + "inFolder": false, + "metaFile": false, + "suffix": "territory2", + "xmlName": "Territory2" + }, + { + "directoryName": "campaignInfluenceModels", + "inFolder": false, + "metaFile": false, + "suffix": "campaignInfluenceModel", + "xmlName": "CampaignInfluenceModel" + }, + { + "directoryName": "samlssoconfigs", + "inFolder": false, + "metaFile": false, + "suffix": "samlssoconfig", + "xmlName": "SamlSsoConfig" + }, + { + "directoryName": "corsWhitelistOrigins", + "inFolder": false, + "metaFile": false, + "suffix": "corsWhitelistOrigin", + "xmlName": "CorsWhitelistOrigin" + }, + { + "directoryName": "actionLinkGroupTemplates", + "inFolder": false, + "metaFile": false, + "suffix": "actionLinkGroupTemplate", + "xmlName": "ActionLinkGroupTemplate" + }, + { + "directoryName": "transactionSecurityPolicies", + "inFolder": false, + "metaFile": false, + "suffix": "transactionSecurityPolicy", + "xmlName": "TransactionSecurityPolicy" + }, + { + "directoryName": "liveChatDeployments", + "inFolder": false, + "metaFile": false, + "suffix": "liveChatDeployment", + "xmlName": "LiveChatDeployment" + }, + { + "directoryName": "liveChatButtons", + "inFolder": false, + "metaFile": false, + "suffix": "liveChatButton", + "xmlName": "LiveChatButton" + }, + { + "directoryName": "liveChatAgentConfigs", + "inFolder": false, + "metaFile": false, + "suffix": "liveChatAgentConfig", + "xmlName": "LiveChatAgentConfig" + }, + { + "directoryName": "synonymDictionaries", + "inFolder": false, + "metaFile": false, + "suffix": "synonymDictionary", + "xmlName": "SynonymDictionary" + }, + { + "directoryName": "pathAssistants", + "inFolder": false, + "metaFile": false, + "suffix": "pathAssistant", + "xmlName": "PathAssistant" + }, + { + "directoryName": "animationRules", + "inFolder": false, + "metaFile": false, + "suffix": "animationRule", + "xmlName": "AnimationRule" + }, + { + "directoryName": "LeadConvertSettings", + "inFolder": false, + "metaFile": false, + "suffix": "LeadConvertSetting", + "xmlName": "LeadConvertSettings" + }, + { + "directoryName": "liveChatSensitiveDataRule", + "inFolder": false, + "metaFile": false, + "suffix": "liveChatSensitiveDataRule", + "xmlName": "LiveChatSensitiveDataRule" + }, + { + "directoryName": "cachePartitions", + "inFolder": false, + "metaFile": false, + "suffix": "cachePartition", + "xmlName": "PlatformCachePartition" + }, + { + "directoryName": "topicsForObjects", + "inFolder": false, + "metaFile": false, + "suffix": "topicsForObjects", + "xmlName": "TopicsForObjects" + }, + { + "directoryName": "recommendationStrategies", + "inFolder": false, + "metaFile": false, + "suffix": "recommendationStrategy", + "xmlName": "RecommendationStrategy" + }, + { + "directoryName": "emailservices", + "inFolder": false, + "metaFile": false, + "suffix": "xml", + "xmlName": "EmailServicesFunction" + }, + { + "directoryName": "recordActionDeployments", + "inFolder": false, + "metaFile": false, + "suffix": "deployment", + "xmlName": "RecordActionDeployment" + }, + { + "directoryName": "restrictionRules", + "inFolder": false, + "metaFile": false, + "suffix": "rule", + "xmlName": "RestrictionRule" + }, + { + "directoryName": "EmbeddedServiceConfig", + "inFolder": false, + "metaFile": false, + "suffix": "EmbeddedServiceConfig", + "xmlName": "EmbeddedServiceConfig" + }, + { + "directoryName": "EmbeddedServiceLiveAgent", + "inFolder": false, + "metaFile": false, + "suffix": "EmbeddedServiceLiveAgent", + "xmlName": "EmbeddedServiceLiveAgent" }, { - directoryName: "wave", - inFolder: false, - metaFile: true, - content: [ + "directoryName": "EmbeddedServiceBranding", + "inFolder": false, + "metaFile": false, + "suffix": "EmbeddedServiceBranding", + "xmlName": "EmbeddedServiceBranding" + }, + { + "directoryName": "EmbeddedServiceFlowConfig", + "inFolder": false, + "metaFile": false, + "suffix": "EmbeddedServiceFlowConfig", + "xmlName": "EmbeddedServiceFlowConfig" + }, + { + "directoryName": "EmbeddedServiceMenuSettings", + "inFolder": false, + "metaFile": false, + "suffix": "EmbeddedServiceMenuSettings", + "xmlName": "EmbeddedServiceMenuSettings" + }, + { + "directoryName": "settings", + "inFolder": false, + "metaFile": false, + "suffix": "settings", + "xmlName": "Settings" + }, + { + "directoryName": "mlDomains", + "inFolder": false, + "metaFile": false, + "suffix": "mlDomain", + "xmlName": "MlDomain" + }, + { + "directoryName": "discovery", + "inFolder": false, + "metaFile": true, + "xmlName": "VirtualDiscovery", + "content": [ { - suffix: "wapp", - xmlName: "WaveApplication", + "suffix": "model", + "xmlName": "DiscoveryAIModel" }, { - suffix: "wcomp", - xmlName: "WaveComponent", + "suffix": "goal", + "xmlName": "DiscoveryGoal" + } + ] + }, + { + "directoryName": "wave", + "inFolder": false, + "metaFile": true, + "xmlName": "VirtualWave", + "content": [ + { + "suffix": "wapp", + "xmlName": "WaveApplication" }, { - suffix: "wdf", - xmlName: "WaveDataflow", + "suffix": "wcomp", + "xmlName": "WaveComponent" }, { - suffix: "wdash", - xmlName: "WaveDashboard", + "suffix": "wdf", + "xmlName": "WaveDataflow" }, { - suffix: "wds", - xmlName: "WaveDataset", + "suffix": "wdash", + "xmlName": "WaveDashboard" }, { - suffix: "wlens", - xmlName: "WaveLens", + "suffix": "wds", + "xmlName": "WaveDataset" }, { - suffix: "wdpr", - xmlName: "WaveRecipe", + "suffix": "wlens", + "xmlName": "WaveLens" }, { - suffix: "xmd", - xmlName: "WaveXmd", + "suffix": "wdpr", + "xmlName": "WaveRecipe" }, - ], + { + "suffix": "xmd", + "xmlName": "WaveXmd" + } + ] }, { - directoryName: "waveTemplates", - inFolder: true, - metaFile: false, - xmlName: "WaveTemplateBundle", + "directoryName": "waveTemplates", + "inFolder": true, + "metaFile": false, + "xmlName": "WaveTemplateBundle" }, { - directoryName: "bots", - inFolder: false, - metaFile: true, - content: [ + "directoryName": "bots", + "inFolder": false, + "metaFile": true, + "xmlName": "VirtualBot", + "content": [ { - suffix: "bot", - xmlName: "Bot", + "suffix": "bot", + "xmlName": "Bot" }, { - suffix: "botVersion", - xmlName: "BotVersion", - }, - ], + "suffix": "botVersion", + "xmlName": "BotVersion" + } + ] }, { - directoryName: "workflows.alerts", - inFolder: false, - metaFile: false, - parentXmlName: "Workflow", - xmlName: "WorkflowAlert", - xmlTag: "alerts", - }, - { - directoryName: "workflows.fieldUpdates", - inFolder: false, - metaFile: false, - parentXmlName: "Workflow", - xmlName: "WorkflowFieldUpdate", - xmlTag: "fieldUpdates", - }, - { - directoryName: "labels.labels", - inFolder: false, - metaFile: false, - parentXmlName: "CustomLabels", - xmlName: "CustomLabel", - xmlTag: "labels", - }, - { - directoryName: "workflows.outboundMessages", - inFolder: false, - metaFile: false, - parentXmlName: "Workflow", - xmlName: "WorkflowOutboundMessage", - xmlTag: "outboundMessages", - }, - { - directoryName: "workflows.rules", - inFolder: false, - metaFile: false, - parentXmlName: "Workflow", - xmlName: "WorkflowRule", - xmlTag: "rules", + "childXmlNames": [ + "MarketingAppExtActivity" + ], + "directoryName": "marketingappextensions", + "inFolder": false, + "metaFile": false, + "suffix": "marketingappextension", + "xmlName": "MarketingAppExtension" }, { - directoryName: "sharingRules.sharingCriteriaRules", - inFolder: false, - metaFile: false, - parentXmlName: "SharingRules", - xmlName: "SharingCriteriaRule", - xmlTag: "sharingCriteriaRules", + "inFolder": false, + "metaFile": false, + "parentXmlName": "MarketingAppExtension", + "xmlName": "MarketingAppExtActivity", + "xmlTag": "marketingAppExtActivities", + "key": "fullName" }, { - directoryName: "sharingRules.sharingGuestRules", - inFolder: false, - metaFile: false, - parentXmlName: "SharingRules", - xmlName: "SharingGuestRule", - xmlTag: "sharingGuestRules", + "childXmlNames": [ + "WorkflowFieldUpdate", + "WorkflowKnowledgePublish", + "WorkflowTask", + "WorkflowAlert", + "WorkflowSend", + "WorkflowOutboundMessage", + "WorkflowRule" + ], + "directoryName": "workflows", + "inFolder": false, + "metaFile": false, + "suffix": "workflow", + "xmlName": "Workflow" + }, + { + "directoryName": "alerts", + "inFolder": false, + "metaFile": false, + "suffix": "alert", + "parentXmlName": "Workflow", + "xmlName": "WorkflowAlert", + "xmlTag": "alerts", + "key": "fullName" + }, + { + "directoryName": "fieldUpdates", + "inFolder": false, + "metaFile": false, + "suffix": "fieldUpdate", + "parentXmlName": "Workflow", + "xmlName": "WorkflowFieldUpdate", + "xmlTag": "fieldUpdates", + "key": "fullName" + }, + { + "directoryName": "labels", + "inFolder": false, + "metaFile": false, + "parentXmlName": "CustomLabels", + "xmlName": "CustomLabel", + "childXmlNames": "CustomLabel", + "suffix": "labels", + "xmlTag": "labels", + "key": "fullName" + }, + { + "directoryName": "outboundMessages", + "inFolder": false, + "metaFile": false, + "suffix": "outboundMessage", + "parentXmlName": "Workflow", + "xmlName": "WorkflowOutboundMessage", + "xmlTag": "outboundMessages", + "key": "fullName" + }, + { + "directoryName": "rules", + "inFolder": false, + "metaFile": false, + "suffix": "rule", + "parentXmlName": "Workflow", + "xmlName": "WorkflowRule", + "xmlTag": "rules", + "key": "fullName" + }, + { + "directoryName": "knowledgePublishes", + "inFolder": false, + "metaFile": false, + "suffix": "knowledgePublishe", + "parentXmlName": "Workflow", + "xmlName": "WorkflowKnowledgePublish", + "xmlTag": "knowledgePublishes", + "key": "fullName" + }, + { + "directoryName": "tasks", + "inFolder": false, + "metaFile": false, + "suffix": "task", + "parentXmlName": "Workflow", + "xmlName": "WorkflowTask", + "xmlTag": "tasks", + "key": "fullName" + }, + { + "directoryName": "sharingCriteriaRules", + "inFolder": false, + "metaFile": false, + "suffix": "sharingCriteriaRule", + "parentXmlName": "SharingRules", + "xmlName": "SharingCriteriaRule", + "xmlTag": "sharingCriteriaRules", + "key": "fullName" + }, + { + "directoryName": "sharingGuestRules", + "inFolder": false, + "metaFile": false, + "suffix": "sharingGuestRule", + "parentXmlName": "SharingRules", + "xmlName": "SharingGuestRule", + "xmlTag": "sharingGuestRules", + "key": "fullName" + }, + { + "directoryName": "sharingOwnerRules", + "inFolder": false, + "metaFile": false, + "suffix": "sharingOwnerRule", + "parentXmlName": "SharingRules", + "xmlName": "SharingOwnerRule", + "xmlTag": "sharingOwnerRules", + "key": "fullName" + }, + { + "directoryName": "sharingTerritoryRules", + "inFolder": false, + "metaFile": false, + "suffix": "sharingTerritoryRule", + "parentXmlName": "SharingRules", + "xmlName": "SharingTerritoryRule", + "xmlTag": "sharingTerritoryRules", + "key": "fullName" + }, + { + "childXmlNames": "AssignmentRule", + "directoryName": "assignmentRules", + "inFolder": false, + "metaFile": false, + "suffix": "assignmentRules", + "xmlName": "AssignmentRules" + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "AssignmentRules", + "xmlName": "AssignmentRule", + "xmlTag": "assignmentRule", + "key": "fullName" + }, + { + "childXmlNames": "AutoResponseRule", + "directoryName": "autoResponseRules", + "inFolder": false, + "metaFile": false, + "suffix": "autoResponseRules", + "xmlName": "AutoResponseRules" + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "AutoResponseRules", + "xmlName": "AutoResponseRule", + "xmlTag": "autoResponseRule", + "key": "fullName" + }, + { + "childXmlNames": "EscalationRule", + "directoryName": "escalationRules", + "inFolder": false, + "metaFile": false, + "suffix": "escalationRules", + "xmlName": "EscalationRules" + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "EscalationRules", + "xmlName": "EscalationRule", + "xmlTag": "escalationRule", + "key": "fullName" + }, + { + "childXmlNames": "MatchingRule", + "directoryName": "matchingRules", + "inFolder": false, + "metaFile": false, + "suffix": "matchingRule", + "xmlName": "MatchingRules" + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "MatchingRules", + "xmlName": "MatchingRule", + "xmlTag": "matchingRules", + "key": "fullName" + }, + { + "directoryName": "globalValueSetTranslations", + "inFolder": false, + "metaFile": false, + "suffix": "globalValueSetTranslation", + "xmlName": "GlobalValueSetTranslation", + "pruneOnly": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "GlobalValueSetTranslation", + "xmlName": "ValueTranslation", + "xmlTag": "valueTranslation", + "key": "masterLabel", + "excluded": true + }, + { + "directoryName": "standardValueSetTranslations", + "inFolder": false, + "metaFile": false, + "suffix": "standardValueSetTranslation", + "xmlName": "StandardValueSetTranslation", + "pruneOnly": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "StandardValueSetTranslation", + "xmlName": "ValueTranslation", + "xmlTag": "valueTranslation", + "key": "masterLabel", + "excluded": true + }, + { + "directoryName": "profiles", + "inFolder": false, + "metaFile": false, + "suffix": "profile", + "xmlName": "Profile", + "pruneOnly": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Profile", + "xmlName": "ProfileApplicationVisibility", + "xmlTag": "categoryGroupVisibilities", + "key": "dataCategoryGroup", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Profile", + "xmlName": "ProfileCategoryGroupVisibility", + "xmlTag": "applicationVisibilities", + "key": "application", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Profile", + "xmlName": "ProfileApexClassAccess", + "xmlTag": "classAccesses", + "key": "apexClass", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Profile", + "xmlName": "ProfileCustomMetadataTypeAccess[", + "xmlTag": "customMetadataTypeAccesses", + "key": "name", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Profile", + "xmlName": "ProfileCustomPermissions", + "xmlTag": "customPermissions", + "key": "name", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Profile", + "xmlName": "ProfileCustomSettingAccesses", + "xmlTag": "customSettingAccesses", + "key": "name", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Profile", + "xmlName": "ProfileExternalDataSourceAccess", + "xmlTag": "externalDataSourceAccesses", + "key": "externalDataSource", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Profile", + "xmlName": "ProfileFieldLevelSecurity", + "xmlTag": "fieldPermissions", + "key": "field", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Profile", + "xmlName": "ProfileFlowAccess", + "xmlTag": "flowAccesses", + "key": "flow", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Profile", + "xmlName": "LoginFlow", + "xmlTag": "loginFlows", + "key": "friendlyname", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Profile", + "xmlName": "ProfileObjectPermissions", + "xmlTag": "objectPermissions", + "key": "object", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Profile", + "xmlName": "ProfileApexPageAccess", + "xmlTag": "pageAccesses", + "key": "apexPage", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Profile", + "xmlName": "ProfileActionOverride", + "xmlTag": "profileActionOverrides", + "key": "actionName", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Profile", + "xmlName": "ProfileRecordTypeVisibility", + "xmlTag": "recordTypeVisibilities", + "key": "recordType", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Profile", + "xmlName": "ProfileTabVisibility", + "xmlTag": "tabVisibilities", + "key": "tab", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Profile", + "xmlName": "ProfileUserPermission", + "xmlTag": "userPermissions", + "key": "name", + "excluded": true + }, + { + "directoryName": "translations", + "inFolder": false, + "metaFile": false, + "suffix": "translation", + "xmlName": "Translations", + "pruneOnly": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Translations", + "xmlName": "BotTranslation", + "xmlTag": "bots", + "key": "fullName", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Translations", + "xmlName": "CustomApplicationTranslation", + "xmlTag": "customApplications", + "key": "name", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Translations", + "xmlName": "CustomLabelTranslation", + "xmlTag": "customLabels", + "key": "name", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Translations", + "xmlName": "CustomPageWebLinkTranslation", + "xmlTag": "customPageWebLinks", + "key": "name", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Translations", + "xmlName": "CustomTabTranslation", + "xmlTag": "customTabs", + "key": "name", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Translations", + "xmlName": "FlowDefinitionTranslation", + "xmlTag": "flowDefinitions", + "key": "fullName", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Translations", + "xmlName": "PipelineInspMetricConfigTranslation", + "xmlTag": "pipelineInspMetricConfigs", + "key": "name", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Translations", + "xmlName": "PromptTranslation", + "xmlTag": "prompts", + "key": "name", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Translations", + "xmlName": "GlobalQuickActionTranslation", + "xmlTag": "quickActions", + "key": "name", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Translations", + "xmlName": "ReportTypeTranslation", + "xmlTag": "reportTypes", + "key": "name", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "Translations", + "xmlName": "ScontrolTranslation", + "xmlTag": "scontrols", + "key": "name", + "excluded": true + }, + { + "directoryName": "objectTranslations", + "inFolder": false, + "metaFile": false, + "suffix": "objectTranslation", + "xmlName": "CustomObjectTranslation", + "pruneOnly": true + }, + { + "directoryName": "objectTranslations", + "inFolder": false, + "metaFile": false, + "parentXmlName": "CustomObjectTranslation", + "suffix": "fieldTranslation", + "xmlName": "CustomObjectTranslation", + "xmlTag": "fields", + "key": "name", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "CustomObjectTranslation", + "xmlName": "FieldSetTranslation", + "xmlTag": "fieldSets", + "key": "name", + "excluded": true + }, + { + + "inFolder": false, + "metaFile": false, + "parentXmlName": "CustomObjectTranslation", + "xmlName": "LayoutTranslation", + "xmlTag": "layouts", + "key": "layout", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "CustomObjectTranslation", + "xmlName": "NamedFilterTranslation", + "xmlTag": "namedFilters", + "key": "name", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "CustomObjectTranslation", + "xmlName": "QuickActionTranslation", + "xmlTag": "quickActions", + "key": "name", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "CustomObjectTranslation", + "xmlName": "RecordTypeTranslation", + "xmlTag": "recordTypes", + "key": "name", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "CustomObjectTranslation", + "xmlName": "SharingReasonTranslation", + "xmlTag": "sharingReasons", + "key": "name", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "CustomObjectTranslation", + "xmlName": "ValidationRuleTranslation", + "xmlTag": "validationRules", + "key": "name", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "CustomObjectTranslation", + "xmlName": "WebLinkTranslation", + "xmlTag": "webLinks", + "key": "name", + "excluded": true + }, + { + "inFolder": false, + "metaFile": false, + "parentXmlName": "CustomObjectTranslation", + "xmlName": "WorkflowTaskTranslation", + "xmlTag": "workflowTasks", + "key": "name", + "excluded": true + }, + { + "directoryName": "clauseCatgConfigurations", + "inFolder": false, + "metaFile": false, + "suffix": "clauseCatgConfiguration", + "xmlName": "ClauseCatgConfiguration" + }, + { + "directoryName": "disclosureDefinitions", + "inFolder": false, + "metaFile": false, + "suffix": "disclosureDefinition", + "xmlName": "DisclosureDefinition" + }, + { + "directoryName": "disclosureDefinitionVersions", + "inFolder": false, + "metaFile": false, + "suffix": "disclosureDefinitionVersion", + "xmlName": "DisclosureDefinitionVersion" + }, + { + "directoryName": "disclosureTypes", + "inFolder": false, + "metaFile": false, + "suffix": "disclosureType ", + "xmlName": "DisclosureType" + }, + { + "directoryName": "fuelTypes", + "inFolder": false, + "metaFile": false, + "suffix": "fuelType", + "xmlName": "FuelType" }, { - directoryName: "sharingRules.sharingOwnerRules", - inFolder: false, - metaFile: false, - parentXmlName: "SharingRules", - xmlName: "SharingOwnerRule", - xmlTag: "sharingOwnerRules", + "directoryName": "fuelTypeSustnUoms", + "inFolder": false, + "metaFile": false, + "suffix": "fuelTypeSustnUom", + "xmlName": "FuelTypeSustnUom" }, { - directoryName: "sharingRules.sharingTerritoryRules", - inFolder: false, - metaFile: false, - parentXmlName: "SharingRules", - xmlName: "SharingTerritoryRule", - xmlTag: "sharingTerritoryRules", - }, + "directoryName": "sustnUomConversions", + "inFolder": false, + "metaFile": false, + "suffix": "sustnUomConversion", + "xmlName": "SustnUomConversion" + }, { - directoryName: "workflows.tasks", - inFolder: false, - metaFile: false, - parentXmlName: "Workflow", - xmlName: "WorkflowTask", - xmlTag: "tasks", - }, + "directoryName": "sustainabilityUoms", + "inFolder": false, + "metaFile": false, + "suffix": "sustainabilityUom", + "xmlName": "SustainabilityUom" + } ]; } diff --git a/src/common/notifProvider/apiProvider.ts b/src/common/notifProvider/apiProvider.ts index b5efff492..df5324be9 100644 --- a/src/common/notifProvider/apiProvider.ts +++ b/src/common/notifProvider/apiProvider.ts @@ -1,24 +1,23 @@ -import { SfdxError } from "@salesforce/core"; -import * as c from "chalk"; -import { NotifProviderRoot } from "./notifProviderRoot"; -import { getCurrentGitBranch, getGitRepoName, uxLog } from "../utils"; -import { NotifMessage, NotifSeverity, UtilsNotifs } from "."; -import { getEnvVar } from "../../config"; +import { Connection, SfError } from "@salesforce/core"; +import c from "chalk"; +import { NotifProviderRoot } from "./notifProviderRoot.js"; +import { getCurrentGitBranch, getGitRepoName, uxLog } from "../utils/index.js"; +import { NotifMessage, NotifSeverity, UtilsNotifs } from "./index.js"; +import { CONSTANTS, getEnvVar } from "../../config/index.js"; -import { getSeverityIcon, removeMarkdown } from "../utils/notifUtils"; -import { Connection } from "jsforce"; -import { GitProvider } from "../gitProvider"; +import { getSeverityIcon, removeMarkdown } from "../utils/notifUtils.js"; +import { GitProvider } from "../gitProvider/index.js"; import axios, { AxiosRequestConfig } from "axios"; const MAX_LOKI_LOG_LENGTH = Number(process.env.MAX_LOKI_LOG_LENGTH || 200000); const TRUNCATE_LOKI_ELEMENTS_LENGTH = Number(process.env.TRUNCATE_LOKI_ELEMENTS_LENGTH || 500); export class ApiProvider extends NotifProviderRoot { - protected apiUrl: string; + protected apiUrl: string | null; public payload: ApiNotifMessage; public payloadFormatted: any; - protected metricsApiUrl: string; + protected metricsApiUrl: string | null; public metricsPayload: string; public getLabel(): string { @@ -40,7 +39,7 @@ export class ApiProvider extends NotifProviderRoot { const apiPromises: Promise[] = []; // Use Promises to optimize performances with api calls this.apiUrl = getEnvVar("NOTIF_API_URL"); if (this.apiUrl == null) { - throw new SfdxError("[ApiProvider] You need to define a variable NOTIF_API_URL to use sfdx-hardis Api notifications"); + throw new SfError("[ApiProvider] You need to define a variable NOTIF_API_URL to use sfdx-hardis Api notifications"); } // Build initial payload data from notifMessage await this.buildPayload(notifMessage); @@ -70,7 +69,7 @@ export class ApiProvider extends NotifProviderRoot { let logBodyText = UtilsNotifs.prefixWithSeverityEmoji(UtilsNotifs.slackToTeamsMarkdown(notifMessage.text), notifMessage.severity); // Add text details - if (notifMessage?.attachments?.length > 0) { + if (notifMessage?.attachments?.length && notifMessage?.attachments?.length > 0) { let text = "\n\n"; for (const attachment of notifMessage.attachments) { if (attachment.text) { @@ -83,7 +82,7 @@ export class ApiProvider extends NotifProviderRoot { } // Add action blocks - if (notifMessage.buttons?.length > 0) { + if (notifMessage.buttons?.length && notifMessage.buttons?.length > 0) { logBodyText += "Links:\n\n"; for (const button of notifMessage.buttons) { // Url button @@ -95,14 +94,14 @@ export class ApiProvider extends NotifProviderRoot { } // Add sfdx-hardis ref - logBodyText += "Powered by sfdx-hardis: https://sfdx-hardis.cloudity.com"; + logBodyText += `Powered by sfdx-hardis: ${CONSTANTS.DOC_URL_ROOT}`; logBodyText = removeMarkdown(logBodyText); // Build payload - const repoName = (await getGitRepoName()).replace(".git", ""); + const repoName = (await getGitRepoName() || "").replace(".git", ""); const currentGitBranch = await getCurrentGitBranch(); const conn: Connection = globalThis.jsForceConn; - const orgIdentifier = conn.instanceUrl.replace("https://", "").replace(".my.salesforce.com", "").replace(/\./gm, "__"); + const orgIdentifier = (conn.instanceUrl) ? conn.instanceUrl.replace("https://", "").replace(".my.salesforce.com", "").replace(/\./gm, "__") : currentGitBranch || "ERROR apiProvider"; const notifKey = orgIdentifier + "!!" + notifMessage.type; this.payload = { source: "sfdx-hardis", @@ -129,7 +128,7 @@ export class ApiProvider extends NotifProviderRoot { } private async formatPayload() { - if (this.apiUrl.includes("loki/api/v1/push")) { + if ((this.apiUrl || "").includes("loki/api/v1/push")) { await this.formatPayloadLoki(); return; } @@ -181,8 +180,8 @@ export class ApiProvider extends NotifProviderRoot { // Basic Auth if (getEnvVar("NOTIF_API_BASIC_AUTH_USERNAME") != null) { axiosConfig.auth = { - username: getEnvVar("NOTIF_API_BASIC_AUTH_USERNAME"), - password: getEnvVar("NOTIF_API_BASIC_AUTH_PASSWORD"), + username: getEnvVar("NOTIF_API_BASIC_AUTH_USERNAME") || "", + password: getEnvVar("NOTIF_API_BASIC_AUTH_PASSWORD") || "", }; } // Bearer token @@ -191,7 +190,7 @@ export class ApiProvider extends NotifProviderRoot { } // POST message try { - const axiosResponse = await axios.post(this.apiUrl, this.payloadFormatted, axiosConfig); + const axiosResponse = await axios.post(this.apiUrl || "", this.payloadFormatted, axiosConfig); const httpStatus = axiosResponse.status; if (httpStatus > 200 && httpStatus < 300) { uxLog(this, c.cyan(`[ApiProvider] Posted message to API ${this.apiUrl} (${httpStatus})`)); @@ -200,9 +199,9 @@ export class ApiProvider extends NotifProviderRoot { } } } catch (e) { - uxLog(this, c.yellow(`[ApiProvider] Error while sending message to API ${this.apiUrl}: ${e.message}`)); + uxLog(this, c.yellow(`[ApiProvider] Error while sending message to API ${this.apiUrl}: ${(e as Error).message}`)); uxLog(this, c.grey("Request body: \n" + JSON.stringify(this.payloadFormatted))); - uxLog(this, c.grey("Response body: \n" + JSON.stringify(e?.response?.data || {}))); + uxLog(this, c.grey("Response body: \n" + JSON.stringify((e as any)?.response?.data || {}))); } } @@ -215,7 +214,7 @@ export class ApiProvider extends NotifProviderRoot { `orgIdentifier=${this.payload.orgIdentifier},` + `gitIdentifier=${this.payload.gitIdentifier}`; // Add extra fields and value - const metricsPayloadLines = []; + const metricsPayloadLines: any[] = []; for (const metricId of Object.keys(this.payload.data._metrics)) { const metricData = this.payload.data._metrics[metricId]; let metricPayloadLine = metricId + "," + metricTags + " "; @@ -223,7 +222,7 @@ export class ApiProvider extends NotifProviderRoot { metricPayloadLine += "metric=" + metricData.toFixed(2); metricsPayloadLines.push(metricPayloadLine); } else if (typeof metricData === "object") { - const metricFields = []; + const metricFields: any[] = []; if (metricData.min) { metricFields.push("min=" + metricData.min.toFixed(2)); } @@ -251,8 +250,8 @@ export class ApiProvider extends NotifProviderRoot { // Basic Auth if (getEnvVar("NOTIF_API_METRICS_BASIC_AUTH_USERNAME") != null) { axiosConfig.auth = { - username: getEnvVar("NOTIF_API_METRICS_BASIC_AUTH_USERNAME"), - password: getEnvVar("NOTIF_API_METRICS_BASIC_AUTH_PASSWORD"), + username: getEnvVar("NOTIF_API_METRICS_BASIC_AUTH_USERNAME") || "", + password: getEnvVar("NOTIF_API_METRICS_BASIC_AUTH_PASSWORD") || "", }; } // Bearer token @@ -261,7 +260,7 @@ export class ApiProvider extends NotifProviderRoot { } // POST message try { - const axiosResponse = await axios.post(this.metricsApiUrl, this.metricsPayload, axiosConfig); + const axiosResponse = await axios.post(this.metricsApiUrl || "", this.metricsPayload, axiosConfig); const httpStatus = axiosResponse.status; if (httpStatus > 200 && httpStatus < 300) { uxLog(this, c.cyan(`[ApiMetricProvider] Posted message to API ${this.metricsApiUrl} (${httpStatus})`)); @@ -270,9 +269,9 @@ export class ApiProvider extends NotifProviderRoot { } } } catch (e) { - uxLog(this, c.yellow(`[ApiMetricProvider] Error while sending message to API ${this.metricsApiUrl}: ${e.message}`)); + uxLog(this, c.yellow(`[ApiMetricProvider] Error while sending message to API ${this.metricsApiUrl}: ${(e as Error).message}`)); uxLog(this, c.grey("Request body: \n" + JSON.stringify(this.metricsPayload))); - uxLog(this, c.grey("Response body: \n" + JSON.stringify(e?.response?.data || {}))); + uxLog(this, c.grey("Response body: \n" + JSON.stringify((e as any)?.response?.data || {}))); } } } diff --git a/src/common/notifProvider/emailProvider.ts b/src/common/notifProvider/emailProvider.ts index acc2703ad..8227460b9 100644 --- a/src/common/notifProvider/emailProvider.ts +++ b/src/common/notifProvider/emailProvider.ts @@ -1,13 +1,13 @@ -import { SfdxError } from "@salesforce/core"; -import * as DOMPurify from "isomorphic-dompurify"; -import * as c from "chalk"; -import { NotifProviderRoot } from "./notifProviderRoot"; -import { getCurrentGitBranch, uxLog } from "../utils"; -import { NotifMessage, UtilsNotifs } from "."; -import { getEnvVar } from "../../config"; +import { SfError } from "@salesforce/core"; +import DOMPurify from "isomorphic-dompurify"; +import c from "chalk"; +import { NotifProviderRoot } from "./notifProviderRoot.js"; +import { getCurrentGitBranch, uxLog } from "../utils/index.js"; +import { NotifMessage, UtilsNotifs } from "./index.js"; +import { CONSTANTS, getEnvVar } from "../../config/index.js"; import { marked } from "marked"; -import { EmailMessage, sendEmail } from "../utils/emailUtils"; -import { removeMarkdown } from "../utils/notifUtils"; +import { EmailMessage, sendEmail } from "../utils/emailUtils.js"; +import { removeMarkdown } from "../utils/notifUtils.js"; export class EmailProvider extends NotifProviderRoot { public getLabel(): string { @@ -18,13 +18,13 @@ export class EmailProvider extends NotifProviderRoot { public async postNotification(notifMessage: NotifMessage): Promise { const mainEmailAddress = getEnvVar("NOTIF_EMAIL_ADDRESS"); if (mainEmailAddress == null) { - throw new SfdxError("[EmailProvider] You need to define a variable NOTIF_EMAIL_ADDRESS to use sfdx-hardis Email notifications"); + throw new SfError("[EmailProvider] You need to define a variable NOTIF_EMAIL_ADDRESS to use sfdx-hardis Email notifications"); } const emailAddresses = mainEmailAddress.split(","); // Add branch custom Teams channel if defined - const customEmailChannelVariable = `NOTIF_EMAIL_ADDRESS_${(await getCurrentGitBranch()).toUpperCase()}`; + const customEmailChannelVariable = `NOTIF_EMAIL_ADDRESS_${(await getCurrentGitBranch() || "").toUpperCase()}`; if (getEnvVar(customEmailChannelVariable)) { - emailAddresses.push(...getEnvVar(customEmailChannelVariable).split(",")); + emailAddresses.push(...(getEnvVar(customEmailChannelVariable) || "").split(",")); } /* jscpd:ignore-start */ @@ -37,7 +37,7 @@ export class EmailProvider extends NotifProviderRoot { let emailBody = UtilsNotifs.prefixWithSeverityEmoji(UtilsNotifs.slackToTeamsMarkdown(notifMessage.text), notifMessage.severity); // Add text details - if (notifMessage?.attachments?.length > 0) { + if (notifMessage?.attachments?.length && notifMessage?.attachments?.length > 0) { let text = "\n\n"; for (const attachment of notifMessage.attachments) { if (attachment.text) { @@ -51,7 +51,7 @@ export class EmailProvider extends NotifProviderRoot { /* jscpd:ignore-end */ // Add action blocks - if (notifMessage.buttons?.length > 0) { + if (notifMessage.buttons?.length && notifMessage.buttons?.length > 0) { emailBody += "**Links:**\n\n"; for (const button of notifMessage.buttons) { // Url button @@ -63,7 +63,7 @@ export class EmailProvider extends NotifProviderRoot { } // Add sfdx-hardis ref - emailBody += "_Powered by [sfdx-hardis](https://sfdx-hardis.cloudity.com)_"; + emailBody += `_Powered by [sfdx-hardis](${CONSTANTS.DOC_URL_ROOT})_`; // Send email const emailBodyHtml1 = marked.parse(emailBody); @@ -76,11 +76,11 @@ export class EmailProvider extends NotifProviderRoot { attachments: notifMessage?.attachedFiles || [], }; const emailRes = await sendEmail(emailMessage); - if (emailRes.success) { + if (emailRes?.success) { uxLog(this, c.cyan(`[EmailProvider] Sent email to ${emailAddresses.join(",")}`)); } else { uxLog(this, c.yellow(`[EmailProvider] Error while sending email to ${emailAddresses.join(",")}`)); - uxLog(this, c.grey(JSON.stringify(emailRes.detail, null, 2))); + uxLog(this, c.grey(JSON.stringify(emailRes?.detail, null, 2))); } return; } diff --git a/src/common/notifProvider/index.ts b/src/common/notifProvider/index.ts index 59b4c88e7..4fc883433 100644 --- a/src/common/notifProvider/index.ts +++ b/src/common/notifProvider/index.ts @@ -1,12 +1,12 @@ -import { uxLog } from "../utils"; -import * as c from "chalk"; -import { NotifProviderRoot } from "./notifProviderRoot"; -import { SlackProvider } from "./slackProvider"; -import { UtilsNotifs as utilsNotifs } from "./utils"; -import { TeamsProvider } from "./teamsProvider"; -import { getConfig } from "../../config"; -import { EmailProvider } from "./emailProvider"; -import { ApiProvider } from "./apiProvider"; +import { uxLog } from "../utils/index.js"; +import c from "chalk"; +import { NotifProviderRoot } from "./notifProviderRoot.js"; +import { SlackProvider } from "./slackProvider.js"; +import { UtilsNotifs as utilsNotifs } from "./utils.js"; +import { TeamsProvider } from "./teamsProvider.js"; +import { CONSTANTS, getConfig } from "../../config/index.js"; +import { EmailProvider } from "./emailProvider.js"; +import { ApiProvider } from "./apiProvider.js"; export abstract class NotifProvider { static getInstances(): NotifProviderRoot[] { @@ -42,7 +42,7 @@ export abstract class NotifProvider { uxLog( this, c.gray( - `[NotifProvider] No notif has been configured: https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-integrations-home/#message-notifications`, + `[NotifProvider] No notif has been configured: ${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-integrations-home/#message-notifications`, ), ); } @@ -82,21 +82,22 @@ export type NotifSeverity = "critical" | "error" | "warning" | "info" | "success export interface NotifMessage { text: string; type: - | "ACTIVE_USERS" - | "AUDIT_TRAIL" - | "APEX_TESTS" - | "BACKUP" - | "DEPLOYMENT" - | "LEGACY_API" - | "LICENSES" - | "LINT_ACCESS" - | "UNUSED_METADATAS" - | "METADATA_STATUS" - | "MISSING_ATTRIBUTES" - | "UNUSED_LICENSES" - | "UNUSED_USERS" - | "ORG_INFO" - | "ORG_LIMITS"; + | "ACTIVE_USERS" + | "AUDIT_TRAIL" + | "APEX_TESTS" + | "BACKUP" + | "DEPLOYMENT" + | "LEGACY_API" + | "LICENSES" + | "LINT_ACCESS" + | "UNUSED_METADATAS" + | "METADATA_STATUS" + | "MISSING_ATTRIBUTES" + | "UNUSED_LICENSES" + | "UNUSED_USERS" + | "ORG_INFO" + | "ORG_LIMITS" + | "RELEASE_UPDATES"; buttons?: NotifButton[]; attachments?: any[]; severity: NotifSeverity; diff --git a/src/common/notifProvider/notifProviderRoot.ts b/src/common/notifProvider/notifProviderRoot.ts index 44cc84451..459ad52b2 100644 --- a/src/common/notifProvider/notifProviderRoot.ts +++ b/src/common/notifProvider/notifProviderRoot.ts @@ -1,12 +1,12 @@ -import { SfdxError } from "@salesforce/core"; -import { uxLog } from "../utils"; -import { NotifMessage } from "."; +import { SfError } from "@salesforce/core"; +import { uxLog } from "../utils/index.js"; +import { NotifMessage } from "./index.js"; export abstract class NotifProviderRoot { protected token: string; public getLabel(): string { - throw new SfdxError("getLabel should be implemented on this call"); + throw new SfError("getLabel should be implemented on this call"); } // By default, we don't send logs to other notif targets than API to avoid noise diff --git a/src/common/notifProvider/slackProvider.ts b/src/common/notifProvider/slackProvider.ts index 0fb040ab1..554bcb798 100644 --- a/src/common/notifProvider/slackProvider.ts +++ b/src/common/notifProvider/slackProvider.ts @@ -1,17 +1,17 @@ -import { SfdxError } from "@salesforce/core"; -import * as c from "chalk"; -import { NotifProviderRoot } from "./notifProviderRoot"; +import { SfError } from "@salesforce/core"; +import c from "chalk"; +import { NotifProviderRoot } from "./notifProviderRoot.js"; import { ActionsBlock, Block, Button, SectionBlock, WebClient } from "@slack/web-api"; -import { getCurrentGitBranch, uxLog } from "../utils"; -import { NotifMessage, UtilsNotifs } from "."; -import { getEnvVar } from "../../config"; +import { getCurrentGitBranch, uxLog } from "../utils/index.js"; +import { NotifMessage, UtilsNotifs } from "./index.js"; +import { getEnvVar } from "../../config/index.js"; export class SlackProvider extends NotifProviderRoot { private slackClient: InstanceType; constructor() { super(); - this.token = process.env.SLACK_TOKEN; + this.token = process.env.SLACK_TOKEN || ""; this.slackClient = new WebClient(this.token); } @@ -23,15 +23,15 @@ export class SlackProvider extends NotifProviderRoot { public async postNotification(notifMessage: NotifMessage): Promise { const mainNotifsChannelId = getEnvVar("SLACK_CHANNEL_ID"); if (mainNotifsChannelId == null) { - throw new SfdxError( + throw new SfError( "[SlackProvider] You need to define a variable SLACK_CHANNEL_ID to use sfdx-hardis Slack Integration. Otherwise, remove variable SLACK_TOKEN", ); } const slackChannelsIds = mainNotifsChannelId.split(","); // Add branch custom slack channel if defined - const customSlackChannelVariable = `SLACK_CHANNEL_ID_${(await getCurrentGitBranch()).toUpperCase()}`; + const customSlackChannelVariable = `SLACK_CHANNEL_ID_${(await getCurrentGitBranch() || "").toUpperCase()}`; if (getEnvVar(customSlackChannelVariable)) { - slackChannelsIds.push(...getEnvVar(customSlackChannelVariable).split(",")); + slackChannelsIds.push(...(getEnvVar(customSlackChannelVariable) || "").split(",")); } // Handle specific channel for Warnings and errors const warningsErrorsChannelId = getEnvVar("SLACK_CHANNEL_ID_ERRORS_WARNINGS"); @@ -58,8 +58,8 @@ export class SlackProvider extends NotifProviderRoot { } */ blocks.push(block); // Add action blocks - if (notifMessage.buttons?.length > 0) { - const actionElements = []; + if (notifMessage.buttons?.length && notifMessage.buttons?.length > 0) { + const actionElements: any[] = []; for (const button of notifMessage.buttons) { // Url button if (button.url) { @@ -96,7 +96,7 @@ export class SlackProvider extends NotifProviderRoot { uxLog(this, c.cyan(`[SlackProvider] Sent slack notification to channel ${mainNotifsChannelId}: ${resp.ok}`)); } catch (error) { uxLog(this, c.gray("[SlackProvider] Failed slack message content: \n" + JSON.stringify(slackMessage, null, 2))); - uxLog(this, c.red(`[SlackProvider] Error while sending message to channel ${mainNotifsChannelId}\n${error.message}`)); + uxLog(this, c.red(`[SlackProvider] Error while sending message to channel ${mainNotifsChannelId}\n${(error as any).message}`)); } } return; diff --git a/src/common/notifProvider/teamsProvider.ts b/src/common/notifProvider/teamsProvider.ts index e73925fc6..51505cbb0 100644 --- a/src/common/notifProvider/teamsProvider.ts +++ b/src/common/notifProvider/teamsProvider.ts @@ -1,7 +1,8 @@ -import * as c from "chalk"; -import { NotifProviderRoot } from "./notifProviderRoot"; -import { uxLog } from "../utils"; -import { NotifMessage } from "."; +import c from "chalk"; +import { NotifProviderRoot } from "./notifProviderRoot.js"; +import { uxLog } from "../utils/index.js"; +import { NotifMessage } from "./index.js"; +import { CONSTANTS } from "../../config/index.js"; export class TeamsProvider extends NotifProviderRoot { public getLabel(): string { @@ -14,6 +15,6 @@ export class TeamsProvider extends NotifProviderRoot { this, c.bold(c.yellow(`[TeamsProvider] MsTeams Web Hooks will be soon deprecated. Instead, please use EmailProvider with Ms Teams Channel e-mail`)), ); - uxLog(this, c.bold(c.yellow(`[TeamsProvider] User Guide: https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-integration-email/`))); + uxLog(this, c.bold(c.yellow(`[TeamsProvider] User Guide: ${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-integration-email/`))); } } diff --git a/src/common/notifProvider/utils.ts b/src/common/notifProvider/utils.ts index c300fcb83..6757c9f31 100644 --- a/src/common/notifProvider/utils.ts +++ b/src/common/notifProvider/utils.ts @@ -1,5 +1,5 @@ -import { NotifSeverity } from "."; -import { getEnvVar } from "../../config"; +import { NotifSeverity } from "./index.js"; +import { getEnvVar } from "../../config/index.js"; export class UtilsNotifs { public static isSlackAvailable() { @@ -44,14 +44,14 @@ export class UtilsNotifs { } public static prefixWithSeverityEmoji(text: string, severity: NotifSeverity | null) { - const emojis = { + const emojis: any = { critical: "💥", error: "❌", warning: "⚠️", info: "ℹ️", success: "✅", }; - const emoji = emojis[severity] || emojis["info"]; + const emoji = emojis[severity || ""] || emojis["info"]; return `${emoji} ${text}`; } diff --git a/src/common/ticketProvider/azureBoardsProvider.ts b/src/common/ticketProvider/azureBoardsProvider.ts index 9983dd102..7c044638c 100644 --- a/src/common/ticketProvider/azureBoardsProvider.ts +++ b/src/common/ticketProvider/azureBoardsProvider.ts @@ -1,21 +1,21 @@ /* jscpd:ignore-start */ import * as azdev from "azure-devops-node-api"; -import { TicketProviderRoot } from "./ticketProviderRoot"; -import * as c from "chalk"; -import * as sortArray from "sort-array"; -import { Ticket } from "."; -import { getBranchMarkdown, getOrgMarkdown } from "../utils/notifUtils"; -import { extractRegexMatches, uxLog } from "../utils"; -import { SfdxError } from "@salesforce/core"; -import { GitCommitRef } from "azure-devops-node-api/interfaces/GitInterfaces"; -import { JsonPatchDocument } from "azure-devops-node-api/interfaces/common/VSSInterfaces"; -import { getEnvVar } from "../../config"; +import { TicketProviderRoot } from "./ticketProviderRoot.js"; +import c from "chalk"; +import sortArray from "sort-array"; +import { Ticket } from "./index.js"; +import { getBranchMarkdown, getOrgMarkdown } from "../utils/notifUtils.js"; +import { extractRegexMatches, uxLog } from "../utils/index.js"; +import { SfError } from "@salesforce/core"; +import { getEnvVar } from "../../config/index.js"; +import { GitCommitRef } from "azure-devops-node-api/interfaces/GitInterfaces.js"; +import { JsonPatchDocument } from "azure-devops-node-api/interfaces/common/VSSInterfaces.js"; /* jscpd:ignore-end */ export class AzureBoardsProvider extends TicketProviderRoot { - protected serverUrl: string; + protected serverUrl: string | null; protected azureApi: InstanceType; - protected teamProject: string; + protected teamProject: string | null; constructor() { super(); @@ -28,8 +28,8 @@ export class AzureBoardsProvider extends TicketProviderRoot { this.isActive = true; } if (this.isActive) { - const authHandler = azdev.getHandlerFromToken(this.token); - this.azureApi = new azdev.WebApi(this.serverUrl, authHandler); + const authHandler = azdev.getHandlerFromToken(this.token || ""); + this.azureApi = new azdev.WebApi(this.serverUrl || "", authHandler); } } @@ -76,7 +76,7 @@ export class AzureBoardsProvider extends TicketProviderRoot { const azureBoardsProvider = new AzureBoardsProvider(); const azureApi = azureBoardsProvider.azureApi; const azureGitApi = await azureApi.getGitApi(); - const repositoryId = getEnvVar("BUILD_REPOSITORY_ID"); + const repositoryId = getEnvVar("BUILD_REPOSITORY_ID") || ""; const commitIds = options.commits.filter((commit) => commit.hash).map((commit) => commit.hash); const azureCommits: GitCommitRef[] = []; for (const commitId of commitIds) { @@ -88,8 +88,8 @@ export class AzureBoardsProvider extends TicketProviderRoot { if (!tickets.some((ticket) => ticket.id === workItem.id)) { tickets.push({ provider: "AZURE", - url: workItem.url, - id: workItem.id, + url: workItem.url || "", + id: workItem.id || "", }); } } @@ -120,12 +120,12 @@ export class AzureBoardsProvider extends TicketProviderRoot { this, c.cyan( `[AzureBoardsProvider] Now trying to collect ${azureTicketsNumber} tickets infos from Azure Boards Server ` + - process.env.SYSTEM_COLLECTIONURI + - " ...", + process.env.SYSTEM_COLLECTIONURI + + " ...", ), ); } - const azureWorkItemApi = await this.azureApi.getWorkItemTrackingApi(this.serverUrl); + const azureWorkItemApi = await this.azureApi.getWorkItemTrackingApi(this.serverUrl || ""); for (const ticket of tickets) { if (ticket.provider === "AZURE") { const ticketInfo = await azureWorkItemApi.getWorkItem(Number(ticket.id)); @@ -153,7 +153,7 @@ export class AzureBoardsProvider extends TicketProviderRoot { const tag = await this.getDeploymentTag(); const commentedTickets: Ticket[] = []; const taggedTickets: Ticket[] = []; - const azureWorkItemApi = await this.azureApi.getWorkItemTrackingApi(this.serverUrl); + const azureWorkItemApi = await this.azureApi.getWorkItemTrackingApi(this.serverUrl || ""); for (const ticket of tickets) { if (ticket.foundOnServer) { let azureBoardsComment = `Deployed from branch ${branchMarkdown} to org ${orgMarkdown}`; @@ -167,14 +167,14 @@ export class AzureBoardsProvider extends TicketProviderRoot { // Post comment try { - const commentPostRes = await azureWorkItemApi.addComment({ text: azureBoardsComment }, this.teamProject, Number(ticket.id)); - if (commentPostRes && commentPostRes?.id > 0) { + const commentPostRes = await azureWorkItemApi.addComment({ text: azureBoardsComment }, this.teamProject || "", Number(ticket.id)); + if (commentPostRes?.id && commentPostRes?.id > 0) { commentedTickets.push(ticket); } else { - throw new SfdxError("commentPostRes: " + commentPostRes); + throw new SfError("commentPostRes: " + commentPostRes); } } catch (e6) { - uxLog(this, c.yellow(`[AzureBoardsProvider] Error while posting comment on ${ticket.id}\n${e6.message}\n${c.grey(e6.stack)}`)); + uxLog(this, c.yellow(`[AzureBoardsProvider] Error while posting comment on ${ticket.id}\n${(e6 as any).message}\n${c.grey((e6 as any).stack)}`)); } // Add tag @@ -186,14 +186,14 @@ export class AzureBoardsProvider extends TicketProviderRoot { value: tag, }, ]; - const workItem = await azureWorkItemApi.updateWorkItem({}, patchDocument, Number(ticket.id), this.teamProject); - if (workItem && workItem?.id > 0) { + const workItem = await azureWorkItemApi.updateWorkItem({}, patchDocument, Number(ticket.id), this.teamProject || ""); + if (workItem?.id && workItem?.id > 0) { taggedTickets.push(ticket); } else { - throw new SfdxError("tag workItem: " + workItem); + throw new SfError("tag workItem: " + workItem); } } catch (e6) { - uxLog(this, c.yellow(`[AzureBoardsProvider] Error while adding tag ${tag} on ${ticket.id}\n${e6.message}\n${c.grey(e6.stack)}`)); + uxLog(this, c.yellow(`[AzureBoardsProvider] Error while adding tag ${tag} on ${ticket.id}\n${(e6 as any).message} \n${c.grey((e6 as any).stack)} `)); } } } diff --git a/src/common/ticketProvider/genericProvider.ts b/src/common/ticketProvider/genericProvider.ts index 9d2be154f..cf7d31cdd 100644 --- a/src/common/ticketProvider/genericProvider.ts +++ b/src/common/ticketProvider/genericProvider.ts @@ -1,12 +1,12 @@ -import { Ticket } from "."; -import * as sortArray from "sort-array"; -import { extractRegexMatches } from "../utils"; -import { TicketProviderRoot } from "./ticketProviderRoot"; -import { getEnvVar } from "../../config"; +import { Ticket } from "./index.js"; +import sortArray from "sort-array"; +import { extractRegexMatches } from "../utils/index.js"; +import { TicketProviderRoot } from "./ticketProviderRoot.js"; +import { getEnvVar } from "../../config/index.js"; export class GenericTicketingProvider extends TicketProviderRoot { - private ticketRefRegex: string; - private ticketUrlBuilder: string; + private ticketRefRegex: string | null; + private ticketUrlBuilder: string | null; constructor() { super(); @@ -28,9 +28,9 @@ export class GenericTicketingProvider extends TicketProviderRoot { return tickets; } // Extract tickets using GENERIC_TICKETING_PROVIDER_REGEX regexp - const ticketRefRegexExec = new RegExp(getEnvVar("GENERIC_TICKETING_PROVIDER_REGEX"), "g"); + const ticketRefRegexExec = new RegExp(getEnvVar("GENERIC_TICKETING_PROVIDER_REGEX") || "", "g"); const regexMatches = await extractRegexMatches(ticketRefRegexExec, text); - const ticketUrlBuilder = getEnvVar("GENERIC_TICKETING_PROVIDER_URL_BUILDER"); + const ticketUrlBuilder = getEnvVar("GENERIC_TICKETING_PROVIDER_URL_BUILDER") || ""; for (const genericTicketRef of regexMatches) { const genericTicketUrl = ticketUrlBuilder.replace("{REF}", genericTicketRef); if (!tickets.some((ticket) => ticket.url === genericTicketUrl)) { diff --git a/src/common/ticketProvider/index.ts b/src/common/ticketProvider/index.ts index 23e38b212..178652254 100644 --- a/src/common/ticketProvider/index.ts +++ b/src/common/ticketProvider/index.ts @@ -1,10 +1,10 @@ -import * as c from "chalk"; -import * as sortArray from "sort-array"; -import { JiraProvider } from "./jiraProvider"; -import { TicketProviderRoot } from "./ticketProviderRoot"; -import { uxLog } from "../utils"; -import { GenericTicketingProvider } from "./genericProvider"; -import { AzureBoardsProvider } from "./azureBoardsProvider"; +import c from "chalk"; +import sortArray from "sort-array"; +import { JiraProvider } from "./jiraProvider.js"; +import { TicketProviderRoot } from "./ticketProviderRoot.js"; +import { uxLog } from "../utils/index.js"; +import { GenericTicketingProvider } from "./genericProvider.js"; +import { AzureBoardsProvider } from "./azureBoardsProvider.js"; export const allTicketProviders = [JiraProvider, GenericTicketingProvider, AzureBoardsProvider]; diff --git a/src/common/ticketProvider/jiraProvider.ts b/src/common/ticketProvider/jiraProvider.ts index a5aeb9242..1b83aea6f 100644 --- a/src/common/ticketProvider/jiraProvider.ts +++ b/src/common/ticketProvider/jiraProvider.ts @@ -1,15 +1,15 @@ -import * as JiraApi from "jira-client"; -import { TicketProviderRoot } from "./ticketProviderRoot"; -import * as c from "chalk"; -import * as sortArray from "sort-array"; -import { Ticket } from "."; -import { getBranchMarkdown, getOrgMarkdown } from "../utils/notifUtils"; -import { extractRegexMatches, uxLog } from "../utils"; -import { SfdxError } from "@salesforce/core"; -import { getEnvVar } from "../../config"; +import JiraApi from "jira-client"; +import { TicketProviderRoot } from "./ticketProviderRoot.js"; +import c from "chalk"; +import sortArray from "sort-array"; +import { Ticket } from "./index.js"; +import { getBranchMarkdown, getOrgMarkdown } from "../utils/notifUtils.js"; +import { extractRegexMatches, uxLog } from "../utils/index.js"; +import { SfError } from "@salesforce/core"; +import { getEnvVar } from "../../config/index.js"; export class JiraProvider extends TicketProviderRoot { - private jiraClient: InstanceType; + private jiraClient: InstanceType | any = null; constructor() { super(); @@ -21,13 +21,13 @@ export class JiraProvider extends TicketProviderRoot { }; // Basic Auth if (getEnvVar("JIRA_EMAIL") && getEnvVar("JIRA_TOKEN")) { - jiraOptions.username = getEnvVar("JIRA_EMAIL"); - jiraOptions.password = getEnvVar("JIRA_TOKEN"); + jiraOptions.username = getEnvVar("JIRA_EMAIL") || ""; + jiraOptions.password = getEnvVar("JIRA_TOKEN") || ""; this.isActive = true; } // Personal access token if (getEnvVar("JIRA_PAT")) { - jiraOptions.bearer = getEnvVar("JIRA_PAT"); + jiraOptions.bearer = getEnvVar("JIRA_PAT") || ""; this.isActive = true; } if (this.isActive) { @@ -109,11 +109,11 @@ export class JiraProvider extends TicketProviderRoot { } for (const ticket of tickets) { if (ticket.provider === "JIRA") { - let ticketInfo: JiraApi.JsonResponse; + let ticketInfo: JiraApi.JsonResponse | null = null; try { ticketInfo = await this.jiraClient.getIssue(ticket.id); } catch (e) { - uxLog(this, c.yellow(`[JiraApi] Error while trying to get ${ticket.id} information: ${e.message}`)); + uxLog(this, c.yellow(`[JiraApi] Error while trying to get ${ticket.id} information: ${(e as Error).message}`)); } if (ticketInfo) { const body = @@ -178,11 +178,11 @@ export class JiraProvider extends TicketProviderRoot { try { const commentPostRes = await this.jiraClient.addCommentAdvanced(ticket.id, { body: jiraComment }); if (JSON.stringify(commentPostRes).includes("")) { - throw new SfdxError(genericHtmlResponseError); + throw new SfError(genericHtmlResponseError); } commentedTickets.push(ticket); } catch (e6) { - uxLog(this, c.yellow(`[JiraProvider] Error while posting comment on ${ticket.id}: ${e6.message}`)); + uxLog(this, c.yellow(`[JiraProvider] Error while posting comment on ${ticket.id}: ${(e6 as any).message}`)); } // Add deployment label to JIRA ticket @@ -195,10 +195,10 @@ export class JiraProvider extends TicketProviderRoot { await this.jiraClient.updateIssue(ticket.id, issueUpdate); taggedTickets.push(ticket); } catch (e6) { - if (e6.message != null && e6.message.includes("")) { - e6.message = genericHtmlResponseError; + if ((e6 as any).message != null && (e6 as any).message.includes("")) { + (e6 as any).message = genericHtmlResponseError; } - uxLog(this, c.yellow(`[JiraProvider] Error while adding label ${tag} on ${ticket.id}: ${e6.message}`)); + uxLog(this, c.yellow(`[JiraProvider] Error while adding label ${tag} on ${ticket.id}: ${(e6 as any).message}`)); } } } @@ -243,7 +243,7 @@ export class JiraProvider extends TicketProviderRoot { { type: "link", attrs: { - href: "https://sfdx-hardis.cloudity.com/", + href: "${CONSTANTS.DOC_URL_ROOT}/", }, }, ], diff --git a/src/common/ticketProvider/ticketProviderRoot.ts b/src/common/ticketProvider/ticketProviderRoot.ts index 977b3e6cf..fa1a1e033 100644 --- a/src/common/ticketProvider/ticketProviderRoot.ts +++ b/src/common/ticketProvider/ticketProviderRoot.ts @@ -1,14 +1,14 @@ -import { SfdxError } from "@salesforce/core"; -import * as c from "chalk"; -import { Ticket } from "."; -import { getCurrentGitBranch, uxLog } from "../utils"; +import { SfError } from "@salesforce/core"; +import c from "chalk"; +import { Ticket } from "./index.js"; +import { getCurrentGitBranch, uxLog } from "../utils/index.js"; export abstract class TicketProviderRoot { public isActive = false; - protected token: string; + protected token: string | null; public getLabel(): string { - throw new SfdxError("getLabel should be implemented on this call"); + throw new SfError("getLabel should be implemented on this call"); } public async collectTicketsInfo(tickets: Ticket[]) { @@ -23,7 +23,7 @@ export abstract class TicketProviderRoot { } public async getDeploymentTag(): Promise { - const currentGitBranch = await getCurrentGitBranch(); + const currentGitBranch = await getCurrentGitBranch() || ""; let tag = currentGitBranch.toUpperCase() + "_DEPLOYED"; if (process.env?.DEPLOYED_TAG_TEMPLATE && !(process.env?.DEPLOYED_TAG_TEMPLATE || "").includes("$(")) { tag = process.env?.DEPLOYED_TAG_TEMPLATE.replace("{BRANCH}", currentGitBranch.toUpperCase()); diff --git a/src/common/utils/apiUtils.ts b/src/common/utils/apiUtils.ts index 0f8f6f353..626fd0b02 100644 --- a/src/common/utils/apiUtils.ts +++ b/src/common/utils/apiUtils.ts @@ -1,155 +1,166 @@ -import { uxLog } from "."; -import * as c from "chalk"; -import { Connection, SfdxError } from "@salesforce/core"; -import { RestApiOptions, RecordResult } from "jsforce"; -import * as ora from "ora"; +import { execSfdxJson, uxLog } from './index.js'; +import c from 'chalk'; +import { Connection } from '@salesforce/core'; +import ora, { Ora } from 'ora'; // Perform simple SOQL query (max results: 10000) export function soqlQuery(soqlQuery: string, conn: Connection): Promise { - uxLog(this, c.grey("SOQL REST: " + c.italic(soqlQuery.length > 500 ? soqlQuery.substr(0, 500) + "..." : soqlQuery) + " on " + conn.instanceUrl)); - return conn.query(soqlQuery); + uxLog( + this, + c.grey( + 'SOQL REST: ' + + c.italic(soqlQuery.length > 500 ? soqlQuery.substr(0, 500) + '...' : soqlQuery) + + ' on ' + + conn.instanceUrl + ) + ); + return Promise.resolve(conn.query(soqlQuery)); } // Perform simple SOQL query with Tooling API export function soqlQueryTooling(soqlQuery: string, conn: Connection): Promise { uxLog( this, - c.grey("SOQL REST Tooling: " + c.italic(soqlQuery.length > 500 ? soqlQuery.substr(0, 500) + "..." : soqlQuery) + " on " + conn.instanceUrl), + c.grey( + 'SOQL REST Tooling: ' + + c.italic(soqlQuery.length > 500 ? soqlQuery.substr(0, 500) + '...' : soqlQuery) + + ' on ' + + conn.instanceUrl + ) ); - return conn.tooling.query(soqlQuery); + return Promise.resolve(conn.tooling.query(soqlQuery)); } let spinnerQ; const maxRetry = Number(process.env.BULK_QUERY_RETRY || 5); // Same than soqlQuery but using bulk. Do not use if there will be too many results for javascript to handle in memory export async function bulkQuery(soqlQuery: string, conn: Connection, retries = 3): Promise { - uxLog(this, c.grey("SOQL BULK: " + c.italic(soqlQuery.length > 500 ? soqlQuery.substr(0, 500) + "..." : soqlQuery))); + const queryLabel = soqlQuery.length > 500 ? soqlQuery.substr(0, 500) + '...' : soqlQuery; + uxLog(this, c.grey('[BulkApiV2] ' + c.italic(queryLabel))); conn.bulk.pollInterval = 5000; // 5 sec conn.bulk.pollTimeout = 60000; // 60 sec - const records = []; - return new Promise((resolve, reject) => { - spinnerQ = ora({ text: `Bulk query...`, spinner: "moon" }).start(); - const job = conn.bulk.query(soqlQuery); - job - .on("record", async (record) => { - records.push(record); - }) - .on("error", async (err) => { - spinnerQ.fail(`Bulk query error.`); - uxLog(this, c.yellow("Bulk query error: " + err)); - // In case of timeout, retry if max retry is not reached - if ((err + "").includes("ETIMEDOUT") && retries < maxRetry) { - uxLog(this, c.yellow("Bulk query retry attempt #" + retries + 1)); - bulkQuery(soqlQuery, conn, retries + 1) - .then((resRetry) => { - resolve(resRetry); - }) - .catch((resErr) => { - reject(resErr); - }); - } else { - // If max retry attempts reached, give up - uxLog(this, c.red("Bulk query error: max retry attempts reached, or not timeout error.")); - globalThis.sfdxHardisFatalError = true; - reject(err); - } - }) - .on("end", () => { - spinnerQ.succeed(`Bulk query completed with ${records.length} results.`); - resolve({ records: records, totalSize: records.length }); - }); - }); + // Start query + try { + spinnerQ = ora({ text: `[BulkApiV2] Bulk Query: ${queryLabel}`, spinner: 'moon' }).start(); + const recordStream = await conn.bulk2.query(soqlQuery); + recordStream.on('error', (err) => { + uxLog(this, c.yellow('Bulk Query error: ' + err)); + globalThis.sfdxHardisFatalError = true; + }); + // Wait for all results + const records = await recordStream.toArray(); + spinnerQ.succeed(`[BulkApiV2] Bulk Query completed with ${records.length} results.`); + return { records: records }; + } catch (e: any) { + spinnerQ.fail(`[BulkApiV2] Bulk query error: ${e.message}`); + // Try again if the reason is a timeout and max number of retries is not reached yet + if ((e + '').includes('ETIMEDOUT') && retries < maxRetry) { + uxLog(this, c.yellow('[BulkApiV2] Bulk Query retry attempt #' + retries + 1)); + return await bulkQuery(soqlQuery, conn, retries + 1); + } else { + throw e; + } + } } // When you might have more than 1000 elements in a IN condition, you need to split the request into several requests // Think to use {{IN}} in soqlQuery -export async function bulkQueryChunksIn(soqlQuery: string, conn: Connection, inElements: string[], batchSize = 1000, retries = 3): Promise { - const results = { records: [] }; +export async function bulkQueryChunksIn( + soqlQuery: string, + conn: Connection, + inElements: string[], + batchSize = 1000, + retries = 3 +): Promise { + const results = { records: [] as any[] }; for (let i = 0; i < inElements.length; i += batchSize) { const inElementsChunk = inElements.slice(i, i + batchSize); const replacementString = "'" + inElementsChunk.join("','") + "'"; - const soqlQueryWithInConstraint = soqlQuery.replace("{{IN}}", replacementString); + const soqlQueryWithInConstraint = soqlQuery.replace('{{IN}}', replacementString); const chunkResults = await bulkQuery(soqlQueryWithInConstraint, conn, retries); results.records.push(...chunkResults.records); } return results; } -let spinner; +let spinner: Ora; // Same than soqlQuery but using bulk. Do not use if there will be too many results for javascript to handle in memory -export async function bulkUpdate(objectName: string, action: string, records: Array, conn: Connection): Promise { - uxLog(this, c.grey(`SOQL BULK on object ${c.bold(objectName)} with action ${c.bold(action)} (${c.bold(records.length)} records)`)); - conn.bulk.pollInterval = 5000; // 5 sec - conn.bulk.pollTimeout = 60000; // 60 sec - return new Promise((resolve, reject) => { - const job = conn.bulk.createJob(objectName, action); - const batch = job.createBatch(); - batch.execute(records); - batch.on("queue", async (batchInfo) => { - uxLog(this, c.grey("Bulk API job batch has been queued")); - uxLog(this, c.grey(JSON.stringify(batchInfo, null, 2))); - spinner = ora({ text: `Bulk Load on ${objectName} (${action})`, spinner: "moon" }).start(); - batch.poll(3 * 1000, 120 * 1000); - }); - batch.on("error", (batchInfo) => { - job.close(); - spinner.fail(`Bulk Load on ${objectName} (${action}) failed.`); - uxLog(this, c.red("Bulk query error:" + batchInfo)); - reject(batchInfo); - throw new SfdxError(c.red("Bulk query error:" + batchInfo)); - }); - batch.on("response", (results) => { - job.close(); - spinner.succeed(`Bulk Load on ${objectName} (${action}) completed.`); - resolve({ - results: results, - totalSize: results.length, - successRecordsNb: results.filter((result) => result.success).length, - errorRecordsNb: results.filter((result) => !result.success).length, - }); - }); +export async function bulkUpdate( + objectName: string, + action: string, + records: Array, + conn: Connection +): Promise { + uxLog( + this, + c.grey( + `SOQL BULK on object ${c.bold(objectName)} with action ${c.bold(action)} (${c.bold(records.length)} records)` + ) + ); + conn.bulk2.pollInterval = 5000; // 5 sec + conn.bulk2.pollTimeout = 60000; // 60 sec + // Initialize Job + spinner = ora({ text: `[BulkApiV2] Bulk Load on ${objectName} (${action})`, spinner: 'moon' }).start(); + const job = conn.bulk2.createJob({ + operation: action as any, + object: objectName, + }); + job.on('open', () => { + spinner.text = `[BulkApiV2] Load Job ${job.id} successfully created.`; + }); + // Upload job data + await job.open(); + await job.uploadData(records); + await job.close(); + // Monitor job execution + job.on('inProgress', (jobInfo: any) => { + spinner.text = `[BulkApiV2] Processed: ${jobInfo.numberRecordsProcessed}. Failed: ${jobInfo.numberRecordsFailed}`; }); + job.on('failed', (e) => { + spinner.fail(`[BulkApiV2] Error: ${e.message}`); + }); + await job.poll(); + const res = await job.getAllResults(); + spinner.succeed(`Bulk Load on ${objectName} (${action}) completed.`); + return res; } -export async function bulkDeleteTooling(objectName: string, recordsFull: { Id: string }[], conn: Connection): Promise { - return new Promise((resolve, reject) => { - const records = recordsFull.map((record) => record.Id); - const options: RestApiOptions = { allOrNone: false }; - const handleCallback = (err: Error, result: RecordResult | RecordResult[]) => { - if (err) { - const resultObject = createResultObject(records, false, `One or more ${objectName} records failed to delete.`); - uxLog(this, c.red(`Error deleting ${objectName} records:` + resultObject)); - reject(err); - throw new SfdxError(c.red(`Error deleting ${objectName} records:` + resultObject)); - } else { - const resultsArray = Array.isArray(result) ? result : [result]; - const anyFailure = resultsArray.some((result) => !result.success); +export async function bulkDelete( + objectName: string, + recordIds: string[], + conn: Connection +): Promise { + const records = recordIds.map(recordId => { return { Id: recordId } }); + return await bulkUpdate(objectName, "delete", records, conn); +} - const resultObject = createResultObject(records, !anyFailure, anyFailure ? `One or more ${objectName} records failed to delete.` : ""); - resolve(resultObject); +export async function bulkDeleteTooling( + objectName: string, + recordsIds: string[], + conn: Connection +): Promise { + uxLog(this, c.grey(`[ToolingApi] Delete ${recordsIds.length} records on ${objectName}: ${JSON.stringify(recordsIds)}`)); + try { + const deleteJobResults = await conn.tooling.destroy(objectName, recordsIds, { allOrNone: false }); + return deleteJobResults + } catch (e: any) { + uxLog(this, c.yellow(`[ToolingApi] jsforce error while calling Tooling API. Fallback to to unitary delete (longer but should work !)`)); + uxLog(this, c.grey(e.message)); + const deleteJobResults: any = []; + for (const record of recordsIds) { + const deleteCommand = + `sf data:delete:record --sobject ${objectName} --record-id ${record} --target-org ${conn.getUsername()} --use-tooling-api`; + const deleteCommandRes = await execSfdxJson(deleteCommand, this, { + fail: false, + output: true + }); + const deleteResult: any = { Id: record, success: true } + if (!(deleteCommandRes.status === 0)) { + deleteResult.success = false; + deleteResult.error = JSON.stringify(deleteCommandRes); } - }; - const createResultObject = (records: string | string[], success: boolean, errorMessage: string) => { - const recordsArray = Array.isArray(records) ? records : [records]; - - return { - results: recordsArray.map((record) => ({ - id: record, - success: success, - errors: success ? [] : [errorMessage], - })), - totalSize: recordsArray.length, - successRecordsNb: success ? recordsArray.length : 0, - errorRecordsNb: success ? 0 : recordsArray.length, - errorDetails: success ? [] : [{ error: errorMessage }], - }; - }; - try { - conn.tooling.del(objectName, records, options, handleCallback); - } catch (error) { - const resultObject = createResultObject(records, false, `One or more records failed to delete due to a synchronous error.\n${error.message}`); - reject(resultObject); - throw new SfdxError(c.red("Tooling Error:" + resultObject)); + deleteJobResults.push(deleteResult); } - }); + return { results: deleteJobResults }; + } } diff --git a/src/common/utils/authUtils.ts b/src/common/utils/authUtils.ts new file mode 100644 index 000000000..dcb9c0e17 --- /dev/null +++ b/src/common/utils/authUtils.ts @@ -0,0 +1,421 @@ +import c from 'chalk'; +import * as crossSpawn from 'cross-spawn'; +import fs from 'fs-extra'; +import * as path from 'path'; +import { + createTempDir, + execCommand, + execSfdxJson, + getCurrentGitBranch, + isCI, + promptInstanceUrl, + uxLog, +} from './index.js'; +import { CONSTANTS, getConfig } from '../../config/index.js'; +import { SfError } from '@salesforce/core'; +import { prompts } from './prompts.js'; +import { clearCache } from '../cache/index.js'; +import { WebSocketClient } from '../websocketClient.js'; +import { decryptFile } from '../cryptoUtils.js'; + +// Authorize an org with sfdxAuthUrl, manually or with JWT +export async function authOrg(orgAlias: string, options: any) { + const isDevHub = orgAlias.includes('DevHub'); + + let doConnect = true; + if (!options.checkAuth) { + // Check if we are already authenticated + let orgDisplayCommand = 'sf org display'; + let setDefaultOrg = false; + if (orgAlias && (isCI || isDevHub) && !orgAlias.includes('force://')) { + orgDisplayCommand += ' --target-org ' + orgAlias; + setDefaultOrg = true; + } else { + if ( + options?.argv.includes('--target-org') || + options?.argv.includes('--targetusername') || + options?.argv.includes('-o') || + options?.argv.includes('-u') + ) { + const posUsername = + options.argv.indexOf('--target-org') > -1 + ? options.argv.indexOf('--target-org') + 1 + : options.argv.indexOf('--targetusername') > -1 + ? options.argv.indexOf('--targetusername') + 1 + : options.argv.indexOf('-o') > -1 + ? options.argv.indexOf('-o') + 1 + : options.argv.indexOf('-u') > -1; + orgDisplayCommand += ' --target-org ' + options.argv[posUsername]; + } + } + const orgInfoResult = await execSfdxJson(orgDisplayCommand, this, { + fail: false, + output: false, + debug: options.debug, + }); + if ( + orgInfoResult.result && + orgInfoResult.result.connectedStatus !== 'RefreshTokenAuthError' && + ((orgInfoResult.result.connectedStatus && orgInfoResult.result.connectedStatus.includes('Connected')) || + (options.scratch && orgInfoResult.result.connectedStatus.includes('Unknown')) || + (orgInfoResult.result.alias === orgAlias && orgInfoResult.result.id != null) || + (orgInfoResult.result.username === orgAlias && orgInfoResult.result.id != null) || + (isDevHub && orgInfoResult.result.id != null)) + ) { + // Set as default username or devhubusername + uxLog( + this, + `[sfdx-hardis] You are already ${c.green('connected')} as ${c.green( + orgInfoResult.result.username + )} on org ${c.green(orgInfoResult.result.instanceUrl)}` + ); + if (orgInfoResult.result.expirationDate) { + uxLog(this, c.cyan(`[sfdx-hardis] Org expiration date: ${c.yellow(orgInfoResult.result.expirationDate)}`)); + } + if (!isCI) { + uxLog( + this, + c.yellow( + c.italic( + `[sfdx-hardis] If this is NOT the org you want to play with, ${c.whiteBright( + c.bold('hit CTRL+C') + )}, then input ${c.whiteBright(c.bold('sf hardis:org:select'))}` + ) + ) + ); + } + if (setDefaultOrg) { + const setDefaultOrgCommand = `sf config set ${isDevHub ? 'target-dev-hub' : 'target-org'}=${orgInfoResult.result.username + }`; + await execSfdxJson(setDefaultOrgCommand, this, { fail: false }); + } + doConnect = false; + } + } + // Perform authentication + if (doConnect) { + let logged = false; + const config = await getConfig('user'); + + // Manage auth with sfdxAuthUrl (CI & scratch org only) + const authUrlVarName = `SFDX_AUTH_URL_${orgAlias}`; + const authUrlVarNameUpper = `SFDX_AUTH_URL_${orgAlias.toUpperCase()}`; + let authUrl = process.env[authUrlVarName] || process.env[authUrlVarNameUpper] || orgAlias || ''; + if (isDevHub) { + authUrl = + process.env[authUrlVarName] || + process.env[authUrlVarNameUpper] || + process.env.SFDX_AUTH_URL_DEV_HUB || + orgAlias || + ''; + } + if (authUrl.includes('force://')) { + const authFile = path.join(await createTempDir(), 'sfdxScratchAuth.txt'); + await fs.writeFile(authFile, authUrl, 'utf8'); + const authCommand = + `sf org login sfdx-url -f ${authFile}` + + (isDevHub ? ` --set-default-dev-hub` : ` --set-default`) + + (!orgAlias.includes('force://') ? ` --alias ${orgAlias}` : ''); + await execCommand(authCommand, this, { fail: true, output: false }); + uxLog(this, c.cyan('Successfully logged using sfdxAuthUrl')); + await fs.remove(authFile); + return; + } + + // Get auth variables, with priority CLI arguments, environment variables, then .sfdx-hardis.yml config file + let username = + typeof options.Command.flags?.targetusername === 'string' + ? options.Command.flags?.targetusername + : process.env.TARGET_USERNAME || isDevHub + ? config.devHubUsername + : config.targetUsername; + if (username == null && isCI) { + const gitBranchFormatted = await getCurrentGitBranch({ formatted: true }); + console.error( + c.yellow( + `[sfdx-hardis][WARNING] You may have to define ${c.bold( + isDevHub + ? 'devHubUsername in .sfdx-hardis.yml' + : options.scratch + ? 'cache between your CI jobs: folder ".cache/sfdx-hardis/.sfdx"' + : `targetUsername in config/branches/.sfdx-hardis.${gitBranchFormatted}.yml` + )} ` + ) + ); + process.exit(1); + } + let instanceUrl = + typeof options.Command?.flags?.instanceurl === 'string' && + (options.Command?.flags?.instanceurl || '').startsWith('https') + ? options.Command.flags.instanceurl + : (process.env.INSTANCE_URL || '').startsWith('https') + ? process.env.INSTANCE_URL + : config.instanceUrl + ? config.instanceUrl + : 'https://login.salesforce.com'; + // Get JWT items clientId and certificate key + const sfdxClientId = await getSfdxClientId(orgAlias, config); + const crtKeyfile = await getCertificateKeyFile(orgAlias, config); + const usernameArg = options.setDefault === false ? '' : isDevHub ? '--set-default-dev-hub' : '--set-default'; + if (crtKeyfile && sfdxClientId && username) { + // Login with JWT + const loginCommand = + 'sf org login jwt' + + ` ${usernameArg}` + + ` --client-id ${sfdxClientId}` + + ` --jwt-key-file ${crtKeyfile}` + + ` --username ${username}` + + ` --instance-url ${instanceUrl}` + + (orgAlias ? ` --alias ${orgAlias}` : ''); + const jwtAuthRes = await execSfdxJson(loginCommand, this, { + fail: false, + output: false + }); + // await fs.remove(crtKeyfile); // Delete private key file from temp folder TODO: move to postrun hook + logged = jwtAuthRes.status === 0; + if (!logged) { + console.error(c.red(`[sfdx-hardis][ERROR] JWT login error: \n${JSON.stringify(jwtAuthRes)}`)); + process.exit(1); + } + } else if (!isCI) { + // Login with web auth + const orgLabel = `org ${orgAlias}`; + console.warn( + c.yellow( + c.bold( + `[sfdx-hardis] You must be connected to ${orgLabel} to perform this command. Please login in the open web browser` + ) + ) + ); + + if (isCI) { + console.error( + c.red(`See CI authentication doc at ${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-auth/`) + ); + throw new SfError( + `In CI context, you may define: + - a .sfdx-hardis.yml file with instanceUrl and targetUsername properties (or INSTANCE_URL and TARGET_USERNAME repo variables) + - a repository secret variable SFDX_CLIENT_ID with consumer key of SF CLI connected app + - store server.key file within ssh folder + ` + ); + } + const orgTypes = isDevHub ? ['login'] : ['login', 'test']; + instanceUrl = await promptInstanceUrl(orgTypes, orgAlias); + + const configInfoUsr = await getConfig('user'); + + // Prompt user for Web or Device login + const loginTypeRes = await prompts({ + name: 'loginType', + type: 'select', + message: "Select a login type (if you don't know, use Web)", + choices: [ + { + title: '🌐 Web Login (If VsCode is locally installed on your computer)', + value: 'web', + }, + { + title: '📟 Device Login (Useful for CodeBuilder / CodeSpaces)', + value: 'device', + description: 'Look at the instructions in the console terminal if you select this option', + }, + ], + default: 'web', + initial: 'web', + }); + + let loginResult: any = null; + // Manage device login + if (loginTypeRes.loginType === 'device') { + const loginCommandArgs = ['org login device', '--instance-url', instanceUrl]; + if (orgAlias && orgAlias !== configInfoUsr?.scratchOrgAlias) { + loginCommandArgs.push(...['--alias', orgAlias]); + } + if (options.setDefault === true && isDevHub) { + loginCommandArgs.push('--set-default-dev-hub'); + } + if (options.setDefault === true && !isDevHub) { + loginCommandArgs.push('--set-default'); + } + const commandStr = 'sf ' + loginCommandArgs.join(' '); + uxLog(this, `[sfdx-hardis][command] ${c.bold(c.bgWhite(c.grey(commandStr)))}`); + loginResult = crossSpawn.sync('sf', loginCommandArgs, { stdio: 'inherit' }); + } + // Web Login if device login not used + if (loginResult == null) { + const loginCommand = + 'sf org login web' + + (options.setDefault === false ? '' : isDevHub ? ' --set-default-dev-hub' : ' --set-default') + + ` --instance-url ${instanceUrl}` + + (orgAlias && orgAlias !== configInfoUsr?.scratchOrgAlias ? ` --alias ${orgAlias}` : ''); + try { + loginResult = await execCommand(loginCommand, this, { output: false, fail: true, spinner: false }); + } catch (e) { + // Give instructions if server is unavailable + if (((e as Error).message || '').includes('Cannot start the OAuth redirect server on port')) { + uxLog( + this, + c.yellow( + c.bold( + 'You might have a ghost SF CLI command. Open Task Manager, search for Node.js processes, kill them, then try again' + ) + ) + ); + } + throw e; + } + } + await clearCache('sf org list'); + uxLog(this, c.grey(JSON.stringify(loginResult, null, 2))); + logged = loginResult.status === 0; + username = loginResult?.username || 'err'; + instanceUrl = loginResult?.instanceUrl || instanceUrl; + } else { + console.error(c.red(`[sfdx-hardis] Unable to connect to org ${orgAlias} with browser. Please try again :)`)); + } + if (logged) { + // Retrieve default username or dev hub username if not returned by command + if (username === 'err') { + const configGetRes = await execSfdxJson('sf config get ' + (isDevHub ? 'target-dev-hub' : 'target-org'), this, { + output: false, + fail: false, + }); + username = configGetRes?.result[0]?.value || ''; + } + uxLog(this, `Successfully logged to ${c.green(instanceUrl)} with ${c.green(username)}`); + WebSocketClient.sendMessage({ event: 'refreshStatus' }); + // Assign org to SfCommands + if (isDevHub) { + options.Command.flags.targetdevhubusername = username; + // options.Command.assignHubOrg(); // seems to be automatically done by SfCommand under the hook + } else { + options.Command.flags.targetusername = username; + // options.Command.assignOrg(); // seems to be automatically done by SfCommand under the hook + } + // Display warning message in case of local usage (not CI), and not login command + // if (!(options?.Command?.id || "").startsWith("hardis:auth:login")) { + // console.warn(c.yellow("*** IF YOU SEE AN AUTH ERROR PLEASE RUN AGAIN THE SAME COMMAND :) ***")); + // } + } else { + console.error(c.red('[sfdx-hardis][ERROR] You must be logged to an org to perform this action')); + process.exit(1); // Exit because we should succeed to connect + } + } +} + +// Get clientId for SFDX connected app +async function getSfdxClientId(orgAlias: string, config: any) { + // Try to find in global variables + const sfdxClientIdVarName = `SFDX_CLIENT_ID_${orgAlias}`; + if (process.env[sfdxClientIdVarName]) { + return process.env[sfdxClientIdVarName]; + } + const sfdxClientIdVarNameUpper = sfdxClientIdVarName.toUpperCase(); + if (process.env[sfdxClientIdVarNameUpper]) { + return process.env[sfdxClientIdVarNameUpper]; + } + if (process.env.SFDX_CLIENT_ID) { + console.warn( + c.yellow( + `[sfdx-hardis] If you use CI on multiple branches & orgs, you should better define CI variable ${c.bold( + sfdxClientIdVarNameUpper + )} than SFDX_CLIENT_ID` + ) + ); + console.warn( + c.yellow(`See CI authentication doc at ${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-auth/`) + ); + return process.env.SFDX_CLIENT_ID; + } + // Try to find in config files ONLY IN LOCAL MODE (in CI, it's supposed to be a CI variable) + if (!isCI && config.devHubSfdxClientId) { + return config.devHubSfdxClientId; + } + if (isCI) { + console.error( + c.red( + `[sfdx-hardis] You must set env variable ${c.bold( + sfdxClientIdVarNameUpper + )} with the Consumer Key value defined on SFDX Connected app` + ) + ); + console.error(c.red(`See CI authentication doc at ${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-auth/`)); + } + return null; +} + +// Get clientId for SFDX connected app +async function getKey(orgAlias: string, config: any) { + // Try to find in global variables + const sfdxClientKeyVarName = `SFDX_CLIENT_KEY_${orgAlias}`; + if (process.env[sfdxClientKeyVarName]) { + return process.env[sfdxClientKeyVarName]; + } + const sfdxClientKeyVarNameUpper = sfdxClientKeyVarName.toUpperCase(); + if (process.env[sfdxClientKeyVarNameUpper]) { + return process.env[sfdxClientKeyVarNameUpper]; + } + if (process.env.SFDX_CLIENT_KEY) { + console.warn( + c.yellow( + `[sfdx-hardis] If you use CI on multiple branches & orgs, you should better define CI variable ${c.bold( + sfdxClientKeyVarNameUpper + )} than SFDX_CLIENT_KEY` + ) + ); + console.warn( + c.yellow(`See CI authentication doc at ${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-auth/`) + ); + return process.env.SFDX_CLIENT_KEY; + } + // Try to find in config files ONLY IN LOCAL MODE (in CI, it's supposed to be a CI variable) + if (!isCI && config.devHubSfdxClientKey) { + return config.devHubSfdxClientKey; + } + if (isCI) { + console.error( + c.red( + `[sfdx-hardis] You must set env variable ${c.bold( + sfdxClientKeyVarNameUpper + )} with the value of SSH private key encryption key` + ) + ); + console.error(c.red(`See CI authentication doc at ${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-auth/`)); + } + return null; +} + +// Try to find certificate key file for SF CLI connected app in different locations +async function getCertificateKeyFile(orgAlias: string, config: any) { + const filesToTry = [ + `./config/branches/.jwt/${orgAlias}.key`, + `./config/.jwt/${orgAlias}.key`, + `./ssh/${orgAlias}.key`, + `./.ssh/${orgAlias}.key`, + './ssh/server.key', + ]; + for (const file of filesToTry) { + if (fs.existsSync(file)) { + // Decrypt SSH private key and write a temporary file + const sshKey = await getKey(orgAlias, config); + if (sshKey == null) { + continue; + } + const tmpSshKeyFile = path.join(await createTempDir(), `${orgAlias}.key`); + await decryptFile(file, tmpSshKeyFile, sshKey); + return tmpSshKeyFile; + } + } + if (isCI) { + console.error( + c.red( + `[sfdx-hardis] You must put a certificate key to connect via JWT.Possible locations:\n -${filesToTry.join( + '\n -' + )}` + ) + ); + console.error(c.red(`See CI authentication doc at ${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-auth/`)); + } + return null; +} diff --git a/src/common/utils/classUtils.ts b/src/common/utils/classUtils.ts index 45cdc0e0f..07cafaf10 100644 --- a/src/common/utils/classUtils.ts +++ b/src/common/utils/classUtils.ts @@ -1,6 +1,6 @@ -import { countRegexMatches, uxLog } from "."; -import * as c from "chalk"; -import * as readFilesRecursive from "fs-readdir-recursive"; +import { countRegexMatches, uxLog } from "./index.js"; +import c from "chalk"; +import readFilesRecursive from "fs-readdir-recursive"; import * as path from "path"; import * as fs from "fs"; @@ -19,12 +19,12 @@ function findSubstringInFile(filePath: string, substring: string): Promise !file.includes("node_modules") && file.includes("classes") && file.endsWith(".cls")) .map((file) => { diff --git a/src/common/utils/dataUtils.ts b/src/common/utils/dataUtils.ts index 0c5aae743..8b8bb1e31 100644 --- a/src/common/utils/dataUtils.ts +++ b/src/common/utils/dataUtils.ts @@ -1,100 +1,110 @@ -import { SfdxError } from "@salesforce/core"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; -import { elapseEnd, elapseStart, execCommand, uxLog } from "."; -import { getConfig } from "../../config"; -import { prompts } from "./prompts"; +import { SfError } from '@salesforce/core'; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; +import { elapseEnd, elapseStart, execCommand, uxLog } from './index.js'; +import { getConfig } from '../../config/index.js'; +import { prompts } from './prompts.js'; +import { isProductionOrg } from './orgUtils.js'; -export const dataFolderRoot = path.join(".", "scripts", "data"); +export const dataFolderRoot = path.join('.', 'scripts', 'data'); // Import data from sfdmu folder export async function importData(sfdmuPath: string, commandThis: any, options: any = {}) { const dtl = await getDataWorkspaceDetail(sfdmuPath); - if (dtl.isDelete === true) { - throw new SfdxError("Your export.json contains deletion info, please use appropriate delete command"); + if (dtl?.isDelete === true) { + throw new SfError('Your export.json contains deletion info, please use appropriate delete command'); } - uxLog(commandThis, c.cyan(`Importing data from ${c.green(dtl.full_label)} ...`)); + uxLog(commandThis, c.cyan(`Importing data from ${c.green(dtl?.full_label)} ...`)); /* jscpd:ignore-start */ - uxLog(commandThis, c.italic(c.grey(dtl.description))); + uxLog(commandThis, c.italic(c.grey(dtl?.description))); const targetUsername = options.targetUsername || commandThis.org.getConnection().username; - await fs.ensureDir(path.join(sfdmuPath, "logs")); - const config = await getConfig("branch"); + await fs.ensureDir(path.join(sfdmuPath, 'logs')); + const config = await getConfig('branch'); const dataImportCommand = - "sfdx sfdmu:run" + + 'sf sfdmu:run' + ` --sourceusername csvfile` + - ` --targetusername ${targetUsername}` + + ` --targetusername ${targetUsername}` + // Keep targetusername until sfdmu switches to target-org ` -p ${sfdmuPath}` + - " --noprompt" + - (config.sfdmuCanModify ? ` --canmodify ${config.sfdmuCanModify}` : ""); + ' --noprompt' + + (config.sfdmuCanModify ? ` --canmodify ${config.sfdmuCanModify}` : ''); /* jscpd:ignore-end */ - elapseStart(`import ${dtl.full_label}`); + elapseStart(`import ${dtl?.full_label}`); await execCommand(dataImportCommand, commandThis, { fail: true, output: true, }); - elapseEnd(`import ${dtl.full_label}`); + elapseEnd(`import ${dtl?.full_label}`); } // Delete data using sfdmu folder export async function deleteData(sfdmuPath: string, commandThis: any, options: any = {}) { + const config = await getConfig('branch'); const dtl = await getDataWorkspaceDetail(sfdmuPath); - if (dtl.isDelete === false) { - throw new SfdxError( - "Your export.json does not contain deletion information. Please check http://help.sfdmu.com/full-documentation/advanced-features/delete-from-source", + if (dtl?.isDelete === false) { + throw new SfError( + 'Your export.json does not contain deletion information. Please check http://help.sfdmu.com/full-documentation/advanced-features/delete-from-source' ); } - uxLog(commandThis, c.cyan(`Deleting data from ${c.green(dtl.full_label)} ...`)); - uxLog(commandThis, c.italic(c.grey(dtl.description))); - const targetUsername = options.targetUsername || commandThis.org.getConnection().username; - await fs.ensureDir(path.join(sfdmuPath, "logs")); - const config = await getConfig("branch"); + // If org is production, make sure that "runnableInProduction": true is present in export.json + const isProdOrg = await isProductionOrg(options?.targetUsername || options?.conn?.username || "ERROR", options); + if (isProdOrg === true && (dtl?.runnableInProduction || false) !== true) { + throw new SfError(`To run this delete SFDMU script in production, you need to define "runnableInProduction": true in its export.json file`); + } + if (isProdOrg === true && !config.sfdmuCanModify) { + uxLog(this, c.yellow(`If you see a sfdmu error, you probably need to add a property sfdmuCanModify: YOUR_ORG_INSTANCE_URL in the related config/branches/.sfdx-hardis.YOUR_BRANCH.yml config file.`)); + } + // Delete using sfdmu + uxLog(commandThis, c.cyan(`Deleting data from ${c.green(dtl?.full_label)} ...`)); + uxLog(commandThis, c.italic(c.grey(dtl?.description))); + const targetUsername = options.targetUsername || options.conn.username; + await fs.ensureDir(path.join(sfdmuPath, 'logs')); const dataImportCommand = - "sfdx sfdmu:run" + + 'sf sfdmu:run' + ` --sourceusername ${targetUsername}` + ` -p ${sfdmuPath}` + - " --noprompt" + - (config.sfdmuCanModify ? ` --canmodify ${config.sfdmuCanModify}` : ""); - elapseStart(`delete ${dtl.full_label}`); + ' --noprompt' + + (config.sfdmuCanModify ? ` --canmodify ${config.sfdmuCanModify}` : ''); + elapseStart(`delete ${dtl?.full_label}`); await execCommand(dataImportCommand, commandThis, { fail: true, output: true, }); - elapseEnd(`delete ${dtl.full_label}`); + elapseEnd(`delete ${dtl?.full_label}`); } // Export data from sfdmu folder export async function exportData(sfdmuPath: string, commandThis: any, options: any = {}) { /* jscpd:ignore-start */ const dtl = await getDataWorkspaceDetail(sfdmuPath); - if (dtl.isDelete === true) { - throw new SfdxError("Your export.json contains deletion info, please use appropriate delete command"); + if (dtl?.isDelete === true) { + throw new SfError('Your export.json contains deletion info, please use appropriate delete command'); } /* jscpd:ignore-end */ - uxLog(commandThis, c.cyan(`Exporting data from ${c.green(dtl.full_label)} ...`)); - uxLog(commandThis, c.italic(c.grey(dtl.description))); + uxLog(commandThis, c.cyan(`Exporting data from ${c.green(dtl?.full_label)} ...`)); + uxLog(commandThis, c.italic(c.grey(dtl?.description))); const sourceUsername = options.sourceUsername || commandThis.org.getConnection().username; - await fs.ensureDir(path.join(sfdmuPath, "logs")); - const dataImportCommand = `sfdx sfdmu:run --sourceusername ${sourceUsername} --targetusername csvfile -p ${sfdmuPath} --noprompt`; + await fs.ensureDir(path.join(sfdmuPath, 'logs')); + const dataImportCommand = `sf sfdmu:run --sourceusername ${sourceUsername} --targetusername csvfile -p ${sfdmuPath} --noprompt`; // keep target username until sfdmu migrates to SF cli base await execCommand(dataImportCommand, commandThis, { fail: true, output: true, }); } -export async function selectDataWorkspace(opts = { selectDataLabel: "Please select a data workspace to export" }) { +export async function selectDataWorkspace(opts = { selectDataLabel: 'Please select a data workspace to export' }) { if (!fs.existsSync(dataFolderRoot)) { - throw new SfdxError( - "There is no sfdmu root folder 'scripts/data' in your workspace. Create it and define sfdmu exports using sfdmu: https://help.sfdmu.com/", + throw new SfError( + "There is no sfdmu root folder 'scripts/data' in your workspace. Create it and define sfdmu exports using sfdmu: https://help.sfdmu.com/" ); } const sfdmuFolders = fs .readdirSync(dataFolderRoot, { withFileTypes: true }) .filter((dirent) => dirent.isDirectory()) - .map((dirent) => path.join(".", "scripts", "data", dirent.name)); + .map((dirent) => path.join('.', 'scripts', 'data', dirent.name)); if (sfdmuFolders.length === 0) { - throw new SfdxError("There is no sfdmu folder in your workspace. Create them using sfdmu: https://help.sfdmu.com/"); + throw new SfError('There is no sfdmu folder in your workspace. Create them using sfdmu: https://help.sfdmu.com/'); } const choices: any = []; for (const sfdmuFolder of sfdmuFolders) { @@ -108,8 +118,8 @@ export async function selectDataWorkspace(opts = { selectDataLabel: "Please sele } } const sfdmuDirResult = await prompts({ - type: "select", - name: "value", + type: 'select', + name: 'value', message: c.cyanBright(opts.selectDataLabel), choices: choices, }); @@ -117,21 +127,25 @@ export async function selectDataWorkspace(opts = { selectDataLabel: "Please sele } export async function getDataWorkspaceDetail(dataWorkspace: string) { - const exportFile = path.join(dataWorkspace, "export.json"); + const exportFile = path.join(dataWorkspace, 'export.json'); if (!fs.existsSync(exportFile)) { - uxLog(this, c.yellow(`Your SFDMU folder ${c.bold(dataWorkspace)} must contain an ${c.bold("export.json")} configuration file`)); + uxLog( + this, + c.yellow(`Your SFDMU folder ${c.bold(dataWorkspace)} must contain an ${c.bold('export.json')} configuration file`) + ); return null; } - const exportFileJson = JSON.parse(await fs.readFile(exportFile, "utf8")); - const folderName = dataWorkspace.replace(/\\/g, "/").match(/([^/]*)\/*$/)[1]; + const exportFileJson = JSON.parse(await fs.readFile(exportFile, 'utf8')); + const folderName = (dataWorkspace.replace(/\\/g, '/').match(/([^/]*)\/*$/) || [])[1]; const hardisLabel = exportFileJson.sfdxHardisLabel || folderName; const hardisDescription = exportFileJson.sfdxHardisDescription || dataWorkspace; return { - full_label: `[${folderName}]${folderName != hardisLabel ? `: ${hardisLabel}` : ""}`, + full_label: `[${folderName}]${folderName != hardisLabel ? `: ${hardisLabel}` : ''}`, label: hardisLabel, description: hardisDescription, exportJson: exportFileJson, isDelete: isDeleteDataWorkspace(exportFileJson), + runnableInProduction: exportFileJson.runnableInProduction || false }; } @@ -139,7 +153,7 @@ export async function getDataWorkspaceDetail(dataWorkspace: string) { export function isDeleteDataWorkspace(exportFileJson: any) { let isDelete = false; for (const objectConfig of exportFileJson.objects) { - if (objectConfig?.deleteFromSource === true || objectConfig.operation === "DeleteSource") { + if (objectConfig?.deleteFromSource === true || objectConfig.operation === 'DeleteSource') { isDelete = true; } } diff --git a/src/common/utils/deployTips.ts b/src/common/utils/deployTips.ts index dea93ded2..bfc6f93c6 100644 --- a/src/common/utils/deployTips.ts +++ b/src/common/utils/deployTips.ts @@ -1,15 +1,15 @@ // Analyze deployment errors to provide tips to user :) -import * as c from "chalk"; +import c from "chalk"; import * as format from "string-template"; -import { getAllTips } from "./deployTipsList"; -import { deployErrorsToMarkdown, testFailuresToMarkdown } from "../gitProvider/utilsMarkdown"; -import { stripAnsi, uxLog } from "."; -import { AiProvider, AiResponse } from "../aiProvider"; +import { getAllTips } from "./deployTipsList.js"; +import { deployErrorsToMarkdown, testFailuresToMarkdown } from "../gitProvider/utilsMarkdown.js"; +import { stripAnsi, uxLog } from "./index.js"; +import { AiProvider, AiResponse } from "../aiProvider/index.js"; -let logRes = null; -let errorsAndTips = []; -let alreadyProcessedErrors = []; +let logRes: string | null = null; +let errorsAndTips: any[] = []; +let alreadyProcessedErrors: any[] = []; const firstYellowChar = c.yellow("*")[0]; // Checks for deploy tips in a log string @@ -25,7 +25,7 @@ export async function analyzeDeployErrorLogs(log: string, includeInLog = true, o } } // Add default error messages for errors without tips - const logResLines = []; + const logResLines: any[] = []; const updatedLogLines = returnErrorLines(logRes); let index = 0; for (const logLine of updatedLogLines) { @@ -65,15 +65,15 @@ export async function analyzeDeployErrorLogs(log: string, includeInLog = true, o } // Extract failed test classes - const failedTests = []; + const failedTests: any[] = []; const logRaw = stripAnsi(log); const regexFailedTests = /Test Failures([\S\s]*?)Test Success/gm; if (logRaw.match(regexFailedTests)) { - const failedTestsLines = regexFailedTests - .exec(logRaw)[1] + const failedTestsLines = (regexFailedTests + .exec(logRaw) || [])[1] .split("\n") .map((s) => s.trim()); - let failedTest = null; + let failedTest: any = null; // Parse strings to extract main error line then stack for (const line of failedTestsLines) { const regex = /^(\w+[\d_]*)\s+(\w+[\d_]*)\s*(.*)$/; @@ -87,7 +87,7 @@ export async function analyzeDeployErrorLogs(log: string, includeInLog = true, o failedTest = { class: match[1], method: match[2], - error: errSplit.shift().trim(), + error: (errSplit.shift() || "").trim(), }; if (errSplit.length > 0) { failedTest.stack = "Class." + errSplit.join("\nClass."); @@ -103,12 +103,12 @@ export async function analyzeDeployErrorLogs(log: string, includeInLog = true, o // Checks if the error string or regex is found in the log // Adds the fix tip under the line if includeInLog is true async function matchesTip(tipDefinition: any, includeInLog = true): Promise { - const newLogLines = []; + const newLogLines: any[] = []; // string matching if ( tipDefinition.expressionString && tipDefinition.expressionString.filter((expressionString: any) => { - return logRes.includes(expressionString); + return (logRes || "").includes(expressionString); }).length > 0 ) { if (includeInLog) { @@ -162,7 +162,7 @@ async function matchesTip(tipDefinition: any, includeInLog = true): Promise 0 ) { if (includeInLog) { - const newLogLines = []; + const newLogLines: any[] = []; const logLines = returnErrorLines(logRes); for (const line of logLines) { newLogLines.push(line); @@ -248,7 +248,7 @@ async function findAiTip(errorLine: any): Promise { const aiResponse = await AiProvider.promptAi(prompt); return aiResponse; } catch (e) { - uxLog(this, c.yellow("[AI] Error while calling OpenAI: " + e.message)); + uxLog(this, c.yellow("[AI] Error while calling OpenAI: " + (e as Error).message)); } } return null; diff --git a/src/common/utils/deployTipsList.ts b/src/common/utils/deployTipsList.ts index 07b2742a5..58ac3d9b4 100644 --- a/src/common/utils/deployTipsList.ts +++ b/src/common/utils/deployTipsList.ts @@ -1,4 +1,15 @@ +import { CONSTANTS } from "../../config/index.js"; + export function getAllTips() { + const allTips = listAllTips().map((tip: any) => { + tip.docUrl = `${CONSTANTS.DOC_URL_ROOT}/sf-deployment-assistant/${tip.label.replace(/[^a-zA-Z0-9 -]|\s/g, '-')}.md` + return tip; + }); + + return allTips; +} + +function listAllTips() { return [ { name: "api-version-error", @@ -6,7 +17,7 @@ export function getAllTips() { expressionRegex: [/Error (.*) The (.*) apiVersion can't be "([0-9]+)"/gm], tip: `{1} metadata has probably been created/updated in a sandbox already upgraded to next platform version (ex: Sandbox in Summer'23 and Production in Spring'23) - First, try to update the api version in the XML of {1} metadata file (decrement the number in {3}.0) -- If it still doesn't work because the metadata structure has changed between version, you may try a force:source:retrieve of the metadata by forcing --apiversion at the end of the command. +- If it still doesn't work because the metadata structure has changed between version, you may try a sf project:retrieve:start of the metadata by forcing --api-version at the end of the command. `, }, { @@ -67,7 +78,7 @@ THIS MAY BE A FALSE POSITIVE if you are just testing the deployment, as destruct expressionRegex: [/Error (.*) Cannot find folder:(.*)/gm], tip: `Folder {2} is missing. - If folder {2} is existing in sources, add it in related package.xml -- If folder {2} is not existing in DX sources, please use sfdx hardis:project:clean:retrievefolders to retrieve it +- If folder {2} is not existing in DX sources, please use sf hardis:project:clean:retrievefolders to retrieve it - If both previous solutions did not work, go create manually folder {2} in target org `, }, @@ -106,7 +117,12 @@ Example of XML you have to remove in {1}: - Delete field {1} in target org: it will be recreated after deployment (but you will loose data on existing records, so be careful if your target is a production org) - Create another field with desired type and manage data recovery if the target is a production org`, }, - + { + name: "change-matching-rule", + label: "Change Matching Rule", + expressionRegex: [/Error (.*) Before you change a matching rule, you must deactivate it/gm], + tip: `To be able to deploy, you must go in target org setup to manually deactivate matching rule {1}`, + }, { name: "condition-missing-reference", label: "Condition missing reference", @@ -124,7 +140,7 @@ Example of XML you have to remove in {1}: - If you renamed the custom object, do a search/replace in sources with previous object name and new object name - If you deleted the custom object, or if you don't want to deploy it, do a search on the custom object name, and remove XML elements referencing it - If the object should exist, make sure it is in force-app/main/default/objects and that the object name is in manifest/package.xml in CustomObject section -You may also have a look to command sfdx hardis:project:clean:references +You may also have a look to command sf hardis:project:clean:references `, }, { @@ -136,9 +152,12 @@ You may also have a look to command sfdx hardis:project:clean:references - If you deleted {3}.{4}, or if you don't want to deploy it, do a search on {4} in all sources, and remove all XML elements referring to {3}.{4} (except in destructiveChanges.xml) - If {3}.{4} should exist, make sure it is in force-app/main/default/objects/{3}/fields and that {3}.{4} is in manifest/package.xml in CustomField section - If {3}.{4} is standard, the error is because {3}.{4} is not available in the org you are trying to deploy to. You can: - - Remove the reference to {4} in the XML of {1} ( maybe sfdx hardis:project:clean:references can clean automatically for you ! ) + - Remove the reference to {4} in the XML of {1} ( maybe sf hardis:project:clean:references can clean automatically for you ! ) - Activate the required features/license in the target org `, + examples: [ + "Error PS_Admin In field: field - no CustomField named User.expcloud__Portal_Username__c found" + ] }, { name: "custom-field-rights-mandatory", @@ -163,6 +182,12 @@ Example of element to delete: - If you use a package.xml, is {3} present within type CustomMetadata ? `, }, + { + name: "expired-access-token", + label: "Expired Access / Refresh Token", + expressionString: ["expired access/refresh token"], + tip: `Run command "Select another org" from Status panel (or sf hardis:org:select) to authenticate again to your org`, + }, { name: "missingDataCategoryGroup", label: "Missing Data Category Group", @@ -196,7 +221,7 @@ Example of element to delete: label: "Missing e-mail template", expressionRegex: [/In field: template - no EmailTemplate named (.*) found/gm], tip: `An email template should be present in the sources. To retrieve it, you can run: -sfdx force:source:retrieve -m EmailTemplate:{1} -u YOUR_ORG_USERNAME`, +sf project retrieve start -m EmailTemplate:{1} -o YOUR_ORG_USERNAME`, }, { name: "empty-item", @@ -219,7 +244,7 @@ You probably also need to add CRM Analytics Admin Permission Set assignment to t { name: "error-parsing-file", label: "Error parsing file", - expressionRegex: [/Error (.*) Error parsing file: (.*) /gm], + expressionRegex: [/Error (.*) Error parsing file: (.*)/gm], tip: `There has been an error parsing the XML file of {1}: {2} - Open file {1} and look where the error can be ! (merge issue, typo, XML tag not closed...)`, }, @@ -278,7 +303,7 @@ More details at https://help.salesforce.com/articleView?id=sf.tips_on_building_f expressionString: ["Invalid scope:Mine, not allowed"], tip: `Replace Mine by Everything in the list view SFDX source XML. Have a look at this command to manage that automatically :) -https://sfdx-hardis.cloudity.com/hardis/org/fix/listviewmine/ +${CONSTANTS.DOC_URL_ROOT}/hardis/org/fix/listviewmine/ `, }, { @@ -418,7 +443,7 @@ If it is already done, you may manually check "MarketingUser" field on the scrat expressionString: ["ProductRequest"], tip: `ProductRequest object is not available in the target org. Maybe you would like to clean its references within Profiles / PS using the following command ? -sfdx hardis:project:clean:references , then select "ProductRequest references"`, +sf hardis:project:clean:references , then select "ProductRequest references"`, }, { name: "missing-feature-social-customer-service", @@ -490,7 +515,7 @@ sfdx hardis:project:clean:references , then select "ProductRequest references"`, label: "Missing report", expressionRegex: [/Error (.*) The (.*) report chart has a problem with the "reportName" field/gm], tip: `{1} is referring to unknown report {2}. To retrieve it, you can run: -- sfdx force:source:retrieve -m Report:{2} -u YOUR_ORG_USERNAME +- sf project retrieve start -m Report:{2} -o YOUR_ORG_USERNAME - If it fails, looks for the report folder and add it before report name to the retrieve command (ex: MYFOLDER/MYREPORTNAME) `, }, @@ -512,7 +537,7 @@ sfdx hardis:project:clean:references , then select "ProductRequest references"`, expressionString: ["sharing operation already in progress"], tip: `You can not deploy multiple SharingRules at the same time. You can either: - Remove SharingOwnerRules and SharingRule from package.xml (so it becomes a manual operation) -- Use sfdx hardis:work:save to generate a deploymentPlan in .sfdx-hardis.json, +- Use sf hardis:work:save to generate a deploymentPlan in .sfdx-hardis.json, - If you are trying to create a scratch org, add DeferSharingCalc in features in project-scratch-def.json `, }, @@ -553,7 +578,7 @@ Go manually make the change in the target org, so the deployment will pass label: "Picklist value not found", expressionRegex: [/Picklist value: (.*) in picklist: (.*) not found/gm], tip: `Sources have references to value {1} of picklist {2} -- If picklist {2} is standard, add the picklist to sfdx sources by using "sfdx force:source:retrieve -m StandardValueSet:{2}", then save again +- If picklist {2} is standard, add the picklist to sfdx sources by using "sf project retrieve start -m StandardValueSet:{2}", then save again - Else, perform a search in all code of {1}, then remove XML tags referring to {1} (for example in record types metadatas) `, }, @@ -585,8 +610,8 @@ Go manually make the change in the target org, so the deployment will pass label: "CRM Analytics: A Recipe must specify a DataFlow", expressionRegex: [/Error (.*) A Recipe must specify a Dataflow/gm], tip: `You must include related WaveDataFlow {1} in sources (and probably in package.xml too). -To retrieve it, run: sfdx force:source:retrieve -m WaveDataFlow:{1} -u SOURCE_ORG_USERNAME -You can also retrieve all analytics sources in one shot using sfdx hardis:org:retrieve:source:analytics -u SOURCE_ORG_USERNAME +To retrieve it, run: sf project retrieve start -m WaveDataFlow:{1} -u SOURCE_ORG_USERNAME +You can also retrieve all analytics sources in one shot using sf hardis:org:retrieve:source:analytics -u SOURCE_ORG_USERNAME - https://salesforce.stackexchange.com/a/365453/33522 - https://help.salesforce.com/s/articleView?id=000319274&type=1`, }, @@ -714,3 +739,4 @@ If you see two {2} XML blocks with {3}, please decide which one you keep and rem }, ]; } + diff --git a/src/common/utils/deployUtils.ts b/src/common/utils/deployUtils.ts index c9274e368..bd10e685b 100644 --- a/src/common/utils/deployUtils.ts +++ b/src/common/utils/deployUtils.ts @@ -1,37 +1,49 @@ -import { SfdxError } from "@salesforce/core"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import { glob } from "glob"; -import * as path from "path"; -import * as sortArray from "sort-array"; -import { createTempDir, elapseEnd, elapseStart, execCommand, execSfdxJson, getCurrentGitBranch, git, gitHasLocalUpdates, isCI, uxLog } from "."; -import { CONSTANTS, getConfig, setConfig } from "../../config"; -import { GitProvider } from "../gitProvider"; -import { deployCodeCoverageToMarkdown } from "../gitProvider/utilsMarkdown"; -import { MetadataUtils } from "../metadata-utils"; -import { importData } from "./dataUtils"; -import { analyzeDeployErrorLogs } from "./deployTips"; -import { callSfdxGitDelta } from "./gitUtils"; -import { createBlankSfdxProject, isSfdxProject } from "./projectUtils"; -import { prompts } from "./prompts"; -import { arrangeFilesBefore, restoreArrangedFiles } from "./workaroundUtils"; -import { isPackageXmlEmpty, parseXmlFile, removePackageXmlFilesContent, writeXmlFile } from "./xmlUtils"; -import { ResetMode } from "simple-git"; -import { isSandbox } from "./orgUtils"; +import { Connection, SfError } from '@salesforce/core'; +import c from 'chalk'; +import fs from 'fs-extra'; +import { glob } from 'glob'; +import * as path from 'path'; +import sortArray from 'sort-array'; +import { + createTempDir, + elapseEnd, + elapseStart, + execCommand, + execSfdxJson, + getCurrentGitBranch, + git, + gitHasLocalUpdates, + isCI, + uxLog, +} from './index.js'; +import { CONSTANTS, getConfig, setConfig } from '../../config/index.js'; +import { GitProvider } from '../gitProvider/index.js'; +import { deployCodeCoverageToMarkdown } from '../gitProvider/utilsMarkdown.js'; +import { MetadataUtils } from '../metadata-utils/index.js'; +import { importData } from './dataUtils.js'; +import { analyzeDeployErrorLogs } from './deployTips.js'; +import { callSfdxGitDelta } from './gitUtils.js'; +import { createBlankSfdxProject, isSfdxProject } from './projectUtils.js'; +import { prompts } from './prompts.js'; +import { arrangeFilesBefore, restoreArrangedFiles } from './workaroundUtils.js'; +import { isPackageXmlEmpty, parseXmlFile, removePackageXmlFilesContent, writeXmlFile } from './xmlUtils.js'; +import { ResetMode } from 'simple-git'; +import { isProductionOrg } from './orgUtils.js'; +import { soqlQuery } from './apiUtils.js'; +import { checkSfdxHardisTraceAvailable } from './orgConfigUtils.js'; // Push sources to org // For some cases, push must be performed in 2 times: the first with all passing sources, and the second with updated sources requiring the first push export async function forceSourcePush(scratchOrgAlias: string, commandThis: any, debug = false, options: any = {}) { - elapseStart("force:source:push"); - const config = await getConfig("user"); + elapseStart('project:deploy:start'); + const config = await getConfig('user'); const currentBranch = await getCurrentGitBranch(); - let arrangedFiles = []; + let arrangedFiles: any[] = []; if (!(config[`tmp_${currentBranch}_pushed`] === true)) { arrangedFiles = await arrangeFilesBefore(commandThis, options); } try { - const sfdxPushCommand = options.sfdxPushCommand || "force:source:push"; - const pushCommand = `sfdx ${sfdxPushCommand} -g -w 60 --forceoverwrite -u ${scratchOrgAlias}`; + const pushCommand = `sf project deploy start --ignore-warnings --ignore-conflicts -o ${scratchOrgAlias} --wait 60`; await execCommand(pushCommand, commandThis, { fail: true, output: !isCI, @@ -46,74 +58,58 @@ export async function forceSourcePush(scratchOrgAlias: string, commandThis: any, }); const configToSet = {}; configToSet[`tmp_${currentBranch}_pushed`] = true; - await setConfig("user", configToSet); + await setConfig('user', configToSet); } - elapseEnd("force:source:push"); + elapseEnd('project:deploy:start'); } catch (e) { await restoreArrangedFiles(arrangedFiles, commandThis); // Manage beta/legacy boza - const stdOut = e.stdout + e.stderr; - if (stdOut.includes(`'force:source:legacy:push' with your existing tracking files`)) { - options.sfdxPushCommand = "force:source:legacy:push"; - uxLog(this, c.yellow("Salesforce internal mess... trying with force:source:legacy:push")); - const pullRes = await forceSourcePush(scratchOrgAlias, commandThis, debug, options); - return pullRes; - } else if (stdOut.includes(`'force:source:beta:push' with your existing tracking files`)) { - options.sfdxPushCommand = "force:source:beta:push"; - uxLog(this, c.yellow("Salesforce internal mess... trying with force:source:beta:push")); - const pullRes = await forceSourcePush(scratchOrgAlias, commandThis, debug, options); - return pullRes; - } else if (stdOut.includes(`getaddrinfo EAI_AGAIN`)) { - uxLog(this, c.red(c.bold("The error has been caused by your unstable internet connection. Please Try again !"))); + const stdOut = (e as any).stdout + (e as any).stderr; + if (stdOut.includes(`getaddrinfo EAI_AGAIN`)) { + uxLog(this, c.red(c.bold('The error has been caused by your unstable internet connection. Please Try again !'))); } // Analyze errors const { tips, errLog } = await analyzeDeployErrorLogs(stdOut, true, {}); - uxLog(commandThis, c.red("Sadly there has been push error(s)")); - uxLog(this, c.red("\n" + errLog)); + uxLog(commandThis, c.red('Sadly there has been push error(s)')); + uxLog(this, c.red('\n' + errLog)); uxLog( commandThis, - c.yellow(c.bold(`You may${tips.length > 0 ? " also" : ""} copy-paste errors on google to find how to solve the push issues :)`)), + c.yellow( + c.bold( + `You may${tips.length > 0 ? ' also' : ''} copy-paste errors on google to find how to solve the push issues :)` + ) + ) ); - elapseEnd("force:source:push"); - throw new SfdxError("Deployment failure. Check messages above"); + elapseEnd('project:deploy:start'); + throw new SfError('Deployment failure. Check messages above'); } } -export async function forceSourcePull(scratchOrgAlias: string, debug = false, options: any = {}) { - const sfdxPullCommand = options.sfdxPullCommand || "force:source:pull"; +export async function forceSourcePull(scratchOrgAlias: string, debug = false) { + let pullCommandResult: any; try { - const pullCommand = `sfdx ${sfdxPullCommand} -w 60 --forceoverwrite -u ${scratchOrgAlias}`; - await execCommand(pullCommand, this, { + const pullCommand = `sf project retrieve start --ignore-conflicts -o ${scratchOrgAlias} --wait 60`; + pullCommandResult = await execCommand(pullCommand, this, { fail: true, output: true, debug: debug, }); } catch (e) { // Manage beta/legacy boza - const stdOut = e.stdout + e.stderr; - if (stdOut.includes(`'force:source:legacy:pull' with your existing tracking files`)) { - options.sfdxPullCommand = "force:source:legacy:pull"; - uxLog(this, c.yellow("Salesforce internal mess... trying with force:source:legacy:pull")); - const pullRes = await forceSourcePull(scratchOrgAlias, debug, options); - return pullRes; - } else if (stdOut.includes(`'force:source:beta:pull' with your existing tracking files`)) { - options.sfdxPullCommand = "force:source:beta:pull"; - uxLog(this, c.yellow("Salesforce internal mess... trying with force:source:beta:pull")); - const pullRes = await forceSourcePull(scratchOrgAlias, debug, options); - return pullRes; - } + const stdOut = (e as any).stdout + (e as any).stderr; // Analyze errors const { tips, errLog } = await analyzeDeployErrorLogs(stdOut, true, {}); - uxLog(this, c.red("Sadly there has been pull error(s)")); - uxLog(this, c.red("\n" + errLog)); + uxLog(this, c.red('Sadly there has been pull error(s)')); + uxLog(this, c.red('\n' + errLog)); // List unknown elements from output const forceIgnoreElements = [...stdOut.matchAll(/Entity of type '(.*)' named '(.*)' cannot be found/gm)]; if (forceIgnoreElements.length > 0 && !isCI) { // Propose user to ignore elements const forceIgnoreRes = await prompts({ - type: "multiselect", - message: "If you want to try again with updated .forceignore file, please select elements you want to add, else escape", - name: "value", + type: 'multiselect', + message: + 'If you want to try again with updated .forceignore file, please select elements you want to add, else escape', + name: 'value', choices: forceIgnoreElements.map((forceIgnoreElt) => { return { title: `${forceIgnoreElt[1]}: ${forceIgnoreElt[2]}`, @@ -121,76 +117,103 @@ export async function forceSourcePull(scratchOrgAlias: string, debug = false, op }; }), }); - if (forceIgnoreRes.value.length > 0 && forceIgnoreRes.value[0] !== "exitNow") { - const forceIgnoreFile = "./.forceignore"; - const forceIgnore = await fs.readFile(forceIgnoreFile, "utf-8"); - const forceIgnoreLines = forceIgnore.replace("\r\n", "\n").split("\n"); + if (forceIgnoreRes.value.length > 0 && forceIgnoreRes.value[0] !== 'exitNow') { + const forceIgnoreFile = './.forceignore'; + const forceIgnore = await fs.readFile(forceIgnoreFile, 'utf-8'); + const forceIgnoreLines = forceIgnore.replace('\r\n', '\n').split('\n'); forceIgnoreLines.push(...forceIgnoreRes.value); - await fs.writeFile(forceIgnoreFile, forceIgnoreLines.join("\n") + "\n"); - uxLog(this, "Updated .forceignore file"); + await fs.writeFile(forceIgnoreFile, forceIgnoreLines.join('\n') + '\n'); + uxLog(this, 'Updated .forceignore file'); return await forceSourcePull(scratchOrgAlias, debug); } } - uxLog(this, c.yellow(c.bold(`You may${tips.length > 0 ? " also" : ""} copy-paste errors on google to find how to solve the pull issues :)`))); - throw new SfdxError("Pull failure. Check messages above"); + uxLog( + this, + c.yellow( + c.bold( + `You may${tips.length > 0 ? ' also' : ''} copy-paste errors on google to find how to solve the pull issues :)` + ) + ) + ); + throw new SfError('Pull failure. Check messages above'); } - // Check if some items has to be forced-retrieved because sfdx does not detect updates - const config = await getConfig("project"); + // Check if some items has to be forced-retrieved because SF CLI does not detect updates + const config = await getConfig('project'); if (config.autoRetrieveWhenPull) { - uxLog(this, c.cyan("Retrieving additional sources that are usually forgotten by force:source:pull ...")); - const metadataConstraint = config.autoRetrieveWhenPull.join(", "); - const retrieveCommand = `sfdx force:source:retrieve -w 60 -m "${metadataConstraint}" -u ${scratchOrgAlias}`; + uxLog(this, c.cyan('Retrieving additional sources that are usually forgotten by sf project:retrieve:start ...')); + const metadataConstraint = config.autoRetrieveWhenPull.join(', '); + const retrieveCommand = `sf project retrieve start -m "${metadataConstraint}" -o ${scratchOrgAlias} --wait 60`; await execCommand(retrieveCommand, this, { fail: true, output: true, debug: debug, }); } + + // If there are SharingRules, retrieve all of them to avoid the previous one are deleted (SF Cli strange/buggy behavior) + if (pullCommandResult?.stdout?.includes("SharingRules")) { + uxLog(this, c.yellow('Detected Sharing Rules in the pull: retrieving the whole of them to avoid silly overrides !')); + const sharingRulesNamesMatches = [...pullCommandResult.stdout.matchAll(/([^ \\/]+)\.sharingRules-meta\.xml/gm)]; + for (const match of sharingRulesNamesMatches) { + uxLog(this, c.grey(`Retrieve the whole ${match[1]} SharingRules...`)); + const retrieveCommand = `sf project retrieve start -m "SharingRules:${match[1]}" -o ${scratchOrgAlias} --wait 60`; + await execCommand(retrieveCommand, this, { + fail: true, + output: true, + debug: debug, + }); + } + } } -export async function forceSourceDeploy( +export async function smartDeploy( packageXmlFile: string, check = false, - testlevel = "RunLocalTests", + testlevel = 'RunLocalTests', debugMode = false, commandThis: any = this, - options: any = {}, + options: any = {} ): Promise { - elapseStart("all deployments"); + elapseStart('all deployments'); let quickDeploy = false; const splitDeployments = await buildDeploymentPackageXmls(packageXmlFile, check, debugMode, options); - const messages = []; + const messages: any[] = []; let deployXmlCount = splitDeployments.length; if (deployXmlCount === 0) { - uxLog(this, "No deployment to perform"); + uxLog(this, 'No deployment to perform'); return { messages, quickDeploy, deployXmlCount }; } // Replace quick actions with dummy content in case we have dependencies between Flows & QuickActions await replaceQuickActionsWithDummy(); // Run deployment pre-commands - await executePrePostCommands("commandsPreDeploy", true); + await executePrePostCommands('commandsPreDeploy', { success: true, checkOnly: check, conn: options.conn }); // Process items of deployment plan - uxLog(this, c.cyan("Processing split deployments build from deployment plan...")); + uxLog(this, c.cyan('Processing split deployments build from deployment plan...')); uxLog(this, c.whiteBright(JSON.stringify(splitDeployments, null, 2))); for (const deployment of splitDeployments) { elapseStart(`deploy ${deployment.label}`); // Skip this deployment items if there is nothing to deploy in package.xml - if (deployment.packageXmlFile && (await isPackageXmlEmpty(deployment.packageXmlFile, { ignoreStandaloneParentItems: true }))) { + if ( + deployment.packageXmlFile && + (await isPackageXmlEmpty(deployment.packageXmlFile, { ignoreStandaloneParentItems: true })) + ) { uxLog( commandThis, c.cyan( - `Skipped ${c.bold(deployment.label)} deployment because package.xml is empty or contains only standalone parent items.\n${c.grey( - c.italic("This may be related to filtering using package-no-overwrite.xml or packageDeployOnChange.xml"), - )}`, - ), + `Skipped ${c.bold( + deployment.label + )} deployment because package.xml is empty or contains only standalone parent items.\n${c.grey( + c.italic('This may be related to filtering using package-no-overwrite.xml or packageDeployOnChange.xml') + )}` + ) ); deployXmlCount--; elapseEnd(`deploy ${deployment.label}`); continue; } - let message = ""; + let message = ''; // Wait before deployment item process if necessary if (deployment.waitBefore) { uxLog(commandThis, `Waiting ${deployment.waitBefore} seconds before deployment according to deployment plan`); @@ -200,19 +223,22 @@ export async function forceSourceDeploy( if (deployment.packageXmlFile) { uxLog( commandThis, - c.cyan(`${check ? "Simulating deployment of" : "Deploying"} ${c.bold(deployment.label)} package: ${deployment.packageXmlFile} ...`), + c.cyan( + `${check ? 'Simulating deployment of' : 'Deploying'} ${c.bold(deployment.label)} package: ${deployment.packageXmlFile + } ...` + ) ); // Try QuickDeploy - if (check === false && (process.env?.SFDX_HARDIS_QUICK_DEPLOY || "") !== "false") { + if (check === false && (process.env?.SFDX_HARDIS_QUICK_DEPLOY || '') !== 'false') { const deploymentCheckId = await GitProvider.getDeploymentCheckId(); if (deploymentCheckId) { const quickDeployCommand = - `sfdx force:source:deploy` + - ` --validateddeployrequestid ${deploymentCheckId} ` + - (options.targetUsername ? ` --targetusername ${options.targetUsername}` : "") + - ` --wait ${process.env.SFDX_DEPLOY_WAIT_MINUTES || "60"}` + + `sf project deploy quick` + + ` --job-id ${deploymentCheckId} ` + + (options.targetUsername ? ` -o ${options.targetUsername}` : '') + + ` --wait ${process.env.SFDX_DEPLOY_WAIT_MINUTES || '60'}` + ` --verbose` + - (process.env.SFDX_DEPLOY_DEV_DEBUG ? " --dev-debug" : ""); + (process.env.SFDX_DEPLOY_DEV_DEBUG ? ' --dev-debug' : ''); const quickDeployRes = await execSfdxJson(quickDeployCommand, commandThis, { output: true, debug: debugMode, @@ -220,35 +246,52 @@ export async function forceSourceDeploy( }); if (quickDeployRes.status === 0) { uxLog(commandThis, c.green(`Successfully processed QuickDeploy for deploymentId ${deploymentCheckId}`)); - uxLog(commandThis, c.yellow("If you do not want to use QuickDeploy feature, define env variable SFDX_HARDIS_QUICK_DEPLOY=false")); + uxLog( + commandThis, + c.yellow( + 'If you do not want to use QuickDeploy feature, define env variable SFDX_HARDIS_QUICK_DEPLOY=false' + ) + ); quickDeploy = true; continue; } else { - uxLog(commandThis, c.yellow(`Unable to perform QuickDeploy for deploymentId ${deploymentCheckId}.\n${quickDeployRes.errorMessage}.`)); + uxLog( + commandThis, + c.yellow( + `Unable to perform QuickDeploy for deploymentId ${deploymentCheckId}.\n${quickDeployRes.errorMessage}.` + ) + ); uxLog(commandThis, c.green("Switching back to effective deployment not using QuickDeploy: that's ok :)")); - const isSandboxOrg = await isSandbox(options); - if (isSandboxOrg) { - testlevel = "NoTestRun"; - uxLog(commandThis, c.green("Note: run with NoTestRun to improve perfs as we had previously succeeded to simulate the deployment")); + const isProdOrg = await isProductionOrg(options.targetUsername || "", options); + if (!isProdOrg) { + testlevel = 'NoTestRun'; + uxLog( + commandThis, + c.green( + 'Note: run with NoTestRun to improve perfs as we had previously succeeded to simulate the deployment' + ) + ); } } } } // No QuickDeploy Available, or QuickDeploy failing : try full deploy - const branchConfig = await getConfig("branch"); + const branchConfig = await getConfig('branch'); const deployCommand = - `sfdx force:source:deploy -x "${deployment.packageXmlFile}"` + - ` --wait ${process.env.SFDX_DEPLOY_WAIT_MINUTES || "60"}` + - " --ignorewarnings" + // So it does not fail in for objectTranslations stuff - ` --testlevel ${testlevel}` + - (options.testClasses && testlevel !== "NoTestRun" ? ` --runtests ${options.testClasses}` : "") + - (options.preDestructiveChanges ? ` --predestructivechanges ${options.preDestructiveChanges}` : "") + - (options.postDestructiveChanges ? ` --postdestructivechanges ${options.postDestructiveChanges}` : "") + - (options.targetUsername ? ` --targetusername ${options.targetUsername}` : "") + - (check ? " --checkonly" : "") + - " --verbose" + - (branchConfig?.skipCodeCoverage === true ? "" : " --coverageformatters json-summary") + - (process.env.SFDX_DEPLOY_DEV_DEBUG ? " --dev-debug" : ""); + `sf project deploy` + + (check && testlevel !== 'NoTestRun' ? ' validate' : ' start') + + (check && testlevel === 'NoTestRun' ? ' --dry-run' : '') + // validate with NoTestRun does not work, so use --dry-run + ` --manifest "${deployment.packageXmlFile}"` + + (check === false ? ' --ignore-warnings' : '') + // So it does not fail in for objectTranslations stuff for example + ` --test-level ${testlevel}` + + (options.testClasses && testlevel !== 'NoTestRun' ? ` --tests ${options.testClasses}` : '') + + (options.preDestructiveChanges ? ` --pre-destructive-changes ${options.preDestructiveChanges}` : '') + + (options.postDestructiveChanges ? ` --post-destructive-changes ${options.postDestructiveChanges}` : '') + + (options.targetUsername ? ` -o ${options.targetUsername}` : '') + + (branchConfig?.skipCodeCoverage === true ? '' : ' --coverage-formatters json-summary') + + ' --verbose' + + ` --wait ${process.env.SFDX_DEPLOY_WAIT_MINUTES || '60'}` + + (process.env.SFDX_DEPLOY_DEV_DEBUG ? ' --dev-debug' : ''); let deployRes; try { deployRes = await execCommand(deployCommand, commandThis, { @@ -262,13 +305,13 @@ export async function forceSourceDeploy( } // Set deployment id - await getDeploymentId(deployRes.stdout + deployRes.stderr || ""); + await getDeploymentId(deployRes.stdout + deployRes.stderr || ''); // Check org coverage if found in logs - const orgCoveragePercent = await extractOrgCoverageFromLog(deployRes.stdout + deployRes.stderr || ""); + const orgCoveragePercent = await extractOrgCoverageFromLog(deployRes.stdout + deployRes.stderr || ''); if (orgCoveragePercent) { try { - await checkDeploymentOrgCoverage(orgCoveragePercent, { check: check, testlevel: testlevel }); + await checkDeploymentOrgCoverage(Number(orgCoveragePercent), { check: check, testlevel: testlevel }); } catch (errCoverage) { if (check) { await GitProvider.managePostPullRequestComment(); @@ -279,13 +322,13 @@ export async function forceSourceDeploy( // Handle notif message when there is no apex const existingPrData = globalThis.pullRequestData || {}; const prDataCodeCoverage: any = { - messageKey: existingPrData.messageKey ?? "deployment", - title: (existingPrData.title ?? check) ? "✅ Deployment check success" : "✅ Deployment success", + messageKey: existingPrData.messageKey ?? 'deployment', + title: existingPrData.title ?? check ? '✅ Deployment check success' : '✅ Deployment success', codeCoverageMarkdownBody: branchConfig?.skipCodeCoverage === true - ? "✅⚠️ Code coverage has been skipped for this level" - : "✅ No code coverage: It seems there is not Apex in this project", - deployStatus: "valid", + ? '✅⚠️ Code coverage has been skipped for this level' + : '✅ No code coverage: It seems there is not Apex in this project', + deployStatus: 'valid', }; globalThis.pullRequestData = Object.assign(globalThis.pullRequestData || {}, prDataCodeCoverage); } @@ -294,23 +337,24 @@ export async function forceSourceDeploy( await GitProvider.managePostPullRequestComment(); } - let extraInfo = options?.delta === true ? "DELTA Deployment" : "FULL Deployment"; + let extraInfo = options?.delta === true ? 'DELTA Deployment' : 'FULL Deployment'; if (quickDeploy === true) { - extraInfo += " (using Quick Deploy)"; + extraInfo += ' (using Quick Deploy)'; } // Display deployment status if (deployRes.status === 0) { message = - `[sfdx-hardis] Successfully ${check ? "checked deployment of" : "deployed"} ${c.bold(deployment.label)} to target Salesforce org - ` + - extraInfo; + `[sfdx-hardis] Successfully ${check ? 'checked deployment of' : 'deployed'} ${c.bold( + deployment.label + )} to target Salesforce org - ` + extraInfo; uxLog(commandThis, c.green(message)); if (deployRes?.testCoverageNotBlockingActivated === true) { uxLog( commandThis, c.yellow( - "There is a code coverage issue, but the check is passing by design because you configured testCoverageNotBlocking: true in your branch .sfdx-hardis.yml", - ), + 'There is a code coverage issue, but the check is passing by design because you configured testCoverageNotBlocking: true in your branch .sfdx-hardis.yml' + ) ); } } else { @@ -319,7 +363,7 @@ export async function forceSourceDeploy( await displayDeploymentLink(deployRes.errorMessage, options); } // Restore quickActions after deployment of main package - if (deployment.packageXmlFile.includes("mainPackage.xml")) { + if (deployment.packageXmlFile.includes('mainPackage.xml')) { await restoreQuickActions(); } elapseEnd(`deploy ${deployment.label}`); @@ -337,57 +381,69 @@ export async function forceSourceDeploy( messages.push(message); } // Run deployment post commands - await executePrePostCommands("commandsPostDeploy", true); - elapseEnd("all deployments"); + await executePrePostCommands('commandsPostDeploy', { success: true, checkOnly: check, conn: options.conn }); + elapseEnd('all deployments'); return { messages, quickDeploy, deployXmlCount }; } -async function handleDeployError(e: any, check: boolean, branchConfig: any, commandThis: any, options: any, deployment: any) { - const output: string = e.stdout + e.stderr; +async function handleDeployError( + e: any, + check: boolean, + branchConfig: any, + commandThis: any, + options: any, + deployment: any +) { + const output: string = (e as any).stdout + (e as any).stderr; // Handle coverage error if ignored if ( check === true && branchConfig?.testCoverageNotBlocking === true && - output.includes("=== Test Success") && - !output.includes("Test Failures") && - output.includes("=== Apex Code Coverage") + output.includes('=== Test Success') && + !output.includes('Test Failures') && + output.includes('=== Apex Code Coverage') ) { - uxLog(commandThis, c.yellow(c.bold("Deployment status: Deploy check success & Ignored test coverage error"))); - return { status: 0, stdout: e.stdout, stderr: e.stderr, testCoverageNotBlockingActivated: true }; + uxLog(commandThis, c.yellow(c.bold('Deployment status: Deploy check success & Ignored test coverage error'))); + return { status: 0, stdout: (e as any).stdout, stderr: (e as any).stderr, testCoverageNotBlockingActivated: true }; } // Handle Effective error const { tips, errLog } = await analyzeDeployErrorLogs(output, true, { check: check }); - uxLog(commandThis, c.red(c.bold("Sadly there has been Deployment error(s)"))); - if (process.env?.SFDX_HARDIS_DEPLOY_ERR_COLORS === "false") { - uxLog(this, "\n" + errLog); + uxLog(commandThis, c.red(c.bold('Sadly there has been Deployment error(s)'))); + if (process.env?.SFDX_HARDIS_DEPLOY_ERR_COLORS === 'false') { + uxLog(this, '\n' + errLog); } else { - uxLog(this, c.red("\n" + errLog)); + uxLog(this, c.red('\n' + errLog)); } uxLog( commandThis, - c.yellow(c.bold(`You may${tips.length > 0 ? " also" : ""} copy-paste errors on google to find how to solve the deployment issues :)`)), + c.yellow( + c.bold( + `You may${tips.length > 0 ? ' also' : '' + } copy-paste errors on google to find how to solve the deployment issues :)` + ) + ) ); await displayDeploymentLink(output, options); elapseEnd(`deploy ${deployment.label}`); if (check) { await GitProvider.managePostPullRequestComment(); } - await executePrePostCommands("commandsPostDeploy", false); - throw new SfdxError("Deployment failure. Check messages above"); + await executePrePostCommands('commandsPostDeploy', { success: false, checkOnly: check, conn: options.conn }); + throw new SfError('Deployment failure. Check messages above'); } export function truncateProgressLogLines(rawLog: string) { const rawLogCleaned = rawLog - .replace(/(SOURCE PROGRESS \|.*\n)/gm, "") - .replace(/(MDAPI PROGRESS \|.*\n)/gm, "") - .replace(/(DEPLOY PROGRESS \|.*\n)/gm, ""); + .replace(/(SOURCE PROGRESS \|.*\n)/gm, '') + .replace(/(MDAPI PROGRESS \|.*\n)/gm, '') + .replace(/(DEPLOY PROGRESS \|.*\n)/gm, ''); return rawLogCleaned; } async function getDeploymentId(rawLog: string) { const regex = /Deploy ID: (.*)/gm; if (rawLog && rawLog.match(regex)) { - const deploymentId = regex.exec(rawLog)[1]; + const deploymentId = (regex.exec(rawLog) || [])[1]; globalThis.pullRequestDeploymentId = deploymentId; return deploymentId; } @@ -396,53 +452,68 @@ async function getDeploymentId(rawLog: string) { // Display deployment link in target org async function displayDeploymentLink(rawLog: string, options: any) { - if (process?.env?.SFDX_HARDIS_DISPLAY_DEPLOYMENT_LINK === "true") { - let deploymentUrl = "lightning/setup/DeployStatus/home"; + if (process?.env?.SFDX_HARDIS_DISPLAY_DEPLOYMENT_LINK === 'true') { + let deploymentUrl = 'lightning/setup/DeployStatus/home'; const deploymentId = await getDeploymentId(rawLog); if (deploymentId) { const detailedDeploymentUrl = - "/changemgmt/monitorDeploymentsDetails.apexp?" + encodeURIComponent(`retURL=/changemgmt/monitorDeployment.apexp&asyncId=${deploymentId}`); - deploymentUrl = "lightning/setup/DeployStatus/page?address=" + encodeURIComponent(detailedDeploymentUrl); + '/changemgmt/monitorDeploymentsDetails.apexp?' + + encodeURIComponent(`retURL=/changemgmt/monitorDeployment.apexp&asyncId=${deploymentId}`); + deploymentUrl = 'lightning/setup/DeployStatus/page?address=' + encodeURIComponent(detailedDeploymentUrl); } const openRes = await execSfdxJson( - `sf org open -p ${deploymentUrl} --url-only` + (options.targetUsername ? ` --target-org ${options.targetUsername}` : ""), + `sf org open -p ${deploymentUrl} --url-only` + + (options.targetUsername ? ` --target-org ${options.targetUsername}` : ''), this, { fail: true, output: false, - }, + } + ); + uxLog( + this, + c.yellowBright(`Open deployment status page in org with url: ${c.bold(c.greenBright(openRes?.result?.url))}`) ); - uxLog(this, c.yellowBright(`Open deployment status page in org with url: ${c.bold(c.greenBright(openRes?.result?.url))}`)); } } // In some case we can not deploy the whole package.xml, so let's split it before :) -async function buildDeploymentPackageXmls(packageXmlFile: string, check: boolean, debugMode: boolean, options: any = {}): Promise { +async function buildDeploymentPackageXmls( + packageXmlFile: string, + check: boolean, + debugMode: boolean, + options: any = {} +): Promise { // Check for empty package.xml if (await isPackageXmlEmpty(packageXmlFile)) { - uxLog(this, "Empty package.xml: nothing to deploy"); + uxLog(this, 'Empty package.xml: nothing to deploy'); return []; } const deployOncePackageXml = await buildDeployOncePackageXml(debugMode, options); const deployOnChangePackageXml = await buildDeployOnChangePackageXml(debugMode, options); // Copy main package.xml so it can be dynamically updated before deployment const tmpDeployDir = await createTempDir(); - const mainPackageXmlCopyFileName = path.join(tmpDeployDir, "calculated-package.xml"); + const mainPackageXmlCopyFileName = path.join(tmpDeployDir, 'calculated-package.xml'); await fs.copy(packageXmlFile, mainPackageXmlCopyFileName); const mainPackageXmlItem = { - label: "calculated-package-xml", + label: 'calculated-package-xml', packageXmlFile: mainPackageXmlCopyFileName, order: 0, }; - const config = await getConfig("user"); + const config = await getConfig('user'); // Build list of package.xml according to plan if (config.deploymentPlan && !check) { const deploymentItems = [mainPackageXmlItem]; // Work on deploymentPlan packages before deploying them - const skipSplitPackages = (process.env.SFDX_HARDIS_DEPLOY_IGNORE_SPLIT_PACKAGES || "true") !== "false"; + const skipSplitPackages = (process.env.SFDX_HARDIS_DEPLOY_IGNORE_SPLIT_PACKAGES || 'true') !== 'false'; if (skipSplitPackages === true) { - uxLog(this, c.yellow("Do not split package.xml, as SFDX_HARDIS_DEPLOY_IGNORE_SPLIT_PACKAGES=false has not been found in ENV vars")); + uxLog( + this, + c.yellow( + 'Do not split package.xml, as SFDX_HARDIS_DEPLOY_IGNORE_SPLIT_PACKAGES=false has not been found in ENV vars' + ) + ); } else { for (const deploymentItem of config.deploymentPlan.packages) { if (deploymentItem.packageXmlFile) { @@ -456,26 +527,41 @@ async function buildDeploymentPackageXmls(packageXmlFile: string, check: boolean debugMode: debugMode, keepEmptyTypes: true, }); - await applyPackageXmlFiltering(deploymentItem.packageXmlFile, deployOncePackageXml, deployOnChangePackageXml, debugMode); + await applyPackageXmlFiltering( + deploymentItem.packageXmlFile, + deployOncePackageXml, + deployOnChangePackageXml, + debugMode + ); } deploymentItems.push(deploymentItem); } } - await applyPackageXmlFiltering(mainPackageXmlCopyFileName, deployOncePackageXml, deployOnChangePackageXml, debugMode); + await applyPackageXmlFiltering( + mainPackageXmlCopyFileName, + deployOncePackageXml, + deployOnChangePackageXml, + debugMode + ); // Sort in requested order const deploymentItemsSorted = sortArray(deploymentItems, { - by: ["order", "label"], - order: ["asc", "asc"], + by: ['order', 'label'], + order: ['asc', 'asc'], }); return deploymentItemsSorted; } // Return initial package.xml file minus deployOnce and deployOnChange items else { - await applyPackageXmlFiltering(mainPackageXmlCopyFileName, deployOncePackageXml, deployOnChangePackageXml, debugMode); + await applyPackageXmlFiltering( + mainPackageXmlCopyFileName, + deployOncePackageXml, + deployOnChangePackageXml, + debugMode + ); return [ { - label: "calculated-package-xml", + label: 'calculated-package-xml', packageXmlFile: mainPackageXmlCopyFileName, }, ]; @@ -486,41 +572,63 @@ async function buildDeploymentPackageXmls(packageXmlFile: string, check: boolean async function applyPackageXmlFiltering(packageXml, deployOncePackageXml, deployOnChangePackageXml, debugMode) { // Main packageXml: Remove package-no-overwrite.xml items that are already present in target org if (deployOncePackageXml) { - await removePackageXmlContent(packageXml, deployOncePackageXml, false, { debugMode: debugMode, keepEmptyTypes: true }); + await removePackageXmlContent(packageXml, deployOncePackageXml, false, { + debugMode: debugMode, + keepEmptyTypes: true, + }); } //Main packageXml: Remove packageDeployOnChange.xml items that are not different in target org if (deployOnChangePackageXml) { - await removePackageXmlContent(packageXml, deployOnChangePackageXml, false, { debugMode: debugMode, keepEmptyTypes: true }); + await removePackageXmlContent(packageXml, deployOnChangePackageXml, false, { + debugMode: debugMode, + keepEmptyTypes: true, + }); } } // package-no-overwrite.xml items are deployed only if they are not in the target org async function buildDeployOncePackageXml(debugMode = false, options: any = {}) { - if (process.env.SKIP_PACKAGE_DEPLOY_ONCE === "true") { - uxLog(this, c.yellow("Skipped package-no-overwrite.xml management because of env variable SKIP_PACKAGE_DEPLOY_ONCE='true'")); + if (process.env.SKIP_PACKAGE_DEPLOY_ONCE === 'true') { + uxLog( + this, + c.yellow("Skipped package-no-overwrite.xml management because of env variable SKIP_PACKAGE_DEPLOY_ONCE='true'") + ); return null; } - let packageNoOverwrite = path.resolve("./manifest/package-no-overwrite.xml"); + let packageNoOverwrite = path.resolve('./manifest/package-no-overwrite.xml'); if (!fs.existsSync(packageNoOverwrite)) { - packageNoOverwrite = path.resolve("./manifest/packageDeployOnce.xml"); + packageNoOverwrite = path.resolve('./manifest/packageDeployOnce.xml'); } if (fs.existsSync(packageNoOverwrite)) { - uxLog(this, c.cyan("Handling package-no-overwrite.xml...")); + uxLog(this, c.cyan('Handling package-no-overwrite.xml...')); // If package-no-overwrite.xml is not empty, build target org package.xml and remove its content from packageOnce.xml if (!(await isPackageXmlEmpty(packageNoOverwrite))) { const tmpDir = await createTempDir(); // Build target org package.xml - uxLog(this, c.cyan(`Generating full package.xml from target org to identify its items matching with package-no-overwrite.xml ...`)); - const targetOrgPackageXml = path.join(tmpDir, "packageTargetOrg.xml"); + uxLog( + this, + c.cyan( + `Generating full package.xml from target org to identify its items matching with package-no-overwrite.xml ...` + ) + ); + const targetOrgPackageXml = path.join(tmpDir, 'packageTargetOrg.xml'); await buildOrgManifest(options.targetUsername, targetOrgPackageXml, options.conn); - let calculatedPackageNoOverwrite = path.join(tmpDir, "package-no-overwrite.xml"); + let calculatedPackageNoOverwrite = path.join(tmpDir, 'package-no-overwrite.xml'); await fs.copy(packageNoOverwrite, calculatedPackageNoOverwrite); // Keep in deployOnce.xml only what is necessary to deploy - await removePackageXmlContent(calculatedPackageNoOverwrite, targetOrgPackageXml, true, { debugMode: debugMode, keepEmptyTypes: false }); - await fs.copy(calculatedPackageNoOverwrite, path.join(tmpDir, "calculated-package-no-overwrite.xml")); - calculatedPackageNoOverwrite = path.join(tmpDir, "calculated-package-no-overwrite.xml"); - uxLog(this, c.grey(`calculated-package-no-overwrite.xml with only items that already exist in target org: ${calculatedPackageNoOverwrite}`)); + await removePackageXmlContent(calculatedPackageNoOverwrite, targetOrgPackageXml, true, { + debugMode: debugMode, + keepEmptyTypes: false, + }); + await fs.copy(calculatedPackageNoOverwrite, path.join(tmpDir, 'calculated-package-no-overwrite.xml')); + calculatedPackageNoOverwrite = path.join(tmpDir, 'calculated-package-no-overwrite.xml'); + uxLog( + this, + c.grey( + `calculated-package-no-overwrite.xml with only items that already exist in target org: ${calculatedPackageNoOverwrite}` + ) + ); // Check if there is still something in calculated-package-no-overwrite.xml if (!(await isPackageXmlEmpty(calculatedPackageNoOverwrite))) { return calculatedPackageNoOverwrite; @@ -532,58 +640,72 @@ async function buildDeployOncePackageXml(debugMode = false, options: any = {}) { // packageDeployOnChange.xml items are deployed only if they have changed in target org export async function buildDeployOnChangePackageXml(debugMode: boolean, options: any = {}) { - if (process.env.SKIP_PACKAGE_DEPLOY_ON_CHANGE === "true") { - uxLog(this, c.yellow("Skipped packageDeployOnChange.xml management because of env variable SKIP_PACKAGE_DEPLOY_ON_CHANGE='true'")); + if (process.env.SKIP_PACKAGE_DEPLOY_ON_CHANGE === 'true') { + uxLog( + this, + c.yellow( + "Skipped packageDeployOnChange.xml management because of env variable SKIP_PACKAGE_DEPLOY_ON_CHANGE='true'" + ) + ); return null; } // Check if packageDeployOnChange.xml is defined - const packageDeployOnChangePath = "./manifest/packageDeployOnChange.xml"; + const packageDeployOnChangePath = './manifest/packageDeployOnChange.xml'; if (!fs.existsSync(packageDeployOnChangePath)) { return null; } // Retrieve sfdx sources in local git repo await execCommand( - `sfdx force:source:retrieve -x ${packageDeployOnChangePath}` + (options.targetUsername ? ` -u ${options.targetUsername}` : ""), + `sf project retrieve start --manifest ${packageDeployOnChangePath}` + + (options.targetUsername ? ` --target-org ${options.targetUsername}` : ''), this, { fail: true, output: true, debug: debugMode, - }, + } ); // Do not call delta if no updated file has been retrieved const hasGitLocalUpdates = await gitHasLocalUpdates(); if (hasGitLocalUpdates === false) { - uxLog(this, c.grey("No diff retrieved from packageDeployOnChange.xml")); + uxLog(this, c.grey('No diff retrieved from packageDeployOnChange.xml')); return null; } - // "Temporarily" commit updates so sfdx git delta can build diff package.xml - await git().addConfig("user.email", "bot@hardis.com", false, "global"); - await git().addConfig("user.name", "Hardis", false, "global"); - await git().add("--all"); - await git().commit('"temp"', ["--no-verify"]); + // "Temporarily" commit updates so sfdx-git-delta can build diff package.xml + await git().addConfig('user.email', 'bot@hardis.com', false, 'global'); + await git().addConfig('user.name', 'Hardis', false, 'global'); + await git().add('--all'); + await git().commit('"temp"', ['--no-verify']); // Generate package.xml git delta const tmpDir = await createTempDir(); - const gitDeltaCommandRes = await callSfdxGitDelta("HEAD~1", "HEAD", tmpDir, { debug: debugMode }); + const gitDeltaCommandRes = await callSfdxGitDelta('HEAD~1', 'HEAD', tmpDir, { debug: debugMode }); // Now that the diff is computed, we can dump the temporary commit - await git().reset(ResetMode.HARD, ["HEAD~1"]); + await git().reset(ResetMode.HARD, ['HEAD~1']); // Check git delta is ok - const diffPackageXml = path.join(tmpDir, "package", "package.xml"); + const diffPackageXml = path.join(tmpDir, 'package', 'package.xml'); if (gitDeltaCommandRes?.status !== 0 || !fs.existsSync(diffPackageXml)) { - throw new SfdxError("Error while running sfdx git delta:\n" + JSON.stringify(gitDeltaCommandRes)); + throw new SfError('Error while running sfdx-git-delta:\n' + JSON.stringify(gitDeltaCommandRes)); } // Remove from original packageDeployOnChange the items that has not been updated - const packageXmlDeployOnChangeToUse = path.join(tmpDir, "packageDeployOnChange.xml"); + const packageXmlDeployOnChangeToUse = path.join(tmpDir, 'packageDeployOnChange.xml'); await fs.copy(packageDeployOnChangePath, packageXmlDeployOnChangeToUse); - await removePackageXmlContent(packageXmlDeployOnChangeToUse, diffPackageXml, false, { debugMode: debugMode, keepEmptyTypes: false }); - uxLog(this, c.grey(`packageDeployOnChange.xml filtered to keep only metadatas that have changed: ${packageXmlDeployOnChangeToUse}`)); + await removePackageXmlContent(packageXmlDeployOnChangeToUse, diffPackageXml, false, { + debugMode: debugMode, + keepEmptyTypes: false, + }); + uxLog( + this, + c.grey( + `packageDeployOnChange.xml filtered to keep only metadatas that have changed: ${packageXmlDeployOnChangeToUse}` + ) + ); // Return result return packageXmlDeployOnChangeToUse; } @@ -593,18 +715,25 @@ export async function removePackageXmlContent( packageXmlFile: string, packageXmlFileToRemove: string, removedOnly = false, - options = { debugMode: false, keepEmptyTypes: false }, + options = { debugMode: false, keepEmptyTypes: false } ) { if (removedOnly === false) { - uxLog(this, c.cyan(`Removing ${c.green(path.basename(packageXmlFileToRemove))} items from ${c.green(path.basename(packageXmlFile))}...`)); + uxLog( + this, + c.cyan( + `Removing ${c.green(path.basename(packageXmlFileToRemove))} items from ${c.green( + path.basename(packageXmlFile) + )}...` + ) + ); } else { uxLog( this, c.cyan( `Keeping ${c.green(path.basename(packageXmlFileToRemove))} items matching with ${c.green( - path.basename(packageXmlFile), - )} (and remove the rest)...`, - ), + path.basename(packageXmlFile) + )} (and remove the rest)...` + ) ); } await removePackageXmlFilesContent(packageXmlFile, packageXmlFileToRemove, { @@ -616,31 +745,34 @@ export async function removePackageXmlContent( } // Deploy destructive changes -export async function deployDestructiveChanges(packageDeletedXmlFile: string, options: any = { debug: false, check: false }, commandThis: any) { - // Create empty deployment file because of sfdx limitation +export async function deployDestructiveChanges( + packageDeletedXmlFile: string, + options: any = { debug: false, check: false }, + commandThis: any +) { + // Create empty deployment file because of SF CLI limitation // cf https://gist.github.com/benahm/b590ecf575ff3c42265425233a2d727e uxLog(commandThis, c.cyan(`Deploying destructive changes from file ${path.resolve(packageDeletedXmlFile)}`)); const tmpDir = await createTempDir(); - const emptyPackageXmlFile = path.join(tmpDir, "package.xml"); + const emptyPackageXmlFile = path.join(tmpDir, 'package.xml'); await fs.writeFile( emptyPackageXmlFile, ` ${CONSTANTS.API_VERSION} `, - "utf8", + 'utf8' ); - await fs.copy(packageDeletedXmlFile, path.join(tmpDir, "destructiveChanges.xml")); + await fs.copy(packageDeletedXmlFile, path.join(tmpDir, 'destructiveChanges.xml')); const deployDelete = - `sfdx force:mdapi:deploy -d ${tmpDir}` + - ` --wait ${process.env.SFDX_DEPLOY_WAIT_MINUTES || "60"}` + - ` --testlevel ${options.testLevel || "NoTestRun"}` + - " --ignorewarnings" + // So it does not fail in case metadata is already deleted - (options.targetUsername ? ` --targetusername ${options.targetUsername}` : "") + - (options.check ? " --checkonly" : "") + - (options.debug ? " --verbose" : ""); + `sf project deploy ${options.check ? 'validate' : 'start'} --metadata-dir ${tmpDir}` + + ` --wait ${process.env.SFDX_DEPLOY_WAIT_MINUTES || '60'}` + + ` --test-level ${options.testLevel || 'NoTestRun'}` + + ' --ignore-warnings' + // So it does not fail in case metadata is already deleted + (options.targetUsername ? ` --target-org ${options.targetUsername}` : '') + + (options.debug ? ' --verbose' : ''); // Deploy destructive changes - let deployDeleteRes = null; + let deployDeleteRes: any = {}; try { deployDeleteRes = await execCommand(deployDelete, commandThis, { output: true, @@ -648,52 +780,50 @@ export async function deployDestructiveChanges(packageDeletedXmlFile: string, op fail: true, }); } catch (e) { - const { errLog } = await analyzeDeployErrorLogs(e.stdout + e.stderr, true, {}); - uxLog(this, c.red("Sadly there has been destruction error(s)")); - uxLog(this, c.red("\n" + errLog)); + const { errLog } = await analyzeDeployErrorLogs((e as any).stdout + (e as any).stderr, true, {}); + uxLog(this, c.red('Sadly there has been destruction error(s)')); + uxLog(this, c.red('\n' + errLog)); uxLog( this, c.yellow( c.bold( - "That could be a false positive, as in real deployment, the package.xml deployment will be committed before the use of destructiveChanges.xml", - ), - ), + 'That could be a false positive, as in real deployment, the package.xml deployment will be committed before the use of destructiveChanges.xml' + ) + ) ); - throw new SfdxError("Error while deploying destructive changes"); + throw new SfError('Error while deploying destructive changes'); } await fs.remove(tmpDir); - let deleteMsg = ""; + let deleteMsg = ''; if (deployDeleteRes.status === 0) { - deleteMsg = `[sfdx-hardis] Successfully ${options.check ? "checked deployment of" : "deployed"} destructive changes to Salesforce org`; + deleteMsg = `[sfdx-hardis] Successfully ${options.check ? 'checked deployment of' : 'deployed' + } destructive changes to Salesforce org`; uxLog(commandThis, c.green(deleteMsg)); } else { - deleteMsg = "[sfdx-hardis] Unable to deploy destructive changes to Salesforce org"; + deleteMsg = '[sfdx-hardis] Unable to deploy destructive changes to Salesforce org'; uxLog(commandThis, c.red(deployDeleteRes.errorMessage)); } } export async function deployMetadatas( options: any = { - deployDir: ".", - testlevel: "RunLocalTests", + deployDir: '.', + testlevel: 'RunLocalTests', check: false, debug: false, - soap: false, targetUsername: null, tryOnce: false, - }, + } ) { // Perform deployment const deployCommand = - "sfdx force:mdapi:deploy" + - ` --deploydir ${options.deployDir || "."}` + - ` --wait ${process.env.SFDX_DEPLOY_WAIT_MINUTES || "60"}` + - ` --testlevel ${options.testlevel || "RunLocalTests"}` + - ` --apiversion ${options.apiVersion || CONSTANTS.API_VERSION}` + - (options.soap ? " --soapdeploy" : "") + - (options.check ? " --checkonly" : "") + - (options.targetUsername ? ` --targetusername ${options.targetUsername}` : "") + - " --verbose"; + `sf project deploy ${options.check ? 'validate' : 'start'}` + + ` --metadata-dir ${options.deployDir || '.'}` + + ` --wait ${process.env.SFDX_DEPLOY_WAIT_MINUTES || '60'}` + + ` --test-level ${options.testlevel || 'RunLocalTests'}` + + ` --api-version ${options.apiVersion || CONSTANTS.API_VERSION}` + + (options.targetUsername ? ` --target-org ${options.targetUsername}` : '') + + ' --verbose'; let deployRes; try { deployRes = await execCommand(deployCommand, this, { @@ -703,23 +833,27 @@ export async function deployMetadatas( }); } catch (e) { // workaround if --soapdeploy is not available - if (JSON.stringify(e).includes("--soapdeploy") && !options.tryOnce === true) { + if (JSON.stringify(e).includes('--soapdeploy') && !options.tryOnce === true) { uxLog(this, c.yellow("This may be a error with a workaround... let's try it :)")); try { - deployRes = await execCommand(deployCommand.replace(" --soapdeploy", ""), this, { + deployRes = await execCommand(deployCommand.replace(' --soapdeploy', ''), this, { output: true, debug: options.debug, fail: true, }); } catch (e2) { - if (JSON.stringify(e2).includes("NoTestRun")) { + if (JSON.stringify(e2).includes('NoTestRun')) { // Another workaround: try running tests uxLog(this, c.yellow("This may be again an error with a workaround... let's make a last attempt :)")); - deployRes = await execCommand(deployCommand.replace(" --soapdeploy", "").replace("NoTestRun", "RunLocalTests"), this, { - output: true, - debug: options.debug, - fail: true, - }); + deployRes = await execCommand( + deployCommand.replace(' --soapdeploy', '').replace('NoTestRun', 'RunLocalTests'), + this, + { + output: true, + debug: options.debug, + fail: true, + } + ); } else { throw e2; } @@ -735,13 +869,16 @@ let quickActionsBackUpFolder: string; // Replace QuickAction content with Dummy content that will always pass async function replaceQuickActionsWithDummy() { - if (process.env.CI_DEPLOY_QUICK_ACTIONS_DUMMY === "true") { - uxLog(this, c.cyan("Replacing QuickActions content with Dummy content that will always pass...")); + if (process.env.CI_DEPLOY_QUICK_ACTIONS_DUMMY === 'true') { + uxLog(this, c.cyan('Replacing QuickActions content with Dummy content that will always pass...')); quickActionsBackUpFolder = await createTempDir(); - const patternQuickActions = process.cwd() + "/force-app/" + `**/quickActions/*__c.*.quickAction-meta.xml`; + const patternQuickActions = process.cwd() + '/force-app/' + `**/quickActions/*__c.*.quickAction-meta.xml`; const matchQuickActions = await glob(patternQuickActions, { cwd: process.cwd() }); for (const quickActionFile of matchQuickActions) { - const tmpBackupFile = path.join(quickActionsBackUpFolder, path.resolve(quickActionFile).replace(path.resolve(process.cwd()), "")); + const tmpBackupFile = path.join( + quickActionsBackUpFolder, + path.resolve(quickActionFile).replace(path.resolve(process.cwd()), '') + ); await fs.ensureDir(path.dirname(tmpBackupFile)); await fs.copy(quickActionFile, tmpBackupFile); await fs.writeFile( @@ -754,90 +891,99 @@ async function replaceQuickActionsWithDummy() { false LightningComponent 100 -`, +` ); - uxLog(this, c.grey("Backuped and replaced " + quickActionFile)); + uxLog(this, c.grey('Backuped and replaced ' + quickActionFile)); } } } // Restore original QuickActions async function restoreQuickActions() { - if (process.env.CI_DEPLOY_QUICK_ACTIONS_DUMMY === "true") { - const patternQuickActionsBackup = quickActionsBackUpFolder + "/force-app/" + `**/quickActions/*.quickAction-meta.xml`; + if (process.env.CI_DEPLOY_QUICK_ACTIONS_DUMMY === 'true') { + const patternQuickActionsBackup = + quickActionsBackUpFolder + '/force-app/' + `**/quickActions/*.quickAction-meta.xml`; const matchQuickActions = await glob(patternQuickActionsBackup, { cwd: process.cwd(), }); for (const quickActionFile of matchQuickActions) { - const prevFileName = path.resolve(quickActionFile).replace(path.resolve(quickActionsBackUpFolder), path.resolve(process.cwd())); + const prevFileName = path + .resolve(quickActionFile) + .replace(path.resolve(quickActionsBackUpFolder), path.resolve(process.cwd())); await fs.copy(quickActionFile, prevFileName); - uxLog(this, c.grey("Restored " + quickActionFile)); + uxLog(this, c.grey('Restored ' + quickActionFile)); } } } // Build target org package.xml manifest -export async function buildOrgManifest(targetOrgUsernameAlias, packageXmlOutputFile = null, conn = null) { +export async function buildOrgManifest( + targetOrgUsernameAlias, + packageXmlOutputFile: string | null = null, + conn: any | null = null +) { // Manage file name if (packageXmlOutputFile === null) { const tmpDir = await createTempDir(); uxLog(this, c.cyan(`Generating full package.xml from target org ${targetOrgUsernameAlias}...`)); - packageXmlOutputFile = path.join(tmpDir, "packageTargetOrg.xml"); + packageXmlOutputFile = path.join(tmpDir, 'packageTargetOrg.xml'); } const manifestName = path.basename(packageXmlOutputFile); const manifestDir = path.dirname(packageXmlOutputFile); // Get default org if not sent as argument (should not happen but better safe than sorry) - if (targetOrgUsernameAlias == null || targetOrgUsernameAlias == "") { + if (targetOrgUsernameAlias == null || targetOrgUsernameAlias == '') { const currentOrg = await MetadataUtils.getCurrentOrg(); if (currentOrg == null) { - throw new SfdxError("You should call buildOrgManifest while having a default org set !"); + throw new SfError('You should call buildOrgManifest while having a default org set !'); } targetOrgUsernameAlias = currentOrg.username; } if (isSfdxProject()) { // Use sfdx manifest build in current project await execCommand( - `sfdx force:source:manifest:create` + - ` --manifestname ${manifestName}` + - ` --outputdir ${path.resolve(manifestDir)}` + - ` --includepackages managed,unlocked` + - ` --fromorg ${targetOrgUsernameAlias}`, + `sf project generate manifest` + + ` --name ${manifestName}` + + ` --output-dir ${path.resolve(manifestDir)}` + + ` --include-packages managed,unlocked` + + ` --from-org ${targetOrgUsernameAlias}`, this, { fail: true, debug: process.env.DEBUG, output: true, - }, + } ); } else { const tmpDirSfdxProject = await createTempDir(); await createBlankSfdxProject(tmpDirSfdxProject); // Use sfdx manifest build in dummy project await execCommand( - `sfdx force:source:manifest:create` + - ` --manifestname ${manifestName}` + - ` --outputdir ${path.resolve(manifestDir)}` + - ` --includepackages managed,unlocked` + - ` --fromorg ${targetOrgUsernameAlias}`, + `sf project generate manifest` + + ` --name ${manifestName}` + + ` --output-dir ${path.resolve(manifestDir)}` + + ` --include-packages managed,unlocked` + + ` --from-org ${targetOrgUsernameAlias}`, this, { fail: true, - cwd: path.join(tmpDirSfdxProject, "sfdx-hardis-blank-project"), + cwd: path.join(tmpDirSfdxProject, 'sfdx-hardis-blank-project'), debug: process.env.DEBUG, output: true, - }, + } ); } const packageXmlFull = packageXmlOutputFile; if (!fs.existsSync(packageXmlFull)) { - throw new SfdxError( - c.red("[sfdx-hardis] Unable to generate package.xml. This is probably an auth issue or a Salesforce technical issue, please try again later"), + throw new SfError( + c.red( + '[sfdx-hardis] Unable to generate package.xml. This is probably an auth issue or a Salesforce technical issue, please try again later' + ) ); } - // Add Elements that are not returned by sfdx command + // Add Elements that are not returned by SF CLI command if (conn) { - uxLog(this, c.grey("Looking for package.xml elements that are not returned by manifest create command...")); - const mdTypes = [{ type: "ListView" }, { type: "CustomLabel" }]; + uxLog(this, c.grey('Looking for package.xml elements that are not returned by manifest create command...')); + const mdTypes = [{ type: 'ListView' }, { type: 'CustomLabel' }]; const mdList = await conn.metadata.list(mdTypes, CONSTANTS.API_VERSION); const parsedPackageXml = await parseXmlFile(packageXmlFull); for (const element of mdList) { @@ -847,7 +993,9 @@ export async function buildOrgManifest(targetOrgUsernameAlias, packageXmlOutputF const members = matchTypes[0].members || []; members.push(element.fullName); matchTypes[0].members = members.sort(); - parsedPackageXml.Package.types = parsedPackageXml.Package.types.map((type) => (type.name[0] === matchTypes[0].name ? matchTypes[0] : type)); + parsedPackageXml.Package.types = parsedPackageXml.Package.types.map((type) => + type.name[0] === matchTypes[0].name ? matchTypes[0] : type + ); } else { // Create new type const newType = { @@ -859,12 +1007,12 @@ export async function buildOrgManifest(targetOrgUsernameAlias, packageXmlOutputF } // Complete with missing WaveDataflow Ids build from WaveRecipe Ids - const waveRecipeTypeList = parsedPackageXml.Package.types.filter((type) => type.name[0] === "WaveRecipe"); + const waveRecipeTypeList = parsedPackageXml.Package.types.filter((type) => type.name[0] === 'WaveRecipe'); if (waveRecipeTypeList.length === 1) { const waveRecipeType = waveRecipeTypeList[0]; const waveRecipeTypeMembers = waveRecipeType.members || []; - const waveDataFlowTypeList = parsedPackageXml.Package.types.filter((type) => type.name[0] === "WaveDataflow"); - let waveDataFlowType = { name: ["WaveDataflow"], members: [] }; + const waveDataFlowTypeList = parsedPackageXml.Package.types.filter((type) => type.name[0] === 'WaveDataflow'); + let waveDataFlowType: any = { name: ['WaveDataflow'], members: [] }; if (waveDataFlowTypeList.length === 1) { waveDataFlowType = waveDataFlowTypeList[0]; } @@ -877,7 +1025,9 @@ export async function buildOrgManifest(targetOrgUsernameAlias, packageXmlOutputF waveDataFlowType.members.sort(); // Update type if (waveDataFlowTypeList.length === 1) { - parsedPackageXml.Package.types = parsedPackageXml.Package.types.map((type) => (type.name[0] === "WaveDataflow" ? waveDataFlowType : type)); + parsedPackageXml.Package.types = parsedPackageXml.Package.types.map((type) => + type.name[0] === 'WaveDataflow' ? waveDataFlowType : type + ); } // Add type else { @@ -886,36 +1036,75 @@ export async function buildOrgManifest(targetOrgUsernameAlias, packageXmlOutputF } // Delete stuff we don't want - parsedPackageXml.Package.types = parsedPackageXml.Package.types.filter((type) => !["CustomLabels"].includes(type.name[0])); + parsedPackageXml.Package.types = parsedPackageXml.Package.types.filter( + (type) => !['CustomLabels'].includes(type.name[0]) + ); await writeXmlFile(packageXmlFull, parsedPackageXml); } return packageXmlFull; } -export async function executePrePostCommands(property: "commandsPreDeploy" | "commandsPostDeploy", success = true) { - const branchConfig = await getConfig("branch"); +export async function executePrePostCommands(property: 'commandsPreDeploy' | 'commandsPostDeploy', options: { success: boolean, checkOnly: boolean, conn: Connection }) { + const branchConfig = await getConfig('branch'); const commands = branchConfig[property] || []; if (commands.length === 0) { uxLog(this, c.grey(`No ${property} found to run`)); return; } - uxLog(this, c.cyan(`Running ${property} found in .sfdx-hardis.yml configuration...`)); + uxLog(this, c.cyan(`Processing ${property} found in .sfdx-hardis.yml configuration...`)); for (const cmd of commands) { - if (success === false && cmd.skipIfError === true) { + // If if skipIfError is true and deployment failed + if (options.success === false && cmd.skipIfError === true) { uxLog(this, c.yellow(`Skipping skipIfError=true command [${cmd.id}]: ${cmd.label}`)); + continue; } + // Skip if we are in another context than the requested one + const cmdContext = cmd.context || "all"; + if (cmdContext === "check-deployment-only" && options.checkOnly === false) { + uxLog(this, c.grey(`Skipping check-deployment-only command as we are in process deployment mode [${cmd.id}]: ${cmd.label}`)); + continue; + } + if (cmdContext === "process-deployment-only" && options.checkOnly === true) { + uxLog(this, c.grey(`Skipping process-deployment-only command as we are in check deployment mode [${cmd.id}]: ${cmd.label}`)); + continue; + } + const runOnlyOnceByOrg = cmd.runOnlyOnceByOrg || false; + if (runOnlyOnceByOrg) { + await checkSfdxHardisTraceAvailable(options.conn); + const commandTraceQuery = `SELECT Id,CreatedDate FROM SfdxHardisTrace__c WHERE Type__c='${property}' AND Key__c='${cmd.id}' LIMIT 1`; + const commandTraceRes = await soqlQuery(commandTraceQuery, options.conn); + if (commandTraceRes?.records?.length > 0) { + uxLog(this, c.grey(`Skipping command [${cmd.id}]: ${cmd.label} because it has been defined with runOnlyOnceByOrg and has already been run on ${commandTraceRes.records[0].CreatedDate}`)); + continue; + } + } + // Run command uxLog(this, c.cyan(`Running [${cmd.id}]: ${cmd.label}`)); - await execCommand(cmd.command, this, { fail: false, output: true }); + const commandRes = await execCommand(cmd.command, this, { fail: false, output: true }); + if (commandRes.status === 0 && runOnlyOnceByOrg) { + const hardisTraceRecord = { + Name: property + "--" + cmd.id, + Type__c: property, + Key__c: cmd.id + } + const insertRes = await options.conn.insert("SfdxHardisTrace__c", [hardisTraceRecord]); + if (insertRes[0].success) { + uxLog(this, c.green(`Stored SfdxHardisTrace__c entry ${insertRes[0].id} with command [${cmd.id}] so it is not run again in the future (runOnlyOnceByOrg: true)`)); + } + else { + uxLog(this, c.red(`Error storing SfdxHardisTrace__c entry :` + JSON.stringify(insertRes, null, 2))); + } + } } } export async function extractOrgCoverageFromLog(stdout) { - let orgCoverage = null; + let orgCoverage: number | null = null; // Get from output text const fromTest = /Org Wide Coverage *(.*)/.exec(stdout); if (fromTest && fromTest[1]) { - orgCoverage = parseFloat(fromTest[1].replace("%", "")); + orgCoverage = parseFloat(fromTest[1].replace('%', '')); } /* jscpd:ignore-start */ try { @@ -924,33 +1113,35 @@ export async function extractOrgCoverageFromLog(stdout) { } } catch (e) { uxLog(this, c.yellow(`Warning: unable to convert ${orgCoverage} into string`)); - uxLog(this, c.gray(e.message)); + uxLog(this, c.gray((e as Error).message)); } /* jscpd:ignore-end */ // Get from output file const writtenToPath = /written to (.*coverage)/.exec(stdout); if (writtenToPath && writtenToPath[1]) { const jsonFile = path - .resolve(process.cwd() + path.sep + writtenToPath[1].replace(/\\/g, "/") + path.sep + "coverage-summary.json") - .replace(/\\/g, "/"); + .resolve(process.cwd() + path.sep + writtenToPath[1].replace(/\\/g, '/') + path.sep + 'coverage-summary.json') + .replace(/\\/g, '/'); if (fs.existsSync(jsonFile)) { - const coverageInfo = JSON.parse(fs.readFileSync(jsonFile, "utf-8")); + const coverageInfo = JSON.parse(fs.readFileSync(jsonFile, 'utf-8')); orgCoverage = coverageInfo?.total?.lines?.pct ?? null; try { - if (orgCoverage && orgCoverage.toFixed(2) > 0.0) { + if (orgCoverage && Number(orgCoverage.toFixed(2)) > 0.0) { return orgCoverage.toFixed(2); } } catch (e) { uxLog(this, c.yellow(`Warning: unable to convert ${orgCoverage} into string`)); - uxLog(this, c.gray(e.message)); + uxLog(this, c.gray((e as Error).message)); } } } uxLog( this, c.italic( - c.grey("Unable to get org coverage from results. Maybe try to add --coverageformatters json-summary to your call to force:source:deploy ?"), - ), + c.grey( + 'Unable to get org coverage from results. Maybe try to add --coverage-formatters json-summary to your call to sf project deploy start ?' + ) + ) ); return null; } @@ -958,9 +1149,10 @@ export async function extractOrgCoverageFromLog(stdout) { // Check if min org coverage is reached export async function checkDeploymentOrgCoverage(orgCoverage: number, options: any) { // RunSpecifiedTests will not return org wide coverage, using dynamic text - const codeCoverageText = !options.testlevel || options.testlevel !== "RunSpecifiedTests" ? "code coverage (org wide)" : "code coverage"; + const codeCoverageText = + !options.testlevel || options.testlevel !== 'RunSpecifiedTests' ? 'code coverage (org wide)' : 'code coverage'; - const config = await getConfig("branch"); + const config = await getConfig('branch'); // Parse and validate minimum coverage setting, defaults to 75% const minCoverageConf = @@ -968,64 +1160,86 @@ export async function checkDeploymentOrgCoverage(orgCoverage: number, options: a process.env.APEX_TESTS_MIN_COVERAGE || config.apexTestsMinCoverageOrgWide || config.apexTestsMinCoverage || - "75.00"; + '75.00'; const minCoverage = parseFloat(minCoverageConf); if (isNaN(minCoverage)) { - throw new SfdxError(`[sfdx-hardis] Invalid minimum coverage configuration: ${minCoverageConf}`); + throw new SfError(`[sfdx-hardis] Invalid minimum coverage configuration: ${minCoverageConf}`); } if (minCoverage < 75.0) { - throw new SfdxError(`[sfdx-hardis] Good try, hacker, but minimum ${codeCoverageText} can't be less than 75% :)`); + throw new SfError(`[sfdx-hardis] Good try, hacker, but minimum ${codeCoverageText} can't be less than 75% :)`); } if (orgCoverage < minCoverage) { if (config?.testCoverageNotBlocking === true) { - await updatePullRequestResultCoverage("invalid_ignored", orgCoverage, minCoverage, options); + await updatePullRequestResultCoverage('invalid_ignored', orgCoverage, minCoverage, options); } else { - await updatePullRequestResultCoverage("invalid", orgCoverage, minCoverage, options); - throw new SfdxError(`[sfdx-hardis][apextest] Test run ${codeCoverageText} ${orgCoverage}% should be greater than ${minCoverage}%`); + await updatePullRequestResultCoverage('invalid', orgCoverage, minCoverage, options); + throw new SfError( + `[sfdx-hardis][apextest] Test run ${codeCoverageText} ${orgCoverage}% should be greater than ${minCoverage}%` + ); } } else { - await updatePullRequestResultCoverage("valid", orgCoverage, minCoverage, options); - uxLog(this, c.cyan(`[apextest] Test run ${codeCoverageText} ${c.bold(c.green(orgCoverage))}% is greater than ${c.bold(minCoverage)}%`)); + await updatePullRequestResultCoverage('valid', orgCoverage, minCoverage, options); + uxLog( + this, + c.cyan( + `[apextest] Test run ${codeCoverageText} ${c.bold(c.green(orgCoverage))}% is greater than ${c.bold( + minCoverage + )}%` + ) + ); } } async function checkDeploymentErrors(e, options, commandThis = null) { - const { tips, errLog } = await analyzeDeployErrorLogs(e.stdout + e.stderr, true, options); - uxLog(commandThis, c.red(c.bold("Sadly there has been Metadata deployment error(s)..."))); - uxLog(this, c.red("\n" + errLog)); + const { tips, errLog } = await analyzeDeployErrorLogs((e as any).stdout + (e as any).stderr, true, options); + uxLog(commandThis, c.red(c.bold('Sadly there has been Metadata deployment error(s)...'))); + uxLog(this, c.red('\n' + errLog)); uxLog( commandThis, - c.yellow(c.bold(`You may${tips.length > 0 ? " also" : ""} copy-paste errors on google to find how to solve the metadata deployment issues :)`)), + c.yellow( + c.bold( + `You may${tips.length > 0 ? ' also' : '' + } copy-paste errors on google to find how to solve the metadata deployment issues :)` + ) + ) ); - await displayDeploymentLink(e.stdout + e.stderr, options); + await displayDeploymentLink((e as any).stdout + (e as any).stderr, options); // Post pull requests comments if necessary if (options.check) { await GitProvider.managePostPullRequestComment(); } - throw new SfdxError("Metadata deployment failure. Check messages above"); + throw new SfError('Metadata deployment failure. Check messages above'); } // This data will be caught later to build a pull request message -async function updatePullRequestResultCoverage(coverageStatus: string, orgCoverage: number, orgCoverageTarget: number, options: any) { +async function updatePullRequestResultCoverage( + coverageStatus: string, + orgCoverage: number, + orgCoverageTarget: number, + options: any +) { const existingPrData = globalThis.pullRequestData || {}; const prDataCodeCoverage: any = { - messageKey: existingPrData.messageKey ?? "deployment", - title: (existingPrData.title ?? options.check) ? "✅ Deployment check success" : "✅ Deployment success", - codeCoverageMarkdownBody: "Code coverage is valid", + messageKey: existingPrData.messageKey ?? 'deployment', + title: existingPrData.title ?? options.check ? '✅ Deployment check success' : '✅ Deployment success', + codeCoverageMarkdownBody: 'Code coverage is valid', deployStatus: existingPrData ?? coverageStatus, }; // Code coverage failure - if (coverageStatus === "invalid") { - prDataCodeCoverage.title = existingPrData.deployStatus === "valid" ? "❌ Deployment failed: Code coverage error" : prDataCodeCoverage.title; + if (coverageStatus === 'invalid') { + prDataCodeCoverage.title = + existingPrData.deployStatus === 'valid' ? '❌ Deployment failed: Code coverage error' : prDataCodeCoverage.title; prDataCodeCoverage.codeCoverageMarkdownBody = deployCodeCoverageToMarkdown(orgCoverage, orgCoverageTarget); - prDataCodeCoverage.status = "invalid"; + prDataCodeCoverage.status = 'invalid'; } // Code coverage failure but ignored thanks to config testCoverageNotBlocking - else if (coverageStatus === "invalid_ignored") { + else if (coverageStatus === 'invalid_ignored') { prDataCodeCoverage.title = - existingPrData.deployStatus === "valid" ? "✅⚠️ Deployment success with ignored Code coverage error" : prDataCodeCoverage.title; + existingPrData.deployStatus === 'valid' + ? '✅⚠️ Deployment success with ignored Code coverage error' + : prDataCodeCoverage.title; prDataCodeCoverage.codeCoverageMarkdownBody = deployCodeCoverageToMarkdown(orgCoverage, orgCoverageTarget); } else { prDataCodeCoverage.codeCoverageMarkdownBody = deployCodeCoverageToMarkdown(orgCoverage, orgCoverageTarget); diff --git a/src/common/utils/emailUtils.ts b/src/common/utils/emailUtils.ts index 4b5a3d2be..a9557971e 100644 --- a/src/common/utils/emailUtils.ts +++ b/src/common/utils/emailUtils.ts @@ -1,13 +1,13 @@ -import { Connection } from "jsforce"; -import { getNested, uxLog } from "."; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; +import { Connection } from '@salesforce/core'; +import { getNested, uxLog } from './index.js'; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; export async function sendEmail(emailMessage: EmailMessage) { const conn: Connection = globalThis.jsForceConn || null; if (!conn) { - uxLog(this, c.grey("globalThis.jsForceConn is not set, can not send email")); + uxLog(this, c.grey('globalThis.jsForceConn is not set, can not send email')); return; } // Init message @@ -22,28 +22,29 @@ export async function sendEmail(emailMessage: EmailMessage) { utf8 - ${emailMessage.senderDisplayName || "SFDX-HARDIS Notifications"} + ${emailMessage.senderDisplayName || 'SFDX-HARDIS Notifications' + } ${emailMessage.subject} `; // Plain text Body if (emailMessage.body_text) { - soapBody += ` ${sanitizeForXml(emailMessage.body_text || "")}\n`; + soapBody += ` ${sanitizeForXml(emailMessage.body_text || '')}\n`; } else if (emailMessage.body_html) { - soapBody += ` ${sanitizeForXml(emailMessage.body_html || "")}\n`; + soapBody += ` ${sanitizeForXml(emailMessage.body_html || '')}\n`; } // Addresses - if (emailMessage?.to?.length > 0) { - soapBody += buildArrayOfStrings(emailMessage.to, " ", ""); + if (emailMessage?.to?.length && emailMessage?.to?.length > 0) { + soapBody += buildArrayOfStrings(emailMessage.to, ' ', ''); } - if (emailMessage?.cc?.length > 0) { - soapBody += buildArrayOfStrings(emailMessage.cc, " ", ""); + if (emailMessage?.cc?.length && emailMessage?.cc?.length > 0) { + soapBody += buildArrayOfStrings(emailMessage.cc, ' ', ''); } - if (emailMessage?.cci?.length > 0) { - soapBody += buildArrayOfStrings(emailMessage.cci, " ", ""); + if (emailMessage?.cci?.length && emailMessage?.cci?.length > 0) { + soapBody += buildArrayOfStrings(emailMessage.cci, ' ', ''); } // Attachments - if (emailMessage?.attachments?.length > 0) { + if (emailMessage?.attachments?.length && emailMessage?.attachments?.length > 0) { let totalSize = 0; for (const attachment of emailMessage?.attachments || []) { if (fs.existsSync(attachment)) { @@ -55,7 +56,7 @@ export async function sendEmail(emailMessage: EmailMessage) { continue; } const fileName = path.basename(attachment); - const fileBody = fs.readFileSync(attachment).toString("base64"); + const fileBody = fs.readFileSync(attachment).toString('base64'); soapBody += ` \n`; soapBody += ` ${fileName}\n`; soapBody += ` ${fileBody}\n`; @@ -73,26 +74,32 @@ export async function sendEmail(emailMessage: EmailMessage) { `; const soapResponse = await conn.request( { - method: "POST", + method: 'POST', url: `${conn.instanceUrl}/services/Soap/c/${conn.version}`, body: soapBody, headers: { - "Content-Type": "text/xml;charset=utf-8", - Accept: "text/xml;charset=utf-8", + 'Content-Type': 'text/xml;charset=utf-8', + Accept: 'text/xml;charset=utf-8', SOAPAction: '""', }, }, - { responseType: "text/xml" }, + { responseType: 'text/xml' } ); - const resultTag = getNested(soapResponse, ["soapenv:Envelope", "soapenv:Body", "sendEmailResponse", "result", "success"]); - if (resultTag === "true") { + const resultTag = getNested(soapResponse, [ + 'soapenv:Envelope', + 'soapenv:Body', + 'sendEmailResponse', + 'result', + 'success', + ]); + if (resultTag === 'true') { return { success: true, detail: soapResponse }; } return { success: false, detail: soapResponse }; } function buildArrayOfStrings(elements: string[], openingTag: string, closingTag: string): string { - let result = ""; + let result = ''; for (const element of elements) { result += `${openingTag}${element}${closingTag}\n`; } @@ -100,7 +107,12 @@ function buildArrayOfStrings(elements: string[], openingTag: string, closingTag: } function sanitizeForXml(value) { - return String(value).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); + return String(value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); } export interface EmailMessage { diff --git a/src/common/utils/filesUtils.ts b/src/common/utils/filesUtils.ts index cc10ec291..7aeed1e37 100644 --- a/src/common/utils/filesUtils.ts +++ b/src/common/utils/filesUtils.ts @@ -1,24 +1,25 @@ // External Libraries and Node.js Modules -import * as fs from "fs-extra"; -import * as path from "path"; -import * as c from "chalk"; -import * as fetch from "@adobe/node-fetch-retry"; -import * as split from "split"; -import { PromisePool } from "@supercharge/promise-pool"; +import fs from 'fs-extra'; +import * as path from 'path'; +import c from 'chalk'; +import makeFetchHappen, { FetchOptions } from 'make-fetch-happen'; +import * as split from 'split'; +import { PromisePool } from '@supercharge/promise-pool'; // Salesforce Specific and Other Specific Libraries -import { Connection, SfdxError } from "@salesforce/core"; -import * as Papa from "papaparse"; -import * as ExcelJS from "exceljs"; +import { Connection, SfError } from '@salesforce/core'; +import Papa from 'papaparse'; +import ExcelJS from 'exceljs'; // Project Specific Utilities -import { getCurrentGitBranch, isCI, uxLog } from "."; -import { bulkQuery, soqlQuery } from "./apiUtils"; -import { prompts } from "./prompts"; -import { CONSTANTS, getReportDirectory } from "../../config"; -import { WebSocketClient } from "../websocketClient"; +import { getCurrentGitBranch, isCI, uxLog } from './index.js'; +import { bulkQuery, soqlQuery } from './apiUtils.js'; +import { prompts } from './prompts.js'; +import { CONSTANTS, getReportDirectory } from '../../config/index.js'; +import { WebSocketClient } from '../websocketClient.js'; +import ora from 'ora'; -export const filesFolderRoot = path.join(".", "scripts", "files"); +export const filesFolderRoot = path.join('.', 'scripts', 'files'); export class FilesExporter { private filesPath: string; @@ -28,9 +29,9 @@ export class FilesExporter { private startChunkNumber: number; private commandThis: any; - private fetchOptions: any; + private fetchOptions: FetchOptions; private dtl: any = null; // export config - private exportedFilesFolder: string; + private exportedFilesFolder: string = ''; private recordsChunk: any[] = []; private chunksNumber = 1; @@ -49,14 +50,14 @@ export class FilesExporter { private filesErrors = 0; private filesIgnoredType = 0; private filesIgnoredExisting = 0; - private apiUsedBefore = null; - private apiLimit = null; + private apiUsedBefore: number = 0; + private apiLimit: number = 0; constructor( filesPath: string, conn: Connection, options: { pollTimeout?: number; recordsChunkSize?: number; exportConfig?: any; startChunkNumber?: number }, - commandThis: any, + commandThis: any ) { this.filesPath = filesPath; this.conn = conn; @@ -69,10 +70,15 @@ export class FilesExporter { } // Build fetch options for HTTP calls to retrieve document files this.fetchOptions = { - method: "GET", + method: 'GET', headers: { - Authorization: "Bearer " + this.conn.accessToken, - "Content-Type": "blob", + Authorization: 'Bearer ' + this.conn.accessToken, + 'Content-Type': 'blob', + }, + retry: { + retries: 20, + factor: 3, + randomize: true, }, }; } @@ -85,7 +91,7 @@ export class FilesExporter { uxLog(this.commandThis, c.cyan(`Exporting files from ${c.green(this.dtl.full_label)} ...`)); uxLog(this.commandThis, c.italic(c.grey(this.dtl.description))); // Make sure export folder for files is existing - this.exportedFilesFolder = path.join(this.filesPath, "export"); + this.exportedFilesFolder = path.join(this.filesPath, 'export'); await fs.ensureDir(this.exportedFilesFolder); await this.calculateApiConsumption(); @@ -97,36 +103,43 @@ export class FilesExporter { // Calculate API consumption private async calculateApiConsumption() { - const countSoqlQuery = this.dtl.soqlQuery.replace(/SELECT (.*) FROM/gi, "SELECT COUNT() FROM"); + const countSoqlQuery = this.dtl.soqlQuery.replace(/SELECT (.*) FROM/gi, 'SELECT COUNT() FROM'); this.totalSoqlRequests++; const countSoqlQueryRes = await soqlQuery(countSoqlQuery, this.conn); this.chunksNumber = Math.round(countSoqlQueryRes.totalSize / this.recordsChunkSize); const estimatedApiCalls = Math.round(this.chunksNumber * 2) + 1; - this.apiUsedBefore = (this.conn as any)?.limitInfo?.apiUsage?.used ? (this.conn as any).limitInfo.apiUsage.used - 1 : this.apiUsedBefore; + this.apiUsedBefore = (this.conn as any)?.limitInfo?.apiUsage?.used + ? (this.conn as any).limitInfo.apiUsage.used - 1 + : this.apiUsedBefore; this.apiLimit = (this.conn as any)?.limitInfo?.apiUsage?.limit; // Check if there are enough API calls available if (this.apiLimit - this.apiUsedBefore < estimatedApiCalls + 1000) { - throw new SfdxError( - `You don't have enough API calls available (${c.bold(this.apiLimit - this.apiUsedBefore)}) to perform this export that could consume ${c.bold( - estimatedApiCalls, - )} API calls`, + throw new SfError( + `You don't have enough API calls available (${c.bold( + this.apiLimit - this.apiUsedBefore + )}) to perform this export that could consume ${c.bold(estimatedApiCalls)} API calls` ); } // Request user confirmation if (!isCI) { const warningMessage = c.cyanBright( `This export of files could run on ${c.bold(c.yellow(countSoqlQueryRes.totalSize))} records, in ${c.bold( - c.yellow(this.chunksNumber), + c.yellow(this.chunksNumber) )} chunks, and consume up to ${c.bold(c.yellow(estimatedApiCalls))} API calls on the ${c.bold( - c.yellow(this.apiLimit - this.apiUsedBefore), - )} remaining API calls. Do you want to proceed ?`, + c.yellow(this.apiLimit - this.apiUsedBefore) + )} remaining API calls. Do you want to proceed ?` ); - const promptRes = await prompts({ type: "confirm", message: warningMessage }); + const promptRes = await prompts({ type: 'confirm', message: warningMessage }); if (promptRes.value !== true) { - throw new SfdxError("Command cancelled by user"); + throw new SfError('Command cancelled by user'); } if (this.startChunkNumber === 0) { - uxLog(this, c.yellow(c.italic("Use --startchunknumber command line argument if you do not want to start from first chunk"))); + uxLog( + this, + c.yellow( + c.italic('Use --startchunknumber command line argument if you do not want to start from first chunk') + ) + ); } } } @@ -140,7 +153,11 @@ export class FilesExporter { await this.processRecordsChunk(recordChunk); this.recordsChunkQueueRunning = false; // Manage last chunk - } else if (this.bulkApiRecordsEnded === true && this.recordsChunkQueue.length === 0 && this.recordsChunk.length > 0) { + } else if ( + this.bulkApiRecordsEnded === true && + this.recordsChunkQueue.length === 0 && + this.recordsChunk.length > 0 + ) { const recordsToProcess = [...this.recordsChunk]; this.recordsChunk = []; this.recordsChunkQueue.push(recordsToProcess); @@ -162,7 +179,7 @@ export class FilesExporter { resolve(true); } if (globalThis.sfdxHardisFatalError === true) { - uxLog(this, c.red("Fatal error while processing chunks queue")); + uxLog(this, c.red('Fatal error while processing chunks queue')); process.exit(1); } }, 1000); @@ -173,27 +190,27 @@ export class FilesExporter { private async processParentRecords() { // Query parent records using SOQL defined in export.json file - uxLog(this, c.grey("Bulk query: " + c.italic(this.dtl.soqlQuery))); this.totalSoqlRequests++; this.conn.bulk.pollTimeout = this.pollTimeout || 600000; // Increase timeout in case we are on a bad internet connection or if the bulk api batch is queued - await this.conn.bulk - .query(this.dtl.soqlQuery) - .on("record", async (record) => { - this.totalParentRecords++; - const parentRecordFolderForFiles = path.resolve(path.join(this.exportedFilesFolder, record[this.dtl.outputFolderNameField] || record.Id)); - if (this.dtl.overwriteParentRecords !== true && fs.existsSync(parentRecordFolderForFiles)) { - uxLog(this, c.grey(`Skipped record - ${record[this.dtl.outputFolderNameField] || record.Id} - Record files already downloaded`)); - this.recordsIgnored++; - return; - } - await this.addToRecordsChunk(record); - }) - .on("error", (err) => { - throw new SfdxError(c.red("Bulk query error:" + err)); - }) - .on("end", () => { - this.bulkApiRecordsEnded = true; - }); + const queryRes = await bulkQuery(this.dtl.soqlQuery, this.conn, 3); + for (const record of queryRes.records) { + this.totalParentRecords++; + const parentRecordFolderForFiles = path.resolve( + path.join(this.exportedFilesFolder, record[this.dtl.outputFolderNameField] || record.Id) + ); + if (this.dtl.overwriteParentRecords !== true && fs.existsSync(parentRecordFolderForFiles)) { + uxLog( + this, + c.grey( + `Skipped record - ${record[this.dtl.outputFolderNameField] || record.Id} - Record files already downloaded` + ) + ); + this.recordsIgnored++; + return; + } + await this.addToRecordsChunk(record); + } + this.bulkApiRecordsEnded = true; } private async addToRecordsChunk(record: any) { @@ -209,22 +226,34 @@ export class FilesExporter { private async processRecordsChunk(records: any[]) { this.recordChunksNumber++; if (this.recordChunksNumber < this.startChunkNumber) { - uxLog(this, c.cyan(`Skip parent records chunk #${this.recordChunksNumber} because it is lesser than ${this.startChunkNumber}`)); + uxLog( + this, + c.cyan( + `Skip parent records chunk #${this.recordChunksNumber} because it is lesser than ${this.startChunkNumber}` + ) + ); return; } - uxLog(this, c.cyan(`Processing parent records chunk #${this.recordChunksNumber} on ${this.chunksNumber} (${records.length} records) ...`)); + uxLog( + this, + c.cyan( + `Processing parent records chunk #${this.recordChunksNumber} on ${this.chunksNumber} (${records.length} records) ...` + ) + ); // Request all ContentDocumentLink related to all records of the chunk - const linkedEntityIdIn = records.map((record: any) => `'${record.Id}'`).join(","); + const linkedEntityIdIn = records.map((record: any) => `'${record.Id}'`).join(','); const linkedEntityInQuery = `SELECT ContentDocumentId,LinkedEntityId FROM ContentDocumentLink WHERE LinkedEntityId IN (${linkedEntityIdIn})`; this.totalSoqlRequests++; const contentDocumentLinks = await bulkQuery(linkedEntityInQuery, this.conn); if (contentDocumentLinks.records.length === 0) { - uxLog(this, c.grey("No ContentDocumentLinks found for the parent records in this chunk")); + uxLog(this, c.grey('No ContentDocumentLinks found for the parent records in this chunk')); return; } // Retrieve all ContentVersion related to ContentDocumentLink - const contentDocIdIn = contentDocumentLinks.records.map((contentDocumentLink: any) => `'${contentDocumentLink.ContentDocumentId}'`).join(","); + const contentDocIdIn = contentDocumentLinks.records + .map((contentDocumentLink: any) => `'${contentDocumentLink.ContentDocumentId}'`) + .join(','); const contentVersionSoql = `SELECT Id,ContentDocumentId,Description,FileExtension,FileType,PathOnClient,Title FROM ContentVersion WHERE ContentDocumentId IN (${contentDocIdIn}) AND IsLatest = true`; this.totalSoqlRequests++; const contentVersions = await bulkQuery(contentVersionSoql, this.conn); @@ -233,7 +262,7 @@ export class FilesExporter { // Because of this when we fetch ContentVersion for ContentDocument it can return less results than there is ContentDocumentLink objects to link. // To fix this we create a list of ContentVersion and ContentDocumentLink pairs. // This way we have multiple pairs and we will download ContentVersion objects for each linked object. - const versionsAndLinks = []; + const versionsAndLinks: any[] = []; contentVersions.records.forEach((contentVersion) => { contentDocumentLinks.records.forEach((contentDocumentLink) => { if (contentDocumentLink.ContentDocumentId === contentVersion.ContentDocumentId) { @@ -250,10 +279,14 @@ export class FilesExporter { .for(versionsAndLinks) .process(async (versionAndLink: any) => { try { - await this.downloadContentVersionFile(versionAndLink.contentVersion, records, versionAndLink.contentDocumentLink); + await this.downloadContentVersionFile( + versionAndLink.contentVersion, + records, + versionAndLink.contentDocumentLink + ); } catch (e) { this.filesErrors++; - uxLog(this, c.red("Download file error: " + versionAndLink.contentVersion.Title + "\n" + e)); + uxLog(this, c.red('Download file error: ' + versionAndLink.contentVersion.Title + '\n' + e)); } }); } @@ -262,45 +295,63 @@ export class FilesExporter { // Retrieve initial record to build output files folder name const parentRecord = records.filter((record) => record.Id === contentDocumentLink.LinkedEntityId)[0]; // Build record output files folder (if folder name contains slashes or antislashes, replace them by spaces) - const parentFolderName = (parentRecord[this.dtl.outputFolderNameField] || parentRecord.Id).replace(/[/\\?%*:|"<>]/g, "-"); + const parentFolderName = (parentRecord[this.dtl.outputFolderNameField] || parentRecord.Id).replace( + /[/\\?%*:|"<>]/g, + '-' + ); const parentRecordFolderForFiles = path.resolve(path.join(this.exportedFilesFolder, parentFolderName)); // Define name of the file let outputFile = // Id - this.dtl?.outputFileNameFormat === "id" + this.dtl?.outputFileNameFormat === 'id' ? path.join(parentRecordFolderForFiles, contentVersion.Id) : // Title + Id - this.dtl?.outputFileNameFormat === "title_id" - ? path.join(parentRecordFolderForFiles, `${contentVersion.Title.replace(/[/\\?%*:|"<>]/g, "-")}_${contentVersion.Id}`) + this.dtl?.outputFileNameFormat === 'title_id' + ? path.join( + parentRecordFolderForFiles, + `${contentVersion.Title.replace(/[/\\?%*:|"<>]/g, '-')}_${contentVersion.Id}` + ) : // Id + Title - this.dtl?.outputFileNameFormat === "id_title" - ? path.join(parentRecordFolderForFiles, `${contentVersion.Id}_${contentVersion.Title.replace(/[/\\?%*:|"<>]/g, "-")}`) + this.dtl?.outputFileNameFormat === 'id_title' + ? path.join( + parentRecordFolderForFiles, + `${contentVersion.Id}_${contentVersion.Title.replace(/[/\\?%*:|"<>]/g, '-')}` + ) : // Title - path.join(parentRecordFolderForFiles, contentVersion.Title.replace(/[/\\?%*:|"<>]/g, "-")); + path.join(parentRecordFolderForFiles, contentVersion.Title.replace(/[/\\?%*:|"<>]/g, '-')); // Add file extension if missing in file title, and replace .snote by .html if (contentVersion.FileExtension && path.extname(outputFile) !== contentVersion.FileExtension) { - outputFile = outputFile + "." + (contentVersion.FileExtension !== "snote" ? contentVersion.FileExtension : "html"); + outputFile = + outputFile + '.' + (contentVersion.FileExtension !== 'snote' ? contentVersion.FileExtension : 'html'); } + const outputFileLabel = path.relative(process.cwd(), outputFile); // Check file extension - if (this.dtl.fileTypes !== "all" && !this.dtl.fileTypes.includes(contentVersion.FileType)) { - uxLog(this, c.grey(`Skipped - ${outputFile.replace(this.exportedFilesFolder, "")} - File type ignored`)); + if (this.dtl.fileTypes !== 'all' && !this.dtl.fileTypes.includes(contentVersion.FileType)) { + uxLog(this, c.grey(`Skipped - ${outputFile.replace(this.exportedFilesFolder, '')} - File type ignored`)); this.filesIgnoredType++; return; } // Check file overwrite if (this.dtl.overwriteFiles !== true && fs.existsSync(outputFile)) { - uxLog(this, c.yellow(`Skipped - ${outputFile.replace(this.exportedFilesFolder, "")} - File already existing`)); + uxLog(this, c.yellow(`Skipped - ${outputFile.replace(this.exportedFilesFolder, '')} - File already existing`)); this.filesIgnoredExisting++; return; } // Create directory if not existing await fs.ensureDir(parentRecordFolderForFiles); // Download file locally + const spinnerCustom = ora({ + text: `(${(this.filesDownloaded + 1)}) Downloading ${outputFileLabel}...`, + spinner: 'moon', + }).start(); const fetchUrl = `${this.conn.instanceUrl}/services/data/v${CONSTANTS.API_VERSION}/sobjects/ContentVersion/${contentVersion.Id}/VersionData`; try { - const fetchRes = await fetch(fetchUrl, this.fetchOptions); + this.fetchOptions.onRetry = (cause: unknown) => { + spinnerCustom.text = `(${this.filesDownloaded}) Retrying ${outputFileLabel} (${cause})...`; + } + const fetchRes = await makeFetchHappen(fetchUrl, this.fetchOptions); if (fetchRes.ok !== true) { - throw new SfdxError(`Fetch error - ${fetchUrl} - + ${JSON.stringify(fetchRes.body)}`); + throw new SfError(`Fetch error - ${fetchUrl} - + ${JSON.stringify(fetchRes.body)}`); } // Wait for file to be written const stream = fs.createWriteStream(outputFile); @@ -311,11 +362,14 @@ export class FilesExporter { resolve(true); }); }) */ - uxLog(this, c.green(`Success - ${path.relative(process.cwd(), outputFile)}`)); + + // uxLog(this, c.green(`Success - ${path.relative(process.cwd(), outputFile)}`)); this.filesDownloaded++; - } catch (err) { + spinnerCustom.succeed(`(${this.filesDownloaded}) Downloaded ${outputFileLabel}`); + } catch (err: any) { // Download failure - uxLog(this, c.red(`Error - ${path.relative(process.cwd(), outputFile)} - ${err}`)); + // uxLog(this, c.red(`Error - ${path.relative(process.cwd(), outputFile)} - ${err}`)); + spinnerCustom.fail(`(${this.filesDownloaded}) Error while downloading ${outputFileLabel}: ${err.message}`); this.filesErrors++; } } @@ -361,12 +415,17 @@ export class FilesImporter { private commandThis: any; private dtl: any = null; // export config - private exportedFilesFolder: string; + private exportedFilesFolder: string = ''; private handleOverwrite = false; - constructor(filesPath: string, conn: Connection, options: { exportConfig?: any; handleOverwrite?: boolean }, commandThis: any) { + constructor( + filesPath: string, + conn: Connection, + options: { exportConfig?: any; handleOverwrite?: boolean }, + commandThis: any + ) { this.filesPath = filesPath; - this.exportedFilesFolder = path.join(this.filesPath, "export"); + this.exportedFilesFolder = path.join(this.filesPath, 'export'); this.handleOverwrite = options?.handleOverwrite === true; this.conn = conn; this.commandThis = commandThis; @@ -408,14 +467,16 @@ export class FilesImporter { return fs.statSync(path.join(this.exportedFilesFolder, recordFolder, file)).isFile(); }); // Find Id of parent object using folder name - const parentRecordIds = parentObjects.filter((parentObj) => parentObj[this.dtl.outputFolderNameField] === recordFolder); + const parentRecordIds = parentObjects.filter( + (parentObj) => parentObj[this.dtl.outputFolderNameField] === recordFolder + ); if (parentRecordIds.length === 0) { uxLog(this, c.red(`Unable to find Id for ${this.dtl.outputFolderNameField}=${recordFolder}`)); continue; } const parentRecordId = parentRecordIds[0].Id; - let existingDocuments = []; + let existingDocuments: any[] = []; // Collect existing documents if we handle file overwrite if (this.handleOverwrite) { const existingDocsQuery = `SELECT Id, ContentDocumentId, Title FROM ContentVersion WHERE FirstPublishLocationId = '${parentRecordId}'`; @@ -428,7 +489,7 @@ export class FilesImporter { const contentVersionParams: any = { Title: file, PathOnClient: file, - VersionData: fileData.toString("base64"), + VersionData: fileData.toString('base64'), }; const matchingExistingDocs = existingDocuments.filter((doc) => doc.Title === file); if (matchingExistingDocs.length > 0) { @@ -439,15 +500,15 @@ export class FilesImporter { uxLog(this, c.grey(`Uploading file ${file} ...`)); } try { - const insertResult = await this.conn.sobject("ContentVersion").create(contentVersionParams); - if (!insertResult.success) { + const insertResult = await this.conn.sobject('ContentVersion').create(contentVersionParams); + if (insertResult.length === 0) { uxLog(this, c.red(`Unable to upload file ${file}`)); errorNb++; } else { successNb++; } } catch (e) { - uxLog(this, c.red(`Unable to upload file ${file}: ${e.message}`)); + uxLog(this, c.red(`Unable to upload file ${file}: ${(e as Error).message}`)); errorNb++; } } @@ -471,27 +532,29 @@ export class FilesImporter { if (!isCI) { const warningMessage = c.cyanBright( `Files import consumes one REST API call per uploaded file. - (Estimation: ${bulkCallsNb} Bulks calls and ${totalFilesNumber} REST calls) Do you confirm you want to proceed ?`, + (Estimation: ${bulkCallsNb} Bulks calls and ${totalFilesNumber} REST calls) Do you confirm you want to proceed ?` ); - const promptRes = await prompts({ type: "confirm", message: warningMessage }); + const promptRes = await prompts({ type: 'confirm', message: warningMessage }); if (promptRes.value !== true) { - throw new SfdxError("Command cancelled by user"); + throw new SfError('Command cancelled by user'); } } } } -export async function selectFilesWorkspace(opts = { selectFilesLabel: "Please select a files folder to export" }) { +export async function selectFilesWorkspace(opts = { selectFilesLabel: 'Please select a files folder to export' }) { if (!fs.existsSync(filesFolderRoot)) { - throw new SfdxError("There is no files root folder 'scripts/files' in your workspace. Create it and define a files export configuration"); + throw new SfError( + "There is no files root folder 'scripts/files' in your workspace. Create it and define a files export configuration" + ); } const filesFolders = fs .readdirSync(filesFolderRoot, { withFileTypes: true }) .filter((dirent) => dirent.isDirectory()) - .map((dirent) => path.join(".", "scripts", "files", dirent.name)); + .map((dirent) => path.join('.', 'scripts', 'files', dirent.name)); if (filesFolders.length === 0) { - throw new SfdxError("There is no file exports folder in your workspace"); + throw new SfError('There is no file exports folder in your workspace'); } const choices: any = []; for (const filesFolder of filesFolders) { @@ -505,8 +568,8 @@ export async function selectFilesWorkspace(opts = { selectFilesLabel: "Please se } } const filesDirResult = await prompts({ - type: "select", - name: "value", + type: 'select', + name: 'value', message: c.cyanBright(opts.selectFilesLabel), choices: choices, }); @@ -514,23 +577,29 @@ export async function selectFilesWorkspace(opts = { selectFilesLabel: "Please se } export async function getFilesWorkspaceDetail(filesWorkspace: string) { - const exportFile = path.join(filesWorkspace, "export.json"); + const exportFile = path.join(filesWorkspace, 'export.json'); if (!fs.existsSync(exportFile)) { - uxLog(this, c.yellow(`Your File export folder ${c.bold(filesWorkspace)} must contain an ${c.bold("export.json")} configuration file`)); + uxLog( + this, + c.yellow( + `Your File export folder ${c.bold(filesWorkspace)} must contain an ${c.bold('export.json')} configuration file` + ) + ); return null; } - const exportFileJson = JSON.parse(await fs.readFile(exportFile, "utf8")); - const folderName = filesWorkspace.replace(/\\/g, "/").match(/([^/]*)\/*$/)[1]; + const exportFileJson = JSON.parse(await fs.readFile(exportFile, 'utf8')); + const folderName = (filesWorkspace.replace(/\\/g, '/').match(/([^/]*)\/*$/) || '')[1]; const hardisLabel = exportFileJson.sfdxHardisLabel || folderName; const hardisDescription = exportFileJson.sfdxHardisDescription || filesWorkspace; - const soqlQuery = exportFileJson.soqlQuery || ""; - const fileTypes = exportFileJson.fileTypes || "all"; - const outputFolderNameField = exportFileJson.outputFolderNameField || "Name"; - const outputFileNameFormat = exportFileJson.outputFileNameFormat || "title"; - const overwriteParentRecords = exportFileJson.overwriteParentRecords === false ? false : exportFileJson.overwriteParentRecords || true; + const soqlQuery = exportFileJson.soqlQuery || ''; + const fileTypes = exportFileJson.fileTypes || 'all'; + const outputFolderNameField = exportFileJson.outputFolderNameField || 'Name'; + const outputFileNameFormat = exportFileJson.outputFileNameFormat || 'title'; + const overwriteParentRecords = + exportFileJson.overwriteParentRecords === false ? false : exportFileJson.overwriteParentRecords || true; const overwriteFiles = exportFileJson.overwriteFiles || false; return { - full_label: `[${folderName}]${folderName != hardisLabel ? `: ${hardisLabel}` : ""}`, + full_label: `[${folderName}]${folderName != hardisLabel ? `: ${hardisLabel}` : ''}`, label: hardisLabel, description: hardisDescription, soqlQuery: soqlQuery, @@ -543,69 +612,73 @@ export async function getFilesWorkspaceDetail(filesWorkspace: string) { } export async function promptFilesExportConfiguration(filesExportConfig: any, override = false) { - const questions = []; + const questions: any[] = []; if (override === false) { questions.push( ...[ { - type: "text", - name: "filesExportPath", - message: c.cyanBright('Please input the files export config folder name (PascalCase format). Ex: "OpportunitiesPDF"'), + type: 'text', + name: 'filesExportPath', + message: c.cyanBright( + 'Please input the files export config folder name (PascalCase format). Ex: "OpportunitiesPDF"' + ), }, { - type: "text", - name: "sfdxHardisLabel", - message: c.cyanBright("Please input a label for the files export configuration"), + type: 'text', + name: 'sfdxHardisLabel', + message: c.cyanBright('Please input a label for the files export configuration'), initial: filesExportConfig.sfdxHardisLabel, }, { - type: "text", - name: "sfdxHardisDescription", - message: c.cyanBright("Please input a description of the files export configuration"), + type: 'text', + name: 'sfdxHardisDescription', + message: c.cyanBright('Please input a description of the files export configuration'), initial: filesExportConfig.sfdxHardisDescription, }, - ], + ] ); } questions.push( ...[ { - type: "text", - name: "soqlQuery", - message: "Please input the main SOQL Query to fetch the parent records of files (ContentVersions). Ex: SELECT Id,Name from Opportunity", + type: 'text', + name: 'soqlQuery', + message: + 'Please input the main SOQL Query to fetch the parent records of files (ContentVersions). Ex: SELECT Id,Name from Opportunity', initial: filesExportConfig.soqlQuery, }, { - type: "text", - name: "outputFolderNameField", - message: "Please input the field to use to build the name of the folder containing downloaded files", + type: 'text', + name: 'outputFolderNameField', + message: 'Please input the field to use to build the name of the folder containing downloaded files', initial: filesExportConfig.outputFolderNameField, }, { - type: "select", - name: "outputFileNameFormat", + type: 'select', + name: 'outputFileNameFormat', choices: [ - { value: "title", title: "title" }, - { value: "title_id", title: "title_id" }, - { value: "id_title", title: "id_title" }, - { value: "id", title: "id" }, + { value: 'title', title: 'title' }, + { value: 'title_id', title: 'title_id' }, + { value: 'id_title', title: 'id_title' }, + { value: 'id', title: 'id' }, ], - message: "Please select the format of output files names", + message: 'Please select the format of output files names', initial: filesExportConfig.outputFileNameFormat, }, { - type: "confirm", - name: "overwriteParentRecords", - message: "Do you want to try to download files attached to a parent records whose folder is already existing in local folders ?", + type: 'confirm', + name: 'overwriteParentRecords', + message: + 'Do you want to try to download files attached to a parent records whose folder is already existing in local folders ?', initial: filesExportConfig.overwriteParentRecords, }, { - type: "confirm", - name: "overwriteFiles", - message: "Do you want to overwrite file that has already been previously downloaded ?", + type: 'confirm', + name: 'overwriteFiles', + message: 'Do you want to overwrite file that has already been previously downloaded ?', initial: filesExportConfig.overwriteFiles, }, - ], + ] ); const resp = await prompts(questions); @@ -628,16 +701,16 @@ export async function countLinesInFile(file: string) { return await new Promise((resolve) => { fs.createReadStream(file) .pipe(split()) - .on("data", () => { + .on('data', () => { lineCount++; }) - .on("end", () => { + .on('end', () => { if (readError) { return; } resolve(lineCount - 1); }) - .on("error", (error) => { + .on('error', (error) => { readError = true; resolve(error); }); @@ -657,8 +730,11 @@ export async function countLinesInFile(file: string) { export async function generateReportPath(fileNamePrefix: string, outputFile: string): Promise { if (outputFile == null) { const reportDir = await getReportDirectory(); - const branchName = process.env.CI_COMMIT_REF_NAME || (await getCurrentGitBranch({ formatted: true })) || "Missing CI_COMMIT_REF_NAME variable"; - return path.join(reportDir, `${fileNamePrefix}-${branchName.split("/").pop()}.csv`); + const branchName = + process.env.CI_COMMIT_REF_NAME || + (await getCurrentGitBranch({ formatted: true })) || + 'Missing CI_COMMIT_REF_NAME variable'; + return path.join(reportDir, `${fileNamePrefix}-${branchName.split('/').pop()}.csv`); } else { await fs.ensureDir(path.dirname(outputFile)); return outputFile; @@ -678,28 +754,31 @@ export async function generateCsvFile(data: any[], outputPath: string): Promise< const result: any = {}; try { const csvContent = Papa.unparse(data); - await fs.writeFile(outputPath, csvContent, "utf8"); + await fs.writeFile(outputPath, csvContent, 'utf8'); uxLog(this, c.italic(c.cyan(`Please see detailed CSV log in ${c.bold(outputPath)}`))); result.csvFile = outputPath; WebSocketClient.requestOpenFile(outputPath); if (data.length > 0) { try { // Generate mirror XSLX file - const xlsDirName = path.join(path.dirname(outputPath), "xls"); - const xslFileName = path.basename(outputPath).replace(".csv", ".xlsx"); + const xlsDirName = path.join(path.dirname(outputPath), 'xls'); + const xslFileName = path.basename(outputPath).replace('.csv', '.xlsx'); const xslxFile = path.join(xlsDirName, xslFileName); await fs.ensureDir(xlsDirName); await csvToXls(outputPath, xslxFile); uxLog(this, c.italic(c.cyan(`Please see detailed XSLX log in ${c.bold(xslxFile)}`))); result.xlsxFile = xslxFile; } catch (e2) { - uxLog(this, c.yellow("Error while generating XSLX log file:\n" + e2.message + "\n" + e2.stack)); + uxLog( + this, + c.yellow('Error while generating XSLX log file:\n' + (e2 as Error).message + '\n' + (e2 as Error).stack) + ); } } else { uxLog(this, c.grey(`No XLS file generated as ${outputPath} is empty`)); } } catch (e) { - uxLog(this, c.yellow("Error while generating CSV log file:\n" + e.message + "\n" + e.stack)); + uxLog(this, c.yellow('Error while generating CSV log file:\n' + (e as Error).message + '\n' + (e as Error).stack)); } return result; } @@ -708,12 +787,12 @@ async function csvToXls(csvFile: string, xslxFile: string) { const workbook = new ExcelJS.Workbook(); const worksheet = await workbook.csv.readFile(csvFile); // Set filters - worksheet.autoFilter = "A1:Z1"; + worksheet.autoFilter = 'A1:Z1'; // Adjust column size (only if the file is not too big, to avoid performances issues) if (worksheet.rowCount < 5000) { worksheet.columns.forEach((column) => { - const lengths = column.values.map((v) => v.toString().length); - const maxLength = Math.max(...lengths.filter((v) => typeof v === "number")); + const lengths = (column.values || []).map((v) => (v || '').toString().length); + const maxLength = Math.max(...lengths.filter((v) => typeof v === 'number')); column.width = maxLength; }); } diff --git a/src/common/utils/gitUtils.ts b/src/common/utils/gitUtils.ts index bcd740640..919dbaf9a 100644 --- a/src/common/utils/gitUtils.ts +++ b/src/common/utils/gitUtils.ts @@ -1,7 +1,7 @@ -import { getConfig } from "../../config"; -import { prompts } from "./prompts"; -import * as c from "chalk"; -import * as sortArray from "sort-array"; +import { getConfig } from '../../config/index.js'; +import { prompts } from './prompts.js'; +import c from 'chalk'; +import sortArray from 'sort-array'; import { arrayUniqueByKey, arrayUniqueByKeys, @@ -12,16 +12,16 @@ import { getGitRepoRoot, git, uxLog, -} from "."; -import { GitProvider } from "../gitProvider"; -import { Ticket, TicketProvider } from "../ticketProvider"; -import { DefaultLogFields, ListLogLine } from "simple-git"; +} from './index.js'; +import { GitProvider } from '../gitProvider/index.js'; +import { Ticket, TicketProvider } from '../ticketProvider/index.js'; +import { DefaultLogFields, ListLogLine } from 'simple-git'; export async function selectTargetBranch(options: { message?: string } = {}) { const message = options.message || - "What will be the target branch of your new task ? (the branch where you will make your merge request after the task is completed)"; - const config = await getConfig("user"); + 'What will be the target branch of your new task ? (the branch where you will make your merge request after the task is completed)'; + const config = await getConfig('user'); const availableTargetBranches = config.availableTargetBranches || null; // There is only once choice so return it if (availableTargetBranches === null && config.developmentBranch) { @@ -32,31 +32,46 @@ export async function selectTargetBranch(options: { message?: string } = {}) { // Request info to build branch name. ex features/config/MYTASK const response = await prompts([ { - type: availableTargetBranches ? "select" : "text", - name: "targetBranch", + type: availableTargetBranches ? 'select' : 'text', + name: 'targetBranch', message: c.cyanBright(message), choices: availableTargetBranches ? availableTargetBranches.map((branch) => { - return { title: branch, value: branch }; + return { + title: branch.includes(',') ? branch.split(',').join(' - ') : branch, + value: branch.includes(',') ? branch.split(',')[0] : branch, + }; }) : [], - initial: config.developmentBranch || "integration", + initial: config.developmentBranch || 'integration', }, ]); - const targetBranch = response.targetBranch || "integration"; + const targetBranch = response.targetBranch || 'integration'; return targetBranch; } export async function getGitDeltaScope(currentBranch: string, targetBranch: string) { try { - await git().fetch(["origin", `${targetBranch}:${targetBranch}`]); + await git().fetch(['origin', `${targetBranch}:${targetBranch}`]); } catch (e) { - uxLog(this, c.gray(`[Warning] Unable to fetch target branch ${targetBranch} to prepare call to sfdx-git-delta\n` + JSON.stringify(e))); + uxLog( + this, + c.gray( + `[Warning] Unable to fetch target branch ${targetBranch} to prepare call to sfdx-git-delta\n` + + JSON.stringify(e) + ) + ); } try { - await git().fetch(["origin", `${currentBranch}:${currentBranch}`]); + await git().fetch(['origin', `${currentBranch}:${currentBranch}`]); } catch (e) { - uxLog(this, c.gray(`[Warning] Unable to fetch current branch ${currentBranch} to prepare call to sfdx-git-delta\n` + JSON.stringify(e))); + uxLog( + this, + c.gray( + `[Warning] Unable to fetch current branch ${currentBranch} to prepare call to sfdx-git-delta\n` + + JSON.stringify(e) + ) + ); } const logResult = await git().log([`${targetBranch}..${currentBranch}`]); const toCommit = logResult.latest; @@ -64,16 +79,12 @@ export async function getGitDeltaScope(currentBranch: string, targetBranch: stri const mergeBaseCommandResult = await execCommand(mergeBaseCommand, this, { fail: true, }); - const masterBranchLatestCommit = mergeBaseCommandResult.stdout.replace("\n", "").replace("\r", ""); + const masterBranchLatestCommit = mergeBaseCommandResult.stdout.replace('\n', '').replace('\r', ''); return { fromCommit: masterBranchLatestCommit, toCommit: toCommit, logResult: logResult }; } export async function callSfdxGitDelta(from: string, to: string, outputDir: string, options: any = {}) { - const sgdHelp = (await execCommand(" sfdx sgd:source:delta --help", this, { fail: false, output: false, debug: options?.debugMode || false })) - .stdout; - const packageXmlGitDeltaCommand = - `sfdx sgd:source:delta --from "${from}" --to "${to}" --output ${outputDir}` + - (sgdHelp.includes("--ignore-whitespace") ? " --ignore-whitespace" : ""); + const packageXmlGitDeltaCommand = `sf sgd:source:delta --from "${from}" --to "${to}" --output ${outputDir} --ignore-whitespace`; const gitDeltaCommandRes = await execSfdxJson(packageXmlGitDeltaCommand, this, { output: true, fail: false, @@ -84,42 +95,54 @@ export async function callSfdxGitDelta(from: string, to: string, outputDir: stri } export async function computeCommitsSummary(checkOnly, pullRequestInfo: any) { - uxLog(this, c.cyan("Computing commits summary...")); + uxLog(this, c.cyan('Computing commits summary...')); const currentGitBranch = await getCurrentGitBranch(); let logResults: (DefaultLogFields & ListLogLine)[] = []; if (checkOnly || GitProvider.isDeployBeforeMerge()) { const prInfo = await GitProvider.getPullRequestInfo(); - const deltaScope = await getGitDeltaScope(prInfo?.sourceBranch || currentGitBranch, prInfo?.targetBranch || process.env.FORCE_TARGET_BRANCH); + const deltaScope = await getGitDeltaScope( + prInfo?.sourceBranch || currentGitBranch, + prInfo?.targetBranch || process.env.FORCE_TARGET_BRANCH + ); logResults = [...deltaScope.logResult.all]; } else { const logRes = await git().log([`HEAD^..HEAD`]); logResults = [...logRes.all]; } - logResults = arrayUniqueByKeys(logResults, ["message", "body"]).reverse(); - let commitsSummary = "## Commits summary\n\n"; - const manualActions = []; + logResults = arrayUniqueByKeys(logResults, ['message', 'body']).reverse(); + let commitsSummary = '## Commits summary\n\n'; + const manualActions: any[] = []; const tickets: Ticket[] = []; for (const logResult of logResults) { - commitsSummary += "**" + logResult.message + "**, by " + logResult.author_name; + commitsSummary += '**' + logResult.message + '**, by ' + logResult.author_name; if (logResult.body) { - commitsSummary += "
" + logResult.body + "\n\n"; - await collectTicketsAndManualActions(currentGitBranch + "\n" + logResult.message + "\n" + logResult.body, tickets, manualActions, { + commitsSummary += '
' + logResult.body + '\n\n'; + await collectTicketsAndManualActions( + currentGitBranch + '\n' + logResult.message + '\n' + logResult.body, + tickets, + manualActions, + { + commits: [logResult], + } + ); + } else { + await collectTicketsAndManualActions(currentGitBranch + '\n' + logResult.message, tickets, manualActions, { commits: [logResult], }); - } else { - await collectTicketsAndManualActions(currentGitBranch + "\n" + logResult.message, tickets, manualActions, { commits: [logResult] }); - commitsSummary += "\n\n"; + commitsSummary += '\n\n'; } } // Tickets and references can also be in PR description if (pullRequestInfo) { - const prText = (pullRequestInfo.title || "") + (pullRequestInfo.description || ""); - await collectTicketsAndManualActions(currentGitBranch + "\n" + prText, tickets, manualActions, { pullRequestInfo: pullRequestInfo }); + const prText = (pullRequestInfo.title || '') + (pullRequestInfo.description || ''); + await collectTicketsAndManualActions(currentGitBranch + '\n' + prText, tickets, manualActions, { + pullRequestInfo: pullRequestInfo, + }); } // Unify and sort tickets - const ticketsSorted = sortArray(arrayUniqueByKey(tickets, "id"), { by: ["id"], order: ["asc"] }); + const ticketsSorted: Ticket[] = sortArray(arrayUniqueByKey(tickets, 'id'), { by: ['id'], order: ['asc'] }); uxLog(this, c.grey(`[TicketProvider] Found ${ticketsSorted.length} tickets in commit bodies`)); // Try to contact Ticketing servers to gather more info await TicketProvider.collectTicketsInfo(ticketsSorted); @@ -127,24 +150,24 @@ export async function computeCommitsSummary(checkOnly, pullRequestInfo: any) { // Add manual actions in markdown const manualActionsSorted = [...new Set(manualActions)].reverse(); if (manualActionsSorted.length > 0) { - let manualActionsMarkdown = "## Manual actions\n\n"; + let manualActionsMarkdown = '## Manual actions\n\n'; for (const manualAction of manualActionsSorted) { - manualActionsMarkdown += "- " + manualAction + "\n"; + manualActionsMarkdown += '- ' + manualAction + '\n'; } - commitsSummary = manualActionsMarkdown + "\n\n" + commitsSummary; + commitsSummary = manualActionsMarkdown + '\n\n' + commitsSummary; } // Add tickets in markdown if (ticketsSorted.length > 0) { - let ticketsMarkdown = "## Tickets\n\n"; + let ticketsMarkdown = '## Tickets\n\n'; for (const ticket of ticketsSorted) { if (ticket.foundOnServer) { - ticketsMarkdown += "- [" + ticket.id + "](" + ticket.url + ") " + ticket.subject + "\n"; + ticketsMarkdown += '- [' + ticket.id + '](' + ticket.url + ') ' + ticket.subject + '\n'; } else { - ticketsMarkdown += "- [" + ticket.id + "](" + ticket.url + ")\n"; + ticketsMarkdown += '- [' + ticket.id + '](' + ticket.url + ')\n'; } } - commitsSummary = ticketsMarkdown + "\n\n" + commitsSummary; + commitsSummary = ticketsMarkdown + '\n\n' + commitsSummary; } return { diff --git a/src/common/utils/index.ts b/src/common/utils/index.ts index efaa04f93..610b7ceca 100644 --- a/src/common/utils/index.ts +++ b/src/common/utils/index.ts @@ -1,29 +1,30 @@ -import * as c from "chalk"; -import * as child from "child_process"; -import * as crossSpawn from "cross-spawn"; -import * as crypto from "crypto"; -import * as csvStringify from "csv-stringify/lib/sync"; -import * as fs from "fs-extra"; -import * as os from "os"; -import * as path from "path"; +import c from 'chalk'; +import * as child from 'child_process'; +import { spawn as crossSpawn } from 'cross-spawn'; +import * as crypto from 'crypto'; +import { stringify as csvStringify } from 'csv-stringify/sync'; +import fs from 'fs-extra'; +import * as os from 'os'; +import * as path from 'path'; -import * as util from "util"; -import * as which from "which"; -import * as xml2js from "xml2js"; +import * as util from 'util'; +import which from 'which'; +import * as xml2js from 'xml2js'; const exec = util.promisify(child.exec); -import { SfdxError } from "@salesforce/core"; -import * as ora from "ora"; -import simpleGit, { FileStatusResult, SimpleGit } from "simple-git"; -import { CONSTANTS, getConfig, getReportDirectory, setConfig } from "../../config"; -import { prompts } from "./prompts"; -import { encryptFile } from "../cryptoUtils"; -import { deployMetadatas, truncateProgressLogLines } from "./deployUtils"; -import { promptProfiles, promptUserEmail } from "./orgUtils"; -import { WebSocketClient } from "../websocketClient"; -import * as moment from "moment"; -import { writeXmlFile } from "./xmlUtils"; +import { SfError } from '@salesforce/core'; +import ora from 'ora'; +import { simpleGit, FileStatusResult, SimpleGit } from 'simple-git'; +import { CONSTANTS, getConfig, getReportDirectory, setConfig } from '../../config/index.js'; +import { prompts } from './prompts.js'; +import { encryptFile } from '../cryptoUtils.js'; +import { deployMetadatas, truncateProgressLogLines } from './deployUtils.js'; +import { promptProfiles, promptUserEmail } from './orgUtils.js'; +import { WebSocketClient } from '../websocketClient.js'; +import moment from 'moment'; +import { writeXmlFile } from './xmlUtils.js'; +import { SfCommand } from '@salesforce/sf-plugins-core'; -let pluginsStdout = null; +let pluginsStdout: string | null = null; export const isCI = process.env.CI != null; @@ -33,13 +34,13 @@ export function git(options: any = { output: false }): SimpleGit { // cf: https://github.com/steveukx/git-js/issues/593 return simpleGitInstance.outputHandler((command, stdout, stderr, gitArgs) => { let first = true; - stdout.on("data", (data) => { + stdout.on('data', (data) => { logCommand(); if (options.output) { uxLog(this, c.italic(c.grey(data))); } }); - stderr.on("data", (data) => { + stderr.on('data', (data) => { logCommand(); if (options.output) { uxLog(this, c.italic(c.yellow(data))); @@ -48,9 +49,9 @@ export function git(options: any = { output: false }): SimpleGit { function logCommand() { if (first) { first = false; - const gitArgsStr = (gitArgs || []).join(" "); - if (!(gitArgsStr.includes("branch -v") || gitArgsStr.includes("config --list --show-origin --null"))) { - uxLog(this, `[command] ${c.bold(c.bgWhite(c.grey(command + " " + gitArgsStr)))}`); + const gitArgsStr = (gitArgs || []).join(' '); + if (!(gitArgsStr.includes('branch -v') || gitArgsStr.includes('config --list --show-origin --null'))) { + uxLog(this, `[command] ${c.bold(c.bgWhite(c.grey(command + ' ' + gitArgsStr)))}`); } } } @@ -58,14 +59,14 @@ export function git(options: any = { output: false }): SimpleGit { } export async function createTempDir() { - const tmpDir = path.join(os.tmpdir(), "sfdx-hardis-" + Math.random().toString(36).substring(7)); + const tmpDir = path.join(os.tmpdir(), 'sfdx-hardis-' + Math.random().toString(36).substring(7)); await fs.ensureDir(tmpDir); return tmpDir; } export function isGitRepo() { - const isInsideWorkTree = child.spawnSync("git", ["rev-parse", "--is-inside-work-tree"], { - encoding: "utf8", + const isInsideWorkTree = child.spawnSync('git', ['rev-parse', '--is-inside-work-tree'], { + encoding: 'utf8', windowsHide: true, }); return isInsideWorkTree.status === 0; @@ -75,9 +76,9 @@ export async function getGitRepoName() { if (!isGitRepo) { return null; } - const origin = await git().getConfig("remote.origin.url"); - if (origin.value && origin.value.includes("/")) { - return /[^/]*$/.exec(origin.value)[0]; + const origin = await git().getConfig('remote.origin.url'); + if (origin.value && origin.value.includes('/')) { + return (/[^/]*$/.exec(origin.value) || '')[0]; } return null; } @@ -86,7 +87,7 @@ export async function getGitRepoUrl() { if (!isGitRepo) { return null; } - const origin = await git().getConfig("remote.origin.url"); + const origin = await git().getConfig('remote.origin.url'); if (origin && origin.value) { // Replace https://username:token@gitlab.com/toto by https://gitlab.com/toto return origin.value.replace(/\/\/(.*:.*@)/gm, `//`); @@ -104,38 +105,38 @@ export async function gitHasLocalUpdates(options = { show: false }) { // Install plugin if not present export async function checkSfdxPlugin(pluginName: string) { - // Manage cache of sfdx plugins result + // Manage cache of SF CLI Plugins result if (pluginsStdout == null) { - const config = await getConfig("user"); + const config = await getConfig('user'); if (config.sfdxPluginsStdout) { pluginsStdout = config.sfdxPluginsStdout; } else { - const pluginsRes = await exec("sfdx plugins"); + const pluginsRes = await exec('sf plugins'); pluginsStdout = pluginsRes.stdout; - await setConfig("user", { sfdxPluginsStdout: pluginsStdout }); + await setConfig('user', { sfdxPluginsStdout: pluginsStdout }); } } - if (!pluginsStdout.includes(pluginName)) { + if (!(pluginsStdout || '').includes(pluginName)) { uxLog( this, c.yellow( - `[dependencies] Installing sfdx plugin ${c.green(pluginName)}... \nIf is stays stuck for too long, please run ${c.green( - `sfdx plugins:install ${pluginName}`, - )})`, - ), + `[dependencies] Installing SF CLI plugin ${c.green( + pluginName + )}... \nIf is stays stuck for too long, please run ${c.green(`sf plugins install ${pluginName}`)})` + ) ); - const installCommand = `echo y|sfdx plugins:install ${pluginName}`; + const installCommand = `echo y|sf plugins install ${pluginName}`; await execCommand(installCommand, this, { fail: true, output: false }); } } const dependenciesInstallLink = { - git: "Download installer at https://git-scm.com/downloads", + git: 'Download installer at https://git-scm.com/downloads', openssl: 'Run "choco install openssl" in Windows Powershell, or use Git Bash as command line tool', }; export async function checkAppDependency(appName) { - const config = await getConfig("user"); + const config = await getConfig('user'); const installedApps = config.installedApps || []; if (installedApps.includes(appName)) { return true; @@ -143,41 +144,51 @@ export async function checkAppDependency(appName) { which(appName) .then(async () => { installedApps.push(appName); - await setConfig("user", { installedApps: installedApps }); + await setConfig('user', { installedApps: installedApps }); }) .catch(() => { - uxLog(this, c.red(`You need ${c.bold(appName)} to be locally installed to run this command.\n${dependenciesInstallLink[appName] || ""}`)); + uxLog( + this, + c.red( + `You need ${c.bold(appName)} to be locally installed to run this command.\n${dependenciesInstallLink[appName] || '' + }` + ) + ); process.exit(); }); } -export async function promptInstanceUrl(orgTypes = ["login", "test"], alias = "default org", defaultOrgChoice: any = null) { +export async function promptInstanceUrl( + orgTypes = ['login', 'test'], + alias = 'default org', + defaultOrgChoice: any = null +) { const customLoginUrlExample = - orgTypes && orgTypes.length === 1 && orgTypes[0] === "login" - ? "https://myclient.lightning.force.com/" - : "https://myclient--preprod.sandbox.lightning.force.com/"; + orgTypes && orgTypes.length === 1 && orgTypes[0] === 'login' + ? 'https://myclient.lightning.force.com/' + : 'https://myclient--preprod.sandbox.lightning.force.com/'; const allChoices = [ { - title: "📝 Custom login URL (Sandbox, DevHub or Production Org)", + title: '📝 Custom login URL (Sandbox, DevHub or Production Org)', description: `Recommended option :) Example: ${customLoginUrlExample}`, - value: "custom", + value: 'custom', }, { - title: "🧪 Sandbox or Scratch org (test.salesforce.com)", - description: "The org I want to connect is a sandbox or a scratch org", - value: "https://test.salesforce.com", + title: '🧪 Sandbox or Scratch org (test.salesforce.com)', + description: 'The org I want to connect is a sandbox or a scratch org', + value: 'https://test.salesforce.com', }, { - title: "☢️ Other: Dev org, Production org or DevHub org (login.salesforce.com)", - description: "The org I want to connect is NOT a sandbox", - value: "https://login.salesforce.com", + title: '☢️ Other: Dev org, Production org or DevHub org (login.salesforce.com)', + description: 'The org I want to connect is NOT a sandbox', + value: 'https://login.salesforce.com', }, ]; const choices = allChoices.filter((choice) => { - if (choice.value === "https://login.salesforce.com" && !orgTypes.includes("login")) { + if (choice.value === 'https://login.salesforce.com' && !orgTypes.includes('login')) { return false; } - if (choice.value === "https://test.salesforce.com" && !orgTypes.includes("test")) { + if (choice.value === 'https://test.salesforce.com' && !orgTypes.includes('test')) { return false; } return true; @@ -185,29 +196,29 @@ export async function promptInstanceUrl(orgTypes = ["login", "test"], alias = "d if (defaultOrgChoice != null) { choices.push({ title: `♻️ ${defaultOrgChoice.instanceUrl}`, - description: "Your current default org", + description: 'Your current default org', value: defaultOrgChoice.instanceUrl, }); } const orgTypeResponse = await prompts({ - type: "select", - name: "value", + type: 'select', + name: 'value', message: c.cyanBright(`What is the base URL or the org you want to connect to, as ${alias} ?`), choices: choices, initial: 1, }); // login.salesforce.com or test.salesforce.com const url = orgTypeResponse.value; - if (url.startsWith("http")) { + if (url.startsWith('http')) { return url; } // Custom url to input const customUrlResponse = await prompts({ - type: "text", - name: "value", - message: c.cyanBright("Please input the base URL of the salesforce org (ex: https://myclient.my.salesforce.com)"), + type: 'text', + name: 'value', + message: c.cyanBright('Please input the base URL of the salesforce org (ex: https://myclient.my.salesforce.com)'), }); - const urlCustom = (customUrlResponse.value || []).replace(".lightning.force.com", ".my.salesforce.com"); + const urlCustom = (customUrlResponse?.value || "").replace('.lightning.force.com', '.my.salesforce.com'); return urlCustom; } @@ -216,7 +227,7 @@ export async function ensureGitRepository(options: any = { init: false, clone: f if (!isGitRepo()) { // Init repo if (options.init) { - await exec("git init -b main"); + await exec('git init -b main'); console.info(c.yellow(c.bold(`[sfdx-hardis] Initialized git repository in ${process.cwd()}`))); } else if (options.clone) { // Clone repo @@ -224,37 +235,37 @@ export async function ensureGitRepository(options: any = { init: false, clone: f if (!cloneUrl) { // Request repo url if not provided const cloneUrlPrompt = await prompts({ - type: "text", - name: "value", + type: 'text', + name: 'value', message: c.cyanBright( - "What is the URL of your git repository ? example: https://gitlab.hardis-group.com/busalesforce/monclient/monclient-org-monitoring.git", + 'What is the URL of your git repository ? example: https://gitlab.hardis-group.com/busalesforce/monclient/monclient-org-monitoring.git' ), }); cloneUrl = cloneUrlPrompt.value; } // Git lcone await new Promise((resolve) => { - crossSpawn("git", ["clone", cloneUrl, "."], { stdio: "inherit" }).on("close", () => { + crossSpawn('git', ['clone', cloneUrl, '.'], { stdio: 'inherit' }).on('close', () => { resolve(null); }); }); - uxLog(this, `Git repository cloned. ${c.yellow("Please run again the same command :)")}`); + uxLog(this, `Git repository cloned. ${c.yellow('Please run again the same command :)')}`); process.exit(0); } else { - throw new SfdxError("You need to be at the root of a git repository to run this command"); + throw new SfError('You need to be at the root of a git repository to run this command'); } } // Check if root else if (options.mustBeRoot) { const gitRepoRoot = await getGitRepoRoot(); if (path.resolve(gitRepoRoot) !== path.resolve(process.cwd())) { - throw new SfdxError(`You must be at the root of the git repository (${path.resolve(gitRepoRoot)})`); + throw new SfError(`You must be at the root of the git repository (${path.resolve(gitRepoRoot)})`); } } } export async function getGitRepoRoot() { - const gitRepoRoot = await git().revparse(["--show-toplevel"]); + const gitRepoRoot = await git().revparse(['--show-toplevel']); return gitRepoRoot; } @@ -265,7 +276,7 @@ export async function getCurrentGitBranch(options: any = { formatted: false }) { } const gitBranch = process.env.CI_COMMIT_REF_NAME || (await git().branchLocal()).current; if (options.formatted === true) { - return gitBranch.replace("/", "__"); + return gitBranch.replace('/', '__'); } return gitBranch; } @@ -274,30 +285,32 @@ export async function getLatestGitCommit() { if (git == null) { return null; } - const log = await git().log(["-1"]); + const log = await git().log(['-1']); return log?.latest ?? null; } // Select git branch and checkout & pull if requested -export async function selectGitBranch(options: { remote: true; checkOutPull: boolean } = { remote: true, checkOutPull: false }) { - const gitBranchOptions = ["--list"]; +export async function selectGitBranch( + options: { remote: true; checkOutPull: boolean } = { remote: true, checkOutPull: false } +) { + const gitBranchOptions = ['--list']; if (options.remote) { - gitBranchOptions.push("-r"); + gitBranchOptions.push('-r'); } const branches = await git().branch(gitBranchOptions); const branchResp = await prompts({ - type: "select", - name: "value", - message: "Please select a Git branch", + type: 'select', + name: 'value', + message: 'Please select a Git branch', choices: branches.all.map((branchName) => { - return { title: branchName.replace("origin/", ""), value: branchName.replace("origin/", "") }; + return { title: branchName.replace('origin/', ''), value: branchName.replace('origin/', '') }; }), }); const branch = branchResp.value; // Checkout & pull if requested if (options.checkOutPull) { await gitCheckOutRemote(branch); - WebSocketClient.sendMessage({ event: "refreshStatus" }); + WebSocketClient.sendMessage({ event: 'refreshStatus' }); } return branch; } @@ -308,7 +321,7 @@ export async function gitCheckOutRemote(branchName: string) { } // Get local git branch name -export async function ensureGitBranch(branchName: string, options: any = { init: false, parent: "current" }) { +export async function ensureGitBranch(branchName: string, options: any = { init: false, parent: 'current' }) { if (git == null) { if (options.init) { await ensureGitRepository({ init: true }); @@ -325,15 +338,15 @@ export async function ensureGitBranch(branchName: string, options: any = { init: await git().checkout(branchName); // await git().pull() } else { - if (options?.parent === "main") { + if (options?.parent === 'main') { // Create from main branch - const mainBranch = branches.all.includes("main") - ? "main" - : branches.all.includes("origin/main") - ? "main" - : branches.all.includes("remotes/origin/main") - ? "main" - : "master"; + const mainBranch = branches.all.includes('main') + ? 'main' + : branches.all.includes('origin/main') + ? 'main' + : branches.all.includes('remotes/origin/main') + ? 'main' + : 'master'; await git().checkout(mainBranch); await git().checkoutBranch(branchName, mainBranch); } else { @@ -348,7 +361,7 @@ export async function ensureGitBranch(branchName: string, options: any = { init: // Checks that current git status is clean. export async function checkGitClean(options: any) { if (git == null) { - throw new SfdxError("[sfdx-hardis] You must be within a git repository"); + throw new SfError('[sfdx-hardis] You must be within a git repository'); } const gitStatus = await git({ output: true }).status(); if (gitStatus.files.length > 0) { @@ -356,15 +369,15 @@ export async function checkGitClean(options: any) { .map((fileStatus: FileStatusResult) => { return `(${fileStatus.working_dir}) ${getSfdxFileLabel(fileStatus.path)}`; }) - .join("\n"); + .join('\n'); if (options.allowStash) { - await execCommand("git add --all", this, { output: true, fail: true }); - await execCommand("git stash", this, { output: true, fail: true }); + await execCommand('git add --all', this, { output: true, fail: true }); + await execCommand('git stash', this, { output: true, fail: true }); } else { - throw new SfdxError( + throw new SfError( `[sfdx-hardis] Branch ${c.bold(gitStatus.current)} is not clean. You must ${c.bold( - "commit or reset", - )} the following local updates:\n${c.yellow(localUpdates)}`, + 'commit or reset' + )} the following local updates:\n${c.yellow(localUpdates)}` ); } } @@ -373,14 +386,16 @@ export async function checkGitClean(options: any) { // Interactive git add export async function interactiveGitAdd(options: any = { filter: [], groups: [] }) { if (git == null) { - throw new SfdxError("[sfdx-hardis] You must be within a git repository"); + throw new SfError('[sfdx-hardis] You must be within a git repository'); } // List all files and arrange their format - const config = await getConfig("project"); + const config = await getConfig('project'); const gitStatus = await git().status(); let filesFiltered = gitStatus.files .filter((fileStatus: FileStatusResult) => { - return (options.filter || []).filter((filterString: string) => fileStatus.path.includes(filterString)).length === 0; + return ( + (options.filter || []).filter((filterString: string) => fileStatus.path.includes(filterString)).length === 0 + ); }) .map((fileStatus: FileStatusResult) => { fileStatus.path = normalizeFileStatusPath(fileStatus.path, config); @@ -391,7 +406,7 @@ export async function interactiveGitAdd(options: any = { filter: [], groups: [] if (groups.length === 0) { groups = [ { - label: "All", + label: 'All', regex: /(.*)/i, defaultSelect: false, ignore: false, @@ -399,7 +414,7 @@ export async function interactiveGitAdd(options: any = { filter: [], groups: [] ]; } // Ask user what he/she wants to git add/rm - const result = { added: [], removed: [] }; + const result: any = { added: [], removed: [] }; if (filesFiltered.length > 0) { for (const group of groups) { // Extract files matching group regex @@ -415,10 +430,12 @@ export async function interactiveGitAdd(options: any = { filter: [], groups: [] }); // Ask user for input const selectFilesStatus = await prompts({ - type: "multiselect", - name: "files", + type: 'multiselect', + name: 'files', message: c.cyanBright( - `Please select ${c.red("carefully")} the ${c.bgWhite(c.red(c.bold(group.label.toUpperCase())))} files you want to commit (save)}`, + `Please select ${c.red('carefully')} the ${c.bgWhite( + c.red(c.bold(group.label.toUpperCase())) + )} files you want to commit (save)}` ), choices: matchingFiles.map((fileStatus: FileStatusResult) => { return { @@ -434,26 +451,26 @@ export async function interactiveGitAdd(options: any = { filter: [], groups: [] // Separate added to removed files result.added.push( ...selectFilesStatus.files - .filter((fileStatus: FileStatusResult) => fileStatus.working_dir !== "D") - .map((fileStatus: FileStatusResult) => fileStatus.path.replace('"', "")), + .filter((fileStatus: FileStatusResult) => fileStatus.working_dir !== 'D') + .map((fileStatus: FileStatusResult) => fileStatus.path.replace('"', '')) ); result.removed.push( ...selectFilesStatus.files - .filter((fileStatus: FileStatusResult) => fileStatus.working_dir === "D") - .map((fileStatus: FileStatusResult) => fileStatus.path.replace('"', "")), + .filter((fileStatus: FileStatusResult) => fileStatus.working_dir === 'D') + .map((fileStatus: FileStatusResult) => fileStatus.path.replace('"', '')) ); } if (filesFiltered.length > 0) { uxLog( this, c.grey( - "The following list of files has not been proposed for selection\n" + - filesFiltered - .map((fileStatus: FileStatusResult) => { - return ` - (${getGitWorkingDirLabel(fileStatus.working_dir)}) ${getSfdxFileLabel(fileStatus.path)}`; - }) - .join("\n"), - ), + 'The following list of files has not been proposed for selection\n' + + filesFiltered + .map((fileStatus: FileStatusResult) => { + return ` - (${getGitWorkingDirLabel(fileStatus.working_dir)}) ${getSfdxFileLabel(fileStatus.path)}`; + }) + .join('\n') + ) ); } // Ask user for confirmation @@ -462,29 +479,29 @@ export async function interactiveGitAdd(options: any = { filter: [], groups: [] .map((group) => { return ( c.bgWhite(c.red(c.bold(group.label))) + - "\n" + + '\n' + group.files .map((fileStatus: FileStatusResult) => { return ` - (${getGitWorkingDirLabel(fileStatus.working_dir)}) ${getSfdxFileLabel(fileStatus.path)}`; }) - .join("\n") + - "\n" + .join('\n') + + '\n' ); }) - .join("\n"); + .join('\n'); const addFilesResponse = await prompts({ - type: "select", - name: "addFiles", + type: 'select', + name: 'addFiles', message: c.cyanBright(`Do you confirm that you want to add the following list of files ?\n${confirmationText}`), choices: [ - { title: "Yes, my selection is complete !", value: "yes" }, - { title: "No, I want to select again", value: "no" }, - { title: "Let me out of here !", value: "bye" }, + { title: 'Yes, my selection is complete !', value: 'yes' }, + { title: 'No, I want to select again', value: 'no' }, + { title: 'Let me out of here !', value: 'bye' }, ], initial: 0, }); // Commit if requested - if (addFilesResponse.addFiles === "yes") { + if (addFilesResponse.addFiles === 'yes') { if (result.added.length > 0) { await git({ output: true }).add(result.added); } @@ -493,16 +510,16 @@ export async function interactiveGitAdd(options: any = { filter: [], groups: [] } } // restart selection - else if (addFilesResponse.addFiles === "no") { + else if (addFilesResponse.addFiles === 'no') { return await interactiveGitAdd(options); } // exit else { - uxLog(this, "Cancelled by user"); + uxLog(this, 'Cancelled by user'); process.exit(0); } } else { - uxLog(this, c.cyan("There is no new file to commit")); + uxLog(this, c.cyan('There is no new file to commit')); } return result; } @@ -511,24 +528,24 @@ export async function interactiveGitAdd(options: any = { filter: [], groups: [] export async function gitAddCommitPush( options: any = { init: false, - pattern: "./*", - commitMessage: "Updated by sfdx-hardis", + pattern: './*', + commitMessage: 'Updated by sfdx-hardis', branch: null, - }, + } ) { if (git == null) { if (options.init) { // Initialize git repo - await execCommand("git init -b main", this); - await git().checkoutBranch(options.branch || "dev", "main"); + await execCommand('git init -b main', this); + await git().checkoutBranch(options.branch || 'dev', 'main'); } } // Add, commit & push const currentgitBranch = (await git().branchLocal()).current; await git() - .add(options.pattern || "./*") - .commit(options.commitMessage || "Updated by sfdx-hardis") - .push(["-u", "origin", currentgitBranch]); + .add(options.pattern || './*') + .commit(options.commitMessage || 'Updated by sfdx-hardis') + .push(['-u', 'origin', currentgitBranch]); } // Normalize git FileStatus path @@ -540,7 +557,7 @@ export function normalizeFileStatusPath(fileStatusPath: string, config): string fileStatusPath = fileStatusPath.slice(0, -1); } if (config.gitRootFolderPrefix) { - fileStatusPath = fileStatusPath.replace(config.gitRootFolderPrefix, ""); + fileStatusPath = fileStatusPath.replace(config.gitRootFolderPrefix, ''); } return fileStatusPath; } @@ -553,10 +570,10 @@ export async function execSfdxJson( fail: false, output: false, debug: false, - }, + } ): Promise { - if (!command.includes("--json")) { - command += " --json"; + if (!command.includes('--json')) { + command += ' --json'; } return await execCommand(command, commandThis, options); } @@ -564,30 +581,39 @@ export async function execSfdxJson( // Execute command export async function execCommand( command: string, - commandThis: any, + commandThis: SfCommand | null, options: any = { fail: false, output: false, debug: false, spinner: true, - }, + } ): Promise { let commandLog = `[sfdx-hardis][command] ${c.bold(c.bgWhite(c.grey(command)))}`; const execOptions: any = { maxBuffer: 10000 * 10000 }; if (options.cwd) { execOptions.cwd = options.cwd; if (path.resolve(execOptions.cwd) !== path.resolve(process.cwd())) { - commandLog += c.grey(` ${c.italic("in directory")} ${execOptions.cwd}`); + commandLog += c.grey(` ${c.italic('in directory')} ${execOptions.cwd}`); } } - let commandResult = null; - // Call command (disable color before for json parsing) - const prevForceColor = process.env.FORCE_COLOR; - process.env.FORCE_COLOR = "0"; - const output = options.output !== null ? options.output : !process.argv.includes("--json"); + + const env = Object.assign({}, process.env); + // Disable colors for json parsing + // Remove NODE_OPTIONS in case it contains --inspect-brk to avoid to trigger again the debugger + env.FORCE_COLOR = '0'; + if (env?.NODE_OPTIONS && env.NODE_OPTIONS.includes("--inspect-brk")) { + env.NODE_OPTIONS = ""; + } + if (env?.JSFORCE_LOG_LEVEL) { + env.JSFORCE_LOG_LEVEL = ""; + } + execOptions.env = env; + let commandResult: any = {}; + const output = options.output !== null ? options.output : !commandThis?.argv?.includes('--json'); let spinner: any; if (output && !(options.spinner === false)) { - spinner = ora({ text: commandLog, spinner: "moon" }).start(); + spinner = ora({ text: commandLog, spinner: 'moon' }).start(); } else { uxLog(this, commandLog); } @@ -600,20 +626,23 @@ export async function execCommand( if (spinner) { spinner.fail(commandLog); } - process.env.FORCE_COLOR = prevForceColor; // Display error in red if not json - if (!command.includes("--json") || options.fail) { - const strErr = truncateProgressLogLines(`${e.stdout}\n${e.stderr}`); + if (!command.includes('--json') || options.fail) { + const strErr = truncateProgressLogLines(`${(e as any).stdout}\n${(e as any).stderr}`); console.error(c.red(strErr)); - e.message = e.message += "\n" + strErr; + (e as Error).message = (e as Error).message += '\n' + strErr; // Manage retry if requested if (options.retry != null) { options.retry.tryCount = (options.retry.tryCount || 0) + 1; if ( options.retry.tryCount <= (options.retry.retryMaxAttempts || 1) && - (options.retry.retryStringConstraint == null || (e.stdout + e.stderr).includes(options.retry.retryStringConstraint)) + (options.retry.retryStringConstraint == null || + ((e as any).stdout + (e as any).stderr).includes(options.retry.retryStringConstraint)) ) { - uxLog(commandThis, c.yellow(`Retry command: ${options.retry.tryCount} on ${options.retry.retryMaxAttempts || 1}`)); + uxLog( + commandThis, + c.yellow(`Retry command: ${options.retry.tryCount} on ${options.retry.retryMaxAttempts || 1}`) + ); if (options.retry.retryDelay) { uxLog(this, `Waiting ${options.retry.retryDelay} seconds before retrying command`); await new Promise((resolve) => setTimeout(resolve, options.retry.retryDelay * 1000)); @@ -626,7 +655,7 @@ export async function execCommand( // if --json, we should not have a crash, so return status 1 + output log return { status: 1, - errorMessage: `[sfdx-hardis][ERROR] Error processing command\n$${e.stdout}\n${e.stderr}`, + errorMessage: `[sfdx-hardis][ERROR] Error processing command\n$${(e as any).stdout}\n${(e as any).stderr}`, error: e, }; } @@ -635,8 +664,7 @@ export async function execCommand( uxLog(commandThis, c.italic(c.grey(truncateProgressLogLines(commandResult.stdout)))); } // Return status 0 if not --json - process.env.FORCE_COLOR = prevForceColor; - if (!command.includes("--json")) { + if (!command.includes('--json')) { return { status: 0, stdout: commandResult.stdout, @@ -647,10 +675,10 @@ export async function execCommand( try { const parsedResult = JSON.parse(commandResult.stdout); if (options.fail && parsedResult.status && parsedResult.status > 0) { - throw new SfdxError(c.red(`[sfdx-hardis][ERROR] Command failed: ${commandResult}`)); + throw new SfError(c.red(`[sfdx-hardis][ERROR] Command failed: ${commandResult}`)); } if (commandResult.stderr && commandResult.stderr.length > 2) { - uxLog(this, "[sfdx-hardis][WARNING] stderr: " + c.yellow(commandResult.stderr)); + uxLog(this, '[sfdx-hardis][WARNING] stderr: ' + c.yellow(commandResult.stderr)); } return parsedResult; } catch (e) { @@ -658,7 +686,8 @@ export async function execCommand( return { status: 1, errorMessage: c.red( - `[sfdx-hardis][ERROR] Error parsing JSON in command result: ${e.message}\n${commandResult.stdout}\n${commandResult.stderr})`, + `[sfdx-hardis][ERROR] Error parsing JSON in command result: ${(e as Error).message}\n${commandResult.stdout}\n${commandResult.stderr + })` ), }; } @@ -667,7 +696,9 @@ export async function execCommand( /* Ex: force-app/main/default/layouts/Opportunity-Opportunity %28Marketing%29 Layout.layout-meta.xml becomes layouts/Opportunity-Opportunity (Marketing Layout).layout-meta.xml */ export function getSfdxFileLabel(filePath: string) { - const cleanStr = decodeURIComponent(filePath.replace("force-app/main/default/", "").replace("force-app/main/", "").replace('"', "")); + const cleanStr = decodeURIComponent( + filePath.replace('force-app/main/default/', '').replace('force-app/main/', '').replace('"', '') + ); const dotNumbers = (filePath.match(/\./g) || []).length; if (dotNumbers > 1) { const m = /(.*)\/(.*)\..*\..*/.exec(cleanStr); @@ -684,7 +715,7 @@ export function getSfdxFileLabel(filePath: string) { } function getGitWorkingDirLabel(workingDir) { - return workingDir === "?" ? "CREATED" : workingDir === "D" ? "DELETED" : workingDir === "M" ? "UPDATED" : "OOOOOPS"; + return workingDir === '?' ? 'CREATED' : workingDir === 'D' ? 'DELETED' : workingDir === 'M' ? 'UPDATED' : 'OOOOOPS'; } const elapseAll = {}; @@ -695,7 +726,7 @@ export function elapseEnd(text: string, commandThis: any = this) { if (elapseAll[text]) { const elapsed = Number(process.hrtime.bigint() - elapseAll[text]); const ms = elapsed / 1000000; - uxLog(commandThis, c.grey(c.italic(text + " " + moment().startOf("day").milliseconds(ms).format("H:mm:ss.SSS")))); + uxLog(commandThis, c.grey(c.italic(text + ' ' + moment().startOf('day').milliseconds(ms).format('H:mm:ss.SSS')))); delete elapseAll[text]; } } @@ -737,7 +768,7 @@ export async function filterPackageXml( removeStandard: false, removeFromPackageXmlFile: null, updateApiVersion: null, - }, + } ): Promise<{ updated: boolean; message: string }> { let updated = false; let message = `[sfdx-hardis] ${packageXmlFileOut} not updated`; @@ -745,15 +776,16 @@ export async function filterPackageXml( const manifest = await xml2js.parseStringPromise(initialFileContent); // Remove namespaces if ((options.removeNamespaces || []).length > 0) { - uxLog(this, c.grey(`Removing items from namespaces ${options.removeNamespaces.join(",")} ...`)); + uxLog(this, c.grey(`Removing items from namespaces ${options.removeNamespaces.join(',')} ...`)); manifest.Package.types = manifest.Package.types.map((type: any) => { type.members = type.members.filter((member: string) => { const startsWithNamespace = options.removeNamespaces.filter((ns: string) => member.startsWith(ns)).length > 0; if (startsWithNamespace) { - const splits = member.split("."); + const splits = member.split('.'); if ( splits.length === 2 && - (((splits[1].match(/__/g) || []).length == 1 && splits[1].endsWith("__c")) || (splits[1].match(/__/g) || []).length == 0) + (((splits[1].match(/__/g) || []).length == 1 && splits[1].endsWith('__c')) || + (splits[1].match(/__/g) || []).length == 0) ) { // Keep ns__object__c.field__c and ns__object.stuff return true; @@ -777,7 +809,10 @@ export async function filterPackageXml( }); if (destructiveTypes.length > 0) { type.members = type.members.filter((member: string) => { - return destructiveTypes[0].members.filter((destructiveMember: string) => destructiveMember === member).length === 0; + return ( + destructiveTypes[0].members.filter((destructiveMember: string) => destructiveMember === member).length === + 0 + ); }); } return type; @@ -785,7 +820,11 @@ export async function filterPackageXml( .filter((type: any) => { // Remove types with wildcard const wildcardDestructiveTypes = destructiveManifest.Package.types.filter((destructiveType: any) => { - return destructiveType.name[0] === type.name[0] && destructiveType.members.length === 1 && destructiveType.members[0] === "*"; + return ( + destructiveType.name[0] === type.name[0] && + destructiveType.members.length === 1 && + destructiveType.members[0] === '*' + ); }); if (wildcardDestructiveTypes.length > 0) { uxLog(this, c.grey(`Removed ${type.name[0]} type`)); @@ -796,13 +835,13 @@ export async function filterPackageXml( // Remove standard objects if (options.removeStandard) { manifest.Package.types = manifest.Package.types.map((type: any) => { - if (["CustomObject"].includes(type.name[0])) { + if (['CustomObject'].includes(type.name[0])) { type.members = type.members.filter((member: string) => { - return member.endsWith("__c"); + return member.endsWith('__c'); }); } type.members = type.members.filter((member: string) => { - return !member.startsWith("standard__"); + return !member.startsWith('standard__'); }); return type; }); @@ -816,21 +855,21 @@ export async function filterPackageXml( // Remove metadata types (named, and empty ones) manifest.Package.types = manifest.Package.types.filter((type: any) => { if (options.keepMetadataTypes.includes(type.name[0])) { - uxLog(this, c.grey("kept " + type.name[0])); + uxLog(this, c.grey('kept ' + type.name[0])); return true; } - uxLog(this, c.grey("removed " + type.name[0])); + uxLog(this, c.grey('removed ' + type.name[0])); return false; }); } // Remove metadata types (named, and empty ones) manifest.Package.types = manifest.Package.types.filter( - (type: any) => !(options.removeMetadatas || []).includes(type.name[0]) && (type?.members?.length || 0) > 0, + (type: any) => !(options.removeMetadatas || []).includes(type.name[0]) && (type?.members?.length || 0) > 0 ); - const builder = new xml2js.Builder({ renderOpts: { pretty: true, indent: " ", newline: "\n" } }); + const builder = new xml2js.Builder({ renderOpts: { pretty: true, indent: ' ', newline: '\n' } }); const updatedFileContent = builder.buildObject(manifest); - if (updatedFileContent !== initialFileContent) { + if (updatedFileContent !== initialFileContent.toString()) { await writeXmlFile(packageXmlFileOut, manifest); updated = true; if (packageXmlFile !== packageXmlFileOut) { @@ -847,7 +886,7 @@ export async function filterPackageXml( // Catch matches in files according to criteria export async function catchMatches(catcher: any, file: string, fileText: string, commandThis: any) { - const matchResults = []; + const matchResults: any[] = []; if (catcher.regex) { // Check if there are matches const matches = await countRegexMatches(catcher.regex, fileText); @@ -861,7 +900,7 @@ export async function catchMatches(catcher: any, file: string, fileText: string, detail[detailCrit.name] = detailCritVal; } } - const catcherLabel = catcher.regex ? `regex ${catcher.regex.toString()}` : "ERROR"; + const catcherLabel = catcher.regex ? `regex ${catcher.regex.toString()}` : 'ERROR'; matchResults.push({ fileName, fileText, @@ -872,7 +911,10 @@ export async function catchMatches(catcher: any, file: string, fileText: string, catcherLabel, }); if (commandThis.debug) { - uxLog(commandThis, `[${fileName}]: Match [${matches}] occurrences of [${catcher.type}/${catcher.name}] with catcher [${catcherLabel}]`); + uxLog( + commandThis, + `[${fileName}]: Match [${matches}] occurrences of [${catcher.type}/${catcher.name}] with catcher [${catcherLabel}]` + ); } } } @@ -881,19 +923,19 @@ export async function catchMatches(catcher: any, file: string, fileText: string, // Count matches of a regex export async function countRegexMatches(regex: RegExp, text: string): Promise { - return ((text || "").match(regex) || []).length; + return ((text || '').match(regex) || []).length; } // Get all captured groups of a regex in a string export async function extractRegexGroups(regex: RegExp, text: string): Promise { - const matches = ((text || "").match(regex) || []).map((e) => e.replace(regex, "$1").trim()); + const matches = ((text || '').match(regex) || []).map((e) => e.replace(regex, '$1').trim()); return matches; // return ((text || '').matchAll(regex) || []).map(item => item.trim()); } export async function extractRegexMatches(regex: RegExp, text: string): Promise { let m; - const matchStrings = []; + const matchStrings: any[] = []; while ((m = regex.exec(text)) !== null) { // This is necessary to avoid infinite loops with zero-width matches if (m.index === regex.lastIndex) { @@ -911,14 +953,14 @@ export async function extractRegexMatches(regex: RegExp, text: string): Promise< export async function extractRegexMatchesMultipleGroups(regex: RegExp, text: string): Promise { let m; - const matchResults = []; + const matchResults: any[] = []; while ((m = regex.exec(text)) !== null) { // This is necessary to avoid infinite loops with zero-width matches if (m.index === regex.lastIndex) { regex.lastIndex++; } // Iterate thru the regex matches - const matchGroups = []; + const matchGroups: any[] = []; m.forEach((match) => { matchGroups.push(match); }); @@ -935,7 +977,7 @@ export function arrayUniqueByKey(array, key: string) { export function arrayUniqueByKeys(array, keysIn: string[]) { const keys = new Set(); const buildKey = (el) => { - return keysIn.map((key) => el[key]).join(";"); + return keysIn.map((key) => el[key]).join(';'); }; return array.filter((el) => !keys.has(buildKey(el)) && keys.add(buildKey(el))); } @@ -945,12 +987,12 @@ export async function generateReports( resultSorted: any[], columns: any[], commandThis: any, - options: any = { logFileName: null, logLabel: "Generated report files:" }, + options: any = { logFileName: null, logLabel: 'Generated report files:' } ): Promise { - const logLabel = options.logLabel || "Generated report files:"; + const logLabel = options.logLabel || 'Generated report files:'; let logFileName = options.logFileName || null; if (!logFileName) { - logFileName = "sfdx-hardis-" + commandThis.id.substr(commandThis.id.lastIndexOf(":") + 1); + logFileName = 'sfdx-hardis-' + commandThis.id.substr(commandThis.id.lastIndexOf(':') + 1); } const dateSuffix = new Date().toJSON().slice(0, 10); const reportDir = await getReportDirectory(); @@ -958,43 +1000,43 @@ export async function generateReports( const reportFileExcel = path.resolve(`${reportDir}/${logFileName}-${dateSuffix}.xls`); await fs.ensureDir(path.dirname(reportFile)); const csv = csvStringify(resultSorted, { - delimiter: ";", + delimiter: ';', header: true, columns, }); - await fs.writeFile(reportFile, csv, "utf8"); + await fs.writeFile(reportFile, csv, 'utf8'); // Trigger command to open CSV file in VsCode extension WebSocketClient.requestOpenFile(reportFile); const excel = csvStringify(resultSorted, { - delimiter: "\t", + delimiter: '\t', header: true, columns, }); - await fs.writeFile(reportFileExcel, excel, "utf8"); + await fs.writeFile(reportFileExcel, excel, 'utf8'); uxLog(commandThis, c.cyan(logLabel)); uxLog(commandThis, c.cyan(`- CSV: ${reportFile}`)); uxLog(commandThis, c.cyan(`- XLS: ${reportFileExcel}`)); return [ - { type: "csv", file: reportFile }, - { type: "xls", file: reportFileExcel }, + { type: 'csv', file: reportFile }, + { type: 'xls', file: reportFileExcel }, ]; } export function uxLog(commandThis: any, text: string) { - text = text.includes("[sfdx-hardis]") ? text : "[sfdx-hardis]" + (text.startsWith("[") ? "" : " ") + text; + text = text.includes('[sfdx-hardis]') ? text : '[sfdx-hardis]' + (text.startsWith('[') ? '' : ' ') + text; if (commandThis?.ux) { commandThis.ux.log(text); - } else if (!(globalThis.processArgv || process.argv).includes("--json")) { + } else if (!(globalThis?.processArgv || process?.argv || "").includes('--json')) { console.log(text); } if (globalThis.hardisLogFileStream) { - globalThis.hardisLogFileStream.write(stripAnsi(text) + "\n"); + globalThis.hardisLogFileStream.write(stripAnsi(text) + '\n'); } } // Caching methods -const SFDX_LOCAL_FOLDER = "/root/.sfdx"; -const TMP_COPY_FOLDER = ".cache/sfdx-hardis/.sfdx"; +const SFDX_LOCAL_FOLDER = '/root/.sfdx'; +const TMP_COPY_FOLDER = '.cache/sfdx-hardis/.sfdx'; let RESTORED = false; // Put local sfdx folder in tmp/sfdx-hardis-local for CI tools needing cache/artifacts to be within repo dir @@ -1008,7 +1050,7 @@ export async function copyLocalSfdxInfo() { dereference: true, overwrite: true, }); - // uxLog(this, `[cache] Copied sfdx cache in ${TMP_COPY_FOLDER} for later reuse`); + // uxLog(this, `[cache] Copied SF CLI cache in ${TMP_COPY_FOLDER} for later reuse`); // const files = fs.readdirSync(TMP_COPY_FOLDER, {withFileTypes: true}).map(item => item.name); // uxLog(this, '[cache]' + JSON.stringify(files)); } @@ -1032,15 +1074,21 @@ export async function restoreLocalSfdxInfo() { } // Generate SSL certificate in temporary folder and copy the key in project directory -export async function generateSSLCertificate(branchName: string, folder: string, commandThis: any, conn: any, options: any) { - uxLog(commandThis, "Generating SSL certificate..."); +export async function generateSSLCertificate( + branchName: string, + folder: string, + commandThis: any, + conn: any, + options: any +) { + uxLog(commandThis, 'Generating SSL certificate...'); const tmpDir = await createTempDir(); const prevDir = process.cwd(); process.chdir(tmpDir); const sslCommand = 'openssl req -nodes -newkey rsa:2048 -keyout server.key -out server.csr -subj "/C=GB/ST=Paris/L=Paris/O=Hardis Group/OU=sfdx-hardis/CN=hardis-group.com"'; await execCommand(sslCommand, this, { output: true, fail: true }); - await execCommand("openssl x509 -req -sha256 -days 3650 -in server.csr -signkey server.key -out server.crt", this, { + await execCommand('openssl x509 -req -sha256 -days 3650 -in server.csr -signkey server.key -out server.crt', this, { output: true, fail: true, }); @@ -1048,64 +1096,73 @@ export async function generateSSLCertificate(branchName: string, folder: string, // Copy certificate key in local project await fs.ensureDir(folder); const targetKeyFile = path.join(folder, `${branchName}.key`); - await fs.copy(path.join(tmpDir, "server.key"), targetKeyFile); + await fs.copy(path.join(tmpDir, 'server.key'), targetKeyFile); const encryptionKey = await encryptFile(targetKeyFile); // Copy certificate file in user home project const crtFile = path.join(os.homedir(), `${branchName}.crt`); - await fs.copy(path.join(tmpDir, "server.crt"), crtFile); + await fs.copy(path.join(tmpDir, 'server.crt'), crtFile); // delete temporary cert folder await fs.remove(tmpDir); // Generate random consumer key for Connected app - const consumerKey = crypto.randomBytes(256).toString("base64").substr(0, 119); + const consumerKey = crypto.randomBytes(256).toString('base64').substr(0, 119); // Ask user if he/she wants to create connected app const confirmResponse = await prompts({ - type: "confirm", - name: "value", + type: 'confirm', + name: 'value', initial: true, - message: c.cyanBright("Do you want sfdx-hardis to configure the SFDX connected app on your org ? (say yes if you don't know)"), + message: c.cyanBright( + "Do you want sfdx-hardis to configure the SFDX connected app on your org ? (say yes if you don't know)" + ), }); if (confirmResponse.value === true) { uxLog( commandThis, c.cyanBright( - `You must configure CI variable ${c.green(c.bold(`SFDX_CLIENT_ID_${branchName.toUpperCase()}`))} with value ${c.bold(c.green(consumerKey))}`, - ), + `You must configure CI variable ${c.green( + c.bold(`SFDX_CLIENT_ID_${branchName.toUpperCase()}`) + )} with value ${c.bold(c.green(consumerKey))}` + ) ); uxLog( commandThis, c.cyanBright( - `You must configure CI variable ${c.green(c.bold(`SFDX_CLIENT_KEY_${branchName.toUpperCase()}`))} with value ${c.bold( - c.green(encryptionKey), - )}`, - ), + `You must configure CI variable ${c.green( + c.bold(`SFDX_CLIENT_KEY_${branchName.toUpperCase()}`) + )} with value ${c.bold(c.green(encryptionKey))}` + ) + ); + uxLog( + commandThis, + c.yellow(`Help to configure CI variables are here: ${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-auth/`) ); - uxLog(commandThis, c.yellow("Help to configure CI variables are here: https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-auth/")); await prompts({ - type: "confirm", - message: c.cyanBright("Hit ENTER when the CI/CD variables are set (check info in the console below)"), + type: 'confirm', + message: c.cyanBright('Hit ENTER when the CI/CD variables are set (check info in the console below)'), }); // Request info for deployment const promptResponses = await prompts([ { - type: "text", - name: "appName", - initial: "sfdxhardis" + Math.floor(Math.random() * 9) + 1, - message: c.cyanBright("How would you like to name the Connected App (ex: sfdx_hardis) ?"), + type: 'text', + name: 'appName', + initial: 'sfdxhardis' + Math.floor(Math.random() * 9) + 1, + message: c.cyanBright('How would you like to name the Connected App (ex: sfdx_hardis) ?'), }, ]); - const contactEmail = await promptUserEmail("Enter a contact email for the Connect App (ex: nicolas.vuillamy@cloudity.com)"); + const contactEmail = await promptUserEmail( + 'Enter a contact email for the Connect App (ex: nicolas.vuillamy@cloudity.com)' + ); const profile = await promptProfiles(conn, { multiselect: false, - message: "What profile will be used for the connected app ? (ex: System Administrator)", - initialSelection: ["System Administrator", "Administrateur Système"], + message: 'What profile will be used for the connected app ? (ex: System Administrator)', + initialSelection: ['System Administrator', 'Administrateur Système'], }); - const crtContent = await fs.readFile(crtFile, "utf8"); + const crtContent = await fs.readFile(crtFile, 'utf8'); // Build ConnectedApp metadata const connectedAppMetadata = ` ${contactEmail} - + http://localhost:1717/OauthRedirect ${crtContent} @@ -1122,7 +1179,7 @@ export async function generateSSLCertificate(branchName: string, folder: string, ENFORCE specific_lifetime:3:HOURS - ${profile || "System Administrator"} + ${profile || 'System Administrator'} `; const packageXml = ` @@ -1136,21 +1193,26 @@ export async function generateSSLCertificate(branchName: string, folder: string, `; // create metadata folder const tmpDirMd = await createTempDir(); - const connectedAppDir = path.join(tmpDirMd, "connectedApps"); + const connectedAppDir = path.join(tmpDirMd, 'connectedApps'); await fs.ensureDir(connectedAppDir); - await fs.writeFile(path.join(tmpDirMd, "package.xml"), packageXml); + await fs.writeFile(path.join(tmpDirMd, 'package.xml'), packageXml); await fs.writeFile(path.join(connectedAppDir, `${promptResponses.appName}.connectedApp`), connectedAppMetadata); // Deploy metadatas try { - uxLog(commandThis, c.cyan(`Deploying Connected App ${c.bold(promptResponses.appName)} into target org ${options.targetUsername || ""} ...`)); + uxLog( + commandThis, + c.cyan( + `Deploying Connected App ${c.bold(promptResponses.appName)} into target org ${options.targetUsername || '' + } ...` + ) + ); const deployRes = await deployMetadatas({ deployDir: tmpDirMd, - testlevel: branchName.includes("production") ? "RunLocalTests" : "NoTestRun", - soap: true, + testlevel: branchName.includes('production') ? 'RunLocalTests' : 'NoTestRun', targetUsername: options.targetUsername ? options.targetUsername : null, }); - console.assert(deployRes.status === 0, c.red("[sfdx-hardis] Failed to deploy metadatas")); + console.assert(deployRes.status === 0, c.red('[sfdx-hardis] Failed to deploy metadatas')); uxLog(commandThis, c.cyan(`Successfully deployed ${c.green(promptResponses.appName)} Connected App`)); await fs.remove(tmpDirMd); await fs.remove(crtFile); @@ -1158,69 +1220,83 @@ export async function generateSSLCertificate(branchName: string, folder: string, } catch (e) { uxLog( commandThis, - c.red("Error pushing ConnectedApp metadata. Maybe the app name is already taken ?\nYou may try again with another connected app name"), + c.red( + 'Error pushing ConnectedApp metadata. Maybe the app name is already taken ?\nYou may try again with another connected app name' + ) ); uxLog( commandThis, c.yellow(` -${c.bold("MANUAL INSTRUCTIONS")} -If this is a Test class issue (production env), you may have to create manually connected app ${promptResponses.appName}: +${c.bold('MANUAL INSTRUCTIONS')} +If this is a Test class issue (production env), you may have to create manually connected app ${promptResponses.appName + }: - Follow instructions here: https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_connected_app.htm - Use certificate ${c.bold(crtFile)} in "Use Digital Signature section" (delete the file from your computer after !) - Once created, update CI/CD variable ${c.green( - c.bold(`SFDX_CLIENT_ID_${branchName.toUpperCase()}`), - )} with the ConsumerKey of the newly created connected app`), + c.bold(`SFDX_CLIENT_ID_${branchName.toUpperCase()}`) + )} with the ConsumerKey of the newly created connected app`) ); await prompts({ - type: "confirm", - message: c.cyanBright("You need to manually configure the connected app. Follow the MANUAL INSTRUCTIONS in the console, then continue here"), + type: 'confirm', + message: c.cyanBright( + 'You need to manually configure the connected app. Follow the MANUAL INSTRUCTIONS in the console, then continue here' + ), }); } } else { // Tell infos to install manually - uxLog(commandThis, c.yellow("Now you can configure the sfdx connected app")); + uxLog(commandThis, c.yellow('Now you can configure the SF CLI connected app')); uxLog( commandThis, `Follow instructions here: ${c.bold( - "https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_connected_app.htm", - )}`, + 'https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_connected_app.htm' + )}` + ); + uxLog( + commandThis, + `Use ${c.green(crtFile)} as certificate on Connected App configuration page, ${c.bold( + `then delete ${crtFile} for security` + )}` ); uxLog( commandThis, - `Use ${c.green(crtFile)} as certificate on Connected App configuration page, ${c.bold(`then delete ${crtFile} for security`)}`, + `- configure CI variable ${c.green( + `SFDX_CLIENT_ID_${branchName.toUpperCase()}` + )} with value of ConsumerKey on Connected App configuration page` ); uxLog( commandThis, - `- configure CI variable ${c.green(`SFDX_CLIENT_ID_${branchName.toUpperCase()}`)} with value of ConsumerKey on Connected App configuration page`, + `- configure CI variable ${c.green(`SFDX_CLIENT_KEY_${branchName.toUpperCase()}`)} with value ${c.green( + encryptionKey + )} key` ); - uxLog(commandThis, `- configure CI variable ${c.green(`SFDX_CLIENT_KEY_${branchName.toUpperCase()}`)} with value ${c.green(encryptionKey)} key`); } } export async function isMonitoringJob() { - if (process.env.SFDX_HARDIS_MONITORING === "true") { + if (process.env.SFDX_HARDIS_MONITORING === 'true') { return true; } if (!isCI) { return false; } - const repoName = await git().revparse("--show-toplevel"); - if (isCI && repoName.includes("monitoring")) { + const repoName = await git().revparse('--show-toplevel'); + if (isCI && repoName.includes('monitoring')) { return true; } return false; } export function getNested(nestedObj, pathArr) { - return pathArr.reduce((obj, key) => (obj && obj[key] !== "undefined" ? obj[key] : undefined), nestedObj); + return pathArr.reduce((obj, key) => (obj && obj[key] !== 'undefined' ? obj[key] : undefined), nestedObj); } const ansiPattern = [ - "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", - "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))", -].join("|"); -const ansiRegex = new RegExp(ansiPattern, "g"); + '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', + '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))', +].join('|'); +const ansiRegex = new RegExp(ansiPattern, 'g'); export function stripAnsi(str: string) { - return str.replace(ansiRegex, ""); + return str.replace(ansiRegex, ''); } diff --git a/src/common/utils/notifUtils.ts b/src/common/utils/notifUtils.ts index d4420a8e3..12d4ecf5a 100644 --- a/src/common/utils/notifUtils.ts +++ b/src/common/utils/notifUtils.ts @@ -3,9 +3,9 @@ This class is deprecated and kept for backward compatibility Use NotifProvider class instead :) */ -import { getCurrentGitBranch } from "."; -import { GitProvider } from "../gitProvider"; -import { NotifSeverity, UtilsNotifs } from "../notifProvider"; +import { getCurrentGitBranch } from "./index.js"; +import { GitProvider } from "../gitProvider/index.js"; +import { NotifSeverity, UtilsNotifs } from "../notifProvider/index.js"; /** * @description This function retrieves the job URL from the GitProvider and creates a notification button if the job URL exists. @@ -15,7 +15,7 @@ import { NotifSeverity, UtilsNotifs } from "../notifProvider"; * @returns {Promise<{ text: string; url: string }[]>} - A Promise that resolves to an array of notification buttons. */ export async function getNotificationButtons(): Promise<{ text: string; url: string }[]> { - const notifButtons = []; + const notifButtons: any[] = []; const jobUrl = await GitProvider.getJobUrl(); if (jobUrl) { notifButtons.push({ text: "View Job", url: jobUrl }); @@ -32,7 +32,7 @@ export async function getNotificationButtons(): Promise<{ text: string; url: str * @returns {Promise} - A Promise that resolves to a markdown string for the current Git branch. */ export async function getBranchMarkdown(type = "slack"): Promise { - const currentGitBranch = await getCurrentGitBranch(); + const currentGitBranch = await getCurrentGitBranch() || ""; let branchMd = type === "jira" ? `{ "label": "${currentGitBranch}"}` diff --git a/src/common/utils/orgConfigUtils.ts b/src/common/utils/orgConfigUtils.ts index bb0ce51f8..9975f9a15 100644 --- a/src/common/utils/orgConfigUtils.ts +++ b/src/common/utils/orgConfigUtils.ts @@ -1,17 +1,19 @@ -import * as c from "chalk"; -import * as fs from "fs-extra"; -import { glob } from "glob"; -import * as puppeteer from "puppeteer"; -import * as yaml from "js-yaml"; -import { uxLog } from "."; +import c from 'chalk'; +import fs from 'fs-extra'; +import { glob } from 'glob'; +import puppeteer from 'puppeteer-core'; +import * as yaml from 'js-yaml'; +import { uxLog } from './index.js'; +import { Connection, SfError } from '@salesforce/core'; +import { DescribeSObjectResult } from '@jsforce/jsforce-node'; const listViewRegex = /objects\/(.*)\/listViews\/(.*)\.listView-meta\.xml/gi; export async function restoreListViewMine(listViewStrings: Array, conn: any, options: any = { debug: false }) { - const listViewItems = []; + const listViewItems: any[] = []; for (const listViewStr of listViewStrings) { // Format Object:ListViewName - const splits = listViewStr.split(":"); + const splits = listViewStr.split(':'); if (splits.length === 2) { listViewItems.push({ object: splits[0], listViewName: splits[1] }); } else { @@ -22,10 +24,10 @@ export async function restoreListViewMine(listViewStrings: Array, conn: uxLog( this, c.red( - `Unable to find list view object and name from ${listViewStr}. Use format ${c.bold("Object:ListViewName")} , or ${c.bold( - ".../objects/OBJECT/listViews/LISTVIEWNAME.listview-meta.xml", - )}`, - ), + `Unable to find list view object and name from ${listViewStr}. Use format ${c.bold( + 'Object:ListViewName' + )} , or ${c.bold('.../objects/OBJECT/listViews/LISTVIEWNAME.listview-meta.xml')}` + ) ); continue; } @@ -39,17 +41,17 @@ export async function restoreListViewMine(listViewStrings: Array, conn: // Start puppeteer const browser = await puppeteer.launch({ - args: ["--no-sandbox", "--disable-setuid-sandbox"], + args: ['--no-sandbox', '--disable-setuid-sandbox'], headless: !(options.debug === true), }); const page = await browser.newPage(); // Process login page - await page.goto(loginUrl, { waitUntil: ["domcontentloaded", "networkidle0"] }); + await page.goto(loginUrl, { waitUntil: ['domcontentloaded', 'networkidle0'] }); - const success = []; - const failed = []; - const unnecessary = []; + const success: any[] = []; + const failed: any[] = []; + const unnecessary: any[] = []; // Restore list views with Mine option for (const listView of listViewItems) { @@ -67,40 +69,67 @@ export async function restoreListViewMine(listViewStrings: Array, conn: await navigationPromise; // Open ListView settings - const filterButton = await page.waitForSelector(".filterButton"); - await filterButton.click(); + const filterButton = await page.waitForSelector('.filterButton'); + if (filterButton) { + await filterButton.click(); + } else { + throw new SfError('Puppeteer: .filterButton not found'); + } // Open Filter by owner popup - const filterByOwnerButtons = await page.waitForXPath("//div[contains(text(), 'Filter by Owner')]"); - await filterByOwnerButtons.click(); + const filterByOwnerButtons = await page.waitForSelector("xpath///div[contains(text(), 'Filter by Owner')]"); + if (filterByOwnerButtons) { + await filterByOwnerButtons.click(); + } else { + throw new SfError('Puppeteer: .filterByOwnerButtons not found'); + } // Select Mine value const mineValue = await page.waitForSelector('input[value="mine"]'); - const mineValueClickableLabel = await mineValue.$x("following-sibling::*"); - await mineValueClickableLabel[0].click(); + if (mineValue) { + const mineValueClickableLabel = await mineValue.$('following-sibling::*'); + if (mineValueClickableLabel) { + await mineValueClickableLabel[0].click(); + } + } else { + throw new SfError('Puppeteer: input[value="mine"] not found'); + } // Click done - const doneButtons = await page.waitForXPath("//span[contains(text(), 'Done')]"); - await doneButtons.click(); + const doneButtons = await page.waitForSelector("xpath///span[contains(text(), 'Done')]"); + if (doneButtons) { + await doneButtons.click(); + } else { + throw new SfError('Puppeteer: Done button not found'); + } // Save try { - const saveButton = await page.waitForSelector(".saveButton", { timeout: 3000 }); - await saveButton.click(); + const saveButton = await page.waitForSelector('.saveButton', { timeout: 3000 }); + if (saveButton) { + await saveButton.click(); + } else { + throw new SfError('Puppeteer: .saveButton not found'); + } } catch { unnecessary.push(`${objectName}:${listViewName}`); - uxLog(this, c.yellow(`Unable to hit save button, but it's probably because ${objectName}.${listViewName} was already set to "Mine"`)); + uxLog( + this, + c.yellow( + `Unable to hit save button, but it's probably because ${objectName}.${listViewName} was already set to "Mine"` + ) + ); continue; } // Confirmed saved toast - await page.waitForXPath("//span[contains(text(), 'List view updated.')]"); + await page.waitForSelector("xpath///span[contains(text(), 'List view updated.')]"); success.push(`${objectName}:${listViewName}`); uxLog(this, c.green(`Successfully set ${objectName}.${listViewName} as "Mine"`)); } catch (e) { // Unexpected puppeteer error failed.push(`${objectName}:${listViewName}`); - uxLog(this, c.red(`Puppeteer error while processing ${objectName}:${listViewName}: ${e.message}`)); + uxLog(this, c.red(`Puppeteer error while processing ${objectName}:${listViewName}: ${(e as Error).message}`)); } } // Close puppeteer browser @@ -110,11 +139,11 @@ export async function restoreListViewMine(listViewStrings: Array, conn: // List all yml files in config/branches and build list of major orgs from them export async function listMajorOrgs() { - const majorOrgs = []; - const branchConfigPattern = "**/config/branches/.sfdx-hardis.*.yml"; + const majorOrgs: any[] = []; + const branchConfigPattern = '**/config/branches/.sfdx-hardis.*.yml'; const configFiles = await glob(branchConfigPattern); for (const configFile of configFiles) { - const props = yaml.load(fs.readFileSync(configFile, "utf-8")) || {}; + const props = (yaml.load(fs.readFileSync(configFile, 'utf-8')) || {}) as any; listViewRegex.lastIndex = 0; const branchNameRegex = /\.sfdx-hardis\.(.*)\.yml/gi; const m = branchNameRegex.exec(configFile); @@ -125,3 +154,19 @@ export async function listMajorOrgs() { } return majorOrgs; } + +export async function checkSfdxHardisTraceAvailable(conn: Connection) { + let traceObject: DescribeSObjectResult; + try { + traceObject = await conn.sobject("SfdxHardisTrace__c").describe(); + } catch (e: any) { + throw new SfError("You need a Custom Setting of type List (activate through Schema Settings), named SfdxHardisTrace__c, with Type__c and Key__c fields (both string, length 80)\n" + e.message); + } + const traceObjectFields = traceObject.fields; + if (traceObjectFields.filter(field => field.name === "Type__c").length === 0) { + throw new SfError("You need a field Type__c (string, length 80) on SfdxHardisTrace__c in target org"); + } + if (traceObjectFields.filter(field => field.name === "Key__c").length === 0) { + throw new SfError("You need a field Key__c (string, length 80) on SfdxHardisTrace__c in target org"); + } +} \ No newline at end of file diff --git a/src/common/utils/orgUtils.ts b/src/common/utils/orgUtils.ts index fcf7a1504..9292d2830 100644 --- a/src/common/utils/orgUtils.ts +++ b/src/common/utils/orgUtils.ts @@ -1,26 +1,27 @@ -import { MetadataUtils } from "../metadata-utils"; -import { prompts } from "./prompts"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; -import { createTempDir, elapseEnd, elapseStart, execCommand, execSfdxJson, isCI, uxLog } from "."; -import { WebSocketClient } from "../websocketClient"; -import { getConfig, setConfig } from "../../config"; -import * as EmailValidator from "email-validator"; -import * as sortArray from "sort-array"; -import { Connection, SfdxError } from "@salesforce/core"; -import { importData } from "./dataUtils"; -import { soqlQuery } from "./apiUtils"; -import { isSfdxProject } from "./projectUtils"; -import { deployMetadatas, forceSourceDeploy, forceSourcePush } from "./deployUtils"; -import { PACKAGE_ROOT_DIR } from "../../settings"; -import { clearCache } from "../cache"; +import { MetadataUtils } from '../metadata-utils/index.js'; +import { prompts } from './prompts.js'; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; +import { createTempDir, elapseEnd, elapseStart, execCommand, execSfdxJson, isCI, uxLog } from './index.js'; +import { WebSocketClient } from '../websocketClient.js'; +import { getConfig, setConfig } from '../../config/index.js'; +import * as EmailValidator from 'email-validator'; +import sortArray from 'sort-array'; +import { Connection, SfError } from '@salesforce/core'; +import { importData } from './dataUtils.js'; +import { soqlQuery } from './apiUtils.js'; +import { isSfdxProject } from './projectUtils.js'; +import { deployMetadatas, smartDeploy, forceSourcePush } from './deployUtils.js'; +import { PACKAGE_ROOT_DIR } from '../../settings.js'; +import { clearCache } from '../cache/index.js'; +import { SfCommand } from '@salesforce/sf-plugins-core'; export async function listProfiles(conn: any) { if (conn in [null, undefined]) { return []; } - const profileRes = await conn.queryAll("SELECT Id,Name FROM Profile ORDER BY Name"); + const profileRes = await soqlQuery('SELECT Id,Name FROM Profile ORDER BY Name', conn); return profileRes.records; } @@ -33,9 +34,9 @@ export async function getRecordTypeId(recordTypeInfo: { sObjectType: string; dev } const recordTypeQueryRes = await soqlQuery( `SELECT Id FROM RecordType WHERE SobjectType='${recordTypeInfo.sObjectType}' AND` + - ` DeveloperName='${recordTypeInfo.developerName}'` + - ` LIMIT 1`, - conn, + ` DeveloperName='${recordTypeInfo.developerName}'` + + ` LIMIT 1`, + conn ); if (recordTypeQueryRes.records[0].Id) { recordTypeIdCache[cacheKey] = recordTypeQueryRes.records[0].Id; @@ -47,107 +48,122 @@ export async function getRecordTypeId(recordTypeInfo: { sObjectType: string; dev // Prompt profile(s) for selection /* Example calls from command class: -const profiles = await promptProfiles(this.org.getConnection(),{multiselect: true, initialSelection: ["System Administrator","Administrateur Système"]}); -const profile = await promptProfiles(this.org.getConnection(),{multiselect: false, initialSelection: ["System Administrator","Administrateur Système"]}); +const profiles = await promptProfiles(flags['target-org'].getConnection(),{multiselect: true, initialSelection: ["System Administrator","Administrateur Système"]}); +const profile = await promptProfiles(flags['target-org'].getConnection(),{multiselect: false, initialSelection: ["System Administrator","Administrateur Système"]}); */ export async function promptProfiles( conn: Connection, options: any = { multiselect: false, initialSelection: [], - returnField: "Name", - message: "Please select profile(s)", + returnField: 'Name', + message: 'Please select profile(s)', allowSelectAll: true, - allowSelectAllErrorMessage: "You can not select all profiles", + allowSelectAllErrorMessage: 'You can not select all profiles', allowSelectMine: true, - allowSelectMineErrorMessage: "You can not select the profile your user is assigned to", - }, + allowSelectMineErrorMessage: 'You can not select the profile your user is assigned to', + } ) { const profiles = await listProfiles(conn); // Profiles returned by active connection if (profiles.length > 0) { const profilesSelection = await prompts({ - type: options.multiselect ? "multiselect" : "select", - message: options.message || "Please select profile(s)", - name: "value", + type: options.multiselect ? 'multiselect' : 'select', + message: options.message || 'Please select profile(s)', + name: 'value', choices: profiles.map((profile: any) => { return { title: profile.Name, - value: options.returnField === "record" ? profile : options.returnField === "Id" ? profile.Id : profile.Name, + value: options.returnField === 'record' ? profile : options.returnField === 'Id' ? profile.Id : profile.Name, }; }), }); // Verify that all profiles are not selected if allowSelectAll === false if (options.allowSelectAll === false && profilesSelection.value.length === profiles.length) { - throw new SfdxError(options.allowSelectAllErrorMessage); + throw new SfError(options.allowSelectAllErrorMessage); } // Verify that current user profile is not selected if (options.allowSelectMine === false) { - if (!["record", "Id"].includes(options.returnField)) { - throw new SfdxError("You can not use option allowSelectMine:false if you don't use record or Id as return value"); + if (!['record', 'Id'].includes(options.returnField)) { + throw new SfError("You can not use option allowSelectMine:false if you don't use record or Id as return value"); } - const userRes = await soqlQuery(`SELECT ProfileId FROM User WHERE Id='${(await conn.identity()).user_id}' LIMIT 1`, conn); - const profileId = userRes.records[0]["ProfileId"]; - if (profilesSelection.value.filter((profileSelected) => profileSelected === profileId || profileSelected?.Id === profileId).length > 0) { - throw new SfdxError(options.allowSelectMineErrorMessage); + const userRes = await soqlQuery( + `SELECT ProfileId FROM User WHERE Id='${(await conn.identity()).user_id}' LIMIT 1`, + conn + ); + const profileId = userRes.records[0]['ProfileId']; + if ( + profilesSelection.value.filter( + (profileSelected) => profileSelected === profileId || profileSelected?.Id === profileId + ).length > 0 + ) { + throw new SfError(options.allowSelectMineErrorMessage); } } return profilesSelection.value || null; } else { // Manual input of comma separated profiles const profilesSelection = await prompts({ - type: "text", - message: options.message || "Please input profile name", - name: "value", + type: 'text', + message: options.message || 'Please input profile name', + name: 'value', initial: options?.initialSelection[0] || null, }); - return options.multiselect ? profilesSelection.value.split(",") : profilesSelection.value; + return options.multiselect ? profilesSelection.value.split(',') : profilesSelection.value; } } export async function promptOrg( - commandThis: any, - options: any = { devHub: false, setDefault: true, scratch: false, devSandbox: false, promptMessage: null }, + commandThis: SfCommand, + options: any = { devHub: false, setDefault: true, scratch: false, devSandbox: false, promptMessage: null, quickOrgList: false } ) { // List all local orgs and request to user - const orgListResult = await MetadataUtils.listLocalOrgs(options.devSandbox === true ? "sandbox" : "any"); + const orgListResult = await MetadataUtils.listLocalOrgs(options.devSandbox === true ? 'sandbox' : 'any', { quickOrgList: options.quickOrgList }); let orgList = [ - ...sortArray(orgListResult?.scratchOrgs || [], { by: ["devHubUsername", "username", "alias", "instanceUrl"], order: ["asc", "asc", "asc"] }), - ...sortArray(orgListResult?.nonScratchOrgs || [], { by: ["username", "alias", "instanceUrl"], order: ["asc", "asc", "asc"] }), + ...sortArray(orgListResult?.scratchOrgs || [], { + by: ['devHubUsername', 'username', 'alias', 'instanceUrl'], + order: ['asc', 'asc', 'asc'], + }), + ...sortArray(orgListResult?.nonScratchOrgs || [], { + by: ['username', 'alias', 'instanceUrl'], + order: ['asc', 'asc', 'asc'], + }), { - username: "🌍 Connect to another org", + username: '🌍 Connect to another org', otherOrg: true, - descriptionForUi: "Connect in Web Browser to a Sandbox, a Production Org, a Dev Org or a Scratch Org", + descriptionForUi: 'Connect in Web Browser to a Sandbox, a Production Org, a Dev Org or a Scratch Org', }, { username: "😱 I already authenticated my org but I don't see it !", clearCache: true, - descriptionForUi: "It might be a sfdx-hardis cache issue, reset it and try again !", + descriptionForUi: 'It might be a sfdx-hardis cache issue, reset it and try again !', }, - { username: "❌ Cancel", cancel: true, descriptionForUi: "Get out of here :)" }, + { username: '❌ Cancel', cancel: true, descriptionForUi: 'Get out of here :)' }, ]; // Filter if we want to list only the scratch attached to current devhub if (options.scratch === true) { - const configGetRes = await execSfdxJson("sfdx config:get defaultdevhubusername", this, { + const configGetRes = await execSfdxJson('sf config get target-dev-hub', this, { output: false, fail: true, }); - const hubOrgUsername = configGetRes?.result[0]?.value || ""; - orgList = orgList.filter((org: any) => org.status === "Active" && org.devHubUsername === hubOrgUsername); + const hubOrgUsername = configGetRes?.result[0]?.value || ''; + orgList = orgList.filter((org: any) => org.status === 'Active' && org.devHubUsername === hubOrgUsername); } // Prompt user const orgResponse = await prompts({ - type: "select", - name: "org", - message: c.cyanBright(options.promptMessage || "Please select an org"), + type: 'select', + name: 'org', + message: c.cyanBright(options.promptMessage || 'Please select an org'), choices: orgList.map((org: any) => { const title = org.username || org.alias || org.instanceUrl; - const description = (title !== org.instanceUrl ? org.instanceUrl : "") + (org.devHubUsername ? ` (Hub: ${org.devHubUsername})` : "-"); + const description = + (title !== org.instanceUrl ? org.instanceUrl : '') + + (org.devHubUsername ? ` (Hub: ${org.devHubUsername})` : '-'); return { title: c.cyan(title), - description: org.descriptionForUi ? org.descriptionForUi : description || "-", + description: org.descriptionForUi ? org.descriptionForUi : description || '-', value: org, }; }), @@ -157,13 +173,13 @@ export async function promptOrg( // Cancel if (org.cancel === true) { - uxLog(commandThis, c.cyan("Cancelled")); + uxLog(commandThis, c.cyan('Cancelled')); process.exit(0); } // Connect to new org if (org.otherOrg === true) { - await commandThis.config.runHook("auth", { + await commandThis.config.runHook('auth', { checkAuth: true, Command: commandThis, devHub: options.devHub === true, @@ -179,51 +195,49 @@ export async function promptOrg( } // Token is expired: login again to refresh it - if (org?.connectedStatus === "RefreshTokenAuthError") { + if (org?.connectedStatus === 'RefreshTokenAuthError' || org?.connectedStatus?.includes('expired')) { uxLog(this, c.yellow(`⚠️ Your authentication is expired. Please login again in the web browser`)); - const loginCommand = "sfdx auth:web:login" + ` --instanceurl ${org.instanceUrl}`; - const loginResult = await execSfdxJson(loginCommand, this, { fail: true, output: true }); + const loginCommand = 'sf org login web' + ` --instance-url ${org.instanceUrl}`; + const loginResult = await execSfdxJson(loginCommand, this, { fail: true, output: false }); org = loginResult.result; } if (options.setDefault === true) { // Set default username - const setDefaultUsernameCommand = - `sfdx config:set ` + `${options.devHub ? "defaultdevhubusername" : "defaultusername"}=${org.username}` + (!isSfdxProject() ? " --global" : ""); - await execSfdxJson(setDefaultUsernameCommand, commandThis, { + const setDefaultOrgCommand = + `sf config set ` + + `${options.devHub ? 'target-dev-hub' : 'target-org'}=${org.username}` + + (!isSfdxProject() ? ' --global' : ''); + await execSfdxJson(setDefaultOrgCommand, commandThis, { fail: true, output: false, }); // If devHub , set alias of project devHub from config file - const config = await getConfig("project"); + const config = await getConfig('project'); if (options.devHub && config.devHubAlias) { - const setAliasCommand = `sfdx alias:set ${config.devHubAlias}=${org.username}`; - await execSfdxJson(setAliasCommand, commandThis, { - fail: true, - output: false, - }); - } else { - // If not devHub, set MY_ORG as alias - const setAliasCommand = `sfdx alias:set MY_ORG=${org.username}`; + const setAliasCommand = `sf alias set ${config.devHubAlias}=${org.username}`; await execSfdxJson(setAliasCommand, commandThis, { fail: true, output: false, }); } - WebSocketClient.sendMessage({ event: "refreshStatus" }); + WebSocketClient.sendMessage({ event: 'refreshStatus' }); // Update local user .sfdx-hardis.yml file with response if scratch has been selected - if (org.username.includes("scratch")) { - await setConfig("user", { + if (org.username.includes('scratch')) { + await setConfig('user', { scratchOrgAlias: org.alias || null, scratchOrgUsername: org.username || org.alias, }); } else { - await setConfig("user", { - scratchOrgAlias: null, - scratchOrgUsername: null, - }); + const configUser = await getConfig('user'); + if (configUser.scratchOrgAlias || configUser.scratchOrgUsername) { + await setConfig('user', { + scratchOrgAlias: null, + scratchOrgUsername: null, + }); + } } } // uxLog(commandThis, c.gray(JSON.stringify(org, null, 2))); @@ -231,10 +245,47 @@ export async function promptOrg( return orgResponse.org; } -export async function promptOrgUsernameDefault(commandThis: any, defaultOrg: string, options: any = { devHub: false, setDefault: true }) { +export async function makeSureOrgIsConnected(targetOrg: string | any) { + // Get connected Status and instance URL + let connectedStatus; + let instanceUrl; + if (typeof targetOrg !== 'string') { + instanceUrl = targetOrg.instanceUrl; + connectedStatus = targetOrg.connectedStatus; + targetOrg = targetOrg.username; + } + else { + const displayOrgCommand = `sf org display --target-org ${targetOrg}`; + const displayResult = await execSfdxJson(displayOrgCommand, this, { + fail: false, + output: false, + }); + connectedStatus = displayResult?.result?.connectedStatus || "error"; + instanceUrl = displayResult?.result?.instanceUrl || "error"; + } + // Org is connected + if (connectedStatus === "Connected") { + return; + } + // Authentication is necessary + if (connectedStatus?.includes("expired")) { + uxLog(this, c.yellow("Your auth token is expired, you need to authenticate again")); + const loginCommand = 'sf org login web' + ` --instance-url ${instanceUrl}`; + await execSfdxJson(loginCommand, this, { fail: true, output: false }); + return; + } + // We shouldn't be here :) + uxLog(this, c.yellow("What are we doing here ? Please declare an issue with the following text: " + instanceUrl + ":" + connectedStatus)); +} + +export async function promptOrgUsernameDefault( + commandThis: any, + defaultOrg: string, + options: any = { devHub: false, setDefault: true, message: "", quickOrgList: true } +) { const defaultOrgRes = await prompts({ - type: "confirm", - message: `Do you want to use org ${defaultOrg}`, + type: 'confirm', + message: options.message || `Do you want to use org ${defaultOrg} ?`, }); if (defaultOrgRes.value === true) { return defaultOrg; @@ -245,18 +296,18 @@ export async function promptOrgUsernameDefault(commandThis: any, defaultOrg: str } export async function promptUserEmail(promptMessage: string | null = null) { - const userConfig = await getConfig("user"); + const userConfig = await getConfig('user'); const promptResponse = await prompts({ - type: "text", - name: "value", - initial: userConfig.userEmail || "", - message: c.cyanBright(promptMessage || "Please input your email address (it will be stored locally for later use)"), + type: 'text', + name: 'value', + initial: userConfig.userEmail || '', + message: c.cyanBright(promptMessage || 'Please input your email address (it will be stored locally for later use)'), validate: (value: string) => EmailValidator.validate(value), }); const userEmail = promptResponse.value; // Store email in user .sfdx-hardis.USERNAME.yml file for later reuse if (userConfig.userEmail !== userEmail) { - await setConfig("user", { + await setConfig('user', { userEmail: userEmail, }); } @@ -266,21 +317,25 @@ export async function promptUserEmail(promptMessage: string | null = null) { // Authenticate with SfdxUrlStore export async function authenticateWithSfdxUrlStore(org: any) { // Authenticate to scratch org to delete - const authFile = path.join(await createTempDir(), "sfdxScratchAuth.txt"); + const authFile = path.join(await createTempDir(), 'sfdxScratchAuth.txt'); const authFileContent = org.scratchOrgSfdxAuthUrl || (org.authFileJson ? JSON.stringify(org.authFileJson) : null); - await fs.writeFile(authFile, authFileContent, "utf8"); - const authCommand = `sfdx auth:sfdxurl:store -f ${authFile}`; + await fs.writeFile(authFile, authFileContent, 'utf8'); + const authCommand = `sf org login sfdx-url --sfdx-url-file ${authFile}`; await execCommand(authCommand, this, { fail: true, output: false }); } // Add package installation to project .sfdx-hardis.yml export async function managePackageConfig(installedPackages, packagesToInstallCompleted) { - const config = await getConfig("project"); + const config = await getConfig('project'); let projectPackages = config.installedPackages || []; let updated = false; for (const installedPackage of installedPackages) { - const matchInstalled = packagesToInstallCompleted.filter((pckg) => pckg.SubscriberPackageId === installedPackage.SubscriberPackageId); - const matchLocal = projectPackages.filter((projectPackage) => installedPackage.SubscriberPackageId === projectPackage.SubscriberPackageId); + const matchInstalled = packagesToInstallCompleted.filter( + (pckg) => pckg.SubscriberPackageId === installedPackage.SubscriberPackageId + ); + const matchLocal = projectPackages.filter( + (projectPackage) => installedPackage.SubscriberPackageId === projectPackage.SubscriberPackageId + ); // Upgrade version of already installed package if (matchInstalled.length > 0 && matchLocal.length > 0) { projectPackages = projectPackages.map((projectPackage) => { @@ -297,55 +352,59 @@ export async function managePackageConfig(installedPackages, packagesToInstallCo this, c.cyan( `Updated package ${c.green(installedPackage.SubscriberPackageName)} with version id ${c.green( - installedPackage.SubscriberPackageVersionId, - )}`, - ), + installedPackage.SubscriberPackageVersionId + )}` + ) ); updated = true; } else if (matchInstalled.length > 0 && matchLocal.length === 0) { // Request user about automatic installation during scratch orgs and deployments const installResponse = await prompts({ - type: "select", - name: "value", - message: c.cyanBright(`Please select the install configuration for ${c.bold(installedPackage.SubscriberPackageName)}`), + type: 'select', + name: 'value', + message: c.cyanBright( + `Please select the install configuration for ${c.bold(installedPackage.SubscriberPackageName)}` + ), choices: [ { title: `Install automatically ${c.bold(installedPackage.SubscriberPackageName)} on scratch orgs only`, - value: "scratch", + value: 'scratch', }, { - title: `Deploy automatically ${c.bold(installedPackage.SubscriberPackageName)} on integration/production orgs only`, - value: "deploy", + title: `Deploy automatically ${c.bold( + installedPackage.SubscriberPackageName + )} on integration/production orgs only`, + value: 'deploy', }, { title: `Both: Install & deploy automatically ${c.bold(installedPackage.SubscriberPackageName)}`, - value: "scratch-deploy", + value: 'scratch-deploy', }, { title: `Do not configure ${c.bold(installedPackage.SubscriberPackageName)} installation / deployment`, - value: "none", + value: 'none', }, ], }); - installedPackage.installOnScratchOrgs = installResponse.value.includes("scratch"); - installedPackage.installDuringDeployments = installResponse.value.includes("deploy"); - if (installResponse.value !== "none" && installResponse.value != null) { + installedPackage.installOnScratchOrgs = installResponse.value.includes('scratch'); + installedPackage.installDuringDeployments = installResponse.value.includes('deploy'); + if (installResponse.value !== 'none' && installResponse.value != null) { projectPackages.push(installedPackage); updated = true; } } } if (updated) { - uxLog(this, "Updated package configuration in sfdx-hardis config"); - await setConfig("project", { installedPackages: projectPackages }); + uxLog(this, 'Updated package configuration in sfdx-hardis config'); + await setConfig('project', { installedPackages: projectPackages }); } } export async function installPackages(installedPackages: any[], orgAlias: string) { const packages = installedPackages || []; - elapseStart("Install all packages"); - await MetadataUtils.installPackagesOnOrg(packages, orgAlias, this, "scratch"); - elapseEnd("Install all packages"); + elapseStart('Install all packages'); + await MetadataUtils.installPackagesOnOrg(packages, orgAlias, this, 'scratch'); + elapseEnd('Install all packages'); } export async function initOrgMetadatas( @@ -354,53 +413,64 @@ export async function initOrgMetadatas( orgAlias: string, projectScratchDef: any, debugMode: boolean, - options: any = {}, + options: any = {} ) { // Push or deploy according to config (default: push) - if ((isCI && process.env.CI_SCRATCH_MODE === "deploy") || process.env.DEBUG_DEPLOY === "true") { - // if CI, use force:source:deploy to make sure package.xml is consistent - uxLog(this, c.cyan(`Deploying project sources to scratch org ${c.green(orgAlias)}...`)); + if ((isCI && process.env.CI_SCRATCH_MODE === 'deploy') || process.env.DEBUG_DEPLOY === 'true') { + // if CI, use sf project deploy start to make sure package.xml is consistent + uxLog(this, c.cyan(`Deploying project sources to org ${c.green(orgAlias)}...`)); const packageXmlFile = - process.env.PACKAGE_XML_TO_DEPLOY || configInfo.packageXmlToDeploy || fs.existsSync("./manifest/package.xml") - ? "./manifest/package.xml" - : "./config/package.xml"; - await forceSourceDeploy(packageXmlFile, false, "NoTestRun", debugMode, this, { + process.env.PACKAGE_XML_TO_DEPLOY || configInfo.packageXmlToDeploy || fs.existsSync('./manifest/package.xml') + ? './manifest/package.xml' + : './config/package.xml'; + await smartDeploy(packageXmlFile, false, 'NoTestRun', debugMode, this, { targetUsername: orgUsername, }); } else { // Use push for local scratch orgs - uxLog(this, c.cyan(`Pushing project sources to scratch org ${c.green(orgAlias)}... (You can see progress in Setup -> Deployment Status)`)); + uxLog( + this, + c.cyan( + `Pushing project sources to org ${c.green( + orgAlias + )}... (You can see progress in Setup -> Deployment Status)` + ) + ); // Suspend sharing calc if necessary - const deferSharingCalc = (projectScratchDef.features || []).includes("DeferSharingCalc"); + const deferSharingCalc = (projectScratchDef.features || []).includes('DeferSharingCalc'); if (deferSharingCalc) { // Deploy to permission set allowing to update SharingCalc await deployMetadatas({ - deployDir: path.join(path.join(PACKAGE_ROOT_DIR, "defaults/utils/deferSharingCalc", ".")), - testlevel: "NoTestRun", - soap: true, + deployDir: path.join(path.join(PACKAGE_ROOT_DIR, 'defaults/utils/deferSharingCalc', '.')), + testlevel: 'NoTestRun', }); // Assign to permission set allowing to update SharingCalc try { - const assignCommand = `sfdx force:user:permset:assign -n SfdxHardisDeferSharingRecalc -u ${orgUsername}`; + const assignCommand = `sf org assign permset --name SfdxHardisDeferSharingRecalc --target-org ${orgUsername}`; await execSfdxJson(assignCommand, this, { fail: false, // Do not fail in case permission set already exists output: false, debug: debugMode, }); - await execCommand("sfdx texei:sharingcalc:suspend", this, { + await execCommand('sf texei:sharingcalc:suspend', this, { fail: false, output: true, debug: debugMode, }); } catch (e) { - uxLog(self, c.yellow("Issue while assigning SfdxHardisDeferSharingRecalc PS and suspending Sharing Calc, but it's probably ok anyway")); - uxLog(self, c.grey(e.message)); + uxLog( + this, + c.yellow( + "Issue while assigning SfdxHardisDeferSharingRecalc PS and suspending Sharing Calc, but it's probably ok anyway" + ) + ); + uxLog(this, c.grey((e as Error).message)); } } await forceSourcePush(orgAlias, this, debugMode, options); // Resume sharing calc if necessary if (deferSharingCalc) { - await execCommand("sfdx texei:sharingcalc:resume", this, { + await execCommand('sf texei:sharingcalc:resume', this, { fail: false, output: true, debug: debugMode, @@ -411,33 +481,39 @@ export async function initOrgMetadatas( // Assign permission sets to user export async function initPermissionSetAssignments(permSets: Array, orgUsername: string) { - uxLog(this, c.cyan("Assigning Permission Sets...")); + uxLog(this, c.cyan('Assigning Permission Sets...')); for (const permSet of permSets) { uxLog(this, c.cyan(`Assigning ${c.bold(permSet.name || permSet)} to sandbox org user`)); - const assignCommand = `sfdx force:user:permset:assign -n ${permSet.name || permSet} -u ${orgUsername}`; + const assignCommand = `sf org assign permset --name ${permSet.name || permSet} --target-org ${orgUsername}`; const assignResult = await execSfdxJson(assignCommand, this, { fail: false, output: false, }); - if (assignResult?.result?.failures?.length > 0 && !assignResult?.result?.failures[0].message.includes("Duplicate")) { - uxLog(this, c.red(`Error assigning to ${c.bold(permSet.name || permSet)}\n${assignResult?.result?.failures[0].message}`)); + if ( + assignResult?.result?.failures?.length > 0 && + !assignResult?.result?.failures[0].message.includes('Duplicate') + ) { + uxLog( + this, + c.red(`Error assigning to ${c.bold(permSet.name || permSet)}\n${assignResult?.result?.failures[0].message}`) + ); } } } // Run initialization apex scripts export async function initApexScripts(orgInitApexScripts: Array, orgAlias: string) { - uxLog(this, c.cyan("Running apex initialization scripts...")); + uxLog(this, c.cyan('Running apex initialization scripts...')); // Build list of apex scripts and check their existence const initApexScripts = orgInitApexScripts.map((scriptName: string) => { if (!fs.existsSync(scriptName)) { - throw new SfdxError(c.red(`[sfdx-hardis][ERROR] Unable to find script ${scriptName}`)); + throw new SfError(c.red(`[sfdx-hardis][ERROR] Unable to find script ${scriptName}`)); } return scriptName; }); // Process apex scripts for (const apexScript of initApexScripts) { - const apexScriptCommand = `sfdx force:apex:execute -f "${apexScript}" -u ${orgAlias}`; + const apexScriptCommand = `sf apex run --file "${apexScript}" --target-org ${orgAlias}`; await execCommand(apexScriptCommand, this, { fail: true, output: true, @@ -449,15 +525,20 @@ export async function initApexScripts(orgInitApexScripts: Array, orgAlias: export async function initOrgData(initDataFolder: string, orgUsername: string) { // Init folder (accounts, etc...) if (fs.existsSync(initDataFolder)) { - uxLog(this, c.cyan("Loading sandbox org initialization data...")); + uxLog(this, c.cyan('Loading sandbox org initialization data...')); await importData(initDataFolder, this, { targetUsername: orgUsername, }); } else { - uxLog(this, c.cyan(`No initialization data: Define a sfdmu workspace in ${initDataFolder} if you need data in your new sandbox orgs`)); + uxLog( + this, + c.cyan( + `No initialization data: Define a sfdmu workspace in ${initDataFolder} if you need data in your new sandbox orgs` + ) + ); } // Import data packages - const config = await getConfig("user"); + const config = await getConfig('user'); const dataPackages = config.dataPackages || []; for (const dataPackage of dataPackages) { if (dataPackage.importInSandboxOrgs === true) { @@ -465,13 +546,18 @@ export async function initOrgData(initDataFolder: string, orgUsername: string) { targetUsername: orgUsername, }); } else { - uxLog(this, c.grey(`Skipped import of ${dataPackage.dataPath} as importInSandboxOrgs is not defined to true in .sfdx-hardis.yml`)); + uxLog( + this, + c.grey( + `Skipped import of ${dataPackage.dataPath} as importInSandboxOrgs is not defined to true in .sfdx-hardis.yml` + ) + ); } } } export async function getOrgAliasUsername(alias: string) { - const aliasListRes = await execSfdxJson("sfdx alias:list", this, { + const aliasListRes = await execSfdxJson('sf alias list', this, { output: false, fail: false, }); @@ -482,10 +568,29 @@ export async function getOrgAliasUsername(alias: string) { return null; } +// Returns true if the org is a sandbox and not a scratch org +export async function isProductionOrg(targetUsername: string, options: any) { + // Use jsforce connection is applicable + if (options?.conn?.username && options.conn.username === targetUsername) { + const orgRes = await soqlQuery('SELECT IsSandbox FROM Organization LIMIT 1', options.conn); + return orgRes.records[0].IsSandbox === false; + } + // Use SF Cli command + const orgQuery = `sf data query --query "SELECT IsSandbox FROM Organization LIMIT 1"` + + (targetUsername ? ` --target-org ${targetUsername}` : ""); + const orgQueryRes = await execSfdxJson(orgQuery, this, { + output: false, + debug: options.debugMode || false, + fail: true, + }); + const orgRes = orgQueryRes?.result?.records || orgQueryRes.records || []; + return orgRes[0].IsSandbox === false; +} + // Returns true if the org is a sandbox and not a scratch org export async function isSandbox(options: any) { if (options.conn) { - const orgRes = await soqlQuery("SELECT IsSandbox,TrialExpirationDate FROM Organization LIMIT 1", options.conn); + const orgRes = await soqlQuery('SELECT IsSandbox,TrialExpirationDate FROM Organization LIMIT 1', options.conn); return orgRes.records[0].IsSandbox === true && orgRes.records[0].TrialExpirationDate == null; } else { return options?.scratch === false; @@ -495,7 +600,7 @@ export async function isSandbox(options: any) { // Returns true if the org is a scratch org and not a sandbox export async function isScratchOrg(options: any) { if (options.conn) { - const orgRes = await soqlQuery("SELECT IsSandbox,TrialExpirationDate FROM Organization LIMIT 1", options.conn); + const orgRes = await soqlQuery('SELECT IsSandbox,TrialExpirationDate FROM Organization LIMIT 1', options.conn); return orgRes.records[0].IsSandbox === true && orgRes.records[0].TrialExpirationDate !== null; } else { return options?.scratch === true; diff --git a/src/common/utils/poolUtils.ts b/src/common/utils/poolUtils.ts index d8fee3fa6..bcb5b236d 100644 --- a/src/common/utils/poolUtils.ts +++ b/src/common/utils/poolUtils.ts @@ -1,23 +1,20 @@ -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as moment from "moment"; -import * as os from "os"; -import * as path from "path"; -import { getConfig, setConfig } from "../../config"; -import { createTempDir, execSfdxJson, isCI, uxLog } from "."; -import { KeyValueProviderInterface } from "./keyValueUtils"; -import { KeyValueXyzProvider } from "../keyValueProviders/keyValueXyz"; -import { KvdbIoProvider } from "../keyValueProviders/kvdbIo"; -import { LocalTestProvider } from "../keyValueProviders/localtest"; -import { SfdxError } from "@salesforce/core"; -import { prompts } from "./prompts"; -import { RedisProvider } from "../keyValueProviders/redis"; -import { SalesforceProvider } from "../keyValueProviders/salesforce"; +import c from 'chalk'; +import fs from 'fs-extra'; +import moment from 'moment'; +import * as os from 'os'; +import * as path from 'path'; +import { getConfig, setConfig } from '../../config/index.js'; +import { createTempDir, execSfdxJson, isCI, uxLog } from './index.js'; +import { KeyValueProviderInterface } from './keyValueUtils.js'; +import { LocalTestProvider } from '../keyValueProviders/localtest.js'; +import { SfError } from '@salesforce/core'; +import { prompts } from './prompts.js'; +import { SalesforceProvider } from '../keyValueProviders/salesforce.js'; let keyValueProvider: KeyValueProviderInterface; export async function getPoolConfig() { - const config = await getConfig("branch"); + const config = await getConfig('branch'); return config.poolConfig || null; } @@ -39,9 +36,9 @@ export async function getPoolStorage(options: any = {}) { export async function setPoolStorage(value: any, options: any = {}) { const providerInitialized = await initializeProvider(options); if (providerInitialized) { - uxLog(this, "[pool] " + c.grey(`Updating poolstorage value...`)); + uxLog(this, '[pool] ' + c.grey(`Updating poolstorage value...`)); const valueSetRes = await keyValueProvider.setValue(null, value); - uxLog(this, "[pool] " + c.grey(`Updated poolstorage value`)); + uxLog(this, '[pool] ' + c.grey(`Updated poolstorage value`)); return valueSetRes; } return null; @@ -57,9 +54,9 @@ export async function updateActiveScratchOrg(scratchOrg: string, keyValues: any, } let updatingPool = false; -export async function addScratchOrgToPool(scratchOrg: any, options: any = { position: "last" }) { +export async function addScratchOrgToPool(scratchOrg: any, options: any = { position: 'last' }) { if (updatingPool === true) { - uxLog(this, c.grey("Already updating scratch org pool: try again in 2000 ms")); + uxLog(this, c.grey('Already updating scratch org pool: try again in 2000 ms')); await new Promise((resolve) => setTimeout(resolve, 2000)); return await addScratchOrgToPool(scratchOrg, options); } else { @@ -71,19 +68,21 @@ export async function addScratchOrgToPool(scratchOrg: any, options: any = { posi } // Write scratch org pool remote storage -async function executeAddScratchOrgToPool(scratchOrg: any, options: any = { position: "last" }) { +async function executeAddScratchOrgToPool(scratchOrg: any, options: any = { position: 'last' }) { const poolStorage = await getPoolStorage(options); // Valid scratch orgs if (scratchOrg.status === 0) { const scratchOrgs = poolStorage.scratchOrgs || []; - if (options.position === "first") { + if (options.position === 'first') { scratchOrgs.push(scratchOrg); } else { scratchOrgs.unshift(scratchOrg); } poolStorage.scratchOrgs = scratchOrgs; await setPoolStorage(poolStorage, options); - await updateActiveScratchOrg(scratchOrg, { Description: `Added to pool by ${os.userInfo().username} on ${moment().format("YYYYMMDD_hhmm")}` }); + await updateActiveScratchOrg(scratchOrg, { + Description: `Added to pool by ${os.userInfo().username} on ${moment().format('YYYYMMDD_hhmm')}`, + }); } else { // Store scratch creation errors /* @@ -92,7 +91,7 @@ async function executeAddScratchOrgToPool(scratchOrg: any, options: any = { posi poolStorage.scratchOrgErrors = scratchOrgErrors; await setPoolStorage(poolStorage, options); */ - uxLog(this, "[pool] " + c.red("Scratch org creation error: \n" + JSON.stringify(scratchOrg))); + uxLog(this, '[pool] ' + c.red('Scratch org creation error: \n' + JSON.stringify(scratchOrg))); } } @@ -102,7 +101,12 @@ export async function fetchScratchOrg(options: any) { const scratchOrg = await tryFetchScratchOrg(options); return scratchOrg; } catch (e) { - uxLog(this, c.yellow(`[pool] Unable to fetch scratch org from pool. That's sad because it's faster !\nError: ${e.message}`)); + uxLog( + this, + c.yellow( + `[pool] Unable to fetch scratch org from pool. That's sad because it's faster !\nError: ${(e as Error).message}` + ) + ); return null; } } @@ -110,38 +114,48 @@ export async function fetchScratchOrg(options: any) { export async function tryFetchScratchOrg(options: any) { const poolStorage = await getPoolStorage(options); if (poolStorage === null) { - uxLog(this, "[pool] " + c.yellow("No valid scratch pool storage has been reachable. Consider fixing the scratch pool config and auth")); + uxLog( + this, + '[pool] ' + + c.yellow('No valid scratch pool storage has been reachable. Consider fixing the scratch pool config and auth') + ); return null; } - uxLog(this, "[pool] " + c.cyan("Trying to fetch a scratch org from scratch orgs pool to improve performances")); + uxLog(this, '[pool] ' + c.cyan('Trying to fetch a scratch org from scratch orgs pool to improve performances')); const scratchOrgs: Array = poolStorage.scratchOrgs || []; if (scratchOrgs.length > 0) { const scratchOrg = scratchOrgs.shift(); - await updateActiveScratchOrg(scratchOrg, { Description: `Fetched by ${os.userInfo().username} on ${moment().format("YYYYMMDD_hhmm")}` }); + await updateActiveScratchOrg(scratchOrg, { + Description: `Fetched by ${os.userInfo().username} on ${moment().format('YYYYMMDD_hhmm')}`, + }); // Remove and save poolStorage.scratchOrgs = scratchOrgs; await setPoolStorage(poolStorage, options); // Authenticate to scratch org - uxLog(this, "[pool] " + c.cyan("Authenticating to scratch org from pool...")); + uxLog(this, '[pool] ' + c.cyan('Authenticating to scratch org from pool...')); const authTempDir = await createTempDir(); - const tmpAuthFile = path.join(authTempDir, "authFile.txt"); - const authFileContent = scratchOrg.scratchOrgSfdxAuthUrl || (scratchOrg.authFileJson ? JSON.stringify(scratchOrg.authFileJson) : null); + const tmpAuthFile = path.join(authTempDir, 'authFile.txt'); + const authFileContent = + scratchOrg.scratchOrgSfdxAuthUrl || (scratchOrg.authFileJson ? JSON.stringify(scratchOrg.authFileJson) : null); if (authFileContent == null) { uxLog( this, - c.yellow(`[pool] Unable to authenticate to org ${scratchOrg.scratchOrgAlias}: ${scratchOrg.scratchOrgUsername} (missing sfdxAuthUrl)`), + c.yellow( + `[pool] Unable to authenticate to org ${scratchOrg.scratchOrgAlias}: ${scratchOrg.scratchOrgUsername} (missing sfdxAuthUrl)` + ) ); return null; } - await fs.writeFile(tmpAuthFile, authFileContent, "utf8"); - const authCommand = `sfdx auth:sfdxurl:store -f ${tmpAuthFile} --setdefaultusername --setalias ${scratchOrg.scratchOrgAlias}`; + await fs.writeFile(tmpAuthFile, authFileContent, 'utf8'); + const authCommand = `sf org login sfdx-url --sfdx-url-file ${tmpAuthFile} --set-default --alias ${scratchOrg.scratchOrgAlias}`; const authRes = await execSfdxJson(authCommand, this, { fail: false, output: false }); if (authRes.status !== 0) { uxLog( this, c.yellow( - `[pool] Unable to authenticate to org ${scratchOrg.scratchOrgAlias}: ${scratchOrg.scratchOrgUsername}\n${c.grey(JSON.stringify(authRes))}`, - ), + `[pool] Unable to authenticate to org ${scratchOrg.scratchOrgAlias}: ${scratchOrg.scratchOrgUsername + }\n${c.grey(JSON.stringify(authRes))}` + ) ); return null; } @@ -149,29 +163,34 @@ export async function tryFetchScratchOrg(options: any) { await fs.unlink(tmpAuthFile); // Store sfdxAuthUrl for next step if we are in CI if (isCI) { - await setConfig("user", { sfdxAuthUrl: authFileContent }); + await setConfig('user', { sfdxAuthUrl: authFileContent }); } // Display org URL - const openRes = await execSfdxJson(`sf org open --url-only -u ${scratchOrg.scratchOrgAlias}`, this, { fail: false, output: false }); + const openRes = await execSfdxJson(`sf org open --url-only --target-org ${scratchOrg.scratchOrgAlias}`, this, { + fail: false, + output: false, + }); uxLog(this, c.cyan(`Open scratch org with url: ${c.green(openRes?.result?.url)}`)); // Return scratch org - await updateActiveScratchOrg(scratchOrg, { Description: `Authenticated by ${os.userInfo().username} on ${moment().format("YYYYMMDD_hhmm")}` }); + await updateActiveScratchOrg(scratchOrg, { + Description: `Authenticated by ${os.userInfo().username} on ${moment().format('YYYYMMDD_hhmm')}`, + }); return scratchOrg; } uxLog( this, - "[pool]" + - c.yellow( - `No scratch org available in scratch org pool. You may increase ${c.white("poolConfig.maxScratchOrgsNumber")} or schedule call to ${c.white( - "sfdx hardis:scratch:pool:refresh", - )} more often in CI`, - ), + '[pool]' + + c.yellow( + `No scratch org available in scratch org pool. You may increase ${c.white( + 'poolConfig.maxScratchOrgsNumber' + )} or schedule call to ${c.white('sf hardis:scratch:pool:refresh')} more often in CI` + ) ); return null; } export async function listKeyValueProviders(): Promise> { - return [SalesforceProvider, RedisProvider, KvdbIoProvider, KeyValueXyzProvider, LocalTestProvider].map((cls) => new cls()); + return [SalesforceProvider, LocalTestProvider].map((cls) => new cls()); } async function initializeProvider(options: any) { @@ -189,11 +208,11 @@ async function initializeProvider(options: any) { if (isCI) { throw e; } - uxLog(this, "[pool] " + c.grey("Provider initialization error: " + e.message)); + uxLog(this, '[pool] ' + c.grey('Provider initialization error: ' + (e as Error).message)); // If manual, let's ask the user if he/she has credentials to input const resp = await prompts({ - type: "confirm", - message: "Scratch org pool credentials are missing, do you want to configure them ?", + type: 'confirm', + message: 'Scratch org pool credentials are missing, do you want to configure them ?', }); if (resp.value === true) { await keyValueProvider.userAuthenticate(options); @@ -209,7 +228,7 @@ export async function instantiateProvider(storageService: string) { const providerClasses = await listKeyValueProviders(); const providerClassRes = providerClasses.filter((cls) => cls.name === storageService); if (providerClassRes.length === 0) { - throw new SfdxError(c.red("Unable to find class for storage provider " + storageService)); + throw new SfError(c.red('Unable to find class for storage provider ' + storageService)); } return providerClassRes[0]; } diff --git a/src/common/utils/profileUtils.ts b/src/common/utils/profileUtils.ts index ec98fe055..b750c892f 100644 --- a/src/common/utils/profileUtils.ts +++ b/src/common/utils/profileUtils.ts @@ -1,8 +1,8 @@ -import * as c from "chalk"; +import c from "chalk"; import * as path from "path"; -import { uxLog } from "."; -import { getConfig } from "../../config"; -import { parseXmlFile, writeXmlFile } from "./xmlUtils"; +import { uxLog } from "./index.js"; +import { getConfig } from "../../config/index.js"; +import { parseXmlFile, writeXmlFile } from "./xmlUtils.js"; // Push sources to org // For some cases, push must be performed in 2 times: the first with all passing sources, and the second with updated sources requiring the first push @@ -21,7 +21,7 @@ export async function minimizeProfile(profileFile: string) { const config = await getConfig("branch"); const nodesToRemove = config.minimizeProfilesNodesToRemove || nodesToRemoveDefault; // Remove nodes - const removed = []; + const removed: any[] = []; for (const node of nodesToRemove) { if (profileXml.Profile[node]) { delete profileXml.Profile[node]; @@ -31,7 +31,7 @@ export async function minimizeProfile(profileFile: string) { // Keep only default values or false values const isAdmin = path.basename(profileFile) === "Admin.profile-meta.xml"; let updatedDefaults = false; - const partiallyRemoved = []; + const partiallyRemoved: any[] = []; const nodesHavingDefaultOrFalse = ["applicationVisibilities", "recordTypeVisibilities", "userPermissions"]; for (const node of nodesHavingDefaultOrFalse) { if (profileXml.Profile[node]) { diff --git a/src/common/utils/projectUtils.ts b/src/common/utils/projectUtils.ts index 8a3c56833..c9af7835f 100644 --- a/src/common/utils/projectUtils.ts +++ b/src/common/utils/projectUtils.ts @@ -1,26 +1,26 @@ -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; -import { execCommand, uxLog } from "."; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; +import { execCommand, uxLog } from './index.js'; export const GLOB_IGNORE_PATTERNS = [ - "**/node_modules/**", - "**/.git/**", - "**/cache/**", - "**/.npm/**", - "**/logs/**", - "**/.sfdx/**", - "**/.sf/**", - "**/.vscode/**", + '**/node_modules/**', + '**/.git/**', + '**/cache/**', + '**/.npm/**', + '**/logs/**', + '**/.sfdx/**', + '**/.sf/**', + '**/.vscode/**', ]; export function isSfdxProject(cwd = process.cwd()) { - return fs.existsSync(path.join(cwd, "sfdx-project.json")); + return fs.existsSync(path.join(cwd, 'sfdx-project.json')); } export async function createBlankSfdxProject(cwd = process.cwd(), debug = false) { - uxLog(this, c.cyan("Creating blank SFDX project...")); - const projectCreateCommand = 'sfdx force:project:create --projectname "sfdx-hardis-blank-project"'; + uxLog(this, c.cyan('Creating blank SFDX project...')); + const projectCreateCommand = 'sf project generate --name "sfdx-hardis-blank-project"'; await execCommand(projectCreateCommand, this, { cwd: cwd, fail: true, diff --git a/src/common/utils/prompts.ts b/src/common/utils/prompts.ts index 0469a7db4..4ef1f8264 100644 --- a/src/common/utils/prompts.ts +++ b/src/common/utils/prompts.ts @@ -1,9 +1,9 @@ -import * as c from "chalk"; +import c from "chalk"; // eslint-disable-next-line @typescript-eslint/no-var-requires import inquirer from "inquirer"; -import { SfdxError } from "@salesforce/core"; -import { isCI, uxLog } from "."; -import { WebSocketClient } from "../websocketClient"; +import { SfError } from "@salesforce/core"; +import { isCI, uxLog } from "./index.js"; +import { WebSocketClient } from "../websocketClient.js"; export interface PromptsQuestion { message: string; @@ -19,10 +19,10 @@ export interface PromptsQuestion { // Centralized prompts function export async function prompts(options: PromptsQuestion | PromptsQuestion[]) { if (isCI) { - throw new SfdxError("Nothing should be prompted during CI !"); + throw new SfError("Nothing should be prompted during CI !"); } const questionsRaw = Array.isArray(options) ? options : [options]; - const questionsReformatted = []; + const questionsReformatted: any = []; for (const question of questionsRaw) { if (!question.message.startsWith("🦙")) { question.message = "🦙 " + question.message; @@ -42,6 +42,7 @@ export async function prompts(options: PromptsQuestion | PromptsQuestion[]) { } // Add exit option when possible if (question.type === "select") { + question.choices = question.choices || []; question.choices.push({ title: "⛔ Exit this script", value: "exitNow" }); } if (["select", "multiselect"].includes(question.type) && question.optionsPerPage == null) { @@ -78,7 +79,7 @@ export async function prompts(options: PromptsQuestion | PromptsQuestion[]) { } async function terminalPrompts(questions: PromptsQuestion[]) { - const inquirerQuestions = []; + const inquirerQuestions: any = []; for (const question of questions) { const inquirerQuestion: any = { name: question.name, @@ -107,6 +108,6 @@ async function terminalPrompts(questions: PromptsQuestion[]) { const answers = await inquirer.prompt(inquirerQuestions); return answers; } catch (e) { - throw new SfdxError("Error while prompting: " + e.message); + throw new SfError("Error while prompting: " + (e as Error).message); } } diff --git a/src/common/utils/workaroundUtils.ts b/src/common/utils/workaroundUtils.ts index dd024b94a..7c596bf15 100644 --- a/src/common/utils/workaroundUtils.ts +++ b/src/common/utils/workaroundUtils.ts @@ -1,15 +1,15 @@ -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; -import { createTempDir, uxLog } from "."; -import { glob } from "glob"; -import { parseXmlFile, writeXmlFile } from "./xmlUtils"; -import { isScratchOrg } from "./orgUtils"; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; +import { createTempDir, uxLog } from './index.js'; +import { glob } from 'glob'; +import { parseXmlFile, writeXmlFile } from './xmlUtils.js'; +import { isScratchOrg } from './orgUtils.js'; // Update files for special cases export async function arrangeFilesBefore(commandThis: any, options: any = {}) { const tempDir = await createTempDir(); - const arrangedFiles = []; + const arrangedFiles: any[] = []; if ((await isScratchOrg(options)) === true) { const arrangedLookupFields = await removeLookupFilters(tempDir, commandThis, options); arrangedFiles.push(...arrangedLookupFields); @@ -19,8 +19,8 @@ export async function arrangeFilesBefore(commandThis: any, options: any = {}) { // Remove lookup filters because they aren't pushed well export async function removeLookupFilters(tempDir: string, commandThis: any, options: any = {}) { - const arrangedFiles = []; - const findFieldsPattern = (options.rootFolder || ".") + `/**/objects/**/fields/**.field-meta.xml`; + const arrangedFiles: any = []; + const findFieldsPattern = (options.rootFolder || '.') + `/**/objects/**/fields/**.field-meta.xml`; const matchingFieldFiles = await glob(findFieldsPattern, { cwd: process.cwd() }); for (const fieldFile of matchingFieldFiles) { // skip if managed field diff --git a/src/common/utils/wrapUtils.ts b/src/common/utils/wrapUtils.ts index edc7cc879..e31405bfa 100644 --- a/src/common/utils/wrapUtils.ts +++ b/src/common/utils/wrapUtils.ts @@ -1,9 +1,9 @@ -import { SfdxCommand } from "@salesforce/command"; -import * as c from "chalk"; -import { execCommand, uxLog } from "."; -import { analyzeDeployErrorLogs } from "./deployTips"; +import { SfCommand } from "@salesforce/sf-plugins-core"; +import c from "chalk"; +import { execCommand, uxLog } from "./index.js"; +import { analyzeDeployErrorLogs } from "./deployTips.js"; -export async function wrapSfdxCoreCommand(commandBase: string, argv: string[], commandThis: SfdxCommand, debug = false): Promise { +export async function wrapSfdxCoreCommand(commandBase: string, argv: string[], commandThis: SfCommand, debug = false): Promise { const endArgs = [...argv].map((arg) => { // Add quotes to avoid problems if arguments contain spaces if (!arg.startsWith("-") && !arg.startsWith(`"`) && !arg.startsWith(`'`)) { @@ -45,7 +45,7 @@ export async function wrapSfdxCoreCommand(commandBase: string, argv: string[], c }); } catch (e) { // Add deployment tips in error logs - const { errLog } = await analyzeDeployErrorLogs(e.stdout + e.stderr, true, { check: endArgs.includes("--checkonly") }); + const { errLog } = await analyzeDeployErrorLogs((e as any).stdout + (e as any).stderr, true, { check: endArgs.includes("--checkonly") }); uxLog(commandThis, c.red(c.bold("Sadly there has been error(s)"))); if (process.env?.SFDX_HARDIS_DEPLOY_ERR_COLORS === "false") { uxLog(this, "\n" + errLog); @@ -53,8 +53,8 @@ export async function wrapSfdxCoreCommand(commandBase: string, argv: string[], c uxLog(this, c.red("\n" + errLog)); } deployRes = errLog; - if (e.code) { - process.exitCode = e.code; + if ((e as any).code) { + process.exitCode = (e as any).code; } else { process.exitCode = 1; } diff --git a/src/common/utils/xmlUtils.ts b/src/common/utils/xmlUtils.ts index d70c1bb24..eb9dcd45b 100644 --- a/src/common/utils/xmlUtils.ts +++ b/src/common/utils/xmlUtils.ts @@ -1,15 +1,15 @@ // XML Utils functions -import { SfdxError } from "@salesforce/core"; -import * as c from "chalk"; -import * as fs from "fs-extra"; -import * as path from "path"; -import * as util from "util"; -import * as xml2js from "xml2js"; -import { uxLog } from "."; -import { CONSTANTS } from "../../config"; +import { SfError } from '@salesforce/core'; +import c from 'chalk'; +import fs from 'fs-extra'; +import * as path from 'path'; +import * as util from 'util'; +import * as xml2js from 'xml2js'; +import { uxLog } from './index.js'; +import { CONSTANTS } from '../../config/index.js'; export async function parseXmlFile(xmlFile: string) { - const packageXmlString = await fs.readFile(xmlFile, "utf8"); + const packageXmlString = await fs.readFile(xmlFile, 'utf8'); const parsedXml = await xml2js.parseStringPromise(packageXmlString); return parsedXml; } @@ -18,10 +18,10 @@ export async function writeXmlFile(xmlFile: string, xmlObject: any) { const builder = new xml2js.Builder({ renderOpts: { pretty: true, - indent: process.env.SFDX_XML_INDENT || " ", - newline: "\n", + indent: process.env.SFDX_XML_INDENT || ' ', + newline: '\n', }, - xmldec: { version: "1.0", encoding: "UTF-8" }, + xmldec: { version: '1.0', encoding: 'UTF-8' }, }); const updatedFileContent = builder.buildObject(xmlObject); await fs.ensureDir(path.dirname(xmlFile)); @@ -45,7 +45,7 @@ export async function parsePackageXmlFile(packageXmlFile: string) { } export async function writePackageXmlFile(packageXmlFile: string, packageXmlObject: any) { - let packageXmlContent = { Package: { types: [], version: [CONSTANTS.API_VERSION] } }; + let packageXmlContent: any = { Package: { types: [], version: [CONSTANTS.API_VERSION] } }; if (fs.existsSync(packageXmlFile)) { packageXmlContent = await parseXmlFile(packageXmlFile); } @@ -62,17 +62,28 @@ export async function writePackageXmlFile(packageXmlFile: string, packageXmlObje // Check if a package.xml is empty export async function isPackageXmlEmpty( packageXmlFile: string, - options: { ignoreStandaloneParentItems: boolean } = { ignoreStandaloneParentItems: false }, + options: { ignoreStandaloneParentItems: boolean } = { ignoreStandaloneParentItems: false } ) { const packageXmlContent = await parseXmlFile(packageXmlFile); - if (packageXmlContent && packageXmlContent.Package && packageXmlContent.Package.types && packageXmlContent.Package.types.length > 0) { + if ( + packageXmlContent && + packageXmlContent.Package && + packageXmlContent.Package.types && + packageXmlContent.Package.types.length > 0 + ) { if (options.ignoreStandaloneParentItems === true) { // Check if only contains SharingRules without SharingOwnerRule - if (packageXmlContent.Package.types.length === 1 && packageXmlContent.Package.types[0].name[0] === "SharingRules") { + if ( + packageXmlContent.Package.types.length === 1 && + packageXmlContent.Package.types[0].name[0] === 'SharingRules' + ) { return true; } // Check if only contains SharingOwnerRule without SharingRules - if (packageXmlContent.Package.types.length === 1 && packageXmlContent.Package.types[0].name[0] === "SharingOwnerRule") { + if ( + packageXmlContent.Package.types.length === 1 && + packageXmlContent.Package.types[0].name[0] === 'SharingOwnerRule' + ) { return true; } } @@ -82,11 +93,55 @@ export async function isPackageXmlEmpty( return true; } +// Read package.xml files and build concatenated list of items +export async function appendPackageXmlFilesContent(packageXmlFileList: string[], outputXmlFile: string) { + uxLog(this, c.cyan(`Appending ${packageXmlFileList.join(',')} into ${outputXmlFile}...`)); + let firstPackageXmlContent: any = null; + let allPackageXmlFilesTypes = {}; + // loop on packageXml files + for (const packageXmlFile of packageXmlFileList) { + const result: any = await parseXmlFile(packageXmlFile); + if (firstPackageXmlContent == null) { + firstPackageXmlContent = result; + } + let packageXmlMetadatasTypeLs: any[]; + // Get metadata types in current loop packageXml + try { + packageXmlMetadatasTypeLs = result.Package.types || []; + } catch { + throw new SfError('Unable to find Package XML element in ' + packageXmlFile); + } + // Add metadata members in concatenation list of items & store doublings + for (const typePkg of packageXmlMetadatasTypeLs) { + if (typePkg.name == null) { + continue; + } + const nameKey = typePkg.name[0]; + if (allPackageXmlFilesTypes[nameKey] != null && typePkg.members != null) { + allPackageXmlFilesTypes[nameKey] = Array.from( + new Set(allPackageXmlFilesTypes[nameKey].concat(typePkg.members)) + ).sort(); + } else if (typePkg.members != null) { + allPackageXmlFilesTypes[nameKey] = Array.from(new Set(typePkg.members)).sort(); + } + } + } + // Sort result + allPackageXmlFilesTypes = sortObject(allPackageXmlFilesTypes); + // Write output file + const appendTypesXml: any[] = []; + for (const packageXmlType of Object.keys(allPackageXmlFilesTypes)) { + appendTypesXml.push({ members: allPackageXmlFilesTypes[packageXmlType], name: packageXmlType }); + } + firstPackageXmlContent.Package.types = appendTypesXml; + await writeXmlFile(outputXmlFile, firstPackageXmlContent); +} + // Read package.xml files and remove the content of the export async function removePackageXmlFilesContent( packageXmlFile: string, removePackageXmlFile: string, - { outputXmlFile = null, logFlag = false, removedOnly = false, keepEmptyTypes = false }, + { outputXmlFile = '', logFlag = false, removedOnly = false, keepEmptyTypes = false } ) { // Read package.xml file to update const parsedPackageXml: any = await parseXmlFile(packageXmlFile); @@ -98,7 +153,7 @@ export async function removePackageXmlFilesContent( try { packageXmlMetadatasTypeLs = parsedPackageXml.Package.types || []; } catch { - throw new SfdxError("Unable to parse package Xml file " + packageXmlFile); + throw new SfError('Unable to parse package Xml file ' + packageXmlFile); } // Read package.xml file to use for filtering first file @@ -111,11 +166,11 @@ export async function removePackageXmlFilesContent( try { packageXmlRemoveMetadatasTypeLs = parsedPackageXmlRemove.Package.types || []; } catch { - throw new SfdxError("Unable to parse package Xml file " + removePackageXmlFile); + throw new SfError('Unable to parse package Xml file ' + removePackageXmlFile); } // Filter main package.xml file - const processedTypes = []; + const processedTypes: any[] = []; for (const removeType of packageXmlRemoveMetadatasTypeLs) { const removeTypeName = removeType.name[0] || null; if (removeTypeName) { @@ -132,18 +187,34 @@ export async function removePackageXmlFilesContent( const type = types[0]; let typeMembers = type.members || []; // Manage * case contained in target - if (removedOnly === true && typeMembers.includes("*")) { + if (removedOnly === true && typeMembers.includes('*')) { typeMembers = removeTypeMembers; uxLog(this, c.grey(c.italic(`Found wildcard * on type ${c.bold(type.name)}, kept items: ${typeMembers.length}`))); } // Manage * case contained in source - else if (removeTypeMembers[0] && removeTypeMembers[0] === "*") { + else if (removeTypeMembers[0] && removeTypeMembers[0] === '*') { typeMembers = typeMembers.filter(() => checkRemove(false, removedOnly)); - uxLog(this, c.grey(c.italic(`Found wildcard * on type ${c.bold(type.name)} which have all been ${removedOnly ? "kept" : "removed"}`))); + uxLog( + this, + c.grey( + c.italic( + `Found wildcard * on type ${c.bold(type.name)} which have all been ${removedOnly ? 'kept' : 'removed'}` + ) + ) + ); } else { // Filter members - typeMembers = typeMembers.filter((member: string) => checkRemove(!removeTypeMembers.includes(member), removedOnly)); - uxLog(this, c.grey(c.italic(`Found type ${c.bold(type.name)}, ${typeMembers.length} items have been ${removedOnly ? "removed" : "kept"}`))); + typeMembers = typeMembers.filter((member: string) => + checkRemove(!removeTypeMembers.includes(member), removedOnly) + ); + uxLog( + this, + c.grey( + c.italic( + `Found type ${c.bold(type.name)}, ${typeMembers.length} items have been ${removedOnly ? 'removed' : 'kept'}` + ) + ) + ); } if (typeMembers.length > 0 || keepEmptyTypes === true) { // Update members for type @@ -163,12 +234,14 @@ export async function removePackageXmlFilesContent( // If removedOnly mode, remove types which were not present in removePackageXml if (removedOnly) { - packageXmlMetadatasTypeLs = packageXmlMetadatasTypeLs.filter((type1: any) => processedTypes.includes(type1.name[0])); + packageXmlMetadatasTypeLs = packageXmlMetadatasTypeLs.filter((type1: any) => + processedTypes.includes(type1.name[0]) + ); } // display in logs if requested if (logFlag) { - uxLog(this, "Package.xml remove results :\n" + util.inspect(packageXmlMetadatasTypeLs, false, null)); + uxLog(this, 'Package.xml remove results :\n' + util.inspect(packageXmlMetadatasTypeLs, false, null)); } // Write in output file if required @@ -176,15 +249,103 @@ export async function removePackageXmlFilesContent( parsedPackageXml.Package.types = packageXmlMetadatasTypeLs; await writeXmlFile(outputXmlFile, parsedPackageXml); if (logFlag) { - uxLog(this, "Generated package.xml file: " + outputXmlFile); + uxLog(this, 'Generated package.xml file: ' + outputXmlFile); } } return packageXmlMetadatasTypeLs; } +export function sortObject(o) { + return Object.keys(o) + .sort() + .reduce((r, k) => ((r[k] = o[k]), r), {}); +} + function checkRemove(boolRes, removedOnly = false) { if (removedOnly === true) { return !boolRes; } return boolRes; } + +export async function applyAllReplacementsDefinitions( + allMatchingSourceFiles: string[], + referenceStrings: string[], + replacementDefinitions: any[] +) { + uxLog(this, c.cyan(`Initializing replacements in files for ${referenceStrings.join(',')}...`)); + for (const ref of referenceStrings) { + for (const replacementDefinition of replacementDefinitions) { + replacementDefinition.refRegexes = replacementDefinition.refRegexes.map((refRegex) => { + refRegex.regex = refRegex.regex.replace(new RegExp(`{{REF}}`), ref); + return refRegex; + }); + await applyReplacementDefinition(replacementDefinition, allMatchingSourceFiles, ref); + } + } +} + +export async function applyReplacementDefinition( + replacementDefinition: any, + allMatchingSourceFiles: string[], + ref: string +) { + for (const sourceFile of allMatchingSourceFiles.filter((file) => + replacementDefinition.extensions.some((ext) => file.endsWith(ext)) + )) { + let fileText = await fs.readFile(sourceFile, 'utf8'); + let updated = false; + // Replacement in all text + if (replacementDefinition.replaceMode.includes('all')) { + for (const regexReplace of replacementDefinition.refRegexes) { + const updatedfileText = fileText.replace(new RegExp(regexReplace.regex, 'gm'), regexReplace.replace); + if (updatedfileText !== fileText) { + updated = true; + fileText = updatedfileText; + } + } + } + // Replacement by line + let fileLines = fileText.split(/\r?\n/); + if (replacementDefinition.replaceMode.includes('line')) { + const updatedFileLines = fileLines.map((line) => { + const trimLine = line.trim(); + if (trimLine.startsWith('/') || trimLine.startsWith(' ' + : line; + } + return line; + }); + fileLines = updatedFileLines; + } + // Apply updates on file + if (updated) { + const updatedFileText = fileLines.join('\n'); + await fs.writeFile(sourceFile, updatedFileText); + uxLog(this, c.grey(`- updated ${replacementDefinition.label}: ${sourceFile}`)); + } + } +} diff --git a/src/common/websocketClient.ts b/src/common/websocketClient.ts index fb9e0bc8b..851145b93 100644 --- a/src/common/websocketClient.ts +++ b/src/common/websocketClient.ts @@ -1,7 +1,8 @@ -import * as c from "chalk"; -import * as util from "util"; -import * as WebSocket from "ws"; -import { isCI, uxLog } from "./utils"; +import c from 'chalk'; +import * as util from 'util'; +import WebSocket from 'ws'; +import { isCI, uxLog } from './utils/index.js'; +import { SfError } from '@salesforce/core'; let globalWs: WebSocketClient | null; let isWsOpen = false; @@ -21,7 +22,10 @@ export class WebSocketClient { globalWs = this; // eslint-disable-line this.start(); } catch (err) { - uxLog(this, c.yellow("Warning: Unable to start WebSocket client on " + wsHostPort + "\n" + err.message)); + uxLog( + this, + c.yellow('Warning: Unable to start WebSocket client on ' + wsHostPort + '\n' + (err as Error).message) + ); } } @@ -37,30 +41,33 @@ export class WebSocketClient { // Requests open file within VsCode if linked static requestOpenFile(file: string) { - WebSocketClient.sendMessage({ event: "openFile", file: file.replace(/\\/g, "/") }); + WebSocketClient.sendMessage({ event: 'openFile', file: file.replace(/\\/g, '/') }); } static sendPrompts(prompts: any): Promise { - return globalWs.promptServer(prompts); + if (globalWs) { + return globalWs.promptServer(prompts); + } + throw new SfError('globalWs should be set in sendPrompts'); } start() { - this.ws.on("open", () => { + this.ws.on('open', () => { isWsOpen = true; this.ws.send( JSON.stringify({ - event: "initClient", + event: 'initClient', context: this.wsContext, - }), + }) ); // uxLog(this,c.grey('Initialized WebSocket connection with VsCode SFDX Hardis')); }); - this.ws.on("message", (data: any) => { + this.ws.on('message', (data: any) => { this.receiveMessage(JSON.parse(data)); }); - this.ws.on("error", (err) => { + this.ws.on('error', (err) => { this.ws.terminate(); globalWs = null; if (process.env.DEBUG) { @@ -71,9 +78,9 @@ export class WebSocketClient { receiveMessage(data: any) { if (process.env.DEBUG) { - console.debug("websocket: received: %s", util.inspect(data)); + console.debug('websocket: received: %s', util.inspect(data)); } - if (data.event === "promptsResponse") { + if (data.event === 'promptsResponse') { this.promptResponse = data.promptsResponse; } } @@ -84,16 +91,16 @@ export class WebSocketClient { } promptServer(prompts: any): Promise { - this.sendMessageToServer({ event: "prompts", prompts: prompts }); + this.sendMessageToServer({ event: 'prompts', prompts: prompts }); this.promptResponse = null; let ok = false; return new Promise((resolve, reject) => { - let interval = null; - let timeout = null; + let interval: any = null; + let timeout: any = null; interval = setInterval(() => { if (this.promptResponse != null) { - clearInterval(interval); - clearTimeout(timeout); + clearInterval(interval as NodeJS.Timeout); + clearTimeout(timeout as NodeJS.Timeout); ok = true; resolve(this.promptResponse); } @@ -101,7 +108,7 @@ export class WebSocketClient { timeout = setTimeout(() => { if (ok === false) { clearInterval(interval); - reject("[sfdx-hardis] No response from UI WebSocket Server"); + reject('[sfdx-hardis] No response from UI WebSocket Server'); } }, 7200000); // 2h timeout }); @@ -110,9 +117,9 @@ export class WebSocketClient { dispose() { this.ws.send( JSON.stringify({ - event: "closeClient", + event: 'closeClient', context: this.wsContext, - }), + }) ); this.ws.terminate(); globalWs = null; diff --git a/src/config/index.ts b/src/config/index.ts index b35ef748e..3879c1ce8 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -10,40 +10,77 @@ getConfig(layer) returns: - project if layer is project */ -import { SfdxError } from "@salesforce/core"; -import axios from "axios"; -import * as c from "chalk"; -import { cosmiconfig } from "cosmiconfig"; -import * as fs from "fs-extra"; -import * as yaml from "js-yaml"; -import * as os from "os"; -import * as path from "path"; -import { getCurrentGitBranch, isCI, isGitRepo, uxLog } from "../common/utils"; -import { prompts } from "../common/utils/prompts"; +import { SfError } from '@salesforce/core'; +import axios from 'axios'; +import c from 'chalk'; +import { cosmiconfig } from 'cosmiconfig'; +import fs from 'fs-extra'; +import * as yaml from 'js-yaml'; +import * as os from 'os'; +import * as path from 'path'; +import { getCurrentGitBranch, isCI, isGitRepo, uxLog } from '../common/utils/index.js'; +import { prompts } from '../common/utils/prompts.js'; -const moduleName = "sfdx-hardis"; -const projectConfigFiles = ["package.json", `.${moduleName}.yaml`, `.${moduleName}.yml`, `config/.${moduleName}.yaml`, `config/.${moduleName}.yml`]; +const moduleName = 'sfdx-hardis'; +const projectConfigFiles = [ + 'package.json', + `.${moduleName}.yaml`, + `.${moduleName}.yml`, + `config/.${moduleName}.yaml`, + `config/.${moduleName}.yml`, +]; const username = os.userInfo().username; const userConfigFiles = [`config/user/.${moduleName}.${username}.yaml`, `config/user/.${moduleName}.${username}.yml`]; const REMOTE_CONFIGS: any = {}; +export const CONSTANTS = { + API_VERSION: process.env.SFDX_API_VERSION || '61.0', + DOC_URL_ROOT: "https://sfdx-hardis.cloudity.com", + NOT_IMPACTING_METADATA_TYPES: process.env.NOT_IMPACTING_METADATA_TYPES?.split(",") ?? [ + "Audience", + "AuraDefinitionBundle", + "Bot", + "BotVersion", + "ContentAsset", + "CustomObjectTranslation", + "CustomSite", + "CustomTab", + "Dashboard", + "ExperienceBundle", + "Flexipage", + "GlobalValueSetTranslation", + "Layout", + "LightningComponentBundle", + "NavigationMenu", + "ReportType", + "Report", + "SiteDotCom", + "StandardValueSetTranslation", + "StaticResource", + "Translations" + ] +}; + async function getBranchConfigFiles() { if (!isGitRepo()) { return []; } const gitBranchFormatted = process.env.CONFIG_BRANCH || (await getCurrentGitBranch({ formatted: true })); - const branchConfigFiles = [`config/branches/.${moduleName}.${gitBranchFormatted}.yaml`, `config/branches/.${moduleName}.${gitBranchFormatted}.yml`]; + const branchConfigFiles = [ + `config/branches/.${moduleName}.${gitBranchFormatted}.yaml`, + `config/branches/.${moduleName}.${gitBranchFormatted}.yml`, + ]; return branchConfigFiles; } -export const getConfig = async (layer = "user"): Promise => { +export const getConfig = async (layer = 'user'): Promise => { const defaultConfig = await loadFromConfigFile(projectConfigFiles); - if (layer === "project") { + if (layer === 'project') { return defaultConfig; } let branchConfig = await loadFromConfigFile(await getBranchConfigFiles()); branchConfig = Object.assign(defaultConfig, branchConfig); - if (layer === "branch") { + if (layer === 'branch') { return branchConfig; } let userConfig = await loadFromConfigFile(userConfigFiles); @@ -53,21 +90,23 @@ export const getConfig = async (layer = "user"): Promise => { // Set data in configuration file export const setConfig = async (layer: string, propValues: any): Promise => { - if (layer === "user" && (fs.readdirSync(process.cwd()).length === 0 || !isGitRepo())) { - if (process?.argv?.includes("--debug")) { - uxLog(this, c.grey("Skip update user config file because current directory is not a salesforce project")); + if (layer === 'user' && (fs.readdirSync(process.cwd()).length === 0 || !isGitRepo())) { + if (process?.argv?.includes('--debug')) { + uxLog(this, c.grey('Skip update user config file because current directory is not a salesforce project')); } return; } const configSearchPlaces = - layer === "project" ? projectConfigFiles : layer === "user" ? userConfigFiles : layer === "branch" ? await getBranchConfigFiles() : []; + layer === 'project' + ? projectConfigFiles + : layer === 'user' + ? userConfigFiles + : layer === 'branch' + ? await getBranchConfigFiles() + : []; await setInConfigFile(configSearchPlaces, propValues); }; -export const CONSTANTS = { - API_VERSION: process.env.SFDX_API_VERSION || "61.0", -}; - // Load configuration from file async function loadFromConfigFile(searchPlaces: string[]): Promise { const configExplorer = await cosmiconfig(moduleName, { @@ -87,7 +126,9 @@ async function loadFromRemoteConfigFile(url) { } const remoteConfigResp = await axios.get(url); if (remoteConfigResp.status !== 200) { - throw new SfdxError("[sfdx-hardis] Unable to read remote configuration file at " + url + "\n" + JSON.stringify(remoteConfigResp)); + throw new SfError( + '[sfdx-hardis] Unable to read remote configuration file at ' + url + '\n' + JSON.stringify(remoteConfigResp) + ); } const remoteConfig = yaml.load(remoteConfigResp.data); REMOTE_CONFIGS[url] = remoteConfig; @@ -95,33 +136,36 @@ async function loadFromRemoteConfigFile(url) { } // Update configuration file -export async function setInConfigFile(searchPlaces: string[], propValues: any, configFile: string = null) { - let explorer = null; - if (configFile == null) { +export async function setInConfigFile(searchPlaces: string[], propValues: any, configFile: string = '') { + let explorer; + if (configFile === '') { explorer = cosmiconfig(moduleName, { searchPlaces }); const configExplorer = await explorer.search(); configFile = configExplorer != null ? configExplorer.filepath : searchPlaces.slice(-1)[0]; } - let doc = {}; + let doc: any = {}; if (fs.existsSync(configFile)) { - doc = yaml.load(fs.readFileSync(configFile, "utf-8")); + doc = yaml.load(fs.readFileSync(configFile, 'utf-8')); } doc = Object.assign(doc, propValues); await fs.ensureDir(path.dirname(configFile)); await fs.writeFile(configFile, yaml.dump(doc)); - if (explorer != null) { + if (explorer) { explorer.clearCaches(); } if (!isCI) { - uxLog(this, c.magentaBright(`Updated config file ${c.bold(configFile)} with values: \n${JSON.stringify(propValues, null, 2)}`)); + uxLog( + this, + c.magentaBright(`Updated config file ${c.bold(configFile)} with values: \n${JSON.stringify(propValues, null, 2)}`) + ); } } // Check configuration of project so it works with sfdx-hardis export const checkConfig = async (options: any) => { // Skip hooks from other commands than hardis:scratch commands - const commandId = options?.Command?.id || options?.id || ""; - if (!commandId.startsWith("hardis")) { + const commandId = options?.Command?.id || options?.id || ''; + if (!commandId.startsWith('hardis')) { return; } @@ -134,19 +178,19 @@ export const checkConfig = async (options: any) => { options?.flags?.devhub === true || options.devHub === true) ) { - const configProject = await getConfig("project"); + const configProject = await getConfig('project'); let projectName = process.env.PROJECT_NAME || configProject.projectName; devHubAliasOk = (process.env.DEVHUB_ALIAS || configProject.devHubAlias) != null; // If not found, prompt user project name and store it in user config file if (projectName == null) { const promptResponse = await prompts({ - type: "text", - name: "value", - message: c.cyanBright("Please input your project name without spaces or special characters (ex: MonClient)"), + type: 'text', + name: 'value', + message: c.cyanBright('Please input your project name without spaces or special characters (ex: MonClient)'), validate: (value: string) => !value.match(/^[0-9a-z]+$/), // check only alphanumeric }); projectName = promptResponse.value; - await setConfig("project", { + await setConfig('project', { projectName, devHubAlias: `DevHub_${projectName}`, }); @@ -156,10 +200,10 @@ export const checkConfig = async (options: any) => { // Set DevHub username if not set if (devHubAliasOk === false && options.Command && options.Command.supportsDevhubUsername === true) { - const configProject = await getConfig("project"); + const configProject = await getConfig('project'); const devHubAlias = process.env.DEVHUB_ALIAS || configProject.devHubAlias; if (devHubAlias == null) { - await setConfig("project", { + await setConfig('project', { devHubAlias: `DevHub_${configProject.projectName}`, }); } @@ -167,8 +211,8 @@ export const checkConfig = async (options: any) => { }; export async function getReportDirectory() { - const configProject = await getConfig("project"); - const defaultReportDir = path.join(process.cwd(), "hardis-report"); + const configProject = await getConfig('project'); + const defaultReportDir = path.join(process.cwd(), 'hardis-report'); const reportDir = configProject.reportDirectory || defaultReportDir; await fs.ensureDir(reportDir); return reportDir; diff --git a/src/hooks/auth/auth.ts b/src/hooks/auth/auth.ts new file mode 100644 index 000000000..8c0ce8ac1 --- /dev/null +++ b/src/hooks/auth/auth.ts @@ -0,0 +1,45 @@ +import { + getCurrentGitBranch, + isCI, +} from '../../common/utils/index.js'; +import { checkConfig, getConfig } from '../../config/index.js'; +import { Hook } from '@oclif/core'; +import { authOrg } from '../../common/utils/authUtils.js'; + +const hook: Hook<'auth'> = async (options: any) => { + const commandId = options?.Command?.id || ''; + let configInfo = await getConfig('user'); + + // Manage authentication if DevHub is required but current user is disconnected + if ((options as any)?.devHub === true) { + let devHubAlias = configInfo.devHubAlias || process.env.DEVHUB_ALIAS; + if (devHubAlias == null) { + await checkConfig(options); + configInfo = await getConfig('user'); + devHubAlias = configInfo.devHubAlias || 'DevHub'; + } + await authOrg(devHubAlias, options); + } + // Manage authentication if org is required but current user is disconnected + if ( + (options as any)?.checkAuth === true && + !((options as any)?.devHub === true) + ) { + const orgAlias = (options as any)?.alias + ? (options as any).alias + : process.env.ORG_ALIAS + ? process.env.ORG_ALIAS + : isCI && configInfo.scratchOrgAlias + ? configInfo.scratchOrgAlias + : isCI && (options as any)?.scratch && configInfo.sfdxAuthUrl + ? configInfo.sfdxAuthUrl + : isCI + ? await getCurrentGitBranch({ formatted: true }) + : commandId === 'hardis:auth:login' && configInfo.orgAlias + ? configInfo.orgAlias + : configInfo.scratchOrgAlias || ''; // Can be '' and it's ok if we're not in scratch org context + await authOrg(orgAlias, options); + } +}; + +export default hook; diff --git a/src/hooks/init/check-local-sfdx-hardis-files.ts b/src/hooks/init/check-local-sfdx-hardis-files.ts index 37fea8ec5..9935d45ea 100644 --- a/src/hooks/init/check-local-sfdx-hardis-files.ts +++ b/src/hooks/init/check-local-sfdx-hardis-files.ts @@ -1,53 +1,41 @@ -import * as c from "chalk"; -import * as fs from "fs-extra"; -import { isCI, isMonitoringJob, uxLog } from "../../common/utils"; -import { prompts } from "../../common/utils/prompts"; -import { getConfig, setConfig } from "../../config"; +import c from 'chalk'; +import fs from 'fs-extra'; +import { isCI, isMonitoringJob, uxLog } from '../../common/utils/index.js'; +import { prompts } from '../../common/utils/prompts.js'; +import { getConfig, setConfig } from '../../config/index.js'; +import { Hook } from '@oclif/core'; -export const hook = async (options: any) => { +const hook: Hook<'init'> = async (options) => { // Skip hooks from other commands than hardis:scratch commands - const commandId = options?.id || ""; + const commandId = options?.id || ''; - // Disable this hook for now - if ((process.env?.AUTO_UPDATE || "false") !== "true") { - return; - } - - if (!process.argv.includes("--json")) { + if (!options.argv.includes('--json')) { await manageGitIgnoreForceIgnore(commandId); } }; async function manageGitIgnoreForceIgnore(commandId: string) { - if (!commandId.startsWith("hardis")) { + if (!commandId.startsWith('hardis')) { return; } + // Run this command only during a monitoring job, or a Release Manager local operation const isMon = await isMonitoringJob(); - if (!commandId.includes("monitoring") && !isMon) { - if ( - commandId.startsWith("hardis:work:task:new") || - commandId.startsWith("hardis:doc") || - commandId.startsWith("hardis:scratch") || - commandId.startsWith("hardis:org") - ) { - return; - } - - if (!isCI && process.env.AUTO_UPDATE !== "true" && process.env.AUTO_UPDATE_CI_CONFIG !== "true") { - return; - } + if ( + !((isMon && commandId.includes('backup')) || commandId.startsWith("hardis:project:configure:auth")) + ) { + return; } - const config = await getConfig("user"); + const config = await getConfig('user'); // Manage .gitignore if (!config.skipUpdateGitIgnore === true) { - const gitIgnoreFile = "./.gitignore"; + const gitIgnoreFile = './.gitignore'; if (fs.existsSync(gitIgnoreFile)) { - const gitIgnore = await fs.readFile(gitIgnoreFile, "utf-8"); + const gitIgnore = await fs.readFile(gitIgnoreFile, 'utf-8'); const gitIgnoreLines = gitIgnore - .replace("\r\n", "\n") - .split("\n") + .replace('\r\n', '\n') + .split('\n') .map((line) => line.trim()) - .filter((line) => line !== ""); + .filter((line) => line !== ''); let updated = false; for (const gitIgnoreMandatoryLine of await getHardisGitRepoIgnoreContent()) { if (!gitIgnoreLines.includes(gitIgnoreMandatoryLine)) { @@ -60,22 +48,24 @@ async function manageGitIgnoreForceIgnore(commandId: string) { // Propose user to apply updates if ((updated || gitIgnoreLines.length !== gitIgnoreLinesUnique.length) && !isCI) { const confirm = await prompts({ - type: "select", - name: "value", + type: 'select', + name: 'value', initial: true, - message: c.cyanBright("Your .gitignore is deprecated, do you agree to upgrade it ? (If you hesitate, just trust us and accept)"), + message: c.cyanBright( + 'Your .gitignore is deprecated, do you agree to upgrade it ? (If you hesitate, just trust us and accept)' + ), choices: [ - { title: "Yes", value: "true" }, - { title: "No ", value: "false" }, - { title: "Never ask again ", value: "never" }, + { title: 'Yes', value: 'true' }, + { title: 'No ', value: 'false' }, + { title: 'Never ask again ', value: 'never' }, ], }); - if (confirm.value === "true" || isCI) { - await fs.writeFile(gitIgnoreFile, gitIgnoreLinesUnique.join("\n") + "\n", "utf-8"); - uxLog(this, c.cyan("[sfdx-hardis] Updated .gitignore")); + if (confirm.value === 'true' || isCI) { + await fs.writeFile(gitIgnoreFile, gitIgnoreLinesUnique.join('\n') + '\n', 'utf-8'); + uxLog(this, c.cyan('[sfdx-hardis] Updated .gitignore')); } - if (confirm.value === "never") { - await setConfig("project", { skipUpdateGitIgnore: true }); + if (confirm.value === 'never') { + await setConfig('project', { skipUpdateGitIgnore: true }); } } } @@ -83,14 +73,14 @@ async function manageGitIgnoreForceIgnore(commandId: string) { // Manage .forceignore if (!config.skipUpdateForceIgnore === true) { - const forceIgnoreFile = "./.forceignore"; + const forceIgnoreFile = './.forceignore'; if (fs.existsSync(forceIgnoreFile)) { - const forceIgnore = await fs.readFile(forceIgnoreFile, "utf-8"); + const forceIgnore = await fs.readFile(forceIgnoreFile, 'utf-8'); const forceIgnoreLines = forceIgnore - .replace("\r\n", "\n") - .split("\n") + .replace('\r\n', '\n') + .split('\n') .map((line) => line.trim()) - .filter((line) => line !== ""); + .filter((line) => line !== ''); let updated = false; for (const forceIgnoreMandatoryLine of await getHardisForceIgnoreContent()) { if (!forceIgnoreLines.includes(forceIgnoreMandatoryLine)) { @@ -104,23 +94,23 @@ async function manageGitIgnoreForceIgnore(commandId: string) { /* jscpd:ignore-start */ if ((updated || forceIgnoreLines.length !== forceIgnoreLinesUnique.length) && !isCI) { const confirm = await prompts({ - type: "select", - name: "value", + type: 'select', + name: 'value', initial: true, - message: c.cyanBright("Your .forceignore is deprecated, do you agree to upgrade it ?"), + message: c.cyanBright('Your .forceignore is deprecated, do you agree to upgrade it ?'), choices: [ - { title: "Yes", value: "true" }, - { title: "No ", value: "false" }, - { title: "Never ask again ", value: "never" }, + { title: 'Yes', value: 'true' }, + { title: 'No ', value: 'false' }, + { title: 'Never ask again ', value: 'never' }, ], }); /* jscpd:ignore-end */ - if (confirm.value === "true" || isCI) { - await fs.writeFile(forceIgnoreFile, forceIgnoreLinesUnique.join("\n") + "\n", "utf-8"); - uxLog(this, c.cyan("[sfdx-hardis] Updated .forceignore")); + if (confirm.value === 'true' || isCI) { + await fs.writeFile(forceIgnoreFile, forceIgnoreLinesUnique.join('\n') + '\n', 'utf-8'); + uxLog(this, c.cyan('[sfdx-hardis] Updated .forceignore')); } - if (confirm.value === "never") { - await setConfig("project", { skipUpdateForceIgnore: true }); + if (confirm.value === 'never') { + await setConfig('project', { skipUpdateForceIgnore: true }); } } } @@ -129,42 +119,44 @@ async function manageGitIgnoreForceIgnore(commandId: string) { async function getHardisGitRepoIgnoreContent() { const gitIgnoreContent = [ - ".cache/", - "config/user/", - "hardis-report/", - "tmp/", - "**/__tests__/**", + '.cache/', + 'config/user/', + 'hardis-report/', + 'tmp/', + '**/__tests__/**', // Metadatas to be ignored - "**/cleanDataServices/", - "**/siteDotComSites/*.site", + '**/cleanDataServices/', + '**/siteDotComSites/*.site', // SFDX Items to be ignored - "**/data/**/source/**", - "**/data/**/target/**", - "force-app/main/default/appMenus/AppSwitcher.appMenu-meta.xml", + '**/data/**/source/**', + '**/data/**/target/**', + 'force-app/main/default/appMenus/AppSwitcher.appMenu-meta.xml', ]; return gitIgnoreContent; } async function getHardisForceIgnoreContent() { const forceIgnoreContent = [ - "**/appMenu/**", - "**/appSwitcher/**", - "**/appMenus/AppSwitcher.appMenu-meta.xml", + '**/appMenu/**', + '**/appSwitcher/**', + '**/appMenus/AppSwitcher.appMenu-meta.xml', - "**/connectedApps/**", - "**/certs/**", - "**/profilePasswordPolicies/**", + '**/connectedApps/**', + '**/certs/**', + '**/profilePasswordPolicies/**', //"**/objectTranslations/**", // "**/profiles/**", // "**/settings/**", - "**/jsconfig.json", - "**/.eslintrc.json", + '**/jsconfig.json', + '**/.eslintrc.json', - "**/__tests__/**", - "**/pubsub/**", - "**SfdxHardisDeferSharingRecalc**", + '**/__tests__/**', + '**/pubsub/**', + '**SfdxHardisDeferSharingRecalc**', ]; return forceIgnoreContent; } + +export default hook; diff --git a/src/hooks/init/check-upgrade.ts b/src/hooks/init/check-upgrade.ts index 4c6177532..d9ebebbec 100644 --- a/src/hooks/init/check-upgrade.ts +++ b/src/hooks/init/check-upgrade.ts @@ -1,20 +1,26 @@ -import * as c from "chalk"; -import * as readPkgUp from "read-pkg-up"; -import * as updateNotifier from "update-notifier"; -import * as semver from "semver"; +import c from 'chalk'; +import { readPackageUp } from 'read-package-up'; +import updateNotifier from 'update-notifier'; +import * as semver from 'semver'; +import { fileURLToPath } from 'url'; +import * as path from 'path'; +import { Hook } from '@oclif/core'; -export const hook = async (options: any) => { +const hook: Hook<'init'> = async (options) => { // Skip hooks from other commands than hardis commands - const commandId = options?.id || ""; - if (!commandId.startsWith("hardis")) { + const commandId = options?.id || ''; + if (!commandId.startsWith('hardis')) { return; } + const __filename = fileURLToPath(import.meta.url); + const __dirname = path.dirname(__filename); + // Check if an upgrade of sfdx-hardis is required // Use promise + then to not block plugin execution during that - const pkg = await readPkgUp({ cwd: __dirname }); + const pkg = await readPackageUp({ cwd: __dirname }); const notifier = updateNotifier({ - pkg: pkg.packageJson, + pkg: pkg?.packageJson, updateCheckInterval: 900, // check every 15 mn }); if ( @@ -23,14 +29,24 @@ export const hook = async (options: any) => { notifier.update.current !== notifier.update.latest && semver.compare(notifier.update.latest, notifier.update.current) === 1 ) { - console.warn(c.yellow("***********************************************************************************************************************")); console.warn( c.yellow( - `WARNING: You are using sfdx-hardis v${notifier.update.current}: Please upgrade to v${notifier.update.latest} by running ${c.green( - "sf plugins install sfdx-hardis", - )}`, - ), + '***********************************************************************************************************************' + ) + ); + console.warn( + c.yellow( + `WARNING: You are using sfdx-hardis v${notifier.update.current}: Please upgrade to v${ + notifier.update.latest + } by running ${c.green('sf plugins install sfdx-hardis')}` + ) + ); + console.warn( + c.yellow( + '***********************************************************************************************************************' + ) ); - console.warn(c.yellow("***********************************************************************************************************************")); } }; + +export default hook; diff --git a/src/hooks/init/log.ts b/src/hooks/init/log.ts index da6afbdec..2640b9b54 100644 --- a/src/hooks/init/log.ts +++ b/src/hooks/init/log.ts @@ -1,29 +1,33 @@ -import * as fs from "fs-extra"; -import * as path from "path"; -import * as os from "os"; -import { isCI } from "../../common/utils"; +import fs from 'fs-extra'; +import * as path from 'path'; +import * as os from 'os'; +import { isCI } from '../../common/utils/index.js'; +import { Hook } from '@oclif/core'; -export const hook = async (options: any) => { +const hook: Hook<'init'> = async (options) => { // Set argv as global as sf arch messes with it ! globalThis.processArgv = [...options.argv]; // Skip hooks from other commands than hardis commands - const commandId = options?.id || "unknown"; - if (!commandId.startsWith("hardis")) { + const commandId = options?.id || 'unknown'; + if (!commandId.startsWith('hardis')) { return; } - if (process.env.SFDX_HARDIS_DEBUG_ENV === "true") { - console.log("ENV VARS:\n" + JSON.stringify(process.env, null, 2)); - process.env.SFDX_ENV = "development"; // So when there is an error, the stack is displayed + if (process.env.SFDX_HARDIS_DEBUG_ENV === 'true') { + console.log('ENV VARS:\n' + JSON.stringify(process.env, null, 2)); + process.env.SFDX_ENV = 'development'; // So when there is an error, the stack is displayed } if (!isCI) { // Initialize log file name (in the current directory if not empty) - const reportsDir = fs.readdirSync(process.cwd()).length === 0 ? path.join(os.tmpdir(), "hardis-report") : "./hardis-report"; + const reportsDir = + fs.readdirSync(process.cwd()).length === 0 ? path.join(os.tmpdir(), 'hardis-report') : './hardis-report'; await fs.ensureDir(reportsDir); - const commandsLogFolder = path.join(reportsDir, "commands"); + const commandsLogFolder = path.join(reportsDir, 'commands'); await fs.ensureDir(commandsLogFolder); - const logFileName = (new Date().toJSON().slice(0, 19) + "-" + commandId + ".log").replace(/:/g, "-"); + const logFileName = (new Date().toJSON().slice(0, 19) + '-' + commandId + '.log').replace(/:/g, '-'); const hardisLogFile = path.resolve(path.join(commandsLogFolder, logFileName)); - globalThis.hardisLogFileStream = fs.createWriteStream(hardisLogFile, { flags: "a" }); - globalThis.hardisLogFileStream.write(process.argv.join(" ")); + globalThis.hardisLogFileStream = fs.createWriteStream(hardisLogFile, { flags: 'a' }); + globalThis.hardisLogFileStream.write(globalThis.processArgv.join(' ')); } }; + +export default hook; diff --git a/src/hooks/init/start-ws-client.ts b/src/hooks/init/start-ws-client.ts index 5ca121af2..20d0c9083 100644 --- a/src/hooks/init/start-ws-client.ts +++ b/src/hooks/init/start-ws-client.ts @@ -1,20 +1,23 @@ -import { isCI } from "../../common/utils"; -import { WebSocketClient } from "../../common/websocketClient"; +import { Hook } from '@oclif/core'; +import { isCI } from '../../common/utils/index.js'; +import { WebSocketClient } from '../../common/websocketClient.js'; -export const hook = async (options: any) => { +const hook: Hook<'init'> = async (options) => { // Skip hooks from other commands than hardis commands - const commandId = options?.id || ""; - if (!commandId.startsWith("hardis")) { + const commandId = options?.id || ''; + if (!commandId.startsWith('hardis')) { return; } // Initialize WebSocketClient to communicate with VsCode SFDX Hardis extension if (!isCI) { const context: any = { command: commandId, id: process.pid }; - const websocketArgIndex = options?.argv?.indexOf("--websocket"); + const websocketArgIndex = options?.argv?.indexOf('--websocket'); if (websocketArgIndex || websocketArgIndex === 0) { context.websocketHostPort = options.argv[websocketArgIndex + 1]; } globalThis.webSocketClient = new WebSocketClient(context); } }; + +export default hook; diff --git a/src/hooks/postrun/notify.ts b/src/hooks/postrun/notify.ts index 1bdc70480..91098c196 100644 --- a/src/hooks/postrun/notify.ts +++ b/src/hooks/postrun/notify.ts @@ -1,21 +1,22 @@ -import * as c from "chalk"; -import { elapseEnd, uxLog } from "../../common/utils"; +import c from 'chalk'; +import { elapseEnd, uxLog } from '../../common/utils/index.js'; +import { Hook } from '@oclif/core'; // The use of this method is deprecated: use NotifProvider.sendNotification :) -export const hook = async (options: any) => { +const hook: Hook<'postrun'> = async (options) => { if (globalThis.hardisLogFileStream) { globalThis.hardisLogFileStream.end(); globalThis.hardisLogFileStream = null; } // Skip hooks from other commands than hardis commands - const commandId = options?.Command?.id || ""; - if (!commandId.startsWith("hardis")) { + const commandId = options?.Command?.id || ''; + if (!commandId.startsWith('hardis')) { return; } elapseEnd(`${options?.Command?.id} execution time`); - if (commandId.startsWith("hardis:doc")) { + if (commandId.startsWith('hardis:doc')) { return; } @@ -24,10 +25,12 @@ export const hook = async (options: any) => { try { globalThis.webSocketClient.dispose(); } catch (e) { - if (options.debug) { - uxLog(this, c.yellow("Unable to close webSocketClient") + "\n" + e.message); + if (options.Command.flags.debug) { + uxLog(this, c.yellow('Unable to close websocketClient.js') + '\n' + (e as Error).message); } } globalThis.webSocketClient = null; } }; + +export default hook; diff --git a/src/hooks/postrun/store-cache.ts b/src/hooks/postrun/store-cache.ts index 85e01eada..b20b4c21c 100644 --- a/src/hooks/postrun/store-cache.ts +++ b/src/hooks/postrun/store-cache.ts @@ -1,9 +1,10 @@ -import { copyLocalSfdxInfo } from "../../common/utils"; +import { Hook } from '@oclif/core'; +import { copyLocalSfdxInfo } from '../../common/utils/index.js'; -export const hook = async (options: any) => { +const hook: Hook<'postrun'> = async (options) => { // Skip hooks from other commands than hardis commands - const commandId = options?.Command?.id || ""; - if (!commandId.startsWith("hardis:scratch:create")) { + const commandId = options?.Command?.id || ''; + if (!commandId.startsWith('hardis:scratch:create')) { return; } @@ -11,3 +12,5 @@ export const hook = async (options: any) => { await copyLocalSfdxInfo(); return; }; + +export default hook; diff --git a/src/hooks/prerun/auth.ts b/src/hooks/prerun/auth.ts index fd2b94e02..21268630e 100644 --- a/src/hooks/prerun/auth.ts +++ b/src/hooks/prerun/auth.ts @@ -1,441 +1,84 @@ -import { SfdxError } from "@salesforce/core"; -import * as c from "chalk"; -import * as crossSpawn from "cross-spawn"; -import * as fs from "fs-extra"; -import * as path from "path"; -import { clearCache } from "../../common/cache"; -import { decryptFile } from "../../common/cryptoUtils"; +import c from 'chalk'; import { - createTempDir, elapseStart, - execCommand, - execSfdxJson, getCurrentGitBranch, isCI, - promptInstanceUrl, restoreLocalSfdxInfo, uxLog, -} from "../../common/utils"; -import { WebSocketClient } from "../../common/websocketClient"; -import { checkConfig, getConfig } from "../../config"; -import { prompts } from "../../common/utils/prompts"; +} from '../../common/utils/index.js'; +import { checkConfig, getConfig } from '../../config/index.js'; +import { Hook } from '@oclif/core'; +import { authOrg } from '../../common/utils/authUtils.js'; -export const hook = async (options: any) => { +const hook: Hook<'prerun'> = async (options) => { // Skip hooks from other commands than hardis commands - const commandId = options?.Command?.id || ""; + const commandId = options?.Command?.id || ''; - if (commandId.startsWith("hardis")) { + if (commandId.startsWith('hardis')) { elapseStart(`${options?.Command?.id} execution time`); } if ( - !commandId.startsWith("hardis") || + !commandId.startsWith('hardis') || [ - "hardis:doc:plugin:generate", - "hardis:source:push", - "hardis:source:pull", - "hardis:scratch:pool:view", - "hardis:source:deploy", - "hardis:source:push", - "hardis:mdapi:deploy", + 'hardis:doc:plugin:generate', + 'hardis:source:push', + 'hardis:source:pull', + 'hardis:scratch:pool:view', + 'hardis:source:deploy', + 'hardis:source:push', + 'hardis:mdapi:deploy', + 'hardis:project:deploy:simulate' ].includes(commandId) ) { return; } // skip if during mocha tests - if (typeof global.it === "function") { + if (typeof global.it === 'function') { return; } await restoreLocalSfdxInfo(); - let configInfo = await getConfig("user"); + let configInfo = await getConfig('user'); if (configInfo.skipAuthCheck === true) { - uxLog(this, c.yellow("No authentication check, you better know what you are doing ;)")); + uxLog(this, c.yellow('No authentication check, you better know what you are doing ;)')); return; } // Manage authentication if DevHub is required but current user is disconnected - if ((options.Command && options.Command.requiresDevhubUsername === true) || options.devHub === true) { + if ( + (options.Command && (options?.Command?.flags as any)['target-dev-hub']?.required === true) || + (options as any)?.devHub === true + ) { let devHubAlias = configInfo.devHubAlias || process.env.DEVHUB_ALIAS; if (devHubAlias == null) { await checkConfig(options); - configInfo = await getConfig("user"); - devHubAlias = configInfo.devHubAlias || "DevHub"; + configInfo = await getConfig('user'); + devHubAlias = configInfo.devHubAlias || 'DevHub'; } await authOrg(devHubAlias, options); } // Manage authentication if org is required but current user is disconnected if ( - ((options?.Command?.requiresUsername === true && !options?.argv?.includes("--skipauth")) || options.checkAuth === true) && - !(options.devHub === true) + (((options?.Command?.flags as any)['target-org']?.required === true && !options?.argv?.includes('--skipauth')) || + (options as any)?.checkAuth === true) && + !((options as any)?.devHub === true) ) { - const orgAlias = options.alias - ? options.alias + const orgAlias = (options as any)?.alias + ? (options as any).alias : process.env.ORG_ALIAS ? process.env.ORG_ALIAS : isCI && configInfo.scratchOrgAlias ? configInfo.scratchOrgAlias - : isCI && options.scratch && configInfo.sfdxAuthUrl + : isCI && (options as any)?.scratch && configInfo.sfdxAuthUrl ? configInfo.sfdxAuthUrl : isCI ? await getCurrentGitBranch({ formatted: true }) - : commandId === "hardis:auth:login" && configInfo.orgAlias + : commandId === 'hardis:auth:login' && configInfo.orgAlias ? configInfo.orgAlias - : configInfo.scratchOrgAlias || "MY_ORG"; // Can be null and it's ok if we're not in scratch org context + : configInfo.scratchOrgAlias || ''; // Can be '' and it's ok if we're not in scratch org context await authOrg(orgAlias, options); } }; -// Authorize an org with sfdxAuthUrl, manually or with JWT -async function authOrg(orgAlias: string, options: any) { - const isDevHub = orgAlias.includes("DevHub"); - - let doConnect = true; - if (!options.checkAuth) { - // Check if we are already authenticated - let orgDisplayCommand = "sfdx force:org:display"; - let setDefaultUsername = false; - if (orgAlias !== "MY_ORG" && (isCI || isDevHub) && !orgAlias.includes("force://")) { - orgDisplayCommand += " --targetusername " + orgAlias; - setDefaultUsername = true; - } else { - if (process.argv.includes("-u") || process.argv.includes("--targetusername")) { - const posUsername = process.argv.indexOf("-u") > -1 ? process.argv.indexOf("-u") + 1 : process.argv.indexOf("--targetusername") + 1; - orgDisplayCommand += " --targetusername " + process.argv[posUsername]; - } - } - const orgInfoResult = await execSfdxJson(orgDisplayCommand, this, { - fail: false, - output: false, - debug: options.debug, - }); - if ( - orgInfoResult.result && - orgInfoResult.result.connectedStatus !== "RefreshTokenAuthError" && - ((orgInfoResult.result.connectedStatus && orgInfoResult.result.connectedStatus.includes("Connected")) || - (options.scratch && orgInfoResult.result.connectedStatus.includes("Unknown")) || - (orgInfoResult.result.alias === orgAlias && orgInfoResult.result.id != null) || - (orgInfoResult.result.username === orgAlias && orgInfoResult.result.id != null) || - (isDevHub && orgInfoResult.result.id != null)) - ) { - // Set as default username or devhubusername - uxLog( - this, - `[sfdx-hardis] You are already ${c.green("connected")} as ${c.green(orgInfoResult.result.username)} on org ${c.green( - orgInfoResult.result.instanceUrl, - )}`, - ); - if (orgInfoResult.result.expirationDate) { - uxLog(this, c.cyan(`[sfdx-hardis] Org expiration date: ${c.yellow(orgInfoResult.result.expirationDate)}`)); - } - if (!isCI) { - uxLog( - this, - c.yellow( - c.italic( - `[sfdx-hardis] If this is NOT the org you want to play with, ${c.whiteBright(c.bold("hit CTRL+C"))}, then input ${c.whiteBright( - c.bold("sfdx hardis:org:select"), - )}`, - ), - ), - ); - } - if (setDefaultUsername) { - const setDefaultUsernameCommand = `sfdx config:set ${isDevHub ? "defaultdevhubusername" : "defaultusername"}=${ - orgInfoResult.result.username - }`; - await execSfdxJson(setDefaultUsernameCommand, this, { fail: false }); - } - doConnect = false; - } - } - // Perform authentication - if (doConnect) { - let logged = false; - const config = await getConfig("user"); - - // Manage auth with sfdxAuthUrl (CI & scratch org only) - const authUrlVarName = `SFDX_AUTH_URL_${orgAlias}`; - const authUrlVarNameUpper = `SFDX_AUTH_URL_${orgAlias.toUpperCase()}`; - let authUrl = process.env[authUrlVarName] || process.env[authUrlVarNameUpper] || orgAlias || ""; - if (isDevHub) { - authUrl = process.env[authUrlVarName] || process.env[authUrlVarNameUpper] || process.env.SFDX_AUTH_URL_DEV_HUB || orgAlias || ""; - } - if (authUrl.includes("force://")) { - const authFile = path.join(await createTempDir(), "sfdxScratchAuth.txt"); - await fs.writeFile(authFile, authUrl, "utf8"); - const authCommand = - `sfdx auth:sfdxurl:store -f ${authFile}` + - (isDevHub ? ` --setdefaultdevhubusername` : ` --setdefaultusername`) + - (!orgAlias.includes("force://") ? ` --setalias ${orgAlias}` : ""); - await execCommand(authCommand, this, { fail: true, output: false }); - uxLog(this, c.cyan("Successfully logged using sfdxAuthUrl")); - await fs.remove(authFile); - return; - } - - // Get auth variables, with priority CLI arguments, environment variables, then .hardis-sfdx.yml config file - let username = - typeof options.Command.flags?.targetusername === "string" - ? options.Command.flags?.targetusername - : process.env.TARGET_USERNAME || isDevHub - ? config.devHubUsername - : config.targetUsername; - if (username == null && isCI) { - const gitBranchFormatted = await getCurrentGitBranch({ formatted: true }); - console.error( - c.yellow( - `[sfdx-hardis][WARNING] You may have to define ${c.bold( - isDevHub - ? "devHubUsername in .sfdx-hardis.yml" - : options.scratch - ? 'cache between your CI jobs: folder ".cache/sfdx-hardis/.sfdx"' - : `targetUsername in config/branches/.sfdx-hardis.${gitBranchFormatted}.yml`, - )} `, - ), - ); - process.exit(1); - } - let instanceUrl = - typeof options.Command?.flags?.instanceurl === "string" && (options.Command?.flags?.instanceurl || "").startsWith("https") - ? options.Command.flags.instanceurl - : (process.env.INSTANCE_URL || "").startsWith("https") - ? process.env.INSTANCE_URL - : config.instanceUrl - ? config.instanceUrl - : "https://login.salesforce.com"; - // Get JWT items clientId and certificate key - const sfdxClientId = await getSfdxClientId(orgAlias, config); - const crtKeyfile = await getCertificateKeyFile(orgAlias, config); - const usernameArg = options.setDefault === false ? "" : isDevHub ? "--setdefaultdevhubusername" : "--setdefaultusername"; - if (crtKeyfile && sfdxClientId && username) { - // Login with JWT - const loginCommand = - "sfdx auth:jwt:grant" + - ` ${usernameArg}` + - ` --clientid ${sfdxClientId}` + - ` --jwtkeyfile ${crtKeyfile}` + - ` --username ${username}` + - ` --instanceurl ${instanceUrl}` + - (orgAlias !== "MY_ORG" ? ` --setalias ${orgAlias}` : ""); - const jwtAuthRes = await execSfdxJson(loginCommand, this, { - fail: false, - }); - // await fs.remove(crtKeyfile); // Delete private key file from temp folder TODO: move to postrun hook - logged = jwtAuthRes.status === 0; - if (!logged) { - console.error(c.red(`[sfdx-hardis][ERROR] JWT login error: \n${JSON.stringify(jwtAuthRes)}`)); - process.exit(1); - } - } else if (!isCI) { - // Login with web auth - const orgLabel = `org ${orgAlias}`; - console.warn( - c.yellow(c.bold(`[sfdx-hardis] You must be connected to ${orgLabel} to perform this command. Please login in the open web browser`)), - ); - - if (isCI) { - console.error(c.red(`See CI authentication doc at https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-auth/`)); - throw new SfdxError( - `In CI context, you may define: - - a .sfdx-hardis.yml file with instanceUrl and targetUsername properties (or INSTANCE_URL and TARGET_USERNAME repo variables) - - a repository secret variable SFDX_CLIENT_ID with consumer key of sfdx connected app - - store server.key file within ssh folder - `, - ); - } - const orgTypes = isDevHub ? ["login"] : ["login", "test"]; - instanceUrl = await promptInstanceUrl(orgTypes, orgAlias); - - const configInfoUsr = await getConfig("user"); - - // Prompt user for Web or Device login - const loginTypeRes = await prompts({ - name: "loginType", - type: "select", - message: "Select a login type (if you don't know, use Web)", - choices: [ - { - title: "🌐 Web Login (If VsCode is locally installed on your computer)", - value: "web", - }, - { - title: "📟 Device Login (Useful for CodeBuilder / CodeSpaces)", - value: "device", - description: "Look at the instructions in the console terminal if you select this option", - }, - ], - default: "web", - initial: "web", - }); - - let loginResult: any = null; - // Manage device login - if (loginTypeRes.loginType === "device") { - const loginCommandArgs = ["org:login:device", "--instanceurl", instanceUrl]; - if (orgAlias !== "MY_ORG" && orgAlias !== configInfoUsr?.scratchOrgAlias) { - loginCommandArgs.push(...["--alias", orgAlias]); - } - if (options.setDefault === true && isDevHub) { - loginCommandArgs.push("--setdefaultdevhubusername"); - } - if (options.setDefault === true && !isDevHub) { - loginCommandArgs.push("--set-default"); - } - const commandStr = "sfdx " + loginCommandArgs.join(" "); - uxLog(this, `[sfdx-hardis][command] ${c.bold(c.bgWhite(c.grey(commandStr)))}`); - loginResult = crossSpawn.sync("sfdx", loginCommandArgs, { stdio: "inherit" }); - } - // Web Login if device login not used - if (loginResult == null) { - const loginCommand = - "sfdx auth:web:login" + - (options.setDefault === false ? "" : isDevHub ? " --setdefaultdevhubusername" : " --setdefaultusername") + - ` --instanceurl ${instanceUrl}` + - (orgAlias !== "MY_ORG" && orgAlias !== configInfoUsr?.scratchOrgAlias ? ` --setalias ${orgAlias}` : ""); - try { - loginResult = await execCommand(loginCommand, this, { output: true, fail: true, spinner: false }); - } catch (e) { - // Give instructions if server is unavailable - if ((e?.message || "").includes("Cannot start the OAuth redirect server on port")) { - uxLog( - this, - c.yellow(c.bold("You might have a ghost sfdx command. Open Task Manager, search for Node.js processes, kill them, then try again")), - ); - } - throw e; - } - } - await clearCache("force:org:list"); - uxLog(this, c.grey(JSON.stringify(loginResult, null, 2))); - logged = loginResult.status === 0; - username = loginResult?.username || "err"; - instanceUrl = loginResult?.instanceUrl || instanceUrl; - } else { - console.error(c.red(`[sfdx-hardis] Unable to connect to org ${orgAlias} with browser. Please try again :)`)); - } - if (logged) { - // Retrieve default username or dev hub username if not returned by command - if (username === "err") { - const configGetRes = await execSfdxJson("sfdx config:get " + (isDevHub ? "defaultdevhubusername" : "defaultusername"), this, { - output: false, - fail: false, - }); - username = configGetRes?.result[0]?.value || ""; - } - uxLog(this, `Successfully logged to ${c.green(instanceUrl)} with ${c.green(username)}`); - WebSocketClient.sendMessage({ event: "refreshStatus" }); - // Assign org to SfdxCommands - if (isDevHub) { - options.Command.flags.targetdevhubusername = username; - // options.Command.assignHubOrg(); // seems to be automatically done by SfdxCommand under the hook - } else { - options.Command.flags.targetusername = username; - // options.Command.assignOrg(); // seems to be automatically done by SfdxCommand under the hook - } - // Display warning message in case of local usage (not CI), and not login command - // if (!(options?.Command?.id || "").startsWith("hardis:auth:login")) { - // console.warn(c.yellow("*** IF YOU SEE AN AUTH ERROR PLEASE RUN AGAIN THE SAME COMMAND :) ***")); - // } - } else { - console.error(c.red("[sfdx-hardis][ERROR] You must be logged to an org to perform this action")); - process.exit(1); // Exit because we should succeed to connect - } - } -} - -// Get clientId for SFDX connected app -async function getSfdxClientId(orgAlias: string, config: any) { - // Try to find in global variables - const sfdxClientIdVarName = `SFDX_CLIENT_ID_${orgAlias}`; - if (process.env[sfdxClientIdVarName]) { - return process.env[sfdxClientIdVarName]; - } - const sfdxClientIdVarNameUpper = sfdxClientIdVarName.toUpperCase(); - if (process.env[sfdxClientIdVarNameUpper]) { - return process.env[sfdxClientIdVarNameUpper]; - } - if (process.env.SFDX_CLIENT_ID) { - console.warn( - c.yellow( - `[sfdx-hardis] If you use CI on multiple branches & orgs, you should better define CI variable ${c.bold( - sfdxClientIdVarNameUpper, - )} than SFDX_CLIENT_ID`, - ), - ); - console.warn(c.yellow(`See CI authentication doc at https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-auth/`)); - return process.env.SFDX_CLIENT_ID; - } - // Try to find in config files ONLY IN LOCAL MODE (in CI, it's supposed to be a CI variable) - if (!isCI && config.devHubSfdxClientId) { - return config.devHubSfdxClientId; - } - if (isCI) { - console.error( - c.red(`[sfdx-hardis] You must set env variable ${c.bold(sfdxClientIdVarNameUpper)} with the Consumer Key value defined on SFDX Connected app`), - ); - console.error(c.red(`See CI authentication doc at https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-auth/`)); - } - return null; -} -// Get clientId for SFDX connected app -async function getKey(orgAlias: string, config: any) { - // Try to find in global variables - const sfdxClientKeyVarName = `SFDX_CLIENT_KEY_${orgAlias}`; - if (process.env[sfdxClientKeyVarName]) { - return process.env[sfdxClientKeyVarName]; - } - const sfdxClientKeyVarNameUpper = sfdxClientKeyVarName.toUpperCase(); - if (process.env[sfdxClientKeyVarNameUpper]) { - return process.env[sfdxClientKeyVarNameUpper]; - } - if (process.env.SFDX_CLIENT_KEY) { - console.warn( - c.yellow( - `[sfdx-hardis] If you use CI on multiple branches & orgs, you should better define CI variable ${c.bold( - sfdxClientKeyVarNameUpper, - )} than SFDX_CLIENT_KEY`, - ), - ); - console.warn(c.yellow(`See CI authentication doc at https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-auth/`)); - return process.env.SFDX_CLIENT_KEY; - } - // Try to find in config files ONLY IN LOCAL MODE (in CI, it's supposed to be a CI variable) - if (!isCI && config.devHubSfdxClientKey) { - return config.devHubSfdxClientKey; - } - if (isCI) { - console.error( - c.red(`[sfdx-hardis] You must set env variable ${c.bold(sfdxClientKeyVarNameUpper)} with the value of SSH private key encryption key`), - ); - console.error(c.red(`See CI authentication doc at https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-auth/`)); - } - return null; -} -// Try to find certificate key file for sfdx connected app in different locations -async function getCertificateKeyFile(orgAlias: string, config: any) { - const filesToTry = [ - `./config/branches/.jwt/${orgAlias}.key`, - `./config/.jwt/${orgAlias}.key`, - `./ssh/${orgAlias}.key`, - `./.ssh/${orgAlias}.key`, - "./ssh/server.key", - ]; - for (const file of filesToTry) { - if (fs.existsSync(file)) { - // Decrypt SSH private key and write a temporary file - const sshKey = await getKey(orgAlias, config); - if (sshKey == null) { - continue; - } - const tmpSshKeyFile = path.join(await createTempDir(), `${orgAlias}.key`); - await decryptFile(file, tmpSshKeyFile, sshKey); - return tmpSshKeyFile; - } - } - if (isCI) { - console.error(c.red(`[sfdx-hardis] You must put a certificate key to connect via JWT.Possible locations:\n -${filesToTry.join("\n -")}`)); - console.error(c.red(`See CI authentication doc at https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-auth/`)); - } - return null; -} +export default hook; diff --git a/src/hooks/prerun/check-dependencies.ts b/src/hooks/prerun/check-dependencies.ts index 13d40dce2..438728846 100644 --- a/src/hooks/prerun/check-dependencies.ts +++ b/src/hooks/prerun/check-dependencies.ts @@ -1,16 +1,21 @@ /* jscpd:ignore-start */ -import * as os from "os"; -import { checkSfdxPlugin, git, uxLog, isCI, checkAppDependency, isGitRepo } from "../../common/utils"; -import { getConfig } from "../../config"; +import * as os from 'os'; +import { checkSfdxPlugin, git, uxLog, isCI, checkAppDependency, isGitRepo } from '../../common/utils/index.js'; +import { getConfig } from '../../config/index.js'; +import { Hook } from '@oclif/core'; -export const hook = async (options: any) => { +const hook: Hook<'prerun'> = async (options) => { // Skip hooks from other commands than hardis commands - const commandId = options?.Command?.id || ""; - if (!commandId.startsWith("hardis")) { + const commandId = options?.Command?.id || ''; + if (!commandId.startsWith('hardis')) { return; } - if (commandId.startsWith("hardis:doc") || commandId.startsWith("hardis:org:files") || commandId.startsWith("hardis:org:data")) { + if ( + commandId.startsWith('hardis:doc') || + commandId.startsWith('hardis:org:files') || + commandId.startsWith('hardis:org:data') + ) { return; } @@ -22,48 +27,50 @@ export const hook = async (options: any) => { .then(async (gitConfig) => { const allConfigs = gitConfig.all; // User - if (allConfigs["user.name"] == null) { + if (allConfigs['user.name'] == null) { const username = os.userInfo().username; - await git({ output: true }).addConfig("user.name", username); + await git({ output: true }).addConfig('user.name', username); uxLog(this, `Defined ${username} as git user.name`); } // Email - if (allConfigs["user.email"] == null) { - const config = await getConfig("user"); - const email = config.userEmail || "default@cloudity.com"; - await git({ output: true }).addConfig("user.email", email); - uxLog(this, `Defined ${email} as git user.email` + (email === "default@cloudity.com") ? " (temporary)" : ""); + if (allConfigs['user.email'] == null) { + const config = await getConfig('user'); + const email = config.userEmail || 'default@cloudity.com'; + await git({ output: true }).addConfig('user.email', email); + uxLog(this, `Defined ${email} as git user.email` + (email === 'default@cloudity.com') ? ' (temporary)' : ''); } // Manage special characters in git file / folder names - if (allConfigs["core.quotepath"] == null || allConfigs["core.quotepath"] == "true") { - await git({ output: true }).addConfig("core.quotepath", "false"); + if (allConfigs['core.quotepath'] == null || allConfigs['core.quotepath'] == 'true') { + await git({ output: true }).addConfig('core.quotepath', 'false'); uxLog(this, `Defined "false" as git core.quotepath`); } // Merge tool - if (allConfigs["merge.tool"] == null) { - await git({ output: true }).addConfig("merge.tool", "vscode"); - await git({ output: true }).addConfig("mergetool.vscode.cmd", "code --wait $MERGED"); - uxLog(this, "Defined vscode as git merge tool "); + if (allConfigs['merge.tool'] == null) { + await git({ output: true }).addConfig('merge.tool', 'vscode'); + await git({ output: true }).addConfig('mergetool.vscode.cmd', 'code --wait $MERGED'); + uxLog(this, 'Defined vscode as git merge tool '); } // Diff tool - if (allConfigs["diff.tool"] == null) { - await git({ output: true }).addConfig("diff.tool", "vscode"); - await git({ output: true }).addConfig("difftool.vscode.cmd", "code --wait --diff $LOCAL $REMOTE"); - uxLog(this, "Defined vscode as git diff tool "); + if (allConfigs['diff.tool'] == null) { + await git({ output: true }).addConfig('diff.tool', 'vscode'); + await git({ output: true }).addConfig('difftool.vscode.cmd', 'code --wait --diff $LOCAL $REMOTE'); + uxLog(this, 'Defined vscode as git diff tool '); } }); } // Check required sfdx-plugins to be installed - const requiresSfdxPlugins = options?.Command?.requiresSfdxPlugins || []; + const requiresSfdxPlugins = (options?.Command as any)?.requiresSfdxPlugins || []; for (const sfdxPluginName of requiresSfdxPlugins) { await checkSfdxPlugin(sfdxPluginName); } // Check required dependencies installed - const requiresDependencies = options?.Command?.requiresDependencies || []; - requiresDependencies.push("git"); + const requiresDependencies = (options?.Command as any).requiresDependencies || []; + requiresDependencies.push('git'); for (const appName of requiresDependencies) { await checkAppDependency(appName); } }; + +export default hook; diff --git a/src/settings.ts b/src/settings.ts index cba61076f..4409d931f 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,3 +1,7 @@ -import * as path from "path"; +import * as path from 'path'; +import { fileURLToPath } from 'url'; -export const PACKAGE_ROOT_DIR = path.resolve(__dirname, ".."); +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +export const PACKAGE_ROOT_DIR = path.resolve(__dirname, '..'); diff --git a/test/.eslintrc.cjs b/test/.eslintrc.cjs new file mode 100644 index 000000000..e2cf76d5d --- /dev/null +++ b/test/.eslintrc.cjs @@ -0,0 +1,19 @@ +module.exports = { + extends: '../.eslintrc.json', + // Allow describe and it + env: { mocha: true }, + rules: { + // Allow assert style expressions. i.e. expect(true).to.be.true + 'no-unused-expressions': 'off', + + // It is common for tests to stub out method. + + // Return types are defined by the source code. Allows for quick overwrites. + '@typescript-eslint/explicit-function-return-type': 'off', + // Mocked out the methods that shouldn't do anything in the tests. + '@typescript-eslint/no-empty-function': 'off', + // Easily return a promise in a mocked method. + '@typescript-eslint/require-await': 'off', + header: 'off', + }, +}; diff --git a/test/.sfdx-hardis.yml b/test/.sfdx-hardis.yml index fe4014a79..c2c3004f3 100644 --- a/test/.sfdx-hardis.yml +++ b/test/.sfdx-hardis.yml @@ -7,13 +7,13 @@ customCommands: label: Generate manifest icon: file.svg tooltip: Generates a manifest package.xml using local sfdx source files - command: sfdx force:source:manifest:create --sourcepath force-app --manifestname myNewManifest + command: sf project manifest create --source-path force-app --name myNewManifest helpUrl: https://megalinter.io/ - id: list-all-orgs label: List all orgs icon: salesforce.svg tooltip: List all orgs that has already been authenticated using sfdx - command: sfdx force:org:list --all + command: sf org list --all - id: custom-menu-2 label: Another custom menu commands: diff --git a/test/commands/hello/world.nut.ts b/test/commands/hello/world.nut.ts new file mode 100644 index 000000000..e7f219988 --- /dev/null +++ b/test/commands/hello/world.nut.ts @@ -0,0 +1,27 @@ +import { execCmd, TestSession } from '@salesforce/cli-plugins-testkit'; +import { expect } from 'chai'; +import { HelloWorldResult } from '../../../src/commands/hello/world.js'; + +let testSession: TestSession; + +describe('hello world NUTs', () => { + before('prepare session', async () => { + testSession = await TestSession.create(); + }); + + after(async () => { + await testSession?.clean(); + }); + + it('should say hello to the world', () => { + const result = execCmd('hello world --json', { ensureExitCode: 0 }).jsonOutput?.result; + expect(result?.name).to.equal('World'); + }); + + it('should say hello to a given person', () => { + const result = execCmd('hello world --name Astro --json', { + ensureExitCode: 0, + }).jsonOutput?.result; + expect(result?.name).to.equal('Astro'); + }); +}); diff --git a/test/commands/hello/world.test.ts b/test/commands/hello/world.test.ts new file mode 100644 index 000000000..f2d1ae529 --- /dev/null +++ b/test/commands/hello/world.test.ts @@ -0,0 +1,45 @@ +import { TestContext } from '@salesforce/core/testSetup'; +import { expect } from 'chai'; +import { stubSfCommandUx } from '@salesforce/sf-plugins-core'; +import World from '../../../src/commands/hello/world.js'; + +describe('hello world', () => { + const $$ = new TestContext(); + let sfCommandStubs: ReturnType; + + beforeEach(() => { + sfCommandStubs = stubSfCommandUx($$.SANDBOX); + }); + + afterEach(() => { + $$.restore(); + }); + + it('runs hello world', async () => { + await World.run([]); + const output = sfCommandStubs.log + .getCalls() + .flatMap((c) => c.args) + .join('\n'); + expect(output).to.include('Hello World'); + }); + + it('runs hello world with --json and no provided name', async () => { + const result = await World.run([]); + expect(result.name).to.equal('World'); + }); + + it('runs hello world --name Astro', async () => { + await World.run(['--name', 'Astro']); + const output = sfCommandStubs.log + .getCalls() + .flatMap((c) => c.args) + .join('\n'); + expect(output).to.include('Hello Astro'); + }); + + it('runs hello world --name Astro --json', async () => { + const result = await World.run(['--name', 'Astro', '--json']); + expect(result.name).to.equal('Astro'); + }); +}); diff --git a/test/tsconfig.json b/test/tsconfig.json index 9df43c15e..a5f451cf0 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -1,3 +1,7 @@ { - "extends": "../tsconfig" + "extends": "@salesforce/dev-config/tsconfig-test-strict-esm", + "include": ["./**/*.ts"], + "compilerOptions": { + "skipLibCheck": true + } } diff --git a/tsconfig.json b/tsconfig.json index d6c5062a6..4d65e3fd1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,18 +1,14 @@ { + "extends": "@salesforce/dev-config/tsconfig-strict-esm.js", "compilerOptions": { - "moduleResolution": "node", - "module": "commonjs", - "target": "es2019", - "lib": ["es2019"], - "alwaysStrict": true, - "noUnusedLocals": true, - "sourceMap": true, - "declaration": true, - "outDir": "./lib", - "importHelpers": true, - "resolveJsonModule": true, - "types": ["node", "mocha"], + "outDir": "lib", + "rootDir": "src", + "noImplicitAny": false, + "noImplicitThis": false, + "strictPropertyInitialization": false, "skipLibCheck": true }, - "include": ["./src/**/*"] -} + "include": [ + "./src/**/*.ts" + ] +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 85cac707b..a89bef926 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,374 +2,1104 @@ # yarn lockfile v1 -"@actions/github@^5.1.1": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@actions/github/-/github-5.1.1.tgz#40b9b9e1323a5efcf4ff7dadd33d8ea51651bbcb" - integrity sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g== +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@actions/github@^6.0.0": + version "6.0.0" + resolved "https://registry.npmjs.org/@actions/github/-/github-6.0.0.tgz" + integrity sha512-alScpSVnYmjNEXboZjarjukQEzgCRmjMv6Xj47fsdnqGS73bjJNDpiiXmp8jr0UZLdUB6d9jW63IcmddUP+l0g== dependencies: - "@actions/http-client" "^2.0.1" - "@octokit/core" "^3.6.0" - "@octokit/plugin-paginate-rest" "^2.17.0" - "@octokit/plugin-rest-endpoint-methods" "^5.13.0" + "@actions/http-client" "^2.2.0" + "@octokit/core" "^5.0.1" + "@octokit/plugin-paginate-rest" "^9.0.0" + "@octokit/plugin-rest-endpoint-methods" "^10.0.0" -"@actions/http-client@^2.0.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-2.1.1.tgz#a8e97699c315bed0ecaeaaeb640948470d4586a0" - integrity sha512-qhrkRMB40bbbLo7gF+0vu+X+UawOvQQqNAA/5Unx774RS8poaOhThDOG6BGmxvAnxhQnDp2BG/ZUm65xZILTpw== +"@actions/http-client@^2.2.0": + version "2.2.3" + resolved "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz" + integrity sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA== dependencies: tunnel "^0.0.6" + undici "^5.25.4" -"@adobe/node-fetch-retry@^1.1.2": - version "1.1.2" - resolved "https://registry.npmjs.org/@adobe/node-fetch-retry/-/node-fetch-retry-1.1.2.tgz" - integrity sha512-Fg57i/0otfuuL5wciBKMEd1NR6tz0sGSOx6nsF5+6QX6zI5kCOqnadj5i/28xWC4Y04Iby/yQ+AAFqtaGyBGwg== +"@ampproject/remapping@^2.1.0": + version "2.2.0" + resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz" + integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== dependencies: - abort-controller "^3.0.0" - node-fetch "^2.6.1" + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" -"@amplitude/identify@^1.5.0": - version "1.5.0" - resolved "https://registry.npmjs.org/@amplitude/identify/-/identify-1.5.0.tgz" - integrity sha512-GCDxvwZvfFInZQ6m5LYnb9sVmJP+n+Re6vkBdtclp9uTNVPpdsXtdw2ann5Ts5pV10fivDpgdZ3OHehSxe742A== +"@aws-crypto/crc32@5.2.0": + version "5.2.0" + resolved "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz" + integrity sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg== dependencies: - "@amplitude/types" "^1.5.0" - "@amplitude/utils" "^1.5.0" - tslib "^1.9.3" + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + tslib "^2.6.2" -"@amplitude/node@^1.3.2": - version "1.5.0" - resolved "https://registry.npmjs.org/@amplitude/node/-/node-1.5.0.tgz" - integrity sha512-/jfmYHanhln/OAuL8QG76EjThnaRDnfxLxO4oYstMFZKRktnUahE5i0sH02+tJ7I2nzq+4KTYY2hc8Rvi+Vt4Q== +"@aws-crypto/crc32c@5.2.0": + version "5.2.0" + resolved "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz" + integrity sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag== dependencies: - "@amplitude/identify" "^1.5.0" - "@amplitude/types" "^1.5.0" - "@amplitude/utils" "^1.5.0" - tslib "^1.9.3" - -"@amplitude/types@^1.5.0": - version "1.5.0" - resolved "https://registry.npmjs.org/@amplitude/types/-/types-1.5.0.tgz" - integrity sha512-XspuOsUzUcxwAptHeGiIn4giuLWs285xTJa7h8kAEEynxtEI3/krWCoDYZSB9PekaPXB6phxiO/tMd9t5V9LgQ== + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + tslib "^2.6.2" -"@amplitude/utils@^1.5.0": - version "1.5.0" - resolved "https://registry.npmjs.org/@amplitude/utils/-/utils-1.5.0.tgz" - integrity sha512-1DrDJkb4dVX+FiBXhGpO2Dn2cRKdP+gtrVR8vZcE8wz/V2XxUI3DDx7uQbIS6WbQf6swv6Uo2eMHYtrwebostw== +"@aws-crypto/sha1-browser@5.2.0": + version "5.2.0" + resolved "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz" + integrity sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg== dependencies: - "@amplitude/types" "^1.5.0" - tslib "^1.9.3" + "@aws-crypto/supports-web-crypto" "^5.2.0" + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + "@aws-sdk/util-locate-window" "^3.0.0" + "@smithy/util-utf8" "^2.0.0" + tslib "^2.6.2" -"@babel/code-frame@7.12.11": - version "7.12.11" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz" - integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== +"@aws-crypto/sha256-browser@5.2.0": + version "5.2.0" + resolved "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz" + integrity sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw== + dependencies: + "@aws-crypto/sha256-js" "^5.2.0" + "@aws-crypto/supports-web-crypto" "^5.2.0" + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + "@aws-sdk/util-locate-window" "^3.0.0" + "@smithy/util-utf8" "^2.0.0" + tslib "^2.6.2" + +"@aws-crypto/sha256-js@5.2.0", "@aws-crypto/sha256-js@^5.2.0": + version "5.2.0" + resolved "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz" + integrity sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA== dependencies: - "@babel/highlight" "^7.10.4" + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + tslib "^2.6.2" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13": - version "7.12.13" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz" - integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== +"@aws-crypto/supports-web-crypto@^5.2.0": + version "5.2.0" + resolved "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz" + integrity sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg== dependencies: - "@babel/highlight" "^7.12.13" + tslib "^2.6.2" -"@babel/code-frame@^7.22.13": +"@aws-crypto/util@^5.2.0": + version "5.2.0" + resolved "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz" + integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ== + dependencies: + "@aws-sdk/types" "^3.222.0" + "@smithy/util-utf8" "^2.0.0" + tslib "^2.6.2" + +"@aws-sdk/client-cloudfront@^3.645.0": + version "3.645.0" + resolved "https://registry.npmjs.org/@aws-sdk/client-cloudfront/-/client-cloudfront-3.645.0.tgz" + integrity sha512-buXVTKi3b+B6LqhTxoTELfszRviNI6Cg9eoctKaR4UcKs1VlB4D/rX2Mt1ShQ0QBnC5pboOT0nbDWFkAn/LcBQ== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/client-sso-oidc" "3.645.0" + "@aws-sdk/client-sts" "3.645.0" + "@aws-sdk/core" "3.635.0" + "@aws-sdk/credential-provider-node" "3.645.0" + "@aws-sdk/middleware-host-header" "3.620.0" + "@aws-sdk/middleware-logger" "3.609.0" + "@aws-sdk/middleware-recursion-detection" "3.620.0" + "@aws-sdk/middleware-user-agent" "3.645.0" + "@aws-sdk/region-config-resolver" "3.614.0" + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-endpoints" "3.645.0" + "@aws-sdk/util-user-agent-browser" "3.609.0" + "@aws-sdk/util-user-agent-node" "3.614.0" + "@aws-sdk/xml-builder" "3.609.0" + "@smithy/config-resolver" "^3.0.5" + "@smithy/core" "^2.4.0" + "@smithy/fetch-http-handler" "^3.2.4" + "@smithy/hash-node" "^3.0.3" + "@smithy/invalid-dependency" "^3.0.3" + "@smithy/middleware-content-length" "^3.0.5" + "@smithy/middleware-endpoint" "^3.1.0" + "@smithy/middleware-retry" "^3.0.15" + "@smithy/middleware-serde" "^3.0.3" + "@smithy/middleware-stack" "^3.0.3" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/node-http-handler" "^3.1.4" + "@smithy/protocol-http" "^4.1.0" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.15" + "@smithy/util-defaults-mode-node" "^3.0.15" + "@smithy/util-endpoints" "^2.0.5" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-retry" "^3.0.3" + "@smithy/util-stream" "^3.1.3" + "@smithy/util-utf8" "^3.0.0" + "@smithy/util-waiter" "^3.1.2" + tslib "^2.6.2" + +"@aws-sdk/client-s3@^3.645.0": + version "3.645.0" + resolved "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.645.0.tgz" + integrity sha512-RjT/mfNv4yr1uv/+aEXgSIxC5EB+yHPSU7hH0KZOZrvZEFASLl0i4FeoHzbMEOH5KdKGAi0uu3zRP3D1y45sKg== + dependencies: + "@aws-crypto/sha1-browser" "5.2.0" + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/client-sso-oidc" "3.645.0" + "@aws-sdk/client-sts" "3.645.0" + "@aws-sdk/core" "3.635.0" + "@aws-sdk/credential-provider-node" "3.645.0" + "@aws-sdk/middleware-bucket-endpoint" "3.620.0" + "@aws-sdk/middleware-expect-continue" "3.620.0" + "@aws-sdk/middleware-flexible-checksums" "3.620.0" + "@aws-sdk/middleware-host-header" "3.620.0" + "@aws-sdk/middleware-location-constraint" "3.609.0" + "@aws-sdk/middleware-logger" "3.609.0" + "@aws-sdk/middleware-recursion-detection" "3.620.0" + "@aws-sdk/middleware-sdk-s3" "3.635.0" + "@aws-sdk/middleware-ssec" "3.609.0" + "@aws-sdk/middleware-user-agent" "3.645.0" + "@aws-sdk/region-config-resolver" "3.614.0" + "@aws-sdk/signature-v4-multi-region" "3.635.0" + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-endpoints" "3.645.0" + "@aws-sdk/util-user-agent-browser" "3.609.0" + "@aws-sdk/util-user-agent-node" "3.614.0" + "@aws-sdk/xml-builder" "3.609.0" + "@smithy/config-resolver" "^3.0.5" + "@smithy/core" "^2.4.0" + "@smithy/eventstream-serde-browser" "^3.0.6" + "@smithy/eventstream-serde-config-resolver" "^3.0.3" + "@smithy/eventstream-serde-node" "^3.0.5" + "@smithy/fetch-http-handler" "^3.2.4" + "@smithy/hash-blob-browser" "^3.1.2" + "@smithy/hash-node" "^3.0.3" + "@smithy/hash-stream-node" "^3.1.2" + "@smithy/invalid-dependency" "^3.0.3" + "@smithy/md5-js" "^3.0.3" + "@smithy/middleware-content-length" "^3.0.5" + "@smithy/middleware-endpoint" "^3.1.0" + "@smithy/middleware-retry" "^3.0.15" + "@smithy/middleware-serde" "^3.0.3" + "@smithy/middleware-stack" "^3.0.3" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/node-http-handler" "^3.1.4" + "@smithy/protocol-http" "^4.1.0" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.15" + "@smithy/util-defaults-mode-node" "^3.0.15" + "@smithy/util-endpoints" "^2.0.5" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-retry" "^3.0.3" + "@smithy/util-stream" "^3.1.3" + "@smithy/util-utf8" "^3.0.0" + "@smithy/util-waiter" "^3.1.2" + tslib "^2.6.2" + +"@aws-sdk/client-sso-oidc@3.645.0": + version "3.645.0" + resolved "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.645.0.tgz" + integrity sha512-X9ULtdk3cO+1ysurEkJ1MSnu6U00qodXx+IVual+1jXX4RYY1WmQmfo7uDKf6FFkz7wW1DAqU+GJIBNQr0YH8A== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.635.0" + "@aws-sdk/credential-provider-node" "3.645.0" + "@aws-sdk/middleware-host-header" "3.620.0" + "@aws-sdk/middleware-logger" "3.609.0" + "@aws-sdk/middleware-recursion-detection" "3.620.0" + "@aws-sdk/middleware-user-agent" "3.645.0" + "@aws-sdk/region-config-resolver" "3.614.0" + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-endpoints" "3.645.0" + "@aws-sdk/util-user-agent-browser" "3.609.0" + "@aws-sdk/util-user-agent-node" "3.614.0" + "@smithy/config-resolver" "^3.0.5" + "@smithy/core" "^2.4.0" + "@smithy/fetch-http-handler" "^3.2.4" + "@smithy/hash-node" "^3.0.3" + "@smithy/invalid-dependency" "^3.0.3" + "@smithy/middleware-content-length" "^3.0.5" + "@smithy/middleware-endpoint" "^3.1.0" + "@smithy/middleware-retry" "^3.0.15" + "@smithy/middleware-serde" "^3.0.3" + "@smithy/middleware-stack" "^3.0.3" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/node-http-handler" "^3.1.4" + "@smithy/protocol-http" "^4.1.0" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.15" + "@smithy/util-defaults-mode-node" "^3.0.15" + "@smithy/util-endpoints" "^2.0.5" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-retry" "^3.0.3" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/client-sso@3.645.0": + version "3.645.0" + resolved "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.645.0.tgz" + integrity sha512-2rc8TjnsNddOeKQ/pfNN7deNvGLXAeKeYtHtGDAiM2qfTKxd2sNcAsZ+JCDLyshuD4xLM5fpUyR0X8As9EAouQ== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.635.0" + "@aws-sdk/middleware-host-header" "3.620.0" + "@aws-sdk/middleware-logger" "3.609.0" + "@aws-sdk/middleware-recursion-detection" "3.620.0" + "@aws-sdk/middleware-user-agent" "3.645.0" + "@aws-sdk/region-config-resolver" "3.614.0" + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-endpoints" "3.645.0" + "@aws-sdk/util-user-agent-browser" "3.609.0" + "@aws-sdk/util-user-agent-node" "3.614.0" + "@smithy/config-resolver" "^3.0.5" + "@smithy/core" "^2.4.0" + "@smithy/fetch-http-handler" "^3.2.4" + "@smithy/hash-node" "^3.0.3" + "@smithy/invalid-dependency" "^3.0.3" + "@smithy/middleware-content-length" "^3.0.5" + "@smithy/middleware-endpoint" "^3.1.0" + "@smithy/middleware-retry" "^3.0.15" + "@smithy/middleware-serde" "^3.0.3" + "@smithy/middleware-stack" "^3.0.3" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/node-http-handler" "^3.1.4" + "@smithy/protocol-http" "^4.1.0" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.15" + "@smithy/util-defaults-mode-node" "^3.0.15" + "@smithy/util-endpoints" "^2.0.5" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-retry" "^3.0.3" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/client-sts@3.645.0": + version "3.645.0" + resolved "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.645.0.tgz" + integrity sha512-6azXYtvtnAsPf2ShN9vKynIYVcJOpo6IoVmoMAVgNaBJyllP+s/RORzranYZzckqfmrudSxtct4rVapjLWuAMg== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/client-sso-oidc" "3.645.0" + "@aws-sdk/core" "3.635.0" + "@aws-sdk/credential-provider-node" "3.645.0" + "@aws-sdk/middleware-host-header" "3.620.0" + "@aws-sdk/middleware-logger" "3.609.0" + "@aws-sdk/middleware-recursion-detection" "3.620.0" + "@aws-sdk/middleware-user-agent" "3.645.0" + "@aws-sdk/region-config-resolver" "3.614.0" + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-endpoints" "3.645.0" + "@aws-sdk/util-user-agent-browser" "3.609.0" + "@aws-sdk/util-user-agent-node" "3.614.0" + "@smithy/config-resolver" "^3.0.5" + "@smithy/core" "^2.4.0" + "@smithy/fetch-http-handler" "^3.2.4" + "@smithy/hash-node" "^3.0.3" + "@smithy/invalid-dependency" "^3.0.3" + "@smithy/middleware-content-length" "^3.0.5" + "@smithy/middleware-endpoint" "^3.1.0" + "@smithy/middleware-retry" "^3.0.15" + "@smithy/middleware-serde" "^3.0.3" + "@smithy/middleware-stack" "^3.0.3" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/node-http-handler" "^3.1.4" + "@smithy/protocol-http" "^4.1.0" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.15" + "@smithy/util-defaults-mode-node" "^3.0.15" + "@smithy/util-endpoints" "^2.0.5" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-retry" "^3.0.3" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/core@3.635.0": + version "3.635.0" + resolved "https://registry.npmjs.org/@aws-sdk/core/-/core-3.635.0.tgz" + integrity sha512-i1x/E/sgA+liUE1XJ7rj1dhyXpAKO1UKFUcTTHXok2ARjWTvszHnSXMOsB77aPbmn0fUp1JTx2kHUAZ1LVt5Bg== + dependencies: + "@smithy/core" "^2.4.0" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/property-provider" "^3.1.3" + "@smithy/protocol-http" "^4.1.0" + "@smithy/signature-v4" "^4.1.0" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + "@smithy/util-middleware" "^3.0.3" + fast-xml-parser "4.4.1" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-env@3.620.1": + version "3.620.1" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.620.1.tgz" + integrity sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-http@3.635.0": + version "3.635.0" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.635.0.tgz" + integrity sha512-iJyRgEjOCQlBMXqtwPLIKYc7Bsc6nqjrZybdMDenPDa+kmLg7xh8LxHsu9088e+2/wtLicE34FsJJIfzu3L82g== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/fetch-http-handler" "^3.2.4" + "@smithy/node-http-handler" "^3.1.4" + "@smithy/property-provider" "^3.1.3" + "@smithy/protocol-http" "^4.1.0" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + "@smithy/util-stream" "^3.1.3" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-ini@3.645.0": + version "3.645.0" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.645.0.tgz" + integrity sha512-LlZW0qwUwNlTaAIDCNpLbPsyXvS42pRIwF92fgtCQedmdnpN3XRUC6hcwSYI7Xru3GGKp3RnceOvsdOaRJORsw== + dependencies: + "@aws-sdk/credential-provider-env" "3.620.1" + "@aws-sdk/credential-provider-http" "3.635.0" + "@aws-sdk/credential-provider-process" "3.620.1" + "@aws-sdk/credential-provider-sso" "3.645.0" + "@aws-sdk/credential-provider-web-identity" "3.621.0" + "@aws-sdk/types" "3.609.0" + "@smithy/credential-provider-imds" "^3.2.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/shared-ini-file-loader" "^3.1.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-node@3.645.0": + version "3.645.0" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.645.0.tgz" + integrity sha512-eGFFuNvLeXjCJf5OCIuSEflxUowmK+bCS+lK4M8ofsYOEGAivdx7C0UPxNjHpvM8wKd8vpMl5phTeS9BWX5jMQ== + dependencies: + "@aws-sdk/credential-provider-env" "3.620.1" + "@aws-sdk/credential-provider-http" "3.635.0" + "@aws-sdk/credential-provider-ini" "3.645.0" + "@aws-sdk/credential-provider-process" "3.620.1" + "@aws-sdk/credential-provider-sso" "3.645.0" + "@aws-sdk/credential-provider-web-identity" "3.621.0" + "@aws-sdk/types" "3.609.0" + "@smithy/credential-provider-imds" "^3.2.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/shared-ini-file-loader" "^3.1.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-process@3.620.1": + version "3.620.1" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.620.1.tgz" + integrity sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/shared-ini-file-loader" "^3.1.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-sso@3.645.0": + version "3.645.0" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.645.0.tgz" + integrity sha512-d6XuChAl5NCsCrUexc6AFb4efPmb9+66iwPylKG+iMTMYgO1ackfy1Q2/f35jdn0jolkPkzKsVyfzsEVoID6ew== + dependencies: + "@aws-sdk/client-sso" "3.645.0" + "@aws-sdk/token-providers" "3.614.0" + "@aws-sdk/types" "3.609.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/shared-ini-file-loader" "^3.1.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-web-identity@3.621.0": + version "3.621.0" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.621.0.tgz" + integrity sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-bucket-endpoint@3.620.0": + version "3.620.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.620.0.tgz" + integrity sha512-eGLL0W6L3HDb3OACyetZYOWpHJ+gLo0TehQKeQyy2G8vTYXqNTeqYhuI6up9HVjBzU9eQiULVQETmgQs7TFaRg== + dependencies: + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-arn-parser" "3.568.0" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/protocol-http" "^4.1.0" + "@smithy/types" "^3.3.0" + "@smithy/util-config-provider" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-expect-continue@3.620.0": + version "3.620.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.620.0.tgz" + integrity sha512-QXeRFMLfyQ31nAHLbiTLtk0oHzG9QLMaof5jIfqcUwnOkO8YnQdeqzakrg1Alpy/VQ7aqzIi8qypkBe2KXZz0A== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/protocol-http" "^4.1.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-flexible-checksums@3.620.0": + version "3.620.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.620.0.tgz" + integrity sha512-ftz+NW7qka2sVuwnnO1IzBku5ccP+s5qZGeRTPgrKB7OzRW85gthvIo1vQR2w+OwHFk7WJbbhhWwbCbktnP4UA== + dependencies: + "@aws-crypto/crc32" "5.2.0" + "@aws-crypto/crc32c" "5.2.0" + "@aws-sdk/types" "3.609.0" + "@smithy/is-array-buffer" "^3.0.0" + "@smithy/protocol-http" "^4.1.0" + "@smithy/types" "^3.3.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-host-header@3.620.0": + version "3.620.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.620.0.tgz" + integrity sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/protocol-http" "^4.1.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-location-constraint@3.609.0": + version "3.609.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.609.0.tgz" + integrity sha512-xzsdoTkszGVqGVPjUmgoP7TORiByLueMHieI1fhQL888WPdqctwAx3ES6d/bA9Q/i8jnc6hs+Fjhy8UvBTkE9A== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-logger@3.609.0": + version "3.609.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.609.0.tgz" + integrity sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-recursion-detection@3.620.0": + version "3.620.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.620.0.tgz" + integrity sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/protocol-http" "^4.1.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-sdk-s3@3.635.0": + version "3.635.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.635.0.tgz" + integrity sha512-RLdYJPEV4JL/7NBoFUs7VlP90X++5FlJdxHz0DzCjmiD3qCviKy+Cym3qg1gBgHwucs5XisuClxDrGokhAdTQw== + dependencies: + "@aws-sdk/core" "3.635.0" + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-arn-parser" "3.568.0" + "@smithy/core" "^2.4.0" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/protocol-http" "^4.1.0" + "@smithy/signature-v4" "^4.1.0" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + "@smithy/util-config-provider" "^3.0.0" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-stream" "^3.1.3" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-ssec@3.609.0": + version "3.609.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.609.0.tgz" + integrity sha512-GZSD1s7+JswWOTamVap79QiDaIV7byJFssBW68GYjyRS5EBjNfwA/8s+6uE6g39R3ojyTbYOmvcANoZEhSULXg== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-user-agent@3.645.0": + version "3.645.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.645.0.tgz" + integrity sha512-NpTAtqWK+49lRuxfz7st9for80r4NriCMK0RfdJSoPFVntjsSQiQ7+2nW2XL05uVY633e9DvCAw8YatX3zd1mw== + dependencies: + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-endpoints" "3.645.0" + "@smithy/protocol-http" "^4.1.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/region-config-resolver@3.614.0": + version "3.614.0" + resolved "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.614.0.tgz" + integrity sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/types" "^3.3.0" + "@smithy/util-config-provider" "^3.0.0" + "@smithy/util-middleware" "^3.0.3" + tslib "^2.6.2" + +"@aws-sdk/signature-v4-multi-region@3.635.0": + version "3.635.0" + resolved "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.635.0.tgz" + integrity sha512-J6QY4/invOkpogCHjSaDON1hF03viPpOnsrzVuCvJMmclS/iG62R4EY0wq1alYll0YmSdmKlpJwHMWwGtqK63Q== + dependencies: + "@aws-sdk/middleware-sdk-s3" "3.635.0" + "@aws-sdk/types" "3.609.0" + "@smithy/protocol-http" "^4.1.0" + "@smithy/signature-v4" "^4.1.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/token-providers@3.614.0": + version "3.614.0" + resolved "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.614.0.tgz" + integrity sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/shared-ini-file-loader" "^3.1.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/types@3.609.0": + version "3.609.0" + resolved "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz" + integrity sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/types@^3.222.0": + version "3.535.0" + resolved "https://registry.npmjs.org/@aws-sdk/types/-/types-3.535.0.tgz" + integrity sha512-aY4MYfduNj+sRR37U7XxYR8wemfbKP6lx00ze2M2uubn7mZotuVrWYAafbMSXrdEMSToE5JDhr28vArSOoLcSg== + dependencies: + "@smithy/types" "^2.12.0" + tslib "^2.6.2" + +"@aws-sdk/util-arn-parser@3.568.0": + version "3.568.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.568.0.tgz" + integrity sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w== + dependencies: + tslib "^2.6.2" + +"@aws-sdk/util-endpoints@3.645.0": + version "3.645.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.645.0.tgz" + integrity sha512-Oe+xaU4ic4PB1k3pb5VTC1/MWES13IlgpaQw01bVHGfwP6Yv6zZOxizRzca2Y3E+AyR+nKD7vXtHRY+w3bi4bg== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/types" "^3.3.0" + "@smithy/util-endpoints" "^2.0.5" + tslib "^2.6.2" + +"@aws-sdk/util-locate-window@^3.0.0": + version "3.465.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.465.0.tgz" + integrity sha512-f+QNcWGswredzC1ExNAB/QzODlxwaTdXkNT5cvke2RLX8SFU5pYk6h4uCtWC0vWPELzOfMfloBrJefBzlarhsw== + dependencies: + tslib "^2.5.0" + +"@aws-sdk/util-user-agent-browser@3.609.0": + version "3.609.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.609.0.tgz" + integrity sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/types" "^3.3.0" + bowser "^2.11.0" + tslib "^2.6.2" + +"@aws-sdk/util-user-agent-node@3.614.0": + version "3.614.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.614.0.tgz" + integrity sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/xml-builder@3.609.0": + version "3.609.0" + resolved "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.609.0.tgz" + integrity sha512-l9XxNcA4HX98rwCC2/KoiWcmEiRfZe4G+mYwDbCFT87JIMj6GBhLDkAzr/W8KAaA2IDr8Vc6J8fZPgVulxxfMA== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.18.6": version "7.22.13" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz" integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== dependencies: "@babel/highlight" "^7.22.13" chalk "^2.4.2" -"@babel/compat-data@^7.13.8": - version "7.13.8" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.13.8.tgz" - integrity sha512-EaI33z19T4qN3xLXsGf48M2cDqa6ei9tPZlfLdb2HC+e/cFtREiRd8hdSqDbwdLB0/+gLwqJmCYASH0z2bUdog== +"@babel/code-frame@^7.22.13": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz" + integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== + dependencies: + "@babel/highlight" "^7.24.7" + picocolors "^1.0.0" + +"@babel/compat-data@^7.19.1": + version "7.19.1" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.1.tgz" + integrity sha512-72a9ghR0gnESIa7jBN53U32FOVCEoztyIlKaNoU05zRhEecduGK9L9c3ww7Mp06JiR+0ls0GBPFJQwwtjn9ksg== "@babel/core@^7.7.5": - version "7.13.10" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.13.10.tgz" - integrity sha512-bfIYcT0BdKeAZrovpMqX2Mx5NrgAckGbwT982AkdS5GNfn3KMGiprlBAtmBcFZRUmpaufS6WZFP8trvx8ptFDw== - dependencies: - "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.13.9" - "@babel/helper-compilation-targets" "^7.13.10" - "@babel/helper-module-transforms" "^7.13.0" - "@babel/helpers" "^7.13.10" - "@babel/parser" "^7.13.10" - "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.0" + version "7.19.1" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.19.1.tgz" + integrity sha512-1H8VgqXme4UXCRv7/Wa1bq7RVymKOzC7znjyFM8KiEzwFqcKUKYNoQef4GhdklgNvoBXyW4gYhuBNCM5o1zImw== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.19.0" + "@babel/helper-compilation-targets" "^7.19.1" + "@babel/helper-module-transforms" "^7.19.0" + "@babel/helpers" "^7.19.0" + "@babel/parser" "^7.19.1" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.1" + "@babel/types" "^7.19.0" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.1.2" - lodash "^4.17.19" + json5 "^2.2.1" semver "^6.3.0" - source-map "^0.5.0" - -"@babel/generator@^7.13.9": - version "7.13.9" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz" - integrity sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw== - dependencies: - "@babel/types" "^7.13.0" - jsesc "^2.5.1" - source-map "^0.5.0" -"@babel/generator@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.3.tgz#86e6e83d95903fbe7613f448613b8b319f330a8e" - integrity "sha1-huboPZWQP752E/RIYTuLMZ8zCo4= sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==" +"@babel/generator@^7.19.0": + version "7.19.0" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.19.0.tgz" + integrity sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg== dependencies: - "@babel/types" "^7.23.3" + "@babel/types" "^7.19.0" "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-compilation-targets@^7.13.10": - version "7.13.10" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.10.tgz" - integrity sha512-/Xju7Qg1GQO4mHZ/Kcs6Au7gfafgZnwm+a7sy/ow/tV1sHeraRUHbjdat8/UvDor4Tez+siGKDk6zIKtCPKVJA== +"@babel/helper-compilation-targets@^7.19.1": + version "7.19.1" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.1.tgz" + integrity sha512-LlLkkqhCMyz2lkQPvJNdIYU7O5YjWRgC2R4omjCTpZd8u8KMQzZvX4qce+/BluN1rcQiV7BoGUpmQ0LeHerbhg== dependencies: - "@babel/compat-data" "^7.13.8" - "@babel/helper-validator-option" "^7.12.17" - browserslist "^4.14.5" + "@babel/compat-data" "^7.19.1" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.21.3" semver "^6.3.0" -"@babel/helper-environment-visitor@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" - integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== - -"@babel/helper-function-name@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" - integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== - dependencies: - "@babel/template" "^7.22.15" - "@babel/types" "^7.23.0" - -"@babel/helper-hoist-variables@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" - integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-member-expression-to-functions@^7.13.0": - version "7.13.0" - resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.0.tgz" - integrity sha512-yvRf8Ivk62JwisqV1rFRMxiSMDGnN6KH1/mDMmIrij4jztpQNRoHqqMG3U6apYbGRPJpgPalhva9Yd06HlUxJQ== - dependencies: - "@babel/types" "^7.13.0" - -"@babel/helper-module-imports@^7.12.13": - version "7.12.13" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz" - integrity sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g== - dependencies: - "@babel/types" "^7.12.13" - -"@babel/helper-module-transforms@^7.13.0": - version "7.13.0" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.13.0.tgz" - integrity sha512-Ls8/VBwH577+pw7Ku1QkUWIyRRNHpYlts7+qSqBBFCW3I8QteB9DxfcZ5YJpOwH6Ihe/wn8ch7fMGOP1OhEIvw== - dependencies: - "@babel/helper-module-imports" "^7.12.13" - "@babel/helper-replace-supers" "^7.13.0" - "@babel/helper-simple-access" "^7.12.13" - "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/helper-validator-identifier" "^7.12.11" - "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.0" - lodash "^4.17.19" - -"@babel/helper-optimise-call-expression@^7.12.13": - version "7.12.13" - resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz" - integrity sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA== - dependencies: - "@babel/types" "^7.12.13" - -"@babel/helper-replace-supers@^7.13.0": - version "7.13.0" - resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.0.tgz" - integrity sha512-Segd5me1+Pz+rmN/NFBOplMbZG3SqRJOBlY+mA0SxAv6rjj7zJqr1AVr3SfzUVTLCv7ZLU5FycOM/SBGuLPbZw== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.13.0" - "@babel/helper-optimise-call-expression" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.0" - -"@babel/helper-simple-access@^7.12.13": - version "7.12.13" - resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz" - integrity sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA== - dependencies: - "@babel/types" "^7.12.13" - -"@babel/helper-split-export-declaration@^7.12.13": - version "7.12.13" - resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz" - integrity sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg== - dependencies: - "@babel/types" "^7.12.13" - -"@babel/helper-split-export-declaration@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" - integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-string-parser@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" - integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== - -"@babel/helper-validator-identifier@^7.12.11": - version "7.12.11" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz" - integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== - -"@babel/helper-validator-identifier@^7.22.20": +"@babel/helper-environment-visitor@^7.18.9": + version "7.18.9" + resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz" + integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== + +"@babel/helper-function-name@^7.19.0": + version "7.19.0" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz" + integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== + dependencies: + "@babel/template" "^7.18.10" + "@babel/types" "^7.19.0" + +"@babel/helper-hoist-variables@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-module-imports@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz" + integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-module-transforms@^7.19.0": + version "7.19.0" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz" + integrity sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.18.6" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.0" + "@babel/types" "^7.19.0" + +"@babel/helper-simple-access@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz" + integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-string-parser@^7.18.10": + version "7.18.10" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz" + integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== + +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.22.20": version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz" integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== -"@babel/helper-validator-option@^7.12.17": - version "7.12.17" - resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz" - integrity sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw== +"@babel/helper-validator-identifier@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz" + integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== -"@babel/helpers@^7.13.10": - version "7.13.10" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.13.10.tgz" - integrity sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ== - dependencies: - "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.0" +"@babel/helper-validator-option@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz" + integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== -"@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13": - version "7.13.10" - resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz" - integrity sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg== +"@babel/helpers@^7.19.0": + version "7.19.0" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.0.tgz" + integrity sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg== dependencies: - "@babel/helper-validator-identifier" "^7.12.11" - chalk "^2.0.0" - js-tokens "^4.0.0" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.0" + "@babel/types" "^7.19.0" "@babel/highlight@^7.22.13": version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz" integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== dependencies: "@babel/helper-validator-identifier" "^7.22.20" chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.12.13", "@babel/parser@^7.13.10": - version "7.13.10" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.13.10.tgz" - integrity sha512-0s7Mlrw9uTWkYua7xWr99Wpk2bnGa0ANleKfksYAES8LpWH4gW1OUr42vqKNf0us5UQNfru2wPqMqRITzq/SIQ== +"@babel/highlight@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz" + integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw== + dependencies: + "@babel/helper-validator-identifier" "^7.24.7" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" -"@babel/parser@^7.22.15", "@babel/parser@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.3.tgz#0ce0be31a4ca4f1884b5786057cadcb6c3be58f9" - integrity "sha1-DOC+MaTKTxiEtXhgV8rctsO+WPk= sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==" +"@babel/parser@^7.18.10", "@babel/parser@^7.19.1": + version "7.19.1" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.19.1.tgz" + integrity sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A== "@babel/runtime@^7.6.0": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.6.tgz#c05e610dc228855dc92ef1b53d07389ed8ab521d" - integrity sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ== + version "7.25.4" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz" + integrity sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w== dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.12.13": - version "7.12.13" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz" - integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== +"@babel/template@^7.18.10": + version "7.18.10" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz" + integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.18.10" + "@babel/types" "^7.18.10" + +"@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1": + version "7.19.1" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.1.tgz" + integrity sha512-0j/ZfZMxKukDaag2PtOPDbwuELqIar6lLskVPPJDjXMXjfLb1Obo/1yjxIGqqAJrmfaTIY3z2wFLAQ7qSkLsuA== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.19.0" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.19.1" + "@babel/types" "^7.19.0" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.19.0": + version "7.19.0" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz" + integrity sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA== dependencies: - "@babel/code-frame" "^7.12.13" - "@babel/parser" "^7.12.13" - "@babel/types" "^7.12.13" + "@babel/helper-string-parser" "^7.18.10" + "@babel/helper-validator-identifier" "^7.18.6" + to-fast-properties "^2.0.0" + +"@commitlint/cli@^17.1.2": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/cli/-/cli-17.8.1.tgz" + integrity sha512-ay+WbzQesE0Rv4EQKfNbSMiJJ12KdKTDzIt0tcK4k11FdsWmtwP0Kp1NWMOUswfIWo6Eb7p7Ln721Nx9FLNBjg== + dependencies: + "@commitlint/format" "^17.8.1" + "@commitlint/lint" "^17.8.1" + "@commitlint/load" "^17.8.1" + "@commitlint/read" "^17.8.1" + "@commitlint/types" "^17.8.1" + execa "^5.0.0" + lodash.isfunction "^3.0.9" + resolve-from "5.0.0" + resolve-global "1.0.0" + yargs "^17.0.0" + +"@commitlint/config-conventional@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.8.1.tgz" + integrity sha512-NxCOHx1kgneig3VLauWJcDWS40DVjg7nKOpBEEK9E5fjJpQqLCilcnKkIIjdBH98kEO1q3NpE5NSrZ2kl/QGJg== + dependencies: + conventional-changelog-conventionalcommits "^6.1.0" + +"@commitlint/config-validator@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.8.1.tgz" + integrity sha512-UUgUC+sNiiMwkyiuIFR7JG2cfd9t/7MV8VB4TZ+q02ZFkHoduUS4tJGsCBWvBOGD9Btev6IecPMvlWUfJorkEA== + dependencies: + "@commitlint/types" "^17.8.1" + ajv "^8.11.0" + +"@commitlint/ensure@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/ensure/-/ensure-17.8.1.tgz" + integrity sha512-xjafwKxid8s1K23NFpL8JNo6JnY/ysetKo8kegVM7c8vs+kWLP8VrQq+NbhgVlmCojhEDbzQKp4eRXSjVOGsow== + dependencies: + "@commitlint/types" "^17.8.1" + lodash.camelcase "^4.3.0" + lodash.kebabcase "^4.1.1" + lodash.snakecase "^4.1.1" + lodash.startcase "^4.4.0" + lodash.upperfirst "^4.3.1" + +"@commitlint/execute-rule@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.8.1.tgz" + integrity sha512-JHVupQeSdNI6xzA9SqMF+p/JjrHTcrJdI02PwesQIDCIGUrv04hicJgCcws5nzaoZbROapPs0s6zeVHoxpMwFQ== + +"@commitlint/format@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/format/-/format-17.8.1.tgz" + integrity sha512-f3oMTyZ84M9ht7fb93wbCKmWxO5/kKSbwuYvS867duVomoOsgrgljkGGIztmT/srZnaiGbaK8+Wf8Ik2tSr5eg== + dependencies: + "@commitlint/types" "^17.8.1" + chalk "^4.1.0" + +"@commitlint/is-ignored@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.8.1.tgz" + integrity sha512-UshMi4Ltb4ZlNn4F7WtSEugFDZmctzFpmbqvpyxD3la510J+PLcnyhf9chs7EryaRFJMdAKwsEKfNK0jL/QM4g== + dependencies: + "@commitlint/types" "^17.8.1" + semver "7.5.4" + +"@commitlint/lint@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/lint/-/lint-17.8.1.tgz" + integrity sha512-aQUlwIR1/VMv2D4GXSk7PfL5hIaFSfy6hSHV94O8Y27T5q+DlDEgd/cZ4KmVI+MWKzFfCTiTuWqjfRSfdRllCA== + dependencies: + "@commitlint/is-ignored" "^17.8.1" + "@commitlint/parse" "^17.8.1" + "@commitlint/rules" "^17.8.1" + "@commitlint/types" "^17.8.1" + +"@commitlint/load@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/load/-/load-17.8.1.tgz" + integrity sha512-iF4CL7KDFstP1kpVUkT8K2Wl17h2yx9VaR1ztTc8vzByWWcbO/WaKwxsnCOqow9tVAlzPfo1ywk9m2oJ9ucMqA== + dependencies: + "@commitlint/config-validator" "^17.8.1" + "@commitlint/execute-rule" "^17.8.1" + "@commitlint/resolve-extends" "^17.8.1" + "@commitlint/types" "^17.8.1" + "@types/node" "20.5.1" + chalk "^4.1.0" + cosmiconfig "^8.0.0" + cosmiconfig-typescript-loader "^4.0.0" + lodash.isplainobject "^4.0.6" + lodash.merge "^4.6.2" + lodash.uniq "^4.5.0" + resolve-from "^5.0.0" + ts-node "^10.8.1" + typescript "^4.6.4 || ^5.2.2" + +"@commitlint/message@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/message/-/message-17.8.1.tgz" + integrity sha512-6bYL1GUQsD6bLhTH3QQty8pVFoETfFQlMn2Nzmz3AOLqRVfNNtXBaSY0dhZ0dM6A2MEq4+2d7L/2LP8TjqGRkA== + +"@commitlint/parse@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/parse/-/parse-17.8.1.tgz" + integrity sha512-/wLUickTo0rNpQgWwLPavTm7WbwkZoBy3X8PpkUmlSmQJyWQTj0m6bDjiykMaDt41qcUbfeFfaCvXfiR4EGnfw== + dependencies: + "@commitlint/types" "^17.8.1" + conventional-changelog-angular "^6.0.0" + conventional-commits-parser "^4.0.0" + +"@commitlint/read@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/read/-/read-17.8.1.tgz" + integrity sha512-Fd55Oaz9irzBESPCdMd8vWWgxsW3OWR99wOntBDHgf9h7Y6OOHjWEdS9Xzen1GFndqgyoaFplQS5y7KZe0kO2w== + dependencies: + "@commitlint/top-level" "^17.8.1" + "@commitlint/types" "^17.8.1" + fs-extra "^11.0.0" + git-raw-commits "^2.0.11" + minimist "^1.2.6" -"@babel/template@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" - integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== +"@commitlint/resolve-extends@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.8.1.tgz" + integrity sha512-W/ryRoQ0TSVXqJrx5SGkaYuAaE/BUontL1j1HsKckvM6e5ZaG0M9126zcwL6peKSuIetJi7E87PRQF8O86EW0Q== dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/parser" "^7.22.15" - "@babel/types" "^7.22.15" + "@commitlint/config-validator" "^17.8.1" + "@commitlint/types" "^17.8.1" + import-fresh "^3.0.0" + lodash.mergewith "^4.6.2" + resolve-from "^5.0.0" + resolve-global "^1.0.0" -"@babel/traverse@^7.13.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.3.tgz#26ee5f252e725aa7aca3474aa5b324eaf7908b5b" - integrity "sha1-Ju5fJS5yWqeso0dKpbMk6veQi1s= sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==" +"@commitlint/rules@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/rules/-/rules-17.8.1.tgz" + integrity sha512-2b7OdVbN7MTAt9U0vKOYKCDsOvESVXxQmrvuVUZ0rGFMCrCPJWWP1GJ7f0lAypbDAhaGb8zqtdOr47192LBrIA== dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/generator" "^7.23.3" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.23.3" - "@babel/types" "^7.23.3" - debug "^4.1.0" - globals "^11.1.0" + "@commitlint/ensure" "^17.8.1" + "@commitlint/message" "^17.8.1" + "@commitlint/to-lines" "^17.8.1" + "@commitlint/types" "^17.8.1" + execa "^5.0.0" + +"@commitlint/to-lines@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-17.8.1.tgz" + integrity sha512-LE0jb8CuR/mj6xJyrIk8VLz03OEzXFgLdivBytoooKO5xLt5yalc8Ma5guTWobw998sbR3ogDd+2jed03CFmJA== -"@babel/types@^7.12.13", "@babel/types@^7.13.0": - version "7.13.0" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.13.0.tgz" - integrity sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA== +"@commitlint/top-level@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/top-level/-/top-level-17.8.1.tgz" + integrity sha512-l6+Z6rrNf5p333SHfEte6r+WkOxGlWK4bLuZKbtf/2TXRN+qhrvn1XE63VhD8Oe9oIHQ7F7W1nG2k/TJFhx2yA== dependencies: - "@babel/helper-validator-identifier" "^7.12.11" - lodash "^4.17.19" - to-fast-properties "^2.0.0" + find-up "^5.0.0" -"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.3.tgz#d5ea892c07f2ec371ac704420f4dcdb07b5f9598" - integrity "sha1-1eqJLAfy7DcaxwRCD03NsHtflZg= sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==" +"@commitlint/types@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/types/-/types-17.8.1.tgz" + integrity sha512-PXDQXkAmiMEG162Bqdh9ChML/GJZo6vU+7F03ALKDK8zYc6SuAr47LjG7hGYRqUOz+WK0dU7bQ0xzuqFMdxzeQ== dependencies: - "@babel/helper-string-parser" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.20" - to-fast-properties "^2.0.0" + chalk "^4.1.0" -"@cspotcode/source-map-consumer@0.8.0": - version "0.8.0" - resolved "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz" - integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" -"@cspotcode/source-map-support@0.7.0": - version "0.7.0" - resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz" - integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== +"@es-joy/jsdoccomment@~0.41.0": + version "0.41.0" + resolved "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.41.0.tgz" + integrity sha512-aKUhyn1QI5Ksbqcr3fFJj16p99QdjUxXAEuFst1Z47DRyoiMwivIH9MV/ARcJOCXVjPfjITciej8ZD2O/6qUmw== dependencies: - "@cspotcode/source-map-consumer" "0.8.0" + comment-parser "1.4.1" + esquery "^1.5.0" + jsdoc-type-pratt-parser "~4.0.0" -"@eslint/eslintrc@^0.4.0": - version "0.4.0" - resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz" - integrity sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog== +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": + version "4.10.0" + resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz" + integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== dependencies: ajv "^6.12.4" - debug "^4.1.1" - espree "^7.3.0" - globals "^12.1.0" - ignore "^4.0.6" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" import-fresh "^3.2.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" + js-yaml "^4.1.0" + minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@eslint/js@8.57.0": + version "8.57.0" + resolved "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz" + integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== + "@fast-csv/format@4.3.5": version "4.3.5" - resolved "https://registry.yarnpkg.com/@fast-csv/format/-/format-4.3.5.tgz#90d83d1b47b6aaf67be70d6118f84f3e12ee1ff3" + resolved "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz" integrity sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A== dependencies: "@types/node" "^14.0.1" @@ -381,7 +1111,7 @@ "@fast-csv/parse@4.3.6": version "4.3.6" - resolved "https://registry.yarnpkg.com/@fast-csv/parse/-/parse-4.3.6.tgz#ee47d0640ca0291034c7aa94039a744cfb019264" + resolved "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz" integrity sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA== dependencies: "@types/node" "^14.0.1" @@ -392,41 +1122,65 @@ lodash.isundefined "^3.0.1" lodash.uniq "^4.5.0" -"@gitbeaker/core@^35.8.0": - version "35.8.0" - resolved "https://registry.yarnpkg.com/@gitbeaker/core/-/core-35.8.0.tgz#8e55950dd6c45e6b48791432a1fa2c13b9460d39" - integrity sha512-l/LgTmPFeUBnqyxU/VbFmqKsanCITBBMp7A0yXVbiTQCvNWSV6JJyUL3ILR3q825RRU/AzRm40FFli0AgBpXTw== +"@fastify/busboy@^2.0.0": + version "2.1.1" + resolved "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz" + integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== + +"@gitbeaker/core@^35.8.1": + version "35.8.1" + resolved "https://registry.npmjs.org/@gitbeaker/core/-/core-35.8.1.tgz" + integrity sha512-KBrDykVKSmU9Q9Gly8KeHOgdc0lZSa435srECxuO0FGqqBcUQ82hPqUc13YFkkdOI9T1JRA3qSFajg8ds0mZKA== dependencies: - "@gitbeaker/requester-utils" "^35.8.0" + "@gitbeaker/requester-utils" "^35.8.1" form-data "^4.0.0" li "^1.3.0" mime "^3.0.0" query-string "^7.0.0" xcase "^2.0.1" -"@gitbeaker/node@^35.8.0": - version "35.8.0" - resolved "https://registry.yarnpkg.com/@gitbeaker/node/-/node-35.8.0.tgz#cd6d175ffa119ed323251d6e88c7441a18930b07" - integrity sha512-n8xbGemNs3aZb7gaYsEya0FKxemjyAJ4UyaF2MWM6mrj5rCnL3Y9Siko2rT/AuSJwjx82Z7BdKxV9QH/ihqjOQ== +"@gitbeaker/node@^35.8.1": + version "35.8.1" + resolved "https://registry.npmjs.org/@gitbeaker/node/-/node-35.8.1.tgz" + integrity sha512-g6rX853y61qNhzq9cWtxIEoe2KDeFBtXAeWMGWJnc3nz3WRump2pIICvJqw/yobLZqmTNt+ea6w3/n92Mnbn3g== dependencies: - "@gitbeaker/core" "^35.8.0" - "@gitbeaker/requester-utils" "^35.8.0" + "@gitbeaker/core" "^35.8.1" + "@gitbeaker/requester-utils" "^35.8.1" delay "^5.0.0" got "^11.8.3" xcase "^2.0.1" -"@gitbeaker/requester-utils@^35.8.0": - version "35.8.0" - resolved "https://registry.yarnpkg.com/@gitbeaker/requester-utils/-/requester-utils-35.8.0.tgz#e4894e2c67e2ae00e5aa5c869a0d87ec190b63d9" - integrity sha512-d/cseQQUvj1V02jXo6HBpuMarf6e6GdrxEaiWrjAiS2nDEQFRGxDGtPHzqgU84aN11nEBFnFa0vaSMqcZG/+9w== +"@gitbeaker/requester-utils@^35.8.1": + version "35.8.1" + resolved "https://registry.npmjs.org/@gitbeaker/requester-utils/-/requester-utils-35.8.1.tgz" + integrity sha512-MFzdH+Z6eJaCZA5ruWsyvm6SXRyrQHjYVR6aY8POFraIy7ceIHOprWCs1R+0ydDZ8KtBnd8OTHjlJ0sLtSFJCg== dependencies: form-data "^4.0.0" qs "^6.10.1" xcase "^2.0.1" +"@humanwhocodes/config-array@^0.11.14": + version "0.11.14" + resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz" + integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== + dependencies: + "@humanwhocodes/object-schema" "^2.0.2" + debug "^4.3.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.2": + version "2.0.3" + resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz" + integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== + "@inquirer/checkbox@^2.4.7": version "2.4.7" - resolved "https://registry.yarnpkg.com/@inquirer/checkbox/-/checkbox-2.4.7.tgz#0a2867a3a8c5853c79e43e99634e80c1721934ca" + resolved "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-2.4.7.tgz" integrity sha512-5YwCySyV1UEgqzz34gNsC38eKxRBtlRDpJLlKcRtTjlYA/yDKuc1rfw+hjw+2WJxbAZtaDPsRl5Zk7J14SBoBw== dependencies: "@inquirer/core" "^9.0.10" @@ -437,15 +1191,23 @@ "@inquirer/confirm@^3.1.22": version "3.1.22" - resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-3.1.22.tgz#23990624c11f60c6f7a5b0558c7505c35076a037" + resolved "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.1.22.tgz" integrity sha512-gsAKIOWBm2Q87CDfs9fEo7wJT3fwWIJfnDGMn9Qy74gBnNFOACDNfhUzovubbJjWnKLGBln7/NcSmZwj5DuEXg== dependencies: "@inquirer/core" "^9.0.10" "@inquirer/type" "^1.5.2" +"@inquirer/confirm@^3.2.0": + version "3.2.0" + resolved "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.2.0.tgz" + integrity sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw== + dependencies: + "@inquirer/core" "^9.1.0" + "@inquirer/type" "^1.5.3" + "@inquirer/core@^9.0.10": version "9.0.10" - resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-9.0.10.tgz#4270191e2ad3bea6223530a093dd9479bcbc7dd0" + resolved "https://registry.npmjs.org/@inquirer/core/-/core-9.0.10.tgz" integrity sha512-TdESOKSVwf6+YWDz8GhS6nKscwzkIyakEzCLJ5Vh6O3Co2ClhCJ0A4MG909MUWfaWdpJm7DE45ii51/2Kat9tA== dependencies: "@inquirer/figures" "^1.0.5" @@ -462,9 +1224,47 @@ wrap-ansi "^6.2.0" yoctocolors-cjs "^2.1.2" +"@inquirer/core@^9.0.2": + version "9.0.2" + resolved "https://registry.npmjs.org/@inquirer/core/-/core-9.0.2.tgz" + integrity sha512-nguvH3TZar3ACwbytZrraRTzGqyxJfYJwv+ZwqZNatAosdWQMP1GV8zvmkNlBe2JeZSaw0WYBHZk52pDpWC9qA== + dependencies: + "@inquirer/figures" "^1.0.3" + "@inquirer/type" "^1.4.0" + "@types/mute-stream" "^0.0.4" + "@types/node" "^20.14.9" + "@types/wrap-ansi" "^3.0.0" + ansi-escapes "^4.3.2" + cli-spinners "^2.9.2" + cli-width "^4.1.0" + mute-stream "^1.0.0" + signal-exit "^4.1.0" + strip-ansi "^6.0.1" + wrap-ansi "^6.2.0" + yoctocolors-cjs "^2.1.2" + +"@inquirer/core@^9.1.0": + version "9.1.0" + resolved "https://registry.npmjs.org/@inquirer/core/-/core-9.1.0.tgz" + integrity sha512-RZVfH//2ytTjmaBIzeKT1zefcQZzuruwkpTwwbe/i2jTl4o9M+iML5ChULzz6iw1Ok8iUBBsRCjY2IEbD8Ft4w== + dependencies: + "@inquirer/figures" "^1.0.5" + "@inquirer/type" "^1.5.3" + "@types/mute-stream" "^0.0.4" + "@types/node" "^22.5.2" + "@types/wrap-ansi" "^3.0.0" + ansi-escapes "^4.3.2" + cli-spinners "^2.9.2" + cli-width "^4.1.0" + mute-stream "^1.0.0" + signal-exit "^4.1.0" + strip-ansi "^6.0.1" + wrap-ansi "^6.2.0" + yoctocolors-cjs "^2.1.2" + "@inquirer/editor@^2.1.22": version "2.1.22" - resolved "https://registry.yarnpkg.com/@inquirer/editor/-/editor-2.1.22.tgz#f97eda20954da1dab47df9f4c3ae11604d56360c" + resolved "https://registry.npmjs.org/@inquirer/editor/-/editor-2.1.22.tgz" integrity sha512-K1QwTu7GCK+nKOVRBp5HY9jt3DXOfPGPr6WRDrPImkcJRelG9UTx2cAtK1liXmibRrzJlTWOwqgWT3k2XnS62w== dependencies: "@inquirer/core" "^9.0.10" @@ -473,21 +1273,34 @@ "@inquirer/expand@^2.1.22": version "2.1.22" - resolved "https://registry.yarnpkg.com/@inquirer/expand/-/expand-2.1.22.tgz#7593e93a516a49434629c41f3738479c8234d2df" + resolved "https://registry.npmjs.org/@inquirer/expand/-/expand-2.1.22.tgz" integrity sha512-wTZOBkzH+ItPuZ3ZPa9lynBsdMp6kQ9zbjVPYEtSBG7UulGjg2kQiAnUjgyG4SlntpTce5bOmXAPvE4sguXjpA== dependencies: "@inquirer/core" "^9.0.10" "@inquirer/type" "^1.5.2" yoctocolors-cjs "^2.1.2" +"@inquirer/figures@^1.0.3": + version "1.0.3" + resolved "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.3.tgz" + integrity sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw== + "@inquirer/figures@^1.0.5": version "1.0.5" - resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.5.tgz#57f9a996d64d3e3345d2a3ca04d36912e94f8790" + resolved "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.5.tgz" integrity sha512-79hP/VWdZ2UVc9bFGJnoQ/lQMpL74mGgzSYX1xUqCVk7/v73vJCMw1VuyWN1jGkZ9B3z7THAbySqGbCNefcjfA== +"@inquirer/input@^2.2.4": + version "2.3.0" + resolved "https://registry.npmjs.org/@inquirer/input/-/input-2.3.0.tgz" + integrity sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw== + dependencies: + "@inquirer/core" "^9.1.0" + "@inquirer/type" "^1.5.3" + "@inquirer/input@^2.2.9": version "2.2.9" - resolved "https://registry.yarnpkg.com/@inquirer/input/-/input-2.2.9.tgz#08fdf9a48e4f6fc64c2d508b9d10afac843f9bd8" + resolved "https://registry.npmjs.org/@inquirer/input/-/input-2.2.9.tgz" integrity sha512-7Z6N+uzkWM7+xsE+3rJdhdG/+mQgejOVqspoW+w0AbSZnL6nq5tGMEVASaYVWbkoSzecABWwmludO2evU3d31g== dependencies: "@inquirer/core" "^9.0.10" @@ -495,7 +1308,7 @@ "@inquirer/number@^1.0.10": version "1.0.10" - resolved "https://registry.yarnpkg.com/@inquirer/number/-/number-1.0.10.tgz#ac2b440ca57b5de5a231e4898c12d4453683c055" + resolved "https://registry.npmjs.org/@inquirer/number/-/number-1.0.10.tgz" integrity sha512-kWTxRF8zHjQOn2TJs+XttLioBih6bdc5CcosXIzZsrTY383PXI35DuhIllZKu7CdXFi2rz2BWPN9l0dPsvrQOA== dependencies: "@inquirer/core" "^9.0.10" @@ -503,7 +1316,7 @@ "@inquirer/password@^2.1.22": version "2.1.22" - resolved "https://registry.yarnpkg.com/@inquirer/password/-/password-2.1.22.tgz#ec7ee5709923cf285b3e0ae53eed4fdc3c05b422" + resolved "https://registry.npmjs.org/@inquirer/password/-/password-2.1.22.tgz" integrity sha512-5Fxt1L9vh3rAKqjYwqsjU4DZsEvY/2Gll+QkqR4yEpy6wvzLxdSgFhUcxfDAOtO4BEoTreWoznC0phagwLU5Kw== dependencies: "@inquirer/core" "^9.0.10" @@ -512,7 +1325,7 @@ "@inquirer/prompts@^5.3.8": version "5.3.8" - resolved "https://registry.yarnpkg.com/@inquirer/prompts/-/prompts-5.3.8.tgz#f394050d95076c2f1b046be324f06f619b257c3e" + resolved "https://registry.npmjs.org/@inquirer/prompts/-/prompts-5.3.8.tgz" integrity sha512-b2BudQY/Si4Y2a0PdZZL6BeJtl8llgeZa7U2j47aaJSCeAl1e4UI7y8a9bSkO3o/ZbZrgT5muy/34JbsjfIWxA== dependencies: "@inquirer/checkbox" "^2.4.7" @@ -528,7 +1341,7 @@ "@inquirer/rawlist@^2.2.4": version "2.2.4" - resolved "https://registry.yarnpkg.com/@inquirer/rawlist/-/rawlist-2.2.4.tgz#73d5d4fafa2ca012e6cfb9eb1d8ddf33bab2dde0" + resolved "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-2.2.4.tgz" integrity sha512-pb6w9pWrm7EfnYDgQObOurh2d2YH07+eDo3xQBsNAM2GRhliz6wFXGi1thKQ4bN6B0xDd6C3tBsjdr3obsCl3Q== dependencies: "@inquirer/core" "^9.0.10" @@ -537,7 +1350,7 @@ "@inquirer/search@^1.0.7": version "1.0.7" - resolved "https://registry.yarnpkg.com/@inquirer/search/-/search-1.0.7.tgz#72ab9ccfb57f05dd81a8b2df26214588e048be18" + resolved "https://registry.npmjs.org/@inquirer/search/-/search-1.0.7.tgz" integrity sha512-p1wpV+3gd1eST/o5N3yQpYEdFNCzSP0Klrl+5bfD3cTTz8BGG6nf4Z07aBW0xjlKIj1Rp0y3x/X4cZYi6TfcLw== dependencies: "@inquirer/core" "^9.0.10" @@ -545,9 +1358,20 @@ "@inquirer/type" "^1.5.2" yoctocolors-cjs "^2.1.2" +"@inquirer/select@^2.3.10": + version "2.3.10" + resolved "https://registry.npmjs.org/@inquirer/select/-/select-2.3.10.tgz" + integrity sha512-rr7iR0Zj1YFfgM8IUGimPD9Yukd+n/U63CnYT9kdum6DbRXtMxR45rrreP+EA9ixCnShr+W4xj7suRxC1+8t9g== + dependencies: + "@inquirer/core" "^9.0.2" + "@inquirer/figures" "^1.0.3" + "@inquirer/type" "^1.4.0" + ansi-escapes "^4.3.2" + yoctocolors-cjs "^2.1.2" + "@inquirer/select@^2.4.7": version "2.4.7" - resolved "https://registry.yarnpkg.com/@inquirer/select/-/select-2.4.7.tgz#6a23742b4f76d228186dfd42571d973def378ffa" + resolved "https://registry.npmjs.org/@inquirer/select/-/select-2.4.7.tgz" integrity sha512-JH7XqPEkBpNWp3gPCqWqY8ECbyMoFcCZANlL6pV9hf59qK6dGmkOlx1ydyhY+KZ0c5X74+W6Mtp+nm2QX0/MAQ== dependencies: "@inquirer/core" "^9.0.10" @@ -556,16 +1380,30 @@ ansi-escapes "^4.3.2" yoctocolors-cjs "^2.1.2" +"@inquirer/type@^1.4.0": + version "1.4.0" + resolved "https://registry.npmjs.org/@inquirer/type/-/type-1.4.0.tgz" + integrity sha512-AjOqykVyjdJQvtfkNDGUyMYGF8xN50VUxftCQWsOyIo4DFRLr6VQhW0VItGI1JIyQGCGgIpKa7hMMwNhZb4OIw== + dependencies: + mute-stream "^1.0.0" + "@inquirer/type@^1.5.2": version "1.5.2" - resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-1.5.2.tgz#15f5e4a4dae02c4203650cb07c8a000cdd423939" + resolved "https://registry.npmjs.org/@inquirer/type/-/type-1.5.2.tgz" integrity sha512-w9qFkumYDCNyDZmNQjf/n6qQuvQ4dMC3BJesY4oF+yr0CxR5vxujflAVeIcS6U336uzi9GM0kAfZlLrZ9UTkpA== dependencies: mute-stream "^1.0.0" +"@inquirer/type@^1.5.3": + version "1.5.3" + resolved "https://registry.npmjs.org/@inquirer/type/-/type-1.5.3.tgz" + integrity sha512-xUQ14WQGR/HK5ei+2CvgcwoH9fQ4PgPGmVFSN0pc1+fVyDL3MREhyAY7nxEErSu6CkllBM3D7e3e+kOvtu+eIg== + dependencies: + mute-stream "^1.0.0" + "@isaacs/cliui@^8.0.2": version "8.0.2" - resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz" integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== dependencies: string-width "^5.1.2" @@ -575,6 +1413,11 @@ wrap-ansi "^8.1.0" wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" +"@isaacs/string-locale-compare@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz" + integrity sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" @@ -591,317 +1434,432 @@ resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== +"@jridgewell/gen-mapping@^0.1.0": + version "0.1.1" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz" + integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/gen-mapping@^0.3.2": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" - integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + version "0.3.2" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== dependencies: "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@^3.1.0": +"@jridgewell/resolve-uri@^3.0.3": version "3.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz" integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== -"@jridgewell/set-array@^1.0.1": +"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": version "1.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": +"@jridgewell/sourcemap-codec@^1.4.10": version "1.4.15" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== -"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.20" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" - integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" -"@keyv/redis@^2.1.2": - version "2.1.2" - resolved "https://registry.npmjs.org/@keyv/redis/-/redis-2.1.2.tgz" - integrity sha512-D6vNOuyH/5cBNfHcyxzck1l7V+Qd4RAT7uz2SHYAjutbXQ03o3SSneRyvrp76H4/uvHyutPWTJ1Za3EpGSVe5g== +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.15" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz" + integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g== dependencies: - ioredis "~4.17.1" + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jsforce/jsforce-node@^3.2.0", "@jsforce/jsforce-node@^3.4.1": + version "3.4.1" + resolved "https://registry.npmjs.org/@jsforce/jsforce-node/-/jsforce-node-3.4.1.tgz" + integrity sha512-PsBKfglH0/8W/Srr4LsxEFsVmjmZjEj/T4XLGpbBoK8yVObwbiMk4VqwA6XwiA6SHqnEqqQbHZxk2rr7dZC+4A== + dependencies: + "@sindresorhus/is" "^4" + abort-controller "^3.0.0" + base64url "^3.0.1" + csv-parse "^5.5.2" + csv-stringify "^6.4.4" + faye "^1.4.0" + form-data "^4.0.0" + https-proxy-agent "^5.0.0" + multistream "^3.1.0" + node-fetch "^2.6.1" + strip-ansi "^6.0.0" + xml2js "^0.6.2" + +"@keyv/serialize@*": + version "1.0.1" + resolved "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.0.1.tgz" + integrity sha512-kKXeynfORDGPUEEl2PvTExM2zs+IldC6ZD8jPcfvI351MDNtfMlw9V9s4XZXuJNDK2qR5gbEKxRyoYx3quHUVQ== + dependencies: + buffer "^6.0.3" "@kwsites/file-exists@^1.1.1": version "1.1.1" - resolved "https://registry.yarnpkg.com/@kwsites/file-exists/-/file-exists-1.1.1.tgz#ad1efcac13e1987d8dbaf235ef3be5b0d96faa99" + resolved "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz" integrity sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw== dependencies: debug "^4.1.1" "@kwsites/promise-deferred@^1.1.1": version "1.1.1" - resolved "https://registry.yarnpkg.com/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz#8ace5259254426ccef57f3175bc64ed7095ed919" + resolved "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz" integrity sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw== -"@mrmlnc/readdir-enhanced@^2.2.1": - version "2.2.1" - resolved "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz" - integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== - dependencies: - call-me-maybe "^1.0.1" - glob-to-regexp "^0.3.0" - -"@nodelib/fs.scandir@2.1.4": - version "2.1.4" - resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz" - integrity sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA== +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: - "@nodelib/fs.stat" "2.0.4" + "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@2.0.4", "@nodelib/fs.stat@^2.0.2": - version "2.0.4" - resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz" - integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q== - -"@nodelib/fs.stat@^1.1.2": - version "1.1.3" - resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz" - integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3": - version "1.2.6" - resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz" - integrity sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow== +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== dependencies: - "@nodelib/fs.scandir" "2.1.4" + "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@oclif/command@^1", "@oclif/command@^1.5.13", "@oclif/command@^1.5.20", "@oclif/command@^1.6.0", "@oclif/command@^1.8.0": - version "1.8.0" - resolved "https://registry.npmjs.org/@oclif/command/-/command-1.8.0.tgz" - integrity sha512-5vwpq6kbvwkQwKqAoOU3L72GZ3Ta8RRrewKj9OJRolx28KLJJ8Dg9Rf7obRwt5jQA9bkYd8gqzMTrI7H3xLfaw== +"@npmcli/agent@^2.0.0": + version "2.2.2" + resolved "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz" + integrity sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og== dependencies: - "@oclif/config" "^1.15.1" - "@oclif/errors" "^1.3.3" - "@oclif/parser" "^3.8.3" - "@oclif/plugin-help" "^3" - debug "^4.1.1" - semver "^7.3.2" + agent-base "^7.1.0" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.1" + lru-cache "^10.0.1" + socks-proxy-agent "^8.0.3" -"@oclif/command@^1.8.1": - version "1.8.16" - resolved "https://registry.npmjs.org/@oclif/command/-/command-1.8.16.tgz" - integrity sha512-rmVKYEsKzurfRU0xJz+iHelbi1LGlihIWZ7Qvmb/CBz1EkhL7nOkW4SVXmG2dA5Ce0si2gr88i6q4eBOMRNJ1w== - dependencies: - "@oclif/config" "^1.18.2" - "@oclif/errors" "^1.3.5" - "@oclif/help" "^1.0.1" - "@oclif/parser" "^3.8.6" - debug "^4.1.1" - semver "^7.3.2" +"@npmcli/arborist@^7.2.0": + version "7.5.4" + resolved "https://registry.npmjs.org/@npmcli/arborist/-/arborist-7.5.4.tgz" + integrity sha512-nWtIc6QwwoUORCRNzKx4ypHqCk3drI+5aeYdMTQQiRCcn4lOOgfQh7WyZobGYTxXPSq1VwV53lkpN/BRlRk08g== + dependencies: + "@isaacs/string-locale-compare" "^1.1.0" + "@npmcli/fs" "^3.1.1" + "@npmcli/installed-package-contents" "^2.1.0" + "@npmcli/map-workspaces" "^3.0.2" + "@npmcli/metavuln-calculator" "^7.1.1" + "@npmcli/name-from-folder" "^2.0.0" + "@npmcli/node-gyp" "^3.0.0" + "@npmcli/package-json" "^5.1.0" + "@npmcli/query" "^3.1.0" + "@npmcli/redact" "^2.0.0" + "@npmcli/run-script" "^8.1.0" + bin-links "^4.0.4" + cacache "^18.0.3" + common-ancestor-path "^1.0.1" + hosted-git-info "^7.0.2" + json-parse-even-better-errors "^3.0.2" + json-stringify-nice "^1.1.4" + lru-cache "^10.2.2" + minimatch "^9.0.4" + nopt "^7.2.1" + npm-install-checks "^6.2.0" + npm-package-arg "^11.0.2" + npm-pick-manifest "^9.0.1" + npm-registry-fetch "^17.0.1" + pacote "^18.0.6" + parse-conflict-json "^3.0.0" + proc-log "^4.2.0" + proggy "^2.0.0" + promise-all-reject-late "^1.0.0" + promise-call-limit "^3.0.1" + read-package-json-fast "^3.0.2" + semver "^7.3.7" + ssri "^10.0.6" + treeverse "^3.0.0" + walk-up-path "^3.0.1" -"@oclif/config@1.18.2": - version "1.18.2" - resolved "https://registry.npmjs.org/@oclif/config/-/config-1.18.2.tgz" - integrity sha512-cE3qfHWv8hGRCP31j7fIS7BfCflm/BNZ2HNqHexH+fDrdF2f1D5S8VmXWLC77ffv3oDvWyvE9AZeR0RfmHCCaA== +"@npmcli/fs@^3.1.0", "@npmcli/fs@^3.1.1": + version "3.1.1" + resolved "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz" + integrity sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg== dependencies: - "@oclif/errors" "^1.3.3" - "@oclif/parser" "^3.8.0" - debug "^4.1.1" - globby "^11.0.1" - is-wsl "^2.1.1" - tslib "^2.0.0" + semver "^7.3.5" -"@oclif/config@^1.15.1", "@oclif/config@^1.18.3": - version "1.18.3" - resolved "https://registry.npmjs.org/@oclif/config/-/config-1.18.3.tgz" - integrity sha512-sBpko86IrTscc39EvHUhL+c++81BVTsIZ3ETu/vG+cCdi0N6vb2DoahR67A9FI2CGnxRRHjnTfa3m6LulwNATA== - dependencies: - "@oclif/errors" "^1.3.5" - "@oclif/parser" "^3.8.0" - debug "^4.1.1" - globby "^11.0.1" - is-wsl "^2.1.1" - tslib "^2.3.1" +"@npmcli/git@^5.0.0": + version "5.0.8" + resolved "https://registry.npmjs.org/@npmcli/git/-/git-5.0.8.tgz" + integrity sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ== + dependencies: + "@npmcli/promise-spawn" "^7.0.0" + ini "^4.1.3" + lru-cache "^10.0.1" + npm-pick-manifest "^9.0.0" + proc-log "^4.0.0" + promise-inflight "^1.0.1" + promise-retry "^2.0.1" + semver "^7.3.5" + which "^4.0.0" -"@oclif/config@^1.17.0": - version "1.17.0" - resolved "https://registry.npmjs.org/@oclif/config/-/config-1.17.0.tgz" - integrity sha512-Lmfuf6ubjQ4ifC/9bz1fSCHc6F6E653oyaRXxg+lgT4+bYf9bk+nqrUpAbrXyABkCqgIBiFr3J4zR/kiFdE1PA== +"@npmcli/installed-package-contents@^2.0.1", "@npmcli/installed-package-contents@^2.1.0": + version "2.1.0" + resolved "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz" + integrity sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w== dependencies: - "@oclif/errors" "^1.3.3" - "@oclif/parser" "^3.8.0" - debug "^4.1.1" - globby "^11.0.1" - is-wsl "^2.1.1" - tslib "^2.0.0" + npm-bundled "^3.0.0" + npm-normalize-package-bin "^3.0.0" -"@oclif/config@^1.18.2": - version "1.18.6" - resolved "https://registry.yarnpkg.com/@oclif/config/-/config-1.18.6.tgz#37367026b3110a2f04875509b1920a8ee4489f21" - integrity sha512-OWhCpdu4QqggOPX1YPZ4XVmLLRX+lhGjXV6RNA7sogOwLqlEmSslnN/lhR5dkhcWZbKWBQH29YCrB3LDPRu/IA== +"@npmcli/map-workspaces@^3.0.2": + version "3.0.6" + resolved "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-3.0.6.tgz" + integrity sha512-tkYs0OYnzQm6iIRdfy+LcLBjcKuQCeE5YLb8KnrIlutJfheNaPvPpgoFEyEFgbjzl5PLZ3IA/BWAwRU0eHuQDA== dependencies: - "@oclif/errors" "^1.3.6" - "@oclif/parser" "^3.8.9" - debug "^4.3.4" - globby "^11.1.0" - is-wsl "^2.1.1" - tslib "^2.3.1" - -"@oclif/dev-cli@^1.26.0": - version "1.26.0" - resolved "https://registry.npmjs.org/@oclif/dev-cli/-/dev-cli-1.26.0.tgz" - integrity sha512-272udZP+bG4qahoAcpWcMTJKiA+V42kRMqQM7n4tgW35brYb2UP5kK+p08PpF8sgSfRTV8MoJVJG9ax5kY82PA== - dependencies: - "@oclif/command" "^1.8.0" - "@oclif/config" "^1.17.0" - "@oclif/errors" "^1.3.3" - "@oclif/plugin-help" "^3.2.0" - cli-ux "^5.2.1" - debug "^4.1.1" - find-yarn-workspace-root "^2.0.0" - fs-extra "^8.1" - github-slugger "^1.2.1" - lodash "^4.17.11" - normalize-package-data "^3.0.0" - qqjs "^0.3.10" - tslib "^2.0.3" + "@npmcli/name-from-folder" "^2.0.0" + glob "^10.2.2" + minimatch "^9.0.0" + read-package-json-fast "^3.0.0" -"@oclif/errors@1.3.5", "@oclif/errors@^1.3.5": - version "1.3.5" - resolved "https://registry.npmjs.org/@oclif/errors/-/errors-1.3.5.tgz" - integrity sha512-OivucXPH/eLLlOT7FkCMoZXiaVYf8I/w1eTAM1+gKzfhALwWTusxEx7wBmW0uzvkSg/9ovWLycPaBgJbM3LOCQ== +"@npmcli/metavuln-calculator@^7.1.1": + version "7.1.1" + resolved "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-7.1.1.tgz" + integrity sha512-Nkxf96V0lAx3HCpVda7Vw4P23RILgdi/5K1fmj2tZkWIYLpXAN8k2UVVOsW16TsS5F8Ws2I7Cm+PU1/rsVF47g== dependencies: - clean-stack "^3.0.0" - fs-extra "^8.1" - indent-string "^4.0.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" + cacache "^18.0.0" + json-parse-even-better-errors "^3.0.0" + pacote "^18.0.0" + proc-log "^4.1.0" + semver "^7.3.5" + +"@npmcli/name-from-folder@^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-2.0.0.tgz" + integrity sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg== -"@oclif/errors@^1", "@oclif/errors@^1.2.1", "@oclif/errors@^1.2.2", "@oclif/errors@^1.3.3": - version "1.3.4" - resolved "https://registry.npmjs.org/@oclif/errors/-/errors-1.3.4.tgz" - integrity sha512-pJKXyEqwdfRTUdM8n5FIHiQQHg5ETM0Wlso8bF9GodczO40mF5Z3HufnYWJE7z8sGKxOeJCdbAVZbS8Y+d5GCw== +"@npmcli/node-gyp@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz" + integrity sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA== + +"@npmcli/package-json@^5.0.0", "@npmcli/package-json@^5.1.0": + version "5.2.0" + resolved "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.2.0.tgz" + integrity sha512-qe/kiqqkW0AGtvBjL8TJKZk/eBBSpnJkUWvHdQ9jM2lKHXRYYJuyNpJPlJw3c8QjC2ow6NZYiLExhUaeJelbxQ== + dependencies: + "@npmcli/git" "^5.0.0" + glob "^10.2.2" + hosted-git-info "^7.0.0" + json-parse-even-better-errors "^3.0.0" + normalize-package-data "^6.0.0" + proc-log "^4.0.0" + semver "^7.5.3" + +"@npmcli/promise-spawn@^7.0.0": + version "7.0.2" + resolved "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz" + integrity sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ== dependencies: - clean-stack "^3.0.0" - fs-extra "^8.1" - indent-string "^4.0.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" + which "^4.0.0" -"@oclif/errors@^1.3.6": - version "1.3.6" - resolved "https://registry.yarnpkg.com/@oclif/errors/-/errors-1.3.6.tgz#e8fe1fc12346cb77c4f274e26891964f5175f75d" - integrity sha512-fYaU4aDceETd89KXP+3cLyg9EHZsLD3RxF2IU9yxahhBpspWjkWi3Dy3bTgcwZ3V47BgxQaGapzJWDM33XIVDQ== +"@npmcli/query@^3.1.0": + version "3.1.0" + resolved "https://registry.npmjs.org/@npmcli/query/-/query-3.1.0.tgz" + integrity sha512-C/iR0tk7KSKGldibYIB9x8GtO/0Bd0I2mhOaDb8ucQL/bQVTmGoeREaFj64Z5+iCBRf3dQfed0CjJL7I8iTkiQ== dependencies: - clean-stack "^3.0.0" - fs-extra "^8.1" + postcss-selector-parser "^6.0.10" + +"@npmcli/redact@^2.0.0": + version "2.0.1" + resolved "https://registry.npmjs.org/@npmcli/redact/-/redact-2.0.1.tgz" + integrity sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw== + +"@npmcli/run-script@^8.0.0", "@npmcli/run-script@^8.1.0": + version "8.1.0" + resolved "https://registry.npmjs.org/@npmcli/run-script/-/run-script-8.1.0.tgz" + integrity sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg== + dependencies: + "@npmcli/node-gyp" "^3.0.0" + "@npmcli/package-json" "^5.0.0" + "@npmcli/promise-spawn" "^7.0.0" + node-gyp "^10.0.0" + proc-log "^4.0.0" + which "^4.0.0" + +"@oclif/core@3.26.6": + version "3.26.6" + resolved "https://registry.npmjs.org/@oclif/core/-/core-3.26.6.tgz" + integrity sha512-+FiTw1IPuJTF9tSAlTsY8bGK4sgthehjz7c2SvYdgQncTkxI2xvUch/8QpjNYGLEmUneNygvYMRBax2KJcLccA== + dependencies: + "@types/cli-progress" "^3.11.5" + ansi-escapes "^4.3.2" + ansi-styles "^4.3.0" + cardinal "^2.1.1" + chalk "^4.1.2" + clean-stack "^3.0.1" + cli-progress "^3.12.0" + color "^4.2.3" + debug "^4.3.4" + ejs "^3.1.10" + get-package-type "^0.1.0" + globby "^11.1.0" + hyperlinker "^1.0.0" indent-string "^4.0.0" + is-wsl "^2.2.0" + js-yaml "^3.14.1" + minimatch "^9.0.4" + natural-orderby "^2.0.3" + object-treeify "^1.1.33" + password-prompt "^1.1.3" + slice-ansi "^4.0.0" + string-width "^4.2.3" strip-ansi "^6.0.1" + supports-color "^8.1.1" + supports-hyperlinks "^2.2.0" + widest-line "^3.1.0" + wordwrap "^1.0.0" wrap-ansi "^7.0.0" -"@oclif/help@^1.0.1": - version "1.0.1" - resolved "https://registry.npmjs.org/@oclif/help/-/help-1.0.1.tgz" - integrity sha512-8rsl4RHL5+vBUAKBL6PFI3mj58hjPCp2VYyXD4TAa7IMStikFfOH2gtWmqLzIlxAED2EpD0dfYwo9JJxYsH7Aw== +"@oclif/core@^4": + version "4.0.3" + resolved "https://registry.npmjs.org/@oclif/core/-/core-4.0.3.tgz" + integrity sha512-yM8R/M2WfSNfL9eF8clS/SbQvanvHZJgoij5bLs3ro7uAFCkL/LYcagyj0UVGbysx5AI+SNIof6OHQUXYZpOZw== dependencies: - "@oclif/config" "1.18.2" - "@oclif/errors" "1.3.5" - chalk "^4.1.2" + ansi-escapes "^4.3.2" + ansis "^3.1.1" + clean-stack "^3.0.1" + cli-spinners "^2.9.2" + cosmiconfig "^9.0.0" + debug "^4.3.5" + ejs "^3.1.10" + get-package-type "^0.1.0" + globby "^11.1.0" indent-string "^4.0.0" - lodash "^4.17.21" - string-width "^4.2.0" - strip-ansi "^6.0.0" + is-wsl "^2.2.0" + minimatch "^9.0.4" + string-width "^4.2.3" + supports-color "^8" widest-line "^3.1.0" - wrap-ansi "^6.2.0" - -"@oclif/linewrap@^1.0.0": - version "1.0.0" - resolved "https://registry.npmjs.org/@oclif/linewrap/-/linewrap-1.0.0.tgz" - integrity sha512-Ups2dShK52xXa8w6iBWLgcjPJWjais6KPJQq3gQ/88AY6BXoTX+MIGFPrWQO1KLMiQfoTpcLnUwloN4brrVUHw== - -"@oclif/parser@3.8.6", "@oclif/parser@^3.8.6": - version "3.8.6" - resolved "https://registry.npmjs.org/@oclif/parser/-/parser-3.8.6.tgz" - integrity sha512-tXb0NKgSgNxmf6baN6naK+CCwOueaFk93FG9u202U7mTBHUKsioOUlw1SG/iPi9aJM3WE4pHLXmty59pci0OEw== - dependencies: - "@oclif/errors" "^1.2.2" - "@oclif/linewrap" "^1.0.0" - chalk "^4.1.0" - tslib "^2.0.0" - -"@oclif/parser@^3.8.0", "@oclif/parser@^3.8.3": - version "3.8.5" - resolved "https://registry.npmjs.org/@oclif/parser/-/parser-3.8.5.tgz" - integrity sha512-yojzeEfmSxjjkAvMRj0KzspXlMjCfBzNRPkWw8ZwOSoNWoJn+OCS/m/S+yfV6BvAM4u2lTzX9Y5rCbrFIgkJLg== - dependencies: - "@oclif/errors" "^1.2.2" - "@oclif/linewrap" "^1.0.0" - chalk "^2.4.2" - tslib "^1.9.3" - -"@oclif/parser@^3.8.9": - version "3.8.9" - resolved "https://registry.yarnpkg.com/@oclif/parser/-/parser-3.8.9.tgz#9399041ada7e465043f34b24f4d82a8beb68a023" - integrity sha512-1j/kThdse7yHQz6+c3v8RA1I3gD6+SGt2O7IAb/MAMoxqyBrFQDabQHH2UU4eVFGMLN7U91AiYJp11zJ9LcQAg== - dependencies: - "@oclif/errors" "^1.3.6" - "@oclif/linewrap" "^1.0.0" - chalk "^4.1.0" - tslib "^2.4.1" + wordwrap "^1.0.0" + wrap-ansi "^7.0.0" -"@oclif/plugin-help@^2.2.0": - version "2.2.3" - resolved "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-2.2.3.tgz" - integrity sha512-bGHUdo5e7DjPJ0vTeRBMIrfqTRDBfyR5w0MP41u0n3r7YG5p14lvMmiCXxi6WDaP2Hw5nqx3PnkAIntCKZZN7g== +"@oclif/core@^4.0.19": + version "4.0.19" + resolved "https://registry.npmjs.org/@oclif/core/-/core-4.0.19.tgz" + integrity sha512-VXnsYNVfmucXp5BdOA/OcWi8F/h2h8ofW1GxQDdspodnmnUgALEpqrxXBl5NFuA+iEihtAJeXzX260ICHYDaBg== dependencies: - "@oclif/command" "^1.5.13" - chalk "^2.4.1" + ansi-escapes "^4.3.2" + ansis "^3.3.2" + clean-stack "^3.0.1" + cli-spinners "^2.9.2" + debug "^4.3.5" + ejs "^3.1.10" + get-package-type "^0.1.0" + globby "^11.1.0" indent-string "^4.0.0" - lodash.template "^4.4.0" - string-width "^3.0.0" - strip-ansi "^5.0.0" - widest-line "^2.0.1" - wrap-ansi "^4.0.0" + is-wsl "^2.2.0" + lilconfig "^3.1.2" + minimatch "^9.0.5" + string-width "^4.2.3" + supports-color "^8" + widest-line "^3.1.0" + wordwrap "^1.0.0" + wrap-ansi "^7.0.0" -"@oclif/plugin-help@^3", "@oclif/plugin-help@^3.2.0", "@oclif/plugin-help@^3.2.2": - version "3.2.2" - resolved "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-3.2.2.tgz" - integrity sha512-SPZ8U8PBYK0n4srFjCLedk0jWU4QlxgEYLCXIBShJgOwPhTTQknkUlsEwaMIevvCU4iCQZhfMX+D8Pz5GZjFgA== +"@oclif/core@^4.0.21": + version "4.0.21" + resolved "https://registry.npmjs.org/@oclif/core/-/core-4.0.21.tgz" + integrity sha512-SvLTSclf104IVX8BY7nWqess1pBmeNl9qRFTWjOXg7B1/ESemfEtZYBDRAXAp1ILvazDng5IF/7YSbTxDVbwNg== dependencies: - "@oclif/command" "^1.5.20" - "@oclif/config" "^1.15.1" - "@oclif/errors" "^1.2.2" - chalk "^4.1.0" + ansi-escapes "^4.3.2" + ansis "^3.3.2" + clean-stack "^3.0.1" + cli-spinners "^2.9.2" + debug "^4.3.7" + ejs "^3.1.10" + get-package-type "^0.1.0" + globby "^11.1.0" indent-string "^4.0.0" - lodash.template "^4.4.0" - string-width "^4.2.0" - strip-ansi "^6.0.0" + is-wsl "^2.2.0" + lilconfig "^3.1.2" + minimatch "^9.0.5" + string-width "^4.2.3" + supports-color "^8" widest-line "^3.1.0" - wrap-ansi "^4.0.0" - -"@oclif/screen@^1.0.3": - version "1.0.4" - resolved "https://registry.npmjs.org/@oclif/screen/-/screen-1.0.4.tgz" - integrity sha512-60CHpq+eqnTxLZQ4PGHYNwUX572hgpMHGPtTWMjdTMsAvlm69lZV/4ly6O3sAYkomo4NggGcomrDpBe34rxUqw== + wordwrap "^1.0.0" + wrap-ansi "^7.0.0" -"@oclif/test@^1", "@oclif/test@^1.2.4": - version "1.2.8" - resolved "https://registry.npmjs.org/@oclif/test/-/test-1.2.8.tgz" - integrity sha512-HCh0qPge1JCqTEw4s2ScnicEZd4Ro4/0VvdjpsfCiX6fuDV53fRZ2uqLTgxKGHrVoqOZnVrRZHyhFyEsFGs+zQ== +"@oclif/plugin-command-snapshot@^5.1.9": + version "5.1.9" + resolved "https://registry.npmjs.org/@oclif/plugin-command-snapshot/-/plugin-command-snapshot-5.1.9.tgz" + integrity sha512-PeSQP2IxzL34e3DYoEdAw8UCcsuawcJqnQe97HNUSBpBSC2PY/7uL887TfmMIo/rhzM7PtSUNxfNwOiKw5cNUw== dependencies: - fancy-test "^1.4.3" + "@oclif/core" "3.26.6" + "@types/lodash.difference" "^4.5.9" + chalk "^5.3.0" + globby "^14.0.1" + just-diff "^5.2.0" + lodash.difference "^4.5.0" + lodash.get "^4.4.2" + lodash.sortby "^4.7.0" + semver "^7.6.0" + ts-json-schema-generator "^1.5.1" + +"@oclif/plugin-help@^6.2.10": + version "6.2.11" + resolved "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-6.2.11.tgz" + integrity sha512-Vo854dALtNhA34g6m4T9uWIrYfm/JFM82LWa5gLrsJGwpUGgeBwBX4P64HLo5ro59LF3YO2xPWViLaoK6gkm3g== + dependencies: + "@oclif/core" "^4" + +"@oclif/plugin-not-found@^3.2.16": + version "3.2.20" + resolved "https://registry.npmjs.org/@oclif/plugin-not-found/-/plugin-not-found-3.2.20.tgz" + integrity sha512-Le/+9TpXn7HQu4hU7bi6oBDu7aRHNtqBm+JvNlRAcv9tsw3IySOq9DWhsQI3ZN6w6ou1524gx+0DObxKwciZFA== + dependencies: + "@inquirer/confirm" "^3.2.0" + "@oclif/core" "^4" + ansis "^3.3.1" + fast-levenshtein "^3.0.0" + +"@oclif/plugin-warn-if-update-available@^3.1.11": + version "3.1.15" + resolved "https://registry.npmjs.org/@oclif/plugin-warn-if-update-available/-/plugin-warn-if-update-available-3.1.15.tgz" + integrity sha512-VzKbjzKiJssh7bDG9vTsZ9r6g7WuCL1Q7/Njqw7t33f17rGAn6w31S7eCZiWJBBjCEqxKF2JmhkfZ4YSA8Xt2g== + dependencies: + "@oclif/core" "^4" + ansis "^3.3.1" + debug "^4.3.5" + http-call "^5.2.2" + lodash "^4.17.21" + registry-auth-token "^5.0.2" "@octokit/auth-token@^2.4.4": version "2.5.0" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.5.0.tgz#27c37ea26c205f28443402477ffd261311f21e36" + resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz" integrity sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g== dependencies: "@octokit/types" "^6.0.3" -"@octokit/core@^3.6.0": +"@octokit/auth-token@^4.0.0": + version "4.0.0" + resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz" + integrity sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA== + +"@octokit/core@^3.5.1": version "3.6.0" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.6.0.tgz#3376cb9f3008d9b3d110370d90e0a1fcd5fe6085" + resolved "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz" integrity sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q== dependencies: "@octokit/auth-token" "^2.4.4" @@ -912,39 +1870,98 @@ before-after-hook "^2.2.0" universal-user-agent "^6.0.0" +"@octokit/core@^5.0.1": + version "5.2.0" + resolved "https://registry.npmjs.org/@octokit/core/-/core-5.2.0.tgz" + integrity sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg== + dependencies: + "@octokit/auth-token" "^4.0.0" + "@octokit/graphql" "^7.1.0" + "@octokit/request" "^8.3.1" + "@octokit/request-error" "^5.1.0" + "@octokit/types" "^13.0.0" + before-after-hook "^2.2.0" + universal-user-agent "^6.0.0" + "@octokit/endpoint@^6.0.1": version "6.0.12" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" + resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz" integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== dependencies: "@octokit/types" "^6.0.3" is-plain-object "^5.0.0" universal-user-agent "^6.0.0" +"@octokit/endpoint@^9.0.1": + version "9.0.5" + resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.5.tgz" + integrity sha512-ekqR4/+PCLkEBF6qgj8WqJfvDq65RH85OAgrtnVp1mSxaXF03u2xW/hUdweGS5654IlC0wkNYC18Z50tSYTAFw== + dependencies: + "@octokit/types" "^13.1.0" + universal-user-agent "^6.0.0" + "@octokit/graphql@^4.5.8": version "4.8.0" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.8.0.tgz#664d9b11c0e12112cbf78e10f49a05959aa22cc3" + resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz" integrity sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg== dependencies: "@octokit/request" "^5.6.0" "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" +"@octokit/graphql@^7.1.0": + version "7.1.0" + resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.0.tgz" + integrity sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ== + dependencies: + "@octokit/request" "^8.3.0" + "@octokit/types" "^13.0.0" + universal-user-agent "^6.0.0" + "@octokit/openapi-types@^12.11.0": version "12.11.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0" + resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz" integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ== -"@octokit/plugin-paginate-rest@^2.17.0": +"@octokit/openapi-types@^20.0.0": + version "20.0.0" + resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz" + integrity sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA== + +"@octokit/openapi-types@^22.2.0": + version "22.2.0" + resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz" + integrity sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg== + +"@octokit/plugin-paginate-rest@^2.16.8": version "2.21.3" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz#7f12532797775640dbb8224da577da7dc210c87e" + resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz" integrity sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw== dependencies: "@octokit/types" "^6.40.0" -"@octokit/plugin-rest-endpoint-methods@^5.13.0": +"@octokit/plugin-paginate-rest@^9.0.0": + version "9.2.1" + resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.1.tgz" + integrity sha512-wfGhE/TAkXZRLjksFXuDZdmGnJQHvtU/joFQdweXUgzo1XwvBCD4o4+75NtFfjfLK5IwLf9vHTfSiU3sLRYpRw== + dependencies: + "@octokit/types" "^12.6.0" + +"@octokit/plugin-request-log@^1.0.4": + version "1.0.4" + resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz" + integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== + +"@octokit/plugin-rest-endpoint-methods@^10.0.0": + version "10.4.1" + resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz" + integrity sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg== + dependencies: + "@octokit/types" "^12.6.0" + +"@octokit/plugin-rest-endpoint-methods@^5.12.0": version "5.16.2" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz#7ee8bf586df97dd6868cf68f641354e908c25342" + resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz" integrity sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw== dependencies: "@octokit/types" "^6.39.0" @@ -952,16 +1969,25 @@ "@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": version "2.1.0" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" + resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz" integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== dependencies: "@octokit/types" "^6.0.3" deprecation "^2.0.0" once "^1.4.0" +"@octokit/request-error@^5.1.0": + version "5.1.0" + resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.0.tgz" + integrity sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q== + dependencies: + "@octokit/types" "^13.1.0" + deprecation "^2.0.0" + once "^1.4.0" + "@octokit/request@^5.6.0", "@octokit/request@^5.6.3": version "5.6.3" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.3.tgz#19a022515a5bba965ac06c9d1334514eb50c48b0" + resolved "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz" integrity sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A== dependencies: "@octokit/endpoint" "^6.0.1" @@ -971,21 +1997,76 @@ node-fetch "^2.6.7" universal-user-agent "^6.0.0" +"@octokit/request@^8.3.0", "@octokit/request@^8.3.1": + version "8.4.0" + resolved "https://registry.npmjs.org/@octokit/request/-/request-8.4.0.tgz" + integrity sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw== + dependencies: + "@octokit/endpoint" "^9.0.1" + "@octokit/request-error" "^5.1.0" + "@octokit/types" "^13.1.0" + universal-user-agent "^6.0.0" + +"@octokit/rest@^18.12.0": + version "18.12.0" + resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz" + integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== + dependencies: + "@octokit/core" "^3.5.1" + "@octokit/plugin-paginate-rest" "^2.16.8" + "@octokit/plugin-request-log" "^1.0.4" + "@octokit/plugin-rest-endpoint-methods" "^5.12.0" + +"@octokit/types@^12.6.0": + version "12.6.0" + resolved "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz" + integrity sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw== + dependencies: + "@octokit/openapi-types" "^20.0.0" + +"@octokit/types@^13.0.0", "@octokit/types@^13.1.0": + version "13.5.0" + resolved "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz" + integrity sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ== + dependencies: + "@octokit/openapi-types" "^22.2.0" + "@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.39.0", "@octokit/types@^6.40.0": version "6.41.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.41.0.tgz#e58ef78d78596d2fb7df9c6259802464b5f84a04" + resolved "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz" integrity sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg== dependencies: "@octokit/openapi-types" "^12.11.0" "@pkgjs/parseargs@^0.11.0": version "0.11.0" - resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== +"@pnpm/config.env-replace@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz" + integrity sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w== + +"@pnpm/network.ca-file@^1.0.1": + version "1.0.2" + resolved "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz" + integrity sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA== + dependencies: + graceful-fs "4.2.10" + +"@pnpm/npm-conf@^2.1.0": + version "2.3.1" + resolved "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz" + integrity sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw== + dependencies: + "@pnpm/config.env-replace" "^1.1.0" + "@pnpm/network.ca-file" "^1.0.1" + config-chain "^1.1.11" + "@postman/form-data@~3.1.1": version "3.1.1" - resolved "https://registry.yarnpkg.com/@postman/form-data/-/form-data-3.1.1.tgz#d0446d0d3639a291f5e800e89fa1d0d3723f9414" + resolved "https://registry.npmjs.org/@postman/form-data/-/form-data-3.1.1.tgz" integrity sha512-vjh8Q2a8S6UCm/KKs31XFJqEEgmbjBmpPNVV2eVav6905wyFAwaUOBGA1NPBI4ERH9MMZc6w0umFgM6WbEPMdg== dependencies: asynckit "^0.4.0" @@ -994,7 +2075,7 @@ "@postman/tough-cookie@~4.1.3-postman.1": version "4.1.3-postman.1" - resolved "https://registry.yarnpkg.com/@postman/tough-cookie/-/tough-cookie-4.1.3-postman.1.tgz#9b2aa98f0b42f7e8381ee7e66277cd0253a91f11" + resolved "https://registry.npmjs.org/@postman/tough-cookie/-/tough-cookie-4.1.3-postman.1.tgz" integrity sha512-txpgUqZOnWYnUHZpHjkfb0IwVH4qJmyq77pPnJLlfhMtdCLMFTEeQHlzQiK906aaNCe4NEB5fGJHo9uzGbFMeA== dependencies: psl "^1.1.33" @@ -1002,222 +2083,911 @@ universalify "^0.2.0" url-parse "^1.5.3" -"@postman/tunnel-agent@^0.6.3": - version "0.6.3" - resolved "https://registry.yarnpkg.com/@postman/tunnel-agent/-/tunnel-agent-0.6.3.tgz#23048d8d8618d453c571f03189e944afdc2292b7" - integrity sha512-k57fzmAZ2PJGxfOA4SGR05ejorHbVAa/84Hxh/2nAztjNXc4ZjOm9NUIk6/Z6LCrBvJZqjRZbN8e/nROVUPVdg== +"@postman/tunnel-agent@^0.6.4": + version "0.6.4" + resolved "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.4.tgz" + integrity sha512-CJJlq8V7rNKhAw4sBfjixKpJW00SHqebqNUQKxMoepgeWZIbdPcD+rguRcivGhS4N12PymDcKgUgSD4rVC+RjQ== + dependencies: + safe-buffer "^5.0.1" + +"@puppeteer/browsers@2.4.0": + version "2.4.0" + resolved "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.4.0.tgz" + integrity sha512-x8J1csfIygOwf6D6qUAZ0ASk3z63zPb7wkNeHRerCMh82qWKUrOgkuP005AJC8lDL6/evtXETGEJVcwykKT4/g== + dependencies: + debug "^4.3.6" + extract-zip "^2.0.1" + progress "^2.0.3" + proxy-agent "^6.4.0" + semver "^7.6.3" + tar-fs "^3.0.6" + unbzip2-stream "^1.4.3" + yargs "^17.7.2" + +"@salesforce/cli-plugins-testkit@^5.3.28": + version "5.3.28" + resolved "https://registry.npmjs.org/@salesforce/cli-plugins-testkit/-/cli-plugins-testkit-5.3.28.tgz" + integrity sha512-kO/TgIOGin+grB+T93dILA1VbrQbtuoLyD2N/LAAhBeqBG8XtrZdll/9GJ6FdSfpZO5p4KNvffBjtrO34pUJVw== + dependencies: + "@salesforce/core" "^8.5.1" + "@salesforce/kit" "^3.2.1" + "@salesforce/ts-types" "^2.0.11" + "@types/shelljs" "^0.8.15" + debug "^4.3.6" + jszip "^3.10.1" + shelljs "^0.8.4" + sinon "^17.0.2" + strip-ansi "6.0.1" + ts-retry-promise "^0.8.1" + +"@salesforce/core@^7.3.9": + version "7.4.0" + resolved "https://registry.npmjs.org/@salesforce/core/-/core-7.4.0.tgz" + integrity sha512-wn3fJnpG8h493qKR52Il3xgRbXg4zwbV34wonf/mTZP3n5rJeC/7S69249CGN155Os6Lds5rEUo3pGJFwgCBdQ== + dependencies: + "@jsforce/jsforce-node" "^3.2.0" + "@salesforce/kit" "^3.1.2" + "@salesforce/schemas" "^1.9.0" + "@salesforce/ts-types" "^2.0.9" + ajv "^8.15.0" + change-case "^4.1.2" + fast-levenshtein "^3.0.0" + faye "^1.4.0" + form-data "^4.0.0" + js2xmlparser "^4.0.1" + jsonwebtoken "9.0.2" + jszip "3.10.1" + pino "^8.21.0" + pino-abstract-transport "^1.2.0" + pino-pretty "^10.3.1" + proper-lockfile "^4.1.2" + semver "^7.6.2" + ts-retry-promise "^0.8.1" + +"@salesforce/core@^8.5.1": + version "8.5.4" + resolved "https://registry.npmjs.org/@salesforce/core/-/core-8.5.4.tgz" + integrity sha512-dO8tzFxq811qNPeKPPO2OA2KPYW5rO0YRinW/+7zmRJW3EtNpe93dsQVGwBSAAYrSbYeBwiKdliNqNTN7tKJ0A== + dependencies: + "@jsforce/jsforce-node" "^3.4.1" + "@salesforce/kit" "^3.2.2" + "@salesforce/schemas" "^1.9.0" + "@salesforce/ts-types" "^2.0.10" + ajv "^8.17.1" + change-case "^4.1.2" + fast-levenshtein "^3.0.0" + faye "^1.4.0" + form-data "^4.0.0" + js2xmlparser "^4.0.1" + jsonwebtoken "9.0.2" + jszip "3.10.1" + pino "^9.3.2" + pino-abstract-transport "^1.2.0" + pino-pretty "^11.2.2" + proper-lockfile "^4.1.2" + semver "^7.6.3" + ts-retry-promise "^0.8.1" + +"@salesforce/core@^8.5.3": + version "8.5.3" + resolved "https://registry.npmjs.org/@salesforce/core/-/core-8.5.3.tgz" + integrity sha512-EZC1SS8IiJAV3O/B4qKhhzTi4jHMNBsYTzwMM4RTqTyFP2dd7uQQPFHNDybBXcWrUgA4UqQBRoOhW2YPYyuPow== + dependencies: + "@jsforce/jsforce-node" "^3.4.1" + "@salesforce/kit" "^3.2.2" + "@salesforce/schemas" "^1.9.0" + "@salesforce/ts-types" "^2.0.10" + ajv "^8.17.1" + change-case "^4.1.2" + fast-levenshtein "^3.0.0" + faye "^1.4.0" + form-data "^4.0.0" + js2xmlparser "^4.0.1" + jsonwebtoken "9.0.2" + jszip "3.10.1" + pino "^9.3.2" + pino-abstract-transport "^1.2.0" + pino-pretty "^11.2.2" + proper-lockfile "^4.1.2" + semver "^7.6.3" + ts-retry-promise "^0.8.1" + +"@salesforce/dev-config@^4.1.0": + version "4.1.0" + resolved "https://registry.npmjs.org/@salesforce/dev-config/-/dev-config-4.1.0.tgz" + integrity sha512-2iDDepiIwjXHS5IVY7pwv8jMo4xWosJ7p/UTj+lllpB/gnJiYLhjJPE4Z3FCGFKyvfg5jGaimCd8Ca6bLGsCQA== + +"@salesforce/dev-config@^4.3.1": + version "4.3.1" + resolved "https://registry.npmjs.org/@salesforce/dev-config/-/dev-config-4.3.1.tgz" + integrity sha512-rO6axodoRF2SA1kknGttIWuL7HhIwSmweGlBzM8y2m5TH8DeIv4xsqYc8Cu+SrR3JT1FN4nh6XgrogI83AJfKg== + +"@salesforce/dev-scripts@^10": + version "10.1.0" + resolved "https://registry.npmjs.org/@salesforce/dev-scripts/-/dev-scripts-10.1.0.tgz" + integrity sha512-8fItXnCR8G5n950Ymgrjc6CGJjR80376v6GIdjatv3AmMVJAB/GerglcNF/LIJjBXB5OLxiTLAjQdPCvWqBL1g== + dependencies: + "@commitlint/cli" "^17.1.2" + "@commitlint/config-conventional" "^17.8.1" + "@salesforce/dev-config" "^4.1.0" + "@salesforce/prettier-config" "^0.0.3" + "@types/chai" "^4.3.14" + "@types/mocha" "^10.0.6" + "@types/node" "^18.19.32" + "@types/sinon" "^10.0.20" + chai "^4.3.10" + chalk "^4.0.0" + cosmiconfig "^8.3.6" + eslint-config-salesforce-typescript "^3.3.0" + husky "^7.0.4" + linkinator "^6.0.4" + mocha "^10.4.0" + nyc "^15.1.0" + prettier "^2.8.8" + pretty-quick "^3.3.1" + shelljs "^0.8.5" + sinon "10.0.0" + source-map-support "^0.5.21" + ts-node "^10.9.2" + typedoc "^0.25.12" + typedoc-plugin-missing-exports "0.23.0" + typescript "^5.4.3" + wireit "^0.14.4" + +"@salesforce/kit@^3.1.2": + version "3.1.2" + resolved "https://registry.npmjs.org/@salesforce/kit/-/kit-3.1.2.tgz" + integrity sha512-si+ddvZDgx9q5czxAANuK5xhz3pv+KGspQy1wyia/7HDPKadA0QZkLTzUnO1Ju4Mux32CNHEb2y9lw9jj+eVTA== + dependencies: + "@salesforce/ts-types" "^2.0.9" + tslib "^2.6.2" + +"@salesforce/kit@^3.2.1", "@salesforce/kit@^3.2.2": + version "3.2.2" + resolved "https://registry.npmjs.org/@salesforce/kit/-/kit-3.2.2.tgz" + integrity sha512-Qh+Jx65LKR3BlH+bxNBbvI4+/+/igAJ9x2iEDM3tHb3B2JCEnssPP0lw+K/zWHsdtk+OorBiKpHaC6RrjW+9fw== + dependencies: + "@salesforce/ts-types" "^2.0.12" + +"@salesforce/prettier-config@^0.0.3": + version "0.0.3" + resolved "https://registry.npmjs.org/@salesforce/prettier-config/-/prettier-config-0.0.3.tgz" + integrity sha512-hYOhoPTCSYMDYn+U1rlEk16PoBeAJPkrdg4/UtAzupM1mRRJOwEPMG1d7U8DxJFKuXW3DMEYWr2MwAIBDaHmFg== + +"@salesforce/schemas@^1.9.0": + version "1.9.0" + resolved "https://registry.npmjs.org/@salesforce/schemas/-/schemas-1.9.0.tgz" + integrity sha512-LiN37zG5ODT6z70sL1fxF7BQwtCX9JOWofSU8iliSNIM+WDEeinnoFtVqPInRSNt8I0RiJxIKCrqstsmQRBNvA== + +"@salesforce/sf-plugins-core@^11.3.7": + version "11.3.7" + resolved "https://registry.npmjs.org/@salesforce/sf-plugins-core/-/sf-plugins-core-11.3.7.tgz" + integrity sha512-aqOAYex3JGHUE/ECWpQuedEJXq00vJOqNr3VYF0Lp9OBAVn0AZZu10fzv+ww2hLDjA9dH782ZNqw/CiE0XKJlA== + dependencies: + "@inquirer/confirm" "^3.1.22" + "@inquirer/password" "^2.1.22" + "@oclif/core" "^4.0.19" + "@salesforce/core" "^8.5.1" + "@salesforce/kit" "^3.2.1" + "@salesforce/ts-types" "^2.0.12" + ansis "^3.3.2" + cli-progress "^3.12.0" + natural-orderby "^3.0.2" + slice-ansi "^7.1.0" + string-width "^7.2.0" + terminal-link "^3.0.0" + +"@salesforce/ts-types@^2.0.10", "@salesforce/ts-types@^2.0.11", "@salesforce/ts-types@^2.0.12", "@salesforce/ts-types@^2.0.9": + version "2.0.12" + resolved "https://registry.npmjs.org/@salesforce/ts-types/-/ts-types-2.0.12.tgz" + integrity sha512-BIJyduJC18Kc8z+arUm5AZ9VkPRyw1KKAm+Tk+9LT99eOzhNilyfKzhZ4t+tG2lIGgnJpmytZfVDZ0e2kFul8g== + +"@sigstore/bundle@^2.3.2": + version "2.3.2" + resolved "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.3.2.tgz" + integrity sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA== + dependencies: + "@sigstore/protobuf-specs" "^0.3.2" + +"@sigstore/core@^1.0.0", "@sigstore/core@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@sigstore/core/-/core-1.1.0.tgz" + integrity sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg== + +"@sigstore/protobuf-specs@^0.3.2": + version "0.3.2" + resolved "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.2.tgz" + integrity sha512-c6B0ehIWxMI8wiS/bj6rHMPqeFvngFV7cDU/MY+B16P9Z3Mp9k8L93eYZ7BYzSickzuqAQqAq0V956b3Ju6mLw== + +"@sigstore/sign@^2.3.2": + version "2.3.2" + resolved "https://registry.npmjs.org/@sigstore/sign/-/sign-2.3.2.tgz" + integrity sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA== + dependencies: + "@sigstore/bundle" "^2.3.2" + "@sigstore/core" "^1.0.0" + "@sigstore/protobuf-specs" "^0.3.2" + make-fetch-happen "^13.0.1" + proc-log "^4.2.0" + promise-retry "^2.0.1" + +"@sigstore/tuf@^2.3.4": + version "2.3.4" + resolved "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.4.tgz" + integrity sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw== + dependencies: + "@sigstore/protobuf-specs" "^0.3.2" + tuf-js "^2.2.1" + +"@sigstore/verify@^1.2.1": + version "1.2.1" + resolved "https://registry.npmjs.org/@sigstore/verify/-/verify-1.2.1.tgz" + integrity sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g== + dependencies: + "@sigstore/bundle" "^2.3.2" + "@sigstore/core" "^1.1.0" + "@sigstore/protobuf-specs" "^0.3.2" + +"@sindresorhus/is@^4", "@sindresorhus/is@^4.0.0": + version "4.6.0" + resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz" + integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== + +"@sindresorhus/is@^5.2.0": + version "5.6.0" + resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz" + integrity sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g== + +"@sindresorhus/merge-streams@^2.1.0": + version "2.3.0" + resolved "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz" + integrity sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg== + +"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1": + version "1.8.3" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz" + integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/commons@^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz" + integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== + dependencies: + type-detect "4.0.8" + +"@sinonjs/commons@^3.0.0", "@sinonjs/commons@^3.0.1": + version "3.0.1" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^11.2.2": + version "11.2.2" + resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz" + integrity sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw== + dependencies: + "@sinonjs/commons" "^3.0.0" + +"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1": + version "6.0.1" + resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz" + integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@sinonjs/samsam@^5.3.1": + version "5.3.1" + resolved "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.1.tgz" + integrity sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg== + dependencies: + "@sinonjs/commons" "^1.6.0" + lodash.get "^4.4.2" + type-detect "^4.0.8" + +"@sinonjs/samsam@^8.0.0": + version "8.0.0" + resolved "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz" + integrity sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew== + dependencies: + "@sinonjs/commons" "^2.0.0" + lodash.get "^4.4.2" + type-detect "^4.0.8" + +"@sinonjs/text-encoding@^0.7.1", "@sinonjs/text-encoding@^0.7.2": + version "0.7.2" + resolved "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz" + integrity sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ== + +"@slack/logger@^4.0.0": + version "4.0.0" + resolved "https://registry.npmjs.org/@slack/logger/-/logger-4.0.0.tgz" + integrity sha512-Wz7QYfPAlG/DR+DfABddUZeNgoeY7d1J39OCR2jR+v7VBsB8ezulDK5szTnDDPDwLH5IWhLvXIHlCFZV7MSKgA== + dependencies: + "@types/node" ">=18.0.0" + +"@slack/types@^2.12.0", "@slack/types@^2.9.0": + version "2.12.0" + resolved "https://registry.npmjs.org/@slack/types/-/types-2.12.0.tgz" + integrity sha512-yFewzUomYZ2BYaGJidPuIgjoYj5wqPDmi7DLSaGIkf+rCi4YZ2Z3DaiYIbz7qb/PL2NmamWjCvB7e9ArI5HkKg== + +"@slack/web-api@^7.3.4": + version "7.3.4" + resolved "https://registry.npmjs.org/@slack/web-api/-/web-api-7.3.4.tgz" + integrity sha512-KwLK8dlz2lhr3NO7kbYQ7zgPTXPKrhq1JfQc0etJ0K8LSJhYYnf8GbVznvgDT/Uz1/pBXfFQnoXjrQIOKAdSuw== + dependencies: + "@slack/logger" "^4.0.0" + "@slack/types" "^2.9.0" + "@types/node" ">=18.0.0" + "@types/retry" "0.12.0" + axios "^1.7.4" + eventemitter3 "^5.0.1" + form-data "^4.0.0" + is-electron "2.2.2" + is-stream "^2" + p-queue "^6" + p-retry "^4" + retry "^0.13.1" + +"@smithy/abort-controller@^3.1.1": + version "3.1.1" + resolved "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.1.tgz" + integrity sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/chunked-blob-reader-native@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-3.0.0.tgz" + integrity sha512-VDkpCYW+peSuM4zJip5WDfqvg2Mo/e8yxOv3VF1m11y7B8KKMKVFtmZWDe36Fvk8rGuWrPZHHXZ7rR7uM5yWyg== + dependencies: + "@smithy/util-base64" "^3.0.0" + tslib "^2.6.2" + +"@smithy/chunked-blob-reader@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-3.0.0.tgz" + integrity sha512-sbnURCwjF0gSToGlsBiAmd1lRCmSn72nu9axfJu5lIx6RUEgHu6GwTMbqCdhQSi0Pumcm5vFxsi9XWXb2mTaoA== + dependencies: + tslib "^2.6.2" + +"@smithy/config-resolver@^3.0.5": + version "3.0.5" + resolved "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.5.tgz" + integrity sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA== + dependencies: + "@smithy/node-config-provider" "^3.1.4" + "@smithy/types" "^3.3.0" + "@smithy/util-config-provider" "^3.0.0" + "@smithy/util-middleware" "^3.0.3" + tslib "^2.6.2" + +"@smithy/core@^2.4.0": + version "2.4.0" + resolved "https://registry.npmjs.org/@smithy/core/-/core-2.4.0.tgz" + integrity sha512-cHXq+FneIF/KJbt4q4pjN186+Jf4ZB0ZOqEaZMBhT79srEyGDDBV31NqBRBjazz8ppQ1bJbDJMY9ba5wKFV36w== + dependencies: + "@smithy/middleware-endpoint" "^3.1.0" + "@smithy/middleware-retry" "^3.0.15" + "@smithy/middleware-serde" "^3.0.3" + "@smithy/protocol-http" "^4.1.0" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/credential-provider-imds@^3.2.0": + version "3.2.0" + resolved "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.0.tgz" + integrity sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA== + dependencies: + "@smithy/node-config-provider" "^3.1.4" + "@smithy/property-provider" "^3.1.3" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + tslib "^2.6.2" + +"@smithy/eventstream-codec@^3.1.2": + version "3.1.2" + resolved "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.2.tgz" + integrity sha512-0mBcu49JWt4MXhrhRAlxASNy0IjDRFU+aWNDRal9OtUJvJNiwDuyKMUONSOjLjSCeGwZaE0wOErdqULer8r7yw== + dependencies: + "@aws-crypto/crc32" "5.2.0" + "@smithy/types" "^3.3.0" + "@smithy/util-hex-encoding" "^3.0.0" + tslib "^2.6.2" + +"@smithy/eventstream-serde-browser@^3.0.6": + version "3.0.6" + resolved "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.6.tgz" + integrity sha512-2hM54UWQUOrki4BtsUI1WzmD13/SeaqT/AB3EUJKbcver/WgKNaiJ5y5F5XXuVe6UekffVzuUDrBZVAA3AWRpQ== + dependencies: + "@smithy/eventstream-serde-universal" "^3.0.5" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/eventstream-serde-config-resolver@^3.0.3": + version "3.0.3" + resolved "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.3.tgz" + integrity sha512-NVTYjOuYpGfrN/VbRQgn31x73KDLfCXCsFdad8DiIc3IcdxL+dYA9zEQPyOP7Fy2QL8CPy2WE4WCUD+ZsLNfaQ== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/eventstream-serde-node@^3.0.5": + version "3.0.5" + resolved "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.5.tgz" + integrity sha512-+upXvnHNyZP095s11jF5dhGw/Ihzqwl5G+/KtMnoQOpdfC3B5HYCcDVG9EmgkhJMXJlM64PyN5gjJl0uXFQehQ== + dependencies: + "@smithy/eventstream-serde-universal" "^3.0.5" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/eventstream-serde-universal@^3.0.5": + version "3.0.5" + resolved "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.5.tgz" + integrity sha512-5u/nXbyoh1s4QxrvNre9V6vfyoLWuiVvvd5TlZjGThIikc3G+uNiG9uOTCWweSRjv1asdDIWK7nOmN7le4RYHQ== + dependencies: + "@smithy/eventstream-codec" "^3.1.2" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/fetch-http-handler@^3.2.4": + version "3.2.4" + resolved "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz" + integrity sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg== + dependencies: + "@smithy/protocol-http" "^4.1.0" + "@smithy/querystring-builder" "^3.0.3" + "@smithy/types" "^3.3.0" + "@smithy/util-base64" "^3.0.0" + tslib "^2.6.2" + +"@smithy/hash-blob-browser@^3.1.2": + version "3.1.2" + resolved "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-3.1.2.tgz" + integrity sha512-hAbfqN2UbISltakCC2TP0kx4LqXBttEv2MqSPE98gVuDFMf05lU+TpC41QtqGP3Ff5A3GwZMPfKnEy0VmEUpmg== + dependencies: + "@smithy/chunked-blob-reader" "^3.0.0" + "@smithy/chunked-blob-reader-native" "^3.0.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/hash-node@^3.0.3": + version "3.0.3" + resolved "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.3.tgz" + integrity sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw== + dependencies: + "@smithy/types" "^3.3.0" + "@smithy/util-buffer-from" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/hash-stream-node@^3.1.2": + version "3.1.2" + resolved "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-3.1.2.tgz" + integrity sha512-PBgDMeEdDzi6JxKwbfBtwQG9eT9cVwsf0dZzLXoJF4sHKHs5HEo/3lJWpn6jibfJwT34I1EBXpBnZE8AxAft6g== + dependencies: + "@smithy/types" "^3.3.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/invalid-dependency@^3.0.3": + version "3.0.3" + resolved "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.3.tgz" + integrity sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/is-array-buffer@^2.2.0": + version "2.2.0" + resolved "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz" + integrity sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA== + dependencies: + tslib "^2.6.2" + +"@smithy/is-array-buffer@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz" + integrity sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ== + dependencies: + tslib "^2.6.2" + +"@smithy/md5-js@^3.0.3": + version "3.0.3" + resolved "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-3.0.3.tgz" + integrity sha512-O/SAkGVwpWmelpj/8yDtsaVe6sINHLB1q8YE/+ZQbDxIw3SRLbTZuRaI10K12sVoENdnHqzPp5i3/H+BcZ3m3Q== + dependencies: + "@smithy/types" "^3.3.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/middleware-content-length@^3.0.5": + version "3.0.5" + resolved "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.5.tgz" + integrity sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw== + dependencies: + "@smithy/protocol-http" "^4.1.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/middleware-endpoint@^3.1.0": + version "3.1.0" + resolved "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz" + integrity sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw== + dependencies: + "@smithy/middleware-serde" "^3.0.3" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/shared-ini-file-loader" "^3.1.4" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + "@smithy/util-middleware" "^3.0.3" + tslib "^2.6.2" + +"@smithy/middleware-retry@^3.0.15": + version "3.0.15" + resolved "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.15.tgz" + integrity sha512-iTMedvNt1ApdvkaoE8aSDuwaoc+BhvHqttbA/FO4Ty+y/S5hW6Ci/CTScG7vam4RYJWZxdTElc3MEfHRVH6cgQ== + dependencies: + "@smithy/node-config-provider" "^3.1.4" + "@smithy/protocol-http" "^4.1.0" + "@smithy/service-error-classification" "^3.0.3" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-retry" "^3.0.3" + tslib "^2.6.2" + uuid "^9.0.1" + +"@smithy/middleware-serde@^3.0.3": + version "3.0.3" + resolved "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz" + integrity sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/middleware-stack@^3.0.3": + version "3.0.3" + resolved "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz" + integrity sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/node-config-provider@^3.1.4": + version "3.1.4" + resolved "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz" + integrity sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ== + dependencies: + "@smithy/property-provider" "^3.1.3" + "@smithy/shared-ini-file-loader" "^3.1.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/node-http-handler@^3.1.4": + version "3.1.4" + resolved "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz" + integrity sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg== + dependencies: + "@smithy/abort-controller" "^3.1.1" + "@smithy/protocol-http" "^4.1.0" + "@smithy/querystring-builder" "^3.0.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/property-provider@^3.1.3": + version "3.1.3" + resolved "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz" + integrity sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/protocol-http@^4.1.0": + version "4.1.0" + resolved "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.0.tgz" + integrity sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/querystring-builder@^3.0.3": + version "3.0.3" + resolved "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz" + integrity sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw== + dependencies: + "@smithy/types" "^3.3.0" + "@smithy/util-uri-escape" "^3.0.0" + tslib "^2.6.2" + +"@smithy/querystring-parser@^3.0.3": + version "3.0.3" + resolved "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz" + integrity sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/service-error-classification@^3.0.3": + version "3.0.3" + resolved "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.3.tgz" + integrity sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ== + dependencies: + "@smithy/types" "^3.3.0" + +"@smithy/shared-ini-file-loader@^3.1.4": + version "3.1.4" + resolved "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz" + integrity sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ== dependencies: - safe-buffer "^5.0.1" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" -"@salesforce/bunyan@^2.0.0": - version "2.0.0" - resolved "https://registry.npmjs.org/@salesforce/bunyan/-/bunyan-2.0.0.tgz" - integrity sha512-5hq+HWQSeymuygl3i9ehlQo3XWrlBE+A+QzmpDaoK37op4u9M+SBUbXfOW0IABOQCg+JmfQPocSMV74hRoqU9w== +"@smithy/signature-v4@^4.1.0": + version "4.1.0" + resolved "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.0.tgz" + integrity sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag== + dependencies: + "@smithy/is-array-buffer" "^3.0.0" + "@smithy/protocol-http" "^4.1.0" + "@smithy/types" "^3.3.0" + "@smithy/util-hex-encoding" "^3.0.0" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-uri-escape" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/smithy-client@^3.2.0": + version "3.2.0" + resolved "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.2.0.tgz" + integrity sha512-pDbtxs8WOhJLJSeaF/eAbPgXg4VVYFlRcL/zoNYA5WbG3wBL06CHtBSg53ppkttDpAJ/hdiede+xApip1CwSLw== dependencies: - dayjs "^1.8.16" - dayjs-plugin-utc "^0.1.2" - optionalDependencies: - dtrace-provider "~0.6" - mv "~2" - safe-json-stringify "~1" + "@smithy/middleware-endpoint" "^3.1.0" + "@smithy/middleware-stack" "^3.0.3" + "@smithy/protocol-http" "^4.1.0" + "@smithy/types" "^3.3.0" + "@smithy/util-stream" "^3.1.3" + tslib "^2.6.2" -"@salesforce/command@^4.2.1": - version "4.2.1" - resolved "https://registry.npmjs.org/@salesforce/command/-/command-4.2.1.tgz" - integrity sha512-hanDjR8yLdeKlrlUXjh18pmbSuF+46jtIfjpG0aA1Q089qol9+nqGt64ToeN7df7BM0Vk0X0+765YNKV/zvxlA== - dependencies: - "@oclif/command" "^1.8.1" - "@oclif/errors" "^1.2.2" - "@oclif/parser" "3.8.6" - "@oclif/plugin-help" "^2.2.0" - "@oclif/test" "^1.2.4" - "@salesforce/core" "^2.31.0" - "@salesforce/kit" "^1.5.17" - "@salesforce/ts-types" "^1.5.20" - chalk "^2.4.2" - cli-ux "^4.9.3" - -"@salesforce/core@^2.31.0", "@salesforce/core@^2.33.1": - version "2.33.1" - resolved "https://registry.npmjs.org/@salesforce/core/-/core-2.33.1.tgz" - integrity sha512-jKVFYEvlV+loBoau5heBOVXmzsPO+RbYh6SPybJK6xF7khQmzu7+WAQbikY2eY8VaXcded2kka8L/FKuD/LKBg== - dependencies: - "@salesforce/bunyan" "^2.0.0" - "@salesforce/kit" "^1.5.0" - "@salesforce/schemas" "^1.0.1" - "@salesforce/ts-types" "^1.5.13" - "@types/graceful-fs" "^4.1.5" - "@types/jsforce" "^1.9.35" - "@types/mkdirp" "^1.0.1" - debug "^3.1.0" - faye "^1.4.0" - graceful-fs "^4.2.4" - jsen "0.6.6" - jsforce "^1.11.0" - jsonwebtoken "8.5.0" - mkdirp "1.0.4" - semver "^7.3.5" - ts-retry-promise "^0.6.0" +"@smithy/types@^2.12.0": + version "2.12.0" + resolved "https://registry.npmjs.org/@smithy/types/-/types-2.12.0.tgz" + integrity sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw== + dependencies: + tslib "^2.6.2" -"@salesforce/dev-config@^2.1.0": - version "2.1.0" - resolved "https://registry.npmjs.org/@salesforce/dev-config/-/dev-config-2.1.0.tgz" - integrity sha512-I+zrptt8zI1jbP3TVA6g7i3quuh71tPky8gwCsXaQ2X9ea1xbeAslN4YjWfgqbY5SaxJhqP979oxFADlH+OehQ== +"@smithy/types@^3.3.0": + version "3.3.0" + resolved "https://registry.npmjs.org/@smithy/types/-/types-3.3.0.tgz" + integrity sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA== + dependencies: + tslib "^2.6.2" -"@salesforce/kit@^1.5.0", "@salesforce/kit@^1.5.17": - version "1.5.25" - resolved "https://registry.npmjs.org/@salesforce/kit/-/kit-1.5.25.tgz" - integrity sha512-Tbb7AZwJ00oGW8uv4DWsb3tjYi/rI8XWICWhLDpi44lTjrd+b22hKZMJLZ6XWEmezwtbzLT7vZIcj3IMtDgkIg== +"@smithy/url-parser@^3.0.3": + version "3.0.3" + resolved "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.3.tgz" + integrity sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A== dependencies: - "@salesforce/ts-types" "^1.5.20" - shx "^0.3.3" - tslib "^2.2.0" + "@smithy/querystring-parser" "^3.0.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" -"@salesforce/schemas@^1.0.1": - version "1.0.4" - resolved "https://registry.npmjs.org/@salesforce/schemas/-/schemas-1.0.4.tgz" - integrity sha512-JatCrSuWbr4aWJlkJ9CVCUucZvY1pfKfO/m1AHxaWix5kUQykA+oI1zQRKjelOy2IbDmz09cBCEGjMrMp3u+dA== +"@smithy/util-base64@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz" + integrity sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ== + dependencies: + "@smithy/util-buffer-from" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" -"@salesforce/ts-sinon@^1.2.4": - version "1.3.5" - resolved "https://registry.npmjs.org/@salesforce/ts-sinon/-/ts-sinon-1.3.5.tgz" - integrity sha512-Zp92SW5IrxAman3G61jhxDiJX+SrvYtfiNKhXi/nGKNk5Utbq0O2KfbjPomqk4vVvhUheZpnHUdN/y/XaxRsoA== +"@smithy/util-body-length-browser@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz" + integrity sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ== dependencies: - "@salesforce/ts-types" "^1.5.5" - sinon "5.1.1" - tslib "^1.10.0" + tslib "^2.6.2" -"@salesforce/ts-types@^1.5.13", "@salesforce/ts-types@^1.5.20": - version "1.5.20" - resolved "https://registry.npmjs.org/@salesforce/ts-types/-/ts-types-1.5.20.tgz" - integrity sha512-Ov6um4CWd63EvkRavkHG0J/P9XYL55sdkDWPMr7+AIgqh5flHxDRz09/C4e9M94aX30rzJxW4TVX6EBf4Cu2BQ== +"@smithy/util-body-length-node@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz" + integrity sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA== dependencies: - tslib "^2.2.0" + tslib "^2.6.2" -"@salesforce/ts-types@^1.5.5": - version "1.5.5" - resolved "https://registry.npmjs.org/@salesforce/ts-types/-/ts-types-1.5.5.tgz" - integrity sha512-d4YdsA3MBTJcC6ZdqHe2+yv7MWKsoYmgjTlc56SOy8sROrQ9RjJYaUnj1h1Zi1aWGgkGaNCCAOBomcrhMa4crw== +"@smithy/util-buffer-from@^2.2.0": + version "2.2.0" + resolved "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz" + integrity sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA== dependencies: - tslib "^1.10.0" + "@smithy/is-array-buffer" "^2.2.0" + tslib "^2.6.2" -"@sindresorhus/is@^0.14.0": - version "0.14.0" - resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz" - integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== +"@smithy/util-buffer-from@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz" + integrity sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA== + dependencies: + "@smithy/is-array-buffer" "^3.0.0" + tslib "^2.6.2" -"@sindresorhus/is@^4.0.0": - version "4.6.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" - integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== +"@smithy/util-config-provider@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz" + integrity sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ== + dependencies: + tslib "^2.6.2" + +"@smithy/util-defaults-mode-browser@^3.0.15": + version "3.0.15" + resolved "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.15.tgz" + integrity sha512-FZ4Psa3vjp8kOXcd3HJOiDPBCWtiilLl57r0cnNtq/Ga9RSDrM5ERL6xt+tO43+2af6Pn5Yp92x2n5vPuduNfg== + dependencies: + "@smithy/property-provider" "^3.1.3" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + bowser "^2.11.0" + tslib "^2.6.2" + +"@smithy/util-defaults-mode-node@^3.0.15": + version "3.0.15" + resolved "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.15.tgz" + integrity sha512-KSyAAx2q6d0t6f/S4XB2+3+6aQacm3aLMhs9aLMqn18uYGUepbdssfogW5JQZpc6lXNBnp0tEnR5e9CEKmEd7A== + dependencies: + "@smithy/config-resolver" "^3.0.5" + "@smithy/credential-provider-imds" "^3.2.0" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/property-provider" "^3.1.3" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/util-endpoints@^2.0.5": + version "2.0.5" + resolved "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.0.5.tgz" + integrity sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg== + dependencies: + "@smithy/node-config-provider" "^3.1.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" -"@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.7.0": - version "1.8.2" - resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.2.tgz" - integrity sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw== +"@smithy/util-hex-encoding@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz" + integrity sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ== dependencies: - type-detect "4.0.8" + tslib "^2.6.2" -"@sinonjs/formatio@^2.0.0": - version "2.0.0" - resolved "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz" - integrity sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg== +"@smithy/util-middleware@^3.0.3": + version "3.0.3" + resolved "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.3.tgz" + integrity sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw== dependencies: - samsam "1.3.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" -"@sinonjs/formatio@^3.2.1": - version "3.2.2" - resolved "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz" - integrity sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ== +"@smithy/util-retry@^3.0.3": + version "3.0.3" + resolved "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.3.tgz" + integrity sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w== dependencies: - "@sinonjs/commons" "^1" - "@sinonjs/samsam" "^3.1.0" + "@smithy/service-error-classification" "^3.0.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" -"@sinonjs/samsam@^3.1.0": - version "3.3.3" - resolved "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz" - integrity sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ== +"@smithy/util-stream@^3.1.3": + version "3.1.3" + resolved "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.3.tgz" + integrity sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw== + dependencies: + "@smithy/fetch-http-handler" "^3.2.4" + "@smithy/node-http-handler" "^3.1.4" + "@smithy/types" "^3.3.0" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-buffer-from" "^3.0.0" + "@smithy/util-hex-encoding" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/util-uri-escape@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz" + integrity sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg== dependencies: - "@sinonjs/commons" "^1.3.0" - array-from "^2.1.1" - lodash "^4.17.15" + tslib "^2.6.2" -"@sinonjs/text-encoding@^0.7.1": - version "0.7.1" - resolved "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz" - integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== +"@smithy/util-utf8@^2.0.0": + version "2.3.0" + resolved "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz" + integrity sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A== + dependencies: + "@smithy/util-buffer-from" "^2.2.0" + tslib "^2.6.2" -"@slack/logger@^3.0.0": +"@smithy/util-utf8@^3.0.0": version "3.0.0" - resolved "https://registry.yarnpkg.com/@slack/logger/-/logger-3.0.0.tgz#b736d4e1c112c22a10ffab0c2d364620aedcb714" - integrity sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA== + resolved "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz" + integrity sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA== dependencies: - "@types/node" ">=12.0.0" + "@smithy/util-buffer-from" "^3.0.0" + tslib "^2.6.2" -"@slack/types@^2.8.0": - version "2.8.0" - resolved "https://registry.yarnpkg.com/@slack/types/-/types-2.8.0.tgz#11ea10872262a7e6f86f54e5bcd4f91e3a41fe91" - integrity sha512-ghdfZSF0b4NC9ckBA8QnQgC9DJw2ZceDq0BIjjRSv6XAZBXJdWgxIsYz0TYnWSiqsKZGH2ZXbj9jYABZdH3OSQ== - -"@slack/web-api@^6.9.0": - version "6.9.0" - resolved "https://registry.yarnpkg.com/@slack/web-api/-/web-api-6.9.0.tgz#d829dcfef490dbce8e338912706b6f39dcde3ad2" - integrity sha512-RME5/F+jvQmZHkoP+ogrDbixq1Ms1mBmylzuWq4sf3f7GCpMPWoiZ+WqWk+sism3vrlveKWIgO9R4Qg9fiRyoQ== - dependencies: - "@slack/logger" "^3.0.0" - "@slack/types" "^2.8.0" - "@types/is-stream" "^1.1.0" - "@types/node" ">=12.0.0" - axios "^0.27.2" - eventemitter3 "^3.1.0" - form-data "^2.5.0" - is-electron "2.2.2" - is-stream "^1.1.0" - p-queue "^6.6.1" - p-retry "^4.0.0" +"@smithy/util-waiter@^3.1.2": + version "3.1.2" + resolved "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.2.tgz" + integrity sha512-4pP0EV3iTsexDx+8PPGAKCQpd/6hsQBaQhqWzU4hqKPHN5epPsxKbvUTIiYIHTxaKt6/kEaqPBpu/ufvfbrRzw== + dependencies: + "@smithy/abort-controller" "^3.1.1" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" "@supercharge/promise-pool@^3.2.0": version "3.2.0" - resolved "https://registry.yarnpkg.com/@supercharge/promise-pool/-/promise-pool-3.2.0.tgz#a6ab4afdf798e453a6bb51c4ae340852e1266af8" + resolved "https://registry.npmjs.org/@supercharge/promise-pool/-/promise-pool-3.2.0.tgz" integrity sha512-pj0cAALblTZBPtMltWOlZTQSLT07jIaFNeM8TWoJD1cQMgDB9mcMlVMoetiB35OzNJpqQ2b+QEtwiR9f20mADg== -"@szmarczak/http-timer@^1.1.2": - version "1.1.2" - resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz" - integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== - dependencies: - defer-to-connect "^1.0.1" - "@szmarczak/http-timer@^4.0.5": version "4.0.6" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" + resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz" integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== dependencies: defer-to-connect "^2.0.0" +"@szmarczak/http-timer@^5.0.1": + version "5.0.1" + resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz" + integrity sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw== + dependencies: + defer-to-connect "^2.0.1" + +"@tootallnate/quickjs-emscripten@^0.23.0": + version "0.23.0" + resolved "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz" + integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA== + "@tsconfig/node10@^1.0.7": - version "1.0.8" - resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz" - integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== + version "1.0.9" + resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== "@tsconfig/node12@^1.0.7": - version "1.0.9" - resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz" - integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== + version "1.0.11" + resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== "@tsconfig/node14@^1.0.0": - version "1.0.1" - resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz" - integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== + version "1.0.3" + resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== "@tsconfig/node16@^1.0.2": - version "1.0.2" - resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz" - integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== + version "1.0.4" + resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@tufjs/canonical-json@2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz" + integrity sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA== + +"@tufjs/models@2.0.1": + version "2.0.1" + resolved "https://registry.npmjs.org/@tufjs/models/-/models-2.0.1.tgz" + integrity sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg== + dependencies: + "@tufjs/canonical-json" "2.0.0" + minimatch "^9.0.4" "@types/cacheable-request@^6.0.1": version "6.0.3" - resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" + resolved "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz" integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw== dependencies: "@types/http-cache-semantics" "*" @@ -1227,178 +2997,290 @@ "@types/caseless@*": version "0.12.5" - resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.5.tgz#db9468cb1b1b5a925b8f34822f1669df0c5472f5" + resolved "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz" integrity sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg== -"@types/chai@*", "@types/chai@^4": - version "4.2.15" - resolved "https://registry.npmjs.org/@types/chai/-/chai-4.2.15.tgz" - integrity sha512-rYff6FI+ZTKAPkJUoyz7Udq3GaoDZnxYDEvdEdFZASiA7PoErltHezDishqQiSDWrGxvxmplH304jyzQmjp0AQ== +"@types/chai@^4.3.14": + version "4.3.14" + resolved "https://registry.npmjs.org/@types/chai/-/chai-4.3.14.tgz" + integrity sha512-Wj71sXE4Q4AkGdG9Tvq1u/fquNz9EdG4LIJMwVVII7ashjD/8cf8fyIfJAjRr6YcsXnSE8cOGQPq1gqeR8z+3w== + +"@types/cli-progress@^3.11.5": + version "3.11.5" + resolved "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.5.tgz" + integrity sha512-D4PbNRbviKyppS5ivBGyFO29POlySLmA2HyUFE4p5QGazAMM3CwkKWcvTl8gvElSuxRh6FPKL8XmidX873ou4g== + dependencies: + "@types/node" "*" + +"@types/columnify@^1.5.4": + version "1.5.4" + resolved "https://registry.npmjs.org/@types/columnify/-/columnify-1.5.4.tgz" + integrity sha512-YPEVzmy3kJupUee1ueLuvGspy6U2JHcxt6rYvRsSCEgVC54+KdBFjQ6NG/0koZk69e1bfXwSusgChwdFhvEXMw== + +"@types/configstore@*": + version "6.0.2" + resolved "https://registry.npmjs.org/@types/configstore/-/configstore-6.0.2.tgz" + integrity sha512-OS//b51j9uyR3zvwD04Kfs5kHpve2qalQ18JhY/ho3voGYUTPLEG90/ocfKPI48hyHH8T04f7KEEbK6Ue60oZQ== + +"@types/cosmiconfig@^6.0.0": + version "6.0.0" + resolved "https://registry.npmjs.org/@types/cosmiconfig/-/cosmiconfig-6.0.0.tgz" + integrity sha512-KxKYXK5K1W+SVj1wjBBlwUs43K2D4iteye+r2ObPPQ7+NVASxSPfTb5H8iPW0bLswapMvaA4YMnxdKx7M4k29A== + dependencies: + cosmiconfig "*" + +"@types/cross-spawn@^6.0.6": + version "6.0.6" + resolved "https://registry.npmjs.org/@types/cross-spawn/-/cross-spawn-6.0.6.tgz" + integrity sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA== + dependencies: + "@types/node" "*" "@types/dompurify@^3.0.5": version "3.0.5" - resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-3.0.5.tgz#02069a2fcb89a163bacf1a788f73cb415dd75cb7" + resolved "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz" integrity sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg== dependencies: "@types/trusted-types" "*" -"@types/find-cache-dir@^3.2.1": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@types/find-cache-dir/-/find-cache-dir-3.2.1.tgz#7b959a4b9643a1e6a1a5fe49032693cc36773501" - integrity sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw== +"@types/ejs@^3.1.4": + version "3.1.5" + resolved "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz" + integrity sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg== + +"@types/expect@^1.20.4": + version "1.20.4" + resolved "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz" + integrity sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg== + +"@types/extract-zip@^2.0.1": + version "2.0.1" + resolved "https://registry.npmjs.org/@types/extract-zip/-/extract-zip-2.0.1.tgz" + integrity sha512-Rvy84OCUrbGquSMH2a2ifbHB3CEu95LguiihLf0gc4uFaZ7psB6Khq+s8a7rsPGztSlxihu1om7+ZVxBh8iJcg== + dependencies: + extract-zip "*" -"@types/fs-extra@^9.0.13": - version "9.0.13" - resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45" - integrity sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA== +"@types/fs-extra@^11.0.4": + version "11.0.4" + resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz" + integrity sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ== dependencies: + "@types/jsonfile" "*" "@types/node" "*" -"@types/glob@^7.1.1": - version "7.1.3" - resolved "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz" - integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w== +"@types/fs-readdir-recursive@^1.1.3": + version "1.1.3" + resolved "https://registry.npmjs.org/@types/fs-readdir-recursive/-/fs-readdir-recursive-1.1.3.tgz" + integrity sha512-2v5JKYQO+14CfurtdaL1cbLrjBeFjmcLkD35zDkaaytYSY/57jb2Kz6FbfJ1k+Lx2aaS0zpTR1dwCyqLkjo9vQ== + +"@types/glob@^8.1.0": + version "8.1.0" + resolved "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz" + integrity sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w== dependencies: - "@types/minimatch" "*" + "@types/minimatch" "^5.1.2" "@types/node" "*" -"@types/graceful-fs@^4.1.5": - version "4.1.5" - resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz" - integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== +"@types/glob@~7.2.0": + version "7.2.0" + resolved "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz" + integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== dependencies: + "@types/minimatch" "*" "@types/node" "*" -"@types/http-cache-semantics@*": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" - integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== +"@types/http-cache-semantics@*", "@types/http-cache-semantics@^4.0.2": + version "4.0.4" + resolved "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz" + integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== -"@types/is-stream@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@types/is-stream/-/is-stream-1.1.0.tgz#b84d7bb207a210f2af9bed431dc0fbe9c4143be1" - integrity sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg== +"@types/inquirer@^9.0.3", "@types/inquirer@^9.0.7": + version "9.0.7" + resolved "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.7.tgz" + integrity sha512-Q0zyBupO6NxGRZut/JdmqYKOnN95Eg5V8Csg3PGKkP+FnvsUZx1jAyK7fztIszxxMuoBA6E3KXWvdZVXIpx60g== dependencies: - "@types/node" "*" + "@types/through" "*" + rxjs "^7.2.0" "@types/jira-client@^7.1.9": version "7.1.9" - resolved "https://registry.yarnpkg.com/@types/jira-client/-/jira-client-7.1.9.tgz#ced37c8c1b37b09afe203e30272983c557ee52a4" + resolved "https://registry.npmjs.org/@types/jira-client/-/jira-client-7.1.9.tgz" integrity sha512-THFFlN8cwCotwHhRJGvjXKdv2AR7WFVsBOkr4tVB+PAY5T4kpAsj1dkwdOEAyyVrRKPM5x+Q4TuO/Xl6e57VQw== dependencies: "@types/node" "*" "@types/request" "*" -"@types/jsforce@^1.9.35": - version "1.9.38" - resolved "https://registry.npmjs.org/@types/jsforce/-/jsforce-1.9.38.tgz" - integrity sha512-+Iwf5jlDiK8z+zI2LAi4mzln8++5lETv2YofFEATu+dNkrP8LACB76lz2tPsXDx/a+5uW8HQhbwL/SyqIic0cg== +"@types/js-yaml@^4.0.9": + version "4.0.9" + resolved "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz" + integrity sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg== + +"@types/json-schema@^7.0.12", "@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + +"@types/jsonfile@*": + version "6.1.4" + resolved "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz" + integrity sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ== dependencies: "@types/node" "*" -"@types/json-schema@^7.0.3": - version "7.0.7" - resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz" - integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== - "@types/keyv@^3.1.4": version "3.1.4" - resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" + resolved "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz" integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== dependencies: "@types/node" "*" -"@types/lodash-es@^4.17.6": - version "4.17.6" - resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.6.tgz#c2ed4c8320ffa6f11b43eb89e9eaeec65966a0a0" - integrity sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg== +"@types/lodash-es@^4.17.9": + version "4.17.12" + resolved "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz" + integrity sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash.difference@^4.5.9": + version "4.5.9" + resolved "https://registry.npmjs.org/@types/lodash.difference/-/lodash.difference-4.5.9.tgz" + integrity sha512-MNlajcjtwzLpXk+cw38UkBvEXJNEPhULgS8A4EHwtUwT7f7yFH/SFKD0iw5Rfilwh60yJIgFo0vsMr7xsa5+aw== dependencies: "@types/lodash" "*" "@types/lodash@*": - version "4.14.168" - resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.168.tgz" - integrity sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q== + version "4.14.201" + resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.201.tgz" + integrity sha512-y9euML0cim1JrykNxADLfaG0FgD1g/yTHwUs/Jg9ZIU7WKj2/4IW9Lbb1WZbvck78W/lfGXFfe+u2EGfIJXdLQ== -"@types/minimatch@*": - version "3.0.3" - resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz" - integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== +"@types/make-fetch-happen@^10.0.4": + version "10.0.4" + resolved "https://registry.npmjs.org/@types/make-fetch-happen/-/make-fetch-happen-10.0.4.tgz" + integrity sha512-jKzweQaEMMAi55ehvR1z0JF6aSVQm/h1BXBhPLOJriaeQBctjw5YbpIGs7zAx9dN0Sa2OO5bcXwCkrlgenoPEA== + dependencies: + "@types/node-fetch" "*" + "@types/retry" "*" + "@types/ssri" "*" -"@types/minimatch@^3.0.3": - version "3.0.5" - resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz" - integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== +"@types/minimatch@*", "@types/minimatch@^5.1.2": + version "5.1.2" + resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz" + integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== -"@types/mkdirp@^1.0.1": - version "1.0.2" - resolved "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-1.0.2.tgz" - integrity sha512-o0K1tSO0Dx5X6xlU5F1D6625FawhC3dU3iqr25lluNv/+/QIVH8RLNEiVokgIZo+mz+87w/3Mkg/VvQS+J51fQ== - dependencies: - "@types/node" "*" +"@types/minimist@^1.2.0": + version "1.2.5" + resolved "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz" + integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== -"@types/mocha@^8.2.1": - version "8.2.1" - resolved "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.1.tgz" - integrity sha512-NysN+bNqj6E0Hv4CTGWSlPzMW6vTKjDpOteycDkV4IWBsO+PU48JonrPzV9ODjiI2XrjmA05KInLgF5ivZ/YGQ== +"@types/mocha@^10.0.6": + version "10.0.6" + resolved "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz" + 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" + resolved "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz" integrity sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow== dependencies: "@types/node" "*" -"@types/node-fetch@^2.6.4": +"@types/node-fetch@*", "@types/node-fetch@^2.6.4": version "2.6.11" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.11.tgz#9b39b78665dae0e82a08f02f4967d62c66f95d24" + resolved "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz" integrity sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g== dependencies: "@types/node" "*" form-data "^4.0.0" -"@types/node@*", "@types/node@^14.14.32": - version "14.14.32" - resolved "https://registry.npmjs.org/@types/node/-/node-14.14.32.tgz" - integrity sha512-/Ctrftx/zp4m8JOujM5ZhwzlWLx22nbQJiVqz8/zE15gOeEW+uly3FSX4fGFpcfEvFzXcMCJwq9lGVWgyARXhg== +"@types/node@*": + version "20.12.7" + resolved "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz" + integrity sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg== + dependencies: + undici-types "~5.26.4" -"@types/node@>=12.0.0": - version "20.6.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.6.0.tgz#9d7daa855d33d4efec8aea88cd66db1c2f0ebe16" - integrity sha512-najjVq5KN2vsH2U/xyh2opaSEz6cZMR2SetLIlxlj08nOcmPOemJmUK2o4kUzfLqfrWE0PIrNeE16XhYDd3nqg== +"@types/node@20.5.1": + version "20.5.1" + resolved "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz" + integrity sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg== + +"@types/node@>=18.0.0", "@types/node@^22.1.0": + version "22.5.0" + resolved "https://registry.npmjs.org/@types/node/-/node-22.5.0.tgz" + integrity sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg== + dependencies: + undici-types "~6.19.2" "@types/node@^14.0.1": version "14.18.63" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.63.tgz#1788fa8da838dbb5f9ea994b834278205db6ca2b" + resolved "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz" integrity sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ== -"@types/node@^18.11.18": - version "18.19.39" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.39.tgz#c316340a5b4adca3aee9dcbf05de385978590593" - integrity sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ== +"@types/node@^16.18.26", "@types/node@^16.18.28", "@types/node@^16.18.31": + version "16.18.106" + resolved "https://registry.npmjs.org/@types/node/-/node-16.18.106.tgz" + integrity sha512-YTgQUcpdXRc7iiEMutkkXl9WUx5lGUCVYvnfRg9CV+IA4l9epctEhCTbaw4KgzXaKYv8emvFJkEM65+MkNUhsQ== + +"@types/node@^18.11.18", "@types/node@^18.18.5": + version "18.19.47" + resolved "https://registry.npmjs.org/@types/node/-/node-18.19.47.tgz" + integrity sha512-1f7dB3BL/bpd9tnDJrrHb66Y+cVrhxSOTGorRNdHwYTUlTay3HuTDPKo9a/4vX9pMQkhYBcAbL4jQdNlhCFP9A== + dependencies: + undici-types "~5.26.4" + +"@types/node@^18.19.32": + version "18.19.34" + resolved "https://registry.npmjs.org/@types/node/-/node-18.19.34.tgz" + integrity sha512-eXF4pfBNV5DAMKGbI02NnDtWrQ40hAN558/2vvS4gMpMIxaf6JmD7YjnZbq0Q9TDSSkKBamime8ewRoomHdt4g== + dependencies: + undici-types "~5.26.4" + +"@types/node@^20.14.9": + version "20.14.10" + resolved "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz" + integrity sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ== dependencies: undici-types "~5.26.4" -"@types/node@^22.1.0": - version "22.4.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.4.1.tgz#9b595d292c65b94c20923159e2ce947731b6fdce" - integrity sha512-1tbpb9325+gPnKK0dMm+/LMriX0vKxf6RnB0SZUqfyVkQ4fMgUSySqhxE/y8Jvs4NyF1yHzTfG9KlnkIODxPKg== +"@types/node@^20.8.3": + version "20.16.2" + resolved "https://registry.npmjs.org/@types/node/-/node-20.16.2.tgz" + integrity sha512-91s/n4qUPV/wg8eE9KHYW1kouTfDk2FPGjXbBMfRWP/2vg1rCXNQL1OCabwGs0XSdukuK+MwCDXE30QpSeMUhQ== dependencies: undici-types "~6.19.2" -"@types/normalize-package-data@^2.4.0": - version "2.4.0" - resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz" - integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== +"@types/node@^22.5.2": + version "22.5.4" + resolved "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz" + integrity sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg== + dependencies: + undici-types "~6.19.2" -"@types/parse-json@^4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz" - integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/normalize-package-data@^2.4.0", "@types/normalize-package-data@^2.4.3": + version "2.4.4" + resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz" + integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== + +"@types/papaparse@^5.3.14": + version "5.3.14" + resolved "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.14.tgz" + integrity sha512-LxJ4iEFcpqc6METwp9f6BV6VVc43m6MfH0VqFosHvrUgfXiFe6ww7R3itkOQ+TCK6Y+Iv/+RnnvtRZnkc5Kc9g== + dependencies: + "@types/node" "*" + +"@types/psl@^1.1.3": + version "1.1.3" + resolved "https://registry.npmjs.org/@types/psl/-/psl-1.1.3.tgz" + integrity sha512-Iu174JHfLd7i/XkXY6VDrqSlPvTDQOtQI7wNAXKKOAADJ9TduRLkNdMgjGiMxSttUIZnomv81JAbAbC0DhggxA== "@types/request@*": version "2.48.12" - resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.12.tgz#0f590f615a10f87da18e9790ac94c29ec4c5ef30" + resolved "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz" integrity sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw== dependencies: "@types/caseless" "*" @@ -1407,154 +3289,278 @@ form-data "^2.5.0" "@types/responselike@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" - integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + version "1.0.3" + resolved "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz" + integrity sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw== dependencies: "@types/node" "*" +"@types/retry@*": + version "0.12.5" + resolved "https://registry.npmjs.org/@types/retry/-/retry-0.12.5.tgz" + integrity sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw== + "@types/retry@0.12.0": version "0.12.0" - resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" + resolved "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz" integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== -"@types/semver@^7.3.12": - version "7.3.13" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" - integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== +"@types/semver@^7.5.0": + version "7.5.6" + resolved "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz" + integrity sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A== + +"@types/set-value@^4.0.3": + version "4.0.3" + resolved "https://registry.npmjs.org/@types/set-value/-/set-value-4.0.3.tgz" + integrity sha512-tSuUcLl6kMzI+l0gG7FZ04xbIcynxNIYgWFj91LPAvRcn7W3L1EveXNdVjqFDgAZPjY1qCOsm8Sb1C70SxAPHw== + +"@types/shelljs@^0.8.15": + version "0.8.15" + resolved "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.8.15.tgz" + integrity sha512-vzmnCHl6hViPu9GNLQJ+DZFd6BQI2DBTUeOvYHqkWQLMfKAAQYMb/xAmZkTogZI/vqXHCWkqDRymDI5p0QTi5Q== + dependencies: + "@types/glob" "~7.2.0" + "@types/node" "*" -"@types/sinon@*": - version "9.0.11" - resolved "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.11.tgz" - integrity sha512-PwP4UY33SeeVKodNE37ZlOsR9cReypbMJOhZ7BVE0lB+Hix3efCOxiJWiE5Ia+yL9Cn2Ch72EjFTRze8RZsNtg== +"@types/sinon@^10.0.20": + version "10.0.20" + resolved "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.20.tgz" + integrity sha512-2APKKruFNCAZgx3daAyACGzWuJ028VVCUDk6o2rw/Z4PXT0ogwdV4KUegW0MwVs0Zu59auPXbbuBJHF12Sx1Eg== dependencies: "@types/sinonjs__fake-timers" "*" "@types/sinonjs__fake-timers@*": - version "6.0.2" - resolved "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz" - integrity sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg== + version "8.1.5" + resolved "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz" + integrity sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ== + +"@types/sort-array@^4.1.2": + version "4.1.2" + resolved "https://registry.npmjs.org/@types/sort-array/-/sort-array-4.1.2.tgz" + integrity sha512-3HGt90YX0/YvNgTOs8fff33YkyOq4tOqNZ1/dMJBpi4dlzD3nxjeRKecpaB3M3gLSwNB1kocuSGMD8cCtLe7vQ== + +"@types/ssri@*": + version "7.1.5" + resolved "https://registry.npmjs.org/@types/ssri/-/ssri-7.1.5.tgz" + integrity sha512-odD/56S3B51liILSk5aXJlnYt99S6Rt9EFDDqGtJM26rKHApHcwyU/UoYHrzKkdkHMAIquGWCuHtQTbes+FRQw== + dependencies: + "@types/node" "*" + +"@types/through@*": + version "0.0.33" + resolved "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz" + integrity sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ== + dependencies: + "@types/node" "*" "@types/tough-cookie@*": version "4.0.5" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" + resolved "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz" integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== "@types/trusted-types@*": version "2.0.7" - resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" + resolved "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz" integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== +"@types/update-notifier@^6.0.8": + version "6.0.8" + resolved "https://registry.npmjs.org/@types/update-notifier/-/update-notifier-6.0.8.tgz" + integrity sha512-IlDFnfSVfYQD+cKIg63DEXn3RFmd7W1iYtKQsJodcHK9R1yr8aKbKaPKfBxzPpcHCq2DU8zUq4PIPmy19Thjfg== + dependencies: + "@types/configstore" "*" + boxen "^7.1.1" + +"@types/vinyl@^2.0.7", "@types/vinyl@^2.0.8": + version "2.0.12" + resolved "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.12.tgz" + integrity sha512-Sr2fYMBUVGYq8kj3UthXFAu5UN6ZW+rYr4NACjZQJvHvj+c8lYv0CahmZ2P/r7iUkN44gGUBwqxZkrKXYPb7cw== + dependencies: + "@types/expect" "^1.20.4" + "@types/node" "*" + +"@types/which@^3.0.4": + version "3.0.4" + resolved "https://registry.npmjs.org/@types/which/-/which-3.0.4.tgz" + integrity sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w== + "@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" + resolved "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz" integrity sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g== -"@types/ws@^7.4.0": - version "7.4.0" - resolved "https://registry.npmjs.org/@types/ws/-/ws-7.4.0.tgz" - integrity sha512-Y29uQ3Uy+58bZrFLhX36hcI3Np37nqWE7ky5tjiDoy1GDZnIwVxS0CgF+s+1bXMzjKBFy+fqaRfb708iNzdinw== +"@types/ws@^8.5.12": + version "8.5.12" + resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz" + integrity sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ== dependencies: "@types/node" "*" -"@types/yarnpkg__lockfile@^1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@types/yarnpkg__lockfile/-/yarnpkg__lockfile-1.1.5.tgz#9639020e1fb65120a2f4387db8f1e8b63efdf229" - integrity sha512-8NYnGOctzsI4W0ApsP/BIHD/LnxpJ6XaGf2AZmz4EyDYJMxtprN4279dLNI1CPZcwC9H18qYcaFv4bXi0wmokg== +"@types/xml2js@^0.4.14": + version "0.4.14" + resolved "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz" + integrity sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ== + dependencies: + "@types/node" "*" "@types/yauzl@^2.9.1": - version "2.9.1" - resolved "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz" - integrity sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA== + version "2.10.3" + resolved "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz" + integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@^4.17.0": - version "4.17.0" - resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.17.0.tgz" - integrity sha512-/fKFDcoHg8oNan39IKFOb5WmV7oWhQe1K6CDaAVfJaNWEhmfqlA24g+u1lqU5bMH7zuNasfMId4LaYWC5ijRLw== +"@typescript-eslint/eslint-plugin@^6.21.0": + version "6.21.0" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz" + integrity sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA== dependencies: - "@typescript-eslint/experimental-utils" "4.17.0" - "@typescript-eslint/scope-manager" "4.17.0" - debug "^4.1.1" - functional-red-black-tree "^1.0.1" - lodash "^4.17.15" - regexpp "^3.0.0" - semver "^7.3.2" - tsutils "^3.17.1" - -"@typescript-eslint/experimental-utils@4.17.0": - version "4.17.0" - resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.17.0.tgz" - integrity sha512-ZR2NIUbnIBj+LGqCFGQ9yk2EBQrpVVFOh9/Kd0Lm6gLpSAcCuLLe5lUCibKGCqyH9HPwYC0GIJce2O1i8VYmWA== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.17.0" - "@typescript-eslint/types" "4.17.0" - "@typescript-eslint/typescript-estree" "4.17.0" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" - -"@typescript-eslint/parser@^4.17.0": - version "4.17.0" - resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.17.0.tgz" - integrity sha512-KYdksiZQ0N1t+6qpnl6JeK9ycCFprS9xBAiIrw4gSphqONt8wydBw4BXJi3C11ywZmyHulvMaLjWsxDjUSDwAw== - dependencies: - "@typescript-eslint/scope-manager" "4.17.0" - "@typescript-eslint/types" "4.17.0" - "@typescript-eslint/typescript-estree" "4.17.0" - debug "^4.1.1" + "@eslint-community/regexpp" "^4.5.1" + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/type-utils" "6.21.0" + "@typescript-eslint/utils" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.4" + natural-compare "^1.4.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/parser@^6.21.0": + version "6.21.0" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz" + integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== + dependencies: + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/typescript-estree" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" + debug "^4.3.4" -"@typescript-eslint/scope-manager@4.17.0": - version "4.17.0" - resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.17.0.tgz" - integrity sha512-OJ+CeTliuW+UZ9qgULrnGpPQ1bhrZNFpfT/Bc0pzNeyZwMik7/ykJ0JHnQ7krHanFN9wcnPK89pwn84cRUmYjw== +"@typescript-eslint/scope-manager@6.21.0": + version "6.21.0" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz" + integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg== dependencies: - "@typescript-eslint/types" "4.17.0" - "@typescript-eslint/visitor-keys" "4.17.0" - -"@typescript-eslint/types@4.17.0": - version "4.17.0" - resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.17.0.tgz" - integrity sha512-RN5z8qYpJ+kXwnLlyzZkiJwfW2AY458Bf8WqllkondQIcN2ZxQowAToGSd9BlAUZDB5Ea8I6mqL2quGYCLT+2g== + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" -"@typescript-eslint/typescript-estree@4.17.0": - version "4.17.0" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.17.0.tgz" - integrity sha512-lRhSFIZKUEPPWpWfwuZBH9trYIEJSI0vYsrxbvVvNyIUDoKWaklOAelsSkeh3E2VBSZiNe9BZ4E5tYBZbUczVQ== +"@typescript-eslint/type-utils@6.21.0": + version "6.21.0" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz" + integrity sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag== dependencies: - "@typescript-eslint/types" "4.17.0" - "@typescript-eslint/visitor-keys" "4.17.0" - debug "^4.1.1" - globby "^11.0.1" - is-glob "^4.0.1" - semver "^7.3.2" - tsutils "^3.17.1" + "@typescript-eslint/typescript-estree" "6.21.0" + "@typescript-eslint/utils" "6.21.0" + debug "^4.3.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/types@6.21.0": + version "6.21.0" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz" + integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== -"@typescript-eslint/visitor-keys@4.17.0": - version "4.17.0" - resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.17.0.tgz" - integrity sha512-WfuMN8mm5SSqXuAr9NM+fItJ0SVVphobWYkWOwQ1odsfC014Vdxk/92t4JwS1Q6fCA/ABfCKpa3AVtpUKTNKGQ== +"@typescript-eslint/typescript-estree@6.21.0": + version "6.21.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz" + integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ== dependencies: - "@typescript-eslint/types" "4.17.0" - eslint-visitor-keys "^2.0.0" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + minimatch "9.0.3" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/utils@6.21.0", "@typescript-eslint/utils@^6.17.0": + version "6.21.0" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz" + integrity sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@types/json-schema" "^7.0.12" + "@types/semver" "^7.5.0" + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/typescript-estree" "6.21.0" + semver "^7.5.4" + +"@typescript-eslint/visitor-keys@6.21.0": + version "6.21.0" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz" + integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A== + dependencies: + "@typescript-eslint/types" "6.21.0" + eslint-visitor-keys "^3.4.1" + +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== -"@ungap/promise-all-settled@1.1.2": - version "1.1.2" - resolved "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz" - integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== +"@xmldom/xmldom@^0.8.10": + version "0.8.10" + resolved "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz" + integrity sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw== + +"@yeoman/adapter@^1.4.0": + version "1.5.0" + resolved "https://registry.npmjs.org/@yeoman/adapter/-/adapter-1.5.0.tgz" + integrity sha512-CHfxtCRFVsR27YcEpiYnKgk0x9T2V7324XntnkBPZncKMJnW+Y164motqqHg+FjwG8NcOUAeom4b6N6/+2ksNA== + dependencies: + "@types/inquirer" "^9.0.3" + chalk "^5.2.0" + inquirer "^9.2.2" + log-symbols "^7.0.0" + ora "^8.1.0" + p-queue "^8.0.1" + text-table "^0.2.0" -"@xmldom/xmldom@^0.8.6": - version "0.8.6" - resolved "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.6.tgz" - integrity sha512-uRjjusqpoqfmRkTaNuLJ2VohVr67Q5YwDATW3VU7PfzTj6IRaihGrYI7zckGZjxQPBIp63nfvJbM+Yu5ICh0Bg== +"@yeoman/conflicter@^2.0.0-alpha.2": + version "2.1.0" + resolved "https://registry.npmjs.org/@yeoman/conflicter/-/conflicter-2.1.0.tgz" + integrity sha512-+RRxlmZTu44X8bc+7wWPKci38tbtY2Tb4eQLzHIwVrAOEuw+amgG+oTf8h20U3ccRVEBdSkxRbrf4z7zYhdmKg== + dependencies: + "@types/node" "^16.18.28" + "@yeoman/transform" "^1.2.0" + binary-extensions "^2.2.0" + cli-table "^0.3.11" + dateformat "^5.0.3" + diff "^5.1.0" + isbinaryfile "^5.0.0" + mem-fs-editor "^11.0.0" + minimatch "^9.0.0" + p-transform "^4.1.3" + pretty-bytes "^6.1.0" + slash "^5.1.0" + textextensions "^6.11.0" + +"@yeoman/namespace@^1.0.0": + version "1.0.1" + resolved "https://registry.npmjs.org/@yeoman/namespace/-/namespace-1.0.1.tgz" + integrity sha512-XGdYL0HCoPvrzW7T8bxD6RbCY/B8uvR2jpOzJc/yEwTueKHwoVhjSLjVXkokQAO0LNl8nQFLVZ1aKfr2eFWZeA== -"@yarnpkg/lockfile@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" - integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== +"@yeoman/transform@^1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@yeoman/transform/-/transform-1.2.0.tgz" + integrity sha512-evb/+2XMEBoHr4BxBeFkjeVTgTS4Qe7VH8DmzZ9kgJK7C7ACPAhW/qBdsKKP1sb5MoeITSaJSVFnc8S1fjZmcw== + dependencies: + "@types/node" "^16.18.28" + minimatch "^9.0.0" + readable-stream "^4.3.0" + +"@yeoman/types@^1.1.1": + version "1.3.0" + resolved "https://registry.npmjs.org/@yeoman/types/-/types-1.3.0.tgz" + integrity sha512-7tcSHdlo1koZg/eMaBMIhC6Z+sg6AIOcjTOvzkKf4BtsTgSPHo/N7HGgYOq2RjGYFyWzKVEK3EoOxXrYqK1msA== + dependencies: + "@types/node" "^16.18.26" -JSONStream@^1.2.1, JSONStream@^1.3.5: +JSONStream@^1.3.5: version "1.3.5" resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== @@ -1562,6 +3568,11 @@ JSONStream@^1.2.1, JSONStream@^1.3.5: jsonparse "^1.2.0" through ">=2.2.7 <3" +abbrev@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz" + integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz" @@ -1569,25 +3580,20 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" -acorn-jsx@^5.3.1: - version "5.3.1" - resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz" - integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^8.1.1: - version "8.2.0" - resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz" - integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== - -acorn@^7.4.0: - version "7.4.1" - resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + version "8.3.0" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz" + integrity sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA== -acorn@^8.4.1: - version "8.7.1" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz" - integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== +acorn@^8.4.1, acorn@^8.9.0: + version "8.11.2" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz" + integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w== agent-base@6: version "6.0.2" @@ -1596,16 +3602,23 @@ agent-base@6: dependencies: debug "4" -agent-base@^7.0.2, agent-base@^7.1.0: +agent-base@^7.0.2: version "7.1.0" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz" integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg== dependencies: debug "^4.3.4" +agent-base@^7.1.0, agent-base@^7.1.1: + version "7.1.1" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + agentkeepalive@^4.2.1: version "4.5.0" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" + resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz" integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== dependencies: humanize-ms "^1.2.1" @@ -1618,7 +3631,7 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: +ajv@^6.12.3, ajv@^6.12.4: version "6.12.6" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -1628,117 +3641,117 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^7.0.2: - version "7.2.1" - resolved "https://registry.npmjs.org/ajv/-/ajv-7.2.1.tgz" - integrity sha512-+nu0HDv7kNSOua9apAVc979qd932rrZeb3WOvoiD31A/p1mIE5/9bN2027pE2rOPYEdS3UHzsvof4hY+lM9/WQ== +ajv@^8.11.0: + version "8.12.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" require-from-string "^2.0.2" uri-js "^4.2.2" -ansi-align@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz" - integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw== +ajv@^8.15.0, ajv@^8.17.1: + version "8.17.1" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +ansi-align@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== dependencies: - string-width "^3.0.0" + string-width "^4.1.0" -ansi-colors@4.1.1, ansi-colors@^4.1.1: +ansi-colors@4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== -ansi-escapes@^3.1.0: - version "3.2.0" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz" - integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== - -ansi-escapes@^4.2.1, ansi-escapes@^4.3.2: +ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" -ansi-escapes@^4.3.0: - version "4.3.1" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz" - integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== +ansi-escapes@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz" + integrity sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA== dependencies: - type-fest "^0.11.0" + type-fest "^1.0.2" ansi-red@^0.1.1: version "0.1.1" resolved "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz" - integrity sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw= + integrity sha512-ewaIr5y+9CUTGFwZfpECUbFlGcC0GCw1oqR9RI6h1gQCd9Aj2GxSckCnPsVJnmfMZbwFYE+leZGASgkWl06Jow== dependencies: ansi-wrap "0.1.0" -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" - integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== - -ansi-regex@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" - integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== - -ansi-regex@^5.0.0, ansi-regex@^5.0.1: +ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-regex@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz" integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= +ansi-sequence-parser@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz" + integrity sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg== -ansi-styles@^3.0.0, ansi-styles@^3.2.0, ansi-styles@^3.2.1: +ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" -ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.2.0: +ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.3.0: version "4.3.0" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" -ansi-styles@^6.1.0: +ansi-styles@^6.1.0, ansi-styles@^6.2.1: version "6.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== ansi-wrap@0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz" - integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= + integrity sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw== ansicolors@~0.3.2: version "0.3.2" resolved "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz" - integrity sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk= + integrity sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg== -anymatch@~3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz" - integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== +ansis@^3.1.1: + version "3.2.0" + resolved "https://registry.npmjs.org/ansis/-/ansis-3.2.0.tgz" + integrity sha512-Yk3BkHH9U7oPyCN3gL5Tc7CpahG/+UFv/6UG03C311Vy9lzRmA5uoxDTpU9CO3rGHL6KzJz/pdDeXZCZ5Mu/Sg== + +ansis@^3.3.1, ansis@^3.3.2: + version "3.3.2" + resolved "https://registry.npmjs.org/ansis/-/ansis-3.3.2.tgz" + integrity sha512-cFthbBlt+Oi0i9Pv/j6YdVWJh54CtjGACaMPCIrEV4Ha7HWsIjXDwseYV79TIL0B4+KfSwD5S70PeQDkPUd1rA== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" @@ -1752,7 +3765,7 @@ append-transform@^2.0.0: archiver-utils@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2" + resolved "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz" integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw== dependencies: glob "^7.1.4" @@ -1768,7 +3781,7 @@ archiver-utils@^2.1.0: archiver-utils@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-3.0.4.tgz#a0d201f1cf8fce7af3b5a05aea0a337329e96ec7" + resolved "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz" integrity sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw== dependencies: glob "^7.2.3" @@ -1784,7 +3797,7 @@ archiver-utils@^3.0.4: archiver@^5.0.0: version "5.3.2" - resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.2.tgz#99991d5957e53bd0303a392979276ac4ddccf3b0" + resolved "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz" integrity sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw== dependencies: archiver-utils "^2.1.0" @@ -1798,7 +3811,12 @@ archiver@^5.0.0: archy@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz" - integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= + integrity sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw== + +are-docs-informative@^0.0.2: + version "0.0.2" + resolved "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz" + integrity sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig== arg@^4.1.0: version "4.1.3" @@ -1817,274 +3835,327 @@ argparse@^2.0.1: resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= - -arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= - array-back@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz" integrity sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw== -array-differ@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz" - integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" -array-from@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz" - integrity sha1-z+nYwmYoudxa7MYqn12PHzUsEZU= +array-differ@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/array-differ/-/array-differ-4.0.0.tgz" + integrity sha512-Q6VPTLMsmXZ47ENG3V+wQyZS1ZxXMxFyYzA+Z/GMrJ6yIutAIEf9wTyroTzmGjNfox9/h3GdGBCVh43GVFx4Uw== -array-union@^1.0.1, array-union@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz" - integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= +array-ify@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz" + integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== + +array-includes@^3.1.7: + version "3.1.7" + resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz" + integrity sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ== dependencies: - array-uniq "^1.0.1" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-string "^1.0.7" array-union@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array-uniq@^1.0.1: - version "1.0.3" - resolved "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz" - integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= +array-union@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz" + integrity sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw== -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz" - integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= +array.prototype.findlastindex@^1.2.3: + version "1.2.3" + resolved "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz" + integrity sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.2.1" + +array.prototype.flat@^1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +arraybuffer.prototype.slice@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz" + integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" arrify@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz" - integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== -arrify@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz" - integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== +arrify@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/arrify/-/arrify-3.0.0.tgz" + integrity sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw== -asap@*, asap@~2.0.3: +asap@*: version "2.0.6" resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" - integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + version "0.2.6" + resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== dependencies: safer-buffer "~2.1.0" assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== assertion-error@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +ast-types@^0.13.4: + version "0.13.4" + resolved "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz" + integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== + dependencies: + tslib "^2.0.1" astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -async@^2.6.2: - version "2.6.4" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" - integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== +async-retry@^1.3.3: + version "1.3.3" + resolved "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz" + integrity sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw== dependencies: - lodash "^4.17.14" + retry "0.13.1" async@^3.2.3: version "3.2.4" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + resolved "https://registry.npmjs.org/async/-/async-3.2.4.tgz" integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== async@^3.2.4: - version "3.2.5" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" - integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== + version "3.2.6" + resolved "https://registry.npmjs.org/async/-/async-3.2.6.tgz" + integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== asynckit@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -at-least-node@^1.0.0: +atomic-sleep@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz" - integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + resolved "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz" + integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +atomically@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/atomically/-/atomically-2.0.3.tgz" + integrity sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw== + dependencies: + stubborn-fs "^1.2.5" + when-exit "^2.1.1" autolinker@~0.28.0: version "0.28.1" resolved "https://registry.npmjs.org/autolinker/-/autolinker-0.28.1.tgz" - integrity sha1-BlK0kYgYefB3XazgzcoyM5QqTkc= + integrity sha512-zQAFO1Dlsn69eXaO6+7YZc+v84aquQKbwpzCE3L0stj56ERn9hutFxPopViLjo9G+rWwjozRhgS5KJ25Xy19cQ== dependencies: gulp-header "^1.7.1" +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== aws4@^1.12.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" - integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== - -aws4@^1.8.0: - version "1.11.0" - resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz" - integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== - -axios@^0.21.1: - version "0.21.4" - resolved "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz" - integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== - dependencies: - follow-redirects "^1.14.0" - -axios@^0.27.2: - version "0.27.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" - integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== - dependencies: - follow-redirects "^1.14.9" - form-data "^4.0.0" + version "1.13.1" + resolved "https://registry.npmjs.org/aws4/-/aws4-1.13.1.tgz" + integrity sha512-u5w79Rd7SU4JaIlA/zFqG+gOiuq25q5VLyZ8E+ijJeILuTxVzZgp2CaGw/UTw6pXYN9XMO9yiqj/nEHmhTG5CA== -axios@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.4.tgz#4c8ded1b43683c8dd362973c393f3ede24052aa2" - integrity sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw== +axios@^1.7.4, axios@^1.7.5: + version "1.7.5" + resolved "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz" + integrity sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw== dependencies: follow-redirects "^1.15.6" form-data "^4.0.0" proxy-from-env "^1.1.0" -azure-devops-node-api@^12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/azure-devops-node-api/-/azure-devops-node-api-12.0.0.tgz#38b9892f88e86da46246218411920923d8dd6a52" - integrity sha512-S6Il++7dQeMlZDokBDWw7YVoPeb90tWF10pYxnoauRMnkuL91jq9M7SOYRVhtO3FUC5URPkB/qzGa7jTLft0Xw== +azure-devops-node-api@^14.0.2: + version "14.0.2" + resolved "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-14.0.2.tgz" + integrity sha512-TwjAEnWnOSZ2oypkDyqppgvJw43qArEfPiJtEWLL3NBgdvAuOuB0xgFz/Eiz4H6Dk0Yv52wCodZxtZvAMhJXwQ== dependencies: tunnel "0.0.6" - typed-rest-client "^1.8.4" + typed-rest-client "^2.0.1" + +b4a@^1.6.4, b4a@^1.6.6: + version "1.6.6" + resolved "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz" + integrity sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg== balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.1.2, base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +bare-events@^2.0.0, bare-events@^2.2.0: + version "2.4.2" + resolved "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz" + integrity sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q== -base64-url@^2.2.0: +bare-fs@^2.1.1: version "2.3.3" - resolved "https://registry.npmjs.org/base64-url/-/base64-url-2.3.3.tgz" - integrity sha512-dLMhIsK7OplcDauDH/tZLvK7JmUZK3A7KiQpjNzsBrM6Etw7hzNI1tLEywqJk9NnwkgWuFKSlx/IUO7vF6Mo8Q== - -base@^0.11.1: - version "0.11.2" - resolved "https://registry.npmjs.org/base/-/base-0.11.2.tgz" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" + resolved "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.3.tgz" + integrity sha512-7RYKL+vZVCyAsMLi5SPu7QGauGGT8avnP/HO571ndEuV4MYdGXvLhtW67FuLPeEI8EiIY7zbbRR9x7x7HU0kgw== + dependencies: + bare-events "^2.0.0" + bare-path "^2.0.0" + bare-stream "^2.0.0" -bash-glob@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/bash-glob/-/bash-glob-2.0.0.tgz#a8ef19450783403ed93fccca2dbe09f2cf6320dc" - integrity sha512-53/NJ+t2UAkEYgQPO6aFjbx1Ue8vNNXCYaA4EljNKP1SR8A9dSQQoBmYWR8BLXO0/NDRJEMSJ4BxWihi//m3Kw== +bare-os@^2.1.0: + version "2.4.2" + resolved "https://registry.npmjs.org/bare-os/-/bare-os-2.4.2.tgz" + integrity sha512-HZoJwzC+rZ9lqEemTMiO0luOePoGYNBgsLLgegKR/cljiJvcDNhDZQkzC+NC5Oh0aHbdBNSOHpghwMuB5tqhjg== + +bare-path@^2.0.0, bare-path@^2.1.0: + version "2.1.3" + resolved "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz" + integrity sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA== dependencies: - bash-path "^1.0.1" - component-emitter "^1.2.1" - cross-spawn "^5.1.0" - each-parallel-async "^1.0.0" - extend-shallow "^2.0.1" - is-extglob "^2.1.1" - is-glob "^4.0.0" + bare-os "^2.1.0" -bash-path@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/bash-path/-/bash-path-1.0.3.tgz#dbc9efbdf18b1c11413dcb59b960e6aa56c84258" - integrity sha512-mGrYvOa6yTY/qNCiZkPFJqWmODK68y6kmVRAJ1NNbWlNoJrUrsFxu7FU2EKg7gbrer6ttrKkF2s/E/lhRy7/OA== +bare-stream@^2.0.0: + version "2.2.1" + resolved "https://registry.npmjs.org/bare-stream/-/bare-stream-2.2.1.tgz" + integrity sha512-YTB47kHwBW9zSG8LD77MIBAAQXjU2WjAkMHeeb7hUplVs6+IoM5I7uEVQNPMB7lj9r8I76UMdoMkGnCodHOLqg== dependencies: - arr-union "^3.1.0" - is-windows "^1.0.1" + b4a "^1.6.6" + streamx "^2.18.0" + +base64-js@^1.1.2, base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +base64url@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz" + integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== + +basic-ftp@^5.0.2: + version "5.0.5" + resolved "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz" + integrity sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg== bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== dependencies: tweetnacl "^0.14.3" before-after-hook@^2.1.0, before-after-hook@^2.2.0: version "2.2.3" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" + resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz" integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ== big-integer@^1.6.17: version "1.6.52" - resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85" + resolved "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz" integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg== +bin-links@^4.0.4: + version "4.0.4" + resolved "https://registry.npmjs.org/bin-links/-/bin-links-4.0.4.tgz" + integrity sha512-cMtq4W5ZsEwcutJrVId+a/tjt8GSbS+h0oNkdl6+6rBuEv8Ot33Bevj5KPm40t309zuhVic8NjpuL42QCiJWWA== + dependencies: + cmd-shim "^6.0.0" + npm-normalize-package-bin "^3.0.0" + read-cmd-shim "^4.0.0" + write-file-atomic "^5.0.0" + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +binary-extensions@^2.2.0: + version "2.3.0" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + binary@~0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79" + resolved "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz" integrity sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg== dependencies: buffers "~0.1.1" chainsaw "~0.1.0" -binaryextensions@^2.1.2: - version "2.3.0" - resolved "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.3.0.tgz" - integrity sha512-nAihlQsYGyc5Bwq6+EsubvANYGExeJKHDO3RjnvwU042fawQTQfM3Kxn7IHUXQOz4bzfwsGYYHGSvXyW4zOGLg== +binaryextensions@^6.11.0: + version "6.11.0" + resolved "https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz" + integrity sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw== + dependencies: + editions "^6.21.0" -bitbucket@^2.11.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/bitbucket/-/bitbucket-2.11.0.tgz#21d6f27f7e6acd1fa215d957aa81b369be5d2802" - integrity sha512-YoUekxzBybCnwbh+9IzKE0sEWIp/nz1JId+yGc5qXFCFN1cZnUCDSesWYQokG1NvXXNE4oPdRezhwgyjBhIckg== +bitbucket@^2.12.0: + version "2.12.0" + resolved "https://registry.npmjs.org/bitbucket/-/bitbucket-2.12.0.tgz" + integrity sha512-YqaiTPEmn5mkwdU2gGcJZcQ6B8/DhCHhc3SSYqSpnef6nSTTSa/2GSBoLEgPLqAuqrQITGKq8MgYkfDMtnJPuw== dependencies: before-after-hook "^2.1.0" deepmerge "^4.2.2" @@ -2103,27 +4174,46 @@ bl@^4.0.3, bl@^4.1.0: bluebird@^2.6.2: version "2.11.0" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" + resolved "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz" integrity sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ== bluebird@~3.4.1: version "3.4.7" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" + resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz" integrity sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA== -boxen@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/boxen/-/boxen-5.0.0.tgz" - integrity sha512-5bvsqw+hhgUi3oYGK0Vf4WpIkyemp60WBInn7+WNfoISzAqk/HX4L7WNROq38E6UR/y3YADpv6pEm4BfkeEAdA== +bowser@^2.11.0: + version "2.11.0" + resolved "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz" + integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA== + +boxen@^7.1.1: + version "7.1.1" + resolved "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz" + integrity sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog== dependencies: - ansi-align "^3.0.0" - camelcase "^6.2.0" - chalk "^4.1.0" - cli-boxes "^2.2.1" - string-width "^4.2.0" - type-fest "^0.20.2" - widest-line "^3.1.0" - wrap-ansi "^7.0.0" + ansi-align "^3.0.1" + camelcase "^7.0.1" + chalk "^5.2.0" + cli-boxes "^3.0.0" + string-width "^5.1.2" + type-fest "^2.13.0" + widest-line "^4.0.1" + wrap-ansi "^8.1.0" + +boxen@^8.0.0: + version "8.0.1" + resolved "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz" + integrity sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw== + dependencies: + ansi-align "^3.0.1" + camelcase "^8.0.0" + chalk "^5.3.0" + cli-boxes "^3.0.0" + string-width "^7.2.0" + type-fest "^4.21.0" + widest-line "^5.0.0" + wrap-ansi "^9.0.0" brace-expansion@^1.1.7: version "1.1.11" @@ -2135,37 +4225,28 @@ brace-expansion@^1.1.7: brace-expansion@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== dependencies: balanced-match "^1.0.0" -braces@^2.3.1: - version "2.3.2" - resolved "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" + fill-range "^7.0.1" -braces@^3.0.1, braces@^3.0.2, braces@~3.0.2: +braces@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" - integrity "sha1-SQMy9AkZRSJy1VqEgK3AxEE1h4k= sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: fill-range "^7.1.1" brotli@^1.3.3: version "1.3.3" - resolved "https://registry.yarnpkg.com/brotli/-/brotli-1.3.3.tgz#7365d8cc00f12cf765d2b2c898716bcf4b604d48" + resolved "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz" integrity sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg== dependencies: base64-js "^1.1.2" @@ -2175,35 +4256,34 @@ browser-stdout@1.3.1: resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browserslist@^4.14.5: - version "4.16.6" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz" - integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== +browserslist@^4.21.3, browserslist@^4.23.0: + version "4.23.0" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz" + integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== dependencies: - caniuse-lite "^1.0.30001219" - colorette "^1.2.2" - electron-to-chromium "^1.3.723" - escalade "^3.1.1" - node-releases "^1.1.71" + caniuse-lite "^1.0.30001587" + electron-to-chromium "^1.4.668" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== buffer-equal-constant-time@1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz" - integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + version "1.1.2" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== buffer-indexof-polyfill@~1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz#d2732135c5999c64b277fcf9b1abe3498254729c" + resolved "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz" integrity sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A== buffer@^5.2.1, buffer@^5.5.0: @@ -2214,48 +4294,83 @@ buffer@^5.2.1, buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + buffers@~0.1.1: version "0.1.1" - resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" + resolved "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz" integrity sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ== -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== +builtin-modules@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz" + integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== + +builtins@^5.0.0: + version "5.0.1" + resolved "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz" + integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ== dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" + semver "^7.0.0" + +bundle-name@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz" + integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== + dependencies: + run-applescript "^7.0.0" + +cacache@^18.0.0, cacache@^18.0.3: + version "18.0.4" + resolved "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz" + integrity sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ== + dependencies: + "@npmcli/fs" "^3.1.0" + fs-minipass "^3.0.0" + glob "^10.2.2" + lru-cache "^10.0.1" + minipass "^7.0.3" + minipass-collect "^2.0.1" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + p-map "^4.0.0" + ssri "^10.0.0" + tar "^6.1.11" + unique-filename "^3.0.0" cacheable-lookup@^5.0.3: version "5.0.4" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz" integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== -cacheable-request@^6.0.0: - version "6.1.0" - resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz" - integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^3.0.0" - lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^1.0.2" +cacheable-lookup@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz" + integrity sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w== + +cacheable-request@^10.2.8: + version "10.2.14" + resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz" + integrity sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ== + dependencies: + "@types/http-cache-semantics" "^4.0.2" + get-stream "^6.0.1" + http-cache-semantics "^4.1.1" + keyv "^4.5.3" + mimic-response "^4.0.0" + normalize-url "^8.0.0" + responselike "^3.0.0" cacheable-request@^7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" - integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== + version "7.0.4" + resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz" + integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg== dependencies: clone-response "^1.0.2" get-stream "^5.1.0" @@ -2275,48 +4390,86 @@ caching-transform@^4.0.0: package-hash "^4.0.0" write-file-atomic "^3.0.0" -call-bind@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" -call-me-maybe@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz" - integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= +call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" callsites@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== +camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + +camelcase-keys@^6.2.2: + version "6.2.2" + resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz" + integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== + dependencies: + camelcase "^5.3.1" + map-obj "^4.0.0" + quick-lru "^4.0.1" + camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.0.0, camelcase@^6.2.0: - version "6.2.0" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz" - integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== +camelcase@^6.0.0, camelcase@^6.2.1: + version "6.3.0" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +camelcase@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz" + integrity sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw== + +camelcase@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz" + integrity sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA== -caniuse-lite@^1.0.30001219: - version "1.0.30001232" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001232.tgz" - integrity sha512-e4Gyp7P8vqC2qV2iHA+cJNf/yqUKOShXQOJHQt81OHxlIZl/j/j3soEA0adAQi8CPUQgvOdDENyQ5kd6a6mNSg== +caniuse-lite@^1.0.30001587: + version "1.0.30001609" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001609.tgz" + integrity sha512-JFPQs34lHKx1B5t1EpQpWH4c+29zIyn/haGsbpfq3suuV9v56enjFt23zqijxGTMwy1p/4H2tjnQMY+p1WoAyA== -capture-stack-trace@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz" - integrity sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw== +capital-case@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz" + integrity sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + upper-case-first "^2.0.2" cardinal@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz" - integrity sha1-fMEFXYItISlU0HsIXeolHMe8VQU= + integrity sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw== dependencies: ansicolors "~0.3.2" redeyed "~2.1.0" @@ -2324,39 +4477,29 @@ cardinal@^2.1.1: caseless@~0.12.0: version "0.12.0" resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== -chai@^4: - version "4.3.3" - resolved "https://registry.npmjs.org/chai/-/chai-4.3.3.tgz" - integrity sha512-MPSLOZwxxnA0DhLE84klnGPojWFK5KuhP7/j5dTsxpr2S3XlkqJP5WbyYl1gCTWvG2Z5N+HD4F472WsbEZL6Pw== +chai@^4.3.10: + version "4.3.10" + resolved "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz" + integrity sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g== dependencies: assertion-error "^1.1.0" - check-error "^1.0.2" - deep-eql "^3.0.1" - get-func-name "^2.0.0" + check-error "^1.0.3" + deep-eql "^4.1.3" + get-func-name "^2.0.2" + loupe "^2.3.6" pathval "^1.1.1" - type-detect "^4.0.5" + type-detect "^4.0.8" chainsaw@~0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" + resolved "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz" integrity sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ== dependencies: traverse ">=0.3.0 <0.4" -chalk@^1.0.0: - version "1.1.3" - resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.4.2: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -2365,15 +4508,7 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz" - integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^4.0.2, chalk@^4.1.2: +chalk@^4, chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -2381,77 +4516,98 @@ chalk@^4.0.2, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^5.0.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.1.2.tgz#d957f370038b75ac572471e83be4c5ca9f8e8c45" - integrity sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ== +chalk@^5.0.0, chalk@^5.2.0, chalk@^5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + +change-case@^4, change-case@^4.1.2: + version "4.1.2" + resolved "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz" + integrity sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A== + dependencies: + camel-case "^4.1.2" + capital-case "^1.0.4" + constant-case "^3.0.4" + dot-case "^3.0.4" + header-case "^2.0.4" + no-case "^3.0.4" + param-case "^3.0.4" + pascal-case "^3.1.2" + path-case "^3.0.4" + sentence-case "^3.0.4" + snake-case "^3.0.4" + tslib "^2.0.3" chardet@^0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -check-error@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz" - integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= +check-error@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz" + integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== + dependencies: + get-func-name "^2.0.2" -chokidar@3.5.1: - version "3.5.1" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz" - integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== +chokidar@3.5.3, chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: - anymatch "~3.1.1" + anymatch "~3.1.2" braces "~3.0.2" - glob-parent "~5.1.0" + glob-parent "~5.1.2" is-binary-path "~2.1.0" is-glob "~4.0.1" normalize-path "~3.0.0" - readdirp "~3.5.0" + readdirp "~3.6.0" optionalDependencies: - fsevents "~2.3.1" + fsevents "~2.3.2" -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - -ci-info@^2.0.0: +chownr@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" - integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +chromium-bidi@0.6.5: + version "0.6.5" + resolved "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.6.5.tgz" + integrity sha512-RuLrmzYrxSb0s9SgpB+QN5jJucPduZQ/9SIe76MDxYJuecPW5mxMdacJ1f4EtgiV+R0p3sCkznTMvH0MPGFqjA== + dependencies: + mitt "3.0.1" + urlpattern-polyfill "10.0.0" + zod "3.23.8" -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== +ci-info@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz" + integrity sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg== + +clean-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz" + integrity sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw== dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" + escape-string-regexp "^1.0.5" clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -clean-stack@^3.0.0: +clean-stack@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz" integrity sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg== dependencies: escape-string-regexp "4.0.0" -cli-boxes@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz" - integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM= - -cli-boxes@^2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz" - integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== +cli-boxes@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz" + integrity sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g== cli-cursor@^3.1.0: version "3.1.0" @@ -2460,93 +4616,35 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-progress@^3.4.0: - version "3.9.0" - resolved "https://registry.npmjs.org/cli-progress/-/cli-progress-3.9.0.tgz" - integrity sha512-g7rLWfhAo/7pF+a/STFH/xPyosaL1zgADhI0OM83hl3c7S43iGvJWEAV2QuDOnQ8i6EMBj/u4+NTd0d5L+4JfA== +cli-cursor@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz" + integrity sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw== dependencies: - colors "^1.1.2" - string-width "^4.2.0" + restore-cursor "^5.0.0" + +cli-progress@^3.12.0: + version "3.12.0" + resolved "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz" + integrity sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A== + dependencies: + string-width "^4.2.3" cli-spinners@^2.5.0, cli-spinners@^2.9.2: version "2.9.2" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz" integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== -cli-table@^0.3.1: - version "0.3.6" - resolved "https://registry.npmjs.org/cli-table/-/cli-table-0.3.6.tgz" - integrity sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ== +cli-table@^0.3.11: + version "0.3.11" + resolved "https://registry.npmjs.org/cli-table/-/cli-table-0.3.11.tgz" + integrity sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ== dependencies: colors "1.0.3" -cli-ux@^4.9.3: - version "4.9.3" - resolved "https://registry.npmjs.org/cli-ux/-/cli-ux-4.9.3.tgz" - integrity sha512-/1owvF0SZ5Gn54cgrikJ0QskgTzeg30HGjkmjFoaHDJzAqFpuX1DBpFR8aLvsE1J5s9MgeYRENQK4BFwOag5VA== - dependencies: - "@oclif/errors" "^1.2.2" - "@oclif/linewrap" "^1.0.0" - "@oclif/screen" "^1.0.3" - ansi-escapes "^3.1.0" - ansi-styles "^3.2.1" - cardinal "^2.1.1" - chalk "^2.4.1" - clean-stack "^2.0.0" - extract-stack "^1.0.0" - fs-extra "^7.0.0" - hyperlinker "^1.0.0" - indent-string "^3.2.0" - is-wsl "^1.1.0" - lodash "^4.17.11" - password-prompt "^1.0.7" - semver "^5.6.0" - strip-ansi "^5.0.0" - supports-color "^5.5.0" - supports-hyperlinks "^1.0.1" - treeify "^1.1.0" - tslib "^1.9.3" - -cli-ux@^5.2.1: - version "5.5.1" - resolved "https://registry.npmjs.org/cli-ux/-/cli-ux-5.5.1.tgz" - integrity sha512-t3DT1U1C3rArLGYLpKa3m9dr/8uKZRI8HRm/rXKL7UTjm4c+Yd9zHNWg1tP8uaJkUbhmvx5SQHwb3VWpPUVdHQ== - dependencies: - "@oclif/command" "^1.6.0" - "@oclif/errors" "^1.2.1" - "@oclif/linewrap" "^1.0.0" - "@oclif/screen" "^1.0.3" - ansi-escapes "^4.3.0" - ansi-styles "^4.2.0" - cardinal "^2.1.1" - chalk "^4.1.0" - clean-stack "^3.0.0" - cli-progress "^3.4.0" - extract-stack "^2.0.0" - fs-extra "^8.1" - hyperlinker "^1.0.0" - indent-string "^4.0.0" - is-wsl "^2.2.0" - js-yaml "^3.13.1" - lodash "^4.17.11" - natural-orderby "^2.0.1" - object-treeify "^1.1.4" - password-prompt "^1.1.2" - semver "^7.3.2" - string-width "^4.2.0" - strip-ansi "^6.0.0" - supports-color "^7.1.0" - supports-hyperlinks "^2.1.0" - tslib "^2.0.0" - -cli-width@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz" - 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" + resolved "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz" integrity sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ== cliui@^6.0.0: @@ -2567,86 +4665,47 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" -clone-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz" - integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= - -clone-deep@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz" - integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.0.0" + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + version "1.0.3" + resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz" + integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== dependencies: mimic-response "^1.0.0" clone-stats@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz" - integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= + integrity sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag== clone@^1.0.2: version "1.0.4" resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" - integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== -clone@^2.1.1: +clone@^2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz" - integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= - -cloneable-readable@^1.0.0: - version "1.1.3" - resolved "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz" - integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ== - dependencies: - inherits "^2.0.1" - process-nextick-args "^2.0.0" - readable-stream "^2.3.5" + integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== -cluster-key-slot@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz" - integrity sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw== - -co-prompt@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/co-prompt/-/co-prompt-1.0.0.tgz" - integrity sha1-+zcOntrEhXayenMv5dfyHZ/G5vY= - dependencies: - keypress "~0.2.1" - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= +cmd-shim@^6.0.0: + version "6.0.3" + resolved "https://registry.npmjs.org/cmd-shim/-/cmd-shim-6.0.3.tgz" + integrity sha512-FMabTRlc5t5zjdenF6mS0MBeFZm0XqHqeOkcskKFb/LYCcRQ5fVgLOHVc4Lq9CqABd9zhjwPjMBCJvMCziSVtA== coffee-script@^1.12.4: version "1.12.7" resolved "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz" integrity sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw== -coffeescript@^1.10.0: - version "1.12.7" - resolved "https://registry.npmjs.org/coffeescript/-/coffeescript-1.12.7.tgz" - integrity sha512-pLXHFxQMPklVoEekowk8b3erNynC+DVJzChxS/LCBBgR6/8AJkHivkm//zbowcfc7BTCAjryuhx6gPqPRfsFoA== - -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz" - integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - color-convert@^1.9.0: version "1.9.3" resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" @@ -2664,34 +4723,45 @@ color-convert@^2.0.1: color-name@1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== -color-name@~1.1.4: +color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colorette@^1.2.2: - version "1.2.2" - resolved "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz" - integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== +color-string@^1.9.0: + version "1.9.1" + resolved "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^4.2.3: + version "4.2.3" + resolved "https://registry.npmjs.org/color/-/color-4.2.3.tgz" + integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A== + dependencies: + color-convert "^2.0.1" + color-string "^1.9.0" + +colorette@^2.0.7: + version "2.0.20" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== colors@1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz" - integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= - -colors@1.4.0, colors@^1.1.2: - version "1.4.0" - resolved "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz" - integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + integrity sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw== -columnify@^1.5.4: - version "1.5.4" - resolved "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz" - integrity sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs= +columnify@^1.6.0: + version "1.6.0" + resolved "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz" + integrity sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q== dependencies: - strip-ansi "^3.0.0" + strip-ansi "^6.0.1" wcwidth "^1.0.0" combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: @@ -2701,39 +4771,42 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@^2.11.0, commander@^2.9.0: - version "2.20.3" - resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^11.1.0: + version "11.1.0" + resolved "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz" + integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ== -commander@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" - integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commander@^12.0.0: + version "12.0.0" + resolved "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz" + integrity sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA== -commander@^9.4.0: - version "9.4.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-9.4.1.tgz#d1dd8f2ce6faf93147295c0df13c7c21141cfbdd" - integrity sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw== +comment-parser@1.4.1: + version "1.4.1" + resolved "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz" + integrity sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg== -common-path-prefix@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0" - integrity sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w== +common-ancestor-path@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz" + integrity sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w== commondir@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== -component-emitter@^1.2.1: - version "1.3.0" - resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== +compare-func@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz" + integrity sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA== + dependencies: + array-ify "^1.0.0" + dot-prop "^5.1.0" compress-commons@^4.1.2: version "4.1.2" - resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-4.1.2.tgz#6542e59cb63e1f46a8b21b0e06f9a32e4c8b06df" + resolved "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz" integrity sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg== dependencies: buffer-crc32 "^0.2.13" @@ -2744,7 +4817,7 @@ compress-commons@^4.1.2: concat-map@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== concat-stream@^1.5.2: version "1.6.2" @@ -2763,103 +4836,129 @@ concat-with-sourcemaps@*: dependencies: source-map "^0.6.1" -configstore@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz" - integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== +config-chain@^1.1.11: + version "1.1.13" + resolved "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz" + integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== dependencies: - dot-prop "^5.2.0" - graceful-fs "^4.1.2" - make-dir "^3.0.0" - unique-string "^2.0.0" - write-file-atomic "^3.0.0" - xdg-basedir "^4.0.0" + ini "^1.3.4" + proto-list "~1.2.1" + +configstore@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/configstore/-/configstore-7.0.0.tgz" + integrity sha512-yk7/5PN5im4qwz0WFZW3PXnzHgPu9mX29Y8uZ3aefe2lBPC1FYttWZRcaW9fKkT0pBCJyuQ2HfbmPVaODi9jcQ== + dependencies: + atomically "^2.0.3" + dot-prop "^9.0.0" + graceful-fs "^4.2.11" + xdg-basedir "^5.1.0" + +constant-case@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz" + integrity sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + upper-case "^2.0.2" content-type@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== +conventional-changelog-angular@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz" + integrity sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg== + dependencies: + compare-func "^2.0.0" + +conventional-changelog-conventionalcommits@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-6.1.0.tgz" + integrity sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw== + dependencies: + compare-func "^2.0.0" + +conventional-commits-parser@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz" + integrity sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg== + dependencies: + JSONStream "^1.3.5" + is-text-path "^1.0.1" + meow "^8.1.2" + split2 "^3.2.2" + convert-source-map@^1.7.0: - version "1.7.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz" - integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + version "1.8.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz" + integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== dependencies: safe-buffer "~5.1.1" -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz" - integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= +core-js-compat@^3.34.0: + version "3.36.1" + resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz" + integrity sha512-Dk997v9ZCt3X/npqzyGdTlq6t7lDBhZwGvV94PKzDArjp7BTRm7WlDAXYd/OWdeFHO8OChQYRJNJvUCqCbrtKA== + dependencies: + browserslist "^4.23.0" -core-util-is@1.0.2, core-util-is@~1.0.0: +core-util-is@1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== -cosmiconfig@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz" - integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cosmiconfig-typescript-loader@^4.0.0: + version "4.4.0" + resolved "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.4.0.tgz" + integrity sha512-BabizFdC3wBHhbI4kJh0VkQP9GkBfoHPydD0COMce1nJ1kJAB3F2TmJ/I7diULBKtmEWSwEbuN/KDtgnmUUVmw== + +cosmiconfig@*, cosmiconfig@^9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz" + integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== dependencies: - "@types/parse-json" "^4.0.0" - import-fresh "^3.2.1" - parse-json "^5.0.0" + env-paths "^2.2.1" + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + +cosmiconfig@^8.0.0, cosmiconfig@^8.3.6: + version "8.3.6" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz" + integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== + dependencies: + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" path-type "^4.0.0" - yaml "^1.10.0" crc-32@^1.2.0: version "1.2.2" - resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + resolved "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz" integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== crc32-stream@^4.0.2: version "4.0.3" - resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-4.0.3.tgz#85dd677eb78fa7cad1ba17cc506a597d41fc6f33" + resolved "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz" integrity sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw== dependencies: crc-32 "^1.2.0" - readable-stream "^3.4.0" - -create-error-class@^3.0.0: - version "3.0.2" - resolved "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz" - integrity sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y= - dependencies: - capture-stack-trace "^1.0.0" + readable-stream "^3.4.0" create-require@^1.1.0: version "1.1.1" resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-fetch@3.1.5: - version "3.1.5" - resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz" - integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== - dependencies: - node-fetch "2.6.7" - -cross-spawn@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" - integrity sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A== - dependencies: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" - -cross-spawn@^6.0.0, cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz" - 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.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" @@ -2869,119 +4968,127 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -crypto-random-string@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz" - integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== - csprng@*: version "0.1.2" resolved "https://registry.npmjs.org/csprng/-/csprng-0.1.2.tgz" - integrity sha1-S8aPEvo2jSUqWYQcusqXSxirReI= + integrity sha512-D3WAbvvgUVIqSxUfdvLeGjuotsB32bvfVPd+AaaTWMtyUeC9zgCnw5xs94no89yFLVsafvY9dMZEhTwsY/ZecA== dependencies: sequin "*" +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + cssstyle@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-4.0.1.tgz#ef29c598a1e90125c870525490ea4f354db0660a" + resolved "https://registry.npmjs.org/cssstyle/-/cssstyle-4.0.1.tgz" integrity sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ== dependencies: rrweb-cssom "^0.6.0" -csv-parse@^4.10.1: - version "4.15.3" - resolved "https://registry.npmjs.org/csv-parse/-/csv-parse-4.15.3.tgz" - integrity sha512-jlTqDvLdHnYMSr08ynNfk4IAUSJgJjTKy2U5CQBSu4cN9vQOJonLVZP4Qo4gKKrIgIQ5dr07UwOJdi+lRqT12w== +csv-parse@^5.5.2: + version "5.5.5" + resolved "https://registry.npmjs.org/csv-parse/-/csv-parse-5.5.5.tgz" + integrity sha512-erCk7tyU3yLWAhk6wvKxnyPtftuy/6Ak622gOO7BCJ05+TYffnPCJF905wmOQm+BpkX54OdAl8pveJwUdpnCXQ== -csv-stringify@^1.0.4: - version "1.1.2" - resolved "https://registry.npmjs.org/csv-stringify/-/csv-stringify-1.1.2.tgz" - integrity sha1-d6QVJlgbzjOA8SsA18W7rHDIK1g= - dependencies: - lodash.get "~4.4.2" +csv-stringify@^6.4.4: + version "6.4.6" + resolved "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.4.6.tgz" + integrity sha512-h2V2XZ3uOTLilF5dPIptgUfN/o2ia/80Ie0Lly18LAnw5s8Eb7kt8rfxSUy24AztJZas9f6DPZpVlzDUtFt/ag== -csv-stringify@^5.6.1: - version "5.6.2" - resolved "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.6.2.tgz" - integrity sha512-n3rIVbX6ylm1YsX2NEug9IaPV8xRnT+9/NNZbrA/bcHgOSSeqtWla6XnI/xmyu57wIw+ASCAoX1oM6EZtqJV0A== +csv-stringify@^6.5.1: + version "6.5.1" + resolved "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.5.1.tgz" + integrity sha512-+9lpZfwpLntpTIEpFbwQyWuW/hmI/eHuJZD1XzeZpfZTqkf1fyvBbBLXTJJMsBuuS11uTShMqPwzx4A6ffXgRQ== -dargs@^6.1.0: - version "6.1.0" - resolved "https://registry.npmjs.org/dargs/-/dargs-6.1.0.tgz" - integrity sha512-5dVBvpBLBnPwSsYXqfybFyehMmC/EenKEcf23AhCTgTf48JFBbmJKqoZBsERDnjL0FyiVTYWdFsRfTLHxLyKdQ== +dargs@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz" + integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== dashdash@^1.12.0: version "1.14.1" resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== dependencies: assert-plus "^1.0.0" -data-uri-to-buffer@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz" - integrity sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA== +data-uri-to-buffer@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz" + integrity sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw== data-urls@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-5.0.0.tgz#2f76906bce1824429ffecb6920f45a0b30f00dde" + resolved "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz" integrity sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg== dependencies: whatwg-mimetype "^4.0.0" whatwg-url "^14.0.0" -dateformat@^3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz" - integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== - -dayjs-plugin-utc@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/dayjs-plugin-utc/-/dayjs-plugin-utc-0.1.2.tgz" - integrity sha512-ExERH5o3oo6jFOdkvMP3gytTCQ9Ksi5PtylclJWghr7k7m3o2U5QrwtdiJkOxLOH4ghr0EKhpqGefzGz1VvVJg== +dateformat@^4.6.3: + version "4.6.3" + resolved "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz" + integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA== -dayjs@^1.8.16: - version "1.10.4" - resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.10.4.tgz" - integrity sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw== +dateformat@^5.0.3: + version "5.0.3" + resolved "https://registry.npmjs.org/dateformat/-/dateformat-5.0.3.tgz" + integrity sha512-Kvr6HmPXUMerlLcLF+Pwq3K7apHpYmGDVqrxcDasBg86UcKeTSNWbEzU8bwdXnxnR44FtMhJAxI4Bov6Y/KUfA== dayjs@^1.8.34: - version "1.11.10" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0" - integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== + version "1.11.13" + resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz" + integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== -debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5: + version "4.3.5" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== dependencies: ms "2.1.2" -debug@4.3.1: - version "4.3.1" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== +debug@4.3.4: + version "4.3.4" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" -debug@^2.2.0, debug@^2.3.3: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^3.1.0: +debug@^3.2.7: version "3.2.7" resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" -decamelize@^1.2.0: +debug@^4.3.6: + version "4.3.6" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz" + integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== + dependencies: + ms "2.1.2" + +debug@^4.3.7: + version "4.3.7" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + dependencies: + ms "^2.1.3" + +decamelize-keys@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz" + integrity sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg== + dependencies: + decamelize "^1.1.0" + map-obj "^1.0.0" + +decamelize@^1.1.0, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== decamelize@^4.0.0: version "4.0.0" @@ -2990,32 +5097,25 @@ decamelize@^4.0.0: decimal.js@^10.4.3: version "10.4.3" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz" integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== -decode-uri-component@^0.2.0, decode-uri-component@^0.2.2: +decode-uri-component@^0.2.2: version "0.2.2" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" - integrity "sha1-5p2+JdN5QRcd1UDgJMREzVGI4ek= sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" - -decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= - dependencies: - mimic-response "^1.0.0" + resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== decompress-response@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz" integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== dependencies: mimic-response "^3.1.0" -deep-eql@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz" - integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== +deep-eql@^4.1.3: + version "4.1.3" + resolved "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz" + integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== dependencies: type-detect "^4.0.0" @@ -3025,15 +5125,28 @@ deep-extend@^0.6.0: integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== deep-is@^0.1.3: - version "0.1.3" - resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz" - integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + version "0.1.4" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== deepmerge@^4.2.2: version "4.3.1" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== +default-browser-id@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz" + integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== + +default-browser@^5.2.1: + version "5.2.1" + resolved "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz" + integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== + dependencies: + bundle-name "^4.1.0" + default-browser-id "^5.0.0" + default-require-extensions@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz" @@ -3042,113 +5155,115 @@ default-require-extensions@^3.0.0: strip-bom "^4.0.0" defaults@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz" - integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + version "1.0.4" + resolved "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz" + integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== dependencies: clone "^1.0.2" -defer-to-connect@^1.0.1: - version "1.1.3" - resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz" - integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== - -defer-to-connect@^2.0.0: +defer-to-connect@^2.0.0, defer-to-connect@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== -define-lazy-prop@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" - integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== +define-data-property@^1.0.1, define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz" - integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== dependencies: - is-descriptor "^0.1.0" + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz" - integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: + version "1.2.1" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== dependencies: - is-descriptor "^1.0.0" + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== +degenerator@^5.0.0: + version "5.0.1" + resolved "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz" + integrity sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ== dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" + ast-types "^0.13.4" + escodegen "^2.1.0" + esprima "^4.0.1" delay@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" + resolved "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz" integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -denque@^1.1.0: - version "1.5.1" - resolved "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz" - integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" + resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== -detect-indent@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-6.0.0.tgz" - integrity sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA== +des.js@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz" + integrity sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +detect-indent@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.1.tgz" + integrity sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g== -devtools-protocol@0.0.969999: - version "0.0.969999" - resolved "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.969999.tgz" - integrity sha512-6GfzuDWU0OFAuOvBokXpXPLxjOJ5DZ157Ue3sGQQM3LgAamb8m0R0ruSfN0DDu+XG5XJgT50i6zZ/0o8RglreQ== +detect-newline@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-4.0.1.tgz" + integrity sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog== + +devtools-protocol@0.0.1330662: + version "0.0.1330662" + resolved "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1330662.tgz" + integrity sha512-pzh6YQ8zZfz3iKlCvgzVCu22NdpZ8hNmwU6WnQjNVquh0A9iVosPtNLWDwaWVGyrntQlltPFztTMK5Cg6lfCuw== diacritics-map@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/diacritics-map/-/diacritics-map-0.1.0.tgz" - integrity sha1-bfwP+dAQAKLt8oZTccrDFulJd68= + integrity sha512-3omnDTYrGigU0i4cJjvaKwD52B8aoqyX/NEIkukFFkogBemsIbhSa1O414fpTp5nuszJG6lvQ5vBvDVNCbSsaQ== diff@5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz" integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== -diff@^3.5.0: - version "3.5.0" - resolved "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz" - integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== - -diff@^4.0.1: +diff@^4.0.1, diff@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -dir-glob@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz" - integrity sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag== - dependencies: - arrify "^1.0.1" - path-type "^3.0.0" - -dir-glob@^2.2.2: - version "2.2.2" - resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz" - integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== - dependencies: - path-type "^3.0.0" +diff@^5.1.0, diff@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz" + integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== dir-glob@^3.0.1: version "3.0.1" @@ -3157,6 +5272,13 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + doctrine@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" @@ -3164,60 +5286,79 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dompurify@^3.0.8: - version "3.0.8" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.0.8.tgz#e0021ab1b09184bc8af7e35c7dd9063f43a8a437" - integrity sha512-b7uwreMYL2eZhrSCRC4ahLTeZcPZxSmYfmcQGXGkXiZSNW1X85v+SDM5KsWcpivIiUBH47Ji7NtyUdpLeF5JZQ== +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +dompurify@^3.1.6: + version "3.1.6" + resolved "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz" + integrity sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ== + +domutils@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz" + integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" -dot-prop@^5.2.0: +dot-prop@^5.1.0: version "5.3.0" resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz" integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== dependencies: is-obj "^2.0.0" -download-stats@^0.3.4: - version "0.3.4" - resolved "https://registry.npmjs.org/download-stats/-/download-stats-0.3.4.tgz" - integrity sha512-ic2BigbyUWx7/CBbsfGjf71zUNZB4edBGC3oRliSzsoNmvyVx3Ycfp1w3vp2Y78Ee0eIIkjIEO5KzW0zThDGaA== +dot-prop@^9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz" + integrity sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ== dependencies: - JSONStream "^1.2.1" - lazy-cache "^2.0.1" - moment "^2.15.1" - -dtrace-provider@~0.6: - version "0.6.0" - resolved "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz" - integrity sha1-CweNVReTfYcxAUUtkUZzdVe3XlE= - dependencies: - nan "^2.0.8" + type-fest "^4.18.2" duplexer2@~0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" + resolved "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz" integrity sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA== dependencies: readable-stream "^2.0.2" -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= - -each-parallel-async@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/each-parallel-async/-/each-parallel-async-1.0.0.tgz#91783e190000c7dd588336b2d468ebaf71980f7b" - integrity sha512-P/9kLQiQj0vZNzphvKKTgRgMnlqs5cJsxeAiuog1jrUnwv0Z3hVUwJDQiP7MnLb2I9S15nR9SRUceFT9IxtqRg== - eastasianwidth@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== dependencies: jsbn "~0.1.0" safer-buffer "^2.1.0" @@ -3229,45 +5370,34 @@ ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer "^5.0.1" -editions@^2.2.0: - version "2.3.1" - resolved "https://registry.npmjs.org/editions/-/editions-2.3.1.tgz" - integrity sha512-ptGvkwTvGdGfC0hfhKg0MT+TRLRKGtUiWGBInxOm5pz7ssADezahjCUaYuZ8Dr+C05FW0AECIIPt4WBxVINEhA== +editions@^6.21.0: + version "6.21.0" + resolved "https://registry.npmjs.org/editions/-/editions-6.21.0.tgz" + integrity sha512-ofkXJtn7z0urokN62DI3SBo/5xAtF0rR7tn+S/bSYV79Ka8pTajIIl+fFQ1q88DQEImymmo97M4azY3WX/nUdg== dependencies: - errlop "^2.0.0" - semver "^6.3.0" - -ejs@^2.6.1: - version "2.7.4" - resolved "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz" - integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== + version-range "^4.13.0" -ejs@^3.1.5: +ejs@^3.1.10: version "3.1.10" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" - integrity "sha1-aauDWLFOiW+AzDnmIIe4hQDDrDs= sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==" + resolved "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz" + integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== dependencies: jake "^10.8.5" -electron-to-chromium@^1.3.723: - version "1.3.743" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.743.tgz" - integrity sha512-K2wXfo9iZQzNJNx67+Pld0DRF+9bYinj62gXCdgPhcu1vidwVuLPHQPPFnCdO55njWigXXpfBiT90jGUPbw8Zg== +electron-to-chromium@^1.4.668: + version "1.4.735" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.735.tgz" + integrity sha512-pkYpvwg8VyOTQAeBqZ7jsmpCjko1Qc6We1ZtZCjRyYbT5v4AIUKDy5cQTRotQlSSZmMr8jqpEt6JtOj5k7lR7A== email-validator@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/email-validator/-/email-validator-2.0.4.tgz" integrity sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ== -"emoji-regex@>=6.0.0 <=6.1.1": - version "6.1.1" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz" - integrity sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4= - -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== +emoji-regex@^10.3.0: + version "10.3.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz" + integrity sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw== emoji-regex@^8.0.0: version "8.0.0" @@ -3276,9 +5406,16 @@ emoji-regex@^8.0.0: emoji-regex@^9.2.2: version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== +encoding@^0.1.13: + version "0.1.13" + resolved "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" @@ -3286,27 +5423,25 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -enquirer@^2.3.5: - version "2.3.6" - resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz" - integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== - dependencies: - ansi-colors "^4.1.1" - -entities@^4.4.0: +entities@^4.2.0, entities@^4.4.0, entities@^4.5.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== -eol@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/eol/-/eol-0.9.1.tgz#f701912f504074be35c6117a5c4ade49cd547acd" - integrity sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg== +env-paths@^2.2.0, env-paths@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + +env-paths@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz" + integrity sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A== -errlop@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/errlop/-/errlop-2.2.0.tgz" - integrity sha512-e64Qj9+4aZzjzzFpZC7p5kmm/ccCrbLhAJplhsDXQFs87XTsXwOpH4s1Io2s90Tau/8r2j9f4l/thhDevRjzxw== +err-code@^2.0.2: + version "2.0.3" + resolved "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz" + integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== error-ex@^1.3.1: version "1.3.2" @@ -3315,12 +5450,87 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -error@^7.0.2: - version "7.2.1" - resolved "https://registry.npmjs.org/error/-/error-7.2.1.tgz" - integrity sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA== +es-abstract@^1.22.1: + version "1.22.3" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz" + integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== + dependencies: + array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.2" + available-typed-arrays "^1.0.5" + call-bind "^1.0.5" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.2" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.12" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + safe-array-concat "^1.0.1" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.8" + string.prototype.trimend "^1.0.7" + string.prototype.trimstart "^1.0.7" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.13" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-set-tostringtag@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz" + integrity sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q== dependencies: - string-template "~0.2.1" + get-intrinsic "^1.2.2" + has-tostringtag "^1.0.0" + hasown "^2.0.0" + +es-shim-unscopables@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== + dependencies: + hasown "^2.0.0" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" es6-error@^4.0.1: version "4.1.1" @@ -3332,107 +5542,232 @@ escalade@^3.1.1: resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -escape-goat@^2.0.0: - version "2.1.1" - resolved "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz" - integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== +escape-goat@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz" + integrity sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg== -escape-string-regexp@4.0.0: +escape-html@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== -eslint-scope@^5.0.0, eslint-scope@^5.1.1: - version "5.1.1" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== +escodegen@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" -eslint-utils@^2.0.0, eslint-utils@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz" - integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== +eslint-config-prettier@^9.1.0: + version "9.1.0" + resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz" + integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw== + +eslint-config-salesforce-license@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/eslint-config-salesforce-license/-/eslint-config-salesforce-license-0.2.0.tgz" + integrity sha512-DJdBvgj82Erum82YMe+YvG/o6ukna3UA++lRl0HSTldj0VlBl3Q8hzCp97nRXZHra6JH1I912yievZzklXDw6w== + +eslint-config-salesforce-typescript@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/eslint-config-salesforce-typescript/-/eslint-config-salesforce-typescript-3.3.0.tgz" + integrity sha512-83+zp2Y2h9oz9D3UksjNGCw+xWD7ylIiAJZ58vUbBD10l8FRUMNyn+RDCKn0xCQz7xed5/LcmgUE4T7roe+HBw== + dependencies: + "@typescript-eslint/eslint-plugin" "^6.21.0" + "@typescript-eslint/parser" "^6.21.0" + eslint "^8.56.0" + eslint-config-prettier "^9.1.0" + eslint-config-salesforce "^2.2.0" + eslint-config-salesforce-license "^0.2.0" + eslint-plugin-header "^3.1.1" + eslint-plugin-import "^2.29.1" + eslint-plugin-jsdoc "^46.10.1" + eslint-plugin-unicorn "^50.0.1" + +eslint-config-salesforce@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/eslint-config-salesforce/-/eslint-config-salesforce-2.2.0.tgz" + integrity sha512-0zUEFJ2nNpMvVO3MgKEDUTGtaFZjL3xEIErr5h+BOft+OhGoIvZBNPnBBu12lvv29ylqIAQz5SwoVCCUzBhyPQ== + +eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== dependencies: - eslint-visitor-keys "^1.1.0" + debug "^3.2.7" + is-core-module "^2.13.0" + resolve "^1.22.4" -eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== +eslint-module-utils@^2.8.0: + version "2.8.0" + resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz" + integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== + dependencies: + debug "^3.2.7" -eslint-visitor-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz" - integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== +eslint-plugin-header@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/eslint-plugin-header/-/eslint-plugin-header-3.1.1.tgz" + integrity sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg== + +eslint-plugin-import@^2.29.1: + version "2.29.1" + resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz" + integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== + dependencies: + array-includes "^3.1.7" + array.prototype.findlastindex "^1.2.3" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.9" + eslint-module-utils "^2.8.0" + hasown "^2.0.0" + is-core-module "^2.13.1" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.fromentries "^2.0.7" + object.groupby "^1.0.1" + object.values "^1.1.7" + semver "^6.3.1" + tsconfig-paths "^3.15.0" + +eslint-plugin-jsdoc@^46.10.1: + version "46.10.1" + resolved "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.10.1.tgz" + integrity sha512-x8wxIpv00Y50NyweDUpa+58ffgSAI5sqe+zcZh33xphD0AVh+1kqr1ombaTRb7Fhpove1zfUuujlX9DWWBP5ag== + dependencies: + "@es-joy/jsdoccomment" "~0.41.0" + are-docs-informative "^0.0.2" + comment-parser "1.4.1" + debug "^4.3.4" + escape-string-regexp "^4.0.0" + esquery "^1.5.0" + is-builtin-module "^3.2.1" + semver "^7.5.4" + spdx-expression-parse "^4.0.0" + +eslint-plugin-sf-plugin@^1.18.6: + version "1.18.6" + resolved "https://registry.npmjs.org/eslint-plugin-sf-plugin/-/eslint-plugin-sf-plugin-1.18.6.tgz" + integrity sha512-wq+dEEKTl540nDa8LGurDNXjPHyWBdBQQ74EA2RQgkjEcICYYDe37Od536v5OalXQeJ/SWcOVfZ544lI1lBltQ== + dependencies: + "@salesforce/core" "^7.3.9" + "@typescript-eslint/utils" "^6.17.0" -eslint@^7.21.0: - version "7.21.0" - resolved "https://registry.npmjs.org/eslint/-/eslint-7.21.0.tgz" - integrity sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg== +eslint-plugin-unicorn@^50.0.1: + version "50.0.1" + resolved "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-50.0.1.tgz" + integrity sha512-KxenCZxqSYW0GWHH18okDlOQcpezcitm5aOSz6EnobyJ6BIByiPDviQRjJIUAjG/tMN11958MxaQ+qCoU6lfDA== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + "@eslint-community/eslint-utils" "^4.4.0" + "@eslint/eslintrc" "^2.1.4" + ci-info "^4.0.0" + clean-regexp "^1.0.0" + core-js-compat "^3.34.0" + esquery "^1.5.0" + indent-string "^4.0.0" + is-builtin-module "^3.2.1" + jsesc "^3.0.2" + pluralize "^8.0.0" + read-pkg-up "^7.0.1" + regexp-tree "^0.1.27" + regjsparser "^0.10.0" + semver "^7.5.4" + strip-indent "^3.0.0" + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== dependencies: - "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.0" - ajv "^6.10.0" + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8.56.0: + version "8.57.0" + resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz" + integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.0" + "@humanwhocodes/config-array" "^0.11.14" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" - debug "^4.0.1" + debug "^4.3.2" doctrine "^3.0.0" - enquirer "^2.3.5" - eslint-scope "^5.1.1" - eslint-utils "^2.1.0" - eslint-visitor-keys "^2.0.0" - espree "^7.3.1" - esquery "^1.4.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" esutils "^2.0.2" + fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^5.0.0" - globals "^12.1.0" - ignore "^4.0.6" - import-fresh "^3.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - js-yaml "^3.13.1" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.20" - minimatch "^3.0.4" + lodash.merge "^4.6.2" + minimatch "^3.1.2" natural-compare "^1.4.0" - optionator "^0.9.1" - progress "^2.0.0" - regexpp "^3.1.0" - semver "^7.2.1" - strip-ansi "^6.0.0" - strip-json-comments "^3.1.0" - table "^6.0.4" + optionator "^0.9.3" + strip-ansi "^6.0.1" text-table "^0.2.0" - v8-compile-cache "^2.0.3" -espree@^7.3.0, espree@^7.3.1: - version "7.3.1" - resolved "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz" - integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: - acorn "^7.4.0" - acorn-jsx "^5.3.1" - eslint-visitor-keys "^1.3.0" + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" -esprima@^4.0.0, esprima@~4.0.0: +esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== +esquery@^1.4.2, esquery@^1.5.0: + version "1.5.0" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== dependencies: estraverse "^5.1.0" @@ -3443,15 +5778,10 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - estraverse@^5.1.0, estraverse@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + version "5.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== esutils@^2.0.2: version "2.0.3" @@ -3463,19 +5793,24 @@ event-target-shim@^5.0.0: resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== -eventemitter3@^3.1.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" - integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== - eventemitter3@^4.0.4: version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + +events@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + exceljs@^4.4.0: version "4.4.0" - resolved "https://registry.yarnpkg.com/exceljs/-/exceljs-4.4.0.tgz#cfb1cb8dcc82c760a9fc9faa9e52dadab66b0156" + resolved "https://registry.npmjs.org/exceljs/-/exceljs-4.4.0.tgz" integrity sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg== dependencies: archiver "^5.0.0" @@ -3488,20 +5823,7 @@ exceljs@^4.4.0: unzipper "^0.10.11" uuid "^8.3.0" -execa@^0.10.0: - version "0.10.0" - resolved "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz" - integrity sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw== - dependencies: - cross-spawn "^6.0.0" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -execa@^4.0.0: +execa@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz" integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== @@ -3516,47 +5838,76 @@ execa@^4.0.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz" - integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +execa@^7.1.1: + version "7.2.0" + resolved "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz" + integrity sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.1" + human-signals "^4.3.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^3.0.7" + strip-final-newline "^3.0.0" + +execa@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz" + integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^8.0.1" + human-signals "^5.0.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^4.1.0" + strip-final-newline "^3.0.0" expand-range@^1.8.1: version "1.8.2" resolved "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz" - integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= + integrity sha512-AFASGfIlnIbkKPQwX1yHaDjFvh/1gyKJODme52V6IORh69uEYgZp0o9C+qsIGNVEiuuhQU0CSSl++Rlegg1qvA== dependencies: fill-range "^2.1.0" +exponential-backoff@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz" + integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== dependencies: is-extendable "^0.1.0" -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - -extend@~3.0.2: +extend@^3.0.2, extend@~3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -external-editor@^3.0.3, external-editor@^3.1.0: +external-editor@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== @@ -3565,31 +5916,7 @@ external-editor@^3.0.3, external-editor@^3.1.0: iconv-lite "^0.4.24" tmp "^0.0.33" -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -extract-stack@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/extract-stack/-/extract-stack-1.0.0.tgz" - integrity sha1-uXrK+UQe6iMyUpYktzL8WhyBZfo= - -extract-stack@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/extract-stack/-/extract-stack-2.0.0.tgz" - integrity sha512-AEo4zm+TenK7zQorGK1f9mJ8L14hnTDi2ZQPR+Mub1NX8zimka1mXpV5LpH8x9HoUmFSHZCfLHqWvp0Y4FxxzQ== - -extract-zip@2.0.1, extract-zip@^2.0.1: +extract-zip@*, extract-zip@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz" integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== @@ -3603,68 +5930,45 @@ extract-zip@2.0.1, extract-zip@^2.0.1: extsprintf@1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + version "1.4.1" + resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== -fancy-test@^1.4.3: - version "1.4.10" - resolved "https://registry.npmjs.org/fancy-test/-/fancy-test-1.4.10.tgz" - integrity sha512-AaUX6wKS7D5OP2YK2q5G7c8PGx2lgoyLUD7Bbg8z323sb9aebBqzb9UN6phzI73UgO/ViihmNfOxF3kdfZLhew== - dependencies: - "@types/chai" "*" - "@types/lodash" "*" - "@types/node" "*" - "@types/sinon" "*" - lodash "^4.17.13" - mock-stdin "^1.0.0" - nock "^13.0.0" - stdout-stderr "^0.1.9" +fast-copy@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.1.tgz" + integrity sha512-Knr7NOtK3HWRYGtHoJrjkaWepqT8thIVGAwt0p0aUs1zqkAzXZV4vo9fFNwyb5fcqK1GKYFYxldQdIDVKhUAfA== + +fast-copy@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz" + integrity sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ== fast-csv@^4.3.1: version "4.3.6" - resolved "https://registry.yarnpkg.com/fast-csv/-/fast-csv-4.3.6.tgz#70349bdd8fe4d66b1130d8c91820b64a21bc4a63" + resolved "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz" integrity sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw== dependencies: "@fast-csv/format" "4.3.5" "@fast-csv/parse" "4.3.6" -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^2.0.2, fast-glob@^2.2.6: - version "2.2.7" - resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz" - integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== - dependencies: - "@mrmlnc/readdir-enhanced" "^2.2.1" - "@nodelib/fs.stat" "^1.1.2" - glob-parent "^3.1.0" - is-glob "^4.0.0" - merge2 "^1.2.3" - micromatch "^3.1.10" - -fast-glob@^3.0.3, fast-glob@^3.1.1: - version "3.2.5" - resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz" - integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.0" - merge2 "^1.3.0" - micromatch "^4.0.2" - picomatch "^2.2.1" +fast-fifo@^1.2.0, fast-fifo@^1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz" + integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== -fast-glob@^3.2.11, fast-glob@^3.2.9: - version "3.2.12" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" - integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== +fast-glob@^3.2.11, fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.2: + version "3.3.2" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -3680,19 +5984,53 @@ fast-json-stable-stringify@^2.0.0: fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fast-levenshtein@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz" + integrity sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ== + dependencies: + fastest-levenshtein "^1.0.7" + +fast-redact@^3.1.1: + version "3.3.0" + resolved "https://registry.npmjs.org/fast-redact/-/fast-redact-3.3.0.tgz" + integrity sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ== + +fast-safe-stringify@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz" + integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== + +fast-uri@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz" + integrity sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw== + +fast-xml-parser@4.4.1: + version "4.4.1" + resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz" + integrity sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw== + dependencies: + strnum "^1.0.5" + +fastest-levenshtein@^1.0.7: + version "1.0.16" + resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== fastq@^1.6.0: - version "1.11.0" - resolved "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz" - integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g== + version "1.15.0" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== dependencies: reusify "^1.0.4" faye-websocket@>=0.9.1: - version "0.11.3" - resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz" - integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA== + version "0.11.4" + resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== dependencies: websocket-driver ">=0.5.1" @@ -3711,25 +6049,10 @@ faye@^1.4.0: fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz" - integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== dependencies: pend "~1.2.0" -fetch-blob@^3.1.2, fetch-blob@^3.1.4: - version "3.1.4" - resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.4.tgz" - integrity sha512-Eq5Xv5+VlSrYWEqKrusxY1C3Hm/hjeAsCGVG3ft7pZahlUAChpGZT/Ms1WmSLnEAisEXszjzu/s+ce6HZB2VHA== - dependencies: - node-domexception "^1.0.0" - web-streams-polyfill "^3.0.3" - -figures@^3.0.0: - version "3.2.0" - resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== - dependencies: - escape-string-regexp "^1.0.5" - file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" @@ -3738,11 +6061,11 @@ file-entry-cache@^6.0.1: flat-cache "^3.0.4" filelist@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/filelist/-/filelist-1.0.2.tgz" - integrity sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ== + version "1.0.4" + resolved "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz" + integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== dependencies: - minimatch "^3.0.4" + minimatch "^5.0.1" fill-range@^2.1.0: version "2.2.4" @@ -3755,51 +6078,45 @@ fill-range@^2.1.0: repeat-element "^1.1.2" repeat-string "^1.5.2" -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz" - integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" + to-regex-range "^5.0.1" fill-range@^7.1.1: version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" - integrity "sha1-RCZdPKwH4+p9wkdRY4BkN1SgUpI= sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" filter-obj@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" + resolved "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz" integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ== find-cache-dir@^3.2.0: - version "3.3.1" - resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz" - integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== + version "3.3.2" + resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz" + integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== dependencies: commondir "^1.0.1" make-dir "^3.0.2" pkg-dir "^4.1.0" -find-cache-dir@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-4.0.0.tgz#a30ee0448f81a3990708f6453633c733e2f6eec2" - integrity sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg== - dependencies: - common-path-prefix "^3.0.0" - pkg-dir "^7.0.0" - find-package-json@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/find-package-json/-/find-package-json-1.2.0.tgz" integrity sha512-+SOGcLGYDJHtyqHd87ysBhmaeQ95oWspDKnMXBrnQ9Eq4OkLNqejgoaD8xVWu6GPa0B6roa6KinCMEMcVeqONw== -find-up@5.0.0: +find-up-simple@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.0.tgz" + integrity sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw== + +find-up@5.0.0, find-up@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== @@ -3807,13 +6124,6 @@ find-up@5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" @@ -3824,7 +6134,7 @@ find-up@^4.0.0, find-up@^4.1.0: find-up@^6.3.0: version "6.3.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-6.3.0.tgz#2abab3d3280b2dc7ac10199ef324c4e002c8c790" + resolved "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz" integrity sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw== dependencies: locate-path "^7.1.0" @@ -3837,19 +6147,18 @@ find-yarn-workspace-root@^2.0.0: dependencies: micromatch "^4.0.2" -first-chunk-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz" - integrity sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA= - dependencies: - readable-stream "^2.0.2" +first-chunk-stream@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-5.0.0.tgz" + integrity sha512-WdHo4ejd2cG2Dl+sLkW79SctU7mUQDfr4s1i26ffOZRs5mgv+BRttIM9gwcq0rDbemo0KlpVPaa3LBVLqPXzcQ== flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + version "3.1.1" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz" + integrity sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q== dependencies: - flatted "^3.1.0" + flatted "^3.2.9" + keyv "^4.5.3" rimraf "^3.0.2" flat@^5.0.2: @@ -3857,20 +6166,37 @@ flat@^5.0.2: resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== -flatted@^3.1.0: - version "3.1.1" - resolved "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz" - integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== +flatted@^3.2.9: + version "3.2.9" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== -follow-redirects@^1.14.0, follow-redirects@^1.14.9, follow-redirects@^1.15.6: +fly-import@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/fly-import/-/fly-import-0.4.0.tgz" + integrity sha512-sgIZHb7m0eze7hneKzuzXPLWs3RD9vK93Kqc4hvm/eiptVLbYHz4zZp0ckUAXUCoxq5/yGjfh7OUUJOWP9VqGA== + dependencies: + "@npmcli/arborist" "^7.2.0" + env-paths "^3.0.0" + registry-auth-token "^5.0.2" + registry-url "^6.0.1" + +follow-redirects@^1.15.6: version "1.15.6" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" - integrity "sha1-f4FcDNpCScdP8J6V75fCO1/QOZs= sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" for-in@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== foreground-child@^2.0.0: version "2.0.0" @@ -3881,9 +6207,9 @@ foreground-child@^2.0.0: signal-exit "^3.0.2" foreground-child@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.2.1.tgz#767004ccf3a5b30df39bed90718bab43fe0a59f7" - integrity sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA== + version "3.1.1" + resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== dependencies: cross-spawn "^7.0.0" signal-exit "^4.0.1" @@ -3891,16 +6217,21 @@ foreground-child@^3.1.0: forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== form-data-encoder@1.7.2: version "1.7.2" - resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040" + resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz" integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A== +form-data-encoder@^2.1.2: + version "2.1.4" + resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz" + integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw== + form-data@^2.5.0: version "2.5.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" + resolved "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz" integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== dependencies: asynckit "^0.4.0" @@ -3909,44 +6240,21 @@ form-data@^2.5.0: form-data@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== dependencies: asynckit "^0.4.0" combined-stream "^1.0.8" mime-types "^2.1.12" -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - formdata-node@^4.3.2: version "4.4.1" - resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.4.1.tgz#23f6a5cb9cb55315912cbec4ff7b0f59bbd191e2" + resolved "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz" integrity sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ== dependencies: node-domexception "1.0.0" web-streams-polyfill "4.0.0-beta.3" -formdata-polyfill@^4.0.10: - version "4.0.10" - resolved "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz" - integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== - dependencies: - fetch-blob "^3.1.2" - -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz" - integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= - dependencies: - map-cache "^0.2.2" - fromentries@^1.2.0: version "1.3.2" resolved "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz" @@ -3957,32 +6265,23 @@ fs-constants@^1.0.0: resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-extra@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" - integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== +fs-extra@^11.0.0: + version "11.1.1" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz" - integrity sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs-extra@^7.0.0: - version "7.0.1" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz" - integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== +fs-extra@^11.1.1, fs-extra@^11.2.0: + version "11.2.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" fs-extra@^8.1: version "8.1.0" @@ -3993,15 +6292,19 @@ fs-extra@^8.1: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^9.1.0: - version "9.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" + minipass "^3.0.0" + +fs-minipass@^3.0.0: + version "3.0.3" + resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz" + integrity sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw== + dependencies: + minipass "^7.0.3" fs-readdir-recursive@^1.1.0: version "1.1.0" @@ -4011,16 +6314,16 @@ fs-readdir-recursive@^1.1.0: fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@~2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== fstream@^1.0.12: version "1.0.12" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" + resolved "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz" integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== dependencies: graceful-fs "^4.1.2" @@ -4028,15 +6331,35 @@ fstream@^1.0.12: mkdirp ">=0.5 0" rimraf "2" -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +gaxios@^6.0.0: + version "6.1.1" + resolved "https://registry.npmjs.org/gaxios/-/gaxios-6.1.1.tgz" + integrity sha512-bw8smrX+XlAoo9o1JAksBwX+hi/RG15J+NTSxmNPIclKC3ZVK6C2afwY8OSdRvOK0+ZLecUJYtj2MmjOt3Dm0w== + dependencies: + extend "^3.0.2" + https-proxy-agent "^7.0.1" + is-stream "^2.0.0" + node-fetch "^2.6.9" gensync@^1.0.0-beta.2: version "1.0.0-beta.2" @@ -4048,41 +6371,46 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-func-name@^2.0.0: +get-east-asian-width@^1.0.0: + version "1.2.0" + resolved "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz" + integrity sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA== + +get-func-name@^2.0.1, get-func-name@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" + resolved "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz" integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== -get-intrinsic@^1.0.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" - integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== + dependencies: + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== dependencies: - function-bind "^1.1.1" - has "^1.0.3" + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" has-symbols "^1.0.3" + hasown "^2.0.0" get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-stdin@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" - integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= - -get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz" - integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= - -get-stream@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" +get-stdin@^9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz" + integrity sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA== get-stream@^5.0.0, get-stream@^5.1.0: version "5.2.0" @@ -4091,75 +6419,120 @@ get-stream@^5.0.0, get-stream@^5.1.0: dependencies: pump "^3.0.0" -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz" - integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= +get-stream@^6.0.0, get-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-stream@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz" + integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +get-uri@^6.0.1: + version "6.0.3" + resolved "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz" + integrity sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw== + dependencies: + basic-ftp "^5.0.2" + data-uri-to-buffer "^6.0.2" + debug "^4.3.4" + fs-extra "^11.2.0" getpass@^0.1.1: version "0.1.7" resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== dependencies: assert-plus "^1.0.0" -gh-got@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/gh-got/-/gh-got-5.0.0.tgz" - integrity sha1-7pW+NxBv2HSKlvjR20uuqJ4b+oo= - dependencies: - got "^6.2.0" - is-plain-obj "^1.1.0" +git-hooks-list@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.1.0.tgz" + integrity sha512-LF8VeHeR7v+wAbXqfgRlTSX/1BJR9Q1vEMR8JAz1cEg6GX07+zyj3sAdDvYjj/xnlIfVuGgj4qBei1K3hKH+PA== -github-slugger@^1.2.1: - version "1.3.0" - resolved "https://registry.npmjs.org/github-slugger/-/github-slugger-1.3.0.tgz" - integrity sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q== +git-raw-commits@^2.0.11: + version "2.0.11" + resolved "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz" + integrity sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A== dependencies: - emoji-regex ">=6.0.0 <=6.1.1" + dargs "^7.0.0" + lodash "^4.17.15" + meow "^8.0.0" + split2 "^3.0.0" + through2 "^4.0.0" -github-username@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/github-username/-/github-username-3.0.0.tgz" - integrity sha1-CnciGbMTB0NCnyRW0L3T21Xc57E= - dependencies: - gh-got "^5.0.0" +github-slugger@^2: + version "2.0.0" + resolved "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz" + integrity sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw== -glob-parent@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz" - integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= +github-username@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/github-username/-/github-username-7.0.0.tgz" + integrity sha512-mzCjmmR1LcNf0/qvkJRO63di2lUUuEoRuCqzflq8wrpAajOo7zLSXOTTuj2qr1DhFY2pruw5JLw/CokZU/3ilg== dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" + "@octokit/rest" "^18.12.0" -glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@^5.1.2, glob-parent@~5.1.0: +glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob-to-regexp@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz" - integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" -glob@7.1.6: - version "7.1.6" - resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== +glob@8.1.0, glob@^8.0.3: + version "8.1.0" + resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.4" + minimatch "^5.0.1" once "^1.3.0" - path-is-absolute "^1.0.0" + +glob@^10.2.2: + version "10.4.5" + resolved "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + +glob@^10.3.10: + version "10.3.10" + resolved "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz" + integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.3.5" + minimatch "^9.0.1" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-scurry "^1.10.1" glob@^11.0.0: version "11.0.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.0.tgz#6031df0d7b65eaa1ccb9b29b5ced16cea658e77e" + resolved "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz" integrity sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g== dependencies: foreground-child "^3.1.0" @@ -4169,32 +6542,9 @@ glob@^11.0.0: package-json-from-dist "^1.0.0" path-scurry "^2.0.0" -glob@^6.0.1: - version "6.0.4" - resolved "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz" - integrity sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI= - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.0.0, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.2.0" - resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.2.3: +glob@^7.0.0, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.3: version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: fs.realpath "^1.0.0" @@ -4204,54 +6554,42 @@ glob@^7.2.3: once "^1.3.0" path-is-absolute "^1.0.0" -global-dirs@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz" - integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== +global-directory@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz" + integrity sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q== + dependencies: + ini "4.1.1" + +global-dirs@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz" + integrity sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg== dependencies: - ini "2.0.0" + ini "^1.3.4" globals@^11.1.0: version "11.12.0" resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^12.1.0: - version "12.4.0" - resolved "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz" - integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== - dependencies: - type-fest "^0.8.1" - -globby@^10.0.1: - version "10.0.2" - resolved "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz" - integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg== +globals@^13.19.0: + version "13.23.0" + resolved "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz" + integrity sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA== dependencies: - "@types/glob" "^7.1.1" - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.0.3" - glob "^7.1.3" - ignore "^5.1.1" - merge2 "^1.2.3" - slash "^3.0.0" + type-fest "^0.20.2" -globby@^11.0.1, globby@^11.0.2: - version "11.0.2" - resolved "https://registry.npmjs.org/globby/-/globby-11.0.2.tgz" - integrity sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og== +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.1.1" - ignore "^5.1.4" - merge2 "^1.3.0" - slash "^3.0.0" + define-properties "^1.1.3" globby@^11.1.0: version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== dependencies: array-union "^2.1.0" @@ -4262,46 +6600,50 @@ globby@^11.1.0: slash "^3.0.0" globby@^13.1.2: - version "13.1.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.2.tgz#29047105582427ab6eca4f905200667b056da515" - integrity sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ== + version "13.2.2" + resolved "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz" + integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== dependencies: dir-glob "^3.0.1" - fast-glob "^3.2.11" - ignore "^5.2.0" + fast-glob "^3.3.0" + ignore "^5.2.4" merge2 "^1.4.1" slash "^4.0.0" -globby@^8.0.1: - version "8.0.2" - resolved "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz" - integrity sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w== - dependencies: - array-union "^1.0.1" - dir-glob "2.0.0" - fast-glob "^2.0.2" - glob "^7.1.2" - ignore "^3.3.5" - pify "^3.0.0" - slash "^1.0.0" - -globby@^9.2.0: - version "9.2.0" - resolved "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz" - integrity sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg== - dependencies: - "@types/glob" "^7.1.1" - array-union "^1.0.2" - dir-glob "^2.2.2" - fast-glob "^2.2.6" - glob "^7.1.3" - ignore "^4.0.3" - pify "^4.0.1" - slash "^2.0.0" +globby@^14.0.0, globby@^14.0.2: + version "14.0.2" + resolved "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz" + integrity sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw== + dependencies: + "@sindresorhus/merge-streams" "^2.1.0" + fast-glob "^3.3.2" + ignore "^5.2.4" + path-type "^5.0.0" + slash "^5.1.0" + unicorn-magic "^0.1.0" + +globby@^14.0.1: + version "14.0.1" + resolved "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz" + integrity sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ== + dependencies: + "@sindresorhus/merge-streams" "^2.1.0" + fast-glob "^3.3.2" + ignore "^5.2.4" + path-type "^5.0.0" + slash "^5.1.0" + unicorn-magic "^0.1.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" got@^11.8.3: version "11.8.6" - resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" + resolved "https://registry.npmjs.org/got/-/got-11.8.6.tgz" integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== dependencies: "@sindresorhus/is" "^4.0.0" @@ -4316,49 +6658,59 @@ got@^11.8.3: p-cancelable "^2.0.0" responselike "^2.0.0" -got@^6.2.0: - version "6.7.1" - resolved "https://registry.npmjs.org/got/-/got-6.7.1.tgz" - integrity sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA= - dependencies: - create-error-class "^3.0.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-redirect "^1.0.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - lowercase-keys "^1.0.0" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - unzip-response "^2.0.1" - url-parse-lax "^1.0.0" - -got@^9.6.0: - version "9.6.0" - resolved "https://registry.npmjs.org/got/-/got-9.6.0.tgz" - integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== - dependencies: - "@sindresorhus/is" "^0.14.0" - "@szmarczak/http-timer" "^1.1.2" - cacheable-request "^6.0.0" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^4.1.0" - lowercase-keys "^1.0.1" - mimic-response "^1.0.1" - p-cancelable "^1.0.0" - to-readable-stream "^1.0.0" - url-parse-lax "^3.0.0" - -graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4: - version "4.2.6" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz" - integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== +got@^12.1.0: + version "12.6.1" + resolved "https://registry.npmjs.org/got/-/got-12.6.1.tgz" + integrity sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ== + dependencies: + "@sindresorhus/is" "^5.2.0" + "@szmarczak/http-timer" "^5.0.1" + cacheable-lookup "^7.0.0" + cacheable-request "^10.2.8" + decompress-response "^6.0.0" + form-data-encoder "^2.1.2" + get-stream "^6.0.1" + http2-wrapper "^2.1.10" + lowercase-keys "^3.0.0" + p-cancelable "^3.0.0" + responselike "^3.0.0" + +got@^13: + version "13.0.0" + resolved "https://registry.npmjs.org/got/-/got-13.0.0.tgz" + integrity sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA== + dependencies: + "@sindresorhus/is" "^5.2.0" + "@szmarczak/http-timer" "^5.0.1" + cacheable-lookup "^7.0.0" + cacheable-request "^10.2.8" + decompress-response "^6.0.0" + form-data-encoder "^2.1.2" + get-stream "^6.0.1" + http2-wrapper "^2.1.10" + lowercase-keys "^3.0.0" + p-cancelable "^3.0.0" + responselike "^3.0.0" + +graceful-fs@4.2.10: + version "4.2.10" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + +graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.2, graceful-fs@^4.2.4, graceful-fs@^4.2.6: + version "4.2.11" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== gray-matter@^2.1.0: version "2.1.1" resolved "https://registry.npmjs.org/gray-matter/-/gray-matter-2.1.1.tgz" - integrity sha1-MELZrewqHe1qdwep7SOA+KF6Qw4= + integrity sha512-vbmvP1Fe/fxuT2QuLVcqb2BfK7upGhhbLIt9/owWEvPYrZZEkelLcq2HqzxosV+PQ67dUFLaAeNpH7C4hhICAA== dependencies: ansi-red "^0.1.1" coffee-script "^1.12.4" @@ -4366,17 +6718,10 @@ gray-matter@^2.1.0: js-yaml "^3.8.1" toml "^2.3.2" -grouped-queue@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/grouped-queue/-/grouped-queue-1.1.0.tgz" - integrity sha512-rZOFKfCqLhsu5VqjBjEWiwrYqJR07KxIkH4mLZlNlGDfntbb4FbMyGFP14TlvRPrU9S3Hnn/sgxbC5ZeN0no3Q== - dependencies: - lodash "^4.17.15" - -growl@1.10.5: - version "1.10.5" - resolved "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== +grouped-queue@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/grouped-queue/-/grouped-queue-2.0.0.tgz" + integrity sha512-/PiFUa7WIsl48dUeCvhIHnwNmAAzlI/eHoJl0vu3nsFA366JleY7Ff8EVTplZu5kO0MIdZjKTTnzItL61ahbnw== gulp-header@^1.7.1: version "1.8.12" @@ -4390,7 +6735,7 @@ gulp-header@^1.7.1: har-schema@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== har-validator@~5.1.3: version "5.1.5" @@ -4400,75 +6745,56 @@ har-validator@~5.1.3: ajv "^6.12.3" har-schema "^2.0.0" -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" +hard-rejection@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz" + integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== -has-flag@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz" - integrity sha1-6CB68cx7MNRGzHC3NLXovhj4jVE= +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== has-flag@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz" - integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz" - integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= +has-property-descriptors@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" + get-intrinsic "^1.2.2" -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz" - integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz" - integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" + es-define-property "^1.0.0" -has-yarn@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz" - integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== -has@^1.0.3: +has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== dependencies: - function-bind "^1.1.1" + has-symbols "^1.0.2" hasha@^5.0.0: version "5.2.2" @@ -4478,26 +6804,53 @@ hasha@^5.0.0: is-stream "^2.0.0" type-fest "^0.8.0" +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + he@1.2.0, he@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +header-case@^2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz" + integrity sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q== + dependencies: + capital-case "^1.0.4" + tslib "^2.0.3" + +help-me@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz" + integrity sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg== + hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== -hosted-git-info@^3.0.6: - version "3.0.8" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.8.tgz" - integrity sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw== +hosted-git-info@^4.0.1: + version "4.1.0" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz" + integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== dependencies: lru-cache "^6.0.0" +hosted-git-info@^7.0.0, hosted-git-info@^7.0.2: + version "7.0.2" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz" + integrity sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w== + dependencies: + lru-cache "^10.0.1" + html-encoding-sniffer@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448" + resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz" integrity sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ== dependencies: whatwg-encoding "^3.1.1" @@ -4507,12 +6860,22 @@ html-escaper@^2.0.0: resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -http-cache-semantics@^4.0.0: +htmlparser2@^9.0.0: + version "9.1.0" + resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz" + integrity sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.1.0" + entities "^4.5.0" + +http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" - integrity "sha1-q+AvyymFRgvwMjvmZENuw0dqbVo= sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" + resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== -http-call@^5.1.2: +http-call@^5.2.2: version "5.3.0" resolved "https://registry.npmjs.org/http-call/-/http-call-5.3.0.tgz" integrity sha512-ahwimsC23ICE4kPl9xTBjKB4inbRaeLyZeRunC/1Jy/Z6X8tv22MEAjK+KBOMSVLaqXPTTmd8638waVIKLGx2w== @@ -4525,30 +6888,21 @@ http-call@^5.1.2: tunnel-agent "^0.6.0" http-parser-js@>=0.5.1: - version "0.5.3" - resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz" - integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg== + version "0.5.8" + resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz" + integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== -http-proxy-agent@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.1.tgz#f1c7df4bd6c30ba90f2c713fd4b60d3989d4b3d9" - integrity sha512-My1KCEPs6A0hb4qCVzYp8iEvA8j8YqcvXLZZH8C9OFuTYpYjHE7N2dtG3mRl1HMD4+VGXpF3XcDVcxGBT7yDZQ== +http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.1, http-proxy-agent@^7.0.2: + version "7.0.2" + resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== dependencies: agent-base "^7.1.0" debug "^4.3.4" -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - http-signature@~1.3.1: version "1.3.6" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9" + resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz" integrity sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw== dependencies: assert-plus "^1.0.0" @@ -4557,24 +6911,40 @@ http-signature@~1.3.1: http2-wrapper@^1.0.0-beta.5.2: version "1.0.3" - resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + resolved "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz" integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== dependencies: quick-lru "^5.1.1" resolve-alpn "^1.0.0" -https-proxy-agent@5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz" - integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== +http2-wrapper@^2.1.10: + version "2.2.1" + resolved "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz" + integrity sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.2.0" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== dependencies: agent-base "6" debug "4" -https-proxy-agent@^7.0.2: - version "7.0.3" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.3.tgz#93f115f0f106a746faf364d1301b2e561cdf70de" - integrity sha512-kCnwztfX0KZJSLOBrcL0emLeFako55NWMovvyPP2AjsghNk9RB1yjSI+jVumPHYZsNXegNoqupSW9IY3afSH8w== +https-proxy-agent@^7.0.1: + version "7.0.2" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz" + integrity sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA== + dependencies: + agent-base "^7.0.2" + debug "4" + +https-proxy-agent@^7.0.3, https-proxy-agent@^7.0.5: + version "7.0.5" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz" + integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== dependencies: agent-base "^7.0.2" debug "4" @@ -4584,21 +6954,41 @@ human-signals@^1.1.1: resolved "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +human-signals@^4.3.0: + version "4.3.1" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz" + integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== + +human-signals@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz" + integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== + humanize-ms@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + resolved "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz" integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== dependencies: ms "^2.0.0" +husky@^7.0.4: + version "7.0.4" + resolved "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz" + integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ== + hyperlinker@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/hyperlinker/-/hyperlinker-1.0.0.tgz" integrity sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ== -iconv-lite@0.6.3: +iconv-lite@0.6.3, iconv-lite@^0.6.2: version "0.6.3" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== dependencies: safer-buffer ">= 2.1.2 < 3.0.0" @@ -4610,37 +7000,29 @@ iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -ieee754@^1.1.13: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -ignore@^3.3.5: - version "3.3.10" - resolved "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz" - integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== - -ignore@^4.0.3, ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^5.1.1, ignore@^5.1.4: - version "5.1.8" - resolved "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz" - integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== +ignore-walk@^6.0.4: + version "6.0.5" + resolved "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.5.tgz" + integrity sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A== + dependencies: + minimatch "^9.0.0" -ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== +ignore@^5.2.0, ignore@^5.2.4, ignore@^5.3.0: + version "5.3.1" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== immediate@~3.0.5: version "3.0.6" - resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + resolved "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz" integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== -import-fresh@^3.0.0, import-fresh@^3.2.1: +import-fresh@^3.0.0, import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -4648,30 +7030,25 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" -import-lazy@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz" - integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= - imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= - -indent-string@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz" - integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== indent-string@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== +index-to-position@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/index-to-position/-/index-to-position-0.1.2.tgz" + integrity sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" @@ -4681,19 +7058,24 @@ inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz" - integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== +ini@4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz" + integrity sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g== -ini@~1.3.0: +ini@^1.3.4, ini@~1.3.0: version "1.3.8" resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +ini@^4.1.3: + version "4.1.3" + resolved "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz" + integrity sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg== + inquirer@^10.1.8: version "10.1.8" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-10.1.8.tgz#727887af8a3646d8dd3eebc098c42884dc4d7151" + resolved "https://registry.npmjs.org/inquirer/-/inquirer-10.1.8.tgz" integrity sha512-syxGpOzLyqVeZi1KDBjRTnCn5PiGWySGHP0BbqXbqsEK0ckkZk3egAepEWslUjZXj0rhkUapVXM/IpADWe4D6w== dependencies: "@inquirer/prompts" "^5.3.8" @@ -4704,63 +7086,71 @@ inquirer@^10.1.8: run-async "^3.0.0" rxjs "^7.8.1" -inquirer@^7.1.0: - version "7.3.3" - resolved "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz" - integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== +inquirer@^9.2.2: + version "9.3.6" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-9.3.6.tgz#670f1e9408743c3ed23df576f94fe5369f353055" + integrity sha512-riK/iQB2ctwkpWYgjjWIRv3MBLt2gzb2Sj0JNQNbyTXgyXsLWcDPJ5WS5ZDTCx7BRFnJsARtYh+58fjP5M2Y0Q== dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.19" - mute-stream "0.0.8" - run-async "^2.4.0" - rxjs "^6.6.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" + "@inquirer/figures" "^1.0.3" + ansi-escapes "^4.3.2" + cli-width "^4.1.0" + external-editor "^3.1.0" + mute-stream "1.0.0" + ora "^5.4.1" + run-async "^3.0.0" + rxjs "^7.8.1" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wrap-ansi "^6.2.0" + yoctocolors-cjs "^2.1.2" + +internal-slot@^1.0.5: + version "1.0.6" + resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz" + integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg== + dependencies: + get-intrinsic "^1.2.2" + hasown "^2.0.0" + side-channel "^1.0.4" interpret@^1.0.0: version "1.4.0" resolved "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== -ioredis@~4.17.1: - version "4.17.3" - resolved "https://registry.npmjs.org/ioredis/-/ioredis-4.17.3.tgz" - integrity sha512-iRvq4BOYzNFkDnSyhx7cmJNOi1x/HWYe+A4VXHBu4qpwJaGT1Mp+D2bVGJntH9K/Z/GeOM/Nprb8gB3bmitz1Q== +ip-address@^9.0.5: + version "9.0.5" + resolved "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz" + integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== dependencies: - cluster-key-slot "^1.1.0" - debug "^4.1.1" - denque "^1.1.0" - lodash.defaults "^4.2.0" - lodash.flatten "^4.4.0" - redis-commands "1.5.0" - redis-errors "^1.2.0" - redis-parser "^3.0.0" - standard-as-callback "^2.0.1" - -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz" - integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= - dependencies: - kind-of "^3.0.2" + jsbn "1.1.0" + sprintf-js "^1.1.3" -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== dependencies: - kind-of "^6.0.0" + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + is-typed-array "^1.1.10" is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" is-binary-path@~2.1.0: version "2.1.0" @@ -4769,83 +7159,64 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-ci@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz" - integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== - dependencies: - ci-info "^2.0.0" - -is-core-module@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz" - integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== - dependencies: - has "^1.0.3" - -is-core-module@^2.8.0: - version "2.8.1" - resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz" - integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== - dependencies: - has "^1.0.3" - -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz" - integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= +is-builtin-module@^3.2.1: + version "3.2.1" + resolved "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz" + integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== dependencies: - kind-of "^3.0.2" + builtin-modules "^3.3.0" -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== - dependencies: - kind-of "^6.0.0" +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== +is-core-module@^2.13.0, is-core-module@^2.13.1, is-core-module@^2.5.0: + version "2.13.1" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" + hasown "^2.0.0" -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" + has-tostringtag "^1.0.0" is-docker@^2.0.0: - version "2.1.1" - resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz" - integrity sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw== - -is-docker@^2.1.1: version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + is-electron@2.2.2: version "2.2.2" - resolved "https://registry.yarnpkg.com/is-electron/-/is-electron-2.2.2.tgz#3778902a2044d76de98036f5dc58089ac4d80bb9" + resolved "https://registry.npmjs.org/is-electron/-/is-electron-2.2.2.tgz" integrity sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg== -is-extendable@^0.1.0, is-extendable@^0.1.1: +is-extendable@^0.1.0: version "0.1.1" resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== is-extendable@^1.0.1: version "1.0.1" @@ -4854,71 +7225,86 @@ is-extendable@^1.0.1: dependencies: is-plain-object "^2.0.4" -is-extglob@^2.1.0, is-extglob@^2.1.1: +is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz" - integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= +is-fullwidth-code-point@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz" + integrity sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA== dependencies: - is-extglob "^2.1.0" + get-east-asian-width "^1.0.0" -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" -is-installed-globally@^0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz" - integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== +is-in-ci@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-in-ci/-/is-in-ci-1.0.0.tgz" + integrity sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg== + +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + +is-installed-globally@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-1.0.0.tgz" + integrity sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ== dependencies: - global-dirs "^3.0.0" - is-path-inside "^3.0.2" + global-directory "^4.0.1" + is-path-inside "^4.0.0" is-interactive@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== -is-npm@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz" - integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== +is-interactive@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz" + integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== + +is-lambda@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz" + integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== + +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-npm@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/is-npm/-/is-npm-6.0.0.tgz" + integrity sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" is-number@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz" - integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= - dependencies: - kind-of "^3.0.2" - -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + integrity sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg== dependencies: kind-of "^3.0.2" @@ -4937,22 +7323,32 @@ is-obj@^2.0.0: resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-path-inside@^3.0.2: +is-path-inside@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== +is-path-inside@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz" + integrity sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA== + is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz" - integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== -is-plain-obj@^2.0.0, is-plain-obj@^2.1.0: +is-plain-obj@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== -is-plain-object@^2.0.3, is-plain-object@^2.0.4: +is-plain-obj@^4.0.0, is-plain-obj@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz" + integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== + +is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== @@ -4961,17 +7357,17 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: is-plain-object@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.1.tgz#662d92d24c0aa4302407b0d45d21f2251c85f85b" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz" integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g== is-plain-object@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== is-potential-custom-element-name@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== is-primitive@^3.0.1: @@ -4979,120 +7375,175 @@ is-primitive@^3.0.1: resolved "https://registry.npmjs.org/is-primitive/-/is-primitive-3.0.1.tgz" integrity sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w== -is-redirect@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz" - integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ= +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" -is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0: +is-retry-allowed@^1.1.0: version "1.2.0" resolved "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz" integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== -is-scoped@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-scoped/-/is-scoped-1.0.0.tgz" - integrity sha1-RJypgpnnEwOCViieyytUDcQ3yzA= +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== dependencies: - scoped-regex "^1.0.0" + call-bind "^1.0.2" -is-stream@^1.0.0, is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= +is-stream@^2, is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== -is-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz" - integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-text-path@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz" + integrity sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w== + dependencies: + text-extensions "^1.0.0" + +is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9: + version "1.1.12" + resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== + dependencies: + which-typed-array "^1.1.11" is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== is-unicode-supported@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== -is-utf8@^0.2.0, is-utf8@^0.2.1: +is-unicode-supported@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz" + integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== + +is-unicode-supported@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz" + integrity sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q== + +is-utf8@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q== -is-windows@^1.0.1, is-windows@^1.0.2: +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-windows@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== -is-wsl@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz" - integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= - -is-wsl@^2.1.1, is-wsl@^2.2.0: +is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== dependencies: is-docker "^2.0.0" -is-yarn-global@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz" - integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== +is-wsl@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== + dependencies: + is-inside-container "^1.0.0" isarray@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== -isbinaryfile@^4.0.0: - version "4.0.8" - resolved "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.8.tgz" - integrity sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w== +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isbinaryfile@5.0.2, isbinaryfile@^5.0.0: + version "5.0.2" + resolved "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.2.tgz" + integrity sha512-GvcjojwonMjWbTkfMpnVHVqXW/wKMYDfEpY94/8zy8HFMOqb/VL6oeONq9v87q4ttVlaTLnGXnJD4B5B1OTGIg== isexe@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isexe@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz" + integrity sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ== isobject@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + integrity sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA== dependencies: isarray "1.0.0" -isobject@^3.0.0, isobject@^3.0.1: +isobject@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== -isomorphic-dompurify@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/isomorphic-dompurify/-/isomorphic-dompurify-2.3.0.tgz#bc48fbdf52f84cf7e0a63a5e8ec89052e7dbc3c5" - integrity sha512-FCoKY4/mW/jnn/+VgE7wXGC2D/RXzVCAmGYuGWEuZXtyWnwmE2100caciIv+RbHk90q9LA0OW5IBn2f+ywHtww== +isomorphic-dompurify@^2.15.0: + version "2.15.0" + resolved "https://registry.npmjs.org/isomorphic-dompurify/-/isomorphic-dompurify-2.15.0.tgz" + integrity sha512-RDHlyeVmwEDAPZuX1VaaBzSn9RrsfvswxH7faEQK9cTHC1dXeNuK6ElUeSr7locFyeLguut8ASfhQWxHB4Ttug== dependencies: "@types/dompurify" "^3.0.5" - dompurify "^3.0.8" - jsdom "^24.0.0" + dompurify "^3.1.6" + jsdom "^25.0.0" isstream@~0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== -istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.0.0-alpha.1: - version "3.0.0" - resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz" - integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz" + integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== istanbul-lib-hook@^3.0.0: version "3.0.0" @@ -5112,17 +7563,16 @@ istanbul-lib-instrument@^4.0.0: semver "^6.3.0" istanbul-lib-processinfo@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz" - integrity sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw== + version "2.0.3" + resolved "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz" + integrity sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg== dependencies: archy "^1.0.0" - cross-spawn "^7.0.0" - istanbul-lib-coverage "^3.0.0-alpha.1" - make-dir "^3.0.0" + cross-spawn "^7.0.3" + istanbul-lib-coverage "^3.2.0" p-map "^3.0.0" rimraf "^3.0.0" - uuid "^3.3.3" + uuid "^8.3.2" istanbul-lib-report@^3.0.0: version "3.0.0" @@ -5134,34 +7584,43 @@ istanbul-lib-report@^3.0.0: supports-color "^7.1.0" istanbul-lib-source-maps@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz" - integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== + version "4.0.1" + resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== dependencies: debug "^4.1.1" istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" istanbul-reports@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz" - integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== + version "3.1.5" + resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz" + integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -istextorbinary@^2.5.1: - version "2.6.0" - resolved "https://registry.npmjs.org/istextorbinary/-/istextorbinary-2.6.0.tgz" - integrity sha512-+XRlFseT8B3L9KyjxxLjfXSLMuErKDsd8DBNrsaxoViABMEZlOSCstwmw0qpoFX3+U6yWU1yhLudAe6/lETGGA== +jackspeak@^2.3.5: + version "2.3.6" + resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz" + integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== dependencies: - binaryextensions "^2.1.2" - editions "^2.2.0" - textextensions "^2.5.0" + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" jackspeak@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.0.1.tgz#9fca4ce961af6083e259c376e9e3541431f5287b" + resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.1.tgz" integrity sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog== dependencies: "@isaacs/cliui" "^8.0.2" @@ -5170,7 +7629,7 @@ jackspeak@^4.0.1: jake@^10.8.5: version "10.8.5" - resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" + resolved "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz" integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw== dependencies: async "^3.2.3" @@ -5180,25 +7639,35 @@ jake@^10.8.5: jira-client@^8.2.2: version "8.2.2" - resolved "https://registry.yarnpkg.com/jira-client/-/jira-client-8.2.2.tgz#b1eb679cf5c78e6398af73260b2f6f8b782b15d2" + resolved "https://registry.npmjs.org/jira-client/-/jira-client-8.2.2.tgz" integrity sha512-zs4BKJQYcdCoXi3G0JI9NFqkt7olUNQZ6bzZwUmZ+at3rjjmiBPVNYQHBgkBEOpX8iN5dppU0oYRVtCYRjSwXQ== dependencies: "@babel/runtime" "^7.6.0" postman-request "^2.88.1-postman.30" +joycon@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz" + integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== + +js-md4@^0.3.2: + version "0.3.2" + resolved "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz" + integrity sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA== + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@4.0.0, js-yaml@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz" - integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q== +js-yaml@4.1.0, js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" -js-yaml@^3.13.1, js-yaml@^3.8.1: +js-yaml@^3.13.1, js-yaml@^3.14.1, js-yaml@^3.8.1: version "3.14.1" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -5206,80 +7675,69 @@ js-yaml@^3.13.1, js-yaml@^3.8.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== +js2xmlparser@^4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz" + integrity sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA== dependencies: - argparse "^2.0.1" + xmlcreate "^2.0.4" + +jsbn@1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz" + integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== jsbn@~0.1.0: version "0.1.1" resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== -jsdom@^24.0.0: - version "24.0.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-24.0.0.tgz#e2dc04e4c79da368481659818ee2b0cd7c39007c" - integrity sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A== +jsdoc-type-pratt-parser@~4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz" + integrity sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ== + +jsdom@^25.0.0: + version "25.0.0" + resolved "https://registry.npmjs.org/jsdom/-/jsdom-25.0.0.tgz" + integrity sha512-OhoFVT59T7aEq75TVw9xxEfkXgacpqAhQaYgP9y/fDqWQCMB/b1H66RfmPm/MaeaAIU9nDwMOVTlPN51+ao6CQ== dependencies: cssstyle "^4.0.1" data-urls "^5.0.0" decimal.js "^10.4.3" form-data "^4.0.0" html-encoding-sniffer "^4.0.0" - http-proxy-agent "^7.0.0" - https-proxy-agent "^7.0.2" + http-proxy-agent "^7.0.2" + https-proxy-agent "^7.0.5" is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.7" + nwsapi "^2.2.12" parse5 "^7.1.2" - rrweb-cssom "^0.6.0" + rrweb-cssom "^0.7.1" saxes "^6.0.0" symbol-tree "^3.2.4" - tough-cookie "^4.1.3" + tough-cookie "^4.1.4" w3c-xmlserializer "^5.0.0" webidl-conversions "^7.0.0" whatwg-encoding "^3.1.1" whatwg-mimetype "^4.0.0" whatwg-url "^14.0.0" - ws "^8.16.0" + ws "^8.18.0" xml-name-validator "^5.0.0" -jsen@0.6.6: - version "0.6.6" - resolved "https://registry.npmjs.org/jsen/-/jsen-0.6.6.tgz" - integrity sha1-AkDBjPETUKwCFFb0in6xO9Z+BCA= - jsesc@^2.5.1: version "2.5.2" resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -jsforce@^1.11.0: - version "1.11.0" - resolved "https://registry.npmjs.org/jsforce/-/jsforce-1.11.0.tgz" - integrity sha512-vYNXJXXdz9ZQNdfRqq/MCJ/zU7JGA7iEduwafQDzChR9FeqXgTNfHTppLVbw9mIniKkQZemmxSOtl7N04lj/5Q== - dependencies: - base64-url "^2.2.0" - co-prompt "^1.0.0" - coffeescript "^1.10.0" - commander "^2.9.0" - csv-parse "^4.10.1" - csv-stringify "^1.0.4" - faye "^1.4.0" - inherits "^2.0.1" - lodash "^4.17.19" - multistream "^2.0.5" - opn "^5.3.0" - promise "^7.1.1" - readable-stream "^2.1.0" - request "^2.72.0" - xml2js "^0.4.16" - -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz" - integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= +jsesc@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz" + integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz" + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== json-buffer@3.0.1: version "3.0.1" @@ -5296,6 +7754,11 @@ json-parse-even-better-errors@^2.3.0: resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== +json-parse-even-better-errors@^3.0.0, json-parse-even-better-errors@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz" + integrity sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" @@ -5306,35 +7769,47 @@ json-schema-traverse@^1.0.0: resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-schema@0.4.0: +json-schema@0.4.0, json-schema@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz" integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json-stringify-nice@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz" + integrity sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw== -json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: +json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" -json5@^2.1.2: +json5@^2.2.1, json5@^2.2.3: version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" - integrity "sha1-eM1vGhm9wStz21rQxh79ZsHikoM= sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonc-parser@^3.0.0, jsonc-parser@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz" + integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== optionalDependencies: graceful-fs "^4.1.6" @@ -5347,17 +7822,17 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -jsonparse@^1.2.0: +jsonparse@^1.2.0, jsonparse@^1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz" - integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== -jsonwebtoken@8.5.0: - version "8.5.0" - resolved "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz" - integrity sha512-IqEycp0znWHNA11TpYi77bVgyBO/pGESDh7Ajhas+u0ttkGkKYIIAjniL4Bw5+oVejVF+SYkaI7XKfwCCyeTuA== +jsonwebtoken@9.0.2: + version "9.0.2" + resolved "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz" + integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== dependencies: - jws "^3.2.1" + jws "^3.2.2" lodash.includes "^4.3.0" lodash.isboolean "^3.0.3" lodash.isinteger "^4.0.4" @@ -5366,21 +7841,11 @@ jsonwebtoken@8.5.0: lodash.isstring "^4.0.1" lodash.once "^4.0.0" ms "^2.1.1" - semver "^5.6.0" - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" + semver "^7.5.4" jsprim@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" + resolved "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz" integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ== dependencies: assert-plus "1.0.0" @@ -5388,9 +7853,9 @@ jsprim@^2.0.2: json-schema "0.4.0" verror "1.10.0" -jszip@^3.10.1: +jszip@3.10.1, jszip@^3.10.1: version "3.10.1" - resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" + resolved "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz" integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== dependencies: lie "~3.3.0" @@ -5398,10 +7863,30 @@ jszip@^3.10.1: readable-stream "~2.3.6" setimmediate "^1.0.5" +just-diff-apply@^5.2.0: + version "5.5.0" + resolved "https://registry.npmjs.org/just-diff-apply/-/just-diff-apply-5.5.0.tgz" + integrity sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw== + +just-diff@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/just-diff/-/just-diff-5.2.0.tgz" + integrity sha512-6ufhP9SHjb7jibNFrNxyFZ6od3g+An6Ai9mhGRvcYe8UJlH0prseN64M+6ZBBUoKYHZsitDP42gAJ8+eVWr3lw== + +just-diff@^6.0.0: + version "6.0.2" + resolved "https://registry.npmjs.org/just-diff/-/just-diff-6.0.2.tgz" + integrity sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA== + just-extend@^4.0.2: - version "4.1.1" - resolved "https://registry.npmjs.org/just-extend/-/just-extend-4.1.1.tgz" - integrity sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA== + version "4.2.1" + resolved "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz" + integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg== + +just-extend@^6.2.0: + version "6.2.0" + resolved "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz" + integrity sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw== jwa@^1.4.1: version "1.4.1" @@ -5412,7 +7897,7 @@ jwa@^1.4.1: ecdsa-sig-formatter "1.0.11" safe-buffer "^5.0.1" -jws@^3.2.1: +jws@^3.2.2: version "3.2.2" resolved "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz" integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== @@ -5420,73 +7905,66 @@ jws@^3.2.1: jwa "^1.4.1" safe-buffer "^5.0.1" -keypress@~0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/keypress/-/keypress-0.2.1.tgz" - integrity sha1-HoBFQlABjbrUw/6USX1uZ7YmnHc= - -keyv@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz" - integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== - dependencies: - json-buffer "3.0.0" - -keyv@^4.0.0: - version "4.5.2" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.2.tgz#0e310ce73bf7851ec702f2eaf46ec4e3805cce56" - integrity sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g== +keyv@^4.0.0, keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== dependencies: json-buffer "3.0.1" -keyv@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz" - integrity sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA== +keyv@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/keyv/-/keyv-5.0.1.tgz" + integrity sha512-NH+3ditq1O5uTSQiiHrGOTkwUniRox/lZ8tHARdsu5Skyv0AhZca0OCycWfR1fTECvSRftMQnXqx7cBpxo8G1g== dependencies: - json-buffer "3.0.1" + "@keyv/serialize" "*" -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: +kind-of@^3.0.2: version "3.2.2" resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== dependencies: is-buffer "^1.1.5" -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0: - version "5.1.0" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2: +kind-of@^6.0.0, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -latest-version@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz" - integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +ky@^1.2.0: + version "1.7.1" + resolved "https://registry.npmjs.org/ky/-/ky-1.7.1.tgz" + integrity sha512-KJ/IXXkFhTDqxcN8wKqMXk1/UoOpc0UnOB6H7QcqlPInh/M2B5Mlj+i9exez1w4RSwJhNFmHiUDPriAYFwb5VA== + +latest-version@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz" + integrity sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg== + dependencies: + package-json "^8.1.0" + +latest-version@^9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/latest-version/-/latest-version-9.0.0.tgz" + integrity sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA== dependencies: - package-json "^6.3.0" + package-json "^10.0.0" -lazy-cache@^2.0.1, lazy-cache@^2.0.2: +lazy-cache@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz" - integrity sha1-uRkKT5EzVGlIQIWfio9whNiCImQ= + integrity sha512-7vp2Acd2+Kz4XkzxGxaB1FWOi8KjWIWsgdfD5MCb86DWvlLqhRPM+d6Pro3iNEL5VT9mstz5hKAlcd+QR6H3aA== dependencies: set-getter "^0.1.0" lazystream@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" + resolved "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz" integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw== dependencies: readable-stream "^2.0.5" @@ -5501,53 +7979,56 @@ levn@^0.4.1: li@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/li/-/li-1.3.0.tgz#22c59bcaefaa9a8ef359cf759784e4bf106aea1b" + resolved "https://registry.npmjs.org/li/-/li-1.3.0.tgz" integrity sha512-z34TU6GlMram52Tss5mt1m//ifRIpKH5Dqm7yUVOdHI+BQCs9qGPHFaCUTIzsWX7edN30aa2WrPwR7IO10FHaw== lie@~3.3.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + resolved "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz" integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== dependencies: immediate "~3.0.5" +lilconfig@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz" + integrity sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow== + lines-and-columns@^1.1.6: - version "1.1.6" - resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz" - integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + version "1.2.4" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +linkinator@^6.0.4: + version "6.0.4" + resolved "https://registry.npmjs.org/linkinator/-/linkinator-6.0.4.tgz" + integrity sha512-gxZ9ePUBeoaCk29p+2gAwrRIVnKOqAV8ZVCEM8N7MvpwbccDBQiKjXFyS6nQx56K1CCZLboan2i5iJfpWCsnSQ== + dependencies: + chalk "^5.0.0" + escape-html "^1.0.3" + gaxios "^6.0.0" + glob "^10.3.10" + htmlparser2 "^9.0.0" + marked "^10.0.0" + meow "^13.0.0" + mime "^4.0.0" + server-destroy "^1.0.1" + srcset "^5.0.0" list-item@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/list-item/-/list-item-1.1.1.tgz" - integrity sha1-DGXQDih8tmPMs8s4Sad+iewmilY= + integrity sha512-S3D0WZ4J6hyM8o5SNKWaMYB1ALSacPZ2nHGEuCjmHZ+dc03gFeNZoNDcqfcnO4vDhTZmNrqrpYZCdXsRh22bzw== dependencies: expand-range "^1.8.1" extend-shallow "^2.0.1" is-number "^2.1.0" repeat-string "^1.5.2" -listenercount@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/listenercount/-/listenercount-1.0.1.tgz#84c8a72ab59c4725321480c975e6508342e70937" - integrity sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ== - -load-json-file@^6.2.0: - version "6.2.0" - resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-6.2.0.tgz" - integrity sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ== - dependencies: - graceful-fs "^4.1.15" - parse-json "^5.0.0" - strip-bom "^4.0.0" - type-fest "^0.6.0" - -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== - dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" +listenercount@~1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz" + integrity sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ== locate-path@^5.0.0: version "5.0.0" @@ -5563,117 +8044,147 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -locate-path@^7.1.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-7.1.1.tgz#8e1e5a75c7343770cef02ff93c4bf1f0aa666374" - integrity sha512-vJXaRMJgRVD3+cUZs3Mncj2mxpt5mP0EmNOsxRSZRMlbqjvxzDEOIUWXGmavo0ZC9+tNZCBLQ66reA11nbpHZg== +locate-path@^7.1.0, locate-path@^7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz" + integrity sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA== dependencies: p-locate "^6.0.0" lodash-es@^4.17.21: version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" + resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz" - integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= + integrity sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA== + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== lodash.defaults@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz" - integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= + integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== lodash.difference@^4.5.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c" + resolved "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz" integrity sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA== lodash.escaperegexp@^4.1.2: version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" + resolved "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz" integrity sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw== lodash.flatten@^4.4.0: version "4.4.0" resolved "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz" - integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= + integrity sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g== lodash.flattendeep@^4.4.0: version "4.4.0" resolved "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz" - integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= + integrity sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ== -lodash.get@^4.4.2, lodash.get@~4.4.2: +lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz" - integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= + integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== lodash.groupby@^4.6.0: version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.groupby/-/lodash.groupby-4.6.0.tgz#0b08a1dcf68397c397855c3239783832df7403d1" + resolved "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz" integrity sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw== lodash.includes@^4.3.0: version "4.3.0" resolved "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz" - integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== lodash.isboolean@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz" - integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== lodash.isequal@^4.5.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + resolved "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz" integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== lodash.isfunction@^3.0.9: version "3.0.9" - resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" + resolved "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz" integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw== lodash.isinteger@^4.0.4: version "4.0.4" resolved "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz" - integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== lodash.isnil@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/lodash.isnil/-/lodash.isnil-4.0.0.tgz#49e28cd559013458c814c5479d3c663a21bfaa6c" + resolved "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz" integrity sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng== lodash.isnumber@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz" - integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== lodash.isplainobject@^4.0.6: version "4.0.6" resolved "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz" - integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== lodash.isstring@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz" - integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== lodash.isundefined@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz#23ef3d9535565203a66cefd5b830f848911afb48" + resolved "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz" integrity sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA== +lodash.kebabcase@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz" + integrity sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.mergewith@^4.6.2: + version "4.6.2" + resolved "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + lodash.once@^4.0.0: version "4.1.1" resolved "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz" - integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== -lodash.set@^4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" - integrity "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM= sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg==" +lodash.snakecase@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz" + integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz" + integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== + +lodash.startcase@^4.4.0: + version "4.4.0" + resolved "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz" + integrity sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg== lodash.template@^4.4.0: version "4.5.0" @@ -5692,76 +8203,82 @@ lodash.templatesettings@^4.0.0: lodash.union@^4.6.0: version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" + resolved "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz" integrity sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw== lodash.uniq@^4.5.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== -lodash@4.17.21, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: +lodash.upperfirst@^4.3.1: + version "4.3.1" + resolved "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz" + integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg== + +lodash@^4.17.15, lodash@^4.17.21: version "4.17.21" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz" - integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== - dependencies: - chalk "^4.0.0" - -log-symbols@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== - dependencies: - chalk "^2.0.1" - -log-symbols@^4.1.0: +log-symbols@4.1.0, log-symbols@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: chalk "^4.1.0" is-unicode-supported "^0.1.0" -lolex@^2.4.2: - version "2.7.5" - resolved "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz" - integrity sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q== +log-symbols@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz" + integrity sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw== + dependencies: + chalk "^5.3.0" + is-unicode-supported "^1.3.0" -lolex@^5.0.1: - version "5.1.2" - resolved "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz" - integrity sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A== +log-symbols@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.0.tgz" + integrity sha512-zrc91EDk2M+2AXo/9BTvK91pqb7qrPg2nX/Hy+u8a5qQlbaOflCKO+6SqgZ+M+xUFxGdKTgwnGiL96b1W3ikRA== dependencies: - "@sinonjs/commons" "^1.7.0" + is-unicode-supported "^2.0.0" + yoctocolors "^2.1.1" -lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== +loupe@^2.3.6: + version "2.3.7" + resolved "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz" + integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== + dependencies: + get-func-name "^2.0.1" + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" lowercase-keys@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +lowercase-keys@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz" + integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== + +lru-cache@^10.0.1, lru-cache@^10.2.0, lru-cache@^10.2.2: + version "10.4.3" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + lru-cache@^11.0.0: version "11.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.0.0.tgz#15d93a196f189034d7166caf9fe55e7384c98a21" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.0.tgz" integrity sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA== -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" @@ -5769,6 +8286,21 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^7.14.1: + version "7.18.3" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + +"lru-cache@^9.1.1 || ^10.0.0": + version "10.1.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz" + integrity sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag== + +lunr@^2.3.9: + version "2.3.9" + resolved "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz" + integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== + make-dir@^3.0.0, make-dir@^3.0.2: version "3.1.0" resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" @@ -5781,22 +8313,38 @@ make-error@^1.1.1: resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= +make-fetch-happen@^13.0.0, make-fetch-happen@^13.0.1: + version "13.0.1" + resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz" + integrity sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA== + dependencies: + "@npmcli/agent" "^2.0.0" + cacache "^18.0.0" + http-cache-semantics "^4.1.1" + is-lambda "^1.0.1" + minipass "^7.0.2" + minipass-fetch "^3.0.0" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.3" + proc-log "^4.2.0" + promise-retry "^2.0.1" + ssri "^10.0.0" + +map-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" + integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz" - integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= - dependencies: - object-visit "^1.0.0" +map-obj@^4.0.0: + version "4.3.0" + resolved "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz" + integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== markdown-link@^0.1.1: version "0.1.1" resolved "https://registry.npmjs.org/markdown-link/-/markdown-link-0.1.1.tgz" - integrity sha1-MsXGUZmmRXMWMi0eQinRNAfIx88= + integrity sha512-TurLymbyLyo+kAUUAV9ggR9EPcDjP/ctlv9QAFiqUH7c+t6FlsbivPo9OKTU8xdOx9oNd2drW/Fi5RRElQbUqA== markdown-toc@^1.2.0: version "1.2.0" @@ -5816,205 +8364,321 @@ markdown-toc@^1.2.0: repeat-string "^1.6.1" strip-color "^0.1.0" -marked@^12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/marked/-/marked-12.0.0.tgz#051ea8c8c7f65148a63003df1499515a2c6de716" - integrity sha512-Vkwtq9rLqXryZnWaQc86+FHLC6tr/fycMfYAhiOIXkrNmeGAyhSxjqu0Rs1i0bBqw5u0S7+lV9fdH2ZSVaoa0w== +marked@^10.0.0: + version "10.0.0" + resolved "https://registry.npmjs.org/marked/-/marked-10.0.0.tgz" + integrity sha512-YiGcYcWj50YrwBgNzFoYhQ1hT6GmQbFG8SksnYJX1z4BXTHSOrz1GB5/Jm2yQvMg4nN1FHP4M6r03R10KrVUiA== + +marked@^14.1.0: + version "14.1.0" + resolved "https://registry.npmjs.org/marked/-/marked-14.1.0.tgz" + integrity sha512-P93GikH/Pde0hM5TAXEd8I4JAYi8IB03n8qzW8Bh1BIEFpEyBoYxi/XWZA53LSpTeLBiMQOoSMj0u5E/tiVYTA== + +marked@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz" + integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== math-random@^1.0.1: version "1.0.4" resolved "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz" integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== -mega-linter-runner@^4.39.0: - version "4.39.0" - resolved "https://registry.npmjs.org/mega-linter-runner/-/mega-linter-runner-4.39.0.tgz" - integrity sha512-QImjrVAHb6/7t0vijRbJpk8C/BmT66IwCL/CEJ0Jw4foKRmhnWhrAHBckFjxsVqMSH02dkD31QLNQ67n6r0bSg== +mega-linter-runner@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/mega-linter-runner/-/mega-linter-runner-8.0.0.tgz" + integrity sha512-dH5HpluOdebeDEk8yKKINaxQYjRiAqmzYEudyEn33AwDfiTTm/YCjNEeVieUQ5j9r1wxwAeMSyVRz3QBplBteg== dependencies: - chalk "^4.1.0" + chalk "^5.3.0" find-package-json "^1.2.0" - optionator "^0.9.1" - which "^2.0.2" - yeoman-environment "^2.10.3" - yeoman-generator "^4.12.0" - yosay "^2.0.2" - -mem-fs-editor@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/mem-fs-editor/-/mem-fs-editor-6.0.0.tgz" - integrity sha512-e0WfJAMm8Gv1mP5fEq/Blzy6Lt1VbLg7gNnZmZak7nhrBTibs+c6nQ4SKs/ZyJYHS1mFgDJeopsLAv7Ow0FMFg== - dependencies: + fs-extra "^11.1.1" + glob "^11.0.0" + mem-fs "^4.0.0" + open "^10.0.2" + optionator "^0.9.3" + prompts "^2.4.2" + uuid "^10.0.0" + which "^4.0.0" + yeoman-environment "^4.0.0" + yeoman-generator "^7.1.0" + +mem-fs-editor@^11.0.0, mem-fs-editor@^11.0.1: + version "11.1.1" + resolved "https://registry.npmjs.org/mem-fs-editor/-/mem-fs-editor-11.1.1.tgz" + integrity sha512-bjBczabthdOjNE9+gRk+wObTihMgwkrhr/ffk7e+adZk0F5I3i5fE/MApOwSG/JYmFAfpoThPkA6i+kKShcl0w== + dependencies: + "@types/ejs" "^3.1.4" + "@types/node" "^18.18.5" + binaryextensions "^6.11.0" commondir "^1.0.1" deep-extend "^0.6.0" - ejs "^2.6.1" - glob "^7.1.4" - globby "^9.2.0" - isbinaryfile "^4.0.0" - mkdirp "^0.5.0" - multimatch "^4.0.0" - rimraf "^2.6.3" - through2 "^3.0.1" - vinyl "^2.2.0" - -mem-fs-editor@^7.0.1: - version "7.1.0" - resolved "https://registry.npmjs.org/mem-fs-editor/-/mem-fs-editor-7.1.0.tgz" - integrity sha512-BH6QEqCXSqGeX48V7zu+e3cMwHU7x640NB8Zk8VNvVZniz+p4FK60pMx/3yfkzo6miI6G3a8pH6z7FeuIzqrzA== - dependencies: - commondir "^1.0.1" - deep-extend "^0.6.0" - ejs "^3.1.5" - glob "^7.1.4" - globby "^9.2.0" - isbinaryfile "^4.0.0" - mkdirp "^1.0.0" - multimatch "^4.0.0" - rimraf "^3.0.0" - through2 "^3.0.2" - vinyl "^2.2.1" + ejs "^3.1.10" + globby "^14.0.2" + isbinaryfile "5.0.2" + minimatch "^9.0.3" + multimatch "^7.0.0" + normalize-path "^3.0.0" + textextensions "^6.11.0" + vinyl "^3.0.0" -mem-fs@^1.1.0: - version "1.2.0" - resolved "https://registry.npmjs.org/mem-fs/-/mem-fs-1.2.0.tgz" - integrity sha512-b8g0jWKdl8pM0LqAPdK9i8ERL7nYrzmJfRhxMiWH2uYdfYnb7uXnmwVb0ZGe7xyEl4lj+nLIU3yf4zPUT+XsVQ== - dependencies: - through2 "^3.0.0" - vinyl "^2.0.1" - vinyl-file "^3.0.0" +mem-fs@^4.0.0: + version "4.1.0" + resolved "https://registry.npmjs.org/mem-fs/-/mem-fs-4.1.0.tgz" + integrity sha512-lOB7haBbxO43eZ/++GA+jBMHQ9DNJeliMt35jNutzCfAgEg5gblFCItnzsss8Z4t81bB5jsz77bptqelHQn0Qw== + dependencies: + "@types/node" "^20.8.3" + "@types/vinyl" "^2.0.8" + vinyl "^3.0.0" + vinyl-file "^5.0.0" + +meow@^13.0.0: + version "13.1.0" + resolved "https://registry.npmjs.org/meow/-/meow-13.1.0.tgz" + integrity sha512-o5R/R3Tzxq0PJ3v3qcQJtSvSE9nKOLSAaDuuoMzDVuGTwHdccMWcYomh9Xolng2tjT6O/Y83d+0coVGof6tqmA== + +meow@^8.0.0, meow@^8.1.2: + version "8.1.2" + resolved "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz" + integrity sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.18.0" + yargs-parser "^20.2.3" merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1: +merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -micromatch@^3.1.10: - version "3.1.10" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - -micromatch@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz" - integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== - dependencies: - braces "^3.0.1" - picomatch "^2.0.5" - -micromatch@^4.0.4: +micromatch@^4.0.2, micromatch@^4.0.4: version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: braces "^3.0.2" picomatch "^2.3.1" -mime-db@1.46.0: - version "1.46.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz" - integrity sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ== +micromatch@^4.0.5: + version "4.0.8" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" mime-db@1.52.0: version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.29" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz" - integrity sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ== - dependencies: - mime-db "1.46.0" - -mime-types@^2.1.35: +mime-types@^2.1.12, mime-types@^2.1.35: version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" mime@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" + resolved "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz" integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== +mime@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/mime/-/mime-4.0.1.tgz" + integrity sha512-5lZ5tyrIfliMXzFtkYyekWbtRXObT9OWa8IwQ5uxTBDHucNNwniRqo0yInflj+iYi5CBa6qxadGzGarDfuEOxA== + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mimic-response@^1.0.0, mimic-response@^1.0.1: +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + +mimic-function@^5.0.0: + version "5.0.1" + resolved "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz" + integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== + +mimic-response@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== mimic-response@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== -"minimatch@2 || 3", minimatch@^3.0.4, minimatch@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity "sha1-Gc0ZS/0+Qo8EmnCBfAONiatL41s= sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==" +mimic-response@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz" + integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg== + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== dependencies: - brace-expansion "^1.1.7" + brace-expansion "^2.0.1" -minimatch@3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== +minimatch@9.0.3: + version "9.0.3" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== dependencies: - brace-expansion "^1.1.7" + brace-expansion "^2.0.1" minimatch@^10.0.0: version "10.0.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz" integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ== dependencies: brace-expansion "^2.0.1" -minimatch@^5.1.0: +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1, minimatch@^5.1.0: version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz" integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== dependencies: brace-expansion "^2.0.1" -minimist@^1.1.0, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: - version "1.2.6" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== +minimatch@^9.0.0, minimatch@^9.0.5: + version "9.0.5" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.1, minimatch@^9.0.3, minimatch@^9.0.4: + version "9.0.4" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz" + integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== + dependencies: + brace-expansion "^2.0.1" + +minimist-options@4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz" + integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + kind-of "^6.0.3" -minimist@^1.2.6: +minimist@^1.2.0, minimist@^1.2.6, minimist@^1.2.8: version "1.2.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -minipass@^7.1.2: +minipass-collect@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz" + integrity sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw== + dependencies: + minipass "^7.0.3" + +minipass-fetch@^3.0.0: + version "3.0.5" + resolved "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz" + integrity sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg== + dependencies: + minipass "^7.0.3" + minipass-sized "^1.0.3" + minizlib "^2.1.2" + optionalDependencies: + encoding "^0.1.13" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + dependencies: + minipass "^3.0.0" + +minipass-pipeline@^1.2.4: + version "1.2.4" + resolved "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz" + integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + dependencies: + minipass "^3.0.0" + +minipass-sized@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz" + integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== + dependencies: + minipass "^3.0.0" + +minipass@^3.0.0: + version "3.3.6" + resolved "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": + version "7.0.4" + resolved "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz" + integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== + +minipass@^7.0.2, minipass@^7.0.3, minipass@^7.1.2: version "7.1.2" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== -mixin-deep@^1.1.3, mixin-deep@^1.2.0: +minizlib@^2.1.1, minizlib@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mitt@3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz" + integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== + +mixin-deep@^1.1.3: version "1.3.2" resolved "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz" integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== @@ -6022,231 +8686,168 @@ mixin-deep@^1.1.3, mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp-classic@^0.5.2: - version "0.5.3" - resolved "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz" - integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== - -mkdirp@1.0.4, mkdirp@^1.0.0: - version "1.0.4" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - "mkdirp@>=0.5 0": version "0.5.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== dependencies: minimist "^1.2.6" -mkdirp@^0.5.0, mkdirp@~0.5.1: - version "0.5.5" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mocha@^8.3.1: - version "8.3.1" - resolved "https://registry.npmjs.org/mocha/-/mocha-8.3.1.tgz" - integrity sha512-5SBMxANWqOv5bw3Hx+HVgaWlcWcFEQDUdaUAr1AUU+qwtx6cowhn7gEDT/DwQP7uYxnvShdUOVLbTYAHOEGfDQ== +mocha@^10.4.0: + version "10.4.0" + resolved "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz" + integrity sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA== dependencies: - "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" browser-stdout "1.3.1" - chokidar "3.5.1" - debug "4.3.1" + chokidar "3.5.3" + debug "4.3.4" diff "5.0.0" escape-string-regexp "4.0.0" find-up "5.0.0" - glob "7.1.6" - growl "1.10.5" + glob "8.1.0" he "1.2.0" - js-yaml "4.0.0" - log-symbols "4.0.0" - minimatch "3.0.4" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "5.0.1" ms "2.1.3" - nanoid "3.1.20" - serialize-javascript "5.0.1" + serialize-javascript "6.0.0" strip-json-comments "3.1.1" supports-color "8.1.1" - which "2.0.2" - wide-align "1.1.3" - workerpool "6.1.0" + workerpool "6.2.1" yargs "16.2.0" yargs-parser "20.2.4" yargs-unparser "2.0.0" -mock-stdin@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/mock-stdin/-/mock-stdin-1.0.0.tgz" - integrity sha512-tukRdb9Beu27t6dN+XztSRHq9J0B/CoAOySGzHfn8UTfmqipA5yNT/sDUEyYdAV3Hpka6Wx6kOMxuObdOex60Q== - -moment@^2.15.1, moment@^2.24.0: - version "2.29.4" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" - integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== - moment@^2.30.1: version "2.30.1" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" + resolved "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz" integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== -ms@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= +mri@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz" + integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== ms@2.1.2: version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.0.0, ms@^2.1.1: +ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -multimatch@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/multimatch/-/multimatch-4.0.0.tgz" - integrity sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ== +multimatch@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/multimatch/-/multimatch-7.0.0.tgz" + integrity sha512-SYU3HBAdF4psHEL/+jXDKHO95/m5P2RvboHT2Y0WtTttvJLP4H/2WS9WlQPFvF6C8d6SpLw8vjCnQOnVIVOSJQ== dependencies: - "@types/minimatch" "^3.0.3" - array-differ "^3.0.0" - array-union "^2.1.0" - arrify "^2.0.1" - minimatch "^3.0.4" + array-differ "^4.0.0" + array-union "^3.0.1" + minimatch "^9.0.3" -multistream@^2.0.5: - version "2.1.1" - resolved "https://registry.npmjs.org/multistream/-/multistream-2.1.1.tgz" - integrity sha512-xasv76hl6nr1dEy3lPvy7Ej7K/Lx3O/FCvwge8PeVJpciPPoNCbaANcNiBug3IpdvTveZUcAV0DJzdnUDMesNQ== +multistream@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/multistream/-/multistream-3.1.0.tgz" + integrity sha512-zBgD3kn8izQAN/TaL1PCMv15vYpf+Vcrsfub06njuYVYlzUldzpopTlrEZ53pZVEbfn3Shtv7vRFoOv6LOV87Q== dependencies: inherits "^2.0.1" - readable-stream "^2.0.5" - -mute-stream@0.0.8: - version "0.0.8" - resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" - integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + readable-stream "^3.4.0" -mute-stream@^1.0.0: +mute-stream@1.0.0, mute-stream@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" + resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz" integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== -mv@~2: - version "2.1.1" - resolved "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz" - integrity sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI= - dependencies: - mkdirp "~0.5.1" - ncp "~2.0.0" - rimraf "~2.4.0" - -nan@^2.0.8: - version "2.14.2" - resolved "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz" - integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== - -nanoid@3.1.20: - version "3.1.20" - resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz" - integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== - -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -natural-orderby@^2.0.1: +natural-orderby@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/natural-orderby/-/natural-orderby-2.0.3.tgz" integrity sha512-p7KTHxU0CUrcOXe62Zfrb5Z13nLvPhSWR/so3kFulUQU0sgUll2Z0LwpsLN351eOOD+hRGu/F1g+6xDfPeD++Q== -ncp@~2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz" - integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M= +natural-orderby@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/natural-orderby/-/natural-orderby-3.0.2.tgz" + integrity sha512-x7ZdOwBxZCEm9MM7+eQCjkrNLrW3rkBKNHVr78zbtqnMGVNlnDi6C/eUEYgxHNrcbu0ymvjzcwIL/6H1iHri9g== -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +negotiator@^0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -nise@^1.3.3: - version "1.5.3" - resolved "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz" - integrity sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ== +netmask@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz" + integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== + +nise@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/nise/-/nise-4.1.0.tgz" + integrity sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA== dependencies: - "@sinonjs/formatio" "^3.2.1" + "@sinonjs/commons" "^1.7.0" + "@sinonjs/fake-timers" "^6.0.0" "@sinonjs/text-encoding" "^0.7.1" just-extend "^4.0.2" - lolex "^5.0.1" path-to-regexp "^1.7.0" -nmtree@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/nmtree/-/nmtree-1.0.6.tgz#953e057ad545e9e627f1275bd25fea4e92c1cf63" - integrity sha512-SUPCoyX5w/lOT6wD/PZEymR+J899984tYEOYjuDqQlIOeX5NSb1MEsCcT0az+dhZD0MLAj5hGBZEpKQxuDdniA== +nise@^5.1.9: + version "5.1.9" + resolved "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz" + integrity sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww== dependencies: - commander "^2.11.0" + "@sinonjs/commons" "^3.0.0" + "@sinonjs/fake-timers" "^11.2.2" + "@sinonjs/text-encoding" "^0.7.2" + just-extend "^6.2.0" + path-to-regexp "^6.2.1" -nock@^13.0.0: - version "13.0.11" - resolved "https://registry.npmjs.org/nock/-/nock-13.0.11.tgz" - integrity sha512-sKZltNkkWblkqqPAsjYW0bm3s9DcHRPiMOyKO/PkfJ+ANHZ2+LA2PLe22r4lLrKgXaiSaDQwW3qGsJFtIpQIeQ== +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== dependencies: - debug "^4.1.0" - json-stringify-safe "^5.0.1" - lodash.set "^4.3.2" - propagate "^2.0.0" + lower-case "^2.0.2" + tslib "^2.0.3" -node-domexception@1.0.0, node-domexception@^1.0.0: +node-domexception@1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== -node-fetch@2.6.7, node-fetch@^2.6.0, node-fetch@^2.6.1: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - -node-fetch@^2.6.7: +node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@^2.6.9: version "2.7.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== dependencies: whatwg-url "^5.0.0" -node-fetch@^3.2.10: - version "3.2.10" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz" - integrity sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA== - dependencies: - data-uri-to-buffer "^4.0.0" - fetch-blob "^3.1.4" - formdata-polyfill "^4.0.10" +node-gyp@^10.0.0: + version "10.2.0" + resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-10.2.0.tgz" + integrity sha512-sp3FonBAaFe4aYTcFdZUn2NYkbP7xroPGYvQmP4Nl5PxamznItBnNCgjrVTKrEfQynInMsJvZrdmqUnysCJ8rw== + dependencies: + env-paths "^2.2.0" + exponential-backoff "^3.1.1" + glob "^10.3.10" + graceful-fs "^4.2.6" + make-fetch-happen "^13.0.0" + nopt "^7.0.0" + proc-log "^4.1.0" + semver "^7.3.5" + tar "^6.2.1" + which "^4.0.0" node-preload@^0.2.1: version "0.2.1" @@ -6255,10 +8856,17 @@ node-preload@^0.2.1: dependencies: process-on-spawn "^1.0.0" -node-releases@^1.1.71: - version "1.1.72" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz" - integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw== +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + +nopt@^7.0.0, nopt@^7.2.1: + version "7.2.1" + resolved "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz" + integrity sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w== + dependencies: + abbrev "^2.0.0" normalize-package-data@^2.5.0: version "2.5.0" @@ -6271,65 +8879,117 @@ normalize-package-data@^2.5.0: validate-npm-package-license "^3.0.1" normalize-package-data@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.0.tgz" - integrity sha512-6lUjEI0d3v6kFrtgA/lOx4zHCWULXsFNIjHolnZCKCTLA6m/G625cdn3O7eNmT0iD3jfo6HZ9cdImGZwf21prw== + version "3.0.3" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz" + integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== dependencies: - hosted-git-info "^3.0.6" - resolve "^1.17.0" - semver "^7.3.2" + hosted-git-info "^4.0.1" + is-core-module "^2.5.0" + semver "^7.3.4" validate-npm-package-license "^3.0.1" +normalize-package-data@^6, normalize-package-data@^6.0.0: + version "6.0.2" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz" + integrity sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g== + dependencies: + hosted-git-info "^7.0.0" + semver "^7.3.5" + validate-npm-package-license "^3.0.4" + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-url@^4.1.0: - version "4.5.1" - resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz" - integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== - normalize-url@^6.0.1: version "6.1.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz" integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== -npm-api@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/npm-api/-/npm-api-1.0.1.tgz" - integrity sha512-4sITrrzEbPcr0aNV28QyOmgn6C9yKiF8k92jn4buYAK8wmA5xo1qL3II5/gT1r7wxbXBflSduZ2K3FbtOrtGkA== +normalize-url@^8.0.0: + version "8.0.1" + resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz" + integrity sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w== + +npm-bundled@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.1.tgz" + integrity sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ== dependencies: - JSONStream "^1.3.5" - clone-deep "^4.0.1" - download-stats "^0.3.4" - moment "^2.24.0" - node-fetch "^2.6.0" - paged-request "^2.0.1" + npm-normalize-package-bin "^3.0.0" -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= +npm-install-checks@^6.0.0, npm-install-checks@^6.2.0: + version "6.3.0" + resolved "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz" + integrity sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw== + dependencies: + semver "^7.1.1" + +npm-normalize-package-bin@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz" + integrity sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ== + +npm-package-arg@^11.0.0, npm-package-arg@^11.0.2: + version "11.0.3" + resolved "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz" + integrity sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw== + dependencies: + hosted-git-info "^7.0.0" + proc-log "^4.0.0" + semver "^7.3.5" + validate-npm-package-name "^5.0.0" + +npm-packlist@^8.0.0: + version "8.0.2" + resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz" + integrity sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA== + dependencies: + ignore-walk "^6.0.4" + +npm-pick-manifest@^9.0.0, npm-pick-manifest@^9.0.1: + version "9.1.0" + resolved "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.1.0.tgz" + integrity sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA== dependencies: - path-key "^2.0.0" + npm-install-checks "^6.0.0" + npm-normalize-package-bin "^3.0.0" + npm-package-arg "^11.0.0" + semver "^7.3.5" -npm-run-path@^4.0.0: +npm-registry-fetch@^17.0.0, npm-registry-fetch@^17.0.1: + version "17.1.0" + resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-17.1.0.tgz" + integrity sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA== + dependencies: + "@npmcli/redact" "^2.0.0" + jsonparse "^1.3.1" + make-fetch-happen "^13.0.0" + minipass "^7.0.2" + minipass-fetch "^3.0.0" + minizlib "^2.1.2" + npm-package-arg "^11.0.0" + proc-log "^4.0.0" + +npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= +npm-run-path@^5.1.0: + version "5.3.0" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz" + integrity sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ== + dependencies: + path-key "^4.0.0" -nwsapi@^2.2.7: - version "2.2.7" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.7.tgz#738e0707d3128cb750dddcfe90e4610482df0f30" - integrity sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ== +nwsapi@^2.2.12: + version "2.2.12" + resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.12.tgz" + integrity sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w== nyc@^15.1.0: version "15.1.0" @@ -6369,66 +9029,143 @@ oauth-sign@~0.9.0: resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz" - integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" +object-inspect@^1.13.1, object-inspect@^1.9.0: + version "1.13.1" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== -object-inspect@^1.9.0: - version "1.12.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" - integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-treeify@^1.1.33: + version "1.1.33" + resolved "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.33.tgz" + integrity sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A== + +object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" -object-treeify@^1.1.4: - version "1.1.32" - resolved "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.32.tgz" - integrity sha512-0ZcGbbeSlvco6ipx/9M0MsYOV7Cao78sEYye9IAfKPXSUcz+5d7kFg1NCwAZIMq8ltO3LmEeVoyReb/pcAaNXg== +object.fromentries@^2.0.7: + version "2.0.7" + resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz" + integrity sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" -object-visit@^1.0.0: +object.groupby@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz" - integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + resolved "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz" + integrity sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ== dependencies: - isobject "^3.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" -object.pick@^1.2.0, object.pick@^1.3.0: +object.pick@^1.2.0: version "1.3.0" resolved "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz" - integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== dependencies: isobject "^3.0.1" +object.values@^1.1.7: + version "1.1.7" + resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz" + integrity sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +oclif@^4.14.31: + version "4.14.31" + resolved "https://registry.npmjs.org/oclif/-/oclif-4.14.31.tgz" + integrity sha512-AbeRAftNTRFNO7Q3++7QIbcx7+RXRHLfWLaVBgusEJcelUDH6wIdktVTKmVM2Mxda/JoNNf8KsUbP/PfsprrOA== + dependencies: + "@aws-sdk/client-cloudfront" "^3.645.0" + "@aws-sdk/client-s3" "^3.645.0" + "@inquirer/confirm" "^3.1.22" + "@inquirer/input" "^2.2.4" + "@inquirer/select" "^2.3.10" + "@oclif/core" "^4" + "@oclif/plugin-help" "^6.2.10" + "@oclif/plugin-not-found" "^3.2.16" + "@oclif/plugin-warn-if-update-available" "^3.1.11" + async-retry "^1.3.3" + chalk "^4" + change-case "^4" + debug "^4.3.4" + ejs "^3.1.10" + find-yarn-workspace-root "^2.0.0" + fs-extra "^8.1" + github-slugger "^2" + got "^13" + lodash "^4.17.21" + normalize-package-data "^6" + semver "^7.6.3" + sort-package-json "^2.10.1" + tiny-jsonc "^1.0.1" + validate-npm-package-name "^5.0.1" + +on-exit-leak-free@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz" + integrity sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w== + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" -onetime@^5.1.0: - version "5.1.2" - resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== +onetime@^5.1.0, onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + +onetime@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz" + integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== dependencies: - mimic-fn "^2.1.0" + mimic-function "^5.0.0" -open@8.4.2: - version "8.4.2" - resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" - integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== +open@^10.0.2, open@^10.1.0: + version "10.1.0" + resolved "https://registry.npmjs.org/open/-/open-10.1.0.tgz" + integrity sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw== dependencies: - define-lazy-prop "^2.0.0" - is-docker "^2.1.1" - is-wsl "^2.2.0" + default-browser "^5.2.1" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^3.1.0" -openai@^4.52.0: - version "4.52.0" - resolved "https://registry.yarnpkg.com/openai/-/openai-4.52.0.tgz#5f93bdbef05ca3407d92f7a68717234ac0ffd09e" - integrity sha512-xmiNcdA9QJ5wffHpZDpIsge6AsPTETJ6h5iqDNuFQ7qGSNtonHn8Qe0VHy4UwLE8rBWiSqh4j+iSvuYZSeKkPg== +openai@^4.56.1: + version "4.56.1" + resolved "https://registry.npmjs.org/openai/-/openai-4.56.1.tgz" + integrity sha512-XMsxdjrWBYgbP6EsDIwbhkQEgeyL2C41te/QrJm8kdfho22exhTUJ/cFJSmCTToam/RSOC1BlOylHvD6i/bmsA== dependencies: "@types/node" "^18.11.18" "@types/node-fetch" "^2.6.4" @@ -6437,30 +9174,22 @@ openai@^4.52.0: form-data-encoder "1.7.2" formdata-node "^4.3.2" node-fetch "^2.6.7" - web-streams-polyfill "^3.2.1" - -opn@^5.3.0: - version "5.5.0" - resolved "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz" - integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== - dependencies: - is-wsl "^1.1.0" -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" deep-is "^0.1.3" fast-levenshtein "^2.0.6" levn "^0.4.1" prelude-ls "^1.2.1" type-check "^0.4.0" - word-wrap "^1.2.3" -ora@5.4.1: +ora@^5.4.1: version "5.4.1" - resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + resolved "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz" integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== dependencies: bl "^4.1.0" @@ -6473,27 +9202,42 @@ ora@5.4.1: strip-ansi "^6.0.0" wcwidth "^1.0.1" +ora@^8.1.0: + version "8.1.0" + resolved "https://registry.npmjs.org/ora/-/ora-8.1.0.tgz" + integrity sha512-GQEkNkH/GHOhPFXcqZs3IDahXEQcQxsSjEkK4KvEEST4t7eNzoMjxTzef+EZ+JluDEV+Raoi3WQ2CflnRdSVnQ== + dependencies: + chalk "^5.3.0" + cli-cursor "^5.0.0" + cli-spinners "^2.9.2" + is-interactive "^2.0.0" + is-unicode-supported "^2.0.0" + log-symbols "^6.0.0" + stdin-discarder "^0.2.2" + string-width "^7.2.0" + strip-ansi "^7.1.0" + os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -p-cancelable@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz" - integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== p-cancelable@^2.0.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" + resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz" integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== +p-cancelable@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz" + integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== -p-limit@^2.0.0, p-limit@^2.2.0: +p-limit@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== @@ -6509,18 +9253,11 @@ p-limit@^3.0.2: p-limit@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz" integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== dependencies: yocto-queue "^1.0.0" -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" - p-locate@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" @@ -6537,7 +9274,7 @@ p-locate@^5.0.0: p-locate@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-6.0.0.tgz#3da9a49d4934b901089dca3302fa65dc5a05c04f" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz" integrity sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw== dependencies: p-limit "^4.0.0" @@ -6549,17 +9286,40 @@ p-map@^3.0.0: dependencies: aggregate-error "^3.0.0" -p-queue@^6.6.1: +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +p-queue@^6: version "6.6.2" - resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" + resolved "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz" integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== dependencies: eventemitter3 "^4.0.4" p-timeout "^3.2.0" -p-retry@^4.0.0: +p-queue@^7.3.0: + version "7.4.1" + resolved "https://registry.npmjs.org/p-queue/-/p-queue-7.4.1.tgz" + integrity sha512-vRpMXmIkYF2/1hLBKisKeVYJZ8S2tZ0zEAmIJgdVKP2nq0nh4qCdf8bgw+ZgKrkh71AOCaqzwbJJk1WtdcF3VA== + dependencies: + eventemitter3 "^5.0.1" + p-timeout "^5.0.2" + +p-queue@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/p-queue/-/p-queue-8.0.1.tgz" + integrity sha512-NXzu9aQJTAzbBqOt2hwsR63ea7yvxJc0PwN/zobNAudYfb1B7R08SzB4TsLeSbUCuG467NhnoT0oO6w1qRO+BA== + dependencies: + eventemitter3 "^5.0.1" + p-timeout "^6.1.2" + +p-retry@^4: version "4.6.2" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" + resolved "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz" integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== dependencies: "@types/retry" "0.12.0" @@ -6567,16 +9327,57 @@ p-retry@^4.0.0: p-timeout@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" + resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz" integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== dependencies: p-finally "^1.0.0" -p-try@^2.0.0, p-try@^2.1.0: +p-timeout@^5.0.2: + version "5.1.0" + resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz" + integrity sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew== + +p-timeout@^6.1.2: + version "6.1.2" + resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.2.tgz" + integrity sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ== + +p-transform@^4.1.3: + version "4.1.5" + resolved "https://registry.npmjs.org/p-transform/-/p-transform-4.1.5.tgz" + integrity sha512-CsXIiCOeBUYMBLpcY71DTq+fg8268ux31pAxI5TcoYEPfWCw5ozrbgWdZ9QmSDd8dUzvNXtmiwJOdTIxIFptfQ== + dependencies: + "@types/node" "^16.18.31" + p-queue "^7.3.0" + readable-stream "^4.3.0" + +p-try@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +pac-proxy-agent@^7.0.1: + version "7.0.2" + resolved "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz" + integrity sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg== + dependencies: + "@tootallnate/quickjs-emscripten" "^0.23.0" + agent-base "^7.0.2" + debug "^4.3.4" + get-uri "^6.0.1" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.5" + pac-resolver "^7.0.1" + socks-proxy-agent "^8.0.4" + +pac-resolver@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz" + integrity sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg== + dependencies: + degenerator "^5.0.0" + netmask "^2.0.2" + package-hash@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz" @@ -6589,40 +9390,69 @@ package-hash@^4.0.0: package-json-from-dist@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00" + resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz" integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw== -package-json@^6.3.0: - version "6.5.0" - resolved "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz" - integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== +package-json@^10.0.0: + version "10.0.1" + resolved "https://registry.npmjs.org/package-json/-/package-json-10.0.1.tgz" + integrity sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg== dependencies: - got "^9.6.0" - registry-auth-token "^4.0.0" - registry-url "^5.0.0" - semver "^6.2.0" - -pad-component@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/pad-component/-/pad-component-0.0.1.tgz" - integrity sha1-rR8izhvw/cDW3dkIrxfzUaQEuKw= + ky "^1.2.0" + registry-auth-token "^5.0.2" + registry-url "^6.0.1" + semver "^7.6.0" -paged-request@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/paged-request/-/paged-request-2.0.2.tgz" - integrity sha512-NWrGqneZImDdcMU/7vMcAOo1bIi5h/pmpJqe7/jdsy85BA/s5MSaU/KlpxwW/IVPmIwBcq2uKPrBWWhEWhtxag== +package-json@^8.1.0: + version "8.1.1" + resolved "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz" + integrity sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA== dependencies: - axios "^0.21.1" + got "^12.1.0" + registry-auth-token "^5.0.1" + registry-url "^6.0.0" + semver "^7.3.7" + +pacote@^18.0.0, pacote@^18.0.6: + version "18.0.6" + resolved "https://registry.npmjs.org/pacote/-/pacote-18.0.6.tgz" + integrity sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A== + dependencies: + "@npmcli/git" "^5.0.0" + "@npmcli/installed-package-contents" "^2.0.1" + "@npmcli/package-json" "^5.1.0" + "@npmcli/promise-spawn" "^7.0.0" + "@npmcli/run-script" "^8.0.0" + cacache "^18.0.0" + fs-minipass "^3.0.0" + minipass "^7.0.2" + npm-package-arg "^11.0.0" + npm-packlist "^8.0.0" + npm-pick-manifest "^9.0.0" + npm-registry-fetch "^17.0.0" + proc-log "^4.0.0" + promise-retry "^2.0.1" + sigstore "^2.2.0" + ssri "^10.0.0" + tar "^6.1.11" pako@~1.0.2: version "1.0.11" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + resolved "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== -papaparse@^5.3.1: - version "5.3.1" - resolved "https://registry.npmjs.org/papaparse/-/papaparse-5.3.1.tgz" - integrity sha512-Dbt2yjLJrCwH2sRqKFFJaN5XgIASO9YOFeFP8rIBRG2Ain8mqk5r1M6DkfvqEVozVcz3r3HaUGw253hA1nLIcA== +papaparse@^5.4.1: + version "5.4.1" + resolved "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz" + integrity sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw== + +param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" parent-module@^1.0.0: version "1.0.1" @@ -6631,15 +9461,24 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +parse-conflict-json@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-3.0.1.tgz" + integrity sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw== + dependencies: + json-parse-even-better-errors "^3.0.0" + just-diff "^6.0.0" + just-diff-apply "^5.2.0" + parse-json@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + integrity sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw== dependencies: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parse-json@^5.0.0: +parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -6649,40 +9488,52 @@ parse-json@^5.0.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-json@^8.0.0: + version "8.1.0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-8.1.0.tgz" + integrity sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA== + dependencies: + "@babel/code-frame" "^7.22.13" + index-to-position "^0.1.2" + type-fest "^4.7.1" + parse5@^7.1.2: version "7.1.2" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" + resolved "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz" integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== dependencies: entities "^4.4.0" -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= - -pascalcase@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/pascalcase/-/pascalcase-1.0.0.tgz" - integrity sha512-BSExi0rRnCHReJys6NocaK+cfTXNinAegfWBvr0JD3hiaEG7Nuc7r0CIdOJunXrs8gU/sbHQ9dxVAtiVQisjmg== +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" -password-prompt@^1.0.7, password-prompt@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/password-prompt/-/password-prompt-1.1.2.tgz" - integrity sha512-bpuBhROdrhuN3E7G/koAju0WjVw9/uQOG5Co5mokNj0MiOSBVZS1JTwM4zl55hu0WFmIEFvO9cU9sJQiBIYeIA== +pascalcase@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/pascalcase/-/pascalcase-2.0.0.tgz" + integrity sha512-DHpENy5Qm/FaX+x3iBLoMLG/XHNCTgL+yErm1TwuVaj6u4fiOSkYkf60vGtITk7hrKHOO4uCl9vRrD4hqjNKjg== dependencies: - ansi-escapes "^3.1.0" - cross-spawn "^6.0.5" + camelcase "^6.2.1" -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz" - integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= +password-prompt@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/password-prompt/-/password-prompt-1.1.3.tgz" + integrity sha512-HkrjG2aJlvF0t2BMH0e2LB/EHf3Lcq3fNMzy4GYHcQblAvOl+QQji1Lx7WRBMqpVK8p+KR7bCg7oqAMXtdgqyw== + dependencies: + ansi-escapes "^4.3.2" + cross-spawn "^7.0.3" -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= +path-case@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz" + integrity sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" path-exists@^4.0.0: version "4.0.0" @@ -6691,32 +9542,48 @@ path-exists@^4.0.0: path-exists@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz" integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6, path-parse@^1.0.7: +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + +path-parse@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.10.1: + version "1.10.1" + resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz" + integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== + dependencies: + lru-cache "^9.1.1 || ^10.0.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-scurry@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580" + resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz" integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg== dependencies: lru-cache "^11.0.0" @@ -6729,18 +9596,21 @@ path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" -path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz" - integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== - dependencies: - pify "^3.0.0" +path-to-regexp@^6.2.1: + version "6.2.2" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz" + integrity sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw== path-type@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +path-type@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz" + integrity sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg== + pathval@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz" @@ -6749,65 +9619,156 @@ pathval@^1.1.1: pend@~1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== performance-now@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== -picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: - version "2.2.2" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz" - integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pify@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= +picomatch@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz" + integrity sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag== -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= +pino-abstract-transport@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz" + integrity sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA== + dependencies: + readable-stream "^4.0.0" + split2 "^4.0.0" -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== +pino-abstract-transport@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz" + integrity sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q== + dependencies: + readable-stream "^4.0.0" + split2 "^4.0.0" + +pino-pretty@^10.3.1: + version "10.3.1" + resolved "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.3.1.tgz" + integrity sha512-az8JbIYeN/1iLj2t0jR9DV48/LQ3RC6hZPpapKPkb84Q+yTidMCpgWxIT3N0flnBDilyBQ1luWNpOeJptjdp/g== + dependencies: + colorette "^2.0.7" + dateformat "^4.6.3" + fast-copy "^3.0.0" + fast-safe-stringify "^2.1.1" + help-me "^5.0.0" + joycon "^3.1.1" + minimist "^1.2.6" + on-exit-leak-free "^2.1.0" + pino-abstract-transport "^1.0.0" + pump "^3.0.0" + readable-stream "^4.0.0" + secure-json-parse "^2.4.0" + sonic-boom "^3.0.0" + strip-json-comments "^3.1.1" + +pino-pretty@^11.2.2: + version "11.2.2" + resolved "https://registry.npmjs.org/pino-pretty/-/pino-pretty-11.2.2.tgz" + integrity sha512-2FnyGir8nAJAqD3srROdrF1J5BIcMT4nwj7hHSc60El6Uxlym00UbCCd8pYIterstVBFlMyF1yFV8XdGIPbj4A== + dependencies: + colorette "^2.0.7" + dateformat "^4.6.3" + fast-copy "^3.0.2" + fast-safe-stringify "^2.1.1" + help-me "^5.0.0" + joycon "^3.1.1" + minimist "^1.2.6" + on-exit-leak-free "^2.1.0" + pino-abstract-transport "^1.0.0" + pump "^3.0.0" + readable-stream "^4.0.0" + secure-json-parse "^2.4.0" + sonic-boom "^4.0.1" + strip-json-comments "^3.1.1" + +pino-std-serializers@^6.0.0: + version "6.2.2" + resolved "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz" + integrity sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA== -pkg-dir@4.2.0, pkg-dir@^4.1.0, pkg-dir@^4.2.0: +pino-std-serializers@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz" + integrity sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA== + +pino@^8.21.0: + version "8.21.0" + resolved "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz" + integrity sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q== + dependencies: + atomic-sleep "^1.0.0" + fast-redact "^3.1.1" + on-exit-leak-free "^2.1.0" + pino-abstract-transport "^1.2.0" + pino-std-serializers "^6.0.0" + process-warning "^3.0.0" + quick-format-unescaped "^4.0.3" + real-require "^0.2.0" + safe-stable-stringify "^2.3.1" + sonic-boom "^3.7.0" + thread-stream "^2.6.0" + +pino@^9.3.2: + version "9.3.2" + resolved "https://registry.npmjs.org/pino/-/pino-9.3.2.tgz" + integrity sha512-WtARBjgZ7LNEkrGWxMBN/jvlFiE17LTbBoH0konmBU684Kd0uIiDwBXlcTCW7iJnA6HfIKwUssS/2AC6cDEanw== + dependencies: + atomic-sleep "^1.0.0" + fast-redact "^3.1.1" + on-exit-leak-free "^2.1.0" + pino-abstract-transport "^1.2.0" + pino-std-serializers "^7.0.0" + process-warning "^4.0.0" + quick-format-unescaped "^4.0.3" + real-require "^0.2.0" + safe-stable-stringify "^2.3.1" + sonic-boom "^4.0.1" + thread-stream "^3.0.0" + +pkg-dir@^4.1.0: version "4.2.0" resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: find-up "^4.0.0" -pkg-dir@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-7.0.0.tgz#8f0c08d6df4476756c5ff29b3282d0bab7517d11" - integrity sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA== - dependencies: - find-up "^6.3.0" +pluralize@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz" + integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz" - integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= +postcss-selector-parser@^6.0.10: + version "6.1.2" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz" + integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" postman-request@^2.88.1-postman.30: - version "2.88.1-postman.33" - resolved "https://registry.yarnpkg.com/postman-request/-/postman-request-2.88.1-postman.33.tgz#684147d61c9a263a28f148d3207b1593e0f01ec5" - integrity sha512-uL9sCML4gPH6Z4hreDWbeinKU0p0Ke261nU7OvII95NU22HN6Dk7T/SaVPaj6T4TsQqGKIFw6/woLZnH7ugFNA== + version "2.88.1-postman.39" + resolved "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.39.tgz" + integrity sha512-rsncxxDlbn1YpygXSgJqbJzIjGlHFcZjbYDzeBPTQHMDfLuSTzZz735JHV8i1+lOROuJ7MjNap4eaSD3UijHzQ== dependencies: "@postman/form-data" "~3.1.1" "@postman/tough-cookie" "~4.1.3-postman.1" - "@postman/tunnel-agent" "^0.6.3" + "@postman/tunnel-agent" "^0.6.4" aws-sign2 "~0.7.0" aws4 "^1.12.0" brotli "^1.3.3" @@ -6833,22 +9794,35 @@ prelude-ls@^1.2.1: resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prepend-http@^1.0.1: - version "1.0.4" - resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz" - integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= +prettier@^2.8.8: + version "2.8.8" + resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= +pretty-bytes@^6.1.0: + version "6.1.1" + resolved "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz" + integrity sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ== -pretty-bytes@^5.2.0: - version "5.6.0" - resolved "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz" - integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== +pretty-quick@^3.3.1: + version "3.3.1" + resolved "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.3.1.tgz" + integrity sha512-3b36UXfYQ+IXXqex6mCca89jC8u0mYLqFAN5eTQKoXO6oCQYcIVYZEB/5AlBHI7JPYygReM2Vv6Vom/Gln7fBg== + dependencies: + execa "^4.1.0" + find-up "^4.1.0" + ignore "^5.3.0" + mri "^1.2.0" + picocolors "^1.0.0" + picomatch "^3.0.1" + tslib "^2.6.2" -process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: +proc-log@^4.0.0, proc-log@^4.1.0, proc-log@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz" + integrity sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA== + +process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== @@ -6860,37 +9834,99 @@ process-on-spawn@^1.0.0: dependencies: fromentries "^1.2.0" -progress@2.0.3, progress@^2.0.0: +process-warning@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz" + integrity sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ== + +process-warning@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/process-warning/-/process-warning-4.0.0.tgz" + integrity sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + +proggy@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/proggy/-/proggy-2.0.0.tgz" + integrity sha512-69agxLtnI8xBs9gUGqEnK26UfiexpHy+KUpBQWabiytQjnn5wFY8rklAi7GRfABIuPNnQ/ik48+LGLkYYJcy4A== + +progress@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -promise@^7.1.1: - version "7.3.1" - resolved "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz" - integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== - dependencies: - asap "~2.0.3" +promise-all-reject-late@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz" + integrity sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw== + +promise-call-limit@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-3.0.1.tgz" + integrity sha512-utl+0x8gIDasV5X+PI5qWEPqH6fJS0pFtQ/4gZ95xfEFb/89dmh+/b895TbFDBLiafBvxD/PGTKfvxl4kH/pQg== + +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz" + integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== -propagate@^2.0.0: +promise-retry@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz" - integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== + resolved "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz" + integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== + dependencies: + err-code "^2.0.2" + retry "^0.12.0" + +prompts@^2.4.2: + version "2.4.2" + resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +proper-lockfile@^4.1.2: + version "4.1.2" + resolved "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz" + integrity sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA== + dependencies: + graceful-fs "^4.2.4" + retry "^0.12.0" + signal-exit "^3.0.2" + +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz" + integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== + +proxy-agent@^6.4.0: + version "6.4.0" + resolved "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz" + integrity sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ== + dependencies: + agent-base "^7.0.2" + debug "^4.3.4" + http-proxy-agent "^7.0.1" + https-proxy-agent "^7.0.3" + lru-cache "^7.14.1" + pac-proxy-agent "^7.0.1" + proxy-from-env "^1.1.0" + socks-proxy-agent "^8.0.2" -proxy-from-env@1.1.0, proxy-from-env@^1.1.0: +proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== - -psl@^1.1.28, psl@^1.1.33, psl@^1.8.0: - version "1.8.0" - resolved "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== +psl@^1.1.33, psl@^1.9.0: + version "1.9.0" + resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== pump@^3.0.0: version "3.0.0" @@ -6900,82 +9936,45 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -punycode@^2.3.1: +punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -pupa@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz" - integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== +pupa@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz" + integrity sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug== dependencies: - escape-goat "^2.0.0" + escape-goat "^4.0.0" -puppeteer@^13.5.2: - version "13.5.2" - resolved "https://registry.npmjs.org/puppeteer/-/puppeteer-13.5.2.tgz" - integrity sha512-DJAyXODBikZ3xPs8C35CtExEw78LZR9RyelGDAs0tX1dERv3OfW7qpQ9VPBgsfz+hG2HiMTO/Tyf7BuMVWsrxg== - dependencies: - cross-fetch "3.1.5" - debug "4.3.4" - devtools-protocol "0.0.969999" - extract-zip "2.0.1" - https-proxy-agent "5.0.0" - pkg-dir "4.2.0" - progress "2.0.3" - proxy-from-env "1.1.0" - rimraf "3.0.2" - tar-fs "2.1.1" - unbzip2-stream "1.4.3" - ws "8.5.0" - -qqjs@^0.3.10: - version "0.3.11" - resolved "https://registry.npmjs.org/qqjs/-/qqjs-0.3.11.tgz" - integrity sha512-pB2X5AduTl78J+xRSxQiEmga1jQV0j43jOPs/MTgTLApGFEOn6NgdE2dEjp7nvDtjkIOZbvFIojAiYUx6ep3zg== - dependencies: - chalk "^2.4.1" - debug "^4.1.1" - execa "^0.10.0" - fs-extra "^6.0.1" - get-stream "^5.1.0" - glob "^7.1.2" - globby "^10.0.1" - http-call "^5.1.2" - load-json-file "^6.2.0" - pkg-dir "^4.2.0" - tar-fs "^2.0.0" - tmp "^0.1.0" - write-json-file "^4.1.1" - -qs@^6.10.1: - version "6.11.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" - integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== +puppeteer-core@^23.3.0: + version "23.3.0" + resolved "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.3.0.tgz" + integrity sha512-sB2SsVMFs4gKad5OCdv6w5vocvtEUrRl0zQqSyRPbo/cj1Ktbarmhxy02Zyb9R9HrssBcJDZbkrvBnbaesPyYg== dependencies: - side-channel "^1.0.4" + "@puppeteer/browsers" "2.4.0" + chromium-bidi "0.6.5" + debug "^4.3.6" + devtools-protocol "0.0.1330662" + typed-query-selector "^2.12.0" + ws "^8.18.0" -qs@^6.9.1: - version "6.11.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.1.tgz#6c29dff97f0c0060765911ba65cbc9764186109f" - integrity sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ== +qs@^6.10.1, qs@^6.10.3: + version "6.13.0" + resolved "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz" + integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== dependencies: - side-channel "^1.0.4" + side-channel "^1.0.6" -qs@~6.5.2, qs@~6.5.3: +qs@~6.5.3: version "6.5.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" - integrity "sha1-Ou7/yRln7241wOSI70b7KWq3aq0= sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" + resolved "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz" + integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== query-string@^7.0.0: version "7.1.3" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.3.tgz#a1cf90e994abb113a325804a972d98276fe02328" + resolved "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz" integrity sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg== dependencies: decode-uri-component "^0.2.2" @@ -6985,17 +9984,32 @@ query-string@^7.0.0: querystringify@^2.1.1: version "2.2.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz" integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== queue-microtask@^1.2.2: - version "1.2.2" - resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.2.tgz" - integrity sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg== + version "1.2.3" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +queue-tick@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz" + integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag== + +quick-format-unescaped@^4.0.3: + version "4.0.4" + resolved "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz" + integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== + +quick-lru@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz" + integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== quick-lru@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== randomatic@^3.0.0: @@ -7014,7 +10028,7 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" -rc@^1.2.8: +rc@1.2.8: version "1.2.8" resolved "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -7024,21 +10038,27 @@ rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" -read-chunk@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/read-chunk/-/read-chunk-3.2.0.tgz" - integrity sha512-CEjy9LCzhmD7nUpJ1oVOE6s/hBkejlcJEgLQHVnQznOSilOPb+kpKktlLfFDK3/WP43+F80xkUTM2VOkYoSYvQ== +read-cmd-shim@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz" + integrity sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q== + +read-package-json-fast@^3.0.0, read-package-json-fast@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz" + integrity sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw== dependencies: - pify "^4.0.1" - with-open-file "^0.1.6" + json-parse-even-better-errors "^3.0.0" + npm-normalize-package-bin "^3.0.0" -read-pkg-up@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-5.0.0.tgz" - integrity sha512-XBQjqOBtTzyol2CpsQOw8LHV0XbDZVG7xMMjmXAJomlVY03WOBRmYgDJETlvcg0H63AJvPRwT7GFi5rvOzUOKg== +read-package-up@^11.0.0: + version "11.0.0" + resolved "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz" + integrity sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ== dependencies: - find-up "^3.0.0" - read-pkg "^5.0.0" + find-up-simple "^1.0.0" + read-pkg "^9.0.0" + type-fest "^4.6.0" read-pkg-up@^7.0.1: version "7.0.1" @@ -7049,7 +10069,7 @@ read-pkg-up@^7.0.1: read-pkg "^5.2.0" type-fest "^0.8.1" -read-pkg@^5.0.0, read-pkg@^5.2.0: +read-pkg@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz" integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== @@ -7059,18 +10079,29 @@ read-pkg@^5.0.0, read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -"readable-stream@2 || 3", readable-stream@^3.1.1, readable-stream@^3.4.0: - version "3.6.0" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== +read-pkg@^9.0.0: + version "9.0.1" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz" + integrity sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA== + dependencies: + "@types/normalize-package-data" "^2.4.3" + normalize-package-data "^6.0.0" + parse-json "^8.0.0" + type-fest "^4.6.0" + unicorn-magic "^0.1.0" + +readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^2.0.0: +readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.2.2, readable-stream@~2.3.6: version "2.3.8" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== dependencies: core-util-is "~1.0.0" @@ -7081,109 +10112,113 @@ readable-stream@^2.0.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.0, readable-stream@^2.2.2, readable-stream@^2.3.5, readable-stream@~2.3.6: - version "2.3.7" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== +readable-stream@^4.0.0: + version "4.4.2" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz" + integrity sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA== dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" -readable-stream@^3.6.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== +readable-stream@^4.3.0: + version "4.5.2" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz" + integrity sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g== dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" readdir-glob@^1.1.2: version "1.1.3" - resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584" + resolved "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz" integrity sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA== dependencies: minimatch "^5.1.0" -readdirp@~3.5.0: - version "3.5.0" - resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz" - integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== dependencies: picomatch "^2.2.1" +real-require@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz" + integrity sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg== + rechoir@^0.6.2: version "0.6.2" resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz" - integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= + integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw== dependencies: resolve "^1.1.6" +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + redeyed@~2.1.0: version "2.1.1" resolved "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz" - integrity sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs= + integrity sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ== dependencies: esprima "~4.0.0" -redis-commands@1.5.0: - version "1.5.0" - resolved "https://registry.npmjs.org/redis-commands/-/redis-commands-1.5.0.tgz" - integrity sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg== - -redis-errors@^1.0.0, redis-errors@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz" - integrity sha1-62LSrbFeTq9GEMBK/hUpOEJQq60= - -redis-parser@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz" - integrity sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ= - dependencies: - redis-errors "^1.0.0" - regenerator-runtime@^0.14.0: version "0.14.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz" integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== +regexp-tree@^0.1.27: + version "0.1.27" + resolved "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz" + integrity sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA== + +regexp.prototype.flags@^1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz" + integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + set-function-name "^2.0.0" -regexpp@^3.0.0, regexpp@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz" - integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== +registry-auth-token@^5.0.1, registry-auth-token@^5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz" + integrity sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ== + dependencies: + "@pnpm/npm-conf" "^2.1.0" -registry-auth-token@^4.0.0: - version "4.2.1" - resolved "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz" - integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== +registry-url@^6.0.0, registry-url@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz" + integrity sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q== dependencies: - rc "^1.2.8" + rc "1.2.8" -registry-url@^5.0.0: - version "5.1.0" - resolved "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz" - integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== +regjsparser@^0.10.0: + version "0.10.0" + resolved "https://registry.npmjs.org/regjsparser/-/regjsparser-0.10.0.tgz" + integrity sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA== dependencies: - rc "^1.2.8" + jsesc "~0.5.0" release-zalgo@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz" - integrity sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA= + integrity sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA== dependencies: es6-error "^4.0.1" @@ -7195,10 +10230,10 @@ remarkable@^1.7.1: argparse "^1.0.10" autolinker "~0.28.0" -remove-trailing-separator@^1.0.1: +remove-trailing-separator@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== repeat-element@^1.1.2: version "1.1.4" @@ -7208,43 +10243,17 @@ repeat-element@^1.1.2: repeat-string@^1.5.2, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= - -replace-ext@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz" - integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== + integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== -request@^2.72.0: - version "2.88.2" - resolved "https://registry.npmjs.org/request/-/request-2.88.2.tgz" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" +replace-ext@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz" + integrity sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug== require-directory@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== require-from-string@^2.0.2: version "2.0.2" @@ -7258,60 +10267,54 @@ require-main-filename@^2.0.0: requires-port@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== -resolve-alpn@^1.0.0: +resolve-alpn@^1.0.0, resolve-alpn@^1.2.0: version "1.2.1" - resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + resolved "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz" integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve-from@^5.0.0: +resolve-from@5.0.0, resolve-from@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz" - integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-global@1.0.0, resolve-global@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz" + integrity sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw== + dependencies: + global-dirs "^0.1.1" -resolve@^1.1.6: - version "1.21.1" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.21.1.tgz" - integrity sha512-lfEImVbnolPuaSZuLQ52cAxPBHeI77sPwCOWRdy12UG/CNa8an7oBHH1R+Fp1/mUqSJi4c8TIP6FOIPSZAUrEQ== +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.22.4: + version "1.22.8" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== dependencies: - is-core-module "^2.8.0" + is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^1.10.0, resolve@^1.17.0: - version "1.20.0" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - -responselike@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz" - integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= - dependencies: - lowercase-keys "^1.0.0" - responselike@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" + resolved "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz" integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== dependencies: lowercase-keys "^2.0.0" +responselike@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz" + integrity sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg== + dependencies: + lowercase-keys "^3.0.0" + restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" @@ -7320,55 +10323,61 @@ restore-cursor@^3.1.0: onetime "^5.1.0" signal-exit "^3.0.2" -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== +restore-cursor@^5.0.0: + version "5.1.0" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz" + integrity sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA== + dependencies: + onetime "^7.0.0" + signal-exit "^4.1.0" -retry@^0.13.1: +retry@0.13.1, retry@^0.13.1: version "0.13.1" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz" integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz" + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== + reusify@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@2, rimraf@^2.6.3: +rimraf@2: version "2.7.1" resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" -rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: +rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" -rimraf@~2.4.0: - version "2.4.5" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz" - integrity sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto= - dependencies: - glob "^6.0.1" - rrweb-cssom@^0.6.0: version "0.6.0" - resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" + resolved "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz" integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw== -run-async@^2.0.0, run-async@^2.4.0: - version "2.4.1" - resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" - integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== +rrweb-cssom@^0.7.1: + version "0.7.1" + resolved "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz" + integrity sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg== + +run-applescript@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz" + integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== run-async@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-3.0.0.tgz#42a432f6d76c689522058984384df28be379daad" + resolved "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz" integrity sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q== run-parallel@^1.1.9: @@ -7378,20 +10387,23 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rxjs@^6.6.0: - version "6.6.7" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz" - integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== - dependencies: - tslib "^1.9.0" - -rxjs@^7.8.1: +rxjs@^7.2.0, rxjs@^7.8.1: version "7.8.1" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz" integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== dependencies: tslib "^2.1.0" +safe-array-concat@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz" + integrity sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + isarray "^2.0.5" + safe-buffer@*, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" @@ -7402,28 +10414,25 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-json-stringify@~1: - version "1.2.0" - resolved "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz" - integrity sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg== - -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz" - integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== dependencies: - ret "~0.1.10" + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + +safe-stable-stringify@^2.3.1, safe-stable-stringify@^2.4.3: + version "2.4.3" + resolved "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -samsam@1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz" - integrity sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg== - sax@>=0.6.0: version "1.2.4" resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz" @@ -7431,70 +10440,111 @@ sax@>=0.6.0: saxes@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + resolved "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz" integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== dependencies: xmlchars "^2.2.0" saxes@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + resolved "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz" integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== dependencies: xmlchars "^2.2.0" -scoped-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/scoped-regex/-/scoped-regex-1.0.0.tgz" - integrity sha1-o0a7Gs1CB65wvXwMfKnlZra63bg= - -semver-diff@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz" - integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== - dependencies: - semver "^6.3.0" +secure-json-parse@^2.4.0: + version "2.7.0" + resolved "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz" + integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== -"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0: +"semver@2 || 3 || 4 || 5": version "5.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: - version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" - integrity "sha1-VW0u+GiRRuRtzqS/3QlfNDTf/LQ= sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - -semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: +semver@7.5.4: version "7.5.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" -semver@^7.6.0: - version "7.6.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" - integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== +semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.0.0, semver@^7.3.4, semver@^7.3.5, semver@^7.5.4, semver@^7.6.0: + version "7.6.2" + resolved "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + +semver@^7.1.1, semver@^7.3.7, semver@^7.5.3, semver@^7.6.2, semver@^7.6.3: + version "7.6.3" + resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + +sentence-case@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz" + integrity sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg== dependencies: - lru-cache "^6.0.0" + no-case "^3.0.4" + tslib "^2.0.3" + upper-case-first "^2.0.2" sequin@*: version "0.1.1" resolved "https://registry.npmjs.org/sequin/-/sequin-0.1.1.tgz" - integrity sha1-XC04nWajg3NOqvvEXt6ywcsb5wE= + integrity sha512-hJWMZRwP75ocoBM+1/YaCsvS0j5MTPeBHJkS2/wruehl9xwtX30HlDF1Gt6UZ8HHHY8SJa2/IL+jo+JJCd59rA== -serialize-javascript@5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz" - integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== dependencies: randombytes "^2.1.0" +server-destroy@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz" + integrity sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ== + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + +set-function-length@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz" + integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== + dependencies: + define-data-property "^1.1.1" + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz" + integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== + dependencies: + define-data-property "^1.0.1" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.0" set-getter@^0.1.0: version "0.1.1" @@ -7503,17 +10553,7 @@ set-getter@^0.1.0: dependencies: to-object-path "^0.3.0" -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - -set-value@^4.0.0: +set-value@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/set-value/-/set-value-4.1.0.tgz" integrity sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw== @@ -7523,23 +10563,9 @@ set-value@^4.0.0: setimmediate@^1.0.5, setimmediate@~1.0.4: version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== -shallow-clone@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz" - integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== - dependencies: - kind-of "^6.0.2" - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" @@ -7547,17 +10573,12 @@ shebang-command@^2.0.0: dependencies: shebang-regex "^3.0.0" -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - shebang-regex@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shelljs@^0.8.4: +shelljs@^0.8.4, shelljs@^0.8.5: version "0.8.5" resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz" integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== @@ -7566,64 +10587,101 @@ shelljs@^0.8.4: interpret "^1.0.0" rechoir "^0.6.2" -shx@^0.3.3: - version "0.3.3" - resolved "https://registry.npmjs.org/shx/-/shx-0.3.3.tgz" - integrity sha512-nZJ3HFWVoTSyyB+evEKjJ1STiixGztlqwKLTUNV5KqMWtGey9fTd4KU1gdZ1X9BV6215pswQ/Jew9NsuS/fNDA== +shiki@^0.14.7: + version "0.14.7" + resolved "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz" + integrity sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg== dependencies: - minimist "^1.2.3" - shelljs "^0.8.4" + ansi-sequence-parser "^1.1.0" + jsonc-parser "^3.2.0" + vscode-oniguruma "^1.7.0" + vscode-textmate "^8.0.0" side-channel@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== dependencies: call-bind "^1.0.0" get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.3" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz" - integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== +side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== 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" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== -simple-git@^3.20.0: - version "3.20.0" - resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-3.20.0.tgz#ff9c3f736d6b2bf0e3510209569d206aac84833d" - integrity sha512-ozK8tl2hvLts8ijTs18iFruE+RoqmC/mqZhjs/+V7gS5W68JpJ3+FCTmLVqmR59MaUQ52MfGQuWsIqfsTbbJ0Q== +sigstore@^2.2.0: + version "2.3.1" + resolved "https://registry.npmjs.org/sigstore/-/sigstore-2.3.1.tgz" + integrity sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ== + dependencies: + "@sigstore/bundle" "^2.3.2" + "@sigstore/core" "^1.0.0" + "@sigstore/protobuf-specs" "^0.3.2" + "@sigstore/sign" "^2.3.2" + "@sigstore/tuf" "^2.3.4" + "@sigstore/verify" "^1.2.1" + +simple-git@^3.20.0, simple-git@^3.25.0: + version "3.25.0" + resolved "https://registry.npmjs.org/simple-git/-/simple-git-3.25.0.tgz" + integrity sha512-KIY5sBnzc4yEcJXW7Tdv4viEz8KyG+nU0hay+DWZasvdFOYKeUZ6Xc25LUHHjw0tinPT7O1eY6pzX7pRT1K8rw== dependencies: "@kwsites/file-exists" "^1.1.1" "@kwsites/promise-deferred" "^1.1.1" - debug "^4.3.4" + debug "^4.3.5" -sinon@5.1.1: - version "5.1.1" - resolved "https://registry.npmjs.org/sinon/-/sinon-5.1.1.tgz" - integrity sha512-h/3uHscbt5pQNxkf7Y/Lb9/OM44YNCicHakcq73ncbrIS8lXg+ZGOZbtuU+/km4YnyiCYfQQEwANaReJz7KDfw== +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== dependencies: - "@sinonjs/formatio" "^2.0.0" - diff "^3.5.0" - lodash.get "^4.4.2" - lolex "^2.4.2" - nise "^1.3.3" - supports-color "^5.4.0" - type-detect "^4.0.8" + is-arrayish "^0.3.1" -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz" - integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= +sinon@10.0.0: + version "10.0.0" + resolved "https://registry.npmjs.org/sinon/-/sinon-10.0.0.tgz" + integrity sha512-XAn5DxtGVJBlBWYrcYKEhWCz7FLwZGdyvANRyK06419hyEpdT0dMc5A8Vcxg5SCGHc40CsqoKsc1bt1CbJPfNw== + dependencies: + "@sinonjs/commons" "^1.8.1" + "@sinonjs/fake-timers" "^6.0.1" + "@sinonjs/samsam" "^5.3.1" + diff "^4.0.2" + nise "^4.1.0" + supports-color "^7.1.0" -slash@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz" - integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== +sinon@^17.0.2: + version "17.0.2" + resolved "https://registry.npmjs.org/sinon/-/sinon-17.0.2.tgz" + integrity sha512-uihLiaB9FhzesElPDFZA7hDcNABzsVHwr3YfmM9sBllVwab3l0ltGlRV1XhpNfIacNDLGD1QRZNLs5nU5+hTuA== + dependencies: + "@sinonjs/commons" "^3.0.1" + "@sinonjs/fake-timers" "^11.2.2" + "@sinonjs/samsam" "^8.0.0" + diff "^5.2.0" + nise "^5.1.9" + supports-color "^7" + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== slash@^3.0.0: version "3.0.0" @@ -7632,9 +10690,14 @@ slash@^3.0.0: slash@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + resolved "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz" integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== +slash@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz" + integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== + slice-ansi@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz" @@ -7644,78 +10707,101 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== +slice-ansi@^7.1.0: + version "7.1.0" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz" + integrity sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg== dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" + ansi-styles "^6.2.1" + is-fullwidth-code-point "^5.0.0" -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== +smart-buffer@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz" + integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== + +snake-case@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz" + integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== dependencies: - kind-of "^3.2.0" + dot-case "^3.0.4" + tslib "^2.0.3" -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== +socks-proxy-agent@^8.0.2, socks-proxy-agent@^8.0.3, socks-proxy-agent@^8.0.4: + version "8.0.4" + resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz" + integrity sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw== dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" + agent-base "^7.1.1" + debug "^4.3.4" + socks "^2.8.3" -sort-array@^4.1.3: - version "4.1.4" - resolved "https://registry.npmjs.org/sort-array/-/sort-array-4.1.4.tgz" - integrity sha512-GVFN6Y1sHKrWaSYOJTk9093ZnrBMc9sP3nuhANU44S4xg3rE6W5Z5WyamuT8VpMBbssnetx5faKCua0LEmUnSw== +socks@^2.8.3: + version "2.8.3" + resolved "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz" + integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw== + dependencies: + ip-address "^9.0.5" + smart-buffer "^4.2.0" + +sonic-boom@^3.0.0, sonic-boom@^3.7.0: + version "3.7.0" + resolved "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.7.0.tgz" + integrity sha512-IudtNvSqA/ObjN97tfgNmOKyDOs4dNcg4cUUsHDebqsgb8wGBBwb31LIgShNO8fye0dFI52X1+tFoKKI6Rq1Gg== + dependencies: + atomic-sleep "^1.0.0" + +sonic-boom@^4.0.1: + version "4.1.0" + resolved "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.1.0.tgz" + integrity sha512-NGipjjRicyJJ03rPiZCJYjwlsuP2d1/5QUviozRXC7S3WdVWNK5e3Ojieb9CCyfhq2UC+3+SRd9nG3I2lPRvUw== + dependencies: + atomic-sleep "^1.0.0" + +sort-array@^4.1.5: + version "4.1.5" + resolved "https://registry.npmjs.org/sort-array/-/sort-array-4.1.5.tgz" + integrity sha512-Ya4peoS1fgFN42RN1REk2FgdNOeLIEMKFGJvs7VTP3OklF8+kl2SkpVliZ4tk/PurWsrWRsdNdU+tgyOBkB9sA== dependencies: array-back "^5.0.0" typical "^6.0.1" -sort-keys@^4.0.0: - version "4.2.0" - resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-4.2.0.tgz" - integrity sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg== +sort-keys@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-5.0.0.tgz" + integrity sha512-Pdz01AvCAottHTPQGzndktFNdbRA75BgOfeT1hH+AMnJFv8lynkPi42rfeEhpx1saTEI3YNMWxfqu0sFD1G8pw== dependencies: - is-plain-obj "^2.0.0" + is-plain-obj "^4.0.0" sort-object-keys@^1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/sort-object-keys/-/sort-object-keys-1.1.3.tgz#bff833fe85cab147b34742e45863453c1e190b45" + resolved "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-1.1.3.tgz" integrity sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg== -source-map-resolve@^0.5.0: - version "0.5.3" - resolved "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz" - integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== +sort-package-json@^2.10.1: + version "2.10.1" + resolved "https://registry.npmjs.org/sort-package-json/-/sort-package-json-2.10.1.tgz" + integrity sha512-d76wfhgUuGypKqY72Unm5LFnMpACbdxXsLPcL27pOsSrmVqH3PztFp1uq+Z22suk15h7vXmTesuh2aEjdCqb5w== dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - -source-map-url@^0.4.0: - version "0.4.1" - resolved "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz" - integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== + detect-indent "^7.0.1" + detect-newline "^4.0.0" + get-stdin "^9.0.0" + git-hooks-list "^3.0.0" + globby "^13.1.2" + is-plain-obj "^4.1.0" + semver "^7.6.0" + sort-object-keys "^1.1.3" -source-map@^0.5.0, source-map@^0.5.6: - version "0.5.7" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= +source-map-support@^0.5.21: + version "0.5.21" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" -source-map@^0.6.1: +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -7733,9 +10819,9 @@ spawn-wrap@^2.0.0: which "^2.0.1" spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + version "3.2.0" + resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" @@ -7753,22 +10839,35 @@ spdx-expression-parse@^3.0.0: spdx-exceptions "^2.1.0" spdx-license-ids "^3.0.0" +spdx-expression-parse@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz" + integrity sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + spdx-license-ids@^3.0.0: - version "3.0.7" - resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz" - integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== + version "3.0.16" + resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz" + integrity sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw== split-on-first@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + resolved "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz" integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== +split2@^3.0.0, split2@^3.2.2: + version "3.2.2" + resolved "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz" + integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== dependencies: - extend-shallow "^3.0.0" + readable-stream "^3.0.0" + +split2@^4.0.0: + version "4.2.0" + resolved "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz" + integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== split@^1.0.1: version "1.0.1" @@ -7777,14 +10876,24 @@ split@^1.0.1: dependencies: through "2" +sprintf-js@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz" + integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +srcset@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/srcset/-/srcset-5.0.0.tgz" + integrity sha512-SqEZaAEhe0A6ETEa9O1IhSPC7MdvehZtCnTR0AftXk3QhY2UNgb+NApFOUPZILXk/YTDfFxMTNJOBpzrJsEdIA== sshpk@^1.14.1: version "1.18.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.18.0.tgz#1663e55cddf4d688b86a46b77f0d5fe363aba028" + resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz" integrity sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ== dependencies: asn1 "~0.2.3" @@ -7797,118 +10906,121 @@ sshpk@^1.14.1: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -standard-as-callback@^2.0.1: - version "2.1.0" - resolved "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz" - integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== - -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz" - integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= +ssri@^10.0.0, ssri@^10.0.6: + version "10.0.6" + resolved "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz" + integrity sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ== dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" + minipass "^7.0.3" -stdout-stderr@^0.1.9: - version "0.1.13" - resolved "https://registry.npmjs.org/stdout-stderr/-/stdout-stderr-0.1.13.tgz" - integrity sha512-Xnt9/HHHYfjZ7NeQLvuQDyL1LnbsbddgMFKCuaQKwGCdJm8LnstZIXop+uOY36UR1UXXoHXfMbC1KlVdVd2JLA== - dependencies: - debug "^4.1.1" - strip-ansi "^6.0.0" +stdin-discarder@^0.2.2: + version "0.2.2" + resolved "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz" + integrity sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ== stream-length@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/stream-length/-/stream-length-1.0.2.tgz#8277f3cbee49a4daabcfdb4e2f4a9b5e9f2c9f00" + resolved "https://registry.npmjs.org/stream-length/-/stream-length-1.0.2.tgz" integrity sha512-aI+qKFiwoDV4rsXiS7WRoCt+v2RX1nUj17+KJC5r2gfh5xoSJIfP6Y3Do/HtvesFcTSWthIuJ3l1cvKQY/+nZg== dependencies: bluebird "^2.6.2" +streamx@^2.12.5: + version "2.19.0" + resolved "https://registry.npmjs.org/streamx/-/streamx-2.19.0.tgz" + integrity sha512-5z6CNR4gtkPbwlxyEqoDGDmWIzoNJqCBt4Eac1ICP9YaIT08ct712cFj0u1rx4F8luAuL+3Qc+RFIdI4OX00kg== + dependencies: + fast-fifo "^1.3.2" + queue-tick "^1.0.1" + text-decoder "^1.1.0" + optionalDependencies: + bare-events "^2.2.0" + +streamx@^2.15.0, streamx@^2.18.0: + version "2.20.0" + resolved "https://registry.npmjs.org/streamx/-/streamx-2.20.0.tgz" + integrity sha512-ZGd1LhDeGFucr1CUCTBOS58ZhEendd0ttpGT3usTvosS4ntIwKN9LJFp+OeCSprsCPL14BXVRZlHGRY1V9PVzQ== + dependencies: + fast-fifo "^1.3.2" + queue-tick "^1.0.1" + text-decoder "^1.1.0" + optionalDependencies: + bare-events "^2.2.0" + strict-uri-encode@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" + resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz" integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ== string-template@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/string-template/-/string-template-1.0.0.tgz" - integrity sha1-np8iM9wA8hhxjsN5oopWc+zKi5Y= - -string-template@~0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz" - integrity sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0= + integrity sha512-SLqR3GBUXuoPP5MmYtD7ompvXiG87QjT6lzOszyXjTM86Uu7At7vNnt2xgyTLq5o9T4IxTYFyGxcULqpsmsfdg== "string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string-width@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" - -string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: - version "4.2.2" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz" - integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.0" + strip-ansi "^6.0.1" string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== dependencies: eastasianwidth "^0.2.0" emoji-regex "^9.2.2" strip-ansi "^7.0.1" -string_decoder@^1.1.1: +string-width@^7.0.0, string-width@^7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz" + integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== + dependencies: + emoji-regex "^10.3.0" + get-east-asian-width "^1.0.0" + strip-ansi "^7.1.0" + +string.prototype.trim@^1.2.8: + version "1.2.8" + resolved "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz" + integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string.prototype.trimend@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz" + integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string.prototype.trimstart@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz" + integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== @@ -7924,74 +11036,44 @@ string_decoder@~1.1.1: "strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - -strip-ansi@^5.0.0, strip-ansi@^5.1.0: - version "5.2.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== - dependencies: - ansi-regex "^5.0.0" - -strip-ansi@^6.0.1: +strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" -strip-ansi@^7.0.1: +strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz" integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== dependencies: ansi-regex "^6.0.1" -strip-bom-buf@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz" - integrity sha1-HLRar1dTD0yvhsf3UXnSyaUd1XI= +strip-bom-buf@^3.0.0, strip-bom-buf@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-3.0.1.tgz" + integrity sha512-iJaWw2WroigLHzQysdc5WWeUc99p7ea7AEgB6JkY8CMyiO1yTVAA1gIlJJgORElUIR+lcZJkNl1OGChMhvc2Cw== dependencies: is-utf8 "^0.2.1" -strip-bom-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz" - integrity sha1-+H217yYT9paKpUWr/h7HKLaoKco= +strip-bom-stream@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-5.0.0.tgz" + integrity sha512-Yo472mU+3smhzqeKlIxClre4s4pwtYZEvDNQvY/sJpnChdaxmKuwU28UVx/v1ORKNMxkmj1GBuvxJQyBk6wYMQ== dependencies: - first-chunk-stream "^2.0.0" - strip-bom "^2.0.0" + first-chunk-stream "^5.0.0" + strip-bom-buf "^3.0.0" -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz" - integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= - dependencies: - is-utf8 "^0.2.0" +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== strip-bom@^4.0.0: version "4.0.0" @@ -8001,19 +11083,26 @@ strip-bom@^4.0.0: strip-color@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/strip-color/-/strip-color-0.1.0.tgz" - integrity sha1-EG9l09PmotlAHKwOsM6LinArT3s= - -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + integrity sha512-p9LsUieSjWNNAxVCXLeilaDlmuUOrDS5/dF9znM1nZc7EGX5+zEFC0bEevsNIaldjlks+2jns5Siz6F9iK6jwA== strip-final-newline@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== -strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +strip-json-comments@3.1.1, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -8021,46 +11110,43 @@ strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1. strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +strnum@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz" + integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== + +stubborn-fs@^1.2.5: + version "1.2.5" + resolved "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-1.2.5.tgz" + integrity sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g== -supports-color@8.1.1: +supports-color@8.1.1, supports-color@^8, supports-color@^8.1.1: version "8.1.1" resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - -supports-color@^5.0.0, supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0: +supports-color@^5.3.0: version "5.5.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7, supports-color@^7.0.0, supports-color@^7.1.0: version "7.2.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" -supports-hyperlinks@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz" - integrity sha512-HHi5kVSefKaJkGYXbDuKbUGRVxqnWGn3J2e39CYcNJEfWciGq2zYtOhXLTlvrOZW1QU7VX67w7fMmWafHX9Pfw== - dependencies: - has-flag "^2.0.0" - supports-color "^5.0.0" - -supports-hyperlinks@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz" - integrity sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA== +supports-hyperlinks@^2.2.0: + version "2.3.0" + resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz" + integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== dependencies: has-flag "^4.0.0" supports-color "^7.0.0" @@ -8072,62 +11158,66 @@ supports-preserve-symlinks-flag@^1.0.0: symbol-tree@^3.2.4: version "3.2.4" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -synp@^1.9.10: - version "1.9.10" - resolved "https://registry.yarnpkg.com/synp/-/synp-1.9.10.tgz#53163321a600418c9b06af0db499939ffce12907" - integrity sha512-G9Z/TXTaBG1xNslUf3dHFidz/8tvvRaR560WWyOwyI7XrGGEGBTEIIg4hdRh1qFtz8mPYynAUYwWXUg/Zh0Pzw== - dependencies: - "@yarnpkg/lockfile" "^1.1.0" - bash-glob "^2.0.0" - colors "1.4.0" - commander "^7.2.0" - eol "^0.9.1" - lodash "4.17.21" - nmtree "^1.0.6" - semver "^7.3.5" - sort-object-keys "^1.1.3" - -table@^6.0.4: - version "6.0.7" - resolved "https://registry.npmjs.org/table/-/table-6.0.7.tgz" - integrity sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g== - dependencies: - ajv "^7.0.2" - lodash "^4.17.20" - slice-ansi "^4.0.0" - string-width "^4.2.0" - -taketalk@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/taketalk/-/taketalk-1.0.0.tgz" - integrity sha1-tNTw3u0gauffd1sSnqLKbeUvJt0= - dependencies: - get-stdin "^4.0.1" - minimist "^1.1.0" - -tar-fs@2.1.1, tar-fs@^2.0.0: - version "2.1.1" - resolved "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz" - integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== +tar-fs@^3.0.6: + version "3.0.6" + resolved "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz" + integrity sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w== dependencies: - chownr "^1.1.1" - mkdirp-classic "^0.5.2" pump "^3.0.0" - tar-stream "^2.1.4" + tar-stream "^3.1.5" + optionalDependencies: + bare-fs "^2.1.1" + bare-path "^2.1.0" -tar-stream@^2.1.4, tar-stream@^2.2.0: +tar-stream@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz" integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +tar-stream@^3.1.5: + version "3.1.7" + resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz" + integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ== + dependencies: + b4a "^1.6.4" + fast-fifo "^1.2.0" + streamx "^2.15.0" + +tar@^6.1.11, tar@^6.2.1: + version "6.2.1" + resolved "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz" + integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +teex@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz" + integrity sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg== + dependencies: + streamx "^2.12.5" + +terminal-link@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/terminal-link/-/terminal-link-3.0.0.tgz" + integrity sha512-flFL3m4wuixmf6IfhFJd1YPiLiMuxEc8uHRM1buzIeZPm22Au2pDqBJQgdo7n1WfPU1ONFGv7YDwpFBmHGF6lg== + dependencies: + ansi-escapes "^5.0.0" + supports-hyperlinks "^2.2.0" test-exclude@^6.0.0: version "6.0.0" @@ -8138,15 +11228,43 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +text-decoder@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.1.tgz" + integrity sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA== + dependencies: + b4a "^1.6.4" + +text-extensions@^1.0.0: + version "1.9.0" + resolved "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz" + integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== + text-table@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +textextensions@^6.11.0: + version "6.11.0" + resolved "https://registry.npmjs.org/textextensions/-/textextensions-6.11.0.tgz" + integrity sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ== + dependencies: + editions "^6.21.0" + +thread-stream@^2.6.0: + version "2.7.0" + resolved "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz" + integrity sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw== + dependencies: + real-require "^0.2.0" -textextensions@^2.5.0: - version "2.6.0" - resolved "https://registry.npmjs.org/textextensions/-/textextensions-2.6.0.tgz" - integrity sha512-49WtAWS+tcsy93dRt6P0P3AMD2m5PvXRhuEA0kaXos5ZLlujtYmpmFsB+QvWUSxE1ZsstmYXfQ7L40+EcQgpAQ== +thread-stream@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz" + integrity sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A== + dependencies: + real-require "^0.2.0" through2@^2.0.0: version "2.0.5" @@ -8156,23 +11274,22 @@ through2@^2.0.0: readable-stream "~2.3.6" xtend "~4.0.1" -through2@^3.0.0, through2@^3.0.1, through2@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz" - integrity sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ== +through2@^4.0.0: + version "4.0.2" + resolved "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz" + integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== dependencies: - inherits "^2.0.4" - readable-stream "2 || 3" + readable-stream "3" -through@2, "through@>=2.2.7 <3", through@^2.3.6, through@^2.3.8: +through@2, "through@>=2.2.7 <3", through@^2.3.8: version "2.3.8" resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== -timed-out@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz" - integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= +tiny-jsonc@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/tiny-jsonc/-/tiny-jsonc-1.0.1.tgz" + integrity sha512-ik6BCxzva9DoiEfDX/li0L2cWKPPENYvixUprFdl3YPi4bZZUhDnNI9YUkacrv+uIG90dnxR5mNqaoD6UhD6Bw== tmp@^0.0.33: version "0.0.33" @@ -8181,45 +11298,23 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" -tmp@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz" - integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw== - dependencies: - rimraf "^2.6.3" - tmp@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" - integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== - dependencies: - rimraf "^3.0.0" + version "0.2.3" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz" + integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== to-object-path@^0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz" - integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + integrity sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg== dependencies: kind-of "^3.0.2" -to-readable-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz" - integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz" - integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" @@ -8227,42 +11322,34 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - toml@^2.3.2: version "2.3.6" resolved "https://registry.npmjs.org/toml/-/toml-2.3.6.tgz" integrity sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ== -tough-cookie@*, tough-cookie@^4.1.3: +tough-cookie@*: version "4.1.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" - integrity "sha1-l7mtsHKLQigKo9gUtrmZsv8DGL8= sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==" + resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz" + integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== dependencies: psl "^1.1.33" punycode "^2.1.1" universalify "^0.2.0" url-parse "^1.5.3" -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== +tough-cookie@^4.1.4: + version "4.1.4" + resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz" + integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== dependencies: - psl "^1.1.28" + psl "^1.1.33" punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" tr46@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.0.0.tgz#3b46d583613ec7283020d79019f1335723801cec" + resolved "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz" integrity sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g== dependencies: punycode "^2.3.1" @@ -8270,24 +11357,47 @@ tr46@^5.0.0: tr46@~0.0.3: version "0.0.3" resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== "traverse@>=0.3.0 <0.4": version "0.3.9" - resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" + resolved "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz" integrity sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ== -treeify@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz" - integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== +treeverse@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/treeverse/-/treeverse-3.0.0.tgz" + integrity sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ== -ts-node@^10.7.0: - version "10.7.0" - resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz" - integrity sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A== +trim-newlines@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz" + integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== + +ts-api-utils@^1.0.1: + version "1.0.3" + resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz" + integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== + +ts-json-schema-generator@^1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-1.5.1.tgz" + integrity sha512-apX5qG2+NA66j7b4AJm8q/DpdTeOsjfh7A3LpKsUiil0FepkNwtN28zYgjrsiiya2/OPhsr/PSjX5FUYg79rCg== + dependencies: + "@types/json-schema" "^7.0.15" + commander "^12.0.0" + glob "^8.0.3" + json5 "^2.2.3" + normalize-path "^3.0.0" + safe-stable-stringify "^2.4.3" + typescript "~5.4.2" + +ts-node@^10.8.1, ts-node@^10.9.2: + version "10.9.2" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== dependencies: - "@cspotcode/source-map-support" "0.7.0" + "@cspotcode/source-map-support" "^0.8.0" "@tsconfig/node10" "^1.0.7" "@tsconfig/node12" "^1.0.7" "@tsconfig/node14" "^1.0.0" @@ -8298,57 +11408,59 @@ ts-node@^10.7.0: create-require "^1.1.0" diff "^4.0.1" make-error "^1.1.1" - v8-compile-cache-lib "^3.0.0" + v8-compile-cache-lib "^3.0.1" yn "3.1.1" -ts-retry-promise@^0.6.0: - version "0.6.0" - resolved "https://registry.npmjs.org/ts-retry-promise/-/ts-retry-promise-0.6.0.tgz" - integrity sha512-8DF80uA7JPu6m8ouHxGkyBpPTIGQnsgIUgLDqcRaD7EEhVowjG72KqCX334gsa1P+AmzeTVdd/xEzVFCAuPCtg== - -tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: - version "1.14.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +ts-retry-promise@^0.8.1: + version "0.8.1" + resolved "https://registry.npmjs.org/ts-retry-promise/-/ts-retry-promise-0.8.1.tgz" + integrity sha512-+AHPUmAhr5bSRRK5CurE9kNH8gZlEHnCgusZ0zy2bjfatUBDX0h6vGQjiT0YrGwSDwRZmU+bapeX6mj55FOPvg== -tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz" - integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== +tsconfig-paths@^3.15.0: + version "3.15.0" + resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz" + integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" -tslib@^2.2.0, tslib@^2.3.1: - version "2.3.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== +tslib@^2.0.1, tslib@^2.1.0: + version "2.7.0" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz" + integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== -tslib@^2.4.0, tslib@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" - integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== +tslib@^2.0.3, tslib@^2.5.0, tslib@^2.6.2: + version "2.6.2" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== -tsutils@^3.17.1: - version "3.21.0" - resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== +tuf-js@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.1.tgz" + integrity sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA== dependencies: - tslib "^1.8.1" + "@tufjs/models" "2.0.1" + debug "^4.3.4" + make-fetch-happen "^13.0.1" tunnel-agent@*, tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== dependencies: safe-buffer "^5.0.1" tunnel@0.0.6, tunnel@^0.0.6: version "0.0.6" - resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" + resolved "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz" integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" @@ -8357,15 +11469,15 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8: +type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.8: version "4.0.8" resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@^0.11.0: - version "0.11.0" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz" - integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== +type-fest@^0.18.0: + version "0.18.1" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz" + integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== type-fest@^0.20.2: version "0.20.2" @@ -8387,12 +11499,73 @@ type-fest@^0.8.0, type-fest@^0.8.1: resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -typed-rest-client@^1.8.4: - version "1.8.9" - resolved "https://registry.yarnpkg.com/typed-rest-client/-/typed-rest-client-1.8.9.tgz#e560226bcadfe71b0fb5c416b587f8da3b8f92d8" - integrity sha512-uSmjE38B80wjL85UFX3sTYEUlvZ1JgCRhsWj/fJ4rZ0FqDUFoIuodtiVeE+cUqiVTOKPdKrp/sdftD15MDek6g== +type-fest@^1.0.2: + version "1.4.0" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz" + integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== + +type-fest@^2.13.0: + version "2.19.0" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + +type-fest@^4.18.2, type-fest@^4.21.0, type-fest@^4.6.0, type-fest@^4.7.1: + version "4.25.0" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-4.25.0.tgz" + integrity sha512-bRkIGlXsnGBRBQRAY56UXBm//9qH4bmJfFvq83gSz41N282df+fjy8ofcEgc1sM8geNt5cl6mC2g9Fht1cs8Aw== + +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + +typed-query-selector@^2.12.0: + version "2.12.0" + resolved "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz" + integrity sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg== + +typed-rest-client@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-2.0.2.tgz" + integrity sha512-rmAQM2gZw/PQpK5+5aSs+I6ZBv4PFC2BT1o+0ADS1SgSejA+14EmbI2Lt8uXwkX7oeOMkwFmg0pHKwe8D9IT5A== dependencies: - qs "^6.9.1" + des.js "^1.1.0" + js-md4 "^0.3.2" + qs "^6.10.3" tunnel "0.0.6" underscore "^1.12.1" @@ -8406,19 +11579,44 @@ typedarray-to-buffer@^3.1.5: typedarray@^0.0.6: version "0.0.6" resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== + +typedoc-plugin-missing-exports@0.23.0: + version "0.23.0" + resolved "https://registry.npmjs.org/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-0.23.0.tgz" + integrity sha512-9smahDSsFRno9ZwoEshQDuIYMHWGB1E6LUud5qMxR2wNZ0T4DlZz0QjoK3HzXtX34mUpTH0dYtt7NQUK4D6B6Q== + +typedoc@^0.25.12: + version "0.25.13" + resolved "https://registry.npmjs.org/typedoc/-/typedoc-0.25.13.tgz" + integrity sha512-pQqiwiJ+Z4pigfOnnysObszLiU3mVLWAExSPf+Mu06G/qsc3wzbuM56SZQvONhHLncLUhYzOVkjFFpFfL5AzhQ== + dependencies: + lunr "^2.3.9" + marked "^4.3.0" + minimatch "^9.0.3" + shiki "^0.14.7" -typescript@^5.5.2: - version "5.5.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.2.tgz#c26f023cb0054e657ce04f72583ea2d85f8d0507" - integrity sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew== +"typescript@^4.6.4 || ^5.2.2", typescript@^5.4.3, typescript@^5.4.5, typescript@~5.4.2: + version "5.4.5" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz" + integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== typical@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/typical/-/typical-6.0.1.tgz" integrity sha512-+g3NEp7fJLe9DPa1TArHm9QAA7YciZmWnfAqEaFrBihQ7epOv9i99rjtgb6Iz0wh3WuQDjsCTDfgRoGnmHN81A== -unbzip2-stream@1.4.3: +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +unbzip2-stream@^1.4.3: version "1.4.3" resolved "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz" integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== @@ -8427,41 +11625,50 @@ unbzip2-stream@1.4.3: through "^2.3.8" underscore@^1.12.1: - version "1.13.6" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.6.tgz#04786a1f589dc6c09f761fc5f45b89e935136441" - integrity sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A== + version "1.13.7" + resolved "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz" + integrity sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g== undici-types@~5.26.4: version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== undici-types@~6.19.2: version "6.19.8" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz" integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== +undici@^5.25.4: + version "5.28.4" + resolved "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz" + integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" + "@fastify/busboy" "^2.0.0" -unique-string@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz" - integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== +unicorn-magic@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz" + integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== + +unique-filename@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz" + integrity sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g== + dependencies: + unique-slug "^4.0.0" + +unique-slug@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz" + integrity sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ== dependencies: - crypto-random-string "^2.0.0" + imurmurhash "^0.1.4" universal-user-agent@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" - integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== + version "6.0.1" + resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz" + integrity sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ== universalify@^0.1.0: version "0.1.2" @@ -8470,35 +11677,22 @@ universalify@^0.1.0: universalify@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz" integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== universalify@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" - integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== - -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz" - integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - -untildify@^3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/untildify/-/untildify-3.0.3.tgz" - integrity sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA== - -unzip-response@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz" - integrity sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c= + resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +untildify@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/untildify/-/untildify-5.0.0.tgz" + integrity sha512-bOgQLUnd2G5rhzaTvh1VCI9Fo6bC5cLTpH17T5aFfamyXFYDbbdzN6IXdeoc3jBS7T9hNTmJtYUzJCJ2Xlc9gA== unzipper@^0.10.11: version "0.10.14" - resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.10.14.tgz#d2b33c977714da0fbc0f82774ad35470a7c962b1" + resolved "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz" integrity sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g== dependencies: big-integer "^1.6.17" @@ -8512,25 +11706,43 @@ unzipper@^0.10.11: readable-stream "~2.3.6" setimmediate "~1.0.4" -update-notifier@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz" - integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== dependencies: - boxen "^5.0.0" - chalk "^4.1.0" - configstore "^5.0.1" - has-yarn "^2.1.0" - import-lazy "^2.1.0" - is-ci "^2.0.0" - is-installed-globally "^0.4.0" - is-npm "^5.0.0" - is-yarn-global "^0.3.0" - latest-version "^5.1.0" - pupa "^2.1.1" - semver "^7.3.4" - semver-diff "^3.1.1" - xdg-basedir "^4.0.0" + escalade "^3.1.1" + picocolors "^1.0.0" + +update-notifier@^7.3.0: + version "7.3.0" + resolved "https://registry.npmjs.org/update-notifier/-/update-notifier-7.3.0.tgz" + integrity sha512-nA5Zoy3rahYd/Lx1s6jZYHfrKKYOgw0kThkLdwgJtXEFsXqEbMnwdVNPT9D+HELlEXqTR7Iq8rjg/NjenGLIvg== + dependencies: + boxen "^8.0.0" + chalk "^5.3.0" + configstore "^7.0.0" + is-in-ci "^1.0.0" + is-installed-globally "^1.0.0" + is-npm "^6.0.0" + latest-version "^9.0.0" + pupa "^3.1.0" + semver "^7.6.3" + xdg-basedir "^5.1.0" + +upper-case-first@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz" + integrity sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg== + dependencies: + tslib "^2.0.3" + +upper-case@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz" + integrity sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg== + dependencies: + tslib "^2.0.3" uri-js@^4.2.2: version "4.4.1" @@ -8539,28 +11751,9 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz" - integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= - -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz" - integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= - dependencies: - prepend-http "^1.0.1" - -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz" - integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= - dependencies: - prepend-http "^2.0.0" - url-parse@^1.5.3: version "1.5.10" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz" integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== dependencies: querystringify "^2.1.1" @@ -8568,40 +11761,40 @@ url-parse@^1.5.3: url-template@^2.0.8: version "2.0.8" - resolved "https://registry.yarnpkg.com/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21" + resolved "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz" integrity sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw== -use@^3.1.0: - version "3.1.1" - resolved "https://registry.npmjs.org/use/-/use-3.1.1.tgz" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== +urlpattern-polyfill@10.0.0: + version "10.0.0" + resolved "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz" + integrity sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg== -util-deprecate@^1.0.1, util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -uuid@^3.3.2, uuid@^3.3.3: - version "3.4.0" - resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +uuid@^10.0.0: + version "10.0.0" + resolved "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz" + integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== uuid@^8.3.0, uuid@^8.3.2: version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -v8-compile-cache-lib@^3.0.0: +uuid@^9.0.1: + version "9.0.1" + resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + +v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== -v8-compile-cache@^2.0.3: - version "2.3.0" - resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz" - integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== - -validate-npm-package-license@^3.0.1: +validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== @@ -8609,75 +11802,95 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +validate-npm-package-name@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz" + integrity sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ== + dependencies: + builtins "^5.0.0" + +validate-npm-package-name@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz" + integrity sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ== + verror@1.10.0: version "1.10.0" resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== dependencies: assert-plus "^1.0.0" core-util-is "1.0.2" extsprintf "^1.2.0" -vinyl-file@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/vinyl-file/-/vinyl-file-3.0.0.tgz" - integrity sha1-sQTZ5ECf+jJfqt1SBkLQo7SIs2U= +version-range@^4.13.0: + version "4.14.0" + resolved "https://registry.npmjs.org/version-range/-/version-range-4.14.0.tgz" + integrity sha512-gjb0ARm9qlcBAonU4zPwkl9ecKkas+tC2CGwFfptTCWWIVTWY1YUbT2zZKsOAF1jR/tNxxyLwwG0cb42XlYcTg== + +vinyl-file@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/vinyl-file/-/vinyl-file-5.0.0.tgz" + integrity sha512-MvkPF/yA1EX7c6p+juVIvp9+Lxp70YUfNKzEWeHMKpUNVSnTZh2coaOqLxI0pmOe2V9nB+OkgFaMDkodaJUyGw== dependencies: - graceful-fs "^4.1.2" - pify "^2.3.0" - strip-bom-buf "^1.0.0" - strip-bom-stream "^2.0.0" - vinyl "^2.0.1" + "@types/vinyl" "^2.0.7" + strip-bom-buf "^3.0.1" + strip-bom-stream "^5.0.0" + vinyl "^3.0.0" -vinyl@^2.0.1, vinyl@^2.2.0, vinyl@^2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz" - integrity sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw== +vinyl@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/vinyl/-/vinyl-3.0.0.tgz" + integrity sha512-rC2VRfAVVCGEgjnxHUnpIVh3AGuk62rP3tqVrn+yab0YH7UULisC085+NYH+mnqf3Wx4SpSi1RQMwudL89N03g== dependencies: - clone "^2.1.1" - clone-buffer "^1.0.0" + clone "^2.1.2" clone-stats "^1.0.0" - cloneable-readable "^1.0.0" - remove-trailing-separator "^1.0.1" - replace-ext "^1.0.0" + remove-trailing-separator "^1.1.0" + replace-ext "^2.0.0" + teex "^1.0.1" + +vscode-oniguruma@^1.7.0: + version "1.7.0" + resolved "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz" + integrity sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA== + +vscode-textmate@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz" + integrity sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg== w3c-xmlserializer@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" + resolved "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz" integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA== dependencies: xml-name-validator "^5.0.0" +walk-up-path@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/walk-up-path/-/walk-up-path-3.0.1.tgz" + integrity sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA== + wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz" - integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== dependencies: defaults "^1.0.3" web-streams-polyfill@4.0.0-beta.3: version "4.0.0-beta.3" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38" + resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz" integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug== -web-streams-polyfill@^3.0.3: - version "3.2.0" - resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz" - integrity sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA== - -web-streams-polyfill@^3.2.1: - version "3.3.3" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b" - integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== - webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== webidl-conversions@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz" integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== websocket-driver@>=0.5.1: @@ -8696,19 +11909,19 @@ websocket-extensions@>=0.1.1: whatwg-encoding@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" + resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz" integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== dependencies: iconv-lite "0.6.3" whatwg-mimetype@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" + resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz" integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== whatwg-url@^14.0.0: version "14.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.0.0.tgz#00baaa7fd198744910c4b1ef68378f2200e4ceb6" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz" integrity sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw== dependencies: tr46 "^5.0.0" @@ -8717,43 +11930,65 @@ whatwg-url@^14.0.0: whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== dependencies: tr46 "~0.0.3" webidl-conversions "^3.0.0" +when-exit@^2.1.1: + version "2.1.3" + resolved "https://registry.npmjs.org/when-exit/-/when-exit-2.1.3.tgz" + integrity sha512-uVieSTccFIr/SFQdFWN/fFaQYmV37OKtuaGphMAzi4DmmUlrvRBJW5WSLkHyjNQY/ePJMz3LoiX9R3yy1Su6Hw== + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + which-module@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== -which@2.0.2, which@^2.0.1, which@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== +which-package-manager@^0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/which-package-manager/-/which-package-manager-0.0.1.tgz" + integrity sha512-a+bCExXd8OdYky5J59nimHxTCRPhxZSQtwKh3Ew6lpC4oY9f3KH77XDxcPrComVhSEPtvMjZigS2vZgZfgJuxA== dependencies: - isexe "^2.0.0" + execa "^7.1.1" + find-up "^6.3.0" + micromatch "^4.0.5" -which@^1.2.9: - version "1.3.1" - resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== +which-typed-array@^1.1.11, which-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz" + integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== dependencies: - isexe "^2.0.0" + available-typed-arrays "^1.0.5" + call-bind "^1.0.4" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" -wide-align@1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== +which@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: - string-width "^1.0.2 || 2" + isexe "^2.0.0" -widest-line@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz" - integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA== +which@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/which/-/which-4.0.0.tgz" + integrity sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg== dependencies: - string-width "^2.1.1" + isexe "^3.1.1" widest-line@^3.1.0: version "3.1.0" @@ -8762,51 +11997,50 @@ widest-line@^3.1.0: dependencies: string-width "^4.0.0" -with-open-file@^0.1.6: - version "0.1.7" - resolved "https://registry.npmjs.org/with-open-file/-/with-open-file-0.1.7.tgz" - integrity sha512-ecJS2/oHtESJ1t3ZfMI3B7KIDKyfN0O16miWxdn30zdh66Yd3LsRFebXZXq6GU4xfxLf6nVxp9kIqElb5fqczA== +widest-line@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz" + integrity sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig== dependencies: - p-finally "^1.0.0" - p-try "^2.1.0" - pify "^4.0.1" + string-width "^5.0.1" -word-wrap@^1.2.3: - version "1.2.5" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" - integrity "sha1-0sRcbdT7zmIaZvE2y+Mor9BBCzQ= sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==" +widest-line@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz" + integrity sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA== + dependencies: + string-width "^7.0.0" -workerpool@6.1.0: - version "6.1.0" - resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz" - integrity sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg== +wireit@^0.14.4: + version "0.14.4" + resolved "https://registry.npmjs.org/wireit/-/wireit-0.14.4.tgz" + integrity sha512-WNAXEw2cJs1nSRNJNRcPypARZNumgtsRTJFTNpd6turCA6JZ6cEwl4ZU3C1IHc/3IaXoPu9LdxcI5TBTdD6/pg== + dependencies: + braces "^3.0.2" + chokidar "^3.5.3" + fast-glob "^3.2.11" + jsonc-parser "^3.0.0" + proper-lockfile "^4.1.2" + +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz" - integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - -wrap-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-4.0.0.tgz" - integrity sha512-uMTsj9rDb0/7kk1PbcbCcwvHUxp60fGDB/NNXpVa0Q+ic/e7y5+BwTxKfQ33VYgDppSwi/FBzpetYzo8s6tfbg== - dependencies: - ansi-styles "^3.2.0" - string-width "^2.1.1" - strip-ansi "^4.0.0" - wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" @@ -8827,17 +12061,26 @@ wrap-ansi@^7.0.0: wrap-ansi@^8.1.0: version "8.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== dependencies: ansi-styles "^6.1.0" string-width "^5.0.1" strip-ansi "^7.0.1" +wrap-ansi@^9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz" + integrity sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q== + dependencies: + ansi-styles "^6.2.1" + string-width "^7.0.0" + strip-ansi "^7.1.0" + wrappy@1: version "1.0.2" resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== write-file-atomic@^3.0.0: version "3.0.3" @@ -8849,60 +12092,38 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write-json-file@^4.1.1: - version "4.3.0" - resolved "https://registry.npmjs.org/write-json-file/-/write-json-file-4.3.0.tgz" - integrity sha512-PxiShnxf0IlnQuMYOPPhPkhExoCQuTUNPOa/2JWCYTmBquU9njyyDuwRKN26IZBlp4yn1nt+Agh2HOOBl+55HQ== +write-file-atomic@^5.0.0: + version "5.0.1" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz" + integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== dependencies: - detect-indent "^6.0.0" - graceful-fs "^4.1.15" - is-plain-obj "^2.0.0" - make-dir "^3.0.0" - sort-keys "^4.0.0" - write-file-atomic "^3.0.0" - -ws@8.5.0: - version "8.5.0" - resolved "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz" - integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== - -ws@^7.4.4: - version "7.5.10" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" - integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== + imurmurhash "^0.1.4" + signal-exit "^4.0.1" -ws@^8.16.0: - version "8.17.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" - integrity "sha1-kpPaUwu1SP68lTcdkPnIeHJ9kZs= sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==" +ws@^8.18.0: + version "8.18.0" + resolved "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== xcase@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/xcase/-/xcase-2.0.1.tgz#c7fa72caa0f440db78fd5673432038ac984450b9" + resolved "https://registry.npmjs.org/xcase/-/xcase-2.0.1.tgz" integrity sha512-UmFXIPU+9Eg3E9m/728Bii0lAIuoc+6nbrNUKaRPJOFp91ih44qqGlWtxMB6kXFrRD6po+86ksHM5XHCfk6iPw== -xdg-basedir@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz" - integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== +xdg-basedir@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz" + integrity sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ== xml-name-validator@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673" + resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz" integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== -xml2js@^0.4.16: - version "0.4.23" - resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz" - integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== - dependencies: - sax ">=0.6.0" - xmlbuilder "~11.0.0" - -xml2js@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7" - integrity sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA== +xml2js@^0.6.2: + version "0.6.2" + resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz" + integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== dependencies: sax ">=0.6.0" xmlbuilder "~11.0.0" @@ -8914,13 +12135,18 @@ xmlbuilder@~11.0.0: xmlchars@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xpath@^0.0.32: - version "0.0.32" - resolved "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz" - integrity sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw== +xmlcreate@^2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz" + integrity sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg== + +xpath@^0.0.34: + version "0.0.34" + resolved "https://registry.npmjs.org/xpath/-/xpath-0.0.34.tgz" + integrity sha512-FxF6+rkr1rNSQrhUNYrAFJpRXNzlDoMxeXN5qI84939ylEv3qqPFKa85Oxr6tDaJKqwW6KKyo2v26TSv3k6LeA== xtend@~4.0.1: version "4.0.2" @@ -8928,30 +12154,20 @@ xtend@~4.0.1: integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== y18n@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz" - integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== + version "4.0.3" + resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== y18n@^5.0.5: - version "5.0.5" - resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz" - integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg== - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== + version "5.0.8" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.0: - version "1.10.0" - resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz" - integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== - yargs-parser@20.2.4: version "20.2.4" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz" @@ -8965,10 +12181,15 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^20.2.2: - version "20.2.6" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.6.tgz" - integrity sha512-AP1+fQIWSM/sMiET8fyayjx/J+JmTPt2Mr0FkrgqB4todtfa53sOsrSAcIrJRD5XS20bKUwaDIuMkWKCEiQLKA== +yargs-parser@^20.2.2, yargs-parser@^20.2.3: + version "20.2.9" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs-unparser@2.0.0: version "2.0.0" @@ -9010,96 +12231,76 @@ yargs@^15.0.2: y18n "^4.0.0" yargs-parser "^18.1.2" -yarn-audit-fix@^9.3.7: - version "9.3.7" - resolved "https://registry.yarnpkg.com/yarn-audit-fix/-/yarn-audit-fix-9.3.7.tgz#bd8c66ce2edecd930d99d55e9c6470aabdd35811" - integrity sha512-zYSp7fp3oJhGGt7o4/DvwQJTfV8M0ozOUPVpHmCAeQkJhNDj/3Zq8vLYToyffvzy2A86zZKEMPTQsLrNvYvqqA== - dependencies: - "@types/find-cache-dir" "^3.2.1" - "@types/fs-extra" "^9.0.13" - "@types/lodash-es" "^4.17.6" - "@types/semver" "^7.3.12" - "@types/yarnpkg__lockfile" "^1.1.5" - "@yarnpkg/lockfile" "^1.1.0" - chalk "^5.0.1" - commander "^9.4.0" - find-cache-dir "^4.0.0" - find-up "^6.3.0" - fs-extra "^10.1.0" - globby "^13.1.2" - js-yaml "^4.1.0" - lodash-es "^4.17.21" - pkg-dir "^7.0.0" - semver "^7.3.7" - synp "^1.9.10" - tslib "^2.4.0" +yargs@^17.0.0, yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" yauzl@^2.10.0: version "2.10.0" resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz" - integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== dependencies: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" -yeoman-environment@^2.10.3, yeoman-environment@^2.9.5: - version "2.10.3" - resolved "https://registry.npmjs.org/yeoman-environment/-/yeoman-environment-2.10.3.tgz" - integrity sha512-pLIhhU9z/G+kjOXmJ2bPFm3nejfbH+f1fjYRSOteEXDBrv1EoJE/e+kuHixSXfCYfTkxjYsvRaDX+1QykLCnpQ== - dependencies: - chalk "^2.4.1" - debug "^3.1.0" - diff "^3.5.0" - escape-string-regexp "^1.0.2" - execa "^4.0.0" - globby "^8.0.1" - grouped-queue "^1.1.0" - inquirer "^7.1.0" - is-scoped "^1.0.0" - lodash "^4.17.10" - log-symbols "^2.2.0" - mem-fs "^1.1.0" - mem-fs-editor "^6.0.0" - npm-api "^1.0.0" - semver "^7.1.3" - strip-ansi "^4.0.0" - text-table "^0.2.0" - untildify "^3.0.3" - yeoman-generator "^4.8.2" - -yeoman-generator@^4.12.0, yeoman-generator@^4.8.2: - version "4.13.0" - resolved "https://registry.npmjs.org/yeoman-generator/-/yeoman-generator-4.13.0.tgz" - integrity sha512-f2/5N5IR3M2Ozm+QocvZQudlQITv2DwI6Mcxfy7R7gTTzaKgvUpgo/pQMJ+WQKm0KN0YMWCFOZpj0xFGxevc1w== - dependencies: - async "^2.6.2" - chalk "^2.4.2" - cli-table "^0.3.1" - cross-spawn "^6.0.5" - dargs "^6.1.0" - dateformat "^3.0.3" +yeoman-environment@^4.0.0: + version "4.4.1" + resolved "https://registry.npmjs.org/yeoman-environment/-/yeoman-environment-4.4.1.tgz" + integrity sha512-x1TXPM6msNm9+zWLUuegfBf2AOwilTIUMAKHmhJvm9zidiDDrkp8cIuK32CvADuMxgbrGrvwHejDWnPScasnkw== + dependencies: + "@yeoman/adapter" "^1.4.0" + "@yeoman/conflicter" "^2.0.0-alpha.2" + "@yeoman/namespace" "^1.0.0" + "@yeoman/transform" "^1.2.0" + "@yeoman/types" "^1.1.1" + arrify "^3.0.0" + chalk "^5.3.0" + commander "^11.1.0" + debug "^4.3.4" + execa "^8.0.1" + fly-import "^0.4.0" + globby "^14.0.0" + grouped-queue "^2.0.0" + locate-path "^7.2.0" + lodash-es "^4.17.21" + mem-fs "^4.0.0" + mem-fs-editor "^11.0.0" + semver "^7.5.4" + slash "^5.1.0" + untildify "^5.0.0" + which-package-manager "^0.0.1" + +yeoman-generator@^7.1.0: + version "7.3.2" + resolved "https://registry.npmjs.org/yeoman-generator/-/yeoman-generator-7.3.2.tgz" + integrity sha512-23w2fyGtkIliDwTFfhJDxU047ILPLs3Oz4xfpYVh6qhEQT+aobO9jTOJEzTR/FR0Gx7YpJhjQoBWPK9oYIyGUA== + dependencies: + "@types/lodash-es" "^4.17.9" + "@types/node" "^18.18.5" + "@yeoman/namespace" "^1.0.0" + chalk "^5.3.0" debug "^4.1.1" - diff "^4.0.1" - error "^7.0.2" - find-up "^3.0.0" - github-username "^3.0.0" - istextorbinary "^2.5.1" - lodash "^4.17.11" - make-dir "^3.0.0" - mem-fs-editor "^7.0.1" - minimist "^1.2.5" - pretty-bytes "^5.2.0" - read-chunk "^3.2.0" - read-pkg-up "^5.0.0" - rimraf "^2.6.3" - run-async "^2.0.0" - semver "^7.2.1" - shelljs "^0.8.4" + execa "^8.0.1" + github-username "^7.0.0" + json-schema "^0.4.0" + latest-version "^7.0.0" + lodash-es "^4.17.21" + mem-fs-editor "^11.0.1" + minimist "^1.2.8" + read-package-up "^11.0.0" + semver "^7.5.4" + simple-git "^3.20.0" + sort-keys "^5.0.0" text-table "^0.2.0" - through2 "^3.0.1" - optionalDependencies: - grouped-queue "^1.1.0" - yeoman-environment "^2.9.5" yn@3.1.1: version "3.1.1" @@ -9112,35 +12313,30 @@ yocto-queue@^0.1.0: integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== yocto-queue@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" - integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== + version "1.1.1" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz" + integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g== yoctocolors-cjs@^2.1.2: version "2.1.2" - resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz#f4b905a840a37506813a7acaa28febe97767a242" + resolved "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz" integrity sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA== -yosay@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/yosay/-/yosay-2.0.2.tgz" - integrity sha512-avX6nz2esp7IMXGag4gu6OyQBsMh/SEn+ZybGu3yKPlOTE6z9qJrzG/0X5vCq/e0rPFy0CUYCze0G5hL310ibA== - dependencies: - ansi-regex "^2.0.0" - ansi-styles "^3.0.0" - chalk "^1.0.0" - cli-boxes "^1.0.0" - pad-component "0.0.1" - string-width "^2.0.0" - strip-ansi "^3.0.0" - taketalk "^1.0.0" - wrap-ansi "^2.0.0" +yoctocolors@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz" + integrity sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ== zip-stream@^4.1.0: version "4.1.1" - resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.1.tgz#1337fe974dbaffd2fa9a1ba09662a66932bd7135" + resolved "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz" integrity sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ== dependencies: archiver-utils "^3.0.4" compress-commons "^4.1.2" readable-stream "^3.6.0" + +zod@3.23.8: + version "3.23.8" + resolved "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz" + integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==