UNPKG

23.4 kBJavaScriptView Raw
1"use strict";
2/*
3 * Copyright (c) 2020, 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 */
8Object.defineProperty(exports, "__esModule", { value: true });
9exports.SfdxConfig = exports.Config = exports.SfProperty = exports.SFDX_ALLOWED_PROPERTIES = exports.SfdxPropertyKeys = exports.SF_ALLOWED_PROPERTIES = exports.SfConfigProperties = void 0;
10const path_1 = require("path");
11const fs = require("fs");
12const kit_1 = require("@salesforce/kit");
13const ts_types_1 = require("@salesforce/ts-types");
14const global_1 = require("../global");
15const logger_1 = require("../logger");
16const messages_1 = require("../messages");
17const sfdc_1 = require("../util/sfdc");
18const sfdcUrl_1 = require("../util/sfdcUrl");
19const orgConfigProperties_1 = require("../org/orgConfigProperties");
20const configFile_1 = require("./configFile");
21messages_1.Messages.importMessagesDirectory(__dirname);
22const messages = messages_1.Messages.load('@salesforce/core', 'config', [
23 'deprecatedConfigKey',
24 'invalidApiVersion',
25 'invalidBooleanConfigValue',
26 'invalidConfigValue',
27 'invalidInstanceUrl',
28 'invalidIsvDebuggerSid',
29 'invalidIsvDebuggerUrl',
30 'invalidNumberConfigValue',
31 'invalidWrite',
32 'unknownConfigKey',
33 'defaultUsername',
34 'defaultDevHubUsername',
35 'isvDebuggerSid',
36 'isvDebuggerUrl',
37 'apiVersion',
38 'disableTelemetry',
39 'maxQueryLimit',
40 'restDeploy',
41 'instanceUrl',
42 'disable-telemetry',
43 'customOrgMetadataTemplates',
44]);
45const SFDX_CONFIG_FILE_NAME = 'sfdx-config.json';
46const CONFIG_FILE_NAME = 'config.json';
47var SfConfigProperties;
48(function (SfConfigProperties) {
49 /**
50 * Disables telemetry reporting
51 */
52 SfConfigProperties["DISABLE_TELEMETRY"] = "disable-telemetry";
53})(SfConfigProperties = exports.SfConfigProperties || (exports.SfConfigProperties = {}));
54exports.SF_ALLOWED_PROPERTIES = [
55 {
56 key: SfConfigProperties.DISABLE_TELEMETRY,
57 description: messages.getMessage(SfConfigProperties.DISABLE_TELEMETRY),
58 input: {
59 validator: (value) => value == null || ['true', 'false'].includes(value.toString()),
60 failedMessage: messages.getMessage('invalidBooleanConfigValue'),
61 },
62 },
63];
64var SfdxPropertyKeys;
65(function (SfdxPropertyKeys) {
66 /**
67 * Username associated with the default dev hub org.
68 *
69 * @deprecated Replaced by OrgConfigProperties.TARGET_DEV_HUB in v3 {@link https://github.com/forcedotcom/sfdx-core/blob/v3/MIGRATING_V2-V3.md#config}
70 * will remain in v3 for the foreseeable future so that `sfdx-core` can map between `sf` and `sfdx` config values
71 */
72 SfdxPropertyKeys["DEFAULT_DEV_HUB_USERNAME"] = "defaultdevhubusername";
73 /**
74 * Username associate with the default org.
75 *
76 * @deprecated Replaced by OrgConfigProperties.TARGET_ORG in v3 {@link https://github.com/forcedotcom/sfdx-core/blob/v3/MIGRATING_V2-V3.md#config}
77 * will remain in v3 for the foreseeable future so that `sfdx-core` can map between `sf` and `sfdx` config values
78 */
79 SfdxPropertyKeys["DEFAULT_USERNAME"] = "defaultusername";
80 /**
81 * The sid for the debugger configuration.
82 *
83 * @deprecated Replaced by OrgConfigProperties.ORG_ISV_DEBUGGER_SID in v3 {@link https://github.com/forcedotcom/sfdx-core/blob/v3/MIGRATING_V2-V3.md#config}
84 */
85 SfdxPropertyKeys["ISV_DEBUGGER_SID"] = "isvDebuggerSid";
86 /**
87 * The url for the debugger configuration.
88 *
89 * @deprecated Replaced by OrgConfigProperties.ORG_ISV_DEBUGGER_URL in v3 {@link https://github.com/forcedotcom/sfdx-core/blob/v3/MIGRATING_V2-V3.md#config}
90 */
91 SfdxPropertyKeys["ISV_DEBUGGER_URL"] = "isvDebuggerUrl";
92 /**
93 * The api version
94 *
95 * @deprecated Replaced by OrgConfigProperties.ORG_API_VERSION in v3 {@link https://github.com/forcedotcom/sfdx-core/blob/v3/MIGRATING_V2-V3.md#config}
96 */
97 SfdxPropertyKeys["API_VERSION"] = "apiVersion";
98 /**
99 * Disables telemetry reporting
100 *
101 * @deprecated Replaced by SfPropertyKeys.DISABLE_TELEMETRY in v3 {@link https://github.com/forcedotcom/sfdx-core/blob/v3/MIGRATING_V2-V3.md#config}
102 */
103 SfdxPropertyKeys["DISABLE_TELEMETRY"] = "disableTelemetry";
104 /**
105 * Custom templates repo or local location.
106 *
107 * @deprecated Replaced by OrgConfigProperties.ORG_CUSTOM_METADATA_TEMPLATES in v3 {@link https://github.com/forcedotcom/sfdx-core/blob/v3/MIGRATING_V2-V3.md#config}
108 */
109 SfdxPropertyKeys["CUSTOM_ORG_METADATA_TEMPLATES"] = "customOrgMetadataTemplates";
110 /**
111 * allows users to override the 10,000 result query limit
112 *
113 * @deprecated Replaced by OrgConfigProperties.ORG_MAX_QUERY_LIMIT in v3 {@link https://github.com/forcedotcom/sfdx-core/blob/v3/MIGRATING_V2-V3.md#config}
114 */
115 SfdxPropertyKeys["MAX_QUERY_LIMIT"] = "maxQueryLimit";
116 /**
117 * @deprecated
118 */
119 SfdxPropertyKeys["REST_DEPLOY"] = "restDeploy";
120 /**
121 * @deprecated Replaced by OrgConfigProperties.ORG_INSTANCE_URL in v3 {@link https://github.com/forcedotcom/sfdx-core/blob/v3/MIGRATING_V2-V3.md#config}
122 */
123 SfdxPropertyKeys["INSTANCE_URL"] = "instanceUrl";
124})(SfdxPropertyKeys = exports.SfdxPropertyKeys || (exports.SfdxPropertyKeys = {}));
125exports.SFDX_ALLOWED_PROPERTIES = [
126 {
127 key: SfdxPropertyKeys.INSTANCE_URL,
128 description: messages.getMessage(SfdxPropertyKeys.INSTANCE_URL),
129 newKey: orgConfigProperties_1.OrgConfigProperties.ORG_INSTANCE_URL,
130 deprecated: true,
131 input: {
132 // If a value is provided validate it otherwise no value is unset.
133 validator: (value) => {
134 if (value == null)
135 return true;
136 // validate if the value is a string and is a valid url and is either a salesforce domain
137 // or an internal url.
138 return ((0, ts_types_1.isString)(value) &&
139 sfdcUrl_1.SfdcUrl.isValidUrl(value) &&
140 (new sfdcUrl_1.SfdcUrl(value).isSalesforceDomain() || new sfdcUrl_1.SfdcUrl(value).isInternalUrl()));
141 },
142 failedMessage: messages.getMessage('invalidInstanceUrl'),
143 },
144 },
145 {
146 key: SfdxPropertyKeys.API_VERSION,
147 newKey: orgConfigProperties_1.OrgConfigProperties.ORG_API_VERSION,
148 deprecated: true,
149 description: messages.getMessage(SfdxPropertyKeys.API_VERSION),
150 hidden: true,
151 input: {
152 // If a value is provided validate it otherwise no value is unset.
153 validator: (value) => value == null || ((0, ts_types_1.isString)(value) && (0, sfdc_1.validateApiVersion)(value)),
154 failedMessage: messages.getMessage('invalidApiVersion'),
155 },
156 },
157 {
158 // will remain in v3 for the foreseeable future so that `sfdx-core` can map between `sf` and `sfdx` config values
159 key: SfdxPropertyKeys.DEFAULT_DEV_HUB_USERNAME,
160 newKey: orgConfigProperties_1.OrgConfigProperties.TARGET_DEV_HUB,
161 deprecated: true,
162 description: messages.getMessage('defaultDevHubUsername'),
163 },
164 {
165 // will remain in v3 for the foreseeable future so that `sfdx-core` can map between `sf` and `sfdx` config values
166 key: SfdxPropertyKeys.DEFAULT_USERNAME,
167 newKey: orgConfigProperties_1.OrgConfigProperties.TARGET_ORG,
168 deprecated: true,
169 description: messages.getMessage('defaultUsername'),
170 },
171 {
172 key: SfdxPropertyKeys.ISV_DEBUGGER_SID,
173 newKey: orgConfigProperties_1.OrgConfigProperties.ORG_ISV_DEBUGGER_SID,
174 deprecated: true,
175 description: messages.getMessage(SfdxPropertyKeys.ISV_DEBUGGER_SID),
176 encrypted: true,
177 input: {
178 // If a value is provided validate it otherwise no value is unset.
179 validator: (value) => value == null || (0, ts_types_1.isString)(value),
180 failedMessage: messages.getMessage('invalidIsvDebuggerSid'),
181 },
182 },
183 {
184 key: SfdxPropertyKeys.ISV_DEBUGGER_URL,
185 newKey: orgConfigProperties_1.OrgConfigProperties.ORG_ISV_DEBUGGER_URL,
186 deprecated: true,
187 description: messages.getMessage(SfdxPropertyKeys.ISV_DEBUGGER_URL),
188 input: {
189 // If a value is provided validate it otherwise no value is unset.
190 validator: (value) => value == null || (0, ts_types_1.isString)(value),
191 failedMessage: messages.getMessage('invalidIsvDebuggerUrl'),
192 },
193 },
194 {
195 key: SfdxPropertyKeys.DISABLE_TELEMETRY,
196 newKey: SfConfigProperties.DISABLE_TELEMETRY,
197 deprecated: true,
198 description: messages.getMessage(SfdxPropertyKeys.DISABLE_TELEMETRY),
199 input: {
200 validator: (value) => value == null || ['true', 'false'].includes(value.toString()),
201 failedMessage: messages.getMessage('invalidBooleanConfigValue'),
202 },
203 },
204 {
205 key: SfdxPropertyKeys.CUSTOM_ORG_METADATA_TEMPLATES,
206 newKey: orgConfigProperties_1.OrgConfigProperties.ORG_CUSTOM_METADATA_TEMPLATES,
207 deprecated: true,
208 description: messages.getMessage(SfdxPropertyKeys.CUSTOM_ORG_METADATA_TEMPLATES),
209 },
210 {
211 key: SfdxPropertyKeys.REST_DEPLOY,
212 description: messages.getMessage(SfdxPropertyKeys.REST_DEPLOY),
213 hidden: true,
214 newKey: 'org-metadata-rest-deploy',
215 deprecated: true,
216 input: {
217 validator: (value) => value != null && ['true', 'false'].includes(value.toString()),
218 failedMessage: messages.getMessage('invalidBooleanConfigValue'),
219 },
220 },
221 {
222 key: SfdxPropertyKeys.MAX_QUERY_LIMIT,
223 description: messages.getMessage(SfdxPropertyKeys.MAX_QUERY_LIMIT),
224 hidden: true,
225 newKey: orgConfigProperties_1.OrgConfigProperties.ORG_MAX_QUERY_LIMIT,
226 deprecated: true,
227 input: {
228 // the bit shift will remove the negative bit, and any decimal numbers
229 // then the parseFloat will handle converting it to a number from a string
230 validator: (value) => value >>> 0 === parseFloat(value) && value > 0,
231 failedMessage: messages.getMessage('invalidNumberConfigValue'),
232 },
233 },
234];
235// Generic global config properties. Specific properties can be loaded like orgConfigProperties.ts.
236exports.SfProperty = {};
237/**
238 * The files where sfdx config values are stored for projects and the global space.
239 *
240 * *Note:* It is not recommended to instantiate this object directly when resolving
241 * config values. Instead use {@link ConfigAggregator}
242 *
243 * ```
244 * const localConfig = await Config.create();
245 * localConfig.set('target-org', 'username@company.org');
246 * await localConfig.write();
247 * ```
248 * https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_cli_config_values.htm
249 */
250class Config extends configFile_1.ConfigFile {
251 constructor(options) {
252 super(Object.assign({
253 isGlobal: false,
254 }, options ?? {}, {
255 // Don't let consumers of config override this. If they really really want to,
256 // they can extend this class.
257 isState: true,
258 filename: Config.getFileName(),
259 stateFolder: global_1.Global.SF_STATE_FOLDER,
260 }));
261 // Resolve the config path on creation.
262 this.getPath();
263 this.sfdxConfig = new SfdxConfig(this.options, this);
264 }
265 /**
266 * Returns the default file name for a config file.
267 *
268 * **See** {@link CONFIG_FILE_NAME}
269 */
270 static getFileName() {
271 return CONFIG_FILE_NAME;
272 }
273 /**
274 * Returns an array of objects representing the allowed config properties.
275 */
276 static getAllowedProperties() {
277 return Config.allowedProperties;
278 }
279 /**
280 * Add an array of allowed config properties.
281 *
282 * @param metas Array of objects to set as the allowed config properties.
283 */
284 static addAllowedProperties(metas) {
285 const currentMetaKeys = Object.keys(Config.propertyConfigMap());
286 // If logger is needed elsewhere in this file, do not move this outside of the Config class.
287 // It was causing issues with Bunyan log rotation. See https://github.com/forcedotcom/sfdx-core/pull/562
288 const logger = logger_1.Logger.childFromRoot('core:config');
289 metas.forEach((meta) => {
290 if (currentMetaKeys.includes(meta.key)) {
291 logger.info(`Key ${meta.key} already exists in allowedProperties, skipping.`);
292 return;
293 }
294 Config.allowedProperties.push(meta);
295 });
296 }
297 /**
298 * The value of a supported config property.
299 *
300 * @param isGlobal True for a global config. False for a local config.
301 * @param propertyName The name of the property to set.
302 * @param value The property value.
303 */
304 static async update(isGlobal, propertyName, value) {
305 const config = await Config.create({ isGlobal });
306 const content = await config.read();
307 if (value == null) {
308 delete content[propertyName];
309 }
310 else {
311 (0, kit_1.set)(content, propertyName, value);
312 }
313 return config.write(content);
314 }
315 /**
316 * Clear all the configured properties both local and global.
317 */
318 static async clear() {
319 const globalConfig = await Config.create({ isGlobal: true });
320 globalConfig.clear();
321 await globalConfig.write();
322 const localConfig = await Config.create();
323 localConfig.clear();
324 await localConfig.write();
325 }
326 static getPropertyConfigMeta(propertyName) {
327 const prop = Config.propertyConfigMap()[propertyName];
328 if (prop?.deprecated && prop?.newKey) {
329 return Config.propertyConfigMap()[prop.newKey];
330 }
331 return prop;
332 }
333 static propertyConfigMap() {
334 return (0, kit_1.keyBy)(Config.allowedProperties, 'key');
335 }
336 /**
337 * Read, assign, and return the config contents.
338 */
339 async read(force = true) {
340 try {
341 const config = await super.read(false, force);
342 // Merge .sfdx/sfdx-config.json and .sf/config.json
343 const merged = this.sfdxConfig.merge(config);
344 this.setContents(merged);
345 await this.cryptProperties(false);
346 return this.getContents();
347 }
348 finally {
349 await this.clearCrypto();
350 }
351 }
352 readSync(force = true) {
353 const config = super.readSync(false, force);
354 // Merge .sfdx/sfdx-config.json and .sf/config.json
355 const merged = this.sfdxConfig.merge(config);
356 this.setContents(merged);
357 return this.getContents();
358 }
359 /**
360 * Writes Config properties taking into account encrypted properties.
361 *
362 * @param newContents The new Config value to persist.
363 */
364 async write(newContents) {
365 if (newContents != null) {
366 this.setContents(newContents);
367 }
368 await this.cryptProperties(true);
369 await super.write();
370 if (global_1.Global.SFDX_INTEROPERABILITY)
371 await this.sfdxConfig.write();
372 await this.cryptProperties(false);
373 return this.getContents();
374 }
375 /**
376 * DO NOT CALL - The config file needs to encrypt values which can only be done asynchronously.
377 * Call {@link SfdxConfig.write} instead.
378 *
379 * **Throws** *{@link SfError}{ name: 'InvalidWriteError' }* Always.
380 *
381 * @param newContents Contents to write
382 */
383 // eslint-disable-next-line @typescript-eslint/no-unused-vars, class-methods-use-this
384 writeSync(newContents) {
385 throw messages.createError('invalidWrite');
386 }
387 /**
388 * Sets a value for a property.
389 *
390 * **Throws** *{@link SfError}{ name: 'UnknownConfigKeyError' }* An attempt to get a property that's not supported.
391 * **Throws** *{@link SfError}{ name: 'InvalidConfigValueError' }* If the input validator fails.
392 *
393 * @param key The property to set.
394 * @param value The value of the property.
395 */
396 set(key, value) {
397 const property = Config.allowedProperties.find((allowedProp) => allowedProp.key === key);
398 if (!property) {
399 throw messages.createError('unknownConfigKey', [key]);
400 }
401 if (property.deprecated && property.newKey) {
402 throw messages.createError('deprecatedConfigKey', [key, property.newKey]);
403 }
404 if (property.input) {
405 if (property.input?.validator(value)) {
406 super.set(property.key, value);
407 }
408 else {
409 let valueError = value?.toString() ?? '';
410 if (property.input.failedMessage) {
411 valueError = (0, ts_types_1.isString)(property.input.failedMessage)
412 ? property.input.failedMessage
413 : property.input.failedMessage(value);
414 }
415 throw messages.createError('invalidConfigValue', [valueError]);
416 }
417 }
418 else {
419 super.set(property.key, value);
420 }
421 return this.getContents();
422 }
423 /**
424 * Unsets a value for a property.
425 *
426 * **Throws** *{@link SfError}{ name: 'UnknownConfigKeyError' }* If the input validator fails.
427 *
428 * @param key The property to unset.
429 */
430 unset(key) {
431 const property = Config.allowedProperties.find((allowedProp) => allowedProp.key === key);
432 if (!property) {
433 throw messages.createError('unknownConfigKey', [key]);
434 }
435 if (property.deprecated && property.newKey) {
436 throw messages.createError('deprecatedConfigKey', [key, property.newKey]);
437 }
438 return super.unset(property.key);
439 }
440 /**
441 * Get an individual property config.
442 *
443 * **Throws** *{@link SfError}{ name: 'UnknownConfigKeyError' }* An attempt to get a property that's not supported.
444 *
445 * @param propertyName The name of the property.
446 */
447 // eslint-disable-next-line class-methods-use-this
448 getPropertyConfig(propertyName) {
449 const prop = Config.propertyConfigMap()[propertyName];
450 if (!prop) {
451 throw messages.createError('unknownConfigKey', [propertyName]);
452 }
453 return prop;
454 }
455 /**
456 * Initializer for supported config types.
457 */
458 async init() {
459 // Super ConfigFile calls read, which has a dependency on crypto, which finally has a dependency on
460 // Config.propertyConfigMap being set. This is why init is called after the setup.
461 await super.init();
462 }
463 /**
464 * Encrypts and content properties that have a encryption attribute.
465 *
466 * @param encrypt `true` to encrypt.
467 */
468 async cryptProperties(encrypt) {
469 const hasEncryptedProperties = this.entries().some(([key]) => !!Config.propertyConfigMap()[key]?.encrypted);
470 if (hasEncryptedProperties) {
471 await this.initCrypto();
472 const crypto = (0, ts_types_1.ensure)(this.crypto);
473 this.forEach((key, value) => {
474 if (this.getPropertyConfig(key).encrypted && (0, ts_types_1.isString)(value)) {
475 this.set(key, (0, ts_types_1.ensure)(encrypt ? crypto.encrypt(value) : crypto.decrypt(value)));
476 }
477 });
478 }
479 }
480}
481exports.Config = Config;
482Config.allowedProperties = [
483 ...exports.SFDX_ALLOWED_PROPERTIES,
484 ...exports.SF_ALLOWED_PROPERTIES,
485 ...orgConfigProperties_1.ORG_CONFIG_ALLOWED_PROPERTIES,
486];
487class SfdxConfig {
488 constructor(options = {}, config) {
489 this.options = options;
490 this.config = config;
491 this.sfdxPath = this.getSfdxPath();
492 }
493 /**
494 * If Global.SFDX_INTEROPERABILITY is enabled, merge the sfdx config into the sf config
495 */
496 merge(config) {
497 if (!global_1.Global.SFDX_INTEROPERABILITY)
498 return config;
499 const sfdxConfig = this.readSync();
500 const sfdxPropKeys = Object.values(SfdxPropertyKeys);
501 // Get a list of config keys that are NOT provided by SfdxPropertyKeys
502 const nonSfdxPropKeys = Config.getAllowedProperties()
503 .filter((p) => !sfdxPropKeys.includes(p.key))
504 .map((p) => p.key);
505 // Remove any config from .sf that isn't also in .sfdx
506 // This handles the scenario where a config has been deleted
507 // from .sfdx and we want to mirror that change in .sf
508 for (const key of nonSfdxPropKeys) {
509 if (!sfdxConfig[key])
510 delete config[key];
511 }
512 return Object.assign(config, sfdxConfig);
513 }
514 async write(config = this.config.toObject()) {
515 try {
516 const translated = this.translate(config, 'toOld');
517 const sfdxPath = this.getSfdxPath();
518 await fs.promises.mkdir((0, path_1.dirname)(sfdxPath), { recursive: true });
519 await fs.promises.writeFile(sfdxPath, JSON.stringify(translated, null, 2));
520 }
521 catch (error) {
522 /* Do nothing */
523 }
524 }
525 readSync() {
526 try {
527 const contents = (0, kit_1.parseJsonMap)(fs.readFileSync(this.getSfdxPath(), 'utf8'));
528 return this.translate(contents, 'toNew');
529 }
530 catch (error) {
531 /* Do nothing */
532 return {};
533 }
534 }
535 getSfdxPath() {
536 if (!this.sfdxPath) {
537 const stateFolder = global_1.Global.SFDX_STATE_FOLDER;
538 const fileName = SFDX_CONFIG_FILE_NAME;
539 // Don't let users store config files in homedir without being in the state folder.
540 let configRootFolder = this.options.rootFolder
541 ? this.options.rootFolder
542 : configFile_1.ConfigFile.resolveRootFolderSync(!!this.options.isGlobal);
543 if (this.options.isGlobal === true || this.options.isState === true) {
544 configRootFolder = (0, path_1.join)(configRootFolder, stateFolder);
545 }
546 this.sfdxPath = (0, path_1.join)(configRootFolder, fileName);
547 }
548 return this.sfdxPath;
549 }
550 /**
551 * If toNew is specified: migrate all deprecated configs with a newKey to the newKey.
552 * - For example, defaultusername will be renamed to target-org.
553 *
554 * If toOld is specified: migrate all deprecated configs back to their original key.
555 * - For example, target-org will be renamed to defaultusername.
556 */
557 translate(contents, direction) {
558 const translated = {};
559 for (const [key, value] of Object.entries(contents)) {
560 const propConfig = direction === 'toNew'
561 ? this.config.getPropertyConfig(key)
562 : Config.getAllowedProperties().find((c) => c.newKey === key) ?? {};
563 if (propConfig.deprecated && propConfig.newKey) {
564 const normalizedKey = direction === 'toNew' ? propConfig.newKey : propConfig.key;
565 translated[normalizedKey] = value;
566 }
567 else {
568 translated[key] = value;
569 }
570 }
571 return translated;
572 }
573}
574exports.SfdxConfig = SfdxConfig;
575//# sourceMappingURL=config.js.map
\No newline at end of file