@salesforce/core 4.0.0 → 4.0.1

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 (153) hide show
  1. package/LICENSE.txt +1 -1
  2. package/README.md +93 -44
  3. package/lib/config/aliasesConfig.d.ts +12 -0
  4. package/lib/config/aliasesConfig.js +28 -0
  5. package/lib/config/authInfoConfig.d.ts +19 -0
  6. package/lib/config/authInfoConfig.js +35 -0
  7. package/lib/config/config.d.ts +87 -22
  8. package/lib/config/config.js +117 -65
  9. package/lib/config/configAggregator.d.ts +41 -35
  10. package/lib/config/configAggregator.js +102 -73
  11. package/lib/config/configFile.d.ts +2 -2
  12. package/lib/config/configFile.js +38 -29
  13. package/lib/config/configGroup.d.ts +141 -0
  14. package/lib/config/configGroup.js +225 -0
  15. package/lib/config/configStore.d.ts +9 -9
  16. package/lib/config/configStore.js +17 -15
  17. package/lib/config/envVars.d.ts +15 -9
  18. package/lib/config/envVars.js +71 -47
  19. package/lib/config/orgUsersConfig.js +2 -0
  20. package/lib/config/sandboxOrgConfig.js +2 -0
  21. package/lib/config/sandboxProcessCache.d.ts +16 -0
  22. package/lib/config/sandboxProcessCache.js +38 -0
  23. package/lib/config/tokensConfig.d.ts +10 -0
  24. package/lib/config/tokensConfig.js +29 -0
  25. package/lib/config/ttlConfig.d.ts +34 -0
  26. package/lib/config/ttlConfig.js +50 -0
  27. package/lib/crypto/crypto.js +15 -22
  28. package/lib/crypto/keyChain.js +2 -3
  29. package/lib/crypto/keyChainImpl.d.ts +5 -3
  30. package/lib/crypto/keyChainImpl.js +58 -61
  31. package/lib/crypto/secureBuffer.d.ts +1 -1
  32. package/lib/deviceOauthService.d.ts +3 -3
  33. package/lib/deviceOauthService.js +27 -25
  34. package/lib/exported.d.ts +15 -12
  35. package/lib/exported.js +28 -16
  36. package/lib/global.d.ts +11 -3
  37. package/lib/global.js +39 -12
  38. package/lib/lifecycleEvents.d.ts +1 -1
  39. package/lib/lifecycleEvents.js +3 -0
  40. package/lib/logger.d.ts +19 -9
  41. package/lib/logger.js +112 -86
  42. package/lib/messages.d.ts +53 -36
  43. package/lib/messages.js +81 -91
  44. package/lib/org/authInfo.d.ts +56 -20
  45. package/lib/org/authInfo.js +232 -131
  46. package/lib/org/authRemover.d.ts +8 -7
  47. package/lib/org/authRemover.js +32 -28
  48. package/lib/org/connection.d.ts +13 -37
  49. package/lib/org/connection.js +78 -124
  50. package/lib/org/index.js +5 -1
  51. package/lib/org/org.d.ts +151 -48
  52. package/lib/org/org.js +466 -220
  53. package/lib/org/orgConfigProperties.d.ts +64 -3
  54. package/lib/org/orgConfigProperties.js +96 -4
  55. package/lib/org/permissionSetAssignment.js +4 -13
  56. package/lib/org/scratchOrgCache.d.ts +20 -0
  57. package/lib/org/scratchOrgCache.js +33 -0
  58. package/lib/org/scratchOrgCreate.d.ts +28 -17
  59. package/lib/org/scratchOrgCreate.js +125 -53
  60. package/lib/org/scratchOrgErrorCodes.d.ts +9 -3
  61. package/lib/org/scratchOrgErrorCodes.js +34 -17
  62. package/lib/org/scratchOrgFeatureDeprecation.js +1 -6
  63. package/lib/org/scratchOrgInfoApi.d.ts +21 -47
  64. package/lib/org/scratchOrgInfoApi.js +129 -63
  65. package/lib/org/scratchOrgInfoGenerator.d.ts +6 -5
  66. package/lib/org/scratchOrgInfoGenerator.js +76 -62
  67. package/lib/org/scratchOrgLifecycleEvents.d.ts +10 -0
  68. package/lib/org/scratchOrgLifecycleEvents.js +41 -0
  69. package/lib/org/scratchOrgSettingsGenerator.d.ts +44 -21
  70. package/lib/org/scratchOrgSettingsGenerator.js +165 -98
  71. package/lib/org/scratchOrgTypes.d.ts +43 -0
  72. package/lib/org/scratchOrgTypes.js +9 -0
  73. package/lib/org/user.d.ts +1 -1
  74. package/lib/org/user.js +25 -34
  75. package/lib/schema/printer.d.ts +6 -0
  76. package/lib/schema/printer.js +34 -31
  77. package/lib/schema/validator.d.ts +12 -10
  78. package/lib/schema/validator.js +56 -76
  79. package/lib/{sfdxError.d.ts → sfError.d.ts} +12 -20
  80. package/lib/{sfdxError.js → sfError.js} +40 -30
  81. package/lib/{sfdxProject.d.ts → sfProject.d.ts} +75 -35
  82. package/lib/sfProject.js +651 -0
  83. package/lib/{globalInfo → stateAggregator}/accessors/aliasAccessor.d.ts +27 -12
  84. package/lib/{globalInfo → stateAggregator}/accessors/aliasAccessor.js +47 -31
  85. package/lib/stateAggregator/accessors/orgAccessor.d.ts +101 -0
  86. package/lib/stateAggregator/accessors/orgAccessor.js +240 -0
  87. package/lib/stateAggregator/accessors/sandboxAccessor.d.ts +8 -0
  88. package/lib/stateAggregator/accessors/sandboxAccessor.js +28 -0
  89. package/lib/stateAggregator/accessors/tokenAccessor.d.ts +63 -0
  90. package/lib/stateAggregator/accessors/tokenAccessor.js +80 -0
  91. package/lib/stateAggregator/index.d.ts +4 -0
  92. package/lib/stateAggregator/index.js +27 -0
  93. package/lib/stateAggregator/stateAggregator.d.ts +25 -0
  94. package/lib/stateAggregator/stateAggregator.js +46 -0
  95. package/lib/status/myDomainResolver.d.ts +1 -1
  96. package/lib/status/myDomainResolver.js +4 -4
  97. package/lib/status/pollingClient.js +4 -4
  98. package/lib/status/streamingClient.d.ts +2 -2
  99. package/lib/status/streamingClient.js +58 -63
  100. package/lib/status/types.d.ts +2 -2
  101. package/lib/testSetup.d.ts +206 -75
  102. package/lib/testSetup.js +463 -165
  103. package/lib/util/cache.d.ts +2 -2
  104. package/lib/util/cache.js +6 -6
  105. package/lib/util/checkLightningDomain.js +3 -4
  106. package/lib/util/directoryWriter.d.ts +12 -0
  107. package/lib/util/directoryWriter.js +54 -0
  108. package/lib/util/getJwtAudienceUrl.js +1 -1
  109. package/lib/util/internal.d.ts +28 -2
  110. package/lib/util/internal.js +65 -8
  111. package/lib/util/jsonXmlTools.js +2 -4
  112. package/lib/util/mapKeys.d.ts +9 -9
  113. package/lib/util/mapKeys.js +13 -9
  114. package/lib/util/sfdc.d.ts +51 -51
  115. package/lib/util/sfdc.js +74 -79
  116. package/lib/util/sfdcUrl.d.ts +5 -19
  117. package/lib/util/sfdcUrl.js +40 -49
  118. package/lib/util/structuredWriter.d.ts +9 -0
  119. package/lib/util/structuredWriter.js +3 -0
  120. package/lib/util/zipWriter.d.ts +8 -6
  121. package/lib/util/zipWriter.js +13 -13
  122. package/lib/webOAuthServer.d.ts +20 -6
  123. package/lib/webOAuthServer.js +102 -56
  124. package/messageTransformer/messageTransformer.ts +93 -0
  125. package/messages/auth.md +9 -1
  126. package/messages/config.md +42 -6
  127. package/messages/connection.md +8 -0
  128. package/messages/core.md +10 -0
  129. package/messages/envVars.md +37 -3
  130. package/messages/org.md +21 -1
  131. package/messages/scratchOrgCreate.md +2 -6
  132. package/messages/scratchOrgErrorCodes.md +17 -1
  133. package/messages/scratchOrgInfoApi.md +9 -0
  134. package/messages/scratchOrgInfoGenerator.md +9 -1
  135. package/package.json +121 -46
  136. package/CHANGELOG.md +0 -1244
  137. package/lib/config/keychainConfig.d.ts +0 -19
  138. package/lib/config/keychainConfig.js +0 -43
  139. package/lib/globalInfo/accessors/orgAccessor.d.ts +0 -13
  140. package/lib/globalInfo/accessors/orgAccessor.js +0 -45
  141. package/lib/globalInfo/accessors/tokenAccessor.d.ts +0 -13
  142. package/lib/globalInfo/accessors/tokenAccessor.js +0 -35
  143. package/lib/globalInfo/globalInfoConfig.d.ts +0 -36
  144. package/lib/globalInfo/globalInfoConfig.js +0 -105
  145. package/lib/globalInfo/index.d.ts +0 -6
  146. package/lib/globalInfo/index.js +0 -29
  147. package/lib/globalInfo/sfdxDataHandler.d.ts +0 -43
  148. package/lib/globalInfo/sfdxDataHandler.js +0 -217
  149. package/lib/globalInfo/types.d.ts +0 -39
  150. package/lib/globalInfo/types.js +0 -10
  151. package/lib/sfdxProject.js +0 -557
  152. package/lib/util/fs.d.ts +0 -201
  153. package/lib/util/fs.js +0 -378
