@salesforce/core 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/LICENSE.txt +1 -1
  2. package/README.md +93 -44
  3. package/lib/config/authInfoConfig.d.ts +19 -0
  4. package/lib/config/authInfoConfig.js +35 -0
  5. package/lib/config/config.d.ts +87 -22
  6. package/lib/config/config.js +117 -65
  7. package/lib/config/configAggregator.d.ts +41 -35
  8. package/lib/config/configAggregator.js +102 -73
  9. package/lib/config/configFile.d.ts +2 -2
  10. package/lib/config/configFile.js +38 -29
  11. package/lib/config/configStore.d.ts +9 -9
  12. package/lib/config/configStore.js +17 -15
  13. package/lib/config/envVars.d.ts +15 -9
  14. package/lib/config/envVars.js +71 -47
  15. package/lib/config/orgUsersConfig.js +2 -0
  16. package/lib/config/sandboxOrgConfig.js +2 -0
  17. package/lib/config/sandboxProcessCache.d.ts +16 -0
  18. package/lib/config/sandboxProcessCache.js +38 -0
  19. package/lib/config/tokensConfig.d.ts +10 -0
  20. package/lib/config/tokensConfig.js +29 -0
  21. package/lib/config/ttlConfig.d.ts +34 -0
  22. package/lib/config/ttlConfig.js +50 -0
  23. package/lib/crypto/crypto.js +15 -22
  24. package/lib/crypto/keyChain.js +2 -3
  25. package/lib/crypto/keyChainImpl.d.ts +5 -3
  26. package/lib/crypto/keyChainImpl.js +58 -61
  27. package/lib/crypto/secureBuffer.d.ts +1 -1
  28. package/lib/deviceOauthService.d.ts +3 -3
  29. package/lib/deviceOauthService.js +27 -25
  30. package/lib/exported.d.ts +15 -12
  31. package/lib/exported.js +28 -16
  32. package/lib/global.d.ts +11 -3
  33. package/lib/global.js +39 -12
  34. package/lib/lifecycleEvents.d.ts +1 -1
  35. package/lib/lifecycleEvents.js +3 -0
  36. package/lib/logger.d.ts +19 -9
  37. package/lib/logger.js +112 -86
  38. package/lib/messages.d.ts +53 -36
  39. package/lib/messages.js +81 -91
  40. package/lib/org/authInfo.d.ts +56 -20
  41. package/lib/org/authInfo.js +232 -131
  42. package/lib/org/authRemover.d.ts +8 -7
  43. package/lib/org/authRemover.js +32 -28
  44. package/lib/org/connection.d.ts +13 -37
  45. package/lib/org/connection.js +78 -124
  46. package/lib/org/index.js +5 -1
  47. package/lib/org/org.d.ts +151 -48
  48. package/lib/org/org.js +468 -225
  49. package/lib/org/orgConfigProperties.d.ts +64 -3
  50. package/lib/org/orgConfigProperties.js +96 -4
  51. package/lib/org/permissionSetAssignment.js +4 -13
  52. package/lib/org/scratchOrgCache.d.ts +20 -0
  53. package/lib/org/scratchOrgCache.js +33 -0
  54. package/lib/org/scratchOrgCreate.d.ts +28 -17
  55. package/lib/org/scratchOrgCreate.js +125 -53
  56. package/lib/org/scratchOrgErrorCodes.d.ts +9 -3
  57. package/lib/org/scratchOrgErrorCodes.js +34 -17
  58. package/lib/org/scratchOrgFeatureDeprecation.js +1 -6
  59. package/lib/org/scratchOrgInfoApi.d.ts +21 -47
  60. package/lib/org/scratchOrgInfoApi.js +129 -63
  61. package/lib/org/scratchOrgInfoGenerator.d.ts +6 -5
  62. package/lib/org/scratchOrgInfoGenerator.js +76 -62
  63. package/lib/org/scratchOrgLifecycleEvents.d.ts +10 -0
  64. package/lib/org/scratchOrgLifecycleEvents.js +41 -0
  65. package/lib/org/scratchOrgSettingsGenerator.d.ts +44 -21
  66. package/lib/org/scratchOrgSettingsGenerator.js +165 -98
  67. package/lib/org/scratchOrgTypes.d.ts +43 -0
  68. package/lib/org/scratchOrgTypes.js +9 -0
  69. package/lib/org/user.d.ts +1 -1
  70. package/lib/org/user.js +25 -34
  71. package/lib/schema/printer.d.ts +6 -0
  72. package/lib/schema/printer.js +34 -31
  73. package/lib/schema/validator.d.ts +12 -10
  74. package/lib/schema/validator.js +56 -76
  75. package/lib/{sfdxError.d.ts → sfError.d.ts} +12 -20
  76. package/lib/{sfdxError.js → sfError.js} +40 -30
  77. package/lib/{sfdxProject.d.ts → sfProject.d.ts} +75 -35
  78. package/lib/sfProject.js +651 -0
  79. package/lib/stateAggregator/accessors/aliasAccessor.d.ts +129 -0
  80. package/lib/stateAggregator/accessors/aliasAccessor.js +263 -0
  81. package/lib/stateAggregator/accessors/orgAccessor.d.ts +101 -0
  82. package/lib/stateAggregator/accessors/orgAccessor.js +240 -0
  83. package/lib/stateAggregator/accessors/sandboxAccessor.d.ts +8 -0
  84. package/lib/stateAggregator/accessors/sandboxAccessor.js +28 -0
  85. package/lib/stateAggregator/accessors/tokenAccessor.d.ts +63 -0
  86. package/lib/stateAggregator/accessors/tokenAccessor.js +80 -0
  87. package/lib/stateAggregator/index.d.ts +4 -0
  88. package/lib/stateAggregator/index.js +27 -0
  89. package/lib/stateAggregator/stateAggregator.d.ts +25 -0
  90. package/lib/stateAggregator/stateAggregator.js +46 -0
  91. package/lib/status/myDomainResolver.d.ts +1 -1
  92. package/lib/status/myDomainResolver.js +4 -4
  93. package/lib/status/pollingClient.js +4 -4
  94. package/lib/status/streamingClient.d.ts +2 -2
  95. package/lib/status/streamingClient.js +58 -63
  96. package/lib/status/types.d.ts +2 -2
  97. package/lib/testSetup.d.ts +204 -75
  98. package/lib/testSetup.js +468 -164
  99. package/lib/util/cache.d.ts +2 -2
  100. package/lib/util/cache.js +6 -6
  101. package/lib/util/checkLightningDomain.js +3 -4
  102. package/lib/util/directoryWriter.d.ts +12 -0
  103. package/lib/util/directoryWriter.js +54 -0
  104. package/lib/util/getJwtAudienceUrl.js +1 -1
  105. package/lib/util/internal.d.ts +28 -2
  106. package/lib/util/internal.js +65 -8
  107. package/lib/util/jsonXmlTools.js +2 -4
  108. package/lib/util/mapKeys.d.ts +9 -9
  109. package/lib/util/mapKeys.js +13 -9
  110. package/lib/util/sfdc.d.ts +51 -51
  111. package/lib/util/sfdc.js +74 -79
  112. package/lib/util/sfdcUrl.d.ts +5 -19
  113. package/lib/util/sfdcUrl.js +40 -49
  114. package/lib/util/structuredWriter.d.ts +9 -0
  115. package/lib/util/structuredWriter.js +3 -0
  116. package/lib/util/zipWriter.d.ts +8 -6
  117. package/lib/util/zipWriter.js +13 -13
  118. package/lib/webOAuthServer.d.ts +20 -6
  119. package/lib/webOAuthServer.js +102 -56
  120. package/messageTransformer/messageTransformer.ts +93 -0
  121. package/messages/auth.md +9 -1
  122. package/messages/config.md +42 -6
  123. package/messages/connection.md +8 -0
  124. package/messages/core.md +10 -0
  125. package/messages/envVars.md +37 -3
  126. package/messages/org.md +21 -1
  127. package/messages/scratchOrgCreate.md +2 -6
  128. package/messages/scratchOrgErrorCodes.md +17 -1
  129. package/messages/scratchOrgInfoApi.md +9 -0
  130. package/messages/scratchOrgInfoGenerator.md +9 -1
  131. package/package.json +123 -46
  132. package/CHANGELOG.md +0 -1244
  133. package/lib/config/keychainConfig.d.ts +0 -19
  134. package/lib/config/keychainConfig.js +0 -43
  135. package/lib/globalInfo/accessors/aliasAccessor.d.ts +0 -83
  136. package/lib/globalInfo/accessors/aliasAccessor.js +0 -130
  137. package/lib/globalInfo/accessors/orgAccessor.d.ts +0 -13
  138. package/lib/globalInfo/accessors/orgAccessor.js +0 -45
  139. package/lib/globalInfo/accessors/tokenAccessor.d.ts +0 -13
  140. package/lib/globalInfo/accessors/tokenAccessor.js +0 -35
  141. package/lib/globalInfo/globalInfoConfig.d.ts +0 -36
  142. package/lib/globalInfo/globalInfoConfig.js +0 -105
  143. package/lib/globalInfo/index.d.ts +0 -6
  144. package/lib/globalInfo/index.js +0 -29
  145. package/lib/globalInfo/sfdxDataHandler.d.ts +0 -43
  146. package/lib/globalInfo/sfdxDataHandler.js +0 -217
  147. package/lib/globalInfo/types.d.ts +0 -39
  148. package/lib/globalInfo/types.js +0 -10
  149. package/lib/sfdxProject.js +0 -557
  150. package/lib/util/fs.d.ts +0 -201
  151. package/lib/util/fs.js +0 -378
