@salesforce/templates 66.13.9 → 66.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/generators/analyticsTemplateGenerator.js +1 -1
- package/lib/generators/analyticsTemplateGenerator.js.map +1 -1
- package/lib/generators/apexClassGenerator.js +1 -1
- package/lib/generators/apexClassGenerator.js.map +1 -1
- package/lib/generators/apexTriggerGenerator.js +1 -1
- package/lib/generators/apexTriggerGenerator.js.map +1 -1
- package/lib/generators/baseGenerator.js +3 -3
- package/lib/generators/baseGenerator.js.map +1 -1
- package/lib/generators/digitalExperienceSiteGenerator.js +2 -2
- package/lib/generators/digitalExperienceSiteGenerator.js.map +1 -1
- package/lib/generators/flexipageGenerator.js +2 -2
- package/lib/generators/flexipageGenerator.js.map +1 -1
- package/lib/generators/lightningAppGenerator.js +1 -1
- package/lib/generators/lightningAppGenerator.js.map +1 -1
- package/lib/generators/lightningComponentGenerator.js +1 -1
- package/lib/generators/lightningComponentGenerator.js.map +1 -1
- package/lib/generators/lightningEventGenerator.js +1 -1
- package/lib/generators/lightningEventGenerator.js.map +1 -1
- package/lib/generators/lightningInterfaceGenerator.js +1 -1
- package/lib/generators/lightningInterfaceGenerator.js.map +1 -1
- package/lib/generators/lightningTestGenerator.js +1 -1
- package/lib/generators/lightningTestGenerator.js.map +1 -1
- package/lib/generators/projectGenerator.js +1 -1
- package/lib/generators/projectGenerator.js.map +1 -1
- package/lib/generators/staticResourceGenerator.js +1 -1
- package/lib/generators/staticResourceGenerator.js.map +1 -1
- package/lib/generators/uiBundleGenerator.js +1 -1
- package/lib/generators/uiBundleGenerator.js.map +1 -1
- package/lib/generators/uiEmbeddingGenerator.js +7 -7
- package/lib/generators/uiEmbeddingGenerator.js.map +1 -1
- package/lib/generators/visualforceComponentGenerator.js +1 -1
- package/lib/generators/visualforceComponentGenerator.js.map +1 -1
- package/lib/generators/visualforcePageGenerator.js +1 -1
- package/lib/generators/visualforcePageGenerator.js.map +1 -1
- package/lib/i18n/i18n.d.ts +2 -2
- package/lib/i18n/i18n.js +2 -2
- package/lib/i18n/i18n.js.map +1 -1
- package/lib/i18n/localization.js +2 -1
- package/lib/i18n/localization.js.map +1 -1
- package/lib/service/gitRepoUtils.js +5 -5
- package/lib/service/gitRepoUtils.js.map +1 -1
- package/lib/templates/project/angularextapp/CHANGELOG.md +429 -0
- package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/package-lock.json +4076 -9105
- package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/package.json +3 -3
- package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/_f_/_ls_/__examples__/html-middleware-site-example.ts +41 -0
- package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/_f_/_ls_/__examples__/language-switcher-example.ts +41 -0
- package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/_f_/_ls_/build-language-url.ts +158 -0
- package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/_f_/_ls_/index.ts +28 -0
- package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/_f_/_ls_/language-switcher.html +22 -0
- package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/_f_/_ls_/language-switcher.ts +110 -0
- package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/_f_/_ls_/languages.ts +46 -0
- package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/auth/auth.service.ts +53 -21
- package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/components/ui/icon/icon.ts +6 -0
- package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/types/sf-globals.d.ts +14 -0
- package/lib/templates/project/angularextapp/package.json +1 -1
- package/lib/templates/project/angularextapp/scripts/org-setup-config-schema.mjs +14 -2
- package/lib/templates/project/angularextapp/scripts/org-setup-dev.mjs +27 -0
- package/lib/templates/project/angularextapp/scripts/org-setup-url.mjs +108 -0
- package/lib/templates/project/angularextapp/scripts/org-setup-xml.mjs +96 -0
- package/lib/templates/project/angularextapp/scripts/org-setup.mjs +195 -3
- package/lib/templates/project/angularintapp/CHANGELOG.md +429 -0
- package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/package-lock.json +4083 -9112
- package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/package.json +4 -4
- package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/components/ui/icon/icon.ts +6 -0
- package/lib/templates/project/angularintapp/package.json +1 -1
- package/lib/templates/project/angularintapp/scripts/org-setup-config-schema.mjs +14 -2
- package/lib/templates/project/angularintapp/scripts/org-setup-dev.mjs +27 -0
- package/lib/templates/project/angularintapp/scripts/org-setup-url.mjs +108 -0
- package/lib/templates/project/angularintapp/scripts/org-setup-xml.mjs +96 -0
- package/lib/templates/project/angularintapp/scripts/org-setup.mjs +195 -3
- package/lib/templates/project/reactexternalapp/CHANGELOG.md +533 -0
- package/lib/templates/project/reactexternalapp/README.md +2 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/package-lock.json +888 -640
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/package.json +6 -4
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/patches/aria-hidden+1.2.6.patch +45 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/pages/Login.tsx +3 -2
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/pages/Register.tsx +3 -2
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/searchableContentTypesService.ts +9 -6
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/cmsQueryFragment.ts +14 -3
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/contentTypeUtils.ts +7 -5
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/index.ts +1 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/searchChannel.ts +17 -1
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/types.ts +2 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/MergedSearchResults.tsx +16 -4
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SearchResults.tsx +1 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SourceSection.tsx +27 -4
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/DefaultFilterPanel.tsx +7 -1
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/hooks/useSearch.ts +12 -7
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/filterUtils.ts +24 -5
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/appLayout.tsx +42 -7
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/components/ObjectBreadcrumb.tsx +1 -5
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/pages/AccountObjectDetailPage.tsx +34 -47
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/pages/Home.tsx +3 -2
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/utils/accountFields.ts +1 -14
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/tsconfig.tsbuildinfo +1 -1
- package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleAuthUtils_Test.cls +141 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleAuthUtils_Test.cls-meta.xml +5 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleSocialLoginConfig.cls +23 -11
- package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleSocialLoginConfig_Test.cls +80 -0
- package/lib/templates/project/reactexternalapp/package.json +1 -1
- package/lib/templates/project/reactexternalapp/scripts/org-setup-config-schema.mjs +14 -2
- package/lib/templates/project/reactexternalapp/scripts/org-setup-dev.mjs +27 -0
- package/lib/templates/project/reactexternalapp/scripts/org-setup-url.mjs +108 -0
- package/lib/templates/project/reactexternalapp/scripts/org-setup-xml.mjs +96 -0
- package/lib/templates/project/reactexternalapp/scripts/org-setup.mjs +195 -3
- package/lib/templates/project/reactinternalapp/CHANGELOG.md +533 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/package-lock.json +892 -644
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/package.json +7 -5
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/patches/aria-hidden+1.2.6.patch +45 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/searchableContentTypesService.ts +9 -6
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/cmsQueryFragment.ts +14 -3
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/contentTypeUtils.ts +7 -5
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/index.ts +1 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/searchChannel.ts +17 -1
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/types.ts +2 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/MergedSearchResults.tsx +16 -4
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SearchResults.tsx +1 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SourceSection.tsx +27 -4
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/DefaultFilterPanel.tsx +7 -1
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/hooks/useSearch.ts +12 -7
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/filterUtils.ts +24 -5
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/appLayout.tsx +41 -7
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/components/ObjectBreadcrumb.tsx +1 -5
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/pages/AccountObjectDetailPage.tsx +40 -50
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/pages/Home.tsx +7 -2
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/utils/accountFields.ts +1 -14
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/tsconfig.tsbuildinfo +1 -1
- package/lib/templates/project/reactinternalapp/package.json +1 -1
- package/lib/templates/project/reactinternalapp/scripts/org-setup-config-schema.mjs +14 -2
- package/lib/templates/project/reactinternalapp/scripts/org-setup-dev.mjs +27 -0
- package/lib/templates/project/reactinternalapp/scripts/org-setup-url.mjs +108 -0
- package/lib/templates/project/reactinternalapp/scripts/org-setup-xml.mjs +96 -0
- package/lib/templates/project/reactinternalapp/scripts/org-setup.mjs +195 -3
- package/lib/templates/uiBundles/angularbasic/package-lock.json +4076 -9105
- package/lib/templates/uiBundles/angularbasic/package.json +3 -3
- package/lib/templates/uiBundles/angularbasic/src/app/components/ui/icon/icon.ts +6 -0
- package/lib/templates/uiBundles/reactbasic/package-lock.json +885 -637
- package/lib/templates/uiBundles/reactbasic/package.json +6 -4
- package/lib/templates/uiBundles/reactbasic/patches/aria-hidden+1.2.6.patch +45 -0
- package/lib/templates/uiembedding/default/default.html +1 -1
- package/lib/utils/constants.js +2 -1
- package/lib/utils/constants.js.map +1 -1
- package/lib/utils/createUtil.js +3 -2
- package/lib/utils/createUtil.js.map +1 -1
- package/lib/utils/template-placeholders.d.ts +1 -1
- package/lib/utils/template-placeholders.js +3 -1
- package/lib/utils/template-placeholders.js.map +1 -1
- package/lib/utils/types.d.ts +1 -1
- package/lib/utils/types.js +17 -16
- package/lib/utils/types.js.map +1 -1
- package/lib/utils/uiBundleTemplateUtils.js +4 -4
- package/lib/utils/uiBundleTemplateUtils.js.map +1 -1
- package/package.json +23 -23
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/README.md +0 -246
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/README.md +0 -246
- package/lib/tsconfig.tsbuildinfo +0 -1
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/api/object-search.service.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/breadcrumb/breadcrumb.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/breadcrumb/breadcrumb.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/active-filters.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/active-filters.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/date-filter.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/date-filter.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/date-range-filter.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/date-range-filter.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/filter-field-wrapper.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/filter-field-wrapper.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/multi-select-filter.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/multi-select-filter.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/numeric-range-filter.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/numeric-range-filter.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/search-filter.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/search-filter.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/select-filter.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/select-filter.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/text-filter.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/text-filter.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/object-search-state.service.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/pagination/pagination.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/pagination/pagination.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/search-bar/search-bar.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/search-bar/search-bar.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/sort-control/sort-control.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/sort-control/sort-control.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/debounce.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/field-utils.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/filter-utils.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/index.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/sort-utils.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/auth/auth.guard.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/auth-form/auth-form.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/auth-form/auth-form.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/auth-layout/auth-layout.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/auth-layout/auth-layout.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/card-layout/card-layout.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/card-layout/card-layout.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/config/authentication.config.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/footer-link/footer-link.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/footer-link/footer-link.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/menu/auth-menu/auth-menu.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/menu/auth-menu/auth-menu.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/reactive-form-page.base.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/session/session.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/session/session.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/status-alert/status-alert.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/status-alert/status-alert.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/submit-button/submit-button.html +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/submit-button/submit-button.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/utils/auth-helpers.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/utils/auth-validators.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/utils/helpers.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/utils/session-time-servlet.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/utils/session-timeout.config.ts +0 -0
- /package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/authentication/utils/session-timeout.service.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/api/object-search.service.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/breadcrumb/breadcrumb.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/breadcrumb/breadcrumb.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/active-filters.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/active-filters.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/date-filter.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/date-filter.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/date-range-filter.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/date-range-filter.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/filter-field-wrapper.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/filter-field-wrapper.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/multi-select-filter.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/multi-select-filter.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/numeric-range-filter.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/numeric-range-filter.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/search-filter.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/search-filter.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/select-filter.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/select-filter.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/text-filter.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/filters/text-filter.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/object-search-state.service.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/pagination/pagination.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/pagination/pagination.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/search-bar/search-bar.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/search-bar/search-bar.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/sort-control/sort-control.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/sort-control/sort-control.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/debounce.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/field-utils.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/filter-utils.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/index.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features/object-search → _f_/_os_}/utils/sort-utils.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/agentforce/agentforce-embed.service.ts +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/agentforce/conversation.html +0 -0
- /package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/{features → _f_}/agentforce/conversation.ts +0 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure URL helpers for org-setup.mjs logout-URL productization.
|
|
3
|
+
*
|
|
4
|
+
* The platform requires an ABSOLUTE logout URL on Network metadata — a relative
|
|
5
|
+
* value is rejected at deploy time ("The logout page URL must be an absolute
|
|
6
|
+
* URL."). Apps ship a domain-INDEPENDENT, site-relative path in
|
|
7
|
+
* org-setup.config.json (so the same value is valid on every org the app deploys
|
|
8
|
+
* to); these helpers resolve it to an absolute URL at deploy time, against the
|
|
9
|
+
* site's Experience Cloud origin discovered from the target org's communities.
|
|
10
|
+
*
|
|
11
|
+
* Pure (no fs / no process / no network): org-setup.mjs fetches the community list
|
|
12
|
+
* (Connect API) and owns the fail modes; these helpers only classify + resolve
|
|
13
|
+
* strings, so they carry automated coverage in the consuming test package
|
|
14
|
+
* (org-setup-tests/test/org-setup-url.spec.ts). Companion to org-setup-xml.mjs.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** True when `value` is an absolute http(s) URL — already deployable as-is. */
|
|
18
|
+
export function isAbsoluteLogoutUrl(value) {
|
|
19
|
+
return /^https?:\/\//i.test(String(value).trim());
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Leading path segment of a URL or path:
|
|
24
|
+
* "/propertyrentalapp/" -> "propertyrentalapp"
|
|
25
|
+
* "https://h.site.com/propertyrentalapp" -> "propertyrentalapp"
|
|
26
|
+
* "/" -> ""
|
|
27
|
+
*/
|
|
28
|
+
export function firstPathSegment(pathOrUrl) {
|
|
29
|
+
let pathname;
|
|
30
|
+
try {
|
|
31
|
+
pathname = new URL(pathOrUrl).pathname; // absolute URL
|
|
32
|
+
} catch {
|
|
33
|
+
pathname = String(pathOrUrl).split(/[?#]/)[0]; // relative path
|
|
34
|
+
}
|
|
35
|
+
return pathname.replace(/^\/+/, '').split('/')[0];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Choose the Experience Cloud community whose site URL should anchor a relative
|
|
40
|
+
* logout path, and return its siteUrl (or null when none matches).
|
|
41
|
+
*
|
|
42
|
+
* Matches on the community's siteUrl PATH first — robust even when a community's
|
|
43
|
+
* `urlPathPrefix` differs from its public path (e.g. a React "site container" site
|
|
44
|
+
* whose companion ChatterNetwork carries a "…vforcesite" prefix), and correct on
|
|
45
|
+
* custom domains where each site can have its own origin. Falls back to a
|
|
46
|
+
* community whose `name` equals the derived site name. Does NOT fall back to an
|
|
47
|
+
* arbitrary community: resolving against the wrong origin would silently produce a
|
|
48
|
+
* valid-but-wrong absolute URL, so an unmatched relative path is left for the
|
|
49
|
+
* caller to surface loudly.
|
|
50
|
+
*
|
|
51
|
+
* @param {Array<{name?: string, siteUrl?: string}>} communities
|
|
52
|
+
* @param {string} configLogoutUrl the site-relative path being resolved
|
|
53
|
+
* @param {string} [siteName]
|
|
54
|
+
* @returns {string|null} the matched community siteUrl
|
|
55
|
+
*/
|
|
56
|
+
export function pickCommunityBaseUrl(communities, configLogoutUrl, siteName) {
|
|
57
|
+
const list = Array.isArray(communities) ? communities.filter((c) => c && c.siteUrl) : [];
|
|
58
|
+
const seg = firstPathSegment(configLogoutUrl);
|
|
59
|
+
|
|
60
|
+
if (seg) {
|
|
61
|
+
const byPath = list.find((c) => firstPathSegment(c.siteUrl) === seg);
|
|
62
|
+
if (byPath) return byPath.siteUrl;
|
|
63
|
+
}
|
|
64
|
+
if (siteName) {
|
|
65
|
+
const byName = list.find((c) => c.name === siteName);
|
|
66
|
+
if (byName) return byName.siteUrl;
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Resolve a shipped logout-URL config value to an absolute URL.
|
|
73
|
+
* - already absolute -> returned unchanged (trimmed).
|
|
74
|
+
* - site-relative path -> resolved against `baseUrl` (a community siteUrl).
|
|
75
|
+
* Because the config path is root-relative ("/app/"), only baseUrl's ORIGIN is
|
|
76
|
+
* used, so any siteUrl on the same Experience Cloud domain yields the same
|
|
77
|
+
* result.
|
|
78
|
+
*
|
|
79
|
+
* Throws when a relative value has no baseUrl to resolve against, or when
|
|
80
|
+
* resolution fails to produce an absolute http(s) URL.
|
|
81
|
+
*
|
|
82
|
+
* @param {string} configLogoutUrl
|
|
83
|
+
* @param {string|null} baseUrl
|
|
84
|
+
* @returns {string} an absolute URL
|
|
85
|
+
*/
|
|
86
|
+
export function resolveLogoutUrl(configLogoutUrl, baseUrl) {
|
|
87
|
+
const value = String(configLogoutUrl).trim();
|
|
88
|
+
if (isAbsoluteLogoutUrl(value)) return value;
|
|
89
|
+
|
|
90
|
+
if (!baseUrl) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`logout URL "${configLogoutUrl}" is site-relative but no Experience Cloud ` +
|
|
93
|
+
`community site URL was found to resolve it into an absolute URL`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
let resolved;
|
|
97
|
+
try {
|
|
98
|
+
resolved = new URL(value, baseUrl).href;
|
|
99
|
+
} catch (e) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`could not resolve logout URL "${configLogoutUrl}" against "${baseUrl}": ${e.message}`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
if (!isAbsoluteLogoutUrl(resolved)) {
|
|
105
|
+
throw new Error(`resolved logout URL "${resolved}" is not an absolute http(s) URL`);
|
|
106
|
+
}
|
|
107
|
+
return resolved;
|
|
108
|
+
}
|
|
@@ -180,3 +180,99 @@ export function enableSelfRegInXml(xml, selfRegProfile) {
|
|
|
180
180
|
);
|
|
181
181
|
return { xml: updated, changed: true };
|
|
182
182
|
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Indentation of the first indented child element, so an inserted node matches
|
|
186
|
+
* the file's own formatting — byte-faithful on 2-space and 4-space files alike.
|
|
187
|
+
* Falls back to 4 spaces when the document has no indented child to sample.
|
|
188
|
+
*/
|
|
189
|
+
function firstChildIndent(xml) {
|
|
190
|
+
const m = xml.match(/\n([ \t]+)</);
|
|
191
|
+
return m ? m[1] : ' ';
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Assert a logout URL is safe to interpolate into the network metadata as-is.
|
|
196
|
+
*
|
|
197
|
+
* Mirrors assertSafeProfileName: the value is developer-authored config, so a
|
|
198
|
+
* character that would need XML entity-escaping (& < > " ') signals a mistake to
|
|
199
|
+
* surface loudly rather than silently rewrite. A logout landing URL is a site
|
|
200
|
+
* home path/URL and legitimately needs none of these. Throws NetworkXmlError
|
|
201
|
+
* (mapped by callers to their fail mode) otherwise.
|
|
202
|
+
*/
|
|
203
|
+
export function assertSafeLogoutUrl(url) {
|
|
204
|
+
const s = String(url);
|
|
205
|
+
if (XML_SPECIAL_CHARS.test(s)) {
|
|
206
|
+
throw new NetworkXmlError(
|
|
207
|
+
`logoutUrl "${url}" contains an XML-special character (& < > " '); ` +
|
|
208
|
+
`use a URL without these characters in org-setup.config.json`,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
return s;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Set <logoutUrl> so members land on THIS site after logging out, instead of the
|
|
216
|
+
* org's default-site logout — which, in an org hosting multiple Experience Cloud
|
|
217
|
+
* sites, can drop a member on a different community's login page.
|
|
218
|
+
*
|
|
219
|
+
* `url` MUST already be an absolute URL: the platform rejects a relative value at
|
|
220
|
+
* deploy time ("The logout page URL must be an absolute URL."). A shipped
|
|
221
|
+
* site-relative config path is resolved to an absolute URL before this is called
|
|
222
|
+
* (see org-setup-url.mjs, driven by ensureLogoutUrl in org-setup.mjs).
|
|
223
|
+
*
|
|
224
|
+
* Asserts this is a Network document (throws NetworkXmlError otherwise). Returns
|
|
225
|
+
* `{ xml, changed }`:
|
|
226
|
+
* - `changed: false` (input returned unchanged) when <logoutUrl> already equals
|
|
227
|
+
* `url` — a configured re-run is a true byte-for-byte no-op.
|
|
228
|
+
* - present-but-different: the value is replaced in place (position preserved).
|
|
229
|
+
* - absent: a <logoutUrl> node is inserted in the CANONICAL position the Metadata
|
|
230
|
+
* API emits. Network's top-level elements serialize in alphabetical order, so
|
|
231
|
+
* the node is placed immediately before the first existing sibling that sorts
|
|
232
|
+
* after "logoutUrl" (or as the last child, before </Network>, when none does).
|
|
233
|
+
* Inserting where a later retrieve would place it keeps this org-deployed file
|
|
234
|
+
* free of reorder churn. The node adopts the file's own indentation and every
|
|
235
|
+
* untouched region is preserved byte-for-byte.
|
|
236
|
+
*
|
|
237
|
+
* Assumes an existing <logoutUrl> is a paired, non-empty node (the shipped sparse
|
|
238
|
+
* network omits it entirely; a deployed org carries a paired value) — the case
|
|
239
|
+
* this productizes.
|
|
240
|
+
*/
|
|
241
|
+
export function setLogoutUrl(xml, url) {
|
|
242
|
+
const network = parseNetwork(xml);
|
|
243
|
+
// Reject XML-special characters rather than escaping them (see
|
|
244
|
+
// assertSafeLogoutUrl) before interpolating raw.
|
|
245
|
+
const safeUrl = assertSafeLogoutUrl(url);
|
|
246
|
+
const current = 'logoutUrl' in network ? String(network.logoutUrl) : null;
|
|
247
|
+
if (current === safeUrl) {
|
|
248
|
+
return { xml, changed: false };
|
|
249
|
+
}
|
|
250
|
+
const node = `<logoutUrl>${safeUrl}</logoutUrl>`;
|
|
251
|
+
// Present-but-different — replace the value in place (preserves position).
|
|
252
|
+
if (/<logoutUrl>[^<]*<\/logoutUrl>/.test(xml)) {
|
|
253
|
+
return { xml: xml.replace(/<logoutUrl>[^<]*<\/logoutUrl>/, node), changed: true };
|
|
254
|
+
}
|
|
255
|
+
// Absent — insert in alphabetical order among the Network's top-level children to
|
|
256
|
+
// match the canonical Metadata API serialization. parseNetwork's keys are the
|
|
257
|
+
// direct child element names; drop fast-xml-parser's attribute (@_*) and text
|
|
258
|
+
// (#*) pseudo-keys.
|
|
259
|
+
const siblings = Object.keys(network).filter((k) => !k.startsWith('@_') && !k.startsWith('#'));
|
|
260
|
+
// The first sibling that sorts AFTER "logoutUrl" — the node goes just before it.
|
|
261
|
+
const successor = siblings.filter((name) => name > 'logoutUrl').sort()[0];
|
|
262
|
+
if (successor) {
|
|
263
|
+
const beforeSuccessor = new RegExp(`(\\n[ \\t]*)(<${successor}\\b)`);
|
|
264
|
+
if (beforeSuccessor.test(xml)) {
|
|
265
|
+
// Reuse the successor's own leading whitespace so the node adopts its indent.
|
|
266
|
+
return { xml: xml.replace(beforeSuccessor, `$1${node}$1$2`), changed: true };
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// No later-sorting sibling (or its tag wasn't found) — insert as the last child,
|
|
270
|
+
// right before </Network>, at the file's own indent.
|
|
271
|
+
const indent = firstChildIndent(xml);
|
|
272
|
+
const beforeClose = /(\n)([ \t]*<\/Network>)/;
|
|
273
|
+
if (beforeClose.test(xml)) {
|
|
274
|
+
return { xml: xml.replace(beforeClose, `\n${indent}${node}$1$2`), changed: true };
|
|
275
|
+
}
|
|
276
|
+
// Degenerate single-line / rootless form — fall back to just after the open tag.
|
|
277
|
+
return { xml: xml.replace(/(<Network\b[^>]*>)/, `$1\n${indent}${node}`), changed: true };
|
|
278
|
+
}
|
|
@@ -58,8 +58,14 @@ import { validateConfig } from './org-setup-config-schema.mjs';
|
|
|
58
58
|
import {
|
|
59
59
|
addProfileToMemberGroups,
|
|
60
60
|
enableSelfRegInXml,
|
|
61
|
+
setLogoutUrl,
|
|
61
62
|
NetworkXmlError,
|
|
62
63
|
} from './org-setup-xml.mjs';
|
|
64
|
+
import {
|
|
65
|
+
isAbsoluteLogoutUrl,
|
|
66
|
+
pickCommunityBaseUrl,
|
|
67
|
+
resolveLogoutUrl,
|
|
68
|
+
} from './org-setup-url.mjs';
|
|
63
69
|
import {
|
|
64
70
|
discoverAllUIBundleDirs as discoverAllUIBundleDirsIn,
|
|
65
71
|
discoverUIBundleDir as discoverUIBundleDirIn,
|
|
@@ -540,7 +546,7 @@ function loadSelfRegConfig(config) {
|
|
|
540
546
|
* - communityMemberProfile: profile added to NetworkMemberGroup (required for access)
|
|
541
547
|
* - authProviderNames: DeveloperNames of AuthProvider (OAuth) or SamlSsoConfig (SAML) records to link to site
|
|
542
548
|
* - communityUserPermset: permset assigned to community users so getCurrentUser()
|
|
543
|
-
* works (requires PermissionsApiEnabled for
|
|
549
|
+
* works (requires PermissionsApiEnabled for UI API GraphQL via /sf/api/)
|
|
544
550
|
*
|
|
545
551
|
* Returns null if no "socialLogin" section exists in config (the step is hidden).
|
|
546
552
|
*/
|
|
@@ -554,6 +560,15 @@ function loadSocialLoginConfig(config) {
|
|
|
554
560
|
};
|
|
555
561
|
}
|
|
556
562
|
|
|
563
|
+
/**
|
|
564
|
+
* Logout URL config, read from the already-validated config. Returns the string
|
|
565
|
+
* (a site-relative path like "/myapp/", or an absolute URL) or null when no
|
|
566
|
+
* "logoutUrl" is set (the step is then a no-op).
|
|
567
|
+
*/
|
|
568
|
+
function loadLogoutUrlConfig(config) {
|
|
569
|
+
return config.logoutUrl ?? null;
|
|
570
|
+
}
|
|
571
|
+
|
|
557
572
|
/**
|
|
558
573
|
* Enable the "Allow using standard external profiles for self-registration,
|
|
559
574
|
* user creation, and login" org setting via Metadata API deploy.
|
|
@@ -706,6 +721,32 @@ if (allProviders.isEmpty()) {
|
|
|
706
721
|
}
|
|
707
722
|
System.debug('TOTAL_PROVIDERS_FOUND:' + allProviders.size());
|
|
708
723
|
|
|
724
|
+
// Fail fast if any requested provider name did not resolve to an AuthProvider
|
|
725
|
+
// (OAuth) or SamlSsoConfig (SAML) record. The isEmpty() check above only catches
|
|
726
|
+
// the case where NONE resolve; comparing the found DeveloperNames against the
|
|
727
|
+
// requested list also catches the strict-subset case (e.g. ["Google","Typo"]
|
|
728
|
+
// links Google but must not silently drop "Typo"). This runs BEFORE any junction
|
|
729
|
+
// record is created, so linking is all-or-nothing. Case-insensitive because SOQL
|
|
730
|
+
// "DeveloperName IN (...)" matches case-insensitively, so a stored DeveloperName
|
|
731
|
+
// may differ in case from the configured value and must not read as "missing".
|
|
732
|
+
Set<String> foundProviderNames = new Set<String>();
|
|
733
|
+
for (AuthProvider ap : oauthProviders) {
|
|
734
|
+
foundProviderNames.add(ap.DeveloperName.toLowerCase());
|
|
735
|
+
}
|
|
736
|
+
for (SamlSsoConfig sp : samlProviders) {
|
|
737
|
+
foundProviderNames.add(sp.DeveloperName.toLowerCase());
|
|
738
|
+
}
|
|
739
|
+
List<String> missingProviderNames = new List<String>();
|
|
740
|
+
for (String requestedName : new List<String>{${providerNamesLiteral}}) {
|
|
741
|
+
if (!foundProviderNames.contains(requestedName.toLowerCase())) {
|
|
742
|
+
missingProviderNames.add(requestedName);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
if (!missingProviderNames.isEmpty()) {
|
|
746
|
+
System.debug('MISSING_PROVIDERS:' + String.join(missingProviderNames, ','));
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
|
|
709
750
|
// Step 4: Check existing AuthConfigProviders to avoid duplicates
|
|
710
751
|
Set<Id> existingProviderIds = new Set<Id>();
|
|
711
752
|
for (AuthConfigProviders acp : [
|
|
@@ -768,6 +809,18 @@ if (inserted == 0 && existingProviderIds.size() >= allProviders.size()) {
|
|
|
768
809
|
if (apexOut.match(/\|DEBUG\|ERROR_NO_PROVIDERS/)) {
|
|
769
810
|
throw new StepError(`no AuthProvider or SamlSsoConfig records found for names: ${authProviderNames.join(', ')} — create them in Setup first`);
|
|
770
811
|
}
|
|
812
|
+
// A SOQL `DeveloperName IN (…)` query silently returns fewer rows for names
|
|
813
|
+
// that don't exist, so the ERROR_NO_PROVIDERS guard above (which only fires
|
|
814
|
+
// when NONE resolve) let a single typo'd name slip through unnoticed whenever
|
|
815
|
+
// at least one other name matched. The Apex now diffs the requested names
|
|
816
|
+
// against the resolved ones (case-insensitively, since SOQL matches that way)
|
|
817
|
+
// and emits MISSING_PROVIDERS naming the offenders — nothing is linked when it
|
|
818
|
+
// fires (all-or-nothing, checked before any insert).
|
|
819
|
+
const missingMatch = apexOut.match(/\|DEBUG\|MISSING_PROVIDERS:([^\n]+)/);
|
|
820
|
+
if (missingMatch) {
|
|
821
|
+
const missing = missingMatch[1].trim();
|
|
822
|
+
throw new StepError(`some configured auth providers were not found: ${missing} — check for typos, or create them in Setup first (nothing was linked)`);
|
|
823
|
+
}
|
|
771
824
|
|
|
772
825
|
// Check for insert failures
|
|
773
826
|
const insertFails = [...apexOut.matchAll(/\|DEBUG\|INSERT_FAILED:([^:]+):(OAuth|SAML):(\d+):([^\n]+)/g)];
|
|
@@ -884,7 +937,7 @@ function addCommunityMemberProfile(profileName, siteName, targetOrg) {
|
|
|
884
937
|
* Assign a permission set to all existing community users with a given profile.
|
|
885
938
|
*
|
|
886
939
|
* After SSO login, community users need the app's API-enabled permset so that
|
|
887
|
-
* getCurrentUser() (which calls
|
|
940
|
+
* getCurrentUser() (which calls UI API GraphQL via the /sf/api/ proxy) works.
|
|
888
941
|
* Without PermissionsApiEnabled the proxy returns API_DISABLED_FOR_ORG and the
|
|
889
942
|
* React app treats the user as unauthenticated.
|
|
890
943
|
*
|
|
@@ -998,6 +1051,123 @@ function ensureNetworkMemberProfile(selfRegConfig, siteName) {
|
|
|
998
1051
|
console.log(` Added profile "${selfRegProfile}" to networkMemberGroups in ${siteName}.network-meta.xml`);
|
|
999
1052
|
}
|
|
1000
1053
|
|
|
1054
|
+
const CONNECT_API_VERSION = '62.0';
|
|
1055
|
+
|
|
1056
|
+
/**
|
|
1057
|
+
* Fetch the org's Experience Cloud communities via the Connect API and return the
|
|
1058
|
+
* `communities` array (possibly empty). Used to discover a site's public origin so
|
|
1059
|
+
* a shipped, site-relative logout path can be resolved to the absolute URL the
|
|
1060
|
+
* platform requires. Throws on transport/parse failure so the caller degrades to a
|
|
1061
|
+
* loud skip. (v62.0 is a safe floor — the /connect/communities resource is stable
|
|
1062
|
+
* across API versions.)
|
|
1063
|
+
*/
|
|
1064
|
+
function fetchCommunities(targetOrg) {
|
|
1065
|
+
const res = spawnSync('sf', [
|
|
1066
|
+
'api', 'request', 'rest',
|
|
1067
|
+
`/services/data/v${CONNECT_API_VERSION}/connect/communities`,
|
|
1068
|
+
'--target-org', targetOrg,
|
|
1069
|
+
], { cwd: ROOT, encoding: 'utf8', timeout: 60000 });
|
|
1070
|
+
if (res.status !== 0) {
|
|
1071
|
+
throw new Error(`Connect communities query failed (sf exit ${res.status ?? 1})`);
|
|
1072
|
+
}
|
|
1073
|
+
let json;
|
|
1074
|
+
try {
|
|
1075
|
+
json = JSON.parse(res.stdout);
|
|
1076
|
+
} catch {
|
|
1077
|
+
throw new Error('could not parse the Connect communities response as JSON');
|
|
1078
|
+
}
|
|
1079
|
+
return Array.isArray(json.communities) ? json.communities : [];
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
/**
|
|
1083
|
+
* Set the site's <logoutUrl> in the network metadata AFTER the initial deploy, then
|
|
1084
|
+
* re-deploy just the network — mirroring the self-registration step.
|
|
1085
|
+
*
|
|
1086
|
+
* Post-deploy (not folded into the main deploy) because the value must be an
|
|
1087
|
+
* ABSOLUTE URL: the platform rejects a relative logout URL at deploy ("The logout
|
|
1088
|
+
* page URL must be an absolute URL."). Apps ship a domain-independent, site-relative
|
|
1089
|
+
* path in org-setup.config.json; it is resolved here against the site's Experience
|
|
1090
|
+
* Cloud origin, which is only discoverable (via the Connect communities API) once
|
|
1091
|
+
* the site exists — i.e. after the main deploy. An already-absolute config value is
|
|
1092
|
+
* used as-is (no lookup).
|
|
1093
|
+
*
|
|
1094
|
+
* Best-effort, mirroring ensureNetworkMemberProfile: a missing network file, a
|
|
1095
|
+
* failure to resolve the absolute URL, a malformed-XML NetworkXmlError, or a failed
|
|
1096
|
+
* network re-deploy each log a LOUD console.error but do NOT throw — the logout URL
|
|
1097
|
+
* is a convenience that must not abort the whole setup. In particular, if the org's
|
|
1098
|
+
* Network emailSenderAddress has drifted from the shipped value, that field blocks
|
|
1099
|
+
* the network deploy; the message then points the operator at the site's
|
|
1100
|
+
* Administration settings. The helper's idempotency check makes a configured re-run
|
|
1101
|
+
* a byte-for-byte no-op with no deploy.
|
|
1102
|
+
*/
|
|
1103
|
+
function ensureLogoutUrl(logoutUrl, siteName, targetOrg) {
|
|
1104
|
+
if (!siteName || !logoutUrl) return;
|
|
1105
|
+
|
|
1106
|
+
const networkXmlPath = resolve(SFDX_SOURCE, 'networks', `${siteName}.network-meta.xml`);
|
|
1107
|
+
if (!existsSync(networkXmlPath)) {
|
|
1108
|
+
console.log(` Network metadata not found: ${networkXmlPath}; skipping logout URL update.`);
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
// Resolve the shipped (site-relative or absolute) config value to the absolute
|
|
1113
|
+
// URL the platform requires. A relative value needs the site's Experience Cloud
|
|
1114
|
+
// origin, discovered from the org's communities.
|
|
1115
|
+
let absoluteUrl;
|
|
1116
|
+
try {
|
|
1117
|
+
let baseUrl = null;
|
|
1118
|
+
if (!isAbsoluteLogoutUrl(logoutUrl)) {
|
|
1119
|
+
baseUrl = pickCommunityBaseUrl(fetchCommunities(targetOrg), logoutUrl, siteName);
|
|
1120
|
+
}
|
|
1121
|
+
absoluteUrl = resolveLogoutUrl(logoutUrl, baseUrl);
|
|
1122
|
+
} catch (e) {
|
|
1123
|
+
console.error(
|
|
1124
|
+
` ERROR: cannot resolve an absolute logout URL for "${siteName}" — ${e.message}. ` +
|
|
1125
|
+
`Skipping; set the logout URL manually in the site's Administration settings.`,
|
|
1126
|
+
);
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
const xml = readFileSync(networkXmlPath, 'utf8');
|
|
1131
|
+
let result;
|
|
1132
|
+
try {
|
|
1133
|
+
result = setLogoutUrl(xml, absoluteUrl);
|
|
1134
|
+
} catch (e) {
|
|
1135
|
+
if (e instanceof NetworkXmlError) {
|
|
1136
|
+
console.error(
|
|
1137
|
+
` ERROR: cannot set logout URL in ${siteName}.network-meta.xml — ${e.message}. ` +
|
|
1138
|
+
`Skipping; set <logoutUrl> manually in the site's Administration settings.`,
|
|
1139
|
+
);
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
throw e;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
if (!result.changed) {
|
|
1146
|
+
console.log(` Logout URL already set to "${absoluteUrl}" in ${siteName}.network-meta.xml; no update needed.`);
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
writeFileSync(networkXmlPath, result.xml);
|
|
1150
|
+
console.log(` Set <logoutUrl>${absoluteUrl}</logoutUrl> in ${siteName}.network-meta.xml`);
|
|
1151
|
+
|
|
1152
|
+
// Re-deploy ONLY the network file (mirrors enableSelfRegistration). Best-effort:
|
|
1153
|
+
// a non-zero exit (e.g. the org's emailSenderAddress differs from the shipped
|
|
1154
|
+
// value and can't be updated) logs loudly and continues.
|
|
1155
|
+
const deployResult = spawnSync('sf', [
|
|
1156
|
+
'project', 'deploy', 'start',
|
|
1157
|
+
'--target-org', targetOrg,
|
|
1158
|
+
'--source-dir', networkXmlPath,
|
|
1159
|
+
], { cwd: ROOT, stdio: 'inherit', shell: true, timeout: 120000 });
|
|
1160
|
+
if (deployResult.status !== 0) {
|
|
1161
|
+
console.error(
|
|
1162
|
+
` ERROR: failed to deploy <logoutUrl> for "${siteName}" (sf exit ${deployResult.status ?? 1}). ` +
|
|
1163
|
+
`If the org's Network emailSenderAddress differs from the shipped value it blocks this deploy — ` +
|
|
1164
|
+
`set the logout URL manually in the site's Administration settings.`,
|
|
1165
|
+
);
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
console.log(` Deployed <logoutUrl> for "${siteName}".`);
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1001
1171
|
/**
|
|
1002
1172
|
* Enable self-registration for an Experience Cloud network.
|
|
1003
1173
|
*
|
|
@@ -1788,6 +1958,7 @@ async function main() {
|
|
|
1788
1958
|
const hasSelfRegConfig = selfRegConfig !== null;
|
|
1789
1959
|
const socialLoginConfig = loadSocialLoginConfig(config);
|
|
1790
1960
|
const hasSocialLoginConfig = socialLoginConfig !== null;
|
|
1961
|
+
const logoutUrl = loadLogoutUrlConfig(config);
|
|
1791
1962
|
|
|
1792
1963
|
// Validate the selfRegProfile name for SOQL-safety up front, alongside the
|
|
1793
1964
|
// config validation and BEFORE any org mutation (login/deploy). A quote /
|
|
@@ -1955,6 +2126,27 @@ async function main() {
|
|
|
1955
2126
|
recordSkipped(deployStep, 'not selected');
|
|
1956
2127
|
}
|
|
1957
2128
|
|
|
2129
|
+
// Set the site's logout URL AFTER deploy so members land back on THIS site after
|
|
2130
|
+
// logging out (not the org default-site login, which in a multi-site org can be a
|
|
2131
|
+
// different community). Post-deploy because the deployed value must be an ABSOLUTE
|
|
2132
|
+
// URL (the platform rejects a relative one) and the shipped site-relative path is
|
|
2133
|
+
// resolved against the site's community origin — which only exists once the site
|
|
2134
|
+
// is deployed. Independent of self-reg; best-effort (a failure logs loudly, does
|
|
2135
|
+
// not abort). An ambiguous multi-network app can't auto-target a single site, so
|
|
2136
|
+
// derivation failure skips it.
|
|
2137
|
+
if (!skipDeploy && logoutUrl) {
|
|
2138
|
+
let logoutSiteName = null;
|
|
2139
|
+
try {
|
|
2140
|
+
logoutSiteName = deriveSiteName();
|
|
2141
|
+
} catch {
|
|
2142
|
+
// ambiguous derivation (multiple network files) — skip logout URL prep
|
|
2143
|
+
}
|
|
2144
|
+
if (logoutSiteName) {
|
|
2145
|
+
console.log('\n--- Ensure logout URL (post-deploy) ---');
|
|
2146
|
+
ensureLogoutUrl(logoutUrl, logoutSiteName, targetOrg);
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
|
|
1958
2150
|
const permsetStep = stepDefs.find((s) => s.key === 'permset');
|
|
1959
2151
|
if (!skipPermset) {
|
|
1960
2152
|
await runStep(permsetStep, targetOrg, async () => {
|
|
@@ -2117,7 +2309,7 @@ async function main() {
|
|
|
2117
2309
|
addCommunityMemberProfile(socialLoginConfig.communityMemberProfile, siteName, targetOrg);
|
|
2118
2310
|
|
|
2119
2311
|
// Sub-step 4 (optional): Assign API-enabled permset to community users.
|
|
2120
|
-
// Without this, getCurrentUser() fails because
|
|
2312
|
+
// Without this, getCurrentUser() fails because UI API GraphQL requires
|
|
2121
2313
|
// PermissionsApiEnabled which the standard community profile lacks.
|
|
2122
2314
|
if (socialLoginConfig.communityUserPermset) {
|
|
2123
2315
|
console.log(` [4/${totalSubSteps}] Assigning API permset to community users...`);
|