package/lib/org/org.js CHANGED
@@ -5,42 +5,30 @@
5
5
  * Licensed under the BSD 3-Clause license.
6
6
  * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
7
7
  */
8
+ /* eslint-disable class-methods-use-this */
8
9
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.Org = exports.SandboxEvents = exports.OrgTypes = void 0;
10
+ exports.Org = exports.sandboxIsResumable = exports.SandboxEvents = exports.OrgTypes = void 0;
10
11
  const path_1 = require("path");
12
+ const fs = require("fs");
11
13
  const kit_1 = require("@salesforce/kit");
12
14
  const ts_types_1 = require("@salesforce/ts-types");
13
15
  const config_1 = require("../config/config");
14
16
  const configAggregator_1 = require("../config/configAggregator");
15
17
  const orgUsersConfig_1 = require("../config/orgUsersConfig");
16
- const sandboxOrgConfig_1 = require("../config/sandboxOrgConfig");
17
18
  const global_1 = require("../global");
18
19
  const lifecycleEvents_1 = require("../lifecycleEvents");
19
20
  const logger_1 = require("../logger");
20
- const sfdxError_1 = require("../sfdxError");
21
- const fs_1 = require("../util/fs");
21
+ const sfError_1 = require("../sfError");
22
22
  const sfdc_1 = require("../util/sfdc");
23
23
  const webOAuthServer_1 = require("../webOAuthServer");
24
24
  const messages_1 = require("../messages");
25
- const globalInfo_1 = require("../globalInfo");
25
+ const stateAggregator_1 = require("../stateAggregator");
26
+ const pollingClient_1 = require("../status/pollingClient");
26
27
  const connection_1 = require("./connection");
27
28
  const authInfo_1 = require("./authInfo");
28
29
  const scratchOrgCreate_1 = require("./scratchOrgCreate");
29
30
  const orgConfigProperties_1 = require("./orgConfigProperties");
30
- messages_1.Messages.importMessagesDirectory(__dirname);
31
- const messages = messages_1.Messages.load('@salesforce/core', 'org', [
32
- 'deleteOrgHubError',
33
- 'insufficientAccessToDelete',
34
- 'missingAuthUsername',
35
- 'noDevHubFound',
36
- 'notADevHub',
37
- 'noUsernameFound',
38
- 'orgPollingTimeout',
39
- 'sandboxDeleteFailed',
40
- 'sandboxInfoCreateFailed',
41
- 'sandboxNotFound',
42
- 'scratchOrgNotFound',
43
- ]);
31
+ const messages = new messages_1.Messages('@salesforce/core', 'org', new Map([["notADevHub", "The provided dev hub username %s is not a valid dev hub."], ["noUsernameFound", "No username found."], ["noDevHubFound", "Unable to associate this scratch org with a DevHub."], ["deleteOrgHubError", "The Dev Hub org cannot be deleted."], ["insufficientAccessToDelete", "You do not have the appropriate permissions to delete a scratch org. Please contact your Salesforce admin."], ["scratchOrgNotFound", "Attempting to delete an expired or deleted org"], ["sandboxDeleteFailed", "The sandbox org deletion failed with a result of %s."], ["sandboxNotFound", "We can't find a SandboxProcess for the sandbox %s."], ["sandboxInfoCreateFailed", "The sandbox org creation failed with a result of %s."], ["missingAuthUsername", "The sandbox %s does not have an authorized username."], ["orgPollingTimeout", "Sandbox status is %s; timed out waiting for completion."], ["NotFoundOnDevHub", "The scratch org does not belong to the dev hub username %s."], ["AuthInfoOrgIdUndefined", "AuthInfo orgId is undefined."], ["sandboxCreateNotComplete", "The sandbox creation has not completed."], ["SandboxProcessNotFoundBySandboxName", "We can't find a SandboxProcess with the SandboxName %s."], ["MultiSandboxProcessNotFoundBySandboxName", "We found more than one SandboxProcess with the SandboxName %s."], ["sandboxNotResumable", "The sandbox %s cannot resume with status of %s."]]));
44
32
  var OrgTypes;
45
33
  (function (OrgTypes) {
46
34
  OrgTypes["Scratch"] = "scratch";
@@ -52,7 +40,13 @@ var SandboxEvents;
52
40
  SandboxEvents["EVENT_ASYNC_RESULT"] = "asyncResult";
53
41
  SandboxEvents["EVENT_RESULT"] = "result";
54
42
  SandboxEvents["EVENT_AUTH"] = "auth";
43
+ SandboxEvents["EVENT_RESUME"] = "resume";
55
44
  })(SandboxEvents = exports.SandboxEvents || (exports.SandboxEvents = {}));
45
+ const resumableSandboxStatus = ['Activating', 'Pending', 'Pending Activation', 'Processing', 'Sampling', 'Completed'];
46
+ function sandboxIsResumable(value) {
47
+ return resumableSandboxStatus.includes(value);
48
+ }
49
+ exports.sandboxIsResumable = sandboxIsResumable;
56
50
  /**
57
51
  * Provides a way to manage a locally authenticated Org.
58
52
  *
@@ -86,7 +80,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
86
80
  constructor(options) {
87
81
  super(options);
88
82
  this.status = Org.Status.UNKNOWN;
89
- this.options = options || {};
83
+ this.options = options ?? {};
90
84
  }
91
85
  /**
92
86
  * create a sandbox from a production org
@@ -95,23 +89,105 @@ class Org extends kit_1.AsyncOptionalCreatable {
95
89
  * @param sandboxReq SandboxRequest options to create the sandbox with
96
90
  * @param options Wait: The amount of time to wait before timing out, Interval: The time interval between polling
97
91
  */
