@salesforce/templates 66.13.5 → 66.13.7

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.
Files changed (281) hide show
  1. package/lib/templates/project/angularextapp/CHANGELOG.md +258 -0
  2. package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/angular.json +1 -1
  3. package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/eslint-plugin-angular-graphql.js +148 -0
  4. package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/eslint.config.js +9 -1
  5. package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/package-lock.json +680 -408
  6. package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/package.json +2 -2
  7. package/lib/templates/project/angularextapp/package.json +1 -1
  8. package/lib/templates/project/angularextapp/scripts/org-setup-config-schema.mjs +17 -0
  9. package/lib/templates/project/angularextapp/scripts/org-setup-utils.mjs +33 -3
  10. package/lib/templates/project/angularextapp/scripts/org-setup.mjs +535 -3
  11. package/lib/templates/project/angularintapp/CHANGELOG.md +266 -0
  12. package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/angular.json +1 -1
  13. package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/eslint-plugin-angular-graphql.js +148 -0
  14. package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/eslint.config.js +9 -1
  15. package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/package-lock.json +690 -418
  16. package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/package.json +2 -2
  17. package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/features/agentforce/agentforce-embed.service.ts +6 -1
  18. package/lib/templates/project/angularintapp/package.json +1 -1
  19. package/lib/templates/project/angularintapp/scripts/org-setup-config-schema.mjs +17 -0
  20. package/lib/templates/project/angularintapp/scripts/org-setup-utils.mjs +33 -3
  21. package/lib/templates/project/angularintapp/scripts/org-setup.mjs +535 -3
  22. package/lib/templates/project/reactexternalapp/CHANGELOG.md +1409 -0
  23. package/lib/templates/project/reactexternalapp/PRD.md +38 -0
  24. package/lib/templates/project/reactexternalapp/README.md +14 -10
  25. package/lib/templates/project/reactexternalapp/_p_/_m_/_ps_/reactexternalapp_Guest_User_Api_Access.permissionset-meta.xml +25 -0
  26. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/package-lock.json +953 -714
  27. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/package.json +9 -7
  28. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/scripts/get-graphql-schema.mjs +8 -5
  29. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_ls_/LanguageSwitcher.tsx +116 -0
  30. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_ls_/__examples__/language-switcher-example.tsx +36 -0
  31. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_ls_/__examples__/vite-config-site-example.ts +44 -0
  32. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_ls_/buildLanguageUrl.ts +158 -0
  33. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_ls_/index.ts +28 -0
  34. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_ls_/languages.ts +46 -0
  35. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/components/SocialLogin.tsx +141 -0
  36. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/pages/Login.tsx +2 -0
  37. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/README.md +246 -0
  38. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/apiUtils.ts +56 -0
  39. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/orgApiVersionService.ts +37 -0
  40. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/searchableContentTypesService.ts +116 -0
  41. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/channelResolver.ts +40 -0
  42. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/cmsQueryFragment.ts +97 -0
  43. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/contentTypeSessionCache.ts +134 -0
  44. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/contentTypeUtils.ts +38 -0
  45. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/hooks/useSearchableContentTypes.ts +128 -0
  46. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/index.ts +54 -0
  47. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/parseResponse.ts +65 -0
  48. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/searchChannel.ts +15 -0
  49. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/types.ts +66 -0
  50. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/registry.ts +38 -0
  51. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/sobject/index.ts +19 -0
  52. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/sobject/parseResponse.ts +56 -0
  53. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/sobject/queryFragment.ts +141 -0
  54. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/types.ts +101 -0
  55. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/api/distinctValuesService.ts +84 -0
  56. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/api/searchService.ts +136 -0
  57. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/GlobalSearchBox.tsx +98 -0
  58. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/MergedSearchResults.tsx +294 -0
  59. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/Search.tsx +203 -0
  60. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SearchResults.tsx +88 -0
  61. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SourceSection.tsx +187 -0
  62. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/PaginationControls.tsx +185 -0
  63. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/ScopeSelector.tsx +107 -0
  64. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/SearchBar.tsx +55 -0
  65. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/SortControl.tsx +62 -0
  66. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/ActiveFilters.tsx +61 -0
  67. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/DefaultFilterPanel.tsx +125 -0
  68. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/{_os_/components → search/components/filters}/FilterContext.tsx +7 -20
  69. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/BooleanFilter.tsx +50 -0
  70. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/DateRangeFilter.tsx +160 -0
  71. package/lib/templates/project/{reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters → reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs}/FilterFieldWrapper.tsx +11 -6
  72. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/MultiSelectFilter.tsx +89 -0
  73. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/NumericRangeFilter.tsx +108 -0
  74. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/SelectFilter.tsx +46 -0
  75. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/TextFilter.tsx +52 -0
  76. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/CmsResultRow.tsx +101 -0
  77. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/DefaultResultRow.tsx +109 -0
  78. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/resolveResultRenderer.ts +49 -0
  79. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/config.json +88 -0
  80. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/constants.ts +8 -0
  81. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/{_os_ → search}/hooks/useAsyncData.ts +3 -10
  82. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/hooks/useDistinctValues.ts +59 -0
  83. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/hooks/useSearch.ts +966 -0
  84. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/index.ts +115 -0
  85. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/loadConfig.ts +14 -0
  86. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/queryBuilder.ts +100 -0
  87. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/types.ts +376 -0
  88. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/debounce.ts +17 -0
  89. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/fieldUtils.ts +14 -0
  90. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/filterUtils.ts +285 -0
  91. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/sortUtils.ts +34 -0
  92. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/api/graphqlClient.ts +24 -8
  93. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/appLayout.tsx +7 -0
  94. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/components/CustomerWebClientChat.tsx +74 -0
  95. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/{_f_/_os_/components → components}/ObjectBreadcrumb.tsx +7 -2
  96. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/components/ui/button.tsx +1 -1
  97. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/components/ui/datePicker.tsx +2 -2
  98. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/components/ui/table.tsx +9 -2
  99. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/index.ts +8 -0
  100. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/pages/AccountObjectDetailPage.tsx +53 -18
  101. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/pages/Home.tsx +3 -27
  102. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/pages/TestChatPage.tsx +26 -0
  103. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/routes.tsx +21 -8
  104. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/types/chat.ts +36 -0
  105. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/types/globals.d.ts +23 -0
  106. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/{_f_/_os_/utils/fieldUtils.ts → utils/accountFields.ts} +9 -0
  107. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/tsconfig.json +2 -2
  108. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/tsconfig.tsbuildinfo +1 -0
  109. package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleSocialLoginConfig.cls +133 -0
  110. package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleSocialLoginConfig.cls-meta.xml +5 -0
  111. package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleSocialLoginConfig_Test.cls +101 -0
  112. package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleSocialLoginConfig_Test.cls-meta.xml +5 -0
  113. package/lib/templates/project/reactexternalapp/_p_/_m_/package.xml +4 -0
  114. package/lib/templates/project/reactexternalapp/package.json +8 -2
  115. package/lib/templates/project/reactexternalapp/scripts/org-setup-config-schema.mjs +120 -0
  116. package/lib/templates/project/reactexternalapp/scripts/org-setup-dev.mjs +87 -0
  117. package/lib/templates/project/reactexternalapp/scripts/org-setup-utils.mjs +569 -0
  118. package/lib/templates/project/reactexternalapp/scripts/org-setup-xml.mjs +182 -0
  119. package/lib/templates/project/reactexternalapp/scripts/org-setup.config.json +6 -2
  120. package/lib/templates/project/reactexternalapp/scripts/org-setup.mjs +1509 -328
  121. package/lib/templates/project/reactexternalapp/scripts/validate-org-setup-config.mjs +38 -0
  122. package/lib/templates/project/reactinternalapp/CHANGELOG.md +1223 -0
  123. package/lib/templates/project/reactinternalapp/PRD.md +35 -0
  124. package/lib/templates/project/reactinternalapp/README.md +14 -10
  125. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/package-lock.json +813 -538
  126. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/package.json +10 -9
  127. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/README.md +246 -0
  128. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/apiUtils.ts +56 -0
  129. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/orgApiVersionService.ts +37 -0
  130. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/searchableContentTypesService.ts +116 -0
  131. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/channelResolver.ts +40 -0
  132. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/cmsQueryFragment.ts +97 -0
  133. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/contentTypeSessionCache.ts +134 -0
  134. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/contentTypeUtils.ts +38 -0
  135. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/hooks/useSearchableContentTypes.ts +128 -0
  136. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/index.ts +54 -0
  137. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/parseResponse.ts +65 -0
  138. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/searchChannel.ts +15 -0
  139. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/types.ts +66 -0
  140. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/registry.ts +38 -0
  141. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/sobject/index.ts +19 -0
  142. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/sobject/parseResponse.ts +56 -0
  143. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/sobject/queryFragment.ts +141 -0
  144. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/types.ts +101 -0
  145. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/api/distinctValuesService.ts +84 -0
  146. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/api/searchService.ts +136 -0
  147. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/GlobalSearchBox.tsx +98 -0
  148. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/MergedSearchResults.tsx +294 -0
  149. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/Search.tsx +203 -0
  150. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SearchResults.tsx +88 -0
  151. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SourceSection.tsx +187 -0
  152. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/PaginationControls.tsx +185 -0
  153. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/ScopeSelector.tsx +107 -0
  154. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/SearchBar.tsx +55 -0
  155. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/SortControl.tsx +62 -0
  156. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/ActiveFilters.tsx +61 -0
  157. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/DefaultFilterPanel.tsx +125 -0
  158. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/{_os_/components → search/components/filters}/FilterContext.tsx +7 -20
  159. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/BooleanFilter.tsx +50 -0
  160. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/DateRangeFilter.tsx +160 -0
  161. package/lib/templates/project/{reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters → reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs}/FilterFieldWrapper.tsx +11 -6
  162. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/MultiSelectFilter.tsx +89 -0
  163. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/NumericRangeFilter.tsx +108 -0
  164. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/SelectFilter.tsx +46 -0
  165. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/TextFilter.tsx +52 -0
  166. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/CmsResultRow.tsx +101 -0
  167. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/DefaultResultRow.tsx +109 -0
  168. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/resolveResultRenderer.ts +49 -0
  169. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/config.json +88 -0
  170. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/constants.ts +8 -0
  171. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/{_os_ → search}/hooks/useAsyncData.ts +3 -10
  172. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/hooks/useDistinctValues.ts +59 -0
  173. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/hooks/useSearch.ts +966 -0
  174. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/index.ts +115 -0
  175. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/loadConfig.ts +14 -0
  176. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/queryBuilder.ts +100 -0
  177. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/types.ts +376 -0
  178. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/debounce.ts +17 -0
  179. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/fieldUtils.ts +14 -0
  180. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/filterUtils.ts +285 -0
  181. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/sortUtils.ts +34 -0
  182. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/components/AgentforceConversationClient.tsx +16 -132
  183. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/{_f_/_os_/components → components}/ObjectBreadcrumb.tsx +7 -2
  184. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/components/ui/button.tsx +1 -1
  185. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/components/ui/datePicker.tsx +2 -2
  186. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/components/ui/table.tsx +9 -2
  187. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/pages/AccountObjectDetailPage.tsx +49 -16
  188. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/pages/Home.tsx +3 -27
  189. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/routes.tsx +12 -5
  190. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/{_f_/_os_/utils/fieldUtils.ts → utils/accountFields.ts} +9 -0
  191. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/tsconfig.json +2 -2
  192. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/tsconfig.tsbuildinfo +1 -0
  193. package/lib/templates/project/reactinternalapp/package.json +4 -2
  194. package/lib/templates/project/reactinternalapp/scripts/org-setup-config-schema.mjs +24 -0
  195. package/lib/templates/project/reactinternalapp/scripts/org-setup-dev.mjs +87 -0
  196. package/lib/templates/project/reactinternalapp/scripts/org-setup-utils.mjs +569 -0
  197. package/lib/templates/project/reactinternalapp/scripts/org-setup-xml.mjs +182 -0
  198. package/lib/templates/project/reactinternalapp/scripts/org-setup.mjs +1162 -214
  199. package/lib/templates/uiBundles/angularbasic/angular.json +1 -1
  200. package/lib/templates/uiBundles/angularbasic/eslint-plugin-angular-graphql.js +148 -0
  201. package/lib/templates/uiBundles/angularbasic/eslint.config.js +9 -1
  202. package/lib/templates/uiBundles/angularbasic/package-lock.json +680 -408
  203. package/lib/templates/uiBundles/angularbasic/package.json +2 -2
  204. package/lib/templates/uiBundles/reactbasic/package-lock.json +738 -440
  205. package/lib/utils/template-placeholders.js +14 -0
  206. package/lib/utils/template-placeholders.js.map +1 -1
  207. package/lib/utils/uiBundleTemplateUtils.d.ts +5 -1
  208. package/lib/utils/uiBundleTemplateUtils.js +7 -1
  209. package/lib/utils/uiBundleTemplateUtils.js.map +1 -1
  210. package/package.json +7 -7
  211. package/lib/templates/project/reactexternalapp/AGENT.md +0 -252
  212. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/accountSearchService.ts +0 -46
  213. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/distinctAccountIndustries.graphql +0 -19
  214. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/distinctAccountTypes.graphql +0 -19
  215. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/getAccountDetail.graphql +0 -121
  216. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/searchAccounts.graphql +0 -51
  217. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/AccountObjectDetailPage.tsx +0 -355
  218. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/AccountSearch.tsx +0 -304
  219. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/Home.tsx +0 -34
  220. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/api/objectSearchService.ts +0 -82
  221. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/ActiveFilters.tsx +0 -89
  222. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/PaginationControls.tsx +0 -109
  223. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/SearchBar.tsx +0 -41
  224. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/SortControl.tsx +0 -143
  225. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/BooleanFilter.tsx +0 -78
  226. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/DateFilter.tsx +0 -128
  227. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/DateRangeFilter.tsx +0 -70
  228. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/MultiSelectFilter.tsx +0 -97
  229. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/NumericRangeFilter.tsx +0 -167
  230. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/SearchFilter.tsx +0 -52
  231. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/SelectFilter.tsx +0 -97
  232. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/TextFilter.tsx +0 -93
  233. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/hooks/useDebouncedCallback.ts +0 -34
  234. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/hooks/useObjectSearchParams.ts +0 -254
  235. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/utils/debounce.ts +0 -25
  236. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/utils/filterUtils.ts +0 -404
  237. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/utils/sortUtils.ts +0 -38
  238. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/api/account/accountSearchService.ts +0 -46
  239. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/api/account/query/distinctAccountIndustries.graphql +0 -19
  240. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/api/account/query/distinctAccountTypes.graphql +0 -19
  241. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/api/account/query/searchAccounts.graphql +0 -51
  242. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/api/graphql-operations-types.ts +0 -11260
  243. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/pages/AccountSearch.tsx +0 -297
  244. package/lib/templates/project/reactexternalapp/scripts/prepare-import-unique-fields.js +0 -122
  245. package/lib/templates/project/reactinternalapp/AGENT.md +0 -252
  246. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/accountSearchService.ts +0 -46
  247. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/distinctAccountIndustries.graphql +0 -19
  248. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/distinctAccountTypes.graphql +0 -19
  249. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/getAccountDetail.graphql +0 -121
  250. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/searchAccounts.graphql +0 -51
  251. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/AccountObjectDetailPage.tsx +0 -355
  252. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/AccountSearch.tsx +0 -304
  253. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/Home.tsx +0 -34
  254. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/api/objectSearchService.ts +0 -82
  255. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/ActiveFilters.tsx +0 -89
  256. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/PaginationControls.tsx +0 -109
  257. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/SearchBar.tsx +0 -41
  258. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/SortControl.tsx +0 -143
  259. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/BooleanFilter.tsx +0 -78
  260. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/DateFilter.tsx +0 -128
  261. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/DateRangeFilter.tsx +0 -70
  262. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/MultiSelectFilter.tsx +0 -97
  263. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/NumericRangeFilter.tsx +0 -167
  264. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/SearchFilter.tsx +0 -52
  265. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/SelectFilter.tsx +0 -97
  266. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/TextFilter.tsx +0 -93
  267. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/hooks/useDebouncedCallback.ts +0 -34
  268. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/hooks/useObjectSearchParams.ts +0 -254
  269. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/utils/debounce.ts +0 -25
  270. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/utils/filterUtils.ts +0 -404
  271. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/utils/sortUtils.ts +0 -38
  272. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/api/account/accountSearchService.ts +0 -46
  273. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/api/account/query/distinctAccountIndustries.graphql +0 -19
  274. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/api/account/query/distinctAccountTypes.graphql +0 -19
  275. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/api/account/query/searchAccounts.graphql +0 -51
  276. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/api/graphql-operations-types.ts +0 -11260
  277. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/pages/AccountSearch.tsx +0 -297
  278. package/lib/templates/project/reactinternalapp/scripts/prepare-import-unique-fields.js +0 -122
  279. /package/lib/templates/project/angularextapp/_p_/_m_/{permissionsets → _ps_}/angularextapp_Guest_User_Api_Access.permissionset-meta.xml +0 -0
  280. /package/lib/templates/project/angularintapp/_p_/_m_/{permissionsets → _ps_}/angularintapp_Access.permissionset-meta.xml +0 -0
  281. /package/lib/templates/project/reactinternalapp/_p_/_m_/{permissionsets → _ps_}/reactinternalapp_Access.permissionset-meta.xml +0 -0