@@ -0,0 +1,651 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SfProject = exports.SfProjectJson = void 0;
4
+ /*
5
+ * Copyright (c) 2020, salesforce.com, inc.
6
+ * All rights reserved.
7
+ * Licensed under the BSD 3-Clause license.
8
+ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
9
+ */
10
+ const path_1 = require("path");
11
+ const fs = require("fs");
12
+ const kit_1 = require("@salesforce/kit");
13
+ const ts_types_1 = require("@salesforce/ts-types");
14
+ const sfdcUrl_1 = require("./util/sfdcUrl");
15
+ const configAggregator_1 = require("./config/configAggregator");
16
+ const configFile_1 = require("./config/configFile");
17
+ const validator_1 = require("./schema/validator");
18
+ const internal_1 = require("./util/internal");
19
+ const sfError_1 = require("./sfError");
20
+ const sfdc_1 = require("./util/sfdc");
21
+ const messages_1 = require("./messages");
22
+ const messages = new messages_1.Messages('@salesforce/core', 'config', new Map([["unknownConfigKey", "Unknown config name: %s."], ["deprecatedConfigKey", "Deprecated config name: %s. Please use %s instead."], ["invalidWrite", "The writeSync method is not allowed on SfdxConfig. Use the async write method instead."], ["invalidConfigValue", "Invalid config value: %s."], ["invalidInstanceUrl", "Specify a valid Salesforce instance URL."], ["invalidApiVersion", "Specify a valid Salesforce API version, for example, 42.0."], ["invalidCustomOrgMetadataTemplates", "Specify a valid repository URL or directory for the custom org metadata templates."], ["invalidIsvDebuggerSid", "Specify a valid Debugger SID."], ["invalidIsvDebuggerUrl", "Specify a valid Debugger URL."], ["invalidNumberConfigValue", "Specify a valid positive integer, for example, 150000."], ["invalidBooleanConfigValue", "The config value can only be set to true or false."], ["invalidProjectWorkspace", "This directory does not contain a valid Salesforce DX project."], ["schemaValidationError", "The config file \"%s\" is not schema valid.\nDue to: %s"], ["schemaValidationError.actions", ["Fix the invalid entries at %s."]], ["missingDefaultPath", "In sfdx-project.json, be sure to specify which package directory (path) is the default. Example: `[{ \"path\": \"packageDirectory1\", \"default\": true }, { \"path\": \"packageDirectory2\" }]`"], ["missingPackageDirectory", "The path \"%s\", specified in sfdx-project.json, does not exist. Be sure this directory is included in your project root."], ["invalidPackageDirectory", "The path \"%s\", specified in sfdx-project.json, must be indicated as a relative path to the project root."], ["multipleDefaultPaths", "In sfdx-project.json, indicate only one package directory (path) as the default."], ["singleNonDefaultPackage", "The sfdx-project.json file must include one, and only one, default package directory (path). Because your sfdx-project.json file contains only one package directory, it must be the default. Remove the `\"default\": false` key and try again."], ["target-org", "Username or alias of the org that all commands run against by default. (sf only)"], ["target-dev-hub", "Username or alias of your default Dev Hub org. (sf only)"], ["defaultUsername", "Username or alias of the org that all commands run against by default. (sfdx only)"], ["defaultDevHubUsername", "Username or alias of your default Dev Hub org. (sfdx only)"], ["isvDebuggerSid", "ISV debugger SID (sfdx only)"], ["isvDebuggerUrl", "ISV debugger URL (sfdx only)"], ["org-isv-debugger-sid", "ISV debugger SID."], ["org-isv-debugger-url", "ISV debugger URL."], ["apiVersion", "API version of your project. Default: API version of your Dev Hub org. (sfdx only)"], ["org-api-version", "API version of your project. Default: API version of your Dev Hub org."], ["disableTelemetry", "Disables the collection of usage and user environment information, etc. Default: false. (sfdx only)"], ["disable-telemetry", "Disables the collection of usage and user environment information, etc. Default: false."], ["maxQueryLimit", "Maximum number of Salesforce records returned by a CLI command. Default: 10,000. (sfdx only)"], ["org-max-query-limit", "Maximum number of Salesforce records returned by a CLI command. Default: 10,000."], ["restDeploy", "Whether deployments use the Metadata REST API (true) or SOAP API (false, default value). (sfdx only)"], ["instanceUrl", "URL of the Salesforce instance hosting your org. Default: https://login.salesforce.com. (sfdx only)"], ["org-instance-url", "URL of the Salesforce instance hosting your org. Default: https://login.salesforce.com."], ["customOrgMetadataTemplates", "A valid repository URL or directory for the custom org metadata templates."], ["org-custom-metadata-templates", "A valid repository URL or directory for the custom org metadata templates."], ["invalidId", "The given id %s is not a valid 15 or 18 character Salesforce ID."]]));
23
+ const coreMessages = new messages_1.Messages('@salesforce/core', 'core', new Map([["authInfoCreationError", "Must pass a username and/or OAuth options when creating an AuthInfo instance."], ["authInfoOverwriteError", "Cannot create an AuthInfo instance that will overwrite existing auth data."], ["authInfoOverwriteError.actions", ["Create the AuthInfo instance using existing auth data by just passing the username. E.g., `AuthInfo.create({ username: 'my@user.org' });`."]], ["authCodeExchangeError", "Error authenticating with auth code due to: %s"], ["authCodeUsernameRetrievalError", "Could not retrieve the username after successful auth code exchange.\n\nDue to: %s"], ["jwtAuthError", "Error authenticating with JWT config due to: %s"], ["jwtAuthErrors", "Error authenticating with JWT.\nErrors encountered:\n%s"], ["refreshTokenAuthError", "Error authenticating with the refresh token due to: %s"], ["orgDataNotAvailableError", "An attempt to refresh the authentication token failed with a 'Data Not Found Error'. The org identified by username %s does not appear to exist. Likely cause is that the org was deleted by another user or has expired."], ["orgDataNotAvailableError.actions", ["Run `sfdx force:org:list --clean` to remove stale org authentications.", "Use `sfdx force:config:set` to update the defaultusername.", "Use `sfdx force:org:create` to create a new org.", "Use `sfdx auth` to authenticate an existing org."]], ["namedOrgNotFound", "No authorization information found for %s."], ["noAliasesFound", "Nothing to set."], ["invalidFormat", "Setting aliases must be in the format <key>=<value> but found: [%s]."], ["invalidJsonCasing", "All JSON input must have heads down camelcase keys. E.g., `{ sfdcLoginUrl: \"https://login.salesforce.com\" }`\nFound \"%s\" at %s"], ["missingClientId", "Client ID is required for JWT authentication."]]));
24
+ /**
25
+ * The sfdx-project.json config object. This file determines if a folder is a valid sfdx project.
26
+ *
27
+ * *Note:* Any non-standard (not owned by Salesforce) properties stored in sfdx-project.json should
28
+ * be in a top level property that represents your project or plugin.
29
+ *
30
+ * ```
31
+ * const project = await SfProject.resolve();
32
+ * const projectJson = await project.resolveProjectConfig();
33
+ * const myPluginProperties = projectJson.get('myplugin') || {};
34
+ * myPluginProperties.myprop = 'someValue';
35
+ * projectJson.set('myplugin', myPluginProperties);
36
+ * await projectJson.write();
37
+ * ```
38
+ *
39
+ * **See** [force:project:create](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_ws_create_new.htm)
40
+ */
41
+ class SfProjectJson extends configFile_1.ConfigFile {
42
+ static getFileName() {
43
+ return internal_1.SFDX_PROJECT_JSON;
44
+ }
45
+ static getDefaultOptions(isGlobal = false) {
46
+ const options = configFile_1.ConfigFile.getDefaultOptions(isGlobal, SfProjectJson.getFileName());
47
+ options.isState = false;
48
+ return options;
49
+ }
50
+ async read() {
51
+ const contents = await super.read();
52
+ this.validateKeys();
53
+ return contents;
54
+ }
55
+ readSync() {
56
+ const contents = super.readSync();
57
+ this.validateKeys();
58
+ return contents;
59
+ }
60
+ async write(newContents) {
61
+ if (newContents) {
62
+ this.setContents(newContents);
63
+ }
64
+ this.validateKeys();
65
+ return super.write();
66
+ }
67
+ writeSync(newContents) {
68
+ if (newContents) {
69
+ this.setContents(newContents);
70
+ }
71
+ this.validateKeys();
72
+ return super.writeSync();
73
+ }
74
+ getContents() {
75
+ return super.getContents();
76
+ }
77
+ // eslint-disable-next-line class-methods-use-this
78
+ getDefaultOptions(options) {
79
+ const defaultOptions = {
80
+ isState: false,
81
+ };
82
+ Object.assign(defaultOptions, options ?? {});
83
+ return defaultOptions;
84
+ }
85
+ /**
86
+ * Validates sfdx-project.json against the schema.
87
+ *
88
+ * Set the `SFDX_PROJECT_JSON_VALIDATION` environment variable to `true` to throw an error when schema validation fails.
89
+ * A warning is logged by default when the file is invalid.
90
+ *
91
+ * ***See*** [sfdx-project.schema.json] ((https://github.com/forcedotcom/schemas/blob/main/sfdx-project.schema.json)
92
+ */
93
+ async schemaValidate() {
94
+ if (!this.hasRead) {
95
+ // read calls back into this method after necessarily setting this.hasRead=true
96
+ await this.read();
97
+ }
98
+ try {
99
+ const projectJsonSchemaPath = require.resolve('@salesforce/schemas/sfdx-project.schema.json');
100
+ const validator = new validator_1.SchemaValidator(this.logger, projectJsonSchemaPath);
101
+ await validator.validate(this.getContents());
102
+ }
103
+ catch (err) {
104
+ const error = err;
105
+ // Don't throw errors if the global isn't valid, but still warn the user.
106
+ if (kit_1.env.getBoolean('SFDX_PROJECT_JSON_VALIDATION', false) && !this.options.isGlobal) {
107
+ throw messages.createError('schemaValidationError', [this.getPath(), error.message], [this.getPath()], error);
108
+ }
109
+ else {
110
+ this.logger.warn(messages.getMessage('schemaValidationError', [this.getPath(), error.message]));
111
+ }
112
+ }
113
+ }
114
+ /**
115
+ * Returns the `packageDirectories` within sfdx-project.json, first reading
116
+ * and validating the file if necessary.
117
+ */
118
+ // eslint-disable-next-line @typescript-eslint/require-await
119
+ async getPackageDirectories() {
120
+ return this.getPackageDirectoriesSync();
121
+ }
122
+ /**
123
+ * Validates sfdx-project.json against the schema.
124
+ *
125
+ * Set the `SFDX_PROJECT_JSON_VALIDATION` environment variable to `true` to throw an error when schema validation fails.
126
+ * A warning is logged by default when the file is invalid.
127
+ *
128
+ * ***See*** [sfdx-project.schema.json] ((https://github.com/forcedotcom/schemas/blob/main/sfdx-project.schema.json)
129
+ */
130
+ schemaValidateSync() {
131
+ if (!this.hasRead) {
132
+ // read calls back into this method after necessarily setting this.hasRead=true
133
+ this.readSync();
134
+ }
135
+ try {
136
+ const projectJsonSchemaPath = require.resolve('@salesforce/schemas/sfdx-project.schema.json');
137
+ const validator = new validator_1.SchemaValidator(this.logger, projectJsonSchemaPath);
138
+ validator.validateSync(this.getContents());
139
+ }
140
+ catch (err) {
141
+ const error = err;
142
+ // Don't throw errors if the global isn't valid, but still warn the user.
143
+ if (kit_1.env.getBoolean('SFDX_PROJECT_JSON_VALIDATION', false) && !this.options.isGlobal) {
144
+ throw messages.createError('schemaValidationError', [this.getPath(), error.message], [this.getPath()], error);
145
+ }
146
+ else {
147
+ this.logger.warn(messages.getMessage('schemaValidationError', [this.getPath(), error.message]));
148
+ }
149
+ }
150
+ }
151
+ /**
152
+ * Returns a read-only list of `packageDirectories` within sfdx-project.json, first reading
153
+ * and validating the file if necessary. i.e. modifying this array will not affect the
154
+ * sfdx-project.json file.
155
+ */
156
+ getPackageDirectoriesSync() {
157
+ const contents = this.getContents();
158
+ // This has to be done on the fly so it won't be written back to the file
159
+ // This is a fast operation so no need to cache it so it stays immutable.
160
+ const packageDirs = (contents.packageDirectories || []).map((packageDir) => {
161
+ if ((0, path_1.isAbsolute)(packageDir.path)) {
162
+ throw messages.createError('invalidPackageDirectory', [packageDir.path]);
163
+ }
164
+ const regex = path_1.sep === '/' ? /\\/g : /\//g;
165
+ // Change packageDir paths to have path separators that match the OS
166
+ const path = packageDir.path.replace(regex, path_1.sep);
167
+ // Normalize and remove any ending path separators
168
+ const name = (0, path_1.normalize)(path).replace(new RegExp(`\\${path_1.sep}$`), '');
169
+ // Always end in a path sep for standardization on folder paths
170
+ const fullPath = `${(0, path_1.dirname)(this.getPath())}${path_1.sep}${name}${path_1.sep}`;
171
+ if (!this.doesPackageExist(fullPath)) {
172
+ throw messages.createError('missingPackageDirectory', [packageDir.path]);
173
+ }
174
+ return Object.assign({}, packageDir, { name, path, fullPath });
175
+ });
176
+ // If we only have one package entry, it must be the default even if not explicitly labelled
177
+ if (packageDirs.length === 1) {
178
+ if (packageDirs[0].default === false) {
179
+ // we have one package but it is explicitly labelled as default=false
180
+ throw messages.createError('singleNonDefaultPackage');
181
+ }
182
+ // add default=true to the package
183
+ packageDirs[0].default = true;
184
+ }
185
+ const defaultDirs = packageDirs.filter((packageDir) => packageDir.default);
186
+ // Don't throw about a missing default path if we are in the global file.
187
+ // Package directories are not really meant to be set at the global level.
188
+ if (defaultDirs.length === 0 && !this.isGlobal()) {
189
+ throw messages.createError('missingDefaultPath');
190
+ }
191
+ else if (defaultDirs.length > 1) {
192
+ throw messages.createError('multipleDefaultPaths');
193
+ }
194
+ return packageDirs;
195
+ }
196
+ /**
197
+ * Returns a read-only list of `packageDirectories` within sfdx-project.json, first reading
198
+ * and validating the file if necessary. i.e. modifying this array will not affect the
199
+ * sfdx-project.json file.
200
+ *
201
+ * There can be multiple packages in packageDirectories that point to the same directory.
202
+ * This method only returns one packageDirectory entry per unique directory path. This is
203
+ * useful when doing source operations based on directories but probably not as useful
204
+ * for packaging operations that want to do something for each package entry.
205
+ */
206
+ getUniquePackageDirectories() {
207
+ const visited = new Set();
208
+ const uniqueValues = [];
209
+ // Keep original order defined in sfdx-project.json
210
+ this.getPackageDirectoriesSync().forEach((packageDir) => {
211
+ if (!visited.has(packageDir.name)) {
212
+ visited.add(packageDir.name);
213
+ uniqueValues.push(packageDir);
214
+ }
215
+ });
216
+ return uniqueValues;
217
+ }
218
+ /**
219
+ * Get a list of the unique package names from within sfdx-project.json. Use {@link SfProject.getUniquePackageDirectories}
220
+ * for data other than the names.
221
+ */
222
+ getUniquePackageNames() {
223
+ return this.getUniquePackageDirectories().map((pkgDir) => pkgDir.name);
224
+ }
225
+ /**
226
+ * Has package directories defined in the project.
227
+ */
228
+ hasPackages() {
229
+ return this.getContents()?.packageDirectories?.length > 0;
230
+ }
231
+ /**
232
+ * Has multiple package directories (MPD) defined in the project.
233
+ */
234
+ hasMultiplePackages() {
235
+ return this.getContents()?.packageDirectories?.length > 1;
236
+ }
237
+ /**
238
+ * Has at least one package alias defined in the project.
239
+ */
240
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/require-await
241
+ async hasPackageAliases() {
242
+ return Object.keys(this.getContents().packageAliases ?? {}).length > 0;
243
+ }
244
+ /**
245
+ * Get package aliases defined in the project.
246
+ */
247
+ getPackageAliases() {
248
+ return this.getContents().packageAliases;
249
+ }
250
+ /**
251
+ * Add a package alias to the project.
252
+ * If the alias already exists, it will be overwritten.
253
+ *
254
+ * @param alias
255
+ * @param id
256
+ */
257
+ addPackageAlias(alias, id) {
258
+ // TODO: validate id (e.g. 04t, 0Ho)
259
+ if (!/^.{15,18}$/.test(id)) {
260
+ throw messages.createError('invalidId', [id]);
261
+ }
262
+ const contents = this.getContents();
263
+ if (!contents.packageAliases) {
264
+ contents.packageAliases = {};
265
+ }
266
+ contents.packageAliases[alias] = id;
267
+ this.setContents(contents);
268
+ }
269
+ /**
270
+ * Add a package directory to the project.
271
+ * If the package directory already exists, the new directory
272
+ * properties will be merged with the existing properties.
273
+ *
274
+ * @param packageDir
275
+ */
276
+ addPackageDirectory(packageDir) {
277
+ // there is no notion of uniqueness in package directory entries
278
+ // so an attempt of matching an existing entry is a bit convoluted
279
+ // an entry w/o a package or id is considered a directory entry for which a package has yet to be created
280
+ // so first attempt is to find a matching dir entry that where path is the same and id and package are not present
281
+ // if that fails, then find a matching dir entry package is present and is same as the new entry
282
+ const dirIndex = this.getContents().packageDirectories.findIndex((pd) => {
283
+ const withId = pd;
284
+ return ((withId.path === packageDir.path && !withId.id && !withId.package) ||
285
+ (!!packageDir.package && packageDir.package === withId.package));
286
+ });
287
+ // merge new package dir with existing entry, if present
288
+ const packageDirEntry = Object.assign({}, dirIndex > -1 ? this.getContents().packageDirectories[dirIndex] : packageDir, packageDir);
289
+ // update package dir entries
290
+ if (dirIndex > -1) {
291
+ this.getContents().packageDirectories[dirIndex] = packageDirEntry;
292
+ }
293
+ else {
294
+ this.getContents().packageDirectories.push(packageDirEntry);
295
+ }
296
+ }
297
+ // eslint-disable-next-line class-methods-use-this
298
+ doesPackageExist(packagePath) {
299
+ return fs.existsSync(packagePath);
300
+ }
301
+ validateKeys() {
302
+ // Verify that the configObject does not have upper case keys; throw if it does. Must be heads down camel case.
303
+ const upperCaseKey = (0, sfdc_1.findUpperCaseKeys)(this.toObject(), SfProjectJson.BLOCKLIST);
304
+ if (upperCaseKey) {
305
+ throw coreMessages.createError('invalidJsonCasing', [upperCaseKey, this.getPath()]);
306
+ }
307
+ }
308
+ }
309
+ exports.SfProjectJson = SfProjectJson;
310
+ SfProjectJson.BLOCKLIST = ['packageAliases'];
311
+ /**
312
+ * Represents an SFDX project directory. This directory contains a {@link SfProjectJson} config file as well as
313
+ * a hidden .sfdx folder that contains all the other local project config files.
314
+ *
315
+ * ```
316
+ * const project = await SfProject.resolve();
317
+ * const projectJson = await project.resolveProjectConfig();
318
+ * console.log(projectJson.sfdcLoginUrl);
319
+ * ```
320
+ */
321
+ class SfProject {
322
+ /**
323
+ * Do not directly construct instances of this class -- use {@link SfProject.resolve} instead.
324
+ *
325
+ * @ignore
326
+ */
327
+ constructor(path) {
328
+ this.path = path;
329
+ }
330
+ /**
331
+ * Get a Project from a given path or from the working directory.
332
+ *
333
+ * @param path The path of the project.
334
+ *
335
+ * **Throws** *{@link SfError}{ name: 'InvalidProjectWorkspaceError' }* If the current folder is not located in a workspace.
336
+ */
337
+ static async resolve(path) {
338
+ path = await this.resolveProjectPath(path ?? process.cwd());
339
+ if (!SfProject.instances.has(path)) {
340
+ const project = new SfProject(path);
341
+ SfProject.instances.set(path, project);
342
+ }
343
+ return (0, ts_types_1.ensure)(SfProject.instances.get(path));
344
+ }
345
+ /**
346
+ * Get a Project from a given path or from the working directory.
347
+ *
348
+ * @param path The path of the project.
349
+ *
350
+ * **Throws** *{@link SfError}{ name: 'InvalidProjectWorkspaceError' }* If the current folder is not located in a workspace.
351
+ */
352
+ static getInstance(path) {
353
+ // Store instance based on the path of the actual project.
354
+ path = this.resolveProjectPathSync(path ?? process.cwd());
355
+ if (!SfProject.instances.has(path)) {
356
+ const project = new SfProject(path);
357
+ SfProject.instances.set(path, project);
358
+ }
359
+ return (0, ts_types_1.ensure)(SfProject.instances.get(path));
360
+ }
361
+ /**
362
+ * Performs an upward directory search for an sfdx project file. Returns the absolute path to the project.
363
+ *
364
+ * @param dir The directory path to start traversing from.
365
+ *
366
+ * **Throws** *{@link SfError}{ name: 'InvalidProjectWorkspaceError' }* If the current folder is not located in a workspace.
367
+ *
368
+ * **See** {@link traverseForFile}
369
+ *
370
+ * **See** [process.cwd()](https://nodejs.org/api/process.html#process_process_cwd)
371
+ */
372
+ static async resolveProjectPath(dir) {
373
+ return (0, internal_1.resolveProjectPath)(dir);
374
+ }
375
+ /**
376
+ * Performs a synchronous upward directory search for an sfdx project file. Returns the absolute path to the project.
377
+ *
378
+ * @param dir The directory path to start traversing from.
379
+ *
380
+ * **Throws** *{@link SfError}{ name: 'InvalidProjectWorkspaceError' }* If the current folder is not located in a workspace.
381
+ *
382
+ * **See** {@link traverseForFileSync}
383
+ *
384
+ * **See** [process.cwd()](https://nodejs.org/api/process.html#process_process_cwd)
385
+ */
386
+ static resolveProjectPathSync(dir) {
387
+ return (0, internal_1.resolveProjectPathSync)(dir);
388
+ }
389
+ /**
390
+ * Returns the project path.
391
+ */
392
+ getPath() {
393
+ return this.path;
394
+ }
395
+ /**
396
+ * Get the sfdx-project.json config. The global sfdx-project.json is used for user defaults
397
+ * that are not checked in to the project specific file.
398
+ *
399
+ * *Note:* When reading values from {@link SfProjectJson}, it is recommended to use
400
+ * {@link SfProject.resolveProjectConfig} instead.
401
+ *
402
+ * @param isGlobal True to get the global project file, otherwise the local project config.
403
+ */
404
+ async retrieveSfProjectJson(isGlobal = false) {
405
+ const options = SfProjectJson.getDefaultOptions(isGlobal);
406
+ if (isGlobal) {
407
+ if (!this.sfProjectJsonGlobal) {
408
+ this.sfProjectJsonGlobal = await SfProjectJson.create(options);
409
+ }
410
+ return this.sfProjectJsonGlobal;
411
+ }
412
+ else {
413
+ options.rootFolder = this.getPath();
414
+ if (!this.sfProjectJson) {
415
+ this.sfProjectJson = await SfProjectJson.create(options);
416
+ }
417
+ return this.sfProjectJson;
418
+ }
419
+ }
420
+ /**
421
+ * Get the sfdx-project.json config. The global sfdx-project.json is used for user defaults
422
+ * that are not checked in to the project specific file.
423
+ *
424
+ * *Note:* When reading values from {@link SfProjectJson}, it is recommended to use
425
+ * {@link SfProject.resolveProjectConfig} instead.
426
+ *
427
+ * This is the sync method of {@link SfProject.resolveSfProjectJson}
428
+ *
429
+ * @param isGlobal True to get the global project file, otherwise the local project config.
430
+ */
431
+ getSfProjectJson(isGlobal = false) {
432
+ const options = SfProjectJson.getDefaultOptions(isGlobal);
433
+ if (isGlobal) {
434
+ if (!this.sfProjectJsonGlobal) {
435
+ this.sfProjectJsonGlobal = new SfProjectJson(options);
436
+ this.sfProjectJsonGlobal.readSync();
437
+ }
438
+ return this.sfProjectJsonGlobal;
439
+ }
440
+ else {
441
+ options.rootFolder = this.getPath();
442
+ if (!this.sfProjectJson) {
443
+ this.sfProjectJson = new SfProjectJson(options);
444
+ this.sfProjectJson.readSync();
445
+ }
446
+ return this.sfProjectJson;
447
+ }
448
+ }
449
+ /**
450
+ * Returns a read-only list of `packageDirectories` within sfdx-project.json, first reading
451
+ * and validating the file if necessary. i.e. modifying this array will not affect the
452
+ * sfdx-project.json file.
453
+ */
454
+ getPackageDirectories() {
455
+ if (!this.packageDirectories) {
456
+ this.packageDirectories = this.getSfProjectJson().getPackageDirectoriesSync();
457
+ }
458
+ return this.packageDirectories;
459
+ }
460
+ /**
461
+ * Returns a read-only list of `packageDirectories` within sfdx-project.json, first reading
462
+ * and validating the file if necessary. i.e. modifying this array will not affect the
463
+ * sfdx-project.json file.
464
+ *
465
+ * There can be multiple packages in packageDirectories that point to the same directory.
466
+ * This method only returns one packageDirectory entry per unique directory path. This is
467
+ * useful when doing source operations based on directories but probably not as useful
468
+ * for packaging operations that want to do something for each package entry.
469
+ */
470
+ getUniquePackageDirectories() {
471
+ return this.getSfProjectJson().getUniquePackageDirectories();
472
+ }
473
+ /**
474
+ * Get a list of the unique package names from within sfdx-project.json. Use {@link SfProject.getUniquePackageDirectories}
475
+ * for data other than the names.
476
+ */
477
+ getUniquePackageNames() {
478
+ return this.getSfProjectJson().getUniquePackageNames();
479
+ }
480
+ /**
481
+ * Returns the package from a file path.
482
+ *
483
+ * @param path A file path. E.g. /Users/jsmith/projects/ebikes-lwc/force-app/apex/my-cls.cls
484
+ */
485
+ getPackageFromPath(path) {
486
+ const packageDirs = this.getPackageDirectories();
487
+ // resolve the given path
488
+ const fullPath = (0, path_1.resolve)(path);
489
+ const match = packageDirs.find((packageDir) => {
490
+ // fullPath will not have a trailing slash, so remove it from packageDir.fullPath, if it exists
491
+ const fullPathSansTrailingSep = packageDir.fullPath.replace(/(\\|\/)$/, '');
492
+ return ((0, path_1.basename)(path) === packageDir.path ||
493
+ fullPath === fullPathSansTrailingSep ||
494
+ fullPath.includes(packageDir.fullPath));
495
+ });
496
+ return match;
497
+ }
498
+ /**
499
+ * Returns the package name, E.g. 'force-app', from a file path.
500
+ *
501
+ * @param path A file path. E.g. /Users/jsmith/projects/ebikes-lwc/force-app/apex/my-cls.cls
502
+ */
503
+ getPackageNameFromPath(path) {
504
+ const packageDir = this.getPackageFromPath(path);
505
+ return packageDir ? packageDir.package ?? packageDir.path : undefined;
506
+ }
507
+ /**
508
+ * Returns the package directory.
509
+ *
510
+ * @param packageName Name of the package directory. E.g., 'force-app'
511
+ */
512
+ getPackage(packageName) {
513
+ const packageDirs = this.getPackageDirectories();
514
+ return packageDirs.find((packageDir) => packageDir.name === packageName);
515
+ }
516
+ /**
517
+ * Returns the package directory.
518
+ *
519
+ * @param packageName Name of the package directory. E.g., 'force-app'
520
+ */
521
+ findPackage(predicate) {
522
+ return this.getPackageDirectories().find(predicate);
523
+ }
524
+ /**
525
+ * Returns the absolute path of the package directory ending with the path separator.
526
+ * E.g., /Users/jsmith/projects/ebikes-lwc/force-app/
527
+ *
528
+ * @param packageName Name of the package directory. E.g., 'force-app'
529
+ */
530
+ getPackagePath(packageName) {
531
+ const packageDir = this.getPackage(packageName);
532
+ return packageDir?.fullPath;
533
+ }
534
+ /**
535
+ * Has package directories defined in the project.
536
+ */
537
+ hasPackages() {
538
+ return this.getSfProjectJson().hasPackages();
539
+ }
540
+ /**
541
+ * Has multiple package directories (MPD) defined in the project.
542
+ */
543
+ hasMultiplePackages() {
544
+ return this.getSfProjectJson().hasMultiplePackages();
545
+ }
546
+ /**
547
+ * Get the currently activated package on the project. This has no implication on sfdx-project.json
548
+ * but is useful for keeping track of package and source specific options in a process.
549
+ */
550
+ getActivePackage() {
551
+ return this.activePackage;
552
+ }
553
+ /**
554
+ * Set the currently activated package on the project. This has no implication on sfdx-project.json
555
+ * but is useful for keeping track of package and source specific options in a process.
556
+ *
557
+ * @param packageName The package name to activate. E.g. 'force-app'
558
+ */
559
+ setActivePackage(packageName) {
560
+ if (packageName == null) {
561
+ this.activePackage = null;
562
+ }
563
+ else {
564
+ this.activePackage = this.getPackage(packageName);
565
+ }
566
+ }
567
+ /**
568
+ * Get the project's default package directory defined in sfdx-project.json using first 'default: true'
569
+ * found. The first entry is returned if no default is specified.
570
+ */
571
+ getDefaultPackage() {
572
+ if (!this.hasPackages()) {
573
+ throw new sfError_1.SfError('The sfdx-project.json does not have any packageDirectories defined.');
574
+ }
575
+ const defaultPackage = this.findPackage((packageDir) => packageDir.default === true);
576
+ return defaultPackage ?? this.getPackageDirectories()[0];
577
+ }
578
+ /**
579
+ * The project config is resolved from local and global {@link SfProjectJson},
580
+ * {@link ConfigAggregator}, and a set of defaults. It is recommended to use
581
+ * this when reading values from SfProjectJson.
582
+ *
583
+ * The global {@link SfProjectJson} is used to allow the user to provide default values they
584
+ * may not want checked into their project's source.
585
+ *
586
+ * @returns A resolved config object that contains a bunch of different
587
+ * properties, including some 3rd party custom properties.
588
+ */
589
+ async resolveProjectConfig() {
590
+ if (!this.projectConfig) {
591
+ // Do fs operations in parallel
592
+ const [global, local, configAggregator] = await Promise.all([
593
+ this.retrieveSfProjectJson(true),
594
+ this.retrieveSfProjectJson(),
595
+ configAggregator_1.ConfigAggregator.create(),
596
+ ]);
597
+ await Promise.all([global.read(), local.read()]);
598
+ this.projectConfig = (0, kit_1.defaults)(local.toObject(), global.toObject());
599
+ // Add fields in sfdx-config.json
600
+ Object.assign(this.projectConfig, configAggregator.getConfig());
601
+ // we don't have a login url yet, so use instanceUrl from config or default
602
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
603
+ if (!this.projectConfig.sfdcLoginUrl) {
604
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
605
+ this.projectConfig.sfdcLoginUrl = configAggregator.getConfig()['org-instance-url'] ?? sfdcUrl_1.SfdcUrl.PRODUCTION;
606
+ }
607
+ // LEGACY - Allow override of sfdcLoginUrl via env var FORCE_SFDC_LOGIN_URL
608
+ if (process.env.FORCE_SFDC_LOGIN_URL) {
609
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
610
+ this.projectConfig.sfdcLoginUrl = process.env.FORCE_SFDC_LOGIN_URL;
611
+ }
612
+ // Allow override of signupTargetLoginUrl via env var SFDX_SCRATCH_ORG_CREATION_LOGIN_URL
613
+ if (process.env.SFDX_SCRATCH_ORG_CREATION_LOGIN_URL) {
614
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
615
+ this.projectConfig.signupTargetLoginUrl = process.env.SFDX_SCRATCH_ORG_CREATION_LOGIN_URL;
616
+ }
617
+ }
618
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
619
+ return this.projectConfig;
620
+ }
621
+ async hasPackageAliases() {
622
+ return this.getSfProjectJson().hasPackageAliases();
623
+ }
624
+ /**
625
+ * Returns a read-only list of `packageDirectories` within sfdx-project.json, first reading
626
+ * and validating the file if necessary. i.e. modifying this array will not affect the
627
+ * sfdx-project.json file.
628
+ */
629
+ getPackageAliases() {
630
+ if (!this.packageAliases) {
631
+ this.packageAliases = this.getSfProjectJson().getPackageAliases();
632
+ }
633
+ return this.packageAliases;
634
+ }
635
+ getPackageIdFromAlias(alias) {
636
+ const packageAliases = this.getPackageAliases();
637
+ return packageAliases ? packageAliases[alias] : undefined;
638
+ }
639
+ getAliasesFromPackageId(id) {
640
+ if (!/^.{15,18}$/.test(id)) {
641
+ throw messages.createError('invalidId', [id]);
642
+ }
643
+ return Object.entries(this.getPackageAliases() ?? {})
644
+ .filter(([, value]) => value?.startsWith(id))
645
+ .map(([key]) => key);
646
+ }
647
+ }
648
+ exports.SfProject = SfProject;
649
+ // Cache of SfProject instances per path.
650
+ SfProject.instances = new Map();
651
+ //# sourceMappingURL=sfProject.js.map