98
- async createSandbox(sandboxReq, options) {
99
- var _a;
100
- this.logger.debug('CreateSandbox called with SandboxRequest: %s ', sandboxReq);
92
+ async createSandbox(sandboxReq, options = {
93
+ wait: kit_1.Duration.minutes(6),
94
+ async: false,
95
+ interval: kit_1.Duration.seconds(30),
96
+ }) {
97
+ this.logger.debug(`CreateSandbox called with SandboxRequest: ${JSON.stringify(sandboxReq, undefined, 2)}`);
101
98
  const createResult = await this.connection.tooling.create('SandboxInfo', sandboxReq);
102
- this.logger.debug('Return from calling tooling.create: %s ', createResult);
99
+ this.logger.debug(`Return from calling tooling.create: ${JSON.stringify(createResult, undefined, 2)}`);
103
100
  if (Array.isArray(createResult) || !createResult.success) {
104
101
  throw messages.createError('sandboxInfoCreateFailed', [JSON.stringify(createResult)]);
105
102
  }
106
- const sandboxCreationProgress = await this.querySandboxProcess(createResult.id);
107
- this.logger.debug('Return from calling singleRecordQuery with tooling: %s', sandboxCreationProgress);
108
- const retries = options.wait ? options.wait.seconds / kit_1.Duration.seconds(30).seconds : 0;
109
- this.logger.debug('pollStatusAndAuth sandboxProcessObj %s, maxPollingRetries %i', sandboxCreationProgress, retries);
110
- const pollInterval = (_a = options.interval) !== null && _a !== void 0 ? _a : kit_1.Duration.seconds(30);
103
+ const sandboxCreationProgress = await this.querySandboxProcessBySandboxInfoId(createResult.id);
104
+ this.logger.debug(`Return from calling singleRecordQuery with tooling: ${JSON.stringify(sandboxCreationProgress, undefined, 2)}`);
105
+ const isAsync = !!options.async;
106
+ if (isAsync) {
107
+ // The user didn't want us to poll, so simply return the status
108
+ await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_ASYNC_RESULT, sandboxCreationProgress);
109
+ return sandboxCreationProgress;
110
+ }
111
+ const [wait, pollInterval] = this.validateWaitOptions(options);
112
+ this.logger.debug(`create - pollStatusAndAuth sandboxProcessObj ${JSON.stringify(sandboxCreationProgress, undefined, 2)}, max wait time of ${wait.minutes} minutes`);
111
113
  return this.pollStatusAndAuth({
112
114
  sandboxProcessObj: sandboxCreationProgress,
113
- retries,
114
- shouldPoll: retries > 0,
115
+ wait,
116
+ pollInterval,
117
+ });
118
+ }
119
+ /**
120
+ *
121
+ * @param sandboxReq SandboxRequest options to create the sandbox with
122
+ * @param sourceSandboxName the name of the sandbox that your new sandbox will be based on
123
+ * @param options Wait: The amount of time to wait before timing out, defaults to 0, Interval: The time interval between polling defaults to 30 seconds
124
+ * @returns {SandboxProcessObject} the newly created sandbox process object
125
+ */
126
+ async cloneSandbox(sandboxReq, sourceSandboxName, options) {
127
+ sandboxReq.SourceId = (await this.querySandboxProcessBySandboxName(sourceSandboxName)).SandboxInfoId;
128
+ this.logger.debug('Clone sandbox sourceId %s', sandboxReq.SourceId);
129
+ return this.createSandbox(sandboxReq, options);
130
+ }
131
+ /**
132
+ * resume a sandbox creation from a production org
133
+ * 'this' needs to be a production org with sandbox licenses available
134
+ *
135
+ * @param resumeSandboxRequest SandboxRequest options to create the sandbox with
136
+ * @param options Wait: The amount of time to wait (default: 30 minutes) before timing out,
137
+ * Interval: The time interval (default: 30 seconds) between polling
138
+ */
139
+ async resumeSandbox(resumeSandboxRequest, options = {
140
+ wait: kit_1.Duration.minutes(0),
141
+ async: false,
142
+ interval: kit_1.Duration.seconds(30),
143
+ }) {
144
+ this.logger.debug(`ResumeSandbox called with ResumeSandboxRequest: ${JSON.stringify(resumeSandboxRequest, undefined, 2)}`);
145
+ let sandboxCreationProgress;
146
+ // seed the sandboxCreationProgress via the resumeSandboxRequest options
147
+ if (resumeSandboxRequest.SandboxName) {
148
+ sandboxCreationProgress = await this.querySandboxProcessBySandboxName(resumeSandboxRequest.SandboxName);
149
+ }
150
+ else if (resumeSandboxRequest.SandboxProcessObjId) {
151
+ sandboxCreationProgress = await this.querySandboxProcessById(resumeSandboxRequest.SandboxProcessObjId);
152
+ }
153
+ else {
154
+ throw messages.createError('sandboxNotFound', [
155
+ resumeSandboxRequest.SandboxName ?? resumeSandboxRequest.SandboxProcessObjId,
156
+ ]);
157
+ }
158
+ this.logger.debug(`Return from calling singleRecordQuery with tooling: ${JSON.stringify(sandboxCreationProgress, undefined, 2)}`);
159
+ if (!sandboxIsResumable(sandboxCreationProgress.Status)) {
160
+ throw messages.createError('sandboxNotResumable', [
161
+ sandboxCreationProgress.SandboxName,
162
+ sandboxCreationProgress.Status,
163
+ ]);
164
+ }
165
+ await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_RESUME, sandboxCreationProgress);
166
+ const [wait, pollInterval] = this.validateWaitOptions(options);
167
+ // if wait is 0, return the sandboxCreationProgress immediately
168
+ if (wait.seconds === 0) {
169
+ if (sandboxCreationProgress.Status === 'Completed') {
170
+ // check to see if sandbox can authenticate via sandboxAuth endpoint
171
+ const sandboxInfo = await this.sandboxSignupComplete(sandboxCreationProgress);
172
+ if (sandboxInfo) {
173
+ await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_AUTH, sandboxInfo);
174
+ try {
175
+ this.logger.debug(`sandbox signup complete with ${JSON.stringify(sandboxInfo, undefined, 2)}`);
176
+ await this.writeSandboxAuthFile(sandboxCreationProgress, sandboxInfo);
177
+ return sandboxCreationProgress;
178
+ }
179
+ catch (err) {
180
+ // eat the error, we don't want to throw an error if we can't write the file
181
+ }
182
+ }
183
+ }
184
+ await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_ASYNC_RESULT, sandboxCreationProgress);
185
+ throw messages.createError('sandboxCreateNotComplete');
186
+ }
187
+ this.logger.debug(`resume - pollStatusAndAuth sandboxProcessObj ${JSON.stringify(sandboxCreationProgress, undefined, 2)}, max wait time of ${wait.minutes} minutes`);
188
+ return this.pollStatusAndAuth({
189
+ sandboxProcessObj: sandboxCreationProgress,
190
+ wait,
115
191
  pollInterval,
116
192
  });
117
193
  }
