Skip to content

Commit 93da505

Browse files
nrkruksvc-cli-bot
andauthored
fix: auto enable local dev for Agentforce Vibes @W-19250167 (#533)
* fix: strip trailing slashes from instance URL in LightningDevComponent * chore(release): 4.5.2-alpha.0 [skip ci] * fix: auto enable local dev * fix: no tests --------- Co-authored-by: svc-cli-bot <[email protected]>
1 parent 75eaad8 commit 93da505

File tree

4 files changed

+249
-6
lines changed

4 files changed

+249
-6
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ EXAMPLES
201201
$ sf lightning dev app --target-org myOrg --device-type ios --device-id "iPhone 15 Pro Max"
202202
```
203203

204-
_See code: [src/commands/lightning/dev/app.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.5.1/src/commands/lightning/dev/app.ts)_
204+
_See code: [src/commands/lightning/dev/app.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.5.2-alpha.0/src/commands/lightning/dev/app.ts)_
205205

206206
## `sf lightning dev component`
207207

@@ -249,7 +249,7 @@ EXAMPLES
249249
$ sf lightning dev component --name myComponent
250250
```
251251

252-
_See code: [src/commands/lightning/dev/component.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.5.1/src/commands/lightning/dev/component.ts)_
252+
_See code: [src/commands/lightning/dev/component.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.5.2-alpha.0/src/commands/lightning/dev/component.ts)_
253253

254254
## `sf lightning dev site`
255255

@@ -305,6 +305,6 @@ EXAMPLES
305305
$ sf lightning dev site --name "Partner Central" --target-org myOrg --get-latest
306306
```
307307

308-
_See code: [src/commands/lightning/dev/site.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.5.1/src/commands/lightning/dev/site.ts)_
308+
_See code: [src/commands/lightning/dev/site.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.5.2-alpha.0/src/commands/lightning/dev/site.ts)_
309309

310310
<!-- commandsstop -->

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@salesforce/plugin-lightning-dev",
33
"description": "Lightning development tools for LEX, Mobile, and Experience Sites",
4-
"version": "4.5.1",
4+
"version": "4.5.2-alpha.0",
55
"author": "Salesforce",
66
"bugs": "https://github.com/forcedotcom/cli/issues",
77
"dependencies": {

src/commands/lightning/dev/component.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { ComponentUtils } from '../../../shared/componentUtils.js';
1313
import { PromptUtils } from '../../../shared/promptUtils.js';
1414
import { PreviewUtils } from '../../../shared/previewUtils.js';
1515
import { startLWCServer } from '../../../lwc-dev-server/index.js';
16+
import { MetaUtils } from '../../../shared/metaUtils.js';
1617

1718
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
1819
const messages = Messages.loadMessages('@salesforce/plugin-lightning-dev', 'lightning.dev.component');
@@ -65,6 +66,15 @@ export default class LightningDevComponent extends SfCommand<ComponentPreviewRes
6566
const targetOrg = flags['target-org'];
6667
const apiVersion = flags['api-version'];
6768

69+
// Auto enable local dev
70+
if (process.env.AUTO_ENABLE_LOCAL_DEV === 'true') {
71+
try {
72+
await MetaUtils.ensureLightningPreviewEnabled(targetOrg.getConnection(undefined));
73+
} catch (error) {
74+
this.log('Error autoenabling local dev', error);
75+
}
76+
}
77+
6878
const { ldpServerId, ldpServerToken } = await PreviewUtils.initializePreviewConnection(targetOrg);
6979

7080
logger.debug('Determining the next available port for Local Dev Server');
@@ -154,8 +164,11 @@ export default class LightningDevComponent extends SfCommand<ComponentPreviewRes
154164
// Construct and log the full URL that will be opened
155165
const connection = targetOrg.getConnection(apiVersion);
156166

167+
// strip trailing slashes
168+
const instanceUrl = connection.instanceUrl.replace(/\/$/, '');
169+
157170
const previewUrl = PreviewUtils.generateComponentPreviewUrl(
158-
connection.instanceUrl,
171+
instanceUrl,
159172
ldpServerUrl,
160173
ldpServerId,
161174
componentName,
@@ -164,7 +177,7 @@ export default class LightningDevComponent extends SfCommand<ComponentPreviewRes
164177

165178
// Prepare the result for JSON output
166179
const result: ComponentPreviewResult = {
167-
instanceUrl: connection.instanceUrl,
180+
instanceUrl,
168181
ldpServerUrl,
169182
ldpServerId,
170183
componentName: componentName ?? '',

src/shared/metaUtils.ts

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/*
2+
* Copyright (c) 2024, salesforce.com, inc.
3+
* All rights reserved.
4+
* Licensed under the BSD 3-Clause license.
5+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
8+
import { Connection, Logger } from '@salesforce/core';
9+
10+
type LightningExperienceSettingsMetadata = {
11+
[key: string]: unknown;
12+
fullName?: string;
13+
enableLightningPreviewPref?: string | boolean;
14+
};
15+
16+
type MyDomainSettingsMetadata = {
17+
[key: string]: unknown;
18+
fullName?: string;
19+
isFirstPartyCookieUseRequired?: string | boolean;
20+
};
21+
22+
type MetadataUpdateResult = {
23+
success: boolean;
24+
fullName: string;
25+
errors?: Array<{ message: string }>;
26+
};
27+
28+
/**
29+
* Utility class for managing Salesforce metadata settings related to Lightning Development.
30+
*/
31+
export class MetaUtils {
32+
private static logger = Logger.childFromRoot('metaUtils');
33+
34+
/**
35+
* Retrieves the Lightning Experience Settings metadata from the org.
36+
*
37+
* @param connection the connection to the org
38+
* @returns LightningExperienceSettingsMetadata object containing the settings
39+
* @throws Error if unable to retrieve the metadata
40+
*/
41+
public static async getLightningExperienceSettings(
42+
connection: Connection
43+
): Promise<LightningExperienceSettingsMetadata> {
44+
this.logger.debug('Retrieving Lightning Experience Settings metadata');
45+
46+
const metadata = await connection.metadata.read('LightningExperienceSettings', 'enableLightningPreviewPref');
47+
48+
if (!metadata) {
49+
throw new Error('Unable to retrieve Lightning Experience Settings metadata.');
50+
}
51+
52+
if (Array.isArray(metadata)) {
53+
if (metadata.length === 0) {
54+
throw new Error('Lightning Experience Settings metadata response was empty.');
55+
}
56+
return metadata[0] as LightningExperienceSettingsMetadata;
57+
}
58+
59+
return metadata as LightningExperienceSettingsMetadata;
60+
}
61+
62+
/**
63+
* Checks if Lightning Preview (Local Dev) is enabled for the org.
64+
*
65+
* @param connection the connection to the org
66+
* @returns boolean indicating whether Lightning Preview is enabled
67+
*/
68+
public static async isLightningPreviewEnabled(connection: Connection): Promise<boolean> {
69+
try {
70+
const settings = await this.getLightningExperienceSettings(connection);
71+
const flagValue = settings.enableLightningPreviewPref ?? 'false';
72+
const enabled = String(flagValue).toLowerCase().trim() === 'true';
73+
this.logger.debug(`Lightning Preview enabled: ${enabled}`);
74+
return enabled;
75+
} catch (error) {
76+
this.logger.warn('Error checking Lightning Preview status, assuming disabled:', error);
77+
return false;
78+
}
79+
}
80+
81+
/**
82+
* Enables or disables Lightning Preview (Local Dev) for the org by updating the metadata.
83+
*
84+
* @param connection the connection to the org
85+
* @param enable boolean indicating whether to enable (true) or disable (false) Lightning Preview
86+
* @throws Error if the metadata update fails
87+
*/
88+
public static async setLightningPreviewEnabled(connection: Connection, enable: boolean): Promise<void> {
89+
this.logger.debug(`Setting Lightning Preview enabled to: ${enable}`);
90+
91+
const updateResult = await connection.metadata.update('LightningExperienceSettings', {
92+
fullName: 'enableLightningPreviewPref',
93+
enableLightningPreviewPref: enable ? 'true' : 'false',
94+
});
95+
96+
const results = Array.isArray(updateResult) ? updateResult : [updateResult];
97+
const typedResults = results as MetadataUpdateResult[];
98+
const errors = typedResults.filter((result) => !result.success);
99+
100+
if (errors.length > 0) {
101+
const message = errors
102+
.flatMap((result) => (Array.isArray(result.errors) ? result.errors : result.errors ? [result.errors] : []))
103+
.filter((error): error is { message: string } => Boolean(error))
104+
.map((error) => error.message)
105+
.join(' ');
106+
107+
throw new Error(message || 'Failed to update Lightning Preview setting.');
108+
}
109+
110+
this.logger.debug('Successfully updated Lightning Preview setting');
111+
}
112+
113+
/**
114+
* Retrieves the My Domain Settings metadata from the org.
115+
*
116+
* @param connection the connection to the org
117+
* @returns MyDomainSettingsMetadata object containing the settings
118+
* @throws Error if unable to retrieve the metadata
119+
*/
120+
public static async getMyDomainSettings(connection: Connection): Promise<MyDomainSettingsMetadata> {
121+
this.logger.debug('Retrieving My Domain Settings metadata');
122+
123+
const metadata = await connection.metadata.read('MyDomainSettings', 'MyDomain');
124+
125+
if (!metadata) {
126+
throw new Error('Unable to retrieve My Domain settings metadata.');
127+
}
128+
129+
if (Array.isArray(metadata)) {
130+
if (metadata.length === 0) {
131+
throw new Error('My Domain settings metadata response was empty.');
132+
}
133+
return metadata[0] as MyDomainSettingsMetadata;
134+
}
135+
136+
return metadata as MyDomainSettingsMetadata;
137+
}
138+
139+
/**
140+
* Checks if first-party cookies are required for the org.
141+
*
142+
* @param connection the connection to the org
143+
* @returns boolean indicating whether first-party cookies are required
144+
*/
145+
public static async isFirstPartyCookieRequired(connection: Connection): Promise<boolean> {
146+
try {
147+
const settings = await this.getMyDomainSettings(connection);
148+
const flagValue = settings.isFirstPartyCookieUseRequired ?? 'false';
149+
const required = String(flagValue).toLowerCase().trim() === 'true';
150+
this.logger.debug(`First-party cookie required: ${required}`);
151+
return required;
152+
} catch (error) {
153+
this.logger.warn('Error checking first-party cookie requirement, assuming not required:', error);
154+
return false;
155+
}
156+
}
157+
158+
/**
159+
* Updates the My Domain setting that controls whether first-party cookies are required.
160+
*
161+
* @param connection the connection to the org
162+
* @param requireFirstPartyCookies boolean indicating whether to require first-party cookies
163+
* @throws Error if the metadata update fails
164+
*/
165+
public static async setMyDomainFirstPartyCookieRequirement(
166+
connection: Connection,
167+
requireFirstPartyCookies: boolean
168+
): Promise<void> {
169+
this.logger.debug(`Setting first-party cookie requirement to: ${requireFirstPartyCookies}`);
170+
171+
const updateResult = await connection.metadata.update('MyDomainSettings', {
172+
fullName: 'MyDomain',
173+
isFirstPartyCookieUseRequired: requireFirstPartyCookies ? 'true' : 'false',
174+
});
175+
176+
const results = Array.isArray(updateResult) ? updateResult : [updateResult];
177+
const typedResults = results as MetadataUpdateResult[];
178+
const errors = typedResults.filter((result) => !result.success);
179+
180+
if (errors.length > 0) {
181+
const message = errors
182+
.flatMap((result) => (Array.isArray(result.errors) ? result.errors : result.errors ? [result.errors] : []))
183+
.filter((error): error is { message: string } => Boolean(error))
184+
.map((error) => error.message)
185+
.join(' ');
186+
187+
throw new Error(message || 'Failed to update My Domain first-party cookie requirement.');
188+
}
189+
190+
this.logger.debug('Successfully updated first-party cookie requirement');
191+
}
192+
193+
/**
194+
* Ensures Lightning Preview is enabled for the org. If it's not enabled, this method will enable it.
195+
*
196+
* @param connection the connection to the org
197+
* @returns boolean indicating whether Lightning Preview was already enabled (true) or had to be enabled (false)
198+
*/
199+
public static async ensureLightningPreviewEnabled(connection: Connection): Promise<boolean> {
200+
const isEnabled = await this.isLightningPreviewEnabled(connection);
201+
202+
if (!isEnabled) {
203+
this.logger.info('Lightning Preview is not enabled. Enabling it now...');
204+
await this.setLightningPreviewEnabled(connection, true);
205+
return false;
206+
}
207+
208+
this.logger.debug('Lightning Preview is already enabled');
209+
return true;
210+
}
211+
212+
/**
213+
* Ensures first-party cookies are not required for the org. If they are required, this method will disable the requirement.
214+
*
215+
* @param connection the connection to the org
216+
* @returns boolean indicating whether first-party cookies were already not required (true) or had to be disabled (false)
217+
*/
218+
public static async ensureFirstPartyCookiesNotRequired(connection: Connection): Promise<boolean> {
219+
const isRequired = await this.isFirstPartyCookieRequired(connection);
220+
221+
if (isRequired) {
222+
this.logger.info('First-party cookies are required. Disabling requirement...');
223+
await this.setMyDomainFirstPartyCookieRequirement(connection, false);
224+
return false;
225+
}
226+
227+
this.logger.debug('First-party cookies are not required');
228+
return true;
229+
}
230+
}

0 commit comments

Comments
 (0)