diff --git a/METADATA_SUPPORT.md b/METADATA_SUPPORT.md index 5a1ed397d6..7ded236551 100644 --- a/METADATA_SUPPORT.md +++ b/METADATA_SUPPORT.md @@ -8,579 +8,577 @@ Currently, there are 533/569 supported metadata types. For status on any existing gaps, please search or file an issue in the [Salesforce CLI issues only repo](https://github.com/forcedotcom/cli/issues). To contribute a new metadata type, please see the [Contributing Metadata Types to the Registry](./contributing/metadata.md) -|Metadata Type|Support|Notes| -|:---|:---|:---| -|AIApplication|✅|| -|AIApplicationConfig|✅|| -|AIReplyRecommendationsSettings|✅|| -|AIScoringModelDefVersion|✅|| -|AIScoringModelDefinition|✅|| -|AIUsecaseDefinition|⚠️|Supports deploy/retrieve but not source tracking| -|AccountForecastSettings|✅|| -|AccountInsightsSettings|✅|| -|AccountIntelligenceSettings|✅|| -|AccountRelationshipShareRule|✅|| -|AccountSettings|✅|| -|AccountingFieldMapping|✅|| -|AccountingModelConfig|✅|| -|AccountingSettings|✅|| -|AcctMgrTargetSettings|✅|| -|ActionLauncherItemDef|✅|| -|ActionLinkGroupTemplate|✅|| -|ActionPlanTemplate|✅|| -|ActionableListDefinition|✅|| -|ActionsSettings|✅|| -|ActivationPlatform|✅|| -|ActivitiesSettings|✅|| -|AddressSettings|✅|| -|AdvAccountForecastSet|✅|| -|AdvAcctForecastDimSource|✅|| -|AdvAcctForecastPeriodGroup|✅|| -|Ai4mSettings|✅|| -|AnalyticSnapshot|✅|| -|AnalyticsSettings|✅|| -|AnimationRule|✅|| -|ApexClass|✅|| -|ApexComponent|✅|| -|ApexEmailNotifications|✅|| -|ApexPage|✅|| -|ApexSettings|✅|| -|ApexTestSuite|✅|| -|ApexTrigger|✅|| -|AppAnalyticsSettings|✅|| -|AppExperienceSettings|✅|| -|AppMenu|✅|| -|ApplicationRecordTypeConfig|✅|| -|ApplicationSubtypeDefinition|✅|| -|AppointmentAssignmentPolicy|✅|| -|AppointmentSchedulingPolicy|✅|| -|ApprovalProcess|✅|| -|AssessmentConfiguration|❌|Not supported, but support could be added| -|AssessmentQuestion|✅|| -|AssessmentQuestionSet|✅|| -|AssignmentRules|✅|| -|AssistantContextItem|✅|| -|AssistantDefinition|✅|| -|AssistantSkillQuickAction|✅|| -|AssistantSkillSobjectAction|✅|| -|AssistantVersion|✅|| -|AssociationEngineSettings|✅|| -|Audience|✅|| -|AuraDefinitionBundle|✅|| -|AuthProvider|✅|| -|AutoResponseRules|✅|| -|AutomatedContactsSettings|✅|| -|BatchCalcJobDefinition|✅|| -|BatchProcessJobDefinition|✅|| -|BenefitAction|✅|| -|BlacklistedConsumer|✅|| -|BldgEnrgyIntensityCnfg|✅|| -|BlockchainSettings|✅|| -|Bot|✅|| -|BotBlock|✅|| -|BotBlockVersion|❌|Not supported, but support could be added| -|BotSettings|✅|| -|BotTemplate|✅|| -|BotVersion|✅|| -|BranchManagementSettings|✅|| -|BrandingSet|✅|| -|BriefcaseDefinition|✅|| -|BusinessHoursSettings|✅|| -|BusinessProcess|✅|| -|BusinessProcessGroup|✅|| -|BusinessProcessTypeDefinition|✅|| -|CMSConnectSource|✅|| -|CallCenter|✅|| -|CallCenterRoutingMap|✅|| -|CallCoachingMediaProvider|⚠️|Supports deploy/retrieve but not source tracking| -|CampaignInfluenceModel|✅|| -|CampaignSettings|✅|| -|CanvasMetadata|✅|| -|CareBenefitVerifySettings|✅|| -|CareLimitType|✅|| -|CareProviderAfflRoleConfig|❌|Not supported, but support could be added| -|CareProviderSearchConfig|✅|| -|CareRequestConfiguration|✅|| -|CareSystemFieldMapping|✅|| -|CaseSettings|✅|| -|CaseSubjectParticle|✅|| -|Certificate|✅|| -|ChannelLayout|✅|| -|ChannelObjectLinkingRule|✅|| -|ChatterAnswersSettings|✅|| -|ChatterEmailsMDSettings|✅|| -|ChatterExtension|✅|| -|ChatterSettings|✅|| -|ClaimFinancialSettings|✅|| -|ClaimMgmtFoundationEnabledSettings|✅|| -|ClauseCatgConfiguration|✅|| -|CleanDataService|✅|| -|CodeBuilderSettings|✅|| -|CollectionsDashboardSettings|✅|| -|CommandAction|✅|| -|CommerceSettings|✅|| -|CommsServiceConsoleSettings|✅|| -|CommunitiesSettings|✅|| -|Community|✅|| -|CommunityTemplateDefinition|✅|| -|CommunityThemeDefinition|✅|| -|CompactLayout|✅|| -|CompanySettings|✅|| -|ConnectedApp|✅|| -|ConnectedAppSettings|✅|| -|ContentAsset|✅|| -|ContentSettings|✅|| -|ContextDefinition|❌|Not supported, but support could be added (but not for tracking)| -|ContractSettings|✅|| -|ContractType|❌|Not supported, but support could be added| -|ConversationChannelDefinition|✅|| -|ConversationServiceIntegrationSettings|✅|| -|ConversationVendorInfo|✅|| -|ConversationalIntelligenceSettings|✅|| -|CorsWhitelistOrigin|✅|| -|CspTrustedSite|✅|| -|CurrencySettings|✅|| -|CustomAddressFieldSettings|✅|| -|CustomApplication|✅|| -|CustomApplicationComponent|✅|| -|CustomFeedFilter|✅|| -|CustomField|✅|| -|CustomHelpMenuSection|✅|| -|CustomIndex|✅|| -|CustomLabels|✅|| -|CustomMetadata|✅|| -|CustomNotificationType|✅|| -|CustomObject|✅|| -|CustomObjectTranslation|✅|| -|CustomPageWebLink|✅|| -|CustomPermission|✅|| -|CustomSite|✅|| -|CustomTab|✅|| -|CustomValue|❌|Not supported, but support could be added| -|CustomerDataPlatformSettings|✅|| -|CustomizablePropensityScoringSettings|✅|| -|Dashboard|✅|| -|DashboardFolder|✅|| -|DataCategoryGroup|✅|| -|DataConnectorIngestApi|✅|| -|DataConnectorS3|✅|| -|DataDotComSettings|✅|| -|DataImportManagementSettings|✅|| -|DataPackageKitDefinition|✅|| -|DataPackageKitObject|✅|| -|DataSource|✅|| -|DataSourceBundleDefinition|✅|| -|DataSourceObject|✅|| -|DataSourceTenant|✅|| -|DataSrcDataModelFieldMap|✅|| -|DataStreamDefinition|✅|| -|DataStreamTemplate|✅|| -|DataWeaveResource|✅|| -|DecisionMatrixDefinition|✅|| -|DecisionMatrixDefinitionVersion|✅|| -|DecisionTable|✅|| -|DecisionTableDatasetLink|✅|| -|DelegateGroup|✅|| -|DeploymentSettings|✅|| -|DevHubSettings|✅|| -|DigitalExperience|✅|| -|DigitalExperienceBundle|✅|| -|DigitalExperienceConfig|✅|| -|DisclosureDefinition|✅|| -|DisclosureDefinitionVersion|✅|| -|DisclosureType|✅|| -|DiscoveryAIModel|✅|| -|DiscoveryGoal|✅|| -|DiscoverySettings|✅|| -|DiscoveryStory|❌|Not supported, but support could be added| -|Document|✅|| -|DocumentCategory|❌|Not supported, but support could be added| -|DocumentCategoryDocumentType|❌|Not supported, but support could be added| -|DocumentChecklistSettings|✅|| -|DocumentFolder|✅|| -|DocumentGenerationSetting|✅|| -|DocumentType|✅|| -|DuplicateRule|✅|| -|DynamicFormsSettings|✅|| -|EACSettings|✅|| -|ESignatureConfig|✅|| -|ESignatureEnvelopeConfig|✅|| -|EclairGeoData|✅|| -|EinsteinAgentSettings|✅|| -|EinsteinAssistantSettings|✅|| -|EinsteinDealInsightsSettings|✅|| -|EinsteinDocumentCaptureSettings|✅|| -|EinsteinGptSettings|✅|| -|EmailAdministrationSettings|✅|| -|EmailFolder|✅|| -|EmailIntegrationSettings|✅|| -|EmailServicesFunction|✅|| -|EmailTemplate|✅|| -|EmailTemplateFolder|✅|| -|EmailTemplateSettings|✅|| -|EmbeddedServiceBranding|✅|| -|EmbeddedServiceConfig|✅|| -|EmbeddedServiceFlowConfig|✅|| -|EmbeddedServiceLiveAgent|✅|| -|EmbeddedServiceMenuSettings|✅|| -|EmployeeDataSyncProfile|❌|Not supported, but support could be added| -|EmployeeFieldAccessSettings|✅|| -|EmployeeUserSettings|✅|| -|EnhancedNotesSettings|✅|| -|EntitlementProcess|✅|| -|EntitlementSettings|✅|| -|EntitlementTemplate|✅|| -|EscalationRules|✅|| -|EssentialsSettings|✅|| -|EventSettings|✅|| -|ExperienceBundle|✅|| -|ExperienceBundleSettings|✅|| -|ExperiencePropertyTypeBundle|✅|| -|ExplainabilityActionDefinition|✅|| -|ExplainabilityActionVersion|✅|| -|ExplainabilityMsgTemplate|✅|| -|ExpressionSetDefinition|✅|| -|ExpressionSetDefinitionVersion|✅|| -|ExpressionSetMessageToken|❌|Not supported, but support could be added| -|ExpressionSetObjectAlias|❌|Not supported, but support could be added| -|ExternalAIModel|❌|Not supported, but support could be added| -|ExternalClientAppSettings|✅|| -|ExternalClientApplication|✅|| -|ExternalCredential|✅|| -|ExternalDataConnector|✅|| -|ExternalDataSource|✅|| -|ExternalDataSrcDescriptor|❌|Not supported, but support could be added| -|ExternalDataTranField|❌|Not supported, but support could be added| -|ExternalDataTranObject|❌|Not supported, but support could be added| -|ExternalDocStorageConfig|❌|Not supported, but support could be added| -|ExternalServiceRegistration|✅|| -|ExtlClntAppGlobalOauthSettings|✅|| -|ExtlClntAppMobileConfigurablePolicies|✅|| -|ExtlClntAppMobileSettings|✅|| -|ExtlClntAppOauthConfigurablePolicies|✅|| -|ExtlClntAppOauthSettings|✅|| -|FeatureParameterBoolean|✅|| -|FeatureParameterDate|✅|| -|FeatureParameterInteger|✅|| -|FieldRestrictionRule|✅|| -|FieldServiceMobileExtension|✅|| -|FieldServiceSettings|✅|| -|FieldSet|✅|| -|FieldSrcTrgtRelationship|✅|| -|FileUploadAndDownloadSecuritySettings|✅|| -|FilesConnectSettings|✅|| -|FlexiPage|✅|| -|Flow|✅|| -|FlowCategory|✅|| -|FlowDefinition|⚠️|Supports deploy/retrieve but not source tracking| -|FlowSettings|✅|| -|FlowTest|✅|| -|ForecastingFilter|✅|| -|ForecastingFilterCondition|✅|| -|ForecastingObjectListSettings|✅|| -|ForecastingSettings|✅|| -|ForecastingSourceDefinition|✅|| -|ForecastingType|✅|| -|ForecastingTypeSource|✅|| -|FormulaSettings|✅|| -|FuelType|✅|| -|FuelTypeSustnUom|✅|| -|FunctionReference|⚠️|Supports deploy/retrieve but not source tracking| -|FundraisingConfig|✅|| -|GatewayProviderPaymentMethodType|✅|| -|GenAiFunction|❌|Not supported, but support could be added| -|GenAiPlugin|❌|Not supported, but support could be added| -|GlobalValueSet|✅|| -|GlobalValueSetTranslation|✅|| -|GoogleAppsSettings|✅|| -|Group|✅|| -|HighVelocitySalesSettings|✅|| -|HomePageComponent|✅|| -|HomePageLayout|✅|| -|IPAddressRange|✅|| -|Icon|✅|| -|IdeasSettings|✅|| -|IdentityProviderSettings|✅|| -|IdentityVerificationProcDef|✅|| -|IframeWhiteListUrlSettings|✅|| -|InboundCertificate|✅|| -|InboundNetworkConnection|✅|| -|IncidentMgmtSettings|✅|| -|IncludeEstTaxInQuoteSettings|✅|| -|Index|⚠️|Supports deploy/retrieve but not source tracking| -|IndustriesAutomotiveSettings|✅|| -|IndustriesContextSettings|✅|| -|IndustriesEinsteinFeatureSettings|✅|| -|IndustriesLoyaltySettings|✅|| -|IndustriesManufacturingSettings|✅|| -|IndustriesPricingSettings|✅|| -|IndustriesSettings|✅|| -|IndustriesUnifiedPromotionsSettings|✅|| -|InstalledPackage|⚠️|Supports deploy/retrieve but not source tracking| -|IntegrationProviderDef|✅|| -|InterestTaggingSettings|✅|| -|InternalDataConnector|✅|| -|InvLatePymntRiskCalcSettings|✅|| -|InventorySettings|✅|| -|InvocableActionSettings|✅|| -|IoTSettings|✅|| -|KeywordList|✅|| -|KnowledgeGenerationSettings|✅|| -|KnowledgeSettings|✅|| -|LanguageSettings|✅|| -|Layout|✅|| -|LeadConfigSettings|✅|| -|LeadConvertSettings|✅|| -|LearningAchievementConfig|❌|Not supported, but support could be added| -|Letterhead|✅|| -|LicensingSettings|✅|| -|LightningBolt|✅|| -|LightningComponentBundle|✅|| -|LightningExperienceSettings|✅|| -|LightningExperienceTheme|✅|| -|LightningMessageChannel|✅|| -|LightningOnboardingConfig|✅|| -|ListView|✅|| -|LiveAgentSettings|✅|| -|LiveChatAgentConfig|✅|| -|LiveChatButton|✅|| -|LiveChatDeployment|✅|| -|LiveChatSensitiveDataRule|✅|| -|LiveMessageSettings|✅|| -|LocationUse|✅|| -|LoyaltyProgramSetup|⚠️|Supports deploy/retrieve but not source tracking| -|MacroSettings|✅|| -|MailMergeSettings|✅|| -|ManagedContentType|⚠️|Supports deploy/retrieve but not source tracking| -|ManagedTopics|✅|| -|MapsAndLocationSettings|✅|| -|MarketSegmentDefinition|✅|| -|MarketingAppExtActivity|❌|Not supported, but support could be added| -|MarketingAppExtension|✅|| -|MatchingRules|✅|| -|MediaAdSalesSettings|✅|| -|MeetingsSettings|✅|| -|MessagingChannel|⚠️|Supports deploy/retrieve but not source tracking| -|MfgProgramTemplate|✅|| -|MfgServiceConsoleSettings|✅|| -|MilestoneType|✅|| -|MktCalcInsightObjectDef|✅|| -|MktDataTranObject|✅|| -|MlDomain|✅|| -|MobSecurityCertPinConfig|✅|| -|MobileApplicationDetail|✅|| -|MobileSecurityAssignment|✅|| -|MobileSecurityPolicy|✅|| -|MobileSettings|✅|| -|ModerationRule|✅|| -|MutingPermissionSet|✅|| -|MyDomainDiscoverableLogin|✅|| -|MyDomainSettings|✅|| -|NameSettings|✅|| -|NamedCredential|✅|| -|NavigationMenu|✅|| -|Network|✅|| -|NetworkBranding|✅|| -|NotificationTypeConfig|✅|| -|NotificationsSettings|✅|| -|OauthCustomScope|✅|| -|OauthOidcSettings|✅|| -|ObjectHierarchyRelationship|✅|| -|ObjectLinkingSettings|✅|| -|ObjectSourceTargetMap|✅|| -|OcrSampleDocument|✅|| -|OcrTemplate|✅|| -|OmniChannelPricingSettings|✅|| -|OmniChannelSettings|✅|| -|OmniDataTransform|⚠️|Supports deploy/retrieve but not source tracking| -|OmniIntegrationProcedure|⚠️|Supports deploy/retrieve but not source tracking| -|OmniInteractionAccessConfig|⚠️|Supports deploy/retrieve but not source tracking| -|OmniInteractionConfig|⚠️|Supports deploy/retrieve but not source tracking| -|OmniScript|⚠️|Supports deploy/retrieve but not source tracking| -|OmniSupervisorConfig|✅|| -|OmniUiCard|⚠️|Supports deploy/retrieve but not source tracking| -|OnlineSalesSettings|✅|| -|OpportunityInsightsSettings|✅|| -|OpportunityScoreSettings|✅|| -|OpportunitySettings|✅|| -|OrderManagementSettings|✅|| -|OrderSettings|✅|| -|OrgSettings|✅|| -|OutboundNetworkConnection|✅|| -|PardotEinsteinSettings|✅|| -|PardotSettings|✅|| -|ParticipantRole|✅|| -|PartyDataModelSettings|✅|| -|PathAssistant|✅|| -|PathAssistantSettings|✅|| -|PaymentGatewayProvider|✅|| -|PaymentsManagementEnabledSettings|✅|| -|PaymentsSettings|✅|| -|PermissionSet|✅|| -|PermissionSetGroup|✅|| -|PermissionSetLicenseDefinition|✅|| -|PersonAccountOwnerPowerUser|✅|| -|PicklistSettings|✅|| -|PicklistValue|❌|Not supported, but support could be added| -|PipelineInspMetricConfig|✅|| -|PlatformCachePartition|✅|| -|PlatformEventChannel|✅|| -|PlatformEventChannelMember|✅|| -|PlatformEventSettings|✅|| -|PlatformEventSubscriberConfig|✅|| -|PlatformSlackSettings|✅|| -|PortalDelegablePermissionSet|❌|Not supported, but support could be added| -|PortalsSettings|✅|| -|PostTemplate|✅|| -|PredictionBuilderSettings|✅|| -|PresenceDeclineReason|✅|| -|PresenceUserConfig|✅|| -|PricingRecipe|✅|| -|PrivacySettings|✅|| -|ProcessFlowMigration|✅|| -|ProductAttrDisplayConfig|❌|Not supported, but support could be added| -|ProductAttributeSet|✅|| -|ProductSettings|✅|| -|ProductSpecificationRecType|❌|Not supported, but support could be added| -|ProductSpecificationType|❌|Not supported, but support could be added| -|Profile|✅|| -|ProfilePasswordPolicy|✅|| -|ProfileSessionSetting|✅|| -|Prompt|✅|| -|Queue|✅|| -|QueueRoutingConfig|✅|| -|QuickAction|✅|| -|QuickTextSettings|✅|| -|QuoteSettings|✅|| -|RealTimeEventSettings|✅|| -|RecAlrtDataSrcExpSetDef|❌|Not supported, but support could be added| -|RecommendationBuilderSettings|✅|| -|RecommendationStrategy|✅|| -|RecordActionDeployment|✅|| -|RecordAggregationDefinition|❌|Not supported, but support could be added| -|RecordAlertCategory|✅|| -|RecordAlertDataSource|✅|| -|RecordAlertTemplate|❌|Not supported, but support could be added| -|RecordPageSettings|✅|| -|RecordType|✅|| -|RedirectWhitelistUrl|✅|| -|ReferencedDashboard|❌|Not supported, but support could be added| -|ReferralMarketingSettings|✅|| -|RegisteredExternalService|❌|Not supported, but support could be added| -|RelatedRecordAssocCriteria|❌|Not supported, but support could be added| -|RelationshipGraphDefinition|✅|| -|RemoteSiteSetting|✅|| -|Report|✅|| -|ReportFolder|✅|| -|ReportType|✅|| -|RestrictionRule|✅|| -|RetailExecutionSettings|✅|| -|Role|✅|| -|SalesAgreementSettings|✅|| -|SalesWorkQueueSettings|✅|| -|SamlSsoConfig|✅|| -|SandboxSettings|✅|| -|SchedulingObjective|✅|| -|SchedulingRule|✅|| -|SchemaSettings|✅|| -|ScoreCategory|✅|| -|SearchSettings|✅|| -|SecuritySettings|✅|| -|ServiceAISetupDefinition|✅|| -|ServiceAISetupField|✅|| -|ServiceChannel|✅|| -|ServiceCloudVoiceSettings|✅|| -|ServicePresenceStatus|✅|| -|ServiceProcess|✅|| -|ServiceSetupAssistantSettings|✅|| -|SharingCriteriaRule|✅|| -|SharingGuestRule|✅|| -|SharingOwnerRule|✅|| -|SharingReason|✅|| -|SharingRules|⚠️|Supports deploy/retrieve but not source tracking| -|SharingSet|✅|| -|SharingSettings|✅|| -|SharingTerritoryRule|✅|| -|SiteDotCom|✅|| -|SiteSettings|✅|| -|Skill|✅|| -|SkillType|✅|| -|SlackApp|✅|| -|SocialCustomerServiceSettings|✅|| -|SocialProfileSettings|✅|| -|SourceTrackingSettings|✅|| -|StandardValue|❌|Not supported, but support could be added| -|StandardValueSet|✅|| -|StandardValueSetTranslation|✅|| -|StaticResource|✅|| -|StnryAssetEnvSrcCnfg|✅|| -|StreamingAppDataConnector|✅|| -|SubscriptionManagementSettings|✅|| -|SurveySettings|✅|| -|SustainabilityUom|✅|| -|SustnUomConversion|✅|| -|SvcCatalogCategory|✅|| -|SvcCatalogFulfillmentFlow|✅|| -|SvcCatalogItemDef|✅|| -|SynonymDictionary|✅|| -|SystemNotificationSettings|✅|| -|Territory|✅|| -|Territory2|✅|| -|Territory2Model|✅|| -|Territory2Rule|✅|| -|Territory2Settings|✅|| -|Territory2Type|✅|| -|TimeSheetTemplate|✅|| -|TimelineObjectDefinition|✅|| -|TopicsForObjects|✅|| -|TrailheadSettings|✅|| -|TransactionSecurityPolicy|✅|| -|Translations|✅|| -|TrialOrgSettings|✅|| -|UIObjectRelationConfig|✅|| -|UiPlugin|✅|| -|UserAccessPolicy|✅|| -|UserAuthCertificate|✅|| -|UserCriteria|✅|| -|UserEngagementSettings|✅|| -|UserInterfaceSettings|✅|| -|UserManagementSettings|✅|| -|UserProfileSearchScope|✅|| -|UserProvisioningConfig|✅|| -|ValidationRule|✅|| -|VehicleAssetEmssnSrcCnfg|✅|| -|ViewDefinition|✅|| -|VirtualVisitConfig|❌|Not supported, but support could be added| -|VoiceSettings|✅|| -|WarrantyLifecycleMgmtSettings|✅|| -|WaveApplication|✅|| -|WaveComponent|✅|| -|WaveDashboard|✅|| -|WaveDataflow|✅|| -|WaveDataset|✅|| -|WaveLens|✅|| -|WaveRecipe|✅|| -|WaveTemplateBundle|✅|| -|WaveXmd|✅|| -|Web3Settings|✅|| -|WebLink|✅|| -|WebStoreBundle|❌|Not supported, but support could be added| -|WebStoreTemplate|✅|| -|WebToXSettings|✅|| -|WorkDotComSettings|✅|| -|WorkSkillRouting|✅|| -|Workflow|✅|| -|WorkflowAlert|✅|| -|WorkflowFieldUpdate|✅|| -|WorkflowFlowAction|❌|Not supported, but support could be added| -|WorkflowKnowledgePublish|✅|| -|WorkflowOutboundMessage|✅|| -|WorkflowRule|✅|| -|WorkflowSend|✅|| -|WorkflowTask|✅|| -|WorkforceEngagementSettings|✅|| - - +| Metadata Type | Support | Notes | +| :------------------------------------- | :------ | :--------------------------------------------------------------- | +| AIApplication | ✅ | | +| AIApplicationConfig | ✅ | | +| AIReplyRecommendationsSettings | ✅ | | +| AIScoringModelDefVersion | ✅ | | +| AIScoringModelDefinition | ✅ | | +| AIUsecaseDefinition | ⚠️ | Supports deploy/retrieve but not source tracking | +| AccountForecastSettings | ✅ | | +| AccountInsightsSettings | ✅ | | +| AccountIntelligenceSettings | ✅ | | +| AccountRelationshipShareRule | ✅ | | +| AccountSettings | ✅ | | +| AccountingFieldMapping | ✅ | | +| AccountingModelConfig | ✅ | | +| AccountingSettings | ✅ | | +| AcctMgrTargetSettings | ✅ | | +| ActionLauncherItemDef | ✅ | | +| ActionLinkGroupTemplate | ✅ | | +| ActionPlanTemplate | ✅ | | +| ActionableListDefinition | ✅ | | +| ActionsSettings | ✅ | | +| ActivationPlatform | ✅ | | +| ActivitiesSettings | ✅ | | +| AddressSettings | ✅ | | +| AdvAccountForecastSet | ✅ | | +| AdvAcctForecastDimSource | ✅ | | +| AdvAcctForecastPeriodGroup | ✅ | | +| Ai4mSettings | ✅ | | +| AnalyticSnapshot | ✅ | | +| AnalyticsSettings | ✅ | | +| AnimationRule | ✅ | | +| ApexClass | ✅ | | +| ApexComponent | ✅ | | +| ApexEmailNotifications | ✅ | | +| ApexPage | ✅ | | +| ApexSettings | ✅ | | +| ApexTestSuite | ✅ | | +| ApexTrigger | ✅ | | +| AppAnalyticsSettings | ✅ | | +| AppExperienceSettings | ✅ | | +| AppMenu | ✅ | | +| ApplicationRecordTypeConfig | ✅ | | +| ApplicationSubtypeDefinition | ✅ | | +| AppointmentAssignmentPolicy | ✅ | | +| AppointmentSchedulingPolicy | ✅ | | +| ApprovalProcess | ✅ | | +| AssessmentConfiguration | ❌ | Not supported, but support could be added | +| AssessmentQuestion | ✅ | | +| AssessmentQuestionSet | ✅ | | +| AssignmentRules | ✅ | | +| AssistantContextItem | ✅ | | +| AssistantDefinition | ✅ | | +| AssistantSkillQuickAction | ✅ | | +| AssistantSkillSobjectAction | ✅ | | +| AssistantVersion | ✅ | | +| AssociationEngineSettings | ✅ | | +| Audience | ✅ | | +| AuraDefinitionBundle | ✅ | | +| AuthProvider | ✅ | | +| AutoResponseRules | ✅ | | +| AutomatedContactsSettings | ✅ | | +| BatchCalcJobDefinition | ✅ | | +| BatchProcessJobDefinition | ✅ | | +| BenefitAction | ✅ | | +| BlacklistedConsumer | ✅ | | +| BldgEnrgyIntensityCnfg | ✅ | | +| BlockchainSettings | ✅ | | +| Bot | ✅ | | +| BotBlock | ✅ | | +| BotBlockVersion | ❌ | Not supported, but support could be added | +| BotSettings | ✅ | | +| BotTemplate | ✅ | | +| BotVersion | ✅ | | +| BranchManagementSettings | ✅ | | +| BrandingSet | ✅ | | +| BriefcaseDefinition | ✅ | | +| BusinessHoursSettings | ✅ | | +| BusinessProcess | ✅ | | +| BusinessProcessGroup | ✅ | | +| BusinessProcessTypeDefinition | ✅ | | +| CMSConnectSource | ✅ | | +| CallCenter | ✅ | | +| CallCenterRoutingMap | ✅ | | +| CallCoachingMediaProvider | ⚠️ | Supports deploy/retrieve but not source tracking | +| CampaignInfluenceModel | ✅ | | +| CampaignSettings | ✅ | | +| CanvasMetadata | ✅ | | +| CareBenefitVerifySettings | ✅ | | +| CareLimitType | ✅ | | +| CareProviderAfflRoleConfig | ❌ | Not supported, but support could be added | +| CareProviderSearchConfig | ✅ | | +| CareRequestConfiguration | ✅ | | +| CareSystemFieldMapping | ✅ | | +| CaseSettings | ✅ | | +| CaseSubjectParticle | ✅ | | +| Certificate | ✅ | | +| ChannelLayout | ✅ | | +| ChannelObjectLinkingRule | ✅ | | +| ChatterAnswersSettings | ✅ | | +| ChatterEmailsMDSettings | ✅ | | +| ChatterExtension | ✅ | | +| ChatterSettings | ✅ | | +| ClaimFinancialSettings | ✅ | | +| ClaimMgmtFoundationEnabledSettings | ✅ | | +| ClauseCatgConfiguration | ✅ | | +| CleanDataService | ✅ | | +| CodeBuilderSettings | ✅ | | +| CollectionsDashboardSettings | ✅ | | +| CommandAction | ✅ | | +| CommerceSettings | ✅ | | +| CommsServiceConsoleSettings | ✅ | | +| CommunitiesSettings | ✅ | | +| Community | ✅ | | +| CommunityTemplateDefinition | ✅ | | +| CommunityThemeDefinition | ✅ | | +| CompactLayout | ✅ | | +| CompanySettings | ✅ | | +| ConnectedApp | ✅ | | +| ConnectedAppSettings | ✅ | | +| ContentAsset | ✅ | | +| ContentSettings | ✅ | | +| ContextDefinition | ❌ | Not supported, but support could be added (but not for tracking) | +| ContractSettings | ✅ | | +| ContractType | ❌ | Not supported, but support could be added | +| ConversationChannelDefinition | ✅ | | +| ConversationServiceIntegrationSettings | ✅ | | +| ConversationVendorInfo | ✅ | | +| ConversationalIntelligenceSettings | ✅ | | +| CorsWhitelistOrigin | ✅ | | +| CspTrustedSite | ✅ | | +| CurrencySettings | ✅ | | +| CustomAddressFieldSettings | ✅ | | +| CustomApplication | ✅ | | +| CustomApplicationComponent | ✅ | | +| CustomFeedFilter | ✅ | | +| CustomField | ✅ | | +| CustomHelpMenuSection | ✅ | | +| CustomIndex | ✅ | | +| CustomLabels | ✅ | | +| CustomMetadata | ✅ | | +| CustomNotificationType | ✅ | | +| CustomObject | ✅ | | +| CustomObjectTranslation | ✅ | | +| CustomPageWebLink | ✅ | | +| CustomPermission | ✅ | | +| CustomSite | ✅ | | +| CustomTab | ✅ | | +| CustomValue | ❌ | Not supported, but support could be added | +| CustomerDataPlatformSettings | ✅ | | +| CustomizablePropensityScoringSettings | ✅ | | +| Dashboard | ✅ | | +| DashboardFolder | ✅ | | +| DataCategoryGroup | ✅ | | +| DataConnectorIngestApi | ✅ | | +| DataConnectorS3 | ✅ | | +| DataDotComSettings | ✅ | | +| DataImportManagementSettings | ✅ | | +| DataPackageKitDefinition | ✅ | | +| DataPackageKitObject | ✅ | | +| DataSource | ✅ | | +| DataSourceBundleDefinition | ✅ | | +| DataSourceObject | ✅ | | +| DataSourceTenant | ✅ | | +| DataSrcDataModelFieldMap | ✅ | | +| DataStreamDefinition | ✅ | | +| DataStreamTemplate | ✅ | | +| DataWeaveResource | ✅ | | +| DecisionMatrixDefinition | ✅ | | +| DecisionMatrixDefinitionVersion | ✅ | | +| DecisionTable | ✅ | | +| DecisionTableDatasetLink | ✅ | | +| DelegateGroup | ✅ | | +| DeploymentSettings | ✅ | | +| DevHubSettings | ✅ | | +| DigitalExperience | ✅ | | +| DigitalExperienceBundle | ✅ | | +| DigitalExperienceConfig | ✅ | | +| DisclosureDefinition | ✅ | | +| DisclosureDefinitionVersion | ✅ | | +| DisclosureType | ✅ | | +| DiscoveryAIModel | ✅ | | +| DiscoveryGoal | ✅ | | +| DiscoverySettings | ✅ | | +| DiscoveryStory | ❌ | Not supported, but support could be added | +| Document | ✅ | | +| DocumentCategory | ❌ | Not supported, but support could be added | +| DocumentCategoryDocumentType | ❌ | Not supported, but support could be added | +| DocumentChecklistSettings | ✅ | | +| DocumentFolder | ✅ | | +| DocumentGenerationSetting | ✅ | | +| DocumentType | ✅ | | +| DuplicateRule | ✅ | | +| DynamicFormsSettings | ✅ | | +| EACSettings | ✅ | | +| ESignatureConfig | ✅ | | +| ESignatureEnvelopeConfig | ✅ | | +| EclairGeoData | ✅ | | +| EinsteinAgentSettings | ✅ | | +| EinsteinAssistantSettings | ✅ | | +| EinsteinDealInsightsSettings | ✅ | | +| EinsteinDocumentCaptureSettings | ✅ | | +| EinsteinGptSettings | ✅ | | +| EmailAdministrationSettings | ✅ | | +| EmailFolder | ✅ | | +| EmailIntegrationSettings | ✅ | | +| EmailServicesFunction | ✅ | | +| EmailTemplate | ✅ | | +| EmailTemplateFolder | ✅ | | +| EmailTemplateSettings | ✅ | | +| EmbeddedServiceBranding | ✅ | | +| EmbeddedServiceConfig | ✅ | | +| EmbeddedServiceFlowConfig | ✅ | | +| EmbeddedServiceLiveAgent | ✅ | | +| EmbeddedServiceMenuSettings | ✅ | | +| EmployeeDataSyncProfile | ❌ | Not supported, but support could be added | +| EmployeeFieldAccessSettings | ✅ | | +| EmployeeUserSettings | ✅ | | +| EnhancedNotesSettings | ✅ | | +| EntitlementProcess | ✅ | | +| EntitlementSettings | ✅ | | +| EntitlementTemplate | ✅ | | +| EscalationRules | ✅ | | +| EssentialsSettings | ✅ | | +| EventSettings | ✅ | | +| ExperienceBundle | ✅ | | +| ExperienceBundleSettings | ✅ | | +| ExperiencePropertyTypeBundle | ✅ | | +| ExplainabilityActionDefinition | ✅ | | +| ExplainabilityActionVersion | ✅ | | +| ExplainabilityMsgTemplate | ✅ | | +| ExpressionSetDefinition | ✅ | | +| ExpressionSetDefinitionVersion | ✅ | | +| ExpressionSetMessageToken | ❌ | Not supported, but support could be added | +| ExpressionSetObjectAlias | ❌ | Not supported, but support could be added | +| ExternalAIModel | ❌ | Not supported, but support could be added | +| ExternalClientAppSettings | ✅ | | +| ExternalClientApplication | ✅ | | +| ExternalCredential | ✅ | | +| ExternalDataConnector | ✅ | | +| ExternalDataSource | ✅ | | +| ExternalDataSrcDescriptor | ❌ | Not supported, but support could be added | +| ExternalDataTranField | ❌ | Not supported, but support could be added | +| ExternalDataTranObject | ❌ | Not supported, but support could be added | +| ExternalDocStorageConfig | ❌ | Not supported, but support could be added | +| ExternalServiceRegistration | ✅ | | +| ExtlClntAppGlobalOauthSettings | ✅ | | +| ExtlClntAppMobileConfigurablePolicies | ✅ | | +| ExtlClntAppMobileSettings | ✅ | | +| ExtlClntAppOauthConfigurablePolicies | ✅ | | +| ExtlClntAppOauthSettings | ✅ | | +| FeatureParameterBoolean | ✅ | | +| FeatureParameterDate | ✅ | | +| FeatureParameterInteger | ✅ | | +| FieldRestrictionRule | ✅ | | +| FieldServiceMobileExtension | ✅ | | +| FieldServiceSettings | ✅ | | +| FieldSet | ✅ | | +| FieldSrcTrgtRelationship | ✅ | | +| FileUploadAndDownloadSecuritySettings | ✅ | | +| FilesConnectSettings | ✅ | | +| FlexiPage | ✅ | | +| Flow | ✅ | | +| FlowCategory | ✅ | | +| FlowDefinition | ⚠️ | Supports deploy/retrieve but not source tracking | +| FlowSettings | ✅ | | +| FlowTest | ✅ | | +| ForecastingFilter | ✅ | | +| ForecastingFilterCondition | ✅ | | +| ForecastingObjectListSettings | ✅ | | +| ForecastingSettings | ✅ | | +| ForecastingSourceDefinition | ✅ | | +| ForecastingType | ✅ | | +| ForecastingTypeSource | ✅ | | +| FormulaSettings | ✅ | | +| FuelType | ✅ | | +| FuelTypeSustnUom | ✅ | | +| FunctionReference | ⚠️ | Supports deploy/retrieve but not source tracking | +| FundraisingConfig | ✅ | | +| GatewayProviderPaymentMethodType | ✅ | | +| GenAiFunction | ❌ | Not supported, but support could be added | +| GenAiPlugin | ❌ | Not supported, but support could be added | +| GlobalValueSet | ✅ | | +| GlobalValueSetTranslation | ✅ | | +| GoogleAppsSettings | ✅ | | +| Group | ✅ | | +| HighVelocitySalesSettings | ✅ | | +| HomePageComponent | ✅ | | +| HomePageLayout | ✅ | | +| IPAddressRange | ✅ | | +| Icon | ✅ | | +| IdeasSettings | ✅ | | +| IdentityProviderSettings | ✅ | | +| IdentityVerificationProcDef | ✅ | | +| IframeWhiteListUrlSettings | ✅ | | +| InboundCertificate | ✅ | | +| InboundNetworkConnection | ✅ | | +| IncidentMgmtSettings | ✅ | | +| IncludeEstTaxInQuoteSettings | ✅ | | +| Index | ⚠️ | Supports deploy/retrieve but not source tracking | +| IndustriesAutomotiveSettings | ✅ | | +| IndustriesContextSettings | ✅ | | +| IndustriesEinsteinFeatureSettings | ✅ | | +| IndustriesLoyaltySettings | ✅ | | +| IndustriesManufacturingSettings | ✅ | | +| IndustriesPricingSettings | ✅ | | +| IndustriesSettings | ✅ | | +| IndustriesUnifiedPromotionsSettings | ✅ | | +| InstalledPackage | ⚠️ | Supports deploy/retrieve but not source tracking | +| IntegrationProviderDef | ✅ | | +| InterestTaggingSettings | ✅ | | +| InternalDataConnector | ✅ | | +| InvLatePymntRiskCalcSettings | ✅ | | +| InventorySettings | ✅ | | +| InvocableActionSettings | ✅ | | +| IoTSettings | ✅ | | +| KeywordList | ✅ | | +| KnowledgeGenerationSettings | ✅ | | +| KnowledgeSettings | ✅ | | +| LanguageSettings | ✅ | | +| Layout | ✅ | | +| LeadConfigSettings | ✅ | | +| LeadConvertSettings | ✅ | | +| LearningAchievementConfig | ❌ | Not supported, but support could be added | +| Letterhead | ✅ | | +| LicensingSettings | ✅ | | +| LightningBolt | ✅ | | +| LightningComponentBundle | ✅ | | +| LightningExperienceSettings | ✅ | | +| LightningExperienceTheme | ✅ | | +| LightningMessageChannel | ✅ | | +| LightningOnboardingConfig | ✅ | | +| ListView | ✅ | | +| LiveAgentSettings | ✅ | | +| LiveChatAgentConfig | ✅ | | +| LiveChatButton | ✅ | | +| LiveChatDeployment | ✅ | | +| LiveChatSensitiveDataRule | ✅ | | +| LiveMessageSettings | ✅ | | +| LocationUse | ✅ | | +| LoyaltyProgramSetup | ⚠️ | Supports deploy/retrieve but not source tracking | +| MacroSettings | ✅ | | +| MailMergeSettings | ✅ | | +| ManagedContentType | ⚠️ | Supports deploy/retrieve but not source tracking | +| ManagedTopics | ✅ | | +| MapsAndLocationSettings | ✅ | | +| MarketSegmentDefinition | ✅ | | +| MarketingAppExtActivity | ❌ | Not supported, but support could be added | +| MarketingAppExtension | ✅ | | +| MatchingRules | ✅ | | +| MediaAdSalesSettings | ✅ | | +| MeetingsSettings | ✅ | | +| MessagingChannel | ⚠️ | Supports deploy/retrieve but not source tracking | +| MfgProgramTemplate | ✅ | | +| MfgServiceConsoleSettings | ✅ | | +| MilestoneType | ✅ | | +| MktCalcInsightObjectDef | ✅ | | +| MktDataTranObject | ✅ | | +| MlDomain | ✅ | | +| MobSecurityCertPinConfig | ✅ | | +| MobileApplicationDetail | ✅ | | +| MobileSecurityAssignment | ✅ | | +| MobileSecurityPolicy | ✅ | | +| MobileSettings | ✅ | | +| ModerationRule | ✅ | | +| MutingPermissionSet | ✅ | | +| MyDomainDiscoverableLogin | ✅ | | +| MyDomainSettings | ✅ | | +| NameSettings | ✅ | | +| NamedCredential | ✅ | | +| NavigationMenu | ✅ | | +| Network | ✅ | | +| NetworkBranding | ✅ | | +| NotificationTypeConfig | ✅ | | +| NotificationsSettings | ✅ | | +| OauthCustomScope | ✅ | | +| OauthOidcSettings | ✅ | | +| ObjectHierarchyRelationship | ✅ | | +| ObjectLinkingSettings | ✅ | | +| ObjectSourceTargetMap | ✅ | | +| OcrSampleDocument | ✅ | | +| OcrTemplate | ✅ | | +| OmniChannelPricingSettings | ✅ | | +| OmniChannelSettings | ✅ | | +| OmniDataTransform | ⚠️ | Supports deploy/retrieve but not source tracking | +| OmniIntegrationProcedure | ⚠️ | Supports deploy/retrieve but not source tracking | +| OmniInteractionAccessConfig | ⚠️ | Supports deploy/retrieve but not source tracking | +| OmniInteractionConfig | ⚠️ | Supports deploy/retrieve but not source tracking | +| OmniScript | ⚠️ | Supports deploy/retrieve but not source tracking | +| OmniSupervisorConfig | ✅ | | +| OmniUiCard | ⚠️ | Supports deploy/retrieve but not source tracking | +| OnlineSalesSettings | ✅ | | +| OpportunityInsightsSettings | ✅ | | +| OpportunityScoreSettings | ✅ | | +| OpportunitySettings | ✅ | | +| OrderManagementSettings | ✅ | | +| OrderSettings | ✅ | | +| OrgSettings | ✅ | | +| OutboundNetworkConnection | ✅ | | +| PardotEinsteinSettings | ✅ | | +| PardotSettings | ✅ | | +| ParticipantRole | ✅ | | +| PartyDataModelSettings | ✅ | | +| PathAssistant | ✅ | | +| PathAssistantSettings | ✅ | | +| PaymentGatewayProvider | ✅ | | +| PaymentsManagementEnabledSettings | ✅ | | +| PaymentsSettings | ✅ | | +| PermissionSet | ✅ | | +| PermissionSetGroup | ✅ | | +| PermissionSetLicenseDefinition | ✅ | | +| PersonAccountOwnerPowerUser | ✅ | | +| PicklistSettings | ✅ | | +| PicklistValue | ❌ | Not supported, but support could be added | +| PipelineInspMetricConfig | ✅ | | +| PlatformCachePartition | ✅ | | +| PlatformEventChannel | ✅ | | +| PlatformEventChannelMember | ✅ | | +| PlatformEventSettings | ✅ | | +| PlatformEventSubscriberConfig | ✅ | | +| PlatformSlackSettings | ✅ | | +| PortalDelegablePermissionSet | ❌ | Not supported, but support could be added | +| PortalsSettings | ✅ | | +| PostTemplate | ✅ | | +| PredictionBuilderSettings | ✅ | | +| PresenceDeclineReason | ✅ | | +| PresenceUserConfig | ✅ | | +| PricingRecipe | ✅ | | +| PrivacySettings | ✅ | | +| ProcessFlowMigration | ✅ | | +| ProductAttrDisplayConfig | ❌ | Not supported, but support could be added | +| ProductAttributeSet | ✅ | | +| ProductSettings | ✅ | | +| ProductSpecificationRecType | ❌ | Not supported, but support could be added | +| ProductSpecificationType | ❌ | Not supported, but support could be added | +| Profile | ✅ | | +| ProfilePasswordPolicy | ✅ | | +| ProfileSessionSetting | ✅ | | +| Prompt | ✅ | | +| Queue | ✅ | | +| QueueRoutingConfig | ✅ | | +| QuickAction | ✅ | | +| QuickTextSettings | ✅ | | +| QuoteSettings | ✅ | | +| RealTimeEventSettings | ✅ | | +| RecAlrtDataSrcExpSetDef | ❌ | Not supported, but support could be added | +| RecommendationBuilderSettings | ✅ | | +| RecommendationStrategy | ✅ | | +| RecordActionDeployment | ✅ | | +| RecordAggregationDefinition | ❌ | Not supported, but support could be added | +| RecordAlertCategory | ✅ | | +| RecordAlertDataSource | ✅ | | +| RecordAlertTemplate | ❌ | Not supported, but support could be added | +| RecordPageSettings | ✅ | | +| RecordType | ✅ | | +| RedirectWhitelistUrl | ✅ | | +| ReferencedDashboard | ❌ | Not supported, but support could be added | +| ReferralMarketingSettings | ✅ | | +| RegisteredExternalService | ❌ | Not supported, but support could be added | +| RelatedRecordAssocCriteria | ❌ | Not supported, but support could be added | +| RelationshipGraphDefinition | ✅ | | +| RemoteSiteSetting | ✅ | | +| Report | ✅ | | +| ReportFolder | ✅ | | +| ReportType | ✅ | | +| RestrictionRule | ✅ | | +| RetailExecutionSettings | ✅ | | +| Role | ✅ | | +| SalesAgreementSettings | ✅ | | +| SalesWorkQueueSettings | ✅ | | +| SamlSsoConfig | ✅ | | +| SandboxSettings | ✅ | | +| SchedulingObjective | ✅ | | +| SchedulingRule | ✅ | | +| SchemaSettings | ✅ | | +| ScoreCategory | ✅ | | +| SearchSettings | ✅ | | +| SecuritySettings | ✅ | | +| ServiceAISetupDefinition | ✅ | | +| ServiceAISetupField | ✅ | | +| ServiceChannel | ✅ | | +| ServiceCloudVoiceSettings | ✅ | | +| ServicePresenceStatus | ✅ | | +| ServiceProcess | ✅ | | +| ServiceSetupAssistantSettings | ✅ | | +| SharingCriteriaRule | ✅ | | +| SharingGuestRule | ✅ | | +| SharingOwnerRule | ✅ | | +| SharingReason | ✅ | | +| SharingRules | ⚠️ | Supports deploy/retrieve but not source tracking | +| SharingSet | ✅ | | +| SharingSettings | ✅ | | +| SharingTerritoryRule | ✅ | | +| SiteDotCom | ✅ | | +| SiteSettings | ✅ | | +| Skill | ✅ | | +| SkillType | ✅ | | +| SlackApp | ✅ | | +| SocialCustomerServiceSettings | ✅ | | +| SocialProfileSettings | ✅ | | +| SourceTrackingSettings | ✅ | | +| StandardValue | ❌ | Not supported, but support could be added | +| StandardValueSet | ✅ | | +| StandardValueSetTranslation | ✅ | | +| StaticResource | ✅ | | +| StnryAssetEnvSrcCnfg | ✅ | | +| StreamingAppDataConnector | ✅ | | +| SubscriptionManagementSettings | ✅ | | +| SurveySettings | ✅ | | +| SustainabilityUom | ✅ | | +| SustnUomConversion | ✅ | | +| SvcCatalogCategory | ✅ | | +| SvcCatalogFulfillmentFlow | ✅ | | +| SvcCatalogItemDef | ✅ | | +| SynonymDictionary | ✅ | | +| SystemNotificationSettings | ✅ | | +| Territory | ✅ | | +| Territory2 | ✅ | | +| Territory2Model | ✅ | | +| Territory2Rule | ✅ | | +| Territory2Settings | ✅ | | +| Territory2Type | ✅ | | +| TimeSheetTemplate | ✅ | | +| TimelineObjectDefinition | ✅ | | +| TopicsForObjects | ✅ | | +| TrailheadSettings | ✅ | | +| TransactionSecurityPolicy | ✅ | | +| Translations | ✅ | | +| TrialOrgSettings | ✅ | | +| UIObjectRelationConfig | ✅ | | +| UiPlugin | ✅ | | +| UserAccessPolicy | ✅ | | +| UserAuthCertificate | ✅ | | +| UserCriteria | ✅ | | +| UserEngagementSettings | ✅ | | +| UserInterfaceSettings | ✅ | | +| UserManagementSettings | ✅ | | +| UserProfileSearchScope | ✅ | | +| UserProvisioningConfig | ✅ | | +| ValidationRule | ✅ | | +| VehicleAssetEmssnSrcCnfg | ✅ | | +| ViewDefinition | ✅ | | +| VirtualVisitConfig | ❌ | Not supported, but support could be added | +| VoiceSettings | ✅ | | +| WarrantyLifecycleMgmtSettings | ✅ | | +| WaveApplication | ✅ | | +| WaveComponent | ✅ | | +| WaveDashboard | ✅ | | +| WaveDataflow | ✅ | | +| WaveDataset | ✅ | | +| WaveLens | ✅ | | +| WaveRecipe | ✅ | | +| WaveTemplateBundle | ✅ | | +| WaveXmd | ✅ | | +| Web3Settings | ✅ | | +| WebLink | ✅ | | +| WebStoreBundle | ❌ | Not supported, but support could be added | +| WebStoreTemplate | ✅ | | +| WebToXSettings | ✅ | | +| WorkDotComSettings | ✅ | | +| WorkSkillRouting | ✅ | | +| Workflow | ✅ | | +| WorkflowAlert | ✅ | | +| WorkflowFieldUpdate | ✅ | | +| WorkflowFlowAction | ❌ | Not supported, but support could be added | +| WorkflowKnowledgePublish | ✅ | | +| WorkflowOutboundMessage | ✅ | | +| WorkflowRule | ✅ | | +| WorkflowSend | ✅ | | +| WorkflowTask | ✅ | | +| WorkforceEngagementSettings | ✅ | | ## Next Release (v60) @@ -609,7 +607,7 @@ v60 introduces the following new types. Here's their current level of support ## Additional Types -> The following types are supported by this library but not in the coverage reports for either version. These are typically +> The following types are supported by this library but not in the coverage reports for either version. These are typically > > 1. types that have been removed from the metadata API but were supported in previous versions > 1. types that are available for pilots but not officially part of the metadata API (use with caution) diff --git a/package.json b/package.json index 6effcec7ab..d751acc228 100644 --- a/package.json +++ b/package.json @@ -36,8 +36,7 @@ "jszip": "^3.10.1", "mime": "2.6.0", "minimatch": "^5.1.6", - "proxy-agent": "^6.3.1", - "unzipper": "0.10.14" + "proxy-agent": "^6.3.1" }, "devDependencies": { "@salesforce/cli-plugins-testkit": "^4.4.10", @@ -52,7 +51,6 @@ "@types/minimatch": "^5.1.2", "@types/proxy-from-env": "^1.0.3", "@types/shelljs": "^0.8.13", - "@types/unzipper": "^0.10.7", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", "chai": "^4.3.8", diff --git a/src/client/metadataApiRetrieve.ts b/src/client/metadataApiRetrieve.ts index 48c6119907..e12035019c 100644 --- a/src/client/metadataApiRetrieve.ts +++ b/src/client/metadataApiRetrieve.ts @@ -6,7 +6,7 @@ */ import * as path from 'path'; import * as fs from 'graceful-fs'; -import * as unzipper from 'unzipper'; +import * as JSZip from 'jszip'; import { asBoolean, isString } from '@salesforce/ts-types'; import { Messages, SfError, Lifecycle } from '@salesforce/core'; import { ensureArray } from '@salesforce/kit'; @@ -185,9 +185,21 @@ export class MetadataApiRetrieve extends MetadataTransfer< fs.writeFileSync(zipFilePath, zipFileContents); if (this.options.unzip) { - const dir = await unzipper.Open.buffer(zipFileContents); + const zip = await JSZip.loadAsync(zipFileContents, { base64: true, createFolders: true }); const extractPath = path.join(this.options.output, path.parse(name).name); - await dir.extract({ path: extractPath }); + fs.mkdirSync(extractPath, { recursive: true }); + for (const filePath of Object.keys(zip.files)) { + const zipObj = zip.file(filePath); + if (!zipObj || zipObj?.dir) { + fs.mkdirSync(path.join(extractPath, filePath), { recursive: true }); + } else { + // eslint-disable-next-line no-await-in-loop + const content = await zipObj?.async('nodebuffer'); + if (content) { + fs.writeFileSync(path.join(extractPath, filePath), content); + } + } + } } } else { components = await this.extract(zipFileContents); diff --git a/src/convert/transformers/staticResourceMetadataTransformer.ts b/src/convert/transformers/staticResourceMetadataTransformer.ts index 0893bd10f5..6c2e2e80ba 100644 --- a/src/convert/transformers/staticResourceMetadataTransformer.ts +++ b/src/convert/transformers/staticResourceMetadataTransformer.ts @@ -8,7 +8,6 @@ import { basename, dirname, isAbsolute, join, relative } from 'path'; import { Readable } from 'stream'; import * as JSZip from 'jszip'; import { getExtension } from 'mime'; -import { CentralDirectory, Open } from 'unzipper'; import { JsonMap } from '@salesforce/ts-types'; import { createWriteStream } from 'graceful-fs'; import { Logger, Messages, SfError } from '@salesforce/core'; @@ -116,20 +115,21 @@ export class StaticResourceMetadataTransformer extends BaseMetadataTransformer { if (shouldUnzipArchive) { // for the bulk of static resource writing we'll start writing ASAP // we'll still defer writing the resource-meta.xml file by pushing it onto the writeInfos - await Promise.all( - ( - await openZipFile(component, content) - ).files - .filter((f) => f.type === 'File') - .map(async (f) => { - const path = join(baseContentPath, f.path); - const fullDest = isAbsolute(path) - ? path - : join(this.defaultDirectory ?? component.getPackageRelativePath('', 'source'), path); - // push onto the pipeline and start writing now - return this.pipeline(f.stream(), fullDest); - }) - ); + + const srZip = await getStaticResourceZip(component, content); + const pipelinePromises: Array> = []; + for (const filePath of Object.keys(srZip.files)) { + const zipObj = srZip.file(filePath); + if (zipObj && !zipObj.dir) { + const path = join(baseContentPath, filePath); + const fullDest = isAbsolute(path) + ? path + : join(this.defaultDirectory ?? component.getPackageRelativePath('', 'source'), path); + pipelinePromises.push(this.pipeline(new Readable().wrap(zipObj.nodeStream()), fullDest)); + } + } + + await Promise.all(pipelinePromises); } if (!xml) { throw messages.createError('error_parsing_xml', [component.fullName, component.type.name]); @@ -233,10 +233,10 @@ const componentIsExpandedArchive = async (component: SourceComponent): Promise { +async function getStaticResourceZip(component: SourceComponent, content: string): Promise { try { - return await Open.buffer(await component.tree.readFile(content)); + const staticResourceZip = await component.tree.readFile(content); + return await JSZip.loadAsync(staticResourceZip, { createFolders: true }); } catch (e) { throw new SfError(`Unable to open zip file ${content} for ${component.name} (${component.xml})`, 'BadZipFile', [ 'Check that your file really is a valid zip archive', diff --git a/src/resolve/treeContainers.ts b/src/resolve/treeContainers.ts index 43003be973..18d400e568 100644 --- a/src/resolve/treeContainers.ts +++ b/src/resolve/treeContainers.ts @@ -8,7 +8,7 @@ import { join, dirname, basename, normalize, sep } from 'path'; import { Readable } from 'stream'; import { statSync, existsSync, readdirSync, createReadStream, readFileSync } from 'graceful-fs'; -import * as unzipper from 'unzipper'; +import * as JSZip from 'jszip'; import { Messages, SfError } from '@salesforce/core'; import { baseName, parseMetadataXml } from '../utils'; import { SourcePath } from '../common'; @@ -120,66 +120,55 @@ export class NodeFSTreeContainer extends TreeContainer { } } -interface ZipEntry { - path: string; - stream?: () => unzipper.Entry; - buffer?: () => Promise; -} - /** - * A {@link TreeContainer} that utilizes the central directory of a zip file - * to perform I/O without unzipping it to the disk first. + * A {@link TreeContainer} that performs I/O without unzipping it to the disk first. */ export class ZipTreeContainer extends TreeContainer { - private tree = new Map(); + private zip: JSZip; - private constructor(directory: unzipper.CentralDirectory) { + private constructor(zip: JSZip) { super(); - this.populate(directory); + this.zip = zip; } - /** - * Creates a `ZipTreeContainer` from a Buffer of a zip file. - * - * @param buffer - Buffer of the zip file - * @returns A Promise of a `ZipTreeContainer` - */ public static async create(buffer: Buffer): Promise { - const directory = await unzipper.Open.buffer(buffer); - return new ZipTreeContainer(directory); + const zip = await JSZip.loadAsync(buffer, { createFolders: true }); + return new ZipTreeContainer(zip); } public exists(fsPath: string): boolean { - return this.tree.has(fsPath); + return !!this.match(fsPath); } public isDirectory(fsPath: string): boolean { - if (this.exists(fsPath)) { - return Array.isArray(this.tree.get(fsPath)); + const resolvedPath = this.match(fsPath); + if (resolvedPath) { + return this.ensureDirectory(resolvedPath); } throw new SfError(messages.getMessage('error_path_not_found', [fsPath]), 'LibraryError'); } public readDirectory(fsPath: string): string[] { - if (this.isDirectory(fsPath)) { - return (this.tree.get(fsPath) as ZipEntry[]).map((entry) => basename(entry.path)); + const resolvedPath = this.match(fsPath); + if (resolvedPath && this.ensureDirectory(resolvedPath)) { + // Remove trailing path sep if it exists. JSZip always adds them for directories but + // when comparing we call `dirname()` which does not include them. + const dirPath = resolvedPath.endsWith('/') ? resolvedPath.slice(0, -1) : resolvedPath; + return Object.keys(this.zip.files) + .filter((filePath) => dirname(filePath) === dirPath) + .map((filePath) => basename(filePath)); } throw new SfError(messages.getMessage('error_expected_directory_path', [fsPath]), 'LibraryError'); } - public readFile(fsPath: string): Promise { - if (!this.isDirectory(fsPath)) { - const matchingFile = this.tree.get(fsPath); - if (!matchingFile) { - throw new SfError(messages.getMessage('error_path_not_found', [matchingFile]), 'LibraryError'); - } - if (Array.isArray(matchingFile)) { - throw messages.createError('tooManyFiles', [fsPath]); + public async readFile(fsPath: string): Promise { + const resolvedPath = this.match(fsPath); + if (resolvedPath) { + const jsZipObj = this.zip.file(resolvedPath); + if (jsZipObj?.dir === false) { + return jsZipObj.async('nodebuffer'); } - if (matchingFile.buffer) { - return matchingFile.buffer(); - } - throw new SfError(`The file at path ${fsPath} does not have a buffer method.`); + throw new SfError(`Expected a file at path ${fsPath} but found a directory.`); } throw new SfError(messages.getMessage('error_expected_file_path', [fsPath]), 'LibraryError'); } @@ -190,43 +179,43 @@ export class ZipTreeContainer extends TreeContainer { } public stream(fsPath: string): Readable { - if (!this.isDirectory(fsPath)) { - const matchingFile = this.tree.get(fsPath); - if (!matchingFile) { - throw new SfError(messages.getMessage('error_path_not_found', [fsPath]), 'LibraryError'); - } - if (Array.isArray(matchingFile)) { - throw messages.createError('tooManyFiles', [fsPath]); + const resolvedPath = this.match(fsPath); + if (resolvedPath) { + const jsZipObj = this.zip.file(resolvedPath); + if (jsZipObj && !jsZipObj.dir) { + return new Readable().wrap(jsZipObj.nodeStream()); } - if (matchingFile.stream) { - return matchingFile.stream(); - } - throw new SfError(`The file at path ${fsPath} does not have a stream method.`); + throw new SfError(messages.getMessage('error_no_directory_stream', [this.constructor.name]), 'LibraryError'); } - throw new SfError(messages.getMessage('error_no_directory_stream', [this.constructor.name]), 'LibraryError'); + throw new SfError(messages.getMessage('error_expected_file_path', [fsPath]), 'LibraryError'); } - private populate(directory: unzipper.CentralDirectory): void { - for (const { path, type, stream, buffer } of directory.files) { - if (type === 'File') { - // normalize path to use OS separator since zip entries always use forward slash - const entry = { path: normalize(path), stream, buffer }; - this.tree.set(entry.path, entry); - this.ensureDirPathExists(entry); - } + // Finds a matching entry in the zip by first comparing basenames, then dirnames. + // Note that zip files always use forward slash separators, so paths within the + // zip files are normalized for the OS file system before comparing. + private match(fsPath: string): string | undefined { + // "dot" has a special meaning as a directory name and always matches. Just return it. + if (fsPath === '.') { + return fsPath; } + + const fsPathBasename = basename(fsPath); + const fsPathDirname = dirname(fsPath); + return Object.keys(this.zip.files).find((filePath) => { + const normFilePath = normalize(filePath); + if (basename(normFilePath) === fsPathBasename) { + return dirname(normFilePath) === fsPathDirname; + } + }); } - private ensureDirPathExists(entry: ZipEntry): void { - const dirPath = dirname(entry.path); - if (dirPath === entry.path) { - return; - } else if (!this.exists(dirPath)) { - this.tree.set(dirPath, [entry]); - this.ensureDirPathExists({ path: dirPath }); - } else { - (this.tree.get(dirPath) as ZipEntry[]).push(entry); + private ensureDirectory(dirPath: string): boolean { + if (dirPath) { + // JSZip can have directory entries or only file entries (with virtual directory entries) + const zipObj = this.zip.file(dirPath); + return zipObj?.dir === true || !zipObj; } + throw new SfError(messages.getMessage('error_path_not_found', [dirPath]), 'LibraryError'); } } diff --git a/test/client/metadataApiRetrieve.test.ts b/test/client/metadataApiRetrieve.test.ts index 4d544a9a03..df028ecffa 100644 --- a/test/client/metadataApiRetrieve.test.ts +++ b/test/client/metadataApiRetrieve.test.ts @@ -10,7 +10,6 @@ import { Messages } from '@salesforce/core'; import { assert, expect } from 'chai'; import chai = require('chai'); import deepEqualInAnyOrder = require('deep-equal-in-any-order'); -import * as unzipper from 'unzipper'; import { SinonStub } from 'sinon'; import { getString } from '@salesforce/ts-types'; import * as fs from 'graceful-fs'; @@ -441,20 +440,30 @@ describe('MetadataApiRetrieve', () => { describe('post', () => { const output = join('mdapi', 'retrieve', 'dir'); const format = 'metadata'; - const zipFile = 'abcd1234'; + // This is what real zip file contents look like from the server + const zipFile = `UEsDBBQACAgIACKPFlcAAAAAAAAAAAAAAAAiAAAAdW5wYWNrYWdlZC9jbGFzc2VzL1BhZ2VkUmVzdWx +0LmNsc53MsQoCMQwG4P2eIu/hoojDLSo6ikPahl6l15Ym4cDj3t2qm5Oa4efnhy9FTQwWpiAD8IA1JA82IjMc0ZM7EWsUmDtot95 +oxV1CE8m9hvLGfRLyVKE0cQ53ghk8yQr4GUv3td3raFr9Q0sWjL3QuM2a5JcPB3MjK5crVLK5Ov6wywNQSwcIZ6ozvYIAAAAgAQA +AUEsDBBQACAgIACKPFlcAAAAAAAAAAAAAAAArAAAAdW5wYWNrYWdlZC9jbGFzc2VzL1BhZ2VkUmVzdWx0LmNscy1tZXRhLnhtbE2 +NQQrCMBBF9zlFyN5MFJUiaUoRPIG6H9KogTYJnbH0+BYq4t+9z4Nnm3no5RRGijnVaquNkiH53MX0rNXtetlUqnHCtiXM5x6J5OI +nqtWLuZwAKGPR9MijD9rnAXbGHMHsYQiMHTIqJ+QyiyXe14g7VNpY+DtWgxj5Ta71HKdg4YvCwi/txAdQSwcIwX3rpIgAAACuAAA +AUEsDBBQACAgIACKPFlcAAAAAAAAAAAAAAAAWAAAAdW5wYWNrYWdlZC9wYWNrYWdlLnhtbE2OywrCMBBF9/2KkL2ZKCpF0hQRXBf +RD4jpWIvNgyZK/XtDa9FZzRkuZ64oB9ORF/ahdbagS8YpQatd3dqmoJfzcZHTUmaiUvqhGiQpbUNB7zH6HUBwyrNwc71Gpp2BFed +b4GswGFWtoqIyI2lEfHsM0z6yQXNNL2WVlPUJw7OLAubjL2aVQbn3OBw6FYKAkScj/CnFt77c5IwLmCkT8G0tsw9QSwcIAYbHtaM +AAADnAAAAUEsBAhQAFAAICAgAIo8WV2eqM72CAAAAIAEAACIAAAAAAAAAAAAAAAAAAAAAAHVucGFja2FnZWQvY2xhc3Nlcy9QYWd +lZFJlc3VsdC5jbHNQSwECFAAUAAgICAAijxZXwX3rpIgAAACuAAAAKwAAAAAAAAAAAAAAAADSAAAAdW5wYWNrYWdlZC9jbGFzc2V +zL1BhZ2VkUmVzdWx0LmNscy1tZXRhLnhtbFBLAQIUABQACAgIACKPFlcBhse1owAAAOcAAAAWAAAAAAAAAAAAAAAAALMBAAB1bnB +hY2thZ2VkL3BhY2thZ2UueG1sUEsFBgAAAAADAAMA7QAAAJoCAAAAAA==`; const zipFileContents = Buffer.from(zipFile, 'base64'); const usernameOrConnection = 'retrieve@test.org'; const fakeResults = { status: RequestStatus.Succeeded, zipFile } as MetadataApiRetrieveStatus; let writeFileStub: SinonStub; - let openBufferStub: SinonStub; - let extractStub: SinonStub; + let mkdirStub: SinonStub; const mdapiRetrieveExtractStub = $$.SANDBOX.stub().resolves({}); beforeEach(() => { writeFileStub = $$.SANDBOX.stub(fs, 'writeFileSync'); - extractStub = $$.SANDBOX.stub().resolves(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument - openBufferStub = $$.SANDBOX.stub(unzipper.Open, 'buffer').resolves({ extract: extractStub } as any); + mkdirStub = $$.SANDBOX.stub(fs, 'mkdirSync'); }); it('should write the retrieved zip when format=metadata', async () => { @@ -466,7 +475,7 @@ describe('MetadataApiRetrieve', () => { expect(writeFileStub.calledOnce).to.be.true; expect(writeFileStub.firstCall.args[0]).to.equal(join(output, 'unpackaged.zip')); expect(writeFileStub.firstCall.args[1]).to.deep.equal(zipFileContents); - expect(openBufferStub.called).to.be.false; + expect(mkdirStub.called).to.be.false; expect(mdapiRetrieveExtractStub.called).to.be.false; }); @@ -476,11 +485,19 @@ describe('MetadataApiRetrieve', () => { mdapiRetrieve.extract = mdapiRetrieveExtractStub; await mdapiRetrieve.post(fakeResults); - expect(writeFileStub.calledOnce).to.be.true; + const unpkg1Dir = join(output, 'unpackaged'); + const unpkg2Dir = join(unpkg1Dir, 'unpackaged/'); + const classesDir = join(unpkg2Dir, 'classes/'); + + expect(writeFileStub.called).to.be.true; expect(writeFileStub.firstCall.args[0]).to.equal(join(output, 'unpackaged.zip')); expect(writeFileStub.firstCall.args[1]).to.deep.equal(zipFileContents); - expect(openBufferStub.called).to.be.true; - expect(extractStub.called).to.be.true; + expect(writeFileStub.secondCall.args[0]).to.equal(join(classesDir, 'PagedResult.cls')); + expect(writeFileStub.thirdCall.args[0]).to.equal(join(classesDir, 'PagedResult.cls-meta.xml')); + expect(mkdirStub.called).to.be.true; + expect(mkdirStub.firstCall.args[0]).to.equal(unpkg1Dir); + expect(mkdirStub.secondCall.args[0]).to.equal(unpkg2Dir); + expect(mkdirStub.thirdCall.args[0]).to.equal(classesDir); expect(mdapiRetrieveExtractStub.called).to.be.false; }); @@ -494,7 +511,7 @@ describe('MetadataApiRetrieve', () => { expect(writeFileStub.calledOnce).to.be.true; expect(writeFileStub.firstCall.args[0]).to.equal(join(output, zipFileName)); expect(writeFileStub.firstCall.args[1]).to.deep.equal(zipFileContents); - expect(openBufferStub.called).to.be.false; + expect(mkdirStub.called).to.be.false; expect(mdapiRetrieveExtractStub.called).to.be.false; }); }); diff --git a/test/convert/transformers/staticResourceMetadataTransformer.test.ts b/test/convert/transformers/staticResourceMetadataTransformer.test.ts index 5ed5472645..a984cc0b94 100644 --- a/test/convert/transformers/staticResourceMetadataTransformer.test.ts +++ b/test/convert/transformers/staticResourceMetadataTransformer.test.ts @@ -11,7 +11,6 @@ import { Messages } from '@salesforce/core'; import { assert, expect } from 'chai'; import { createSandbox } from 'sinon'; import * as JSZip from 'jszip'; -import { CentralDirectory, Entry, Open } from 'unzipper'; import chai = require('chai'); import { registry, SourceComponent, VirtualTreeContainer, WriteInfo } from '../../../src'; import { StaticResourceMetadataTransformer } from '../../../src/convert/transformers/staticResourceMetadataTransformer'; @@ -202,23 +201,6 @@ describe('StaticResourceMetadataTransformer', () => { }); describe('toSourceFormat', () => { - const mockCentralDirectory = { - files: [ - { - path: 'a', - type: 'Directory', - // @ts-expect-error mock - stream: (): Entry => null, - }, - { - path: 'b/c.css', - type: 'File', - // @ts-expect-error mock - stream: (): Entry => null, - }, - ], - } as CentralDirectory; - it('should rename extension from .resource to a mime extension for content file', async () => { const component = mixedContentSingleFile.COMPONENT; const { type, content, xml } = component; @@ -313,7 +295,11 @@ describe('StaticResourceMetadataTransformer', () => { contentType: 'application/zip', }, }); - env.stub(Open, 'buffer').resolves(mockCentralDirectory); + + const filePath = join('b', 'c.css'); + const testZip = new JSZip().file(filePath, 'fake css content'); + env.stub(JSZip, 'loadAsync').resolves(testZip); + const expectedInfos: WriteInfo[] = [ { source: component.tree.stream(xml), @@ -329,8 +315,7 @@ describe('StaticResourceMetadataTransformer', () => { DEFAULT_PACKAGE_ROOT_SFDX, type.directoryName, mixedContentSingleFile.COMPONENT_NAMES[0], - 'b', - 'c.css' + filePath ) ); }); @@ -390,7 +375,11 @@ describe('StaticResourceMetadataTransformer', () => { contentType: 'application/zip', }, }); - env.stub(Open, 'buffer').resolves(mockCentralDirectory); + + const filePath = join('b', 'c.css'); + const testZip = new JSZip().file(filePath, 'fake css content'); + env.stub(JSZip, 'loadAsync').resolves(testZip); + const expectedInfos: WriteInfo[] = [ { source: component.tree.stream(component.xml), diff --git a/test/mock/client/index.ts b/test/mock/client/index.ts index fcf8b42b54..046c341a7e 100644 --- a/test/mock/client/index.ts +++ b/test/mock/client/index.ts @@ -9,7 +9,9 @@ import * as JSZip from 'jszip'; export async function createMockZip(entries: string[]): Promise { const zip = JSZip(); for (const entry of entries) { - zip.file(entry, ''); + // Ensure only posix paths are added to zip files + const relPosixPath = entry.replace(/\\/g, '/'); + zip.file(relPosixPath, ''); } return zip.generateAsync({ type: 'nodebuffer', diff --git a/test/nuts/local/replacements/replacements.nut.ts b/test/nuts/local/replacements/replacements.nut.ts index 898f7a686b..87f1171bed 100644 --- a/test/nuts/local/replacements/replacements.nut.ts +++ b/test/nuts/local/replacements/replacements.nut.ts @@ -6,7 +6,7 @@ */ import * as path from 'path'; import * as fs from 'fs'; -import { Open } from 'unzipper'; +import * as JSZip from 'jszip'; import { TestSession } from '@salesforce/cli-plugins-testkit'; import { assert, expect } from 'chai'; import { ComponentSetBuilder, MetadataConverter } from '../../../../src'; @@ -14,6 +14,23 @@ import { ComponentSetBuilder, MetadataConverter } from '../../../../src'; describe('e2e replacements test', () => { let session: TestSession; + const extractZip = async (zipBuffer: Buffer, extractPath: string) => { + fs.mkdirSync(extractPath); + const zip = await JSZip.loadAsync(zipBuffer); + for (const filePath of Object.keys(zip.files)) { + const zipObj = zip.file(filePath); + if (!zipObj || zipObj?.dir) { + fs.mkdirSync(path.join(extractPath, filePath)); + } else { + // eslint-disable-next-line no-await-in-loop + const content = await zipObj?.async('nodebuffer'); + if (content) { + fs.writeFileSync(path.join(extractPath, filePath), content); + } + } + } + }; + before(async () => { session = await TestSession.create({ project: { @@ -49,7 +66,8 @@ describe('e2e replacements test', () => { type: 'zip', }); assert(zipBuffer, 'zipBuffer should be defined'); - await (await Open.buffer(zipBuffer)).extract({ path: path.join(session.project.dir, 'unzipped') }); + // extract zip files + await extractZip(zipBuffer, path.join(session.project.dir, 'unzipped')); }); it('class replacements as expected', async () => { @@ -81,17 +99,25 @@ describe('e2e replacements test', () => { ); }); it('static resource object replacements as expected', async () => { - const files = ( - await Open.file(path.join(session.project.dir, 'unzipped', 'staticresources', 'Test.resource')) - ).files.filter((f) => f.type === 'File'); + const srZipPath = path.join(session.project.dir, 'unzipped', 'staticresources', 'Test.resource'); + expect(fs.existsSync(srZipPath)).to.be.true; + const srZip = await JSZip.loadAsync(fs.readFileSync(srZipPath)); + + // static resource zip should have 2 files and a dir: + // 1. "folder/", 2. "folder/test2.css", 3. "folder/test.css" + expect(Object.entries(srZip.files).length).to.equal(3); - const buffers = await Promise.all(files.map(async (f) => f.buffer())); - buffers - .map((b) => b.toString()) - .map((contents) => { - expect(contents).to.not.include('placeholder'); - expect(contents).to.include('foo'); - }); + // Content of the 2 css files should have "foo", not "placeholder" (i.e., replaced) + for (const filePath of Object.keys(srZip.files)) { + const zipObj = srZip.file(filePath); + if (zipObj && !zipObj.dir) { + // eslint-disable-next-line no-await-in-loop + const content = await zipObj.async('nodebuffer'); + const contentAsString = content.toString(); + expect(contentAsString).to.not.include('placeholder'); + expect(contentAsString).to.include('foo'); + } + } }); }); }); diff --git a/test/resolve/treeContainers.test.ts b/test/resolve/treeContainers.test.ts index 6495a92385..76a93d897c 100644 --- a/test/resolve/treeContainers.test.ts +++ b/test/resolve/treeContainers.test.ts @@ -6,13 +6,12 @@ */ /* eslint-disable class-methods-use-this */ -import { join, normalize } from 'path'; +import { join } from 'path'; import { Readable } from 'stream'; import { Messages, SfError } from '@salesforce/core'; import { assert, expect } from 'chai'; import { createSandbox } from 'sinon'; import * as fs from 'graceful-fs'; -import * as unzipper from 'unzipper'; import * as JSZip from 'jszip'; import { MetadataResolver, @@ -29,6 +28,17 @@ const messages = Messages.loadMessages('@salesforce/source-deploy-retrieve', 'sd describe('Tree Containers', () => { const readDirResults = ['a.q', 'a.x-meta.xml', 'b', 'b.x-meta.xml', 'c.z', 'c.x-meta.xml']; + const streamToString = async (stream: Readable) => { + stream.setEncoding('utf-8'); + const chunks = []; + + for await (const chunk of stream) { + chunks.push(chunk); + } + + return chunks.join(''); + }; + describe('TreeContainer Base Class', () => { class TestTreeContainer extends TreeContainer { public readDirectory(): string[] { @@ -128,15 +138,20 @@ describe('Tree Containers', () => { let tree: ZipTreeContainer; let zipBuffer: Buffer; - const filesRoot = join('.', 'main', 'default'); - const moreFiles = join(filesRoot, 'morefiles'); + // + // NOTE: All files in zips use a forward slash as a file separator, so we build + // the zip using paths with hard-coded forward slashes, not OS specific seps. + // + + const filesRoot = 'main/default'; + const moreFiles = `${filesRoot}/morefiles`; before(async () => { const zip = new JSZip(); zip - ?.file(join(filesRoot, 'test.txt'), 'test text') - ?.file(join(filesRoot, 'test2.txt'), 'test text 2') - ?.file(join(moreFiles, 'test3.txt'), 'test text 3'); + ?.file(`${filesRoot}/test.txt`, 'test text') + ?.file(`${filesRoot}/test2.txt`, 'test text 2') + ?.file(`${moreFiles}/test3.txt`, 'test text 3'); zipBuffer = await zip.generateAsync({ type: 'nodebuffer', @@ -196,6 +211,10 @@ describe('Tree Containers', () => { }); it('should return correct directory entries for directory with only directories', () => { + expect(tree.readDirectory('main')).to.deep.equal(['default']); + }); + + it('should return correct directory entries for current directory character', () => { expect(tree.readDirectory('.')).to.deep.equal(['main']); }); @@ -216,13 +235,15 @@ describe('Tree Containers', () => { expect(contents).to.equal('test text'); }); - it('should throw an error if path is to directory', () => { - assert.throws( - // eslint-disable-next-line @typescript-eslint/no-misused-promises - () => tree.readFile(filesRoot), - SfError, - messages.getMessage('error_expected_file_path', [filesRoot]) - ); + it('should throw an error if path is to directory', async () => { + try { + await tree.readFile(filesRoot); + assert(false, 'Expected an error to be thrown'); + } catch (err) { + expect(err).to.be.instanceOf(SfError); + const errMsg = `Expected a file at path ${filesRoot} but found a directory.`; + expect(err).to.have.property('message', errMsg); + } }); }); @@ -235,20 +256,21 @@ describe('Tree Containers', () => { describe('stream', () => { it('should return a readable stream', async () => { const path = join(filesRoot, 'test.txt'); - const zipDir = await unzipper.Open.buffer(zipBuffer); - const expectedStream = zipDir.files.find((f) => normalize(f.path) === path)?.stream(); - assert(expectedStream); - const actual = tree.stream(path); - expect(actual instanceof Readable).to.be.true; - expect((actual as unzipper.Entry).path).to.equal(expectedStream.path); + const readableStream = tree.stream(path); + expect(readableStream instanceof Readable).to.be.true; + const contents = await streamToString(readableStream); + expect(contents).to.equal('test text'); }); it('should throw an error if given path is to a directory', () => { - assert.throws( - () => tree.stream(filesRoot), - SfError, - messages.getMessage('error_no_directory_stream', [tree.constructor.name]) - ); + try { + tree.stream(filesRoot); + assert(false, 'Expected an error to be thrown'); + } catch (err) { + expect(err).to.be.instanceOf(SfError); + const errMsg = messages.getMessage('error_no_directory_stream', [tree.constructor.name]); + expect(err).to.have.property('message', errMsg); + } }); }); }); diff --git a/yarn.lock b/yarn.lock index 57249d4bfb..8fff08d506 100644 --- a/yarn.lock +++ b/yarn.lock @@ -892,13 +892,6 @@ resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz#bf2e02a3dbd4aecaf95942ecd99b7402e03fad5e" integrity sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA== -"@types/unzipper@^0.10.7": - version "0.10.7" - resolved "https://registry.yarnpkg.com/@types/unzipper/-/unzipper-0.10.7.tgz#32efdf299a6f8d76f35c35fe10a91aea4ea57515" - integrity sha512-1yZanW3LWgY4wA6x0MyIkyI5rGILLHjXWAvvuz+xF2JzqBLG26ySL+VrSgjz9EWIYLv+icqv5RPW6FN4BJmsHw== - dependencies: - "@types/node" "*" - "@typescript-eslint/eslint-plugin@^5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" @@ -1287,29 +1280,11 @@ basic-ftp@^5.0.2: resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.3.tgz#b14c0fe8111ce001ec913686434fe0c2fb461228" integrity sha512-QHX8HLlncOLpy54mh+k/sWIFd0ThmRqwe9ZjELybGZK+tZ8rUb9VO0saKJUROTbE+KhzDUT7xziGpGrW8Kmd+g== -big-integer@^1.6.17: - version "1.6.51" - resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" - integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== - binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -binary@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79" - integrity sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg== - dependencies: - buffers "~0.1.1" - chainsaw "~0.1.0" - -bluebird@~3.4.1: - version "3.4.7" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" - integrity sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA== - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1357,11 +1332,6 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer-indexof-polyfill@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz#d2732135c5999c64b277fcf9b1abe3498254729c" - integrity sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A== - buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -1370,11 +1340,6 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" -buffers@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" - integrity sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ== - cacheable-lookup@^5.0.3: version "5.0.4" resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" @@ -1470,13 +1435,6 @@ chai@^4.3.8: pathval "^1.1.1" type-detect "^4.0.5" -chainsaw@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" - integrity sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ== - dependencies: - traverse ">=0.3.0 <0.4" - chalk@^2.0.0: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -1963,13 +1921,6 @@ dot-prop@^5.1.0: dependencies: is-obj "^2.0.0" -duplexer2@~0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" - integrity sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA== - dependencies: - readable-stream "^2.0.2" - ecdsa-sig-formatter@1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" @@ -2571,16 +2522,6 @@ fsevents@~2.3.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== -fstream@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" - integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" - function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -2783,7 +2724,7 @@ got@^11.8.6: p-cancelable "^2.0.0" responselike "^2.0.0" -graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.2, graceful-fs@^4.2.4: +graceful-fs@^4.1.15, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -3000,7 +2941,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.3: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -3544,11 +3485,6 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -listenercount@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/listenercount/-/listenercount-1.0.1.tgz#84c8a72ab59c4725321480c975e6508342e70937" - integrity sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ== - locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -3869,7 +3805,7 @@ minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -"mkdirp@>=0.5 0", mkdirp@~0.5.1: +mkdirp@~0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== @@ -4575,7 +4511,18 @@ readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.4.0, readable-stre string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^2.0.2, readable-stream@~2.3.6: +readable-stream@^4.0.0: + version "4.4.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.4.2.tgz#e6aced27ad3b9d726d8308515b9a1b98dc1b9d13" + integrity sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" + +readable-stream@~2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -4588,17 +4535,6 @@ readable-stream@^2.0.2, readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^4.0.0: - version "4.4.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.4.2.tgz#e6aced27ad3b9d726d8308515b9a1b98dc1b9d13" - integrity sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA== - dependencies: - abort-controller "^3.0.0" - buffer "^6.0.3" - events "^3.3.0" - process "^0.11.10" - string_decoder "^1.3.0" - readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -4723,13 +4659,6 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@2: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -4860,7 +4789,7 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== -setimmediate@^1.0.5, setimmediate@~1.0.4: +setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== @@ -5253,11 +5182,6 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -"traverse@>=0.3.0 <0.4": - version "0.3.9" - resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" - integrity sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ== - trim-newlines@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" @@ -5461,22 +5385,6 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== -unzipper@0.10.14: - version "0.10.14" - resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.10.14.tgz#d2b33c977714da0fbc0f82774ad35470a7c962b1" - integrity sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g== - dependencies: - big-integer "^1.6.17" - binary "~0.3.0" - bluebird "~3.4.1" - buffer-indexof-polyfill "~1.0.0" - duplexer2 "~0.1.4" - fstream "^1.0.12" - graceful-fs "^4.2.2" - listenercount "~1.0.1" - readable-stream "~2.3.6" - setimmediate "~1.0.4" - update-browserslist-db@^1.0.10: version "1.0.11" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940"