@@ -125,6 +201,16 @@ class Org extends kit_1.AsyncOptionalCreatable {
125
201
  async scratchOrgCreate(options) {
126
202
  return (0, scratchOrgCreate_1.scratchOrgCreate)({ ...options, hubOrg: this });
127
203
  }
204
+ /**
205
+ * Reports sandbox org creation status. If the org is ready, authenticates to the org.
206
+ *
207
+ * @param {sandboxname} string the sandbox name
208
+ * @param options Wait: The amount of time to wait before timing out, Interval: The time interval between polling
209
+ * @returns {SandboxProcessObject} the sandbox process object
210
+ */
211
+ async sandboxStatus(sandboxname, options) {
212
+ return this.authWithRetriesByName(sandboxname, options);
213
+ }
128
214
  /**
129
215
  * Clean all data files in the org's data path. Usually <workspace>/.sfdx/orgs/<username>.
130
216
  *
@@ -134,8 +220,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
134
220
  async cleanLocalOrgData(orgDataPath, throwWhenRemoveFails = false) {
135
221
  let dataPath;
136
222
  try {
137
- const rootFolder = await config_1.Config.resolveRootFolder(false);
138
- dataPath = (0, path_1.join)(rootFolder, global_1.Global.SFDX_STATE_FOLDER, orgDataPath ? orgDataPath : 'orgs');
223
+ dataPath = await this.getLocalDataDir(orgDataPath);
139
224
  this.logger.debug(`cleaning data for path: ${dataPath}`);
140
225
  }
141
226
  catch (err) {
@@ -147,18 +232,16 @@ class Org extends kit_1.AsyncOptionalCreatable {
147
232
  }
148
233
  throw err;
149
234
  }
150
- return this.manageDelete(async () => await fs_1.fs.remove(dataPath), dataPath, throwWhenRemoveFails);
235
+ return this.manageDelete(async () => fs.promises.rmdir(dataPath), dataPath, throwWhenRemoveFails);
151
236
  }
152
237
  /**
153
238
  * @ignore
154
239
  */
155
240
  async retrieveOrgUsersConfig() {
156
- return await orgUsersConfig_1.OrgUsersConfig.create(orgUsersConfig_1.OrgUsersConfig.getOptions(this.getOrgId()));
241
+ return orgUsersConfig_1.OrgUsersConfig.create(orgUsersConfig_1.OrgUsersConfig.getOptions(this.getOrgId()));
157
242
  }
158
243
  /**
159
- * Removes the scratch org config file at $HOME/.sfdx/[name].json, any project level org
160
- * files, all user auth files for the org, matching default config settings, and any
161
- * matching aliases.
244
+ * Cleans up all org related artifacts including users, sandbox config (if a sandbox), source tracking files, and auth file.
162
245
  *
163
246
  * @param throwWhenRemoveFails Determines if the call should throw an error or fail silently.
164
247
  */
@@ -168,7 +251,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
168
251
  if (this.getConnection().isUsingAccessToken()) {
169
252
  return Promise.resolve();
170
253
  }
171
- await this.removeSandboxConfig(throwWhenRemoveFails);
254
+ await this.removeSandboxConfig();
172
255
  await this.removeUsers(throwWhenRemoveFails);
173
256
  await this.removeUsersConfig();
174
257
  // An attempt to remove this org's auth file occurs in this.removeUsersConfig. That's because this org's usersname is also
@@ -176,20 +259,21 @@ class Org extends kit_1.AsyncOptionalCreatable {
176
259
  //
177
260
  // So, just in case no users are added to this org we will try the remove again.
178
261
  await this.removeAuth();
262
+ await this.removeSourceTrackingFiles();
179
263
  }
180
264
  /**
181
265
  * Check if org is a sandbox org by checking its SandboxOrgConfig.
182
266
  *
183
267
  */
184
268
  async isSandbox() {
185
- return !!(await this.getSandboxOrgConfigField(sandboxOrgConfig_1.SandboxOrgConfig.Fields.PROD_ORG_USERNAME));
269
+ return (await stateAggregator_1.StateAggregator.getInstance()).sandboxes.hasFile(this.getOrgId());
186
270
  }
187
271
  /**
188
272
  * Check that this org is a scratch org by asking the dev hub if it knows about it.
189
273
  *
190
- * **Throws** *{@link SfdxError}{ name: 'NotADevHubError' }* Not a Dev Hub.
274
+ * **Throws** *{@link SfError}{ name: 'NotADevHubError' }* Not a Dev Hub.
191
275
  *
192
- * **Throws** *{@link SfdxError}{ name: 'NoResultsError' }* No results.
276
+ * **Throws** *{@link SfError}{ name: 'NoResultsError' }* No results.
193
277
  *
194
278
  * @param devHubUsernameOrAlias The username or alias of the dev hub org.
195
279
  */
@@ -200,12 +284,12 @@ class Org extends kit_1.AsyncOptionalCreatable {
200
284
  }
201
285
  const devHubConnection = (await Org.create({ aliasOrUsername })).getConnection();
202
286
  const thisOrgAuthConfig = this.getConnection().getAuthInfoFields();
203
- const trimmedId = sfdc_1.sfdc.trimTo15(thisOrgAuthConfig.orgId);
287
+ const trimmedId = (0, sfdc_1.trimTo15)(thisOrgAuthConfig.orgId);
204
288
  const DEV_HUB_SOQL = `SELECT CreatedDate,Edition,ExpirationDate FROM ActiveScratchOrg WHERE ScratchOrg='${trimmedId}'`;
205
289
  try {
206
290
  const results = await devHubConnection.query(DEV_HUB_SOQL);
207
- if ((0, ts_types_1.getNumber)(results, 'records.length') !== 1) {
208
- throw new sfdxError_1.SfdxError('No results', 'NoResultsError');
291
+ if (results.records.length !== 1) {
292
+ throw new sfError_1.SfError('No results', 'NoResultsError');
209
293
  }
210
294
  }
211
295
  catch (err) {
@@ -329,6 +413,45 @@ class Org extends kit_1.AsyncOptionalCreatable {
329
413
  return false;
330
414
  }
331
415
  }
416
+ /**
417
+ * Returns `true` if the org uses source tracking.
418
+ * Side effect: updates files where the property doesn't currently exist
419
+ */
420
+ async tracksSource() {
421
+ // use the property if it exists
422
+ const tracksSource = this.getField(Org.Fields.TRACKS_SOURCE);
423
+ if ((0, ts_types_1.isBoolean)(tracksSource)) {
424
+ return tracksSource;
425
+ }
426
+ // scratch orgs with no property use tracking by default
427
+ if (await this.determineIfScratch()) {
428
+ // save true for next time to avoid checking again
429
+ await this.setTracksSource(true);
430
+ return true;
431
+ }
432
+ if (await this.determineIfSandbox()) {
433
+ // does the sandbox know about the SourceMember object?
434
+ const supportsSourceMembers = await this.supportsSourceTracking();
435
+ await this.setTracksSource(supportsSourceMembers);
436
+ return supportsSourceMembers;
437
+ }
438
+ // any other non-sandbox, non-scratch orgs won't use tracking
439
+ await this.setTracksSource(false);
440
+ return false;
441
+ }
442
+ /**
443
+ * Set the tracking property on the org's auth file
444
+ *
445
+ * @param value true or false (whether the org should use source tracking or not)
446
+ */
447
+ async setTracksSource(value) {
448
+ const originalAuth = await authInfo_1.AuthInfo.create({ username: this.getUsername() });
449
+ return originalAuth.handleAliasAndDefaultSettings({
450
+ setDefault: false,
451
+ setDefaultDevHub: false,
452
+ setTracksSource: value,
453
+ });
454
+ }
332
455
  /**
333
456
  * Returns `true` if the org is a scratch org.
334
457
  *
@@ -376,10 +499,10 @@ class Org extends kit_1.AsyncOptionalCreatable {
376
499
  const username = this.getUsername();
377
500
  if (username) {
378
501
  const organization = await this.retrieveOrganizationInformation();
379
- const isScratch = organization.IsSandbox && organization.TrialExpirationDate;
502
+ const isScratch = organization.IsSandbox && Boolean(organization.TrialExpirationDate);
380
503
  const isSandbox = organization.IsSandbox && !organization.TrialExpirationDate;
381
- const info = await globalInfo_1.GlobalInfo.getInstance();
382
- info.orgs.update(username, {
504
+ const stateAggregator = await stateAggregator_1.StateAggregator.getInstance();
505
+ stateAggregator.orgs.update(username, {
383
506
  [Org.Fields.NAME]: organization.Name,
384
507
  [Org.Fields.INSTANCE_NAME]: organization.InstanceName,
385
508
  [Org.Fields.NAMESPACE_PREFIX]: organization.NamespacePrefix,
@@ -387,7 +510,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
387
510
  [Org.Fields.IS_SCRATCH]: isScratch,
388
511
  [Org.Fields.TRIAL_EXPIRATION_DATE]: organization.TrialExpirationDate,
389
512
  });
390
- await info.write();
513
+ await stateAggregator.orgs.write(username);
391
514
  }
392
515
  }
393
516
  /**
@@ -409,7 +532,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
409
532
  const config = await this.retrieveOrgUsersConfig();
410
533
  const contents = await config.read();
411
534
  const thisUsername = (0, ts_types_1.ensure)(this.getUsername());
412
- const usernames = (0, ts_types_1.ensureJsonArray)(contents.usernames || [thisUsername]);
535
+ const usernames = (0, ts_types_1.ensureJsonArray)(contents.usernames ?? [thisUsername]);
413
536
  return Promise.all(usernames.map((username) => {
414
537
  if (username === thisUsername) {
415
538
  return authInfo_1.AuthInfo.create({
@@ -440,7 +563,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
440
563
  */
441
564
  async addUsername(auth) {
442
565
  if (!auth) {
443
- throw new sfdxError_1.SfdxError('Missing auth info', 'MissingAuthInfo');
566
+ throw new sfError_1.SfError('Missing auth info', 'MissingAuthInfo');
444
567
  }
445
568
  const authInfo = (0, ts_types_1.isString)(auth) ? await authInfo_1.AuthInfo.create({ username: auth }) : auth;
446
569
  this.logger.debug(`adding username ${authInfo.getFields().username}`);
@@ -448,9 +571,9 @@ class Org extends kit_1.AsyncOptionalCreatable {
448
571
  const contents = await orgConfig.read();
449
572
  // TODO: This is kind of screwy because contents values can be `AnyJson | object`...
450
573
  // needs config refactoring to improve
451
- const usernames = contents.usernames || [];
574
+ const usernames = contents.usernames ?? [];
452
575
  if (!(0, ts_types_1.isArray)(usernames)) {
453
- throw new sfdxError_1.SfdxError('Usernames is not an array', 'UnexpectedDataFormat');
576
+ throw new sfError_1.SfError('Usernames is not an array', 'UnexpectedDataFormat');
454
577
  }
455
578
  let shouldUpdate = false;
456
579
  const thisUsername = (0, ts_types_1.ensure)(this.getUsername());
@@ -472,44 +595,41 @@ class Org extends kit_1.AsyncOptionalCreatable {
472
595
  /**
473
596
  * Removes a username from the user config for this object. For convenience `this` object is returned.
474
597
  *
475
- * **Throws** *{@link SfdxError}{ name: 'MissingAuthInfoError' }* Auth info is missing.
598
+ * **Throws** *{@link SfError}{ name: 'MissingAuthInfoError' }* Auth info is missing.
476
599
  *
477
600
  * @param {AuthInfo | string} auth The AuthInfo containing the username to remove.
478
601
  */
479
602
  async removeUsername(auth) {
480
603
  if (!auth) {
481
- throw new sfdxError_1.SfdxError('Missing auth info', 'MissingAuthInfoError');
604
+ throw new sfError_1.SfError('Missing auth info', 'MissingAuthInfoError');
482
605
  }
483
606
  const authInfo = (0, ts_types_1.isString)(auth) ? await authInfo_1.AuthInfo.create({ username: auth }) : auth;
484
607
  this.logger.debug(`removing username ${authInfo.getFields().username}`);
485
608
  const orgConfig = await this.retrieveOrgUsersConfig();
486
609
  const contents = await orgConfig.read();
487
610
  const targetUser = authInfo.getFields().username;
488
- const usernames = (contents.usernames || []);
611
+ const usernames = (contents.usernames ?? []);
489
612
  contents.usernames = usernames.filter((username) => username !== targetUser);
490
613
  await orgConfig.write();
491
614
  return this;
492
615
  }
493
616
  /**
494
- * Sets the key/value pair in the sandbox config for this org. For convenience `this` object is returned.
617
+ * set the sandbox config related to this given org
495
618
  *
496
- *
497
- * @param {key} The key for this value
498
- * @param {value} The value to save
619
+ * @param orgId {string} orgId of the sandbox
620
+ * @param config {SandboxFields} config of the sandbox
499
621
  */
500
- async setSandboxOrgConfigField(field, value) {
501
- const sandboxOrgConfig = await this.retrieveSandboxOrgConfig();
502
- sandboxOrgConfig.set(field, value);
503
- await sandboxOrgConfig.write();
622
+ async setSandboxConfig(orgId, config) {
623
+ (await stateAggregator_1.StateAggregator.getInstance()).sandboxes.set(orgId, config);
504
624
  return this;
505
625
  }
506
626
  /**
507
- * Returns an org config field. Returns undefined if the field is not set or invalid.
627
+ * get the sandbox config for the given orgId
628
+ *
629
+ * @param orgId {string} orgId of the sandbox
508
630
  */
509
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
510
- async getSandboxOrgConfigField(field) {
511
- const sandboxOrgConfig = await this.retrieveSandboxOrgConfig();
512
- return sandboxOrgConfig.get(field);
631
+ async getSandboxConfig(orgId) {
632
+ return (await stateAggregator_1.StateAggregator.getInstance()).sandboxes.read(orgId);
513
633
  }
514
634
  /**
515
635
  * Retrieves the highest api version that is supported by the target server instance. If the apiVersion configured for
@@ -517,7 +637,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
517
637
  * results in a warning.
518
638
  */
519
639
  async retrieveMaxApiVersion() {
520
- return await this.getConnection().retrieveMaxApiVersion();
640
+ return this.getConnection().retrieveMaxApiVersion();
521
641
  }
522
642
  /**
523
643
  * Returns the admin username used to create the org.
@@ -529,7 +649,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
529
649
  * Returns the orgId for this org.
530
650
  */
531
651
  getOrgId() {
532
- return this.orgId || this.getField(Org.Fields.ORG_ID);
652
+ return this.orgId ?? this.getField(Org.Fields.ORG_ID);
533
653
  }
534
654
  /**
535
655
  * Returns for the config aggregator.
@@ -541,14 +661,14 @@ class Org extends kit_1.AsyncOptionalCreatable {
541
661
  * Returns an org field. Returns undefined if the field is not set or invalid.
542
662
  */
543
663
  getField(key) {
544
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
664
+ /* eslint-disable @typescript-eslint/ban-ts-comment, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment */
545
665
  // @ts-ignore Legacy. We really shouldn't be doing this.
546
666
  const ownProp = this[key];
547
667
  if (ownProp && typeof ownProp !== 'function')
548
668
  return ownProp;
549
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
550
669
  // @ts-ignore
551
670
  return this.getConnection().getAuthInfoFields()[key];
671
+ /* eslint-enable @typescript-eslint/ban-ts-comment, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment */
552
672
  }
553
673
  /**
554
674
  * Returns a map of requested fields.
@@ -562,15 +682,69 @@ class Org extends kit_1.AsyncOptionalCreatable {
562
682
  }
563
683
  /**
564
684
  * Returns the JSForce connection for the org.
685
+ * side effect: If you pass it an apiVersion, it will set it on the Org
686
+ * so that future calls to getConnection() will also use that version.
687
+ *
688
+ * @param apiVersion The API version to use for the connection.
565
689
  */
566
- getConnection() {
690
+ getConnection(apiVersion) {
691
+ if (apiVersion) {
692
+ if (this.connection.getApiVersion() === apiVersion) {
693
+ this.logger.warn(`Default API version is already ${apiVersion}`);
694
+ }
695
+ else {
696
+ this.connection.setApiVersion(apiVersion);
697
+ }
698
+ }
567
699
  return this.connection;
568
700
  }
701
+ async supportsSourceTracking() {
702
+ if (this.isScratch()) {
703
+ return true;
704
+ }
705
+ try {
706
+ await this.getConnection().tooling.sobject('SourceMember').describe();
707
+ return true;
708
+ }
709
+ catch (err) {
710
+ if (err.message.includes('The requested resource does not exist')) {
711
+ return false;
712
+ }
713
+ throw err;
714
+ }
715
+ }
716
+ /**
717
+ * query SandboxProcess via sandbox name
718
+ *
719
+ * @param name SandboxName to query for
720
+ * @private
721
+ */
722
+ async querySandboxProcessBySandboxName(name) {
723
+ return this.querySandboxProcess(`SandboxName='${name}'`);
724
+ }
725
+ /**
726
+ * query SandboxProcess via SandboxInfoId
727
+ *
728
+ * @param id SandboxInfoId to query for
729
+ * @private
730
+ */
731
+ async querySandboxProcessBySandboxInfoId(id) {
732
+ return this.querySandboxProcess(`SandboxInfoId='${id}'`);
733
+ }
734
+ /**
735
+ * query SandboxProcess via Id
736
+ *
737
+ * @param id SandboxProcessId to query for
738
+ * @private
739
+ */
740
+ async querySandboxProcessById(id) {
741
+ return this.querySandboxProcess(`Id='${id}'`);
742
+ }
569
743
  /**
570
744
  * Initialize async components.
571
745
  */
572
746
  async init() {
573
- const globalInfo = await globalInfo_1.GlobalInfo.getInstance();
747
+ const stateAggregator = await stateAggregator_1.StateAggregator.getInstance();
574
748
  this.logger = await logger_1.Logger.child('Org');
575
749
  this.configAggregator = this.options.aggregator ? this.options.aggregator : await configAggregator_1.ConfigAggregator.create();
576
750
  if (!this.options.connection) {
@@ -579,14 +753,14 @@ class Org extends kit_1.AsyncOptionalCreatable {
579
753
  const aliasOrUsername = this.options.isDevHub
580
754
  ? this.configAggregator.getPropertyValue(orgConfigProperties_1.OrgConfigProperties.TARGET_DEV_HUB)
581
755
  : this.configAggregator.getPropertyValue(orgConfigProperties_1.OrgConfigProperties.TARGET_ORG);
582
- this.options.aliasOrUsername = aliasOrUsername || undefined;
756
+ this.options.aliasOrUsername = aliasOrUsername ?? undefined;
583
757
  }
584
- const username = globalInfo.aliases.resolveUsername(this.options.aliasOrUsername);
758
+ const username = stateAggregator.aliases.resolveUsername(this.options.aliasOrUsername);
585
759
  if (!username) {
586
760
  throw messages.createError('noUsernameFound');
587
761
  }
588
762
  this.connection = await connection_1.Connection.create({
589
- // If no username is provided or resolvable from an alias, AuthInfo will throw an SfdxError.
763
+ // If no username is provided or resolvable from an alias, AuthInfo will throw an SfError.
590
764
  authInfo: await authInfo_1.AuthInfo.create({ username, isDevHub: this.options.isDevHub }),
591
765
  });
592
766
  }
@@ -596,11 +770,68 @@ class Org extends kit_1.AsyncOptionalCreatable {
596
770
  this.orgId = this.getField(Org.Fields.ORG_ID);
597
771
  }
598
772
  /**
599
- * **Throws** *{@link SfdxError}{ name: 'NotSupportedError' }* Throws an unsupported error.
773
+ * **Throws** *{@link SfError}{ name: 'NotSupportedError' }* Throws an unsupported error.
600
774
  */
775
+ // eslint-disable-next-line class-methods-use-this
601
776
  getDefaultOptions() {
602
- throw new sfdxError_1.SfdxError('Not Supported', 'NotSupportedError');
777
+ throw new sfError_1.SfError('Not Supported', 'NotSupportedError');
778
+ }
779
+ async getLocalDataDir(orgDataPath) {
780
+ const rootFolder = await config_1.Config.resolveRootFolder(false);
781
+ return (0, path_1.join)(rootFolder, global_1.Global.SFDX_STATE_FOLDER, orgDataPath ? orgDataPath : 'orgs');
603
782
  }
783
+ /**
784
+ * Gets the sandboxProcessObject and then polls for it to complete.
785
+ *
786
+ * @param sandboxProcessName sanbox process name
787
+ * @param options { wait?: Duration; interval?: Duration }
788
+ * @returns {SandboxProcessObject} The SandboxProcessObject for the sandbox
789
+ */
790
+ async authWithRetriesByName(sandboxProcessName, options) {
791
+ return this.authWithRetries(await this.queryLatestSandboxProcessBySandboxName(sandboxProcessName), options);
792
+ }
793
+ /**
794
+ * Polls the sandbox org for the sandboxProcessObject.
795
+ *
796
+ * @param sandboxProcessObj: The in-progress sandbox signup request
797
+ * @param options { wait?: Duration; interval?: Duration }
798
+ * @returns {SandboxProcessObject}
799
+ */
800
+ async authWithRetries(sandboxProcessObj, options = {
801
+ wait: kit_1.Duration.minutes(0),
802
+ interval: kit_1.Duration.seconds(30),
803
+ }) {
804
+ const [wait, pollInterval] = this.validateWaitOptions(options);
805
+ this.logger.debug(`AuthWithRetries sandboxProcessObj ${JSON.stringify(sandboxProcessObj, undefined, 2)}, max wait time of ${wait.minutes} minutes`);
806
+ return this.pollStatusAndAuth({
807
+ sandboxProcessObj,
808
+ wait,
809
+ pollInterval,
810
+ });
811
+ }
812
+ /**
813
+ * Query the sandbox for the SandboxProcessObject by sandbox name
814
+ *
815
+ * @param sandboxName The name of the sandbox to query
816
+ * @returns {SandboxProcessObject} The SandboxProcessObject for the sandbox
817
+ */
818
+ async queryLatestSandboxProcessBySandboxName(sandboxNameIn) {
819
+ const { tooling } = this.getConnection();
820
+ this.logger.debug('QueryLatestSandboxProcessBySandboxName called with SandboxName: %s ', sandboxNameIn);
821
+ const queryStr = `SELECT Id, Status, SandboxName, SandboxInfoId, LicenseType, CreatedDate, CopyProgress, SandboxOrganization, SourceId, Description, EndDate FROM SandboxProcess WHERE SandboxName='${sandboxNameIn}' AND Status != 'D' ORDER BY CreatedDate DESC LIMIT 1`;
822
+ const queryResult = await tooling.query(queryStr);
823
+ this.logger.debug('Return from calling queryToolingApi: %s ', queryResult);
824
+ if (queryResult?.records?.length === 1) {
825
+ return queryResult.records[0];
826
+ }
827
+ else if (queryResult.records && queryResult.records.length > 1) {
828
+ throw messages.createError('MultiSandboxProcessNotFoundBySandboxName', [sandboxNameIn]);
829
+ }
830
+ else {
831
+ throw messages.createError('SandboxProcessNotFoundBySandboxName', [sandboxNameIn]);
832
+ }
833
+ }
834
+ // eslint-disable-next-line class-methods-use-this
604
835
  async queryProduction(org, field, value) {
605
836
  return org.connection.singleRecordQuery(`SELECT SandboxInfoId FROM SandboxProcess WHERE ${field} ='${value}' AND Status NOT IN ('D', 'E')`, { tooling: true });
606
837
  }
@@ -617,47 +848,53 @@ class Org extends kit_1.AsyncOptionalCreatable {
617
848
  * @private
618
849
  */
619
850
  async deleteSandbox(prodOrg) {
620
- prodOrg !== null && prodOrg !== void 0 ? prodOrg : (prodOrg = await Org.create({
851
+ const sandbox = await this.getSandboxConfig(this.getOrgId());
852
+ prodOrg ??= await Org.create({
621
853
  aggregator: this.configAggregator,
622
- aliasOrUsername: await this.getSandboxOrgConfigField(sandboxOrgConfig_1.SandboxOrgConfig.Fields.PROD_ORG_USERNAME),
623
- }));
624
- let result;
625
- // attempt to locate sandbox id by username
626
- try {
627
- // try to calculate sandbox name from the production org
628
- // production org: admin@integrationtesthub.org
629
- // this.getUsername: admin@integrationtesthub.org.dev1
630
- // sandboxName in Production: dev1
631
- const sandboxName = (this.getUsername() || '').split(`${prodOrg.getUsername()}.`)[1];
632
- if (!sandboxName) {
633
- this.logger.debug('Could not construct a sandbox name');
634
- throw new Error();
635
- }
636
- this.logger.debug(`attempting to locate sandbox with username ${sandboxName}`);
637
- result = await this.queryProduction(prodOrg, 'SandboxName', sandboxName);
638
- if (!result) {
639
- this.logger.debug(`Failed to find sandbox with username: ${sandboxName}`);
640
- throw new Error();
641
- }
642
- }
643
- catch {
644
- // if an error is thrown, don't panic yet. we'll try querying by orgId
645
- const trimmedId = sfdc_1.sfdc.trimTo15(this.getOrgId());
646
- this.logger.debug(`defaulting to trimming id from ${this.getOrgId()} to ${trimmedId}`);
854
+ aliasOrUsername: sandbox?.prodOrgUsername,
855
+ });
856
+ let sandboxInfoId = sandbox?.sandboxInfoId;
857
+ if (!sandboxInfoId) {
858
+ let result;
647
859
  try {
648
- result = await this.queryProduction(prodOrg, 'SandboxOrganization', trimmedId);
860
+ // grab sandboxName from config or try to calculate from the sandbox username
861
+ const sandboxName = sandbox?.sandboxName ?? (this.getUsername() ?? '').split(`${prodOrg.getUsername()}.`)[1];
862
+ if (!sandboxName) {
863
+ this.logger.debug('Sandbox name is not available');
864
+ // jump to query by orgId
865
+ throw new Error();
866
+ }
867
+ this.logger.debug(`attempting to locate sandbox with sandbox ${sandboxName}`);
868
+ try {
869
+ result = await this.queryProduction(prodOrg, 'SandboxName', sandboxName);
870
+ }
871
+ catch (err) {
872
+ this.logger.debug(`Failed to find sandbox with sandbox name: ${sandboxName}`);
873
+ // jump to query by orgId
874
+ throw err;
875
+ }
649
876
  }
650
877
  catch {
651
- throw messages.createError('sandboxNotFound', [trimmedId]);
878
+ // if an error is thrown, don't panic yet. we'll try querying by orgId
879
+ const trimmedId = (0, sfdc_1.trimTo15)(this.getOrgId());
880
+ this.logger.debug(`defaulting to trimming id from ${this.getOrgId()} to ${trimmedId}`);
881
+ try {
882
+ result = await this.queryProduction(prodOrg, 'SandboxOrganization', trimmedId);
883
+ sandboxInfoId = result.SandboxInfoId;
884
+ }
885
+ catch {
886
+ // eating exceptions when trying to find sandbox process record by orgId
887
+ // allows idempotent cleanup of sandbox orgs
888
+ this.logger.debug(`Failed find a SandboxProcess for the sandbox org: ${this.getOrgId()}`);
889
+ }
652
890
  }
653
891
  }
654
- // const deleteResult = await prodOrg.connection.tooling.delete('SandboxInfo', result.SandboxInfoId);
655
- const deleteResult = await this.destroySandbox(prodOrg, result.SandboxInfoId);
656
- this.logger.debug('Return from calling tooling.delete: %o ', deleteResult);
657
- await this.remove();
658
- if (Array.isArray(deleteResult) || !deleteResult.success) {
659
- throw messages.createError('sandboxDeleteFailed', [JSON.stringify(deleteResult)]);
892
+ if (sandboxInfoId) {
893
+ const deleteResult = await this.destroySandbox(prodOrg, sandboxInfoId);
894
+ this.logger.debug('Return from calling tooling.delete: ', deleteResult);
660
895
  }
896
+ // cleanup remaining artifacts
897
+ await this.remove();
661
898
  }
662
899
  /**
663
900
  * If this Org is a scratch org, calling this method will delete the scratch org from the DevHub and clean up any local files
@@ -667,7 +904,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
667
904
  */
668
905
  async deleteScratchOrg(devHub) {
669
906
  // if we didn't get a devHub, we'll get it from the this org
670
- devHub !== null && devHub !== void 0 ? devHub : (devHub = await this.getDevHubOrg());
907
+ devHub ??= await this.getDevHubOrg();
671
908
  if (!devHub) {
672
909
  throw messages.createError('noDevHubFound');
673
910
  }
@@ -705,14 +942,14 @@ class Org extends kit_1.AsyncOptionalCreatable {
705
942
  * this Org. You don't want to call this method directly. Instead consider calling Org.remove()
706
943
  */
707
944
  async removeAuth() {
708
- const config = await globalInfo_1.GlobalInfo.getInstance();
945
+ const stateAggregator = await stateAggregator_1.StateAggregator.getInstance();
709
946
  const username = this.getUsername();
710
947
  // If there is no username, it has already been removed from the globalInfo.
711
948
  // We can skip the unset and just ensure that globalInfo is updated.
712
949
  if (username) {
713
950
  this.logger.debug(`Removing auth for user: ${username}`);
714
951
  this.logger.debug(`Clearing auth cache for user: ${username}`);
715
- config.orgs.unset(username);
952
+ await stateAggregator.orgs.remove(username);
716
953
  }
717
954
  }
718
955
  /**
@@ -725,12 +962,6 @@ class Org extends kit_1.AsyncOptionalCreatable {
725
962
  await config.unlink();
726
963
  }
727
964
  }
728
- /**
729
- * @ignore
730
- */
731
- async retrieveSandboxOrgConfig() {
732
- return await sandboxOrgConfig_1.SandboxOrgConfig.create(sandboxOrgConfig_1.SandboxOrgConfig.getOptions(this.getOrgId()));
733
- }
734
965
  async manageDelete(cb, dirPath, throwWhenRemoveFails) {
735
966
  return cb().catch((e) => {
736
967
  if (throwWhenRemoveFails) {
@@ -749,7 +980,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
749
980
  */
750
981
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
751
982
  async removeUsers(throwWhenRemoveFails) {
752
- const globalInfo = await globalInfo_1.GlobalInfo.getInstance();
983
+ const stateAggregator = await stateAggregator_1.StateAggregator.getInstance();
753
984
  this.logger.debug(`Removing users associate with org: ${this.getOrgId()}`);
754
985
  const config = await this.retrieveOrgUsersConfig();
755
986
  this.logger.debug(`using path for org users: ${config.getPath()}`);
@@ -757,8 +988,8 @@ class Org extends kit_1.AsyncOptionalCreatable {
757
988
  await Promise.all(authInfos
758
989
  .map((auth) => auth.getFields().username)
759
990
  .map(async (username) => {
760
- const aliasKeys = (username && globalInfo.aliases.getAll(username)) || [];
761
- globalInfo.aliases.unsetAll(username);
991
+ const aliasKeys = (username && stateAggregator.aliases.getAll(username)) ?? [];
992
+ stateAggregator.aliases.unsetAll(username);
762
993
  const orgForUser = username === this.getUsername()
763
994
  ? this
764
995
  : await Org.create({
@@ -773,24 +1004,17 @@ class Org extends kit_1.AsyncOptionalCreatable {
773
1004
  needsConfigUpdate ? config_1.Config.update(configInfo.isGlobal(), orgType, undefined) : undefined,
774
1005
  ].filter(Boolean);
775
1006
  }));
776
- await globalInfo.write();
1007
+ await stateAggregator.aliases.write();
777
1008
  }
778
- /**
779
- * Remove an associate sandbox config.
780
- *
781
- * @param throwWhenRemoveFails true if manageDelete should throw or not if the deleted fails.
782
- */
783
- async removeSandboxConfig(throwWhenRemoveFails) {
784
- const sandboxOrgConfig = await this.retrieveSandboxOrgConfig();
785
- if (await sandboxOrgConfig.exists()) {
786
- await this.manageDelete(async () => await sandboxOrgConfig.unlink(), sandboxOrgConfig.getPath(), throwWhenRemoveFails);
787
- }
1009
+ async removeSandboxConfig() {
1010
+ const stateAggregator = await stateAggregator_1.StateAggregator.getInstance();
1011
+ await stateAggregator.sandboxes.remove(this.getOrgId());
788
1012
  }
789
1013
  async writeSandboxAuthFile(sandboxProcessObj, sandboxRes) {
790
- this.logger.debug('writeSandboxAuthFile sandboxProcessObj: %s, sandboxRes: %s', sandboxProcessObj, sandboxRes);
1014
+ this.logger.debug(`writeSandboxAuthFile sandboxProcessObj: ${JSON.stringify(sandboxProcessObj)}, sandboxRes: ${JSON.stringify(sandboxRes)}`);
791
1015
  if (sandboxRes.authUserName) {
792
1016
  const productionAuthFields = this.connection.getAuthInfoFields();
793
- this.logger.debug('Result from getAuthInfoFields: AuthFields %s', productionAuthFields);
1017
+ this.logger.debug('Result from getAuthInfoFields: AuthFields', productionAuthFields);
794
1018
  // let's do headless auth via jwt (if we have privateKey) or web auth
795
1019
  const oauth2Options = {
796
1020
  loginUrl: sandboxRes.loginUrl,
@@ -802,16 +1026,34 @@ class Org extends kit_1.AsyncOptionalCreatable {
802
1026
  oauth2Options.redirectUri = `http://localhost:${await webOAuthServer_1.WebOAuthServer.determineOauthPort()}/OauthRedirect`;
803
1027
  oauth2Options.authCode = sandboxRes.authCode;
804
1028
  }
1029
+ else {
1030
+ oauth2Options.privateKey = productionAuthFields.privateKey;
1031
+ oauth2Options.clientId = productionAuthFields.clientId;
1032
+ }
805
1033
  const authInfo = await authInfo_1.AuthInfo.create({
806
1034
  username: sandboxRes.authUserName,
807
1035
  oauth2Options,
808
1036
  parentUsername: productionAuthFields.username,
809
1037
  });
1038
+ this.logger.debug('Creating AuthInfo for sandbox', sandboxRes.authUserName, productionAuthFields.username, oauth2Options);
1039
+ // save auth info for new sandbox
810
1040
  await authInfo.save();
811
- const sandboxOrg = await Org.create({ aliasOrUsername: authInfo.getUsername() });
812
- await sandboxOrg.setSandboxOrgConfigField(sandboxOrgConfig_1.SandboxOrgConfig.Fields.PROD_ORG_USERNAME,
813
- // we couldn't get this far into the process without a production org so username will be there
814
- productionAuthFields.username);
1041
+ const sandboxOrgId = authInfo.getFields().orgId;
1042
+ if (!sandboxOrgId) {
1043
+ throw messages.createError('AuthInfoOrgIdUndefined');
1044
+ }
1045
+ // set the sandbox config value
1046
+ const sfSandbox = {
1047
+ sandboxUsername: sandboxRes.authUserName,
1048
+ sandboxOrgId,
1049
+ prodOrgUsername: this.getUsername(),
1050
+ sandboxName: sandboxProcessObj.SandboxName,
1051
+ sandboxProcessId: sandboxProcessObj.Id,
1052
+ sandboxInfoId: sandboxProcessObj.SandboxInfoId,
1053
+ timestamp: new Date().toISOString(),
1054
+ };
1055
+ await this.setSandboxConfig(sandboxOrgId, sfSandbox);
1056
+ await (await stateAggregator_1.StateAggregator.getInstance()).sandboxes.write(sandboxOrgId);
815
1057
  await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_RESULT, {
816
1058
  sandboxProcessObj,
817
1059
  sandboxRes,
@@ -822,96 +1064,67 @@ class Org extends kit_1.AsyncOptionalCreatable {
822
1064
  throw messages.createError('missingAuthUsername', [sandboxProcessObj.SandboxName]);
823
1065
  }
824
1066
  }
825
- /**
826
- * Polls for the new sandbox to be created - and will write the associated auth files
827
- *
828
- * @private
829
- * @param options
830
- * sandboxProcessObj: The in-progress sandbox signup request
831
- * retries: the number of retries to poll for every 30s
832
- * shouldPoll: wait for polling, or just return
833
- * pollInterval: Duration to sleep between poll events, default 30 seconds
834
- */
835
1067
  async pollStatusAndAuth(options) {
836
- var _a;
837
- const { sandboxProcessObj, retries, shouldPoll, pollInterval } = options;
838
- this.logger.debug('PollStatusAndAuth called with SandboxProcessObject%s, retries %s', sandboxProcessObj, retries);
839
- const lifecycle = lifecycleEvents_1.Lifecycle.getInstance();
840
- let pollFinished = false;
1068
+ this.logger.debug('PollStatusAndAuth called with SandboxProcessObject', options.sandboxProcessObj, options.wait.minutes, options.pollInterval.seconds);
1069
+ let remainingWait = options.wait;
841
1070
  let waitingOnAuth = false;
842
- const sandboxInfo = await this.sandboxSignupComplete(sandboxProcessObj);
843
- if (sandboxInfo) {
844
- await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_AUTH, sandboxInfo);
845
- try {
846
- this.logger.debug('sandbox signup complete with %s', sandboxInfo);
847
- await this.writeSandboxAuthFile(sandboxProcessObj, sandboxInfo);
848
- pollFinished = true;
849
- }
850
- catch (err) {
851
- const error = err;
852
- this.logger.debug('Exception while calling writeSandboxAuthFile %s', err);
853
- if ((error === null || error === void 0 ? void 0 : error.name) === 'JWTAuthError' && ((_a = error === null || error === void 0 ? void 0 : error.stack) === null || _a === void 0 ? void 0 : _a.includes("user hasn't approved"))) {
854
- waitingOnAuth = true;
1071
+ const pollingClient = await pollingClient_1.PollingClient.create({
1072
+ poll: async () => {
1073
+ const sandboxProcessObj = await this.querySandboxProcessBySandboxInfoId(options.sandboxProcessObj.SandboxInfoId);
1074
+ // check to see if sandbox can authenticate via sandboxAuth endpoint
1075
+ const sandboxInfo = await this.sandboxSignupComplete(sandboxProcessObj);
1076
+ if (sandboxInfo) {
1077
+ await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_AUTH, sandboxInfo);
1078
+ try {
1079
+ this.logger.debug('sandbox signup complete with', sandboxInfo);
1080
+ await this.writeSandboxAuthFile(sandboxProcessObj, sandboxInfo);
1081
+ return { completed: true, payload: sandboxProcessObj };
1082
+ }
1083
+ catch (err) {
1084
+ const error = err;
1085
+ this.logger.debug('Exception while calling writeSandboxAuthFile', err);
1086
+ if (error?.name === 'JwtAuthError' && error?.stack?.includes("user hasn't approved")) {
1087
+ waitingOnAuth = true;
1088
+ }
1089
+ else {
1090
+ throw sfError_1.SfError.wrap(error);
1091
+ }
1092
+ }
855
1093
  }
856
- else {
857
- throw sfdxError_1.SfdxError.wrap(error);
858
- }
859
- }
860
- }
861
- if (!pollFinished) {
862
- if (retries > 0) {
863
- // emit the signup progress of the sandbox and query the production org again after waiting the interval
864
- await Promise.all([
865
- await lifecycle.emit(SandboxEvents.EVENT_STATUS, {
866
- sandboxProcessObj,
867
- interval: pollInterval.seconds,
868
- retries,
869
- waitingOnAuth,
870
- }),
871
- await (0, kit_1.sleep)(pollInterval),
872
- ]);
873
- const polledSandboxProcessObj = await this.querySandboxProcess(sandboxProcessObj.SandboxInfoId);
874
- return this.pollStatusAndAuth({
875
- sandboxProcessObj: polledSandboxProcessObj,
876
- retries: retries - 1,
877
- shouldPoll,
878
- pollInterval,
1094
+ await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_STATUS, {
1095
+ sandboxProcessObj,
1096
+ remainingWait: remainingWait.seconds,
1097
+ interval: options.pollInterval.seconds,
1098
+ waitingOnAuth,
879
1099
  });
880
- }
881
- else {
882
- if (shouldPoll) {
883
- // timed out on retries
884
- throw messages.createError('orgPollingTimeout', [sandboxProcessObj.Status]);
885
- }
886
- else {
887
- // The user didn't want us to poll, so simply return the status
888
- // simply report status and exit
889
- await lifecycle.emit(SandboxEvents.EVENT_ASYNC_RESULT, sandboxProcessObj);
890
- }
891
- }
892
- }
893
- return sandboxProcessObj;
1100
+ remainingWait = kit_1.Duration.seconds(remainingWait.seconds - options.pollInterval.seconds);
1101
+ return { completed: false, payload: sandboxProcessObj };
1102
+ },
1103
+ frequency: options.pollInterval,
1104
+ timeout: options.wait,
1105
+ });
1106
+ return pollingClient.subscribe();
894
1107
  }
895
1108
  /**
896
- * query SandboxProcess via SandboxInfoId
1109
+ * query SandboxProcess using supplied where clause
897
1110
  *
898
- * @param id SandboxInfoId to query for
1111
+ * @param where clause to query for
899
1112
  * @private
900
1113
  */
901
- async querySandboxProcess(id) {
902
- const queryStr = `SELECT Id, Status, SandboxName, SandboxInfoId, LicenseType, CreatedDate, CopyProgress, SandboxOrganization, SourceId, Description, EndDate FROM SandboxProcess WHERE SandboxInfoId='${id}' AND Status != 'D'`;
903
- return await this.connection.singleRecordQuery(queryStr, {
1114
+ async querySandboxProcess(where) {
1115
+ const queryStr = `SELECT Id, Status, SandboxName, SandboxInfoId, LicenseType, CreatedDate, CopyProgress, SandboxOrganization, SourceId, Description, EndDate FROM SandboxProcess WHERE ${where} AND Status != 'D'`;
1116
+ return this.connection.singleRecordQuery(queryStr, {
904
1117
  tooling: true,
905
1118
  });
906
1119
  }
907
1120
  /**
908
1121
  * determines if the sandbox has successfully been created
909
1122
  *
910
- * @param sandboxProcessObj sandbox signup progeress
1123
+ * @param sandboxProcessObj sandbox signup progress
911
1124
  * @private
912
1125
  */
913
1126
  async sandboxSignupComplete(sandboxProcessObj) {
914
- this.logger.debug('sandboxSignupComplete called with SandboxProcessObject %s', sandboxProcessObj);
1127
+ this.logger.debug('sandboxSignupComplete called with SandboxProcessObject', sandboxProcessObj);
915
1128
  if (!sandboxProcessObj.EndDate) {
916
1129
  return;
917
1130
  }
@@ -925,7 +1138,8 @@ class Org extends kit_1.AsyncOptionalCreatable {
925
1138
  sandboxName: sandboxProcessObj.SandboxName,
926
1139
  callbackUrl,
927
1140
  };
928
- this.logger.debug('Calling sandboxAuth with SandboxUserAuthRequest %s', sandboxReq);
1141
+ this.logger.debug('Calling sandboxAuth with SandboxUserAuthRequest', sandboxReq);
1142
+ // eslint-disable-next-line no-underscore-dangle
929
1143
  const url = `${this.connection.tooling._baseUrl()}/sandboxAuth`;
930
1144
  const params = {
931
1145
  method: 'POST',
@@ -934,22 +1148,49 @@ class Org extends kit_1.AsyncOptionalCreatable {
934
1148
  body: JSON.stringify(sandboxReq),
935
1149
  };
936
1150
  const result = await this.connection.tooling.request(params);
937
- this.logger.debug('Result of calling sandboxAuth %s', result);
1151
+ this.logger.debug('Result of calling sandboxAuth', result);
938
1152
  return result;
939
1153
  }
940
1154
  catch (err) {
941
1155
  const error = err;
942
1156
  // There are cases where the endDate is set before the sandbox has actually completed.
943
1157
  // In that case, the sandboxAuth call will throw a specific exception.
944
- if ((error === null || error === void 0 ? void 0 : error.name) === 'INVALID_STATUS') {
945
- this.logger.debug('Error while authenticating the user %s', error === null || error === void 0 ? void 0 : error.toString());
1158
+ if (error?.name === 'INVALID_STATUS') {
1159
+ this.logger.debug('Error while authenticating the user', error?.toString());
946
1160
  }
947
1161
  else {
948
1162
  // If it fails for any unexpected reason, just pass that through
949
- throw sfdxError_1.SfdxError.wrap(error);
1163
+ throw sfError_1.SfError.wrap(error);
950
1164
  }
951
1165
  }
952
1166
  }
1167
+ validateWaitOptions(options) {
1168
+ const wait = options.wait ?? kit_1.Duration.minutes(30);
1169
+ const interval = options.interval ?? kit_1.Duration.seconds(30);
1170
+ let pollInterval = options.async ? kit_1.Duration.seconds(0) : interval;
1171
+ // pollInterval cannot be > wait.
1172
+ pollInterval = pollInterval.seconds > wait.seconds ? wait : pollInterval;
1173
+ return [wait, pollInterval];
1174
+ }
1175
+ /**
1176
+ * removes source tracking files hosted in the project/.sf/orgs/<org id>/
1177
+ *
1178
+ * @private
1179
+ */
1180
+ async removeSourceTrackingFiles() {
1181
+ try {
1182
+ const rootFolder = await config_1.Config.resolveRootFolder(false);
1183
+ await fs.promises.rm((0, path_1.join)(rootFolder, global_1.Global.SF_STATE_FOLDER, 'orgs', this.getOrgId()), {
1184
+ recursive: true,
1185
+ force: true,
1186
+ });
1187
+ }
1188
+ catch (e) {
1189
+ const err = sfError_1.SfError.wrap(e);
1190
+ // consume the error in case something went wrong
1191
+ this.logger.debug(`error deleting source tracking information for ${this.getOrgId()} error: ${err.message}`);
1192
+ }
1193
+ }
953
1194
  }
954
1195
  exports.Org = Org;
955
1196
  (function (Org) {
@@ -1033,6 +1274,11 @@ exports.Org = Org;
1033
1274
  * The snapshot used to create the scratch org.
1034
1275
  */
1035
1276
  Fields["SNAPSHOT"] = "snapshot";
1277
+ /**
1278
+ * true: the org supports and wants source tracking
1279
+ * false: the org opted out of tracking or can't support it
1280
+ */
1281
+ Fields["TRACKS_SOURCE"] = "tracksSource";
1036
1282
  // Should it be on org? Leave it off for now, as it might
1037
1283
  // be confusing to the consumer what this actually is.
1038
1284
  // USERNAMES = 'usernames',