@@ -46,6 +46,7 @@ import {
46
46
  existsSync,
47
47
  readFileSync,
48
48
  writeFileSync,
49
+ mkdirSync,
49
50
  mkdtempSync,
50
51
  rmSync,
51
52
  openSync,
@@ -286,6 +287,7 @@ function parseArgs() {
286
287
  skipGraphql: false,
287
288
  skipUIBundleBuild: false,
288
289
  skipSelfReg: false,
290
+ skipSocialLogin: false,
289
291
  };
290
292
  for (let i = 0; i < args.length; i++) {
291
293
  if (args[i] === '--target-org' && args[i + 1]) {
@@ -299,6 +301,7 @@ function parseArgs() {
299
301
  else if (args[i] === '--skip-role') flags.skipRole = true;
300
302
  else if (args[i] === '--skip-data') flags.skipData = true;
301
303
  else if (args[i] === '--skip-self-reg') flags.skipSelfReg = true;
304
+ else if (args[i] === '--skip-social-login') flags.skipSocialLogin = true;
302
305
  else if (args[i] === '--skip-graphql') flags.skipGraphql = true;
303
306
  else if (args[i] === '--skip-ui-bundle-build') flags.skipUIBundleBuild = true;
304
307
  // --skip-login and --skip-dev are retired: login is now an unconditional
@@ -322,6 +325,7 @@ Options:
322
325
  --permset-name <name> Assign only this permission set (repeatable). Default: all sets under permissionsets/
323
326
  --skip-deploy Do not deploy metadata
324
327
  --skip-permset Do not assign permission set
328
+ --skip-social-login Do not enable social login (auth providers + community profile)
325
329
  --skip-data Do not prepare data or run data import
326
330
  --skip-graphql Do not fetch schema or run GraphQL codegen
327
331
  --skip-ui-bundle-build Do not npm install / build the UI bundle
@@ -521,6 +525,435 @@ function loadSelfRegConfig(config) {
521
525
  };
522
526
  }
523
527
 
528
+ /**
529
+ * Social login config, read from the already-validated config.
530
+ *
531
+ * Config shape:
532
+ * {
533
+ * "socialLogin": {
534
+ * "communityMemberProfile": "Customer Community User",
535
+ * "authProviderNames": ["Google", "Facebook", "My_SAML_Provider"],
536
+ * "communityUserPermset": "myapp_Guest_User_Api_Access" // optional
537
+ * }
538
+ * }
539
+ *
540
+ * - communityMemberProfile: profile added to NetworkMemberGroup (required for access)
541
+ * - authProviderNames: DeveloperNames of AuthProvider (OAuth) or SamlSsoConfig (SAML) records to link to site
542
+ * - communityUserPermset: permset assigned to community users so getCurrentUser()
543
+ * works (requires PermissionsApiEnabled for /chatter/users/me via /sf/api/)
544
+ *
545
+ * Returns null if no "socialLogin" section exists in config (the step is hidden).
546
+ */
547
+ function loadSocialLoginConfig(config) {
548
+ const section = config.socialLogin;
549
+ if (!section) return null;
550
+ return {
551
+ communityMemberProfile: section.communityMemberProfile,
552
+ authProviderNames: section.authProviderNames,
553
+ communityUserPermset: section.communityUserPermset || null,
554
+ };
555
+ }
556
+
557
+ /**
558
+ * Enable the "Allow using standard external profiles for self-registration,
559
+ * user creation, and login" org setting via Metadata API deploy.
560
+ *
561
+ * This setting is required for SSO registration handlers to create users with
562
+ * standard community profiles (e.g. "Customer Community User"). Without it,
563
+ * auth providers return FIELD_INTEGRITY_EXCEPTION on user insert.
564
+ *
565
+ * The correct metadata field is `enableOotbProfExtUserOpsEnable` in
566
+ * CommunitiesSettings. The REST/Tooling API approaches also fail on many
567
+ * org types. Deploying via Metadata API is the most reliable method.
568
+ */
569
+ function enableExternalProfiles(targetOrg) {
570
+ const dir = mkdtempSync(join(tmpdir(), APEX_TMP_PREFIX));
571
+ try {
572
+ // Create a minimal sfdx project with just the Communities setting
573
+ const settingsDir = join(dir, 'force-app', 'main', 'default', 'settings');
574
+ mkdirSync(settingsDir, { recursive: true });
575
+
576
+ writeFileSync(join(dir, 'sfdx-project.json'), JSON.stringify({
577
+ packageDirectories: [{ path: 'force-app', default: true }],
578
+ sourceApiVersion: '68.0',
579
+ }));
580
+
581
+ writeFileSync(join(settingsDir, 'Communities.settings-meta.xml'), [
582
+ '<?xml version="1.0" encoding="UTF-8"?>',
583
+ '<CommunitiesSettings xmlns="http://soap.sforce.com/2006/04/metadata">',
584
+ ' <enableOotbProfExtUserOpsEnable>true</enableOotbProfExtUserOpsEnable>',
585
+ '</CommunitiesSettings>',
586
+ ].join('\n'));
587
+
588
+ const result = spawnSync('sf', [
589
+ 'project', 'deploy', 'start',
590
+ '--target-org', targetOrg,
591
+ '--source-dir', join(dir, 'force-app'),
592
+ '--json',
593
+ ], { cwd: dir, stdio: 'pipe', shell: true, timeout: 120000 });
594
+
595
+ const out = result.stdout?.toString() || '';
596
+ let deployResult;
597
+ try { deployResult = JSON.parse(out); } catch { deployResult = null; }
598
+
599
+ if (deployResult?.status === 0 || deployResult?.result?.status === 'Succeeded') {
600
+ console.log(' Enabled "Allow using standard external profiles" org setting.');
601
+ } else {
602
+ // Check if it failed because the setting is already enabled or not deployable
603
+ const errMsg = deployResult?.message || deployResult?.result?.details?.componentFailures?.[0]?.problem || '';
604
+ if (errMsg.includes('already') || errMsg.includes('enableOotbProfExtUserOpsEnable')) {
605
+ console.log(' Warning: could not deploy "Allow standard external profiles" setting.');
606
+ console.log(` Detail: ${errMsg}`);
607
+ console.log(' The setting may already be active. Enable manually via Setup > Digital Experiences > Settings if needed.');
608
+ } else {
609
+ console.log(' Warning: could not enable "Allow standard external profiles".');
610
+ console.log(` Deploy result: ${errMsg || 'unknown error'}`);
611
+ console.log(' Enable manually via Setup > Digital Experiences > Settings.');
612
+ }
613
+ }
614
+ } finally {
615
+ try { rmSync(dir, { recursive: true, force: true }); } catch { /* best effort */ }
616
+ }
617
+ }
618
+
619
+ /**
620
+ * Enable Auth Providers for a community by creating AuthConfigProviders junction
621
+ * records via Anonymous Apex + REST API callouts.
622
+ *
623
+ * React (Site Container) sites intentionally hide SSO configuration in the Admin
624
+ * UI, so this must be done programmatically. The approach:
625
+ * 1. Query the AuthConfig record for the site (one per community).
626
+ * 2. Query AuthProvider records by DeveloperName.
627
+ * 3. Query SamlSsoConfig records by DeveloperName (for SAML providers).
628
+ * 4. Check existing AuthConfigProviders to avoid duplicates.
629
+ * 5. Create missing AuthConfigProviders via REST API (DML is not allowed on this object).
630
+ *
631
+ * AuthConfig.AuthOptionsAuthProvider auto-flips to true when AuthConfigProviders
632
+ * are inserted — no separate update needed.
633
+ *
634
+ * @param {string[]} authProviderNames - DeveloperNames of AuthProvider or SamlSsoConfig records
635
+ * @param {string} siteName - Name of the Network/community
636
+ * @param {string} targetOrg - Target org alias
637
+ */
638
+ function enableAuthProvidersForSite(authProviderNames, siteName, targetOrg) {
639
+ // DML is not allowed on AuthConfigProviders, so we use Anonymous Apex with
640
+ // HTTP callouts to the REST API to create the junction records.
641
+ // Whitelist each DeveloperName before it is interpolated into the generated
642
+ // Apex `DeveloperName IN (…)` literal — same call-site guard the data-import
643
+ // path applies with validateApiName, and defense-in-depth alongside the
644
+ // up-front socialLogin validation in main(). loadSocialLoginConfig already
645
+ // rejects an empty list, so the resulting literal is never empty here.
646
+ const providerNamesLiteral = authProviderNames
647
+ .map((n) => `'${validateSoqlName(n, 'socialLogin.authProviderNames entry')}'`)
648
+ .join(', ');
649
+
650
+ const apex = `
651
+ // Enable Auth Providers (OAuth + SAML) for community "${siteName}"
652
+ Http http = new Http();
653
+ String baseUrl = Url.getOrgDomainUrl().toExternalForm();
654
+ String token = UserInfo.getSessionId();
655
+
656
+ // Step 1: Find the AuthConfig for this site
657
+ List<AuthConfig> authConfigs = [
658
+ SELECT Id, Url
659
+ FROM AuthConfig
660
+ WHERE Url LIKE '%${siteName}%'
661
+ LIMIT 1
662
+ ];
663
+ if (authConfigs.isEmpty()) {
664
+ System.debug('ERROR_NO_AUTHCONFIG');
665
+ return;
666
+ }
667
+ AuthConfig ac = authConfigs[0];
668
+ System.debug('AUTHCONFIG_FOUND:' + ac.Id + ':' + ac.Url);
669
+
670
+ // Step 2: Query AuthProvider records by DeveloperName (OAuth providers)
671
+ List<AuthProvider> oauthProviders = [
672
+ SELECT Id, DeveloperName, FriendlyName
673
+ FROM AuthProvider
674
+ WHERE DeveloperName IN (${providerNamesLiteral})
675
+ ];
676
+ System.debug('OAUTH_PROVIDERS_FOUND:' + oauthProviders.size());
677
+
678
+ // Step 3: Query SamlSsoConfig records by DeveloperName (SAML providers)
679
+ List<SamlSsoConfig> samlProviders = [
680
+ SELECT Id, DeveloperName
681
+ FROM SamlSsoConfig
682
+ WHERE DeveloperName IN (${providerNamesLiteral})
683
+ ];
684
+ System.debug('SAML_PROVIDERS_FOUND:' + samlProviders.size());
685
+
686
+ // Combine both into a unified list with (Id, DeveloperName, Type)
687
+ List<Map<String, String>> allProviders = new List<Map<String, String>>();
688
+ for (AuthProvider ap : oauthProviders) {
689
+ allProviders.add(new Map<String, String>{
690
+ 'Id' => ap.Id,
691
+ 'DeveloperName' => ap.DeveloperName,
692
+ 'Type' => 'OAuth'
693
+ });
694
+ }
695
+ for (SamlSsoConfig sp : samlProviders) {
696
+ allProviders.add(new Map<String, String>{
697
+ 'Id' => sp.Id,
698
+ 'DeveloperName' => sp.DeveloperName,
699
+ 'Type' => 'SAML'
700
+ });
701
+ }
702
+
703
+ if (allProviders.isEmpty()) {
704
+ System.debug('ERROR_NO_PROVIDERS');
705
+ return;
706
+ }
707
+ System.debug('TOTAL_PROVIDERS_FOUND:' + allProviders.size());
708
+
709
+ // Step 4: Check existing AuthConfigProviders to avoid duplicates
710
+ Set<Id> existingProviderIds = new Set<Id>();
711
+ for (AuthConfigProviders acp : [
712
+ SELECT AuthProviderId FROM AuthConfigProviders WHERE AuthConfigId = :ac.Id
713
+ ]) {
714
+ existingProviderIds.add(acp.AuthProviderId);
715
+ }
716
+
717
+ // Step 5: Create missing junction records via REST API
718
+ Integer inserted = 0;
719
+ for (Map<String, String> provider : allProviders) {
720
+ String providerId = provider.get('Id');
721
+ String providerName = provider.get('DeveloperName');
722
+ String providerType = provider.get('Type');
723
+
724
+ if (existingProviderIds.contains(providerId)) {
725
+ System.debug('ALREADY_LINKED:' + providerName + ':' + providerType);
726
+ continue;
727
+ }
728
+ HttpRequest req = new HttpRequest();
729
+ req.setEndpoint(baseUrl + '/services/data/v62.0/sobjects/AuthConfigProviders');
730
+ req.setMethod('POST');
731
+ req.setHeader('Authorization', 'Bearer ' + token);
732
+ req.setHeader('Content-Type', 'application/json');
733
+ req.setBody('{"AuthConfigId":"' + ac.Id + '","AuthProviderId":"' + providerId + '"}');
734
+ HttpResponse res = http.send(req);
735
+ if (res.getStatusCode() == 201 || res.getStatusCode() == 200) {
736
+ System.debug('INSERTED:' + providerName + ':' + providerType);
737
+ inserted++;
738
+ } else {
739
+ System.debug('INSERT_FAILED:' + providerName + ':' + providerType + ':' + res.getStatusCode() + ':' + res.getBody());
740
+ }
741
+ }
742
+ if (inserted == 0 && existingProviderIds.size() >= allProviders.size()) {
743
+ System.debug('ALL_ALREADY_LINKED');
744
+ } else {
745
+ System.debug('TOTAL_INSERTED:' + inserted);
746
+ }
747
+ `;
748
+
749
+ const apexOut = withApexTempDir((writeApex) => {
750
+ const tmpApex = writeApex('auth-providers.apex', apex);
751
+ const result = spawnSync('sf', [
752
+ 'apex', 'run', '--target-org', targetOrg, '--file', tmpApex,
753
+ ], { cwd: ROOT, stdio: 'pipe', shell: true, timeout: 60000 });
754
+ const out = result.stdout?.toString() || '';
755
+ const err = result.stderr?.toString() || '';
756
+ if (result.status !== 0 && !out.includes('Compiled successfully')) {
757
+ process.stderr.write(err || out);
758
+ throw new StepError('failed to execute AuthConfigProviders apex');
759
+ }
760
+ return out;
761
+ });
762
+
763
+ // Parse output for status — match only |DEBUG| lines to avoid false positives
764
+ // from the "Execute Anonymous:" source echo that sf apex run always prints.
765
+ if (apexOut.match(/\|DEBUG\|ERROR_NO_AUTHCONFIG/)) {
766
+ throw new StepError(`no AuthConfig found for site "${siteName}" — ensure the site is published/active`);
767
+ }
768
+ if (apexOut.match(/\|DEBUG\|ERROR_NO_PROVIDERS/)) {
769
+ throw new StepError(`no AuthProvider or SamlSsoConfig records found for names: ${authProviderNames.join(', ')} — create them in Setup first`);
770
+ }
771
+
772
+ // Check for insert failures
773
+ const insertFails = [...apexOut.matchAll(/\|DEBUG\|INSERT_FAILED:([^:]+):(OAuth|SAML):(\d+):([^\n]+)/g)];
774
+ if (insertFails.length > 0) {
775
+ for (const m of insertFails) {
776
+ console.error(` ✖ ${m[1]} (${m[2]}) — HTTP ${m[3]}: ${m[4].trim()}`);
777
+ }
778
+ throw new StepError(`failed to link ${insertFails.length} Auth Provider(s) to site AuthConfig`);
779
+ }
780
+
781
+ const totalMatch = apexOut.match(/\|DEBUG\|TOTAL_INSERTED:(\d+)/);
782
+ if (totalMatch) {
783
+ console.log(` Linked ${totalMatch[1]} Auth Provider(s) to site "${siteName}".`);
784
+ } else if (apexOut.match(/\|DEBUG\|ALL_ALREADY_LINKED/)) {
785
+ console.log(` All Auth Provider(s) already linked to site "${siteName}"; no changes needed.`);
786
+ }
787
+
788
+ // Log individual provider statuses
789
+ const alreadyLinked = [...apexOut.matchAll(/\|DEBUG\|ALREADY_LINKED:([^:]+):(OAuth|SAML)/g)];
790
+ for (const m of alreadyLinked) {
791
+ console.log(` ✔ ${m[1]} (${m[2]}, already linked)`);
792
+ }
793
+ const inserted = [...apexOut.matchAll(/\|DEBUG\|INSERTED:([^:]+):(OAuth|SAML)/g)];
794
+ for (const m of inserted) {
795
+ console.log(` + ${m[1]} (${m[2]}, newly linked)`);
796
+ }
797
+ }
798
+
799
+ /**
800
+ * Add a community user profile to the NetworkMemberGroup so that SSO-registered
801
+ * users (who get assigned this profile by the registration handler) can access
802
+ * the community.
803
+ *
804
+ * Without this, users created by the SSO registration handler get
805
+ * "NO_ACCESS: User was not authorized for the community".
806
+ *
807
+ * @param {string} profileName - Profile name to add (e.g. "Customer Community User")
808
+ * @param {string} siteName - Name of the Network/community
809
+ * @param {string} targetOrg - Target org alias
810
+ */
811
+ function addCommunityMemberProfile(profileName, siteName, targetOrg) {
812
+ // Query Network Id
813
+ const netQuery = `SELECT Id FROM Network WHERE Name = '${siteName}'`;
814
+ const netResult = spawnSync('sf', [
815
+ 'data', 'query', '--query', netQuery, '--target-org', targetOrg, '--json',
816
+ ], { cwd: ROOT, encoding: 'utf8' });
817
+ let networkId = null;
818
+ if (netResult.status === 0) {
819
+ try {
820
+ const json = JSON.parse(netResult.stdout);
821
+ networkId = json.result?.records?.[0]?.Id || null;
822
+ } catch { /* fall through */ }
823
+ }
824
+ if (!networkId) {
825
+ throw new StepError(`could not find Network "${siteName}" in org`);
826
+ }
827
+
828
+ // Query Profile Id
829
+ const profQuery = `SELECT Id FROM Profile WHERE Name = '${profileName}'`;
830
+ const profResult = spawnSync('sf', [
831
+ 'data', 'query', '--query', profQuery, '--target-org', targetOrg, '--json',
832
+ ], { cwd: ROOT, encoding: 'utf8' });
833
+ let profileId = null;
834
+ if (profResult.status === 0) {
835
+ try {
836
+ const json = JSON.parse(profResult.stdout);
837
+ profileId = json.result?.records?.[0]?.Id || null;
838
+ } catch { /* fall through */ }
839
+ }
840
+ if (!profileId) {
841
+ throw new StepError(`could not find Profile "${profileName}" in org`);
842
+ }
843
+
844
+ // Check if already a member
845
+ const memberQuery = `SELECT Id FROM NetworkMemberGroup WHERE NetworkId = '${networkId}' AND ParentId = '${profileId}'`;
846
+ const memberResult = spawnSync('sf', [
847
+ 'data', 'query', '--query', memberQuery, '--target-org', targetOrg, '--json',
848
+ ], { cwd: ROOT, encoding: 'utf8' });
849
+ let alreadyExists = false;
850
+ if (memberResult.status === 0) {
851
+ try {
852
+ const json = JSON.parse(memberResult.stdout);
853
+ alreadyExists = (json.result?.records?.length || 0) > 0;
854
+ } catch { /* proceed to create */ }
855
+ }
856
+ if (alreadyExists) {
857
+ console.log(` Profile "${profileName}" is already a member of community "${siteName}"; skipping.`);
858
+ return;
859
+ }
860
+
861
+ // Create NetworkMemberGroup record
862
+ const createResult = spawnSync('sf', [
863
+ 'data', 'create', 'record',
864
+ '--sobject', 'NetworkMemberGroup',
865
+ '--values', `NetworkId='${networkId}' ParentId='${profileId}'`,
866
+ '--target-org', targetOrg,
867
+ '--json',
868
+ ], { cwd: ROOT, encoding: 'utf8' });
869
+ if (createResult.status !== 0) {
870
+ const out = (createResult.stderr || '') + (createResult.stdout || '');
871
+ if (out) process.stderr.write(out);
872
+ throw new StepError(`failed to add profile "${profileName}" to NetworkMemberGroup for "${siteName}"`);
873
+ }
874
+ try {
875
+ const json = JSON.parse(createResult.stdout);
876
+ const id = json.result?.id;
877
+ console.log(` Added profile "${profileName}" to community "${siteName}" (NetworkMemberGroup: ${id}).`);
878
+ } catch {
879
+ console.log(` Added profile "${profileName}" to community "${siteName}".`);
880
+ }
881
+ }
882
+
883
+ /**
884
+ * Assign a permission set to all existing community users with a given profile.
885
+ *
886
+ * After SSO login, community users need the app's API-enabled permset so that
887
+ * getCurrentUser() (which calls /chatter/users/me via the /sf/api/ proxy) works.
888
+ * Without PermissionsApiEnabled the proxy returns API_DISABLED_FOR_ORG and the
889
+ * React app treats the user as unauthenticated.
890
+ *
891
+ * This is idempotent: already-assigned users are skipped.
892
+ */
893
+ function assignPermsetToCommunityUsers(permsetName, profileName, targetOrg) {
894
+ // Resolve PermissionSet Id
895
+ const psQuery = `SELECT Id FROM PermissionSet WHERE Name = '${permsetName}'`;
896
+ const psResult = spawnSync('sf', [
897
+ 'data', 'query', '--query', psQuery, '--target-org', targetOrg, '--json',
898
+ ], { cwd: ROOT, encoding: 'utf8' });
899
+ let permsetId = null;
900
+ if (psResult.status === 0) {
901
+ try {
902
+ const json = JSON.parse(psResult.stdout);
903
+ permsetId = json.result?.records?.[0]?.Id || null;
904
+ } catch { /* fall through */ }
905
+ }
906
+ if (!permsetId) {
907
+ throw new StepError(`could not find PermissionSet "${permsetName}" in org`);
908
+ }
909
+
910
+ // Find community users on the given profile who do NOT already have the permset
911
+ const usersQuery = [
912
+ `SELECT Id, Username FROM User`,
913
+ `WHERE Profile.Name = '${profileName}'`,
914
+ `AND IsActive = true`,
915
+ `AND UserType IN ('CspLitePortal', 'CustomerSuccess', 'PowerCustomerSuccess')`,
916
+ `AND Id NOT IN (SELECT AssigneeId FROM PermissionSetAssignment WHERE PermissionSetId = '${permsetId}')`,
917
+ ].join(' ');
918
+ const usersResult = spawnSync('sf', [
919
+ 'data', 'query', '--query', usersQuery, '--target-org', targetOrg, '--json',
920
+ ], { cwd: ROOT, encoding: 'utf8' });
921
+ let users = [];
922
+ if (usersResult.status === 0) {
923
+ try {
924
+ const json = JSON.parse(usersResult.stdout);
925
+ users = json.result?.records || [];
926
+ } catch { /* empty list */ }
927
+ }
928
+
929
+ if (users.length === 0) {
930
+ console.log(` No community users on profile "${profileName}" need permset "${permsetName}"; skipping.`);
931
+ return;
932
+ }
933
+
934
+ // Assign the permset to each user
935
+ let assigned = 0;
936
+ for (const user of users) {
937
+ const createResult = spawnSync('sf', [
938
+ 'data', 'create', 'record',
939
+ '--sobject', 'PermissionSetAssignment',
940
+ '--values', `PermissionSetId='${permsetId}' AssigneeId='${user.Id}'`,
941
+ '--target-org', targetOrg,
942
+ '--json',
943
+ ], { cwd: ROOT, encoding: 'utf8' });
944
+ if (createResult.status === 0) {
945
+ assigned++;
946
+ } else {
947
+ // Log but don't fail — user might already have it via race condition
948
+ const out = (createResult.stdout || '') + (createResult.stderr || '');
949
+ if (!out.includes('DUPLICATE_VALUE')) {
950
+ console.warn(` Warning: could not assign "${permsetName}" to ${user.Username}: ${out.slice(0, 120)}`);
951
+ }
952
+ }
953
+ }
954
+ console.log(` Assigned permset "${permsetName}" to ${assigned}/${users.length} community user(s).`);
955
+ }
956
+
524
957
  /**
525
958
  * Ensure the self-registration profile is listed in networkMemberGroups.
526
959
  * This must happen BEFORE the initial deploy so that the profile is a recognised
@@ -1056,7 +1489,10 @@ function buildApexInsert(sobject, records, refIds) {
1056
1489
  lines.push(' else { m.put(\'err\', results[i].getErrors()[0].getMessage()); }');
1057
1490
  lines.push(' setupOut.add(m);');
1058
1491
  lines.push('}');
1059
- lines.push("System.debug('SETUP_RESULT_JSON:' + JSON.serialize(setupOut));");
1492
+ // Use LoggingLevel.ERROR to ensure this critical line appears near the end of
1493
+ // the debug log and isn't truncated by excessive DEBUG-level noise from DML
1494
+ // operations, workflow rules, or process builders that fire during insert.
1495
+ lines.push("System.debug(LoggingLevel.ERROR, 'SETUP_RESULT_JSON:' + JSON.serialize(setupOut));");
1060
1496
  return lines.join('\n');
1061
1497
  }
1062
1498
 
@@ -1316,6 +1752,7 @@ async function main() {
1316
1752
  skipPermset: argSkipPermset,
1317
1753
  skipRole: argSkipRole,
1318
1754
  skipSelfReg: argSkipSelfReg,
1755
+ skipSocialLogin: argSkipSocialLogin,
1319
1756
  skipData: argSkipData,
1320
1757
  skipGraphql: argSkipGraphql,
1321
1758
  skipUIBundleBuild: argSkipUIBundleBuild,
@@ -1349,6 +1786,8 @@ async function main() {
1349
1786
  const hasRoleConfig = roleConfig !== null;
1350
1787
  const selfRegConfig = loadSelfRegConfig(config);
1351
1788
  const hasSelfRegConfig = selfRegConfig !== null;
1789
+ const socialLoginConfig = loadSocialLoginConfig(config);
1790
+ const hasSocialLoginConfig = socialLoginConfig !== null;
1352
1791
 
1353
1792
  // Validate the selfRegProfile name for SOQL-safety up front, alongside the
1354
1793
  // config validation and BEFORE any org mutation (login/deploy). A quote /
@@ -1364,6 +1803,30 @@ async function main() {
1364
1803
  }
1365
1804
  }
1366
1805
 
1806
+ // Same fail-fast treatment for the socialLogin identifiers, alongside the
1807
+ // self-reg guard above and BEFORE any org mutation. communityMemberProfile,
1808
+ // every authProviderNames entry, and the optional communityUserPermset are all
1809
+ // interpolated into generated SOQL / CLI value strings — the Profile and
1810
+ // PermissionSet `WHERE Name = '…'` lookups, the AuthProvider/SamlSsoConfig
1811
+ // `DeveloperName IN (…)` list, and the NetworkMemberGroup/User queries. These
1812
+ // are developer-authored identifiers the app ships, so a quote / backslash /
1813
+ // control char is a config mistake to surface loudly here rather than a value
1814
+ // to silently escape (same rationale as validateSoqlName's self-reg use).
1815
+ if (hasSocialLoginConfig) {
1816
+ try {
1817
+ validateSoqlName(socialLoginConfig.communityMemberProfile, 'socialLogin.communityMemberProfile');
1818
+ for (const providerName of socialLoginConfig.authProviderNames ?? []) {
1819
+ validateSoqlName(providerName, 'socialLogin.authProviderNames entry');
1820
+ }
1821
+ if (socialLoginConfig.communityUserPermset) {
1822
+ validateSoqlName(socialLoginConfig.communityUserPermset, 'socialLogin.communityUserPermset');
1823
+ }
1824
+ } catch (err) {
1825
+ console.error(`Invalid org-setup.config.json:\n - ${err.message}`);
1826
+ process.exit(1);
1827
+ }
1828
+ }
1829
+
1367
1830
  // failFast is a fixed, implementer-owned classification — NOT
1368
1831
  // user-configurable. A fail-fast failure aborts the run immediately; a
1369
1832
  // skippable failure is recorded and the run continues. The exit code is
@@ -1378,6 +1841,7 @@ async function main() {
1378
1841
  { key: 'permset', label: permsetStepLabel, enabled: !argSkipPermset, available: true, failFast: false },
1379
1842
  { key: 'role', label: `Role — assign "${roleConfig?.roleName ?? '?'}" to current user`, enabled: !argSkipRole && hasRoleConfig, available: hasRoleConfig, failFast: false },
1380
1843
  { key: 'selfReg', label: 'Self-Registration — enable for site', enabled: !argSkipSelfReg && hasSelfRegConfig, available: hasSelfRegConfig, failFast: false },
1844
+ { key: 'socialLogin', label: 'Social Login — enable auth providers + community profile', enabled: !argSkipSocialLogin && hasSocialLoginConfig, available: hasSocialLoginConfig, failFast: false },
1381
1845
  { key: 'data', label: 'Data — delete + import records via Apex', enabled: !argSkipData && hasDataPlan, available: hasDataPlan, failFast: true },
1382
1846
  { key: 'graphql', label: 'GraphQL — schema introspect + codegen', enabled: !argSkipGraphql, available: true, failFast: true },
1383
1847
  ];
@@ -1393,6 +1857,7 @@ async function main() {
1393
1857
  const skipPermset = !on.permset;
1394
1858
  const skipRole = !on.role;
1395
1859
  const skipSelfReg = !on.selfReg;
1860
+ const skipSocialLogin = !on.socialLogin;
1396
1861
  const skipData = !on.data;
1397
1862
  const skipGraphql = !on.graphql;
1398
1863
 
@@ -1402,11 +1867,12 @@ async function main() {
1402
1867
 
1403
1868
  console.log('Setup — target org:', targetOrg, '| UI bundle:', uiBundleDir ?? '(none)');
1404
1869
  console.log(
1405
- 'Steps: login=always deploy=%s permset=%s role=%s selfReg=%s data=%s graphql=%s uiBundle=%s',
1870
+ 'Steps: login=always deploy=%s permset=%s role=%s selfReg=%s socialLogin=%s data=%s graphql=%s uiBundle=%s',
1406
1871
  !skipDeploy,
1407
1872
  !skipPermset,
1408
1873
  !skipRole,
1409
1874
  !skipSelfReg,
1875
+ !skipSocialLogin,
1410
1876
  doData,
1411
1877
  !skipGraphql,
1412
1878
  !skipUIBundleBuild
@@ -1619,6 +2085,53 @@ async function main() {
1619
2085
  recordSkipped(selfRegStep, selfRegStep.available ? 'not selected' : 'no config');
1620
2086
  }
1621
2087
 
2088
+ const socialLoginStep = stepDefs.find((s) => s.key === 'socialLogin');
2089
+ if (!skipSocialLogin) {
2090
+ console.log('\n--- Social Login — enable auth providers + community profile ---');
2091
+ await runStep(socialLoginStep, targetOrg, () => {
2092
+ const siteName = deriveSiteName();
2093
+ if (!siteName) {
2094
+ throw new StepError(
2095
+ 'socialLogin is configured but no networks/<siteName>.network-meta.xml was found to derive the site',
2096
+ );
2097
+ }
2098
+
2099
+ const totalSubSteps = socialLoginConfig.communityUserPermset ? 4 : 3;
2100
+
2101
+ // Sub-step 1: Enable "Allow using standard external profiles" org setting.
2102
+ // This is required so the SSO registration handler can create users with
2103
+ // standard community profiles (e.g. "Customer Community User").
2104
+ console.log(` [1/${totalSubSteps}] Enabling "Allow standard external profiles" setting...`);
2105
+ enableExternalProfiles(targetOrg);
2106
+
2107
+ // Sub-step 2: Link Auth Providers to the site's AuthConfig.
2108
+ // React (Site Container) sites hide SSO config in Admin UI, so this must
2109
+ // be done programmatically via AuthConfig/AuthConfigProviders records.
2110
+ console.log(` [2/${totalSubSteps}] Linking Auth Providers to site AuthConfig...`);
2111
+ enableAuthProvidersForSite(socialLoginConfig.authProviderNames, siteName, targetOrg);
2112
+
2113
+ // Sub-step 3: Add the community member profile to NetworkMemberGroup.
2114
+ // Without this, users created by the registration handler get
2115
+ // "NO_ACCESS: User was not authorized for the community".
2116
+ console.log(` [3/${totalSubSteps}] Adding community member profile to NetworkMemberGroup...`);
2117
+ addCommunityMemberProfile(socialLoginConfig.communityMemberProfile, siteName, targetOrg);
2118
+
2119
+ // Sub-step 4 (optional): Assign API-enabled permset to community users.
2120
+ // Without this, getCurrentUser() fails because /chatter/users/me requires
2121
+ // PermissionsApiEnabled which the standard community profile lacks.
2122
+ if (socialLoginConfig.communityUserPermset) {
2123
+ console.log(` [4/${totalSubSteps}] Assigning API permset to community users...`);
2124
+ assignPermsetToCommunityUsers(
2125
+ socialLoginConfig.communityUserPermset,
2126
+ socialLoginConfig.communityMemberProfile,
2127
+ targetOrg,
2128
+ );
2129
+ }
2130
+ });
2131
+ } else {
2132
+ recordSkipped(socialLoginStep, socialLoginStep.available ? 'not selected' : 'no config');
2133
+ }
2134
+
1622
2135
  const dataStep = stepDefs.find((s) => s.key === 'data');
1623
2136
  if (doData) {
1624
2137
  await runStep(dataStep, targetOrg, () => {
@@ -1672,8 +2185,14 @@ async function main() {
1672
2185
  // API headers (Sforce-Duplicate-Rule-Action) cannot override.
1673
2186
  console.log('\n--- Data import (Apex) ---');
1674
2187
  const refMap = new Map();
2188
+ // Apex anonymous code is capped at ~1M chars total, but each batch is much
2189
+ // smaller in practice. The real constraint is the SETUP_RESULT_JSON debug log
2190
+ // line: Salesforce truncates debug lines at ~5KB, and large batches produce
2191
+ // JSON arrays that exceed that limit. With RESULT_JSON_PER_RECORD = 150 and
2192
+ // MAX_BATCH = 100, the worst-case output is ~15KB of JSON, which fits
2193
+ // comfortably after the debug-line prefix and the marker.
1675
2194
  const APEX_CHAR_LIMIT = 25000;
1676
- const APEX_MAX_BATCH = 200;
2195
+ const APEX_MAX_BATCH = 100;
1677
2196
 
1678
2197
  for (const entry of planEntries) {
1679
2198
  for (const file of entry.files) {
@@ -1705,7 +2224,16 @@ async function main() {
1705
2224
  // rendered refId literal is counted here too (mirroring the same
1706
2225
  // referenceId/_idx fallback the batch emit uses) — otherwise sizing
1707
2226
  // under-counts and a packed batch could edge past APEX_CHAR_LIMIT.
2227
+ //
2228
+ // CRITICAL: also account for the SETUP_RESULT_JSON output size. The
2229
+ // System.debug line can exceed the Salesforce debug log line limit when
2230
+ // the serialized JSON array is large. Estimate ~85 chars per success
2231
+ // result ({"ref":"_idxNNN","id":"001XXXXXXXXXXXXXXX"}) and ~150 chars per
2232
+ // potential error ({"ref":"_idxNNN","err":"typical error message"}), taking
2233
+ // the higher value to be conservative. This ensures the batching prevents
2234
+ // log truncation that would fail parseApexInsertResults.
1708
2235
  const overhead = buildApexInsert(entry.sobject, [], []).length;
2236
+ const RESULT_JSON_PER_RECORD = 150;
1709
2237
  const recordSizes = records.map((rec, i) => {
1710
2238
  let size = `{ ${entry.sobject} r = new ${entry.sobject}();\n`.length + 'recs.add(r); }\n'.length;
1711
2239
  for (const [key, val] of Object.entries(rec)) {
@@ -1714,6 +2242,10 @@ async function main() {
1714
2242
  }
1715
2243
  const refId = rec.attributes?.referenceId || `_idx${i}`;
1716
2244
  size += `'${escapeSoqlString(refId)}',`.length;
2245
+ // Add the estimated size of this record's JSON result entry in the
2246
+ // SETUP_RESULT_JSON output. Without this, large batches can produce
2247
+ // debug log lines that exceed Salesforce's limit and get truncated.
2248
+ size += RESULT_JSON_PER_RECORD;
1717
2249
  return size;
1718
2250
  });
1719
2251
  const batches = planApexBatches(recordSizes, {