From 80bbc5ff229f6034fd0d3fef870531391fa4a404 Mon Sep 17 00:00:00 2001 From: mshanemc Date: Wed, 24 Jul 2024 16:04:31 -0500 Subject: [PATCH 1/8] refactor: simplify and pseudocode for new section --- src/collections/componentSet.ts | 108 +++++++++++++++++--------------- 1 file changed, 58 insertions(+), 50 deletions(-) diff --git a/src/collections/componentSet.ts b/src/collections/componentSet.ts index 92df18c79c..6e7d9e7c63 100644 --- a/src/collections/componentSet.ts +++ b/src/collections/componentSet.ts @@ -406,64 +406,41 @@ export class ComponentSet extends LazyCollection { * @returns Object representation of a package manifest */ public async getObject(destructiveType?: DestructiveChangesType): Promise { - const version = await this.getApiVersion(); - // 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. - let components = this.components; - if (this.getTypesOfDestructiveChanges().length) { - components = destructiveType ? this.destructiveComponents[destructiveType] : this.manifestComponents; - } + const components = this.getTypesOfDestructiveChanges().length + ? destructiveType + ? this.destructiveComponents[destructiveType] + : this.manifestComponents + : this.components; const typeMap = new Map(); - const addToTypeMap = (type: MetadataType, fullName: string): void => { - if (type.isAddressable !== false) { - const typeName = type.name; - if (!typeMap.has(typeName)) { - typeMap.set(typeName, []); - } - const typeEntry = typeMap.get(typeName); - 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(typeName, [fullName]); - } else if ( - typeEntry && - !typeEntry.includes(fullName) && - (!typeEntry.includes(ComponentSet.WILDCARD) || type.supportsWildcardAndName) - ) { - // if the type supports both wildcards and names, add them regardless - typeMap.get(typeName)?.push(fullName); - } - } - }; - - for (const key of components.keys()) { + [...components.entries()].map(([key, cmpMap]) => { const [typeId, fullName] = splitOnFirstDelimiter(key); - let type = this.registry.getTypeByName(typeId); - - if (type.folderContentType) { - type = this.registry.getTypeByName(type.folderContentType); - } - addToTypeMap( - type, - // they're reassembled like CustomLabels.MyLabel - this.registry.getParentType(type.name)?.strategies?.recomposition === 'startEmpty' && fullName.includes('.') - ? fullName.split('.')[1] - : fullName - ); + const type = this.registry.getTypeByName(typeId); // Add children - const componentMap = components.get(key); - if (componentMap) { - for (const comp of componentMap.values()) { - for (const child of comp.getChildren()) { - addToTypeMap(child.type, child.fullName); - } - } - } - } + [...(cmpMap?.values() ?? [])] + .flatMap((c) => c.getChildren()) + .map((child) => addToTypeMap({ typeMap, type: child.type, fullName: child.fullName, destructiveType })); + + // new logic: if this is a decomposed type, we have to determine whether + // 2. skip its inclusion in the manifest if + // 3. every element from its xml is an addressable child and is in the cs (ex: it's only CustomField) + + addToTypeMap({ + typeMap, + type: type.folderContentType ? this.registry.getTypeByName(type.folderContentType) : type, + fullName: + this.registry.getParentType(type.name)?.strategies?.recomposition === 'startEmpty' && fullName.includes('.') + ? // they're reassembled like CustomLabels.MyLabel + fullName.split('.')[1] + : fullName, + destructiveType, + }); + }); const typeMembers = Array.from(typeMap.entries()) .map(([typeName, members]) => ({ members: members.sort(), name: typeName })) @@ -473,7 +450,7 @@ export class ComponentSet extends LazyCollection { Package: { ...{ types: typeMembers, - version, + version: await this.getApiVersion(), }, ...(this.fullName ? { fullName: this.fullName } : {}), }, @@ -750,3 +727,34 @@ const splitOnFirstDelimiter = (input: string): [string, string] => { const indexOfSplitChar = input.indexOf(KEY_DELIMITER); return [input.substring(0, indexOfSplitChar), input.substring(indexOfSplitChar + 1)]; }; + +/** side effect: mutates the typeMap property */ +const addToTypeMap = ({ + typeMap, + type, + fullName, + destructiveType, +}: { + typeMap: Map; + type: MetadataType; + fullName: string; + destructiveType?: DestructiveChangesType; +}): void => { + if (type.isAddressable === false) return; + const typeName = type.name; + if (!typeMap.has(typeName)) { + typeMap.set(typeName, []); + } + const typeEntry = typeMap.get(typeName); + 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(typeName, [fullName]); + } else if ( + typeEntry && + !typeEntry.includes(fullName) && + (!typeEntry.includes(ComponentSet.WILDCARD) || type.supportsWildcardAndName) + ) { + // if the type supports both wildcards and names, add them regardless + typeMap.get(typeName)?.push(fullName); + } +}; From feb8f4e9bb1113d67cae8aa18317bf0a13aeff98 Mon Sep 17 00:00:00 2001 From: mshanemc Date: Thu, 25 Jul 2024 10:03:26 -0500 Subject: [PATCH 2/8] feat: empty CustomObject don't go in manifest --- src/collections/componentSet.ts | 39 ++++---- .../convertContext/recompositionFinalizer.ts | 18 +--- .../decomposedMetadataTransformer.ts | 10 +- src/utils/decomposed.ts | 25 +++++ test/collections/componentSet.test.ts | 25 +++++ .../convertContext/recomposition.test.ts | 2 +- .../decomposedMetadataTransformer.test.ts | 2 +- .../type-constants/customObjectConstant.ts | 20 ++-- .../customObjectConstantEmptyObjectMeta.ts | 91 +++++++++++++++++++ test/resolve/sourceComponent.test.ts | 4 +- 10 files changed, 182 insertions(+), 54 deletions(-) create mode 100644 test/mock/type-constants/customObjectConstantEmptyObjectMeta.ts diff --git a/src/collections/componentSet.ts b/src/collections/componentSet.ts index 6e7d9e7c63..8eb39a234f 100644 --- a/src/collections/componentSet.ts +++ b/src/collections/componentSet.ts @@ -17,6 +17,7 @@ import { SfProject, } from '@salesforce/core'; import { isString } from '@salesforce/ts-types'; +import { objectHasSomeRealValues } from '../utils/decomposed'; import { MetadataApiDeploy, MetadataApiDeployOptions } from '../client/metadataApiDeploy'; import { MetadataApiRetrieve } from '../client/metadataApiRetrieve'; import type { MetadataApiRetrieveOptions } from '../client/types'; @@ -415,7 +416,7 @@ export class ComponentSet extends LazyCollection { : this.manifestComponents : this.components; - const typeMap = new Map(); + const typeMap = new Map>(); [...components.entries()].map(([key, cmpMap]) => { const [typeId, fullName] = splitOnFirstDelimiter(key); @@ -426,9 +427,17 @@ export class ComponentSet extends LazyCollection { .flatMap((c) => c.getChildren()) .map((child) => addToTypeMap({ typeMap, type: child.type, fullName: child.fullName, destructiveType })); - // new logic: if this is a decomposed type, we have to determine whether - // 2. skip its inclusion in the manifest if - // 3. every element from its xml is an addressable child and is in the cs (ex: it's only CustomField) + // logic: if this is a decomposed type, skip its inclusion in the manifest if the parent is "empty" + if ( + type.strategies?.transformer === 'decomposed' && + // exclude (ex: CustomObjectTranslation) where there are no addressable children + Object.values(type.children?.types ?? {}).every((t) => t.unaddressableWithoutParent !== true) + ) { + const parentComp = [...(cmpMap?.values() ?? [])].find((c) => c.fullName === fullName); + if (parentComp?.xml && !objectHasSomeRealValues(type)(parentComp.parseXmlSync())) { + return; + } + } addToTypeMap({ typeMap, @@ -443,7 +452,7 @@ export class ComponentSet extends LazyCollection { }); const typeMembers = Array.from(typeMap.entries()) - .map(([typeName, members]) => ({ members: members.sort(), name: typeName })) + .map(([typeName, members]) => ({ members: [...members].sort(), name: typeName })) .sort((a, b) => (a.name > b.name ? 1 : -1)); return { @@ -735,26 +744,20 @@ const addToTypeMap = ({ fullName, destructiveType, }: { - typeMap: Map; + typeMap: Map>; type: MetadataType; fullName: string; destructiveType?: DestructiveChangesType; }): void => { if (type.isAddressable === false) return; - const typeName = type.name; - if (!typeMap.has(typeName)) { - typeMap.set(typeName, []); - } - const typeEntry = typeMap.get(typeName); 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(typeName, [fullName]); - } else if ( - typeEntry && - !typeEntry.includes(fullName) && - (!typeEntry.includes(ComponentSet.WILDCARD) || type.supportsWildcardAndName) - ) { + 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.get(typeName)?.push(fullName); + typeMap.set(type.name, existing.add(fullName)); } }; diff --git a/src/convert/convertContext/recompositionFinalizer.ts b/src/convert/convertContext/recompositionFinalizer.ts index 4cd2e2b7e7..81e2cacc16 100644 --- a/src/convert/convertContext/recompositionFinalizer.ts +++ b/src/convert/convertContext/recompositionFinalizer.ts @@ -7,7 +7,7 @@ import { join } from 'node:path'; import { JsonMap } from '@salesforce/ts-types'; import { Messages } from '@salesforce/core'; -import { extractUniqueElementValue, getXmlElement } from '../../utils/decomposed'; +import { extractUniqueElementValue, getXmlElement, unwrapAndOmitNS } from '../../utils/decomposed'; import { MetadataComponent } from '../../resolve/types'; import { XML_NS_KEY, XML_NS_URL } from '../../common/constants'; import { ComponentSet } from '../../collections/componentSet'; @@ -199,19 +199,3 @@ const getXmlFromCache = } return xmlCache.get(key) ?? {}; }; - -/** composed function, exported from module for test */ -export const unwrapAndOmitNS = - (outerType: string) => - (xml: JsonMap): JsonMap => - omitNsKey(unwrapXml(outerType)(xml)); - -/** Remove the namespace key from the json object. Only the parent needs one */ -const omitNsKey = (obj: JsonMap): JsonMap => - Object.fromEntries(Object.entries(obj).filter(([key]) => key !== XML_NS_KEY)) as JsonMap; - -const unwrapXml = - (outerType: string) => - (xml: JsonMap): JsonMap => - // assert that the outerType is also a metadata type name (ex: CustomObject) - (xml[outerType] as JsonMap) ?? xml; diff --git a/src/convert/transformers/decomposedMetadataTransformer.ts b/src/convert/transformers/decomposedMetadataTransformer.ts index a3534d69df..eb2a4bf03f 100644 --- a/src/convert/transformers/decomposedMetadataTransformer.ts +++ b/src/convert/transformers/decomposedMetadataTransformer.ts @@ -12,7 +12,7 @@ import { ensureArray } from '@salesforce/kit'; import { Messages } from '@salesforce/core'; import { calculateRelativePath } from '../../utils/path'; import { ForceIgnore } from '../../resolve/forceIgnore'; -import { extractUniqueElementValue } from '../../utils/decomposed'; +import { extractUniqueElementValue, objectHasSomeRealValues } from '../../utils/decomposed'; import type { MetadataComponent } from '../../resolve/types'; import { DecompositionStrategy, type MetadataType } from '../../registry/types'; import { SourceComponent } from '../../resolve/sourceComponent'; @@ -24,7 +24,7 @@ import { ComponentSet } from '../../collections/componentSet'; import type { DecompositionState, DecompositionStateValue } from '../convertContext/decompositionFinalizer'; import { BaseMetadataTransformer } from './baseMetadataTransformer'; -type XmlObj = { [index: string]: { [XML_NS_KEY]: typeof XML_NS_URL } & JsonMap }; +export type XmlObj = { [index: string]: { [XML_NS_KEY]: typeof XML_NS_URL } & JsonMap }; type StateSetter = (forComponent: MetadataComponent, props: Partial>) => void; Messages.importMessagesDirectory(__dirname); @@ -272,12 +272,6 @@ const tagToChildTypeId = ({ tagKey, type }: { tagKey: string; type: MetadataType Object.values(type.children?.types ?? {}).find((c) => c.xmlElementName === tagKey)?.id ?? type.children?.directories?.[tagKey]; -/** Ex: CustomObject: { '@_xmlns': 'http://soap.sforce.com/2006/04/metadata' } has no real values */ -const objectHasSomeRealValues = - (type: MetadataType) => - (obj: XmlObj): boolean => - Object.keys(obj[type.name] ?? {}).length > 1; - const hasChildTypeId = (cm: ComposedMetadata): cm is Required => !!cm.childTypeId; const addChildType = (cm: Required): ComposedMetadataWithChildType => { diff --git a/src/utils/decomposed.ts b/src/utils/decomposed.ts index ff5943c244..6d6771fb8b 100644 --- a/src/utils/decomposed.ts +++ b/src/utils/decomposed.ts @@ -5,7 +5,10 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ import { JsonMap, getString } from '@salesforce/ts-types'; +import { XmlObj } from '../convert/transformers/decomposedMetadataTransformer'; +import { XML_NS_KEY } from '../common/constants'; import { MetadataType } from '../registry/types'; + /** handle wide-open reading of values from elements inside any metadata xml file...we don't know the type * Return the value of the matching element if supplied, or defaults `fullName` then `name` */ export const extractUniqueElementValue = (xml: JsonMap, uniqueId?: string): string | undefined => @@ -16,3 +19,25 @@ const getStandardElements = (xml: JsonMap): string | undefined => /** @returns xmlElementName if specified, otherwise returns the directoryName */ export const getXmlElement = (mdType: MetadataType): string => mdType.xmlElementName ?? mdType.directoryName; +/** composed function, exported from module for test */ + +export const unwrapAndOmitNS = + (outerType: string) => + (xml: JsonMap): JsonMap => + omitNsKey(unwrapXml(outerType)(xml)); + +/** Remove the namespace key from the json object. Only the parent needs one */ +const omitNsKey = (obj: JsonMap): JsonMap => + Object.fromEntries(Object.entries(obj).filter(([key]) => key !== XML_NS_KEY)) as JsonMap; + +const unwrapXml = + (outerType: string) => + (xml: JsonMap): JsonMap => + // assert that the outerType is also a metadata type name (ex: CustomObject) + (xml[outerType] as JsonMap) ?? xml; + +/** Ex: CustomObject: { '@_xmlns': 'http://soap.sforce.com/2006/04/metadata' } has no real values */ +export const objectHasSomeRealValues = + (type: MetadataType) => + (obj: XmlObj): boolean => + Object.keys(obj[type.name] ?? {}).length > 1; diff --git a/test/collections/componentSet.test.ts b/test/collections/componentSet.test.ts index d95ff9de7f..0190af5541 100644 --- a/test/collections/componentSet.test.ts +++ b/test/collections/componentSet.test.ts @@ -10,6 +10,11 @@ import { MockTestOrgData, TestContext } from '@salesforce/core/testSetup'; import { assert, expect } from 'chai'; import { SinonStub } from 'sinon'; import { AuthInfo, ConfigAggregator, Connection, Lifecycle, Messages, SfProject } from '@salesforce/core'; +import { + DECOMPOSED_CHILD_COMPONENT_1_EMPTY, + DECOMPOSED_CHILD_COMPONENT_2_EMPTY, + DECOMPOSED_COMPONENT_EMPTY, +} from '../mock/type-constants/customObjectConstantEmptyObjectMeta'; import { ComponentSet, ComponentSetBuilder, @@ -975,6 +980,26 @@ describe('ComponentSet', () => { }, ]); }); + + it('omits empty parents from the package manifest', async () => { + const set = new ComponentSet([ + DECOMPOSED_CHILD_COMPONENT_1_EMPTY, + DECOMPOSED_CHILD_COMPONENT_2_EMPTY, + DECOMPOSED_COMPONENT_EMPTY, + ]); + const types = (await set.getObject()).Package.types; + expect(types.map((type) => type.name)).to.not.include(DECOMPOSED_COMPONENT_EMPTY.type.name); + expect((await set.getObject()).Package.types).to.deep.equal([ + { + name: DECOMPOSED_CHILD_COMPONENT_1_EMPTY.type.name, + members: [DECOMPOSED_CHILD_COMPONENT_1_EMPTY.fullName], + }, + { + name: DECOMPOSED_CHILD_COMPONENT_2_EMPTY.type.name, + members: [DECOMPOSED_CHILD_COMPONENT_2_EMPTY.fullName], + }, + ]); + }); }); describe('getPackageXml', () => { diff --git a/test/convert/convertContext/recomposition.test.ts b/test/convert/convertContext/recomposition.test.ts index 3ccdfdcc66..93c567fc51 100644 --- a/test/convert/convertContext/recomposition.test.ts +++ b/test/convert/convertContext/recomposition.test.ts @@ -7,7 +7,7 @@ import { join } from 'node:path'; import { expect } from 'chai'; import { createSandbox } from 'sinon'; -import { unwrapAndOmitNS } from '../../../src/convert/convertContext/recompositionFinalizer'; +import { unwrapAndOmitNS } from '../../../src/utils/decomposed'; import { decomposed, nonDecomposed } from '../../mock'; import { ConvertContext } from '../../../src/convert/convertContext/convertContext'; import { ComponentSet } from '../../../src/collections/componentSet'; diff --git a/test/convert/transformers/decomposedMetadataTransformer.test.ts b/test/convert/transformers/decomposedMetadataTransformer.test.ts index b89f7c0661..1d48d4bfc1 100644 --- a/test/convert/transformers/decomposedMetadataTransformer.test.ts +++ b/test/convert/transformers/decomposedMetadataTransformer.test.ts @@ -454,7 +454,7 @@ describe('DecomposedMetadataTransformer', () => { describe('Merging Components', () => { it('should merge output with merge component that only has children', async () => { assert(registry.types.customobject.children?.types.customfield.name); - const mergeComponentChild = component.getChildren()[0]; + const mergeComponentChild = component.getChildren()[1]; assert(mergeComponentChild.parent); const componentToConvert = SourceComponent.createVirtualComponent( { diff --git a/test/mock/type-constants/customObjectConstant.ts b/test/mock/type-constants/customObjectConstant.ts index 849f3587de..d277c36c34 100644 --- a/test/mock/type-constants/customObjectConstant.ts +++ b/test/mock/type-constants/customObjectConstant.ts @@ -20,12 +20,18 @@ export const DECOMPOSEDS_PATH = join('path', 'to', 'objects'); export const DECOMPOSED_PATH = join(DECOMPOSEDS_PATH, 'customObject__c'); export const DECOMPOSED_XML_NAME = 'customObject__c.object-meta.xml'; export const DECOMPOSED_XML_PATH = join(DECOMPOSED_PATH, DECOMPOSED_XML_NAME); -export const DECOMPOSED_CHILD_XML_NAME_1 = 'Fields__c.field-meta.xml'; -export const DECOMPOSED_CHILD_XML_PATH_1 = join(DECOMPOSED_PATH, DECOMPOSED_CHILD_XML_NAME_1); + export const DECOMPOSED_CHILD_DIR = 'validationRules'; +export const DECOMPOSED_CHILD_DIR_1 = 'fields'; + export const DECOMPOSED_CHILD_DIR_PATH = join(DECOMPOSED_PATH, DECOMPOSED_CHILD_DIR); +export const DECOMPOSED_CHILD_DIR_1_PATH = join(DECOMPOSED_PATH, DECOMPOSED_CHILD_DIR_1); + export const DECOMPOSED_CHILD_XML_NAME_2 = 'myValidationRule.validationRule-meta.xml'; export const DECOMPOSED_CHILD_XML_PATH_2 = join(DECOMPOSED_CHILD_DIR_PATH, DECOMPOSED_CHILD_XML_NAME_2); + +export const DECOMPOSED_CHILD_XML_NAME_1 = 'Fields__c.field-meta.xml'; +export const DECOMPOSED_CHILD_XML_PATH_1 = join(DECOMPOSED_CHILD_DIR_1_PATH, DECOMPOSED_CHILD_XML_NAME_1); export const DECOMPOSED_VIRTUAL_FS: VirtualDirectory[] = [ { dirPath: DECOMPOSED_PATH, @@ -34,11 +40,9 @@ export const DECOMPOSED_VIRTUAL_FS: VirtualDirectory[] = [ name: DECOMPOSED_XML_NAME, data: Buffer.from(`customObject__c`), }, - { - name: DECOMPOSED_CHILD_XML_NAME_1, - data: Buffer.from(`child1`), - }, + DECOMPOSED_CHILD_DIR, + DECOMPOSED_CHILD_DIR_1, ], }, { @@ -51,11 +55,11 @@ export const DECOMPOSED_VIRTUAL_FS: VirtualDirectory[] = [ ], }, { - dirPath: 'fields', + dirPath: DECOMPOSED_CHILD_DIR_1_PATH, children: [ { name: DECOMPOSED_CHILD_XML_NAME_1, - data: Buffer.from('child3'), + data: Buffer.from(`child1`), }, ], }, diff --git a/test/mock/type-constants/customObjectConstantEmptyObjectMeta.ts b/test/mock/type-constants/customObjectConstantEmptyObjectMeta.ts new file mode 100644 index 0000000000..c974fcbe97 --- /dev/null +++ b/test/mock/type-constants/customObjectConstantEmptyObjectMeta.ts @@ -0,0 +1,91 @@ +/* + * 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 + */ +import { assert } from 'chai'; +import { baseName } from '../../../src/utils'; +import { registry, SourceComponent, VirtualDirectory } from '../../../src'; +import { XML_NS_URL } from '../../../src/common'; +import { + DECOMPOSED_CHILD_DIR_1_PATH, + DECOMPOSED_CHILD_DIR, + DECOMPOSED_CHILD_DIR_1, + DECOMPOSED_CHILD_DIR_PATH, + DECOMPOSED_CHILD_XML_NAME_2, + DECOMPOSED_PATH, + DECOMPOSED_XML_PATH, + DECOMPOSED_CHILD_XML_PATH_1, + DECOMPOSED_XML_NAME, + DECOMPOSED_CHILD_XML_PATH_2, + DECOMPOSED_CHILD_XML_NAME_1, +} from './customObjectConstant'; + +// Constants for a decomposed type +const type = registry.types.customobject; +// 2 asserts to guard againt future changes to the registry (TS needs these to trust it, since registry is mushy JSON, not a static type) +assert(type.children?.types.validationrule); +assert(type.children.types.customfield); + +export const DECOMPOSED_VIRTUAL_FS_EMPTY: VirtualDirectory[] = [ + { + dirPath: DECOMPOSED_PATH, + children: [ + { + name: DECOMPOSED_XML_NAME, + data: Buffer.from(``), + }, + + DECOMPOSED_CHILD_DIR, + DECOMPOSED_CHILD_DIR_1, + ], + }, + { + dirPath: DECOMPOSED_CHILD_DIR_PATH, + children: [ + { + name: DECOMPOSED_CHILD_XML_NAME_2, + data: Buffer.from('child2'), + }, + ], + }, + { + dirPath: DECOMPOSED_CHILD_DIR_1_PATH, + children: [ + { + name: DECOMPOSED_CHILD_XML_NAME_1, + data: Buffer.from(`child1`), + }, + ], + }, +]; +export const DECOMPOSED_COMPONENT_EMPTY = SourceComponent.createVirtualComponent( + { + name: baseName(DECOMPOSED_XML_PATH), + type, + xml: DECOMPOSED_XML_PATH, + content: DECOMPOSED_PATH, + }, + DECOMPOSED_VIRTUAL_FS_EMPTY +); + +export const DECOMPOSED_CHILD_COMPONENT_1_EMPTY = SourceComponent.createVirtualComponent( + { + name: baseName(DECOMPOSED_CHILD_XML_NAME_1), + type: type.children.types.customfield, + xml: DECOMPOSED_CHILD_XML_PATH_1, + parent: DECOMPOSED_COMPONENT_EMPTY, + }, + DECOMPOSED_VIRTUAL_FS_EMPTY +); + +export const DECOMPOSED_CHILD_COMPONENT_2_EMPTY = SourceComponent.createVirtualComponent( + { + name: baseName(DECOMPOSED_CHILD_XML_NAME_2), + type: type.children.types.validationrule, + xml: DECOMPOSED_CHILD_XML_PATH_2, + parent: DECOMPOSED_COMPONENT_EMPTY, + }, + DECOMPOSED_VIRTUAL_FS_EMPTY +); diff --git a/test/resolve/sourceComponent.test.ts b/test/resolve/sourceComponent.test.ts index dde00d3a73..18af97d944 100644 --- a/test/resolve/sourceComponent.test.ts +++ b/test/resolve/sourceComponent.test.ts @@ -370,7 +370,9 @@ describe('SourceComponent', () => { ); it('should return child components for a component', () => { - expect(decomposed.DECOMPOSED_COMPONENT.getChildren()).to.deep.equal([expectedChild, expectedChild2]); + const children = decomposed.DECOMPOSED_COMPONENT.getChildren(); + expect(children).to.deep.include(expectedChild); + expect(children).to.deep.include(expectedChild2); }); it('should not include children that are forceignored', () => { From 29f7a24fab1a0ef3ab806c210aed241320aa4f6f Mon Sep 17 00:00:00 2001 From: mshanemc Date: Thu, 25 Jul 2024 10:26:21 -0500 Subject: [PATCH 3/8] fix: exclusions for preset/variant decomposed --- src/collections/componentSet.ts | 3 ++- src/registry/presets/decomposeWorkflowBeta.json | 7 +++++++ .../sampleProjects/variant-workflow/sfdx-project.json | 7 +++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/collections/componentSet.ts b/src/collections/componentSet.ts index 8eb39a234f..29eddb7af0 100644 --- a/src/collections/componentSet.ts +++ b/src/collections/componentSet.ts @@ -431,7 +431,8 @@ export class ComponentSet extends LazyCollection { if ( type.strategies?.transformer === 'decomposed' && // exclude (ex: CustomObjectTranslation) where there are no addressable children - Object.values(type.children?.types ?? {}).every((t) => t.unaddressableWithoutParent !== true) + 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 && !objectHasSomeRealValues(type)(parentComp.parseXmlSync())) { diff --git a/src/registry/presets/decomposeWorkflowBeta.json b/src/registry/presets/decomposeWorkflowBeta.json index aac24f61ce..f8afc61f72 100644 --- a/src/registry/presets/decomposeWorkflowBeta.json +++ b/src/registry/presets/decomposeWorkflowBeta.json @@ -41,6 +41,7 @@ "id": "workflowalert", "name": "WorkflowAlert", "suffix": "workflowAlert", + "unaddressableWithoutParent": true, "xmlElementName": "alerts" }, "workflowfieldupdate": { @@ -48,6 +49,7 @@ "id": "workflowfieldupdate", "name": "WorkflowFieldUpdate", "suffix": "workflowFieldUpdate", + "unaddressableWithoutParent": true, "xmlElementName": "fieldUpdates" }, "workflowknowledgepublish": { @@ -55,6 +57,7 @@ "id": "workflowknowledgepublish", "name": "WorkflowKnowledgePublish", "suffix": "workflowKnowledgePublish", + "unaddressableWithoutParent": true, "xmlElementName": "knowledgePublishes" }, "workflowoutboundmessage": { @@ -62,6 +65,7 @@ "id": "workflowoutboundmessage", "name": "WorkflowOutboundMessage", "suffix": "workflowOutboundMessage", + "unaddressableWithoutParent": true, "xmlElementName": "outboundMessages" }, "workflowrule": { @@ -69,6 +73,7 @@ "id": "workflowrule", "name": "WorkflowRule", "suffix": "workflowRule", + "unaddressableWithoutParent": true, "xmlElementName": "rules" }, "workflowsend": { @@ -76,6 +81,7 @@ "id": "workflowsend", "name": "WorkflowSend", "suffix": "workflowSend", + "unaddressableWithoutParent": true, "xmlElementName": "send" }, "workflowtask": { @@ -83,6 +89,7 @@ "id": "workflowtask", "name": "WorkflowTask", "suffix": "workflowTask", + "unaddressableWithoutParent": true, "xmlElementName": "tasks" } } diff --git a/test/snapshot/sampleProjects/variant-workflow/sfdx-project.json b/test/snapshot/sampleProjects/variant-workflow/sfdx-project.json index 7328a055dc..3d80258253 100644 --- a/test/snapshot/sampleProjects/variant-workflow/sfdx-project.json +++ b/test/snapshot/sampleProjects/variant-workflow/sfdx-project.json @@ -47,6 +47,7 @@ "id": "workflowalert", "name": "WorkflowAlert", "suffix": "workflowAlert", + "unaddressableWithoutParent": true, "xmlElementName": "alerts" }, "workflowfieldupdate": { @@ -54,6 +55,7 @@ "id": "workflowfieldupdate", "name": "WorkflowFieldUpdate", "suffix": "workflowFieldUpdate", + "unaddressableWithoutParent": true, "xmlElementName": "fieldUpdates" }, "workflowknowledgepublish": { @@ -61,6 +63,7 @@ "id": "workflowknowledgepublish", "name": "WorkflowKnowledgePublish", "suffix": "workflowKnowledgePublish", + "unaddressableWithoutParent": true, "xmlElementName": "knowledgePublishes" }, "workflowoutboundmessage": { @@ -68,6 +71,7 @@ "id": "workflowoutboundmessage", "name": "WorkflowOutboundMessage", "suffix": "workflowOutboundMessage", + "unaddressableWithoutParent": true, "xmlElementName": "outboundMessages" }, "workflowrule": { @@ -75,6 +79,7 @@ "id": "workflowrule", "name": "WorkflowRule", "suffix": "workflowRule", + "unaddressableWithoutParent": true, "xmlElementName": "rules" }, "workflowsend": { @@ -82,6 +87,7 @@ "id": "workflowsend", "name": "WorkflowSend", "suffix": "workflowSend", + "unaddressableWithoutParent": true, "xmlElementName": "send" }, "workflowtask": { @@ -89,6 +95,7 @@ "id": "workflowtask", "name": "WorkflowTask", "suffix": "workflowTask", + "unaddressableWithoutParent": true, "xmlElementName": "tasks" } } From d9d000a38cc42cbd7c2f66d5a0a9a5a196c19cee Mon Sep 17 00:00:00 2001 From: mshanemc Date: Thu, 25 Jul 2024 14:38:59 -0500 Subject: [PATCH 4/8] fix: schema updates --- src/registry/presets/decomposeWorkflowBeta.json | 10 +++++----- .../variant-workflow/sfdx-project.json | 17 ++++++++++------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/registry/presets/decomposeWorkflowBeta.json b/src/registry/presets/decomposeWorkflowBeta.json index f8afc61f72..4653ba8dfe 100644 --- a/src/registry/presets/decomposeWorkflowBeta.json +++ b/src/registry/presets/decomposeWorkflowBeta.json @@ -55,41 +55,41 @@ "workflowknowledgepublish": { "directoryName": "workflowKnowledgePublishes", "id": "workflowknowledgepublish", + "isAddressable": false, "name": "WorkflowKnowledgePublish", "suffix": "workflowKnowledgePublish", - "unaddressableWithoutParent": true, "xmlElementName": "knowledgePublishes" }, "workflowoutboundmessage": { "directoryName": "workflowOutboundMessages", "id": "workflowoutboundmessage", + "isAddressable": false, "name": "WorkflowOutboundMessage", "suffix": "workflowOutboundMessage", - "unaddressableWithoutParent": true, "xmlElementName": "outboundMessages" }, "workflowrule": { "directoryName": "workflowRules", "id": "workflowrule", + "isAddressable": false, "name": "WorkflowRule", "suffix": "workflowRule", - "unaddressableWithoutParent": true, "xmlElementName": "rules" }, "workflowsend": { "directoryName": "workflowSends", "id": "workflowsend", + "isAddressable": false, "name": "WorkflowSend", "suffix": "workflowSend", - "unaddressableWithoutParent": true, "xmlElementName": "send" }, "workflowtask": { "directoryName": "workflowTasks", "id": "workflowtask", + "isAddressable": false, "name": "WorkflowTask", "suffix": "workflowTask", - "unaddressableWithoutParent": true, "xmlElementName": "tasks" } } diff --git a/test/snapshot/sampleProjects/variant-workflow/sfdx-project.json b/test/snapshot/sampleProjects/variant-workflow/sfdx-project.json index 3d80258253..d5f8e36937 100644 --- a/test/snapshot/sampleProjects/variant-workflow/sfdx-project.json +++ b/test/snapshot/sampleProjects/variant-workflow/sfdx-project.json @@ -20,6 +20,9 @@ "strictDirectoryNames": { "workflows": "workflow" }, + "suffixes": { + "workflow": "workflow" + }, "types": { "workflow": { "children": { @@ -45,57 +48,57 @@ "workflowalert": { "directoryName": "workflowAlerts", "id": "workflowalert", + "isAddressable": false, "name": "WorkflowAlert", "suffix": "workflowAlert", - "unaddressableWithoutParent": true, "xmlElementName": "alerts" }, "workflowfieldupdate": { "directoryName": "workflowFieldUpdates", "id": "workflowfieldupdate", + "isAddressable": false, "name": "WorkflowFieldUpdate", "suffix": "workflowFieldUpdate", - "unaddressableWithoutParent": true, "xmlElementName": "fieldUpdates" }, "workflowknowledgepublish": { "directoryName": "workflowKnowledgePublishes", "id": "workflowknowledgepublish", + "isAddressable": false, "name": "WorkflowKnowledgePublish", "suffix": "workflowKnowledgePublish", - "unaddressableWithoutParent": true, "xmlElementName": "knowledgePublishes" }, "workflowoutboundmessage": { "directoryName": "workflowOutboundMessages", "id": "workflowoutboundmessage", + "isAddressable": false, "name": "WorkflowOutboundMessage", "suffix": "workflowOutboundMessage", - "unaddressableWithoutParent": true, "xmlElementName": "outboundMessages" }, "workflowrule": { "directoryName": "workflowRules", "id": "workflowrule", + "isAddressable": false, "name": "WorkflowRule", "suffix": "workflowRule", - "unaddressableWithoutParent": true, "xmlElementName": "rules" }, "workflowsend": { "directoryName": "workflowSends", "id": "workflowsend", + "isAddressable": false, "name": "WorkflowSend", "suffix": "workflowSend", - "unaddressableWithoutParent": true, "xmlElementName": "send" }, "workflowtask": { "directoryName": "workflowTasks", "id": "workflowtask", + "isAddressable": false, "name": "WorkflowTask", "suffix": "workflowTask", - "unaddressableWithoutParent": true, "xmlElementName": "tasks" } } From 761b0f65656bb24404214f822940171ce961b058 Mon Sep 17 00:00:00 2001 From: mshanemc Date: Thu, 25 Jul 2024 14:42:07 -0500 Subject: [PATCH 5/8] refactor: type/fn reorganization --- src/convert/transformers/decomposedMetadataTransformer.ts | 3 +-- src/convert/types.ts | 3 +++ src/utils/decomposed.ts | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/convert/transformers/decomposedMetadataTransformer.ts b/src/convert/transformers/decomposedMetadataTransformer.ts index eb2a4bf03f..430fc612bf 100644 --- a/src/convert/transformers/decomposedMetadataTransformer.ts +++ b/src/convert/transformers/decomposedMetadataTransformer.ts @@ -17,14 +17,13 @@ import type { MetadataComponent } from '../../resolve/types'; import { DecompositionStrategy, type MetadataType } from '../../registry/types'; import { SourceComponent } from '../../resolve/sourceComponent'; import { JsToXml } from '../streams'; -import type { WriteInfo } from '../types'; +import type { WriteInfo, XmlObj } from '../types'; import { META_XML_SUFFIX, XML_NS_KEY, XML_NS_URL } from '../../common/constants'; import type { SourcePath } from '../../common/types'; import { ComponentSet } from '../../collections/componentSet'; import type { DecompositionState, DecompositionStateValue } from '../convertContext/decompositionFinalizer'; import { BaseMetadataTransformer } from './baseMetadataTransformer'; -export type XmlObj = { [index: string]: { [XML_NS_KEY]: typeof XML_NS_URL } & JsonMap }; type StateSetter = (forComponent: MetadataComponent, props: Partial>) => void; Messages.importMessagesDirectory(__dirname); diff --git a/src/convert/types.ts b/src/convert/types.ts index 1ad106287f..e0e790a5bf 100644 --- a/src/convert/types.ts +++ b/src/convert/types.ts @@ -5,6 +5,8 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ import { Readable } from 'node:stream'; +import { JsonMap } from '@salesforce/ts-types'; +import { XML_NS_KEY, XML_NS_URL } from '../common/constants'; import { FileResponseSuccess } from '../client/types'; import { SourcePath } from '../common/types'; import { MetadataComponent, SourceComponent } from '../resolve'; @@ -153,3 +155,4 @@ export type ReplacementEvent = { filename: string; replaced: string; }; +export type XmlObj = { [index: string]: { [XML_NS_KEY]: typeof XML_NS_URL } & JsonMap }; diff --git a/src/utils/decomposed.ts b/src/utils/decomposed.ts index 6d6771fb8b..7e835ff1d2 100644 --- a/src/utils/decomposed.ts +++ b/src/utils/decomposed.ts @@ -5,7 +5,7 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ import { JsonMap, getString } from '@salesforce/ts-types'; -import { XmlObj } from '../convert/transformers/decomposedMetadataTransformer'; +import { XmlObj } from '../convert/types'; import { XML_NS_KEY } from '../common/constants'; import { MetadataType } from '../registry/types'; From 7b17eb2d149ed5a28ea3a9014c4f57e2cbc70600 Mon Sep 17 00:00:00 2001 From: mshanemc Date: Thu, 25 Jul 2024 14:49:53 -0500 Subject: [PATCH 6/8] chore: variant details --- src/registry/presets/decomposeWorkflowBeta.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registry/presets/decomposeWorkflowBeta.json b/src/registry/presets/decomposeWorkflowBeta.json index 4653ba8dfe..d66bb3cf3d 100644 --- a/src/registry/presets/decomposeWorkflowBeta.json +++ b/src/registry/presets/decomposeWorkflowBeta.json @@ -39,17 +39,17 @@ "workflowalert": { "directoryName": "workflowAlerts", "id": "workflowalert", + "isAddressable": false, "name": "WorkflowAlert", "suffix": "workflowAlert", - "unaddressableWithoutParent": true, "xmlElementName": "alerts" }, "workflowfieldupdate": { "directoryName": "workflowFieldUpdates", "id": "workflowfieldupdate", + "isAddressable": false, "name": "WorkflowFieldUpdate", "suffix": "workflowFieldUpdate", - "unaddressableWithoutParent": true, "xmlElementName": "fieldUpdates" }, "workflowknowledgepublish": { From a489bab22bdd8296958c66c94e1617b43195c698 Mon Sep 17 00:00:00 2001 From: mshanemc Date: Thu, 25 Jul 2024 14:53:45 -0500 Subject: [PATCH 7/8] test: expect only top-level in manifest for decomposed Workflow --- .../verify-md-files.expected/package.xml | 6 ------ .../verify-md-files.expected/package.xml | 16 ---------------- .../verify-md-files.expected/package.xml | 16 ---------------- 3 files changed, 38 deletions(-) diff --git a/test/snapshot/sampleProjects/preset-workflow-mpd/__snapshots__/verify-md-files.expected/package.xml b/test/snapshot/sampleProjects/preset-workflow-mpd/__snapshots__/verify-md-files.expected/package.xml index b8b5b6efb8..70736a7aac 100644 --- a/test/snapshot/sampleProjects/preset-workflow-mpd/__snapshots__/verify-md-files.expected/package.xml +++ b/test/snapshot/sampleProjects/preset-workflow-mpd/__snapshots__/verify-md-files.expected/package.xml @@ -4,11 +4,5 @@ Case Workflow - - Case.ChangePriorityToHigh - Case.ChangePriorityToLow - Case.ChangePriorityToMedium - WorkflowFieldUpdate - 60.0 diff --git a/test/snapshot/sampleProjects/preset-workflow/__snapshots__/verify-md-files.expected/package.xml b/test/snapshot/sampleProjects/preset-workflow/__snapshots__/verify-md-files.expected/package.xml index 52d35b1edd..bb53d61d26 100644 --- a/test/snapshot/sampleProjects/preset-workflow/__snapshots__/verify-md-files.expected/package.xml +++ b/test/snapshot/sampleProjects/preset-workflow/__snapshots__/verify-md-files.expected/package.xml @@ -8,21 +8,5 @@ Account Workflow - - Account.emailalert1 - WorkflowAlert - - - Account.fieldupdate1 - WorkflowFieldUpdate - - - Account.outboundmsg1 - WorkflowOutboundMessage - - - Account.task_1 - WorkflowTask - 60.0 diff --git a/test/snapshot/sampleProjects/variant-workflow/__snapshots__/verify-md-files.expected/package.xml b/test/snapshot/sampleProjects/variant-workflow/__snapshots__/verify-md-files.expected/package.xml index 52d35b1edd..bb53d61d26 100644 --- a/test/snapshot/sampleProjects/variant-workflow/__snapshots__/verify-md-files.expected/package.xml +++ b/test/snapshot/sampleProjects/variant-workflow/__snapshots__/verify-md-files.expected/package.xml @@ -8,21 +8,5 @@ Account Workflow - - Account.emailalert1 - WorkflowAlert - - - Account.fieldupdate1 - WorkflowFieldUpdate - - - Account.outboundmsg1 - WorkflowOutboundMessage - - - Account.task_1 - WorkflowTask - 60.0 From e6d262da4f4738b1925b3ebf82457b2322a1807a Mon Sep 17 00:00:00 2001 From: mshanemc Date: Thu, 25 Jul 2024 15:11:42 -0500 Subject: [PATCH 8/8] chore: deps for xnuts --- package.json | 6 ++-- yarn.lock | 86 +++++++++++++++++++++++++++++++--------------------- 2 files changed, 55 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index 62440924ea..8c7f7e06e1 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "node": ">=18.0.0" }, "dependencies": { - "@salesforce/core": "^8.2.1", + "@salesforce/core": "^8.2.3", "@salesforce/kit": "^3.1.6", "@salesforce/ts-types": "^2.0.10", "fast-levenshtein": "^3.0.0", @@ -39,8 +39,8 @@ "proxy-agent": "^6.4.0" }, "devDependencies": { - "@jsforce/jsforce-node": "^3.2.4", - "@salesforce/cli-plugins-testkit": "^5.3.18", + "@jsforce/jsforce-node": "^3.3.1", + "@salesforce/cli-plugins-testkit": "^5.3.19", "@salesforce/dev-scripts": "^10.2.2", "@types/deep-equal-in-any-order": "^1.0.1", "@types/fast-levenshtein": "^0.0.4", diff --git a/yarn.lock b/yarn.lock index 6a1d26642e..01cbd1e3aa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -522,6 +522,25 @@ strip-ansi "^6.0.0" xml2js "^0.6.2" +"@jsforce/jsforce-node@^3.3.1": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@jsforce/jsforce-node/-/jsforce-node-3.3.1.tgz#e6a3a6e892a0067e0afbb4f19b50f459efbf96a0" + integrity sha512-wLIQhU3X+ltZFItdQ8XMr2kqB70S2q4ukSELneCDQkDXlzRIqhQ7rZP4jcmcdT0r6Tc/8lvlw2s2paPxossRBA== + dependencies: + "@sindresorhus/is" "^4" + "@types/node" "^18.15.3" + abort-controller "^3.0.0" + base64url "^3.0.1" + csv-parse "^5.5.2" + csv-stringify "^6.4.4" + faye "^1.4.0" + form-data "^4.0.0" + https-proxy-agent "^5.0.0" + multistream "^3.1.0" + node-fetch "^2.6.1" + strip-ansi "^6.0.0" + xml2js "^0.6.2" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -548,12 +567,12 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@salesforce/cli-plugins-testkit@^5.3.18": - version "5.3.18" - resolved "https://registry.yarnpkg.com/@salesforce/cli-plugins-testkit/-/cli-plugins-testkit-5.3.18.tgz#f36ae6ed8573b18396668a4d38b4effe0bd1a2bf" - integrity sha512-9+Yvmw5idIQryJcXVKZ2HASLKj2RUJizDuDFPq5ut9X+8y8MTWAkO0hO6OSj3IPkV+LTo8QF7QOCl16R2PWpMw== +"@salesforce/cli-plugins-testkit@^5.3.19": + version "5.3.19" + resolved "https://registry.yarnpkg.com/@salesforce/cli-plugins-testkit/-/cli-plugins-testkit-5.3.19.tgz#495e7516a6046c42fc7cf60b11894d8922dd01aa" + integrity sha512-4mRlszh1q6bPw7dF4EHbYJ2IFxAanzKBbauZ5pSUHt+++otASmANoPc3TZ4CzNDG3rbt+5qFLnhhaQWMli0Nqw== dependencies: - "@salesforce/core" "^8.1.1" + "@salesforce/core" "^8.2.1" "@salesforce/kit" "^3.1.6" "@salesforce/ts-types" "^2.0.10" "@types/shelljs" "^0.8.15" @@ -564,7 +583,7 @@ strip-ansi "6.0.1" ts-retry-promise "^0.8.1" -"@salesforce/core@^8.1.1", "@salesforce/core@^8.2.1": +"@salesforce/core@^8.2.1": version "8.2.1" resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-8.2.1.tgz#16b383886ec02ee9a99f815d5ce4a9f5a492295e" integrity sha512-juLxb4t12vZD9ozkhKhGtbZzOiYkEcUWdw+gAtgzuSrTFTwvoXlbKtzdGbdYPLTHLGYQCVW6cRdMswfwbE5BnQ== @@ -588,6 +607,30 @@ semver "^7.6.2" ts-retry-promise "^0.8.1" +"@salesforce/core@^8.2.3": + version "8.2.3" + resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-8.2.3.tgz#4714e5ca046c6fcdcffb91aad151affa9e7a0e88" + integrity sha512-epkV2ZU+WQFgxb6q98+9vAp9Qo1bUnCOyk1VyVr2XycJk6BkC0fBE188KpvH0/nqB2+0p2K4Cd3x1/+oC7HYvQ== + dependencies: + "@jsforce/jsforce-node" "^3.2.4" + "@salesforce/kit" "^3.1.6" + "@salesforce/schemas" "^1.9.0" + "@salesforce/ts-types" "^2.0.10" + ajv "^8.17.1" + change-case "^4.1.2" + fast-levenshtein "^3.0.0" + faye "^1.4.0" + form-data "^4.0.0" + js2xmlparser "^4.0.1" + jsonwebtoken "9.0.2" + jszip "3.10.1" + pino "^9.2.0" + pino-abstract-transport "^1.2.0" + pino-pretty "^11.2.1" + proper-lockfile "^4.1.2" + semver "^7.6.2" + ts-retry-promise "^0.8.1" + "@salesforce/dev-config@^4.1.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@salesforce/dev-config/-/dev-config-4.1.0.tgz#e529576466d074e7a5f1441236510fef123da01e" @@ -5032,16 +5075,7 @@ srcset@^5.0.0: resolved "https://registry.yarnpkg.com/srcset/-/srcset-5.0.0.tgz#9df6c3961b5b44a02532ce6ae4544832609e2e3f" integrity sha512-SqEZaAEhe0A6ETEa9O1IhSPC7MdvehZtCnTR0AftXk3QhY2UNgb+NApFOUPZILXk/YTDfFxMTNJOBpzrJsEdIA== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -5100,14 +5134,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -5601,7 +5628,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -5619,15 +5646,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"