@salesforce/core 8.15.1-dev.0 → 8.17.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.
@@ -54,8 +54,9 @@ export type ConfigInfo = {
54
54
  * ```
55
55
  */
56
56
  export declare class ConfigAggregator extends AsyncOptionalCreatable<ConfigAggregator.Options> {
57
- protected static instance: AsyncOptionalCreatable;
58
57
  protected static encrypted: boolean;
58
+ protected static instance: AsyncOptionalCreatable | undefined;
59
+ private static readonly mutex;
59
60
  private allowedProperties;
60
61
  private readonly localConfig?;
61
62
  private readonly globalConfig;
@@ -68,6 +69,12 @@ export declare class ConfigAggregator extends AsyncOptionalCreatable<ConfigAggre
68
69
  constructor(options?: ConfigAggregator.Options);
69
70
  private get config();
70
71
  static create<P extends ConfigAggregator.Options, T extends AsyncOptionalCreatable<P>>(this: new (options?: P) => T, options?: P): Promise<T>;
72
+ /**
73
+ * Clear the cache to force reading from disk.
74
+ *
75
+ * *NOTE: Only call this method if you must and you know what you are doing.*
76
+ */
77
+ static clearInstance(): Promise<void>;
71
78
  /**
72
79
  * Get the info for a given key. If the ConfigAggregator was not asynchronously created OR
73
80
  * the {@link ConfigAggregator.reload} was not called, the config value may be encrypted.
@@ -11,6 +11,7 @@ const kit_1 = require("@salesforce/kit");
11
11
  const ts_types_1 = require("@salesforce/ts-types");
12
12
  const messages_1 = require("../messages");
13
13
  const lifecycleEvents_1 = require("../lifecycleEvents");
14
+ const mutex_1 = require("../util/mutex");
14
15
  const envVars_1 = require("./envVars");
15
16
  const config_1 = require("./config");
16
17
  ;
@@ -31,8 +32,9 @@ const messages = new messages_1.Messages('@salesforce/core', 'config', new Map([
31
32
  * ```
32
33
  */
33
34
  class ConfigAggregator extends kit_1.AsyncOptionalCreatable {
34
- static instance;
35
35
  static encrypted = true;
36
+ static instance;
37
+ static mutex = new mutex_1.Mutex();
36
38
  // Initialized in loadProperties
37
39
  allowedProperties;
38
40
  localConfig;
@@ -64,18 +66,32 @@ class ConfigAggregator extends kit_1.AsyncOptionalCreatable {
64
66
  // Use typing from AsyncOptionalCreatable to support extending ConfigAggregator.
65
67
  // We really don't want ConfigAggregator extended but typescript doesn't support a final.
66
68
  static async create(options) {
67
- let config = ConfigAggregator.instance;
68
- if (!config) {
69
- config = ConfigAggregator.instance = new this(options);
70
- await config.init();
71
- }
72
- if (ConfigAggregator.encrypted) {
73
- await config.loadProperties();
74
- }
75
- if (options?.customConfigMeta) {
76
- config_1.Config.addAllowedProperties(options.customConfigMeta);
77
- }
78
- return ConfigAggregator.instance;
69
+ return ConfigAggregator.mutex.lock(async () => {
70
+ let config = ConfigAggregator.instance;
71
+ if (!config) {
72
+ config = ConfigAggregator.instance = new this(options);
73
+ await config.init();
74
+ }
75
+ if (ConfigAggregator.encrypted) {
76
+ await config.loadProperties();
77
+ }
78
+ if (options?.customConfigMeta) {
79
+ config_1.Config.addAllowedProperties(options.customConfigMeta);
80
+ }
81
+ // console.log(ConfigAggregator.instance);
82
+ return ConfigAggregator.instance;
83
+ });
84
+ }
85
+ /**
86
+ * Clear the cache to force reading from disk.
87
+ *
88
+ * *NOTE: Only call this method if you must and you know what you are doing.*
89
+ */
90
+ static async clearInstance() {
91
+ return ConfigAggregator.mutex.lock(() => {
92
+ ConfigAggregator.instance = undefined;
93
+ ConfigAggregator.encrypted = true; // Reset encryption flag as well
94
+ });
79
95
  }
80
96
  /**
81
97
  * Get the info for a given key. If the ConfigAggregator was not asynchronously created OR
package/lib/index.d.ts CHANGED
@@ -38,3 +38,4 @@ export { ScratchOrgLifecycleEvent, scratchOrgLifecycleEventName, scratchOrgLifec
38
38
  export { ScratchOrgCache } from './org/scratchOrgCache';
39
39
  export { default as ScratchOrgSettingsGenerator } from './org/scratchOrgSettingsGenerator';
40
40
  export * from './util/sfdc';
41
+ export * from './util/mutex';
package/lib/index.js CHANGED
@@ -118,4 +118,5 @@ var scratchOrgSettingsGenerator_1 = require("./org/scratchOrgSettingsGenerator")
118
118
  Object.defineProperty(exports, "ScratchOrgSettingsGenerator", { enumerable: true, get: function () { return __importDefault(scratchOrgSettingsGenerator_1).default; } });
119
119
  // Utility sub-modules
120
120
  __exportStar(require("./util/sfdc"), exports);
121
+ __exportStar(require("./util/mutex"), exports);
121
122
  //# sourceMappingURL=index.js.map
@@ -259,8 +259,6 @@ export declare class AuthInfo extends AsyncOptionalCreatable<AuthInfo.Options> {
259
259
  getFields(decrypt?: boolean): Readonly<AuthFields>;
260
260
  /**
261
261
  * Get the org front door (used for web based oauth flows)
262
- *
263
- * @deprecated Will be removed in the next major version. Use the `Org.getFrontDoorUrl()` method instead.
264
262
  */
265
263
  getOrgFrontDoorUrl(): string;
266
264
  /**
@@ -545,8 +545,6 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
545
545
  }
546
546
  /**
547
547
  * Get the org front door (used for web based oauth flows)
548
- *
549
- * @deprecated Will be removed in the next major version. Use the `Org.getFrontDoorUrl()` method instead.
550
548
  */
551
549
  getOrgFrontDoorUrl() {
552
550
  const authFields = this.getFields(true);
package/lib/org/org.d.ts CHANGED
@@ -152,39 +152,6 @@ export declare class Org extends AsyncOptionalCreatable<Org.Options> {
152
152
  * @ignore
153
153
  */
154
154
  constructor(options?: Org.Options);
155
- /**
156
- * Generate a URL to a metadata UI builder/setup section in an org.
157
- *
158
- * Bot: open in Agentforce Builder
159
- * ApexPage: opens page
160
- * Flow: open in Flow Builder
161
- * FlexiPage: open in Lightning App Builder
162
- * CustomObject: open in Object Manager
163
- * ApexClass: open in Setup -> Apex Classes UI
164
- *
165
- * if you pass any other metadata type you'll get a path to Lightning App Builder
166
- *
167
- * @example
168
- * // use SDR resolver:
169
- * import { MetadataResolver } from '@salesforce/source-deploy-retrieve';
170
- *
171
- * const metadataResolver = new MetadataResolver();
172
- * const components = metadataResolver.getComponentsFromPath(filePath);
173
- * const typeName = components[0]?.type?.name;
174
- *
175
- * const metadataBuilderUrl = await org.getMetadataUIURL(typeName, filePath);
176
- *
177
- * @typeName Bot | ApexPage | Flow | FlexiPage | CustomObject | ApexClass
178
- * @file Absolute file path to the metadata file
179
- */
180
- getMetadataUIURL(typeName: string, file: string): Promise<string>;
181
- /**
182
- * Get a Frontdoor URL
183
- *
184
- * This uses the UI Bridge API to generate a single-use Frontdoor URL:
185
- * https://help.salesforce.com/s/articleView?id=xcloud.frontdoor_singleaccess.htm&type=5
186
- */
187
- getFrontDoorUrl(redirectUri?: string): Promise<string>;
188
155
  /**
189
156
  * create a sandbox from a production org
190
157
  * 'this' needs to be a production org with sandbox licenses available
package/lib/org/org.js CHANGED
@@ -29,13 +29,10 @@ var __importStar = (this && this.__importStar) || function (mod) {
29
29
  __setModuleDefault(result, mod);
30
30
  return result;
31
31
  };
32
- var __importDefault = (this && this.__importDefault) || function (mod) {
33
- return (mod && mod.__esModule) ? mod : { "default": mod };
34
- };
35
32
  Object.defineProperty(exports, "__esModule", { value: true });
36
33
  exports.Org = exports.SandboxEvents = exports.OrgTypes = void 0;
37
34
  exports.sandboxIsResumable = sandboxIsResumable;
38
- const node_path_1 = __importDefault(require("node:path"));
35
+ const node_path_1 = require("node:path");
39
36
  const fs = __importStar(require("node:fs"));
40
37
  const kit_1 = require("@salesforce/kit");
41
38
  const ts_types_1 = require("@salesforce/ts-types");
@@ -56,7 +53,7 @@ const authInfo_1 = require("./authInfo");
56
53
  const scratchOrgCreate_1 = require("./scratchOrgCreate");
57
54
  const orgConfigProperties_1 = require("./orgConfigProperties");
58
55
  ;
59
- 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."], ["sandboxInfoRefreshFailed", "The sandbox org refresh 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."], ["FrontdoorURLError", "Failed to generate a frontdoor URL."], ["FlowIdNotFound", "ID not found for Flow %s."], ["CustomObjectIdNotFound", "ID not found for custom object %s."], ["ApexClassIdNotFound", "ID not found for Apex class %s."]]));
56
+ 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."], ["sandboxInfoRefreshFailed", "The sandbox org refresh 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."]]));
60
57
  var OrgTypes;
61
58
  (function (OrgTypes) {
62
59
  OrgTypes["Scratch"] = "scratch";
@@ -149,111 +146,6 @@ class Org extends kit_1.AsyncOptionalCreatable {
149
146
  super(options);
150
147
  this.options = options ?? {};
151
148
  }
152
- /**
153
- * Generate a URL to a metadata UI builder/setup section in an org.
154
- *
155
- * Bot: open in Agentforce Builder
156
- * ApexPage: opens page
157
- * Flow: open in Flow Builder
158
- * FlexiPage: open in Lightning App Builder
159
- * CustomObject: open in Object Manager
160
- * ApexClass: open in Setup -> Apex Classes UI
161
- *
162
- * if you pass any other metadata type you'll get a path to Lightning App Builder
163
- *
164
- * @example
165
- * // use SDR resolver:
166
- * import { MetadataResolver } from '@salesforce/source-deploy-retrieve';
167
- *
168
- * const metadataResolver = new MetadataResolver();
169
- * const components = metadataResolver.getComponentsFromPath(filePath);
170
- * const typeName = components[0]?.type?.name;
171
- *
172
- * const metadataBuilderUrl = await org.getMetadataUIURL(typeName, filePath);
173
- *
174
- * @typeName Bot | ApexPage | Flow | FlexiPage | CustomObject | ApexClass
175
- * @file Absolute file path to the metadata file
176
- */
177
- async getMetadataUIURL(typeName, file) {
178
- const botFileNameToId = async (conn, filePath) => (await conn.singleRecordQuery(`SELECT id FROM BotDefinition WHERE DeveloperName='${node_path_1.default.basename(filePath, '.bot-meta.xml')}'`)).Id;
179
- /** query flexipage via toolingAPI to get its ID (starts with 0M0) */
180
- const flexiPageFilenameToId = async (conn, filePath) => (await conn.singleRecordQuery(`SELECT id FROM flexipage WHERE DeveloperName='${node_path_1.default.basename(filePath, '.flexipage-meta.xml')}'`, { tooling: true })).Id;
181
- /** query the rest API to turn a flow's filepath into a FlowId (starts with 301) */
182
- const flowFileNameToId = async (conn, filePath) => {
183
- try {
184
- const flow = await conn.singleRecordQuery(`SELECT DurableId FROM FlowVersionView WHERE FlowDefinitionView.ApiName = '${node_path_1.default.basename(filePath, '.flow-meta.xml')}' ORDER BY VersionNumber DESC LIMIT 1`);
185
- return flow.DurableId;
186
- }
187
- catch (error) {
188
- throw messages.createError('FlowIdNotFound', [filePath]);
189
- }
190
- };
191
- const customObjectFileNameToId = async (conn, filePath) => {
192
- try {
193
- const customObject = await conn.singleRecordQuery(`SELECT Id FROM CustomObject WHERE DeveloperName = '${node_path_1.default.basename(filePath.replace(/__c/g, ''), '.object-meta.xml')}'`, {
194
- tooling: true,
195
- });
196
- return customObject.Id;
197
- }
198
- catch (error) {
199
- throw messages.createError('CustomObjectIdNotFound', [filePath]);
200
- }
201
- };
202
- const apexClassFileNameToId = async (conn, filePath) => {
203
- try {
204
- const apexClass = await conn.singleRecordQuery(`SELECT Id FROM ApexClass WHERE Name = '${node_path_1.default.basename(filePath, '.cls')}'`, {
205
- tooling: true,
206
- });
207
- return apexClass.Id;
208
- }
209
- catch (error) {
210
- throw messages.createError('ApexClassIdNotFound', [filePath]);
211
- }
212
- };
213
- let redirectUri = '';
214
- switch (typeName) {
215
- case 'ApexClass':
216
- redirectUri = `lightning/setup/ApexClasses/page?address=%2F${await apexClassFileNameToId(this.connection, file)}`;
217
- break;
218
- case 'CustomObject':
219
- redirectUri = `lightning/setup/ObjectManager/${await customObjectFileNameToId(this.connection, file)}/Details/view`;
220
- break;
221
- case 'Bot':
222
- redirectUri = `/AiCopilot/copilotStudio.app#/copilot/builder?copilotId=${await botFileNameToId(this.connection, file)}`;
223
- break;
224
- case 'ApexPage':
225
- redirectUri = `/apex/${node_path_1.default.basename(file).replace('.page-meta.xml', '').replace('.page', '')}`;
226
- break;
227
- case 'Flow':
228
- redirectUri = `/builder_platform_interaction/flowBuilder.app?flowId=${await flowFileNameToId(this.connection, file)}`;
229
- break;
230
- case 'FlexiPage':
231
- redirectUri = `/visualEditor/appBuilder.app?pageId=${await flexiPageFilenameToId(this.connection, file)}`;
232
- break;
233
- default:
234
- redirectUri = '/lightning/setup/FlexiPageList/home';
235
- break;
236
- }
237
- return this.getFrontDoorUrl(redirectUri);
238
- }
239
- /**
240
- * Get a Frontdoor URL
241
- *
242
- * This uses the UI Bridge API to generate a single-use Frontdoor URL:
243
- * https://help.salesforce.com/s/articleView?id=xcloud.frontdoor_singleaccess.htm&type=5
244
- */
245
- async getFrontDoorUrl(redirectUri) {
246
- // the `singleaccess` endpoint returns 403 when using an expired token and jsforce only triggers a token refresh on 401 so we check if it's valid first
247
- await this.refreshAuth();
248
- const singleAccessUrl = new URL('/services/oauth2/singleaccess', this.connection.instanceUrl);
249
- if (redirectUri) {
250
- singleAccessUrl.searchParams.append('redirect_uri', redirectUri);
251
- }
252
- const response = await this.connection.requestGet(singleAccessUrl.toString());
253
- if (response.frontdoor_uri)
254
- return response.frontdoor_uri;
255
- throw new sfError_1.SfError(messages.getMessage('FrontdoorURLError')).setData(response);
256
- }
257
149
  /**
258
150
  * create a sandbox from a production org
259
151
  * 'this' needs to be a production org with sandbox licenses available
@@ -1033,7 +925,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
1033
925
  }
1034
926
  async getLocalDataDir(orgDataPath) {
1035
927
  const rootFolder = await config_1.Config.resolveRootFolder(false);
1036
- return node_path_1.default.join(rootFolder, global_1.Global.SFDX_STATE_FOLDER, orgDataPath ? orgDataPath : 'orgs');
928
+ return (0, node_path_1.join)(rootFolder, global_1.Global.SFDX_STATE_FOLDER, orgDataPath ? orgDataPath : 'orgs');
1037
929
  }
1038
930
  /**
1039
931
  * Gets the sandboxProcessObject and then polls for it to complete.
@@ -1459,7 +1351,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
1459
1351
  async removeSourceTrackingFiles() {
1460
1352
  try {
1461
1353
  const rootFolder = await config_1.Config.resolveRootFolder(false);
1462
- await fs.promises.rm(node_path_1.default.join(rootFolder, global_1.Global.SF_STATE_FOLDER, 'orgs', this.getOrgId()), {
1354
+ await fs.promises.rm((0, node_path_1.join)(rootFolder, global_1.Global.SF_STATE_FOLDER, 'orgs', this.getOrgId()), {
1463
1355
  recursive: true,
1464
1356
  force: true,
1465
1357
  });
@@ -4,6 +4,7 @@ import { OrgAccessor } from './accessors/orgAccessor';
4
4
  import { SandboxAccessor } from './accessors/sandboxAccessor';
5
5
  export declare class StateAggregator extends AsyncOptionalCreatable {
6
6
  private static instanceMap;
7
+ private static readonly mutex;
7
8
  aliases: AliasAccessor;
8
9
  orgs: OrgAccessor;
9
10
  sandboxes: SandboxAccessor;
@@ -17,7 +18,14 @@ export declare class StateAggregator extends AsyncOptionalCreatable {
17
18
  * Clear the cache to force reading from disk.
18
19
  *
19
20
  * *NOTE: Only call this method if you must and you know what you are doing.*
21
+ * *NOTE: This call is NOT thread-safe, so it should only be called when no other threads are using the StateAggregator.*
20
22
  */
21
23
  static clearInstance(path?: string): void;
24
+ /**
25
+ * Clear the cache to force reading from disk in a thread-safe manner.
26
+ *
27
+ * *NOTE: Only call this method if you must and you know what you are doing.*
28
+ */
29
+ static clearInstanceAsync(path?: string): Promise<void>;
22
30
  protected init(): Promise<void>;
23
31
  }
@@ -9,11 +9,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.StateAggregator = void 0;
10
10
  const kit_1 = require("@salesforce/kit");
11
11
  const global_1 = require("../global");
12
+ const mutex_1 = require("../util/mutex");
12
13
  const aliasAccessor_1 = require("./accessors/aliasAccessor");
13
14
  const orgAccessor_1 = require("./accessors/orgAccessor");
14
15
  const sandboxAccessor_1 = require("./accessors/sandboxAccessor");
15
16
  class StateAggregator extends kit_1.AsyncOptionalCreatable {
16
17
  static instanceMap = new Map();
18
+ static mutex = new mutex_1.Mutex();
17
19
  aliases;
18
20
  orgs;
19
21
  sandboxes;
@@ -23,20 +25,33 @@ class StateAggregator extends kit_1.AsyncOptionalCreatable {
23
25
  * HomeDir might be stubbed in tests
24
26
  */
25
27
  static async getInstance() {
26
- if (!StateAggregator.instanceMap.has(global_1.Global.DIR)) {
27
- StateAggregator.instanceMap.set(global_1.Global.DIR, await StateAggregator.create());
28
- }
29
- // TS assertion is valid because there either was one OR it was just now instantiated
30
- return StateAggregator.instanceMap.get(global_1.Global.DIR);
28
+ return StateAggregator.mutex.lock(async () => {
29
+ if (!StateAggregator.instanceMap.has(global_1.Global.DIR)) {
30
+ StateAggregator.instanceMap.set(global_1.Global.DIR, await StateAggregator.create());
31
+ }
32
+ // TS assertion is valid because there either was one OR it was just now instantiated
33
+ return StateAggregator.instanceMap.get(global_1.Global.DIR);
34
+ });
31
35
  }
32
36
  /**
33
37
  * Clear the cache to force reading from disk.
34
38
  *
35
39
  * *NOTE: Only call this method if you must and you know what you are doing.*
40
+ * *NOTE: This call is NOT thread-safe, so it should only be called when no other threads are using the StateAggregator.*
36
41
  */
37
42
  static clearInstance(path = global_1.Global.DIR) {
38
43
  StateAggregator.instanceMap.delete(path);
39
44
  }
45
+ /**
46
+ * Clear the cache to force reading from disk in a thread-safe manner.
47
+ *
48
+ * *NOTE: Only call this method if you must and you know what you are doing.*
49
+ */
50
+ static async clearInstanceAsync(path = global_1.Global.DIR) {
51
+ return StateAggregator.mutex.lock(() => {
52
+ StateAggregator.clearInstance(path);
53
+ });
54
+ }
40
55
  async init() {
41
56
  this.orgs = await orgAccessor_1.OrgAccessor.create();
42
57
  this.sandboxes = await sandboxAccessor_1.SandboxAccessor.create();
@@ -0,0 +1,49 @@
1
+ /**
2
+ * A mutual exclusion (mutex) class that ensures only one asynchronous operation
3
+ * can execute at a time, providing thread-safe execution of critical sections.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * const mutex = new Mutex();
8
+ *
9
+ * // Only one of these will execute at a time
10
+ * mutex.lock(async () => {
11
+ * // Critical section code here
12
+ * return someAsyncOperation();
13
+ * });
14
+ * ```
15
+ */
16
+ export declare class Mutex {
17
+ /**
18
+ * Internal promise chain that maintains the mutex state.
19
+ * Each new lock acquisition is chained to this promise.
20
+ *
21
+ * @private
22
+ */
23
+ private mutex;
24
+ /**
25
+ * Acquires the mutex lock and executes the provided function.
26
+ * The function will not execute until all previously queued operations complete.
27
+ *
28
+ * @template T - The return type of the function
29
+ * @param fn - The function to execute while holding the mutex lock. Can be synchronous or asynchronous.
30
+ * @returns A promise that resolves with the result of the function execution
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * const result = await mutex.lock(async () => {
35
+ * // This code is guaranteed to run exclusively
36
+ * return await someAsyncOperation();
37
+ * });
38
+ * ```
39
+ */
40
+ lock<T>(fn: () => Promise<T> | T): Promise<T>;
41
+ /**
42
+ * Acquires the mutex by waiting for the current promise chain to resolve
43
+ * and returns a release function to unlock the mutex.
44
+ *
45
+ * @private
46
+ * @returns A promise that resolves to a function that releases the mutex lock
47
+ */
48
+ private acquire;
49
+ }
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2025, salesforce.com, inc.
4
+ * All rights reserved.
5
+ * Licensed under the BSD 3-Clause license.
6
+ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.Mutex = void 0;
10
+ /**
11
+ * A mutual exclusion (mutex) class that ensures only one asynchronous operation
12
+ * can execute at a time, providing thread-safe execution of critical sections.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const mutex = new Mutex();
17
+ *
18
+ * // Only one of these will execute at a time
19
+ * mutex.lock(async () => {
20
+ * // Critical section code here
21
+ * return someAsyncOperation();
22
+ * });
23
+ * ```
24
+ */
25
+ class Mutex {
26
+ /**
27
+ * Internal promise chain that maintains the mutex state.
28
+ * Each new lock acquisition is chained to this promise.
29
+ *
30
+ * @private
31
+ */
32
+ mutex = Promise.resolve();
33
+ /**
34
+ * Acquires the mutex lock and executes the provided function.
35
+ * The function will not execute until all previously queued operations complete.
36
+ *
37
+ * @template T - The return type of the function
38
+ * @param fn - The function to execute while holding the mutex lock. Can be synchronous or asynchronous.
39
+ * @returns A promise that resolves with the result of the function execution
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * const result = await mutex.lock(async () => {
44
+ * // This code is guaranteed to run exclusively
45
+ * return await someAsyncOperation();
46
+ * });
47
+ * ```
48
+ */
49
+ async lock(fn) {
50
+ const unlock = await this.acquire();
51
+ try {
52
+ return await fn();
53
+ }
54
+ finally {
55
+ unlock();
56
+ }
57
+ }
58
+ /**
59
+ * Acquires the mutex by waiting for the current promise chain to resolve
60
+ * and returns a release function to unlock the mutex.
61
+ *
62
+ * @private
63
+ * @returns A promise that resolves to a function that releases the mutex lock
64
+ */
65
+ async acquire() {
66
+ let release;
67
+ const promise = new Promise((resolve) => {
68
+ release = resolve;
69
+ });
70
+ const currentMutex = this.mutex;
71
+ this.mutex = this.mutex.then(() => promise);
72
+ await currentMutex;
73
+ return release;
74
+ }
75
+ }
76
+ exports.Mutex = Mutex;
77
+ //# sourceMappingURL=mutex.js.map
package/messages/org.md CHANGED
@@ -69,19 +69,3 @@ We found more than one SandboxProcess with the SandboxName %s.
69
69
  # sandboxNotResumable
70
70
 
71
71
  The sandbox %s cannot resume with status of %s.
72
-
73
- # FrontdoorURLError
74
-
75
- Failed to generate a frontdoor URL.
76
-
77
- # FlowIdNotFound
78
-
79
- ID not found for Flow %s.
80
-
81
- # CustomObjectIdNotFound
82
-
83
- ID not found for custom object %s.
84
-
85
- # ApexClassIdNotFound
86
-
87
- ID not found for Apex class %s.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/core",
3
- "version": "8.15.1-dev.0",
3
+ "version": "8.17.0",
4
4
  "description": "Core libraries to interact with SFDX projects, orgs, and APIs.",
5
5
  "main": "lib/index",
6
6
  "types": "lib/index.d.ts",
@@ -53,7 +53,7 @@
53
53
  "messageTransformer/messageTransformer.ts"
54
54
  ],
55
55
  "dependencies": {
56
- "@jsforce/jsforce-node": "^3.9.1",
56
+ "@jsforce/jsforce-node": "^3.8.2",
57
57
  "@salesforce/kit": "^3.2.2",
58
58
  "@salesforce/schemas": "^1.9.0",
59
59
  "@salesforce/ts-types": "^2.0.10",