Skip to content

Commit 16bd9d6

Browse files
authored
feat: make resolveConfig() and ConfigSource interface async (#183) (#263)
* feat: make resolveConfig() and ConfigSource interface async Enable async config sources (keychain lookups, credential vaults, network-based config stores) by making resolveConfig() return a Promise. - Add MaybePromise<T> utility type for backwards-compatible async migration - Convert built-in sources (DwJson, Mobify, PackageJson) to async fs.promises - Make InstanceManager methods async - Update CLI commands, MCP server, and VS Code extension callers * fix: add missing await for resolveConfig in logs integration test The integration test file was missed during the async migration.
1 parent 4d12f45 commit 16bd9d6

54 files changed

Lines changed: 855 additions & 696 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/async-resolve-config.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@salesforce/b2c-tooling-sdk': minor
3+
'@salesforce/b2c-cli': patch
4+
'@salesforce/b2c-dx-mcp': patch
5+
---
6+
7+
`resolveConfig()` and the `ConfigSource` interface are now async. This enables config sources that perform async I/O such as keychain lookups, credential vaults, or network-based config stores.
8+
9+
**Breaking:** `resolveConfig()` now returns `Promise<ResolvedB2CConfig>` — callers must `await` the result. The `ConfigSource.load()` method return type is now `MaybePromise<ConfigLoadResult | undefined>`, so existing sync source implementations continue to work without changes.
10+
11+
Built-in sources (`DwJsonSource`, `MobifySource`, `PackageJsonSource`) now use async `fs.promises` for non-blocking file I/O. `InstanceManager` methods and `ConfigSource` instance management methods (`listInstances`, `createInstance`, `removeInstance`, `setActiveInstance`, `storeCredential`, `removeCredential`) also accept async return values via `MaybePromise<T>`.

packages/b2c-cli/src/commands/setup/inspect.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export default class SetupInspect extends BaseCommand<typeof SetupInspect> {
8989

9090
static hiddenAliases = ['config:show', 'config:inspect'];
9191

92-
protected override loadConfiguration(): ResolvedB2CConfig {
92+
protected override async loadConfiguration(): Promise<ResolvedB2CConfig> {
9393
// Include EnvSource so that SFCC_* environment variables are visible in inspect output.
9494
// Other commands handle env vars via oclif flag mappings, but inspect needs to show them
9595
// as a config source since it doesn't have those flags.

packages/b2c-cli/src/commands/setup/instance/create.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ export default class SetupInstanceCreate extends BaseCommand<typeof SetupInstanc
117117
}
118118

119119
// Check if instance already exists
120-
const existingInstances = source.listInstances({configPath: this.flags.config});
120+
const existingInstances = await source.listInstances({configPath: this.flags.config});
121121
if (existingInstances.some((i) => i.name === name)) {
122122
this.error(`Instance "${name}" already exists. Use a different name.`);
123123
}
@@ -287,7 +287,7 @@ export default class SetupInstanceCreate extends BaseCommand<typeof SetupInstanc
287287
}
288288

289289
// Create the instance
290-
source.createInstance({
290+
await source.createInstance({
291291
name,
292292
config,
293293
setActive,

packages/b2c-cli/src/commands/setup/instance/list.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export default class SetupInstanceList extends BaseCommand<typeof SetupInstanceL
5757
async run(): Promise<InstanceListResponse> {
5858
// Get instances from all sources that support listing
5959
const source = new DwJsonSource();
60-
const instances = source.listInstances({
60+
const instances = await source.listInstances({
6161
configPath: this.flags.config,
6262
});
6363

packages/b2c-cli/src/commands/setup/instance/remove.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export default class SetupInstanceRemove extends BaseCommand<typeof SetupInstanc
5353
const name = this.args.name;
5454

5555
// Check if instance exists
56-
const instances = source.listInstances({configPath: this.flags.config});
56+
const instances = await source.listInstances({configPath: this.flags.config});
5757
const instance = instances.find((i) => i.name === name);
5858

5959
if (!instance) {
@@ -82,7 +82,7 @@ export default class SetupInstanceRemove extends BaseCommand<typeof SetupInstanc
8282
}
8383

8484
// Remove the instance
85-
source.removeInstance(name, {configPath: this.flags.config});
85+
await source.removeInstance(name, {configPath: this.flags.config});
8686

8787
const result: InstanceRemoveResponse = {
8888
name,

packages/b2c-cli/src/commands/setup/instance/set-active.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export default class SetupInstanceSetActive extends BaseCommand<typeof SetupInst
4242

4343
async run(): Promise<InstanceSetActiveResponse> {
4444
const source = new DwJsonSource();
45-
const instances = source.listInstances({configPath: this.flags.config});
45+
const instances = await source.listInstances({configPath: this.flags.config});
4646

4747
let name = this.args.name;
4848

@@ -87,7 +87,7 @@ export default class SetupInstanceSetActive extends BaseCommand<typeof SetupInst
8787
}
8888

8989
// Set as active
90-
source.setActiveInstance(name, {configPath: this.flags.config});
90+
await source.setActiveInstance(name, {configPath: this.flags.config});
9191

9292
const result: InstanceSetActiveResponse = {
9393
name,

packages/b2c-cli/src/commands/slas/token.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export default class SlasToken extends SlasClientCommand<typeof SlasToken> {
7373
}),
7474
};
7575

76-
protected override loadConfiguration(): ResolvedB2CConfig {
76+
protected override async loadConfiguration(): Promise<ResolvedB2CConfig> {
7777
const flags = this.flags as Record<string, unknown>;
7878
return loadConfig(
7979
{

packages/b2c-cli/src/lib/scaffold/source-resolver.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export function resolveLocalSource(source: DynamicParameterSource, projectRoot:
4343
* @throws Error if authentication fails or API call fails
4444
*/
4545
export async function resolveRemoteSource(source: DynamicParameterSource): Promise<ScaffoldChoice[]> {
46-
const config = loadConfig({}, {configPath: undefined});
46+
const config = await loadConfig({}, {configPath: undefined});
4747

4848
if (!config.hasB2CInstanceConfig() || !config.hasOAuthConfig()) {
4949
throw new Error('B2C instance configuration with OAuth required for sites source');

packages/b2c-cli/src/utils/cip/command.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export abstract class CipCommand<T extends typeof Command> extends OAuthCommand<
9494
return CIP_AUTH_METHODS;
9595
}
9696

97-
protected override loadConfiguration(): ResolvedB2CConfig {
97+
protected override async loadConfiguration(): Promise<ResolvedB2CConfig> {
9898
const flags = this.flags as Record<string, unknown>;
9999
return loadConfig(
100100
{

packages/b2c-dx-mcp/src/commands/mcp.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ export default class McpServerCommand extends BaseCommand<typeof McpServerComman
261261
* 3. dw.json file (via --config flag or auto-discovered from --project-directory)
262262
* 4. ~/.mobify file (for MRT API key)
263263
*/
264-
protected override loadConfiguration(): ResolvedB2CConfig {
264+
protected override async loadConfiguration(): Promise<ResolvedB2CConfig> {
265265
const mrt = extractMrtFlags(this.flags as Record<string, unknown>);
266266
const options: LoadConfigOptions = {
267267
...this.getBaseConfigOptions(),
@@ -286,8 +286,8 @@ export default class McpServerCommand extends BaseCommand<typeof McpServerComman
286286
*
287287
* @returns A new Services instance with loaded configuration
288288
*/
289-
protected loadServices(): Services {
290-
const config = this.loadConfiguration();
289+
protected async loadServices(): Promise<Services> {
290+
const config = await this.loadConfiguration();
291291
return Services.fromResolvedConfig(config);
292292
}
293293

0 commit comments

Comments
 (0)