UNPKG

@salesforce/source-deploy-retrieve

Version:

JavaScript library to run Salesforce metadata deploys and retrieves

627 lines 35.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.simpleKey = exports.ComponentSet = void 0; /* * Copyright (c) 2020, salesforce.com, inc. * All rights reserved. * Licensed under the BSD 3-Clause license. * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ /* eslint @typescript-eslint/unified-signatures:0 */ const fast_xml_parser_1 = require("fast-xml-parser"); const core_1 = require("@salesforce/core"); const ts_types_1 = require("@salesforce/ts-types"); const decomposed_1 = require("../utils/decomposed"); const metadataApiDeploy_1 = require("../client/metadataApiDeploy"); const metadataApiRetrieve_1 = require("../client/metadataApiRetrieve"); const constants_1 = require("../common/constants"); const sourceComponent_1 = require("../resolve/sourceComponent"); const metadataResolver_1 = require("../resolve/metadataResolver"); const connectionResolver_1 = require("../resolve/connectionResolver"); const manifestResolver_1 = require("../resolve/manifestResolver"); const registryAccess_1 = require("../registry/registryAccess"); const coverage_1 = require("../registry/coverage"); const types_1 = require("./types"); const lazyCollection_1 = require("./lazyCollection"); const decodeableMap_1 = require("./decodeableMap"); ; const messages = new core_1.Messages('@salesforce/source-deploy-retrieve', 'sdr', new Map([["md_request_fail", "Metadata API request failed: %s"], ["error_convert_invalid_format", "Invalid conversion format '%s'"], ["error_could_not_infer_type", "%s: Could not infer a metadata type"], ["error_unexpected_child_type", "Unexpected child metadata [%s] found for parent type [%s]"], ["noParent", "Could not find parent type for %s (%s)"], ["error_expected_source_files", "%s: Expected source files for type '%s'"], ["error_failed_convert", "Component conversion failed: %s"], ["error_merge_metadata_target_unsupported", "Merge convert for metadata target format currently unsupported"], ["error_missing_adapter", "Missing adapter '%s' for metadata type '%s'"], ["error_missing_transformer", "Missing transformer '%s' for metadata type '%s'"], ["error_missing_type_definition", "Missing metadata type definition in registry for id '%s'."], ["error_missing_child_type_definition", "Type %s does not have a child type definition %s."], ["noChildTypes", "No child types found in registry for %s (reading %s at %s)"], ["error_no_metadata_xml_ignore", "Metadata xml file %s is forceignored but is required for %s."], ["noSourceIgnore", "%s metadata types require source files, but %s is forceignored."], ["noSourceIgnore.actions", "- Metadata types with content are composed of two files: a content file (ie MyApexClass.cls) and a -meta.xml file (i.e MyApexClass.cls-meta.xml). You must include both files in your .forceignore file. Or try appending \u201C\\*\u201D to your existing .forceignore entry.\n\nSee <https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_exclude_source.htm> for examples"], ["error_path_not_found", "%s: File or folder not found"], ["noContentFound", "SourceComponent %s (metadata type = %s) is missing its content file."], ["noContentFound.actions", ["Ensure the content file exists in the expected location.", "If the content file is in your .forceignore file, ensure the meta-xml file is also ignored to completely exclude it."]], ["error_parsing_xml", "SourceComponent %s (metadata type = %s) does not have an associated metadata xml to parse"], ["error_expected_file_path", "%s: path is to a directory, expected a file"], ["error_expected_directory_path", "%s: path is to a file, expected a directory"], ["error_directory_not_found_or_not_directory", "%s: path is not a directory"], ["error_no_directory_stream", "%s doesn't support readable streams on directories."], ["error_no_source_to_deploy", "No source-backed components present in the package."], ["error_no_components_to_retrieve", "No components in the package to retrieve."], ["error_static_resource_expected_archive_type", "A StaticResource directory must have a content type of application/zip or application/jar - found %s for %s."], ["error_static_resource_missing_resource_file", "A StaticResource must have an associated .resource file, missing %s.resource-meta.xml"], ["error_no_job_id", "The %s operation is missing a job ID. Initialize an operation with an ID, or start a new job."], ["missingApiVersion", "Could not determine an API version to use for the generated manifest. Tried looking for sourceApiVersion in sfdx-project.json, apiVersion from config vars, and the highest apiVersion from the APEX REST endpoint. Using API version 58.0 as a last resort."], ["invalid_xml_parsing", "error parsing %s due to:\\n message: %s\\n line: %s\\n code: %s"], ["zipBufferError", "Zip buffer was not created during conversion"], ["undefinedComponentSet", "Unable to construct a componentSet. Check the logs for more information."], ["replacementsFileNotRead", "The file \"%s\" specified in the \"replacements\" property of sfdx-project.json could not be read."], ["unsupportedBundleType", "Unsupported Bundle Type: %s"], ["filePathGeneratorNoTypeSupport", "Type not supported for filepath generation: %s"], ["missingFolderType", "The registry has %s as is inFolder but it does not have a folderType"], ["tooManyFiles", "Multiple files found for path: %s."], ["cantGetName", "Unable to calculate fullName from path: %s (%s)"], ["missingMetaFileSuffix", "The metadata registry is configured incorrectly for %s. Expected a metaFileSuffix."], ["uniqueIdElementNotInRegistry", "No uniqueIdElement found in registry for %s (reading %s at %s)."], ["uniqueIdElementNotInChild", "The uniqueIdElement %s was not found the child (reading %s at %s)."], ["suggest_type_header", "A metadata type lookup for \"%s\" found the following close matches:"], ["suggest_type_did_you_mean", "-- Did you mean \".%s%s\" instead for the \"%s\" metadata type?"], ["suggest_type_more_suggestions", "Additional suggestions:\nConfirm the file name, extension, and directory names are correct. Validate against the registry at:\n<https://github.com/forcedotcom/source-deploy-retrieve/blob/main/src/registry/metadataRegistry.json>\n\nIf the type is not listed in the registry, check that it has Metadata API support via the Metadata Coverage Report:\n<https://developer.salesforce.com/docs/metadata-coverage>\n\nIf the type is available via Metadata API but not in the registry\n\n- Open an issue <https://github.com/forcedotcom/cli/issues>\n- Add the type via PR. Instructions: <https://github.com/forcedotcom/source-deploy-retrieve/blob/main/contributing/metadata.md>"], ["type_name_suggestions", "Confirm the metadata type name is correct. Validate against the registry at:\n<https://github.com/forcedotcom/source-deploy-retrieve/blob/main/src/registry/metadataRegistry.json>\n\nIf the type is not listed in the registry, check that it has Metadata API support via the Metadata Coverage Report:\n<https://developer.salesforce.com/docs/metadata-coverage>\n\nIf the type is available via Metadata API but not in the registry\n\n- Open an issue <https://github.com/forcedotcom/cli/issues>\n- Add the type via PR. Instructions: <https://github.com/forcedotcom/source-deploy-retrieve/blob/main/contributing/metadata.md>"]])); const KEY_DELIMITER = '#'; /** * A collection containing no duplicate metadata members (`fullName` and `type` pairs). `ComponentSets` * are a convenient way of constructing a unique collection of components to perform operations such as * deploying and retrieving. * * Multiple {@link SourceComponent}s can be present in the set and correspond to the same member. * This is typically the case when a component's source files are split across locations. For an example, see * the [multiple package directories](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_ws_mpd.htm) * scenario. */ class ComponentSet extends lazyCollection_1.LazyCollection { static WILDCARD = '*'; /** * The metadata API version to use. E.g., 52.0 */ apiVersion; /** * The metadata API version of the deployed/retrieved source. * This is used as the value for the `version` field in the manifest. */ sourceApiVersion; /** * Used to explicitly set the project directory for the component set. * When not present, sfdx-core's SfProject will use the current working directory. */ projectDirectory; fullName; forceIgnoredPaths; logger; registry; // all components stored here, regardless of what manifest they belong to components = new decodeableMap_1.DecodeableMap(); // whether this component set is being used for a deploy // @ts-expect-error this is currently not used but could be used in the future forDeploy = false; // whether this component set is being used for a retrieve forRetrieve = false; // internal component maps used by this.getObject() when building manifests. destructiveComponents = { [types_1.DestructiveChangesType.PRE]: new decodeableMap_1.DecodeableMap(), [types_1.DestructiveChangesType.POST]: new decodeableMap_1.DecodeableMap(), }; // used to store components meant for a "constructive" (not destructive) manifest manifestComponents = new decodeableMap_1.DecodeableMap(); destructiveChangesType = types_1.DestructiveChangesType.POST; constructor(components = [], registry = new registryAccess_1.RegistryAccess()) { super(); this.registry = registry; this.logger = core_1.Logger.childFromRoot(this.constructor.name); for (const component of components) { const destructiveType = component instanceof sourceComponent_1.SourceComponent ? component.getDestructiveChangesType() : this.destructiveChangesType; this.add(component, destructiveType); } } /** * Each {@link SourceComponent} counts as an element in the set, even if multiple * ones map to the same `fullName` and `type` pair. * * @returns number of metadata components in the set */ get size() { let size = 0; for (const collection of this.components.values()) { // just having an entry in the parent map counts as 1 size += collection.size === 0 ? 1 : collection.size; } return size; } get destructiveChangesPre() { return this.destructiveComponents[types_1.DestructiveChangesType.PRE]; } get destructiveChangesPost() { return this.destructiveComponents[types_1.DestructiveChangesType.POST]; } static fromSource(input) { const parseFromSourceInputs = (given) => { if (Array.isArray(given)) { return { fsPaths: given }; } else if (typeof given === 'object') { return given; } else { return { fsPaths: [given] }; } }; const { fsPaths, registry, tree, include, fsDeletePaths = [] } = parseFromSourceInputs(input); const resolver = new metadataResolver_1.MetadataResolver(registry, tree); const set = new ComponentSet([], registry); const buildComponents = (paths, destructiveType) => { for (const path of paths) { for (const component of resolver.getComponentsFromPath(path, include)) { set.add(component, destructiveType); } } }; buildComponents(fsPaths); buildComponents(fsDeletePaths, types_1.DestructiveChangesType.POST); set.forceIgnoredPaths = resolver.forceIgnoredPaths; return set; } static async fromManifest(input) { const manifestPath = typeof input === 'string' ? input : input.manifestPath; const options = (typeof input === 'object' ? input : {}); const manifestResolver = new manifestResolver_1.ManifestResolver(options.tree, options.registry); const manifest = await manifestResolver.resolve(manifestPath); const resolveIncludeSet = options.resolveSourcePaths ? new ComponentSet([], options.registry) : undefined; const resolvePostSet = options.resolveSourcePaths ? new ComponentSet([], options.registry) : undefined; const resolvePreSet = options.resolveSourcePaths ? new ComponentSet([], options.registry) : undefined; const result = new ComponentSet([], options.registry); result.logger.debug(`Setting sourceApiVersion of ${manifest.apiVersion} on ComponentSet from manifest`); result.sourceApiVersion = manifest.apiVersion; result.fullName = manifest.fullName; const addComponent = (component, deletionType) => { if (resolveIncludeSet && !deletionType) { resolveIncludeSet.add(component); } if (resolvePreSet && deletionType === types_1.DestructiveChangesType.PRE) { resolvePreSet.add(component, types_1.DestructiveChangesType.PRE); } if (resolvePostSet && deletionType === types_1.DestructiveChangesType.POST) { resolvePostSet.add(component, types_1.DestructiveChangesType.POST); } const memberIsWildcard = component.fullName === ComponentSet.WILDCARD; if (options.resolveSourcePaths === undefined || !memberIsWildcard || options.forceAddWildcards) { result.add(component, deletionType); } }; const resolveDestructiveChanges = async (path, destructiveChangeType) => { const destructiveManifest = await manifestResolver.resolve(path); for (const comp of destructiveManifest.components) { addComponent(new sourceComponent_1.SourceComponent({ type: comp.type, name: comp.fullName }), destructiveChangeType); } }; if (options.destructivePre) { await resolveDestructiveChanges(options.destructivePre, types_1.DestructiveChangesType.PRE); } if (options.destructivePost) { await resolveDestructiveChanges(options.destructivePost, types_1.DestructiveChangesType.POST); } for (const component of manifest.components) { addComponent(component); } if (options.resolveSourcePaths) { const components = ComponentSet.fromSource({ fsPaths: options.resolveSourcePaths, tree: options.tree, include: resolveIncludeSet, registry: options.registry, }); result.forceIgnoredPaths = components.forceIgnoredPaths; for (const component of components) { result.add(component); } // if there was nothing in the resolveIncludeSet, then we can be missing information that we display to the user for deletes if (resolveIncludeSet?.size === 0) { const preCS = ComponentSet.fromSource({ fsPaths: options.resolveSourcePaths, tree: options.tree, include: resolvePreSet, registry: options.registry, }); for (const component of preCS) { result.add(component, types_1.DestructiveChangesType.PRE); } const postCS = ComponentSet.fromSource({ fsPaths: options.resolveSourcePaths, tree: options.tree, include: resolvePostSet, registry: options.registry, }); for (const component of postCS) { result.add(component, types_1.DestructiveChangesType.POST); } } } return result; } static async fromConnection(input) { let usernameOrConnection = typeof input === 'string' ? input : input.usernameOrConnection; const options = (typeof input === 'object' ? input : {}); if (typeof usernameOrConnection === 'string') { usernameOrConnection = await core_1.Connection.create({ authInfo: await core_1.AuthInfo.create({ username: usernameOrConnection }), }); if (options.apiVersion && options.apiVersion !== usernameOrConnection.version) { usernameOrConnection.setApiVersion(options.apiVersion); } } const connectionResolver = new connectionResolver_1.ConnectionResolver(usernameOrConnection, options.registry, options.metadataTypes); const manifest = await connectionResolver.resolve(options.componentFilter); const result = new ComponentSet([], options.registry); result.apiVersion = manifest.apiVersion; for (const component of manifest.components) { result.add(component); } return result; } /** * Constructs a deploy operation using the components in the set and starts * the deployment. There must be at least one source-backed component in * the set to create an operation. * * @param options * @returns Metadata API deploy operation */ async deploy(options) { const toDeploy = Array.from(this.getSourceComponents()); if (toDeploy.length === 0) { throw new core_1.SfError(messages.getMessage('error_no_source_to_deploy'), 'ComponentSetError'); } this.forDeploy = true; if (typeof options.usernameOrConnection !== 'string' && this.apiVersion && this.apiVersion !== options.usernameOrConnection.version) { options.usernameOrConnection.setApiVersion(this.apiVersion); this.logger.debug(`Received conflicting apiVersion values for deploy. Using option=${this.apiVersion}, Ignoring apiVersion on connection=${options.usernameOrConnection.version}.`); } const operationOptions = Object.assign({}, options, { components: this, registry: this.registry, apiVersion: this.apiVersion, }); const mdapiDeploy = new metadataApiDeploy_1.MetadataApiDeploy(operationOptions); await mdapiDeploy.start(); return mdapiDeploy; } /** * Constructs a retrieve operation using the components in the set and * starts the retrieval. * * @param options * @returns Metadata API retrieve operation */ async retrieve(options) { const operationOptions = Object.assign({}, options, { components: this, registry: this.registry, apiVersion: this.apiVersion, }); this.forRetrieve = true; if (typeof options.usernameOrConnection !== 'string' && this.apiVersion && this.apiVersion !== options.usernameOrConnection.version) { options.usernameOrConnection.setApiVersion(this.apiVersion); this.logger.debug(`Received conflicting apiVersion values for retrieve. Using option=${this.apiVersion}, Ignoring apiVersion on connection=${options.usernameOrConnection.version}.`); } const mdapiRetrieve = new metadataApiRetrieve_1.MetadataApiRetrieve(operationOptions); await mdapiRetrieve.start(); return mdapiRetrieve; } /** * Get an object representation of a package manifest based on the set components. * * @param destructiveType Optional value for generating objects representing destructive change manifests * @returns Object representation of a package manifest */ async getObject(destructiveType) { // If this ComponentSet has components marked for delete, we need to // only include those components in a destructiveChanges.xml and // all other components in the regular manifest. const components = this.getTypesOfDestructiveChanges().length ? destructiveType ? this.destructiveComponents[destructiveType] : this.manifestComponents : this.components; const typeMap = new Map(); [...components.entries()].map(([key, cmpMap]) => { const [typeId, fullName] = splitOnFirstDelimiter(key); const type = this.registry.getTypeByName(typeId); // Add children [...(cmpMap?.values() ?? [])] .flatMap((c) => c.getChildren()) .map((child) => addToTypeMap({ typeMap, type: child.type, fullName: child.fullName, destructiveType })); // logic: if this is a decomposed type not being retrieved, skip its inclusion in the manifest if the parent is "empty" if (!this.forRetrieve && type.strategies?.transformer === 'decomposed' && // exclude (ex: CustomObjectTranslation) where there are no addressable children Object.values(type.children?.types ?? {}).some((t) => t.unaddressableWithoutParent !== true) && Object.values(type.children?.types ?? {}).some((t) => t.isAddressable !== false)) { const parentComp = [...(cmpMap?.values() ?? [])].find((c) => c.fullName === fullName); if (parentComp?.xml && !(0, decomposed_1.objectHasSomeRealValues)(type)(parentComp.parseXmlSync())) { return; } } addToTypeMap({ typeMap, type: type.folderContentType ? this.registry.getTypeByName(type.folderContentType) : type, fullName: constructFullName(this.registry, type, fullName), destructiveType, }); }); const typeMembers = Array.from(typeMap.entries()) .map(([typeName, members]) => ({ members: [...members].sort(), name: typeName })) .sort((a, b) => (a.name > b.name ? 1 : -1)); return { Package: { ...{ types: typeMembers, version: await this.getApiVersion(), }, ...(this.fullName ? { fullName: this.fullName } : {}), }, }; } /** * Create a manifest in xml format based on the set components and the * type of manifest to create. * * E.g. package.xml or destructiveChanges.xml * * @param indentation Number of spaces to indent lines by. * @param destructiveType What type of destructive manifest to build. */ async getPackageXml(indentation = 4, destructiveType) { const builder = new fast_xml_parser_1.XMLBuilder({ format: true, indentBy: ''.padEnd(indentation, ' '), ignoreAttributes: false, }); const toParse = await this.getObject(destructiveType); toParse.Package[constants_1.XML_NS_KEY] = constants_1.XML_NS_URL; // eslint-disable-next-line @typescript-eslint/no-unsafe-argument return constants_1.XML_DECL.concat(builder.build(toParse)); } /** * Get only the source-backed metadata components in the set. * * @param member Member to retrieve source-backed components for. * @returns Collection of source-backed components */ getSourceComponents(member) { let iter; if (member) { // filter optimization const memberCollection = this.components.get((0, exports.simpleKey)(member)); iter = memberCollection && memberCollection.size > 0 ? memberCollection.values() : []; } else { // eslint-disable-next-line @typescript-eslint/no-this-alias iter = this; } return new lazyCollection_1.LazyCollection(iter).filter((c) => c instanceof sourceComponent_1.SourceComponent); } add(component, deletionType) { const key = (0, exports.simpleKey)(component); if (!this.components.has(key)) { this.components.set(key, new decodeableMap_1.DecodeableMap()); } if (!deletionType && typeof component.type !== 'string') { // this component is meant to be added to manifestComponents, even if it's not a fully validated source component, // this ensures when getObject is called, we created consistent manifests whether using this.components, or this.manifestComponents // when no destructive changes are present, we use this.components (not fully validated as source components, so typos end up in the generated manifest) // when destructive changes are used, we use this.manifestComponents (fully validated, would not match this.components) // this ensures this.components manifest === this.manifestComponents manifest const sc = new sourceComponent_1.SourceComponent({ type: component.type, name: component.fullName }); const srcKey = sourceKey(sc); if (!this.manifestComponents.has(key)) { this.manifestComponents.set(key, new decodeableMap_1.DecodeableMap()); } this.manifestComponents.get(key)?.set(srcKey, sc); } if (!(component instanceof sourceComponent_1.SourceComponent)) { return; } const srcKey = sourceKey(component); // we're working with SourceComponents now this.components.get(key)?.set(srcKey, component); // Build maps of destructive components and regular components as they are added // as an optimization when building manifests. if (deletionType) { component.setMarkedForDelete(deletionType); this.logger.debug(`Marking component for delete: ${component.fullName}`); if (!this.destructiveComponents[deletionType].has(key)) { this.destructiveComponents[deletionType].set(key, new decodeableMap_1.DecodeableMap()); } this.destructiveComponents[deletionType].get(key)?.set(srcKey, component); // updated with deletion information this.components.get(key)?.set(srcKey, component); } else { if (!this.manifestComponents.has(key)) { this.manifestComponents.set(key, new decodeableMap_1.DecodeableMap()); } this.manifestComponents.get(key)?.set(srcKey, component); } } /** * Tests whether or not a `fullName` and `type` pair is present in the set. * * A pair is considered present in the set if one of the following criteria is met: * * - The pair is directly in the set, matching the component key "as is" or decoded. * - A wildcard component with the same `type` as the pair * - If a parent is attached to the pair and the parent is directly in the set * - If a parent is attached to the pair, and a wildcard component's `type` matches the parent's `type` * * @param component Component to test for membership in the set * @returns `true` if the component is in the set */ has(component) { const key = (0, exports.simpleKey)(component); if (this.components.has(key)) { return true; } const wildcardMember = { fullName: ComponentSet.WILDCARD, type: typeof component.type === 'object' ? component.type.name : component.type, }; const isIncludedInWildcard = this.components.has((0, exports.simpleKey)(wildcardMember)); if (isIncludedInWildcard) { return true; } if (typeof component.type === 'object') { const { parent } = component; if (parent) { const parentDirectlyInSet = this.components.has((0, exports.simpleKey)(parent)); if (parentDirectlyInSet) { return true; } const wildcardKey = (0, exports.simpleKey)({ fullName: ComponentSet.WILDCARD, type: parent.type, }); const parentInWildcard = this.components.has(wildcardKey); if (parentInWildcard) { return true; } const partialWildcardKey = (0, exports.simpleKey)({ fullName: `${parent.fullName}.${ComponentSet.WILDCARD}`, type: component.type, }); const parentInPartialWildcard = this.components.has(partialWildcardKey); if (parentInPartialWildcard) { return true; } } } return false; } /** * For a fullName and type, this returns the filenames the matching component, or an empty array if the component is not present * * @param param Object with fullName and type properties * @returns string[] */ getComponentFilenamesByNameAndType({ fullName, type }) { const key = (0, exports.simpleKey)({ fullName, type }); const componentMap = this.components.get(key); if (!componentMap) { return []; } const output = new Set(); componentMap.forEach((component) => { [...component.walkContent(), component.content, component.xml] .filter(ts_types_1.isString) .map((filename) => output.add(filename)); }); return Array.from(output); } *[Symbol.iterator]() { for (const [key, sourceComponents] of this.components.entries()) { if (sourceComponents.size === 0) { const [typeName, fullName] = splitOnFirstDelimiter(key); yield { fullName, type: this.registry.getTypeByName(typeName), }; } else { for (const component of sourceComponents.values()) { yield component; } } } } /** * If this `ComponentSet` has components marked for delete, this sets * whether those components are deleted before any other changes are * deployed (`destructiveChangesPre.xml`) or after changes are deployed * (`destructiveChangesPost.xml`). * * @param type The type of destructive changes to make; i.e., pre or post deploy. */ setDestructiveChangesType(type) { this.destructiveChangesType = type; } /** * If this `ComponentSet` has components marked for delete it will use this * type to build the appropriate destructive changes manifest. * * @returns The type of destructive changes to make; i.e., pre or post deploy. */ getDestructiveChangesType() { return this.destructiveChangesType; } /** * Will return the types of destructive changes in the component set * or an empty array if there aren't destructive components present * * @return DestructiveChangesType[] */ getTypesOfDestructiveChanges() { const destructiveChangesTypes = []; if (this.destructiveChangesPre.size) { destructiveChangesTypes.push(types_1.DestructiveChangesType.PRE); } if (this.destructiveChangesPost.size) { destructiveChangesTypes.push(types_1.DestructiveChangesType.POST); } return destructiveChangesTypes; } /** * Returns an API version to use as the value of the `version` field * in a manifest (package.xml) for MDAPI calls in the following order * of preference: * * 1. this.sourceApiVersion * 2. this.apiVersion * 3. sourceApiVersion set in sfdx-project.json * 4. apiVersion from ConfigAggregator (config files and Env Vars) * 5. http call to apexrest endpoint for highest apiVersion * 6. hardcoded value of "58.0" as a last resort * * @returns string The resolved API version to use in a manifest */ async getApiVersion() { let version = this.sourceApiVersion ?? this.apiVersion; if (!version) { try { const project = await core_1.SfProject.resolve(this.projectDirectory); const projectConfig = await project.resolveProjectConfig(); version = projectConfig?.sourceApiVersion; } catch (e) { // If there's any problem just move on to ConfigAggregator } } if (!version) { try { version = core_1.ConfigAggregator.getValue(core_1.OrgConfigProperties.ORG_API_VERSION).value; } catch (e) { // If there's any problem just move on to the REST endpoint } } if (!version) { try { version = `${await (0, coverage_1.getCurrentApiVersion)()}.0`; } catch (e) { version = '58.0'; this.logger.warn(messages.getMessage('missingApiVersion')); } } return version; } } exports.ComponentSet = ComponentSet; const sourceKey = (component) => { const { fullName, type, xml, content } = component; return `${type.name}${fullName}${xml ?? ''}${content ?? ''}`; }; const simpleKey = (component) => { const typeName = typeof component.type === 'string' ? component.type.toLowerCase().trim() : component.type.id; return `${typeName}${KEY_DELIMITER}${component.fullName}`; }; exports.simpleKey = simpleKey; const splitOnFirstDelimiter = (input) => { const indexOfSplitChar = input.indexOf(KEY_DELIMITER); return [input.substring(0, indexOfSplitChar), input.substring(indexOfSplitChar + 1)]; }; const constructFullName = (registry, type, fullName) => // Some InFolder types are different. e.g., Report/ReportFolder & Dashboard/DashboardFolder. // ReportFolders are deployed/retrieved as Reports. If a ReportFolder is being added append // a "/" so the metadata API can identify it as a folder. ['DashboardFolder', 'ReportFolder', 'EmailTemplateFolder'].includes(type.name) && !fullName.endsWith('/') ? `${fullName}/` : registry.getParentType(type.name)?.strategies?.recomposition === 'startEmpty' && fullName.includes('.') ? // they're reassembled like CustomLabels.MyLabel fullName.split('.')[1] : fullName; /** side effect: mutates the typeMap property */ const addToTypeMap = ({ typeMap, type, fullName, destructiveType, }) => { if (type.isAddressable === false) return; if (fullName === ComponentSet.WILDCARD && !type.supportsWildcardAndName && !destructiveType) { // if the type doesn't support mixed wildcards and specific names, overwrite the names to be a wildcard typeMap.set(type.name, new Set([fullName])); return; } const existing = typeMap.get(type.name) ?? new Set(); if (!existing.has(ComponentSet.WILDCARD) || type.supportsWildcardAndName) { // if the type supports both wildcards and names, add them regardless typeMap.set(type.name, existing.add(fullName)); } }; //# sourceMappingURL=componentSet.js.map