@salesforce/templates 66.17.5 → 66.18.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.
Files changed (277) hide show
  1. package/lib/generators/projectGenerator.js +1 -0
  2. package/lib/generators/projectGenerator.js.map +1 -1
  3. package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/package-lock.json +165 -165
  4. package/lib/templates/project/angularintapp/CHANGELOG.md +8 -0
  5. package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/eslint.config.js +2 -1
  6. package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/package-lock.json +169 -169
  7. package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/package.json +4 -4
  8. package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/components/ui/icon/icon.ts +44 -0
  9. package/lib/templates/project/angularintapp/package.json +1 -1
  10. package/lib/templates/project/reactexternalapp/CHANGELOG.md +8 -0
  11. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/eslint.config.js +8 -12
  12. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/package-lock.json +136 -136
  13. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/package.json +5 -5
  14. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/components/ui/button.tsx +1 -1
  15. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/components/ui/sonner.tsx +7 -1
  16. package/lib/templates/project/reactexternalapp/package.json +1 -1
  17. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/package-lock.json +131 -131
  18. package/lib/templates/project/vueinternalapp/.forceignore +15 -0
  19. package/lib/templates/project/vueinternalapp/.husky/pre-commit +4 -0
  20. package/lib/templates/project/vueinternalapp/.prettierignore +11 -0
  21. package/lib/templates/project/vueinternalapp/.prettierrc +17 -0
  22. package/lib/templates/project/vueinternalapp/CHANGELOG.md +5375 -0
  23. package/lib/templates/project/vueinternalapp/PRD.md +35 -0
  24. package/lib/templates/project/vueinternalapp/README.md +125 -0
  25. package/lib/templates/project/vueinternalapp/_p_/_m_/_ps_/vueinternalapp_Access.permissionset-meta.xml +13 -0
  26. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/.forceignore +15 -0
  27. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/.graphqlrc.yml +2 -0
  28. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/.prettierignore +9 -0
  29. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/.prettierrc +11 -0
  30. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/README.md +35 -0
  31. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/codegen.yml +95 -0
  32. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/components.json +19 -0
  33. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/e2e/app.spec.ts +17 -0
  34. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/eslint.config.js +124 -0
  35. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/index.html +12 -0
  36. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/package-lock.json +15530 -0
  37. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/package.json +73 -0
  38. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/playwright.config.ts +24 -0
  39. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/scripts/get-graphql-schema.mjs +71 -0
  40. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/scripts/rewrite-e2e-assets.mjs +23 -0
  41. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/App.vue +5 -0
  42. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/apiUtils.ts +56 -0
  43. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/orgApiVersionService.ts +37 -0
  44. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/api/searchableContentTypesService.ts +119 -0
  45. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/channelResolver.ts +40 -0
  46. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/cmsQueryFragment.ts +108 -0
  47. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/composables/useSearchableContentTypes.ts +132 -0
  48. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/contentTypeSessionCache.ts +134 -0
  49. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/contentTypeUtils.ts +40 -0
  50. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/index.ts +55 -0
  51. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/parseResponse.ts +65 -0
  52. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/searchChannel.ts +31 -0
  53. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/cms/types.ts +66 -0
  54. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/registry.ts +38 -0
  55. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/sobject/index.ts +19 -0
  56. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/sobject/parseResponse.ts +55 -0
  57. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/sobject/queryFragment.ts +141 -0
  58. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/adapters/types.ts +103 -0
  59. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/api/distinctValuesService.ts +81 -0
  60. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/api/searchService.ts +136 -0
  61. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/GlobalSearchBox.types.ts +18 -0
  62. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/GlobalSearchBox.vue +73 -0
  63. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/MergedSearchResults.types.ts +24 -0
  64. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/MergedSearchResults.vue +263 -0
  65. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/Search.vue +200 -0
  66. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SearchResults.vue +99 -0
  67. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SourceSection.vue +204 -0
  68. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/PaginationControls.vue +180 -0
  69. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/ScopeSelector.vue +91 -0
  70. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/SearchBar.vue +56 -0
  71. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/SortControl.vue +55 -0
  72. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/ActiveFilters.vue +59 -0
  73. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/DefaultFilterField.vue +63 -0
  74. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/DefaultFilterPanel.vue +31 -0
  75. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/FilterContext.ts +45 -0
  76. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/FilterProvider.vue +23 -0
  77. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/FilterResetButton.vue +17 -0
  78. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/PicklistField.vue +35 -0
  79. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/BooleanFilter.vue +47 -0
  80. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/DateRangeFilter.vue +144 -0
  81. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/FilterFieldWrapper.vue +23 -0
  82. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/MultiSelectFilter.vue +95 -0
  83. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/NumericRangeFilter.vue +117 -0
  84. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/SelectFilter.vue +44 -0
  85. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/TextFilter.vue +49 -0
  86. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/CmsResultRow.vue +160 -0
  87. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/DefaultResultRow.vue +102 -0
  88. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/RenderVNode.ts +10 -0
  89. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/resolveResultRenderer.ts +41 -0
  90. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/composables/useAsyncData.ts +66 -0
  91. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/composables/useDistinctValues.ts +67 -0
  92. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/composables/useSearch.ts +906 -0
  93. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/config.json +88 -0
  94. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/constants.ts +8 -0
  95. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/index.ts +115 -0
  96. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/loadConfig.ts +14 -0
  97. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/queryBuilder.ts +100 -0
  98. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/types.ts +375 -0
  99. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/debounce.ts +17 -0
  100. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/fieldUtils.ts +14 -0
  101. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/filterUtils.ts +304 -0
  102. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/sortUtils.ts +34 -0
  103. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/api/account/query/getAccountDetail.graphql +121 -0
  104. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/api/graphqlClient.ts +44 -0
  105. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/appLayout.vue +153 -0
  106. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/AgentforceConversationClient.vue +76 -0
  107. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/AccountDetailContent.vue +124 -0
  108. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/AccountDetailError.vue +28 -0
  109. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/AccountDetailNotFound.vue +22 -0
  110. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/AccountDetailSkeleton.vue +57 -0
  111. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/DetailSection.vue +25 -0
  112. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/FieldItem.vue +17 -0
  113. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/FieldRow.vue +5 -0
  114. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/SkeletonFieldRow.vue +12 -0
  115. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/TelephoneField.vue +7 -0
  116. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/account/types.ts +42 -0
  117. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/alerts/status-alert.vue +59 -0
  118. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/layouts/card-layout.vue +30 -0
  119. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/alert/Alert.vue +21 -0
  120. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/alert/AlertAction.vue +17 -0
  121. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/alert/AlertDescription.vue +22 -0
  122. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/alert/AlertTitle.vue +22 -0
  123. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/alert/index.ts +25 -0
  124. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/avatar/Avatar.vue +23 -0
  125. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/avatar/AvatarFallback.vue +31 -0
  126. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/avatar/AvatarImage.vue +16 -0
  127. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/avatar/index.ts +3 -0
  128. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/button/Button.vue +33 -0
  129. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/button/index.ts +43 -0
  130. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/card/Card.vue +29 -0
  131. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/card/CardAction.vue +22 -0
  132. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/card/CardContent.vue +17 -0
  133. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/card/CardDescription.vue +17 -0
  134. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/card/CardFooter.vue +22 -0
  135. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/card/CardHeader.vue +22 -0
  136. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/card/CardTitle.vue +22 -0
  137. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/card/index.ts +7 -0
  138. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/checkbox/Checkbox.vue +43 -0
  139. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/checkbox/index.ts +1 -0
  140. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/collapsible/Collapsible.vue +19 -0
  141. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/collapsible/CollapsibleContent.vue +12 -0
  142. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/collapsible/CollapsibleTrigger.vue +12 -0
  143. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/collapsible/index.ts +3 -0
  144. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/Dialog.vue +15 -0
  145. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/DialogClose.vue +12 -0
  146. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/DialogContent.vue +59 -0
  147. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/DialogDescription.vue +31 -0
  148. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/DialogFooter.vue +33 -0
  149. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/DialogHeader.vue +17 -0
  150. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/DialogOverlay.vue +31 -0
  151. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/DialogPortal.vue +12 -0
  152. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/DialogTitle.vue +26 -0
  153. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/DialogTrigger.vue +12 -0
  154. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dialog/index.ts +10 -0
  155. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenu.vue +19 -0
  156. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +51 -0
  157. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuContent.vue +47 -0
  158. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +12 -0
  159. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuItem.vue +44 -0
  160. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +30 -0
  161. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuPortal.vue +12 -0
  162. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
  163. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +52 -0
  164. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +26 -0
  165. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +19 -0
  166. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuSub.vue +19 -0
  167. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +37 -0
  168. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +38 -0
  169. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
  170. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/dropdown-menu/index.ts +15 -0
  171. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/Field.vue +27 -0
  172. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/FieldContent.vue +22 -0
  173. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/FieldDescription.vue +24 -0
  174. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/FieldError.vue +48 -0
  175. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/FieldGroup.vue +22 -0
  176. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/FieldLabel.vue +24 -0
  177. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/FieldLegend.vue +29 -0
  178. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/FieldSeparator.vue +31 -0
  179. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/FieldSet.vue +22 -0
  180. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/FieldTitle.vue +22 -0
  181. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/field/index.ts +33 -0
  182. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/index.ts +115 -0
  183. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/input/Input.vue +30 -0
  184. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/input/index.ts +1 -0
  185. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/label/Label.vue +29 -0
  186. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/label/index.ts +1 -0
  187. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/pagination/Pagination.vue +17 -0
  188. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/pagination/PaginationContent.vue +15 -0
  189. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/pagination/PaginationEllipsis.vue +23 -0
  190. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/pagination/PaginationItem.vue +7 -0
  191. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/pagination/PaginationLink.vue +31 -0
  192. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/pagination/PaginationNext.vue +29 -0
  193. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/pagination/PaginationPrevious.vue +29 -0
  194. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/pagination/index.ts +7 -0
  195. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/popover/Popover.vue +15 -0
  196. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/popover/PopoverAnchor.vue +12 -0
  197. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/popover/PopoverContent.vue +44 -0
  198. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/popover/PopoverDescription.vue +17 -0
  199. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/popover/PopoverHeader.vue +17 -0
  200. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/popover/PopoverTitle.vue +14 -0
  201. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/popover/PopoverTrigger.vue +12 -0
  202. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/popover/index.ts +7 -0
  203. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/Select.vue +15 -0
  204. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/SelectContent.vue +65 -0
  205. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/SelectGroup.vue +26 -0
  206. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/SelectItem.vue +41 -0
  207. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/SelectLabel.vue +26 -0
  208. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/SelectScrollDownButton.vue +32 -0
  209. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/SelectScrollUpButton.vue +32 -0
  210. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/SelectSeparator.vue +24 -0
  211. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/SelectTrigger.vue +45 -0
  212. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/SelectValue.vue +12 -0
  213. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/select/index.ts +10 -0
  214. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/separator/Separator.vue +33 -0
  215. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/separator/index.ts +1 -0
  216. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/skeleton/Skeleton.vue +15 -0
  217. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/skeleton/index.ts +1 -0
  218. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/sonner/Sonner.vue +29 -0
  219. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/sonner/index.ts +3 -0
  220. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/spinner/Spinner.vue +17 -0
  221. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/components/ui/spinner/index.ts +1 -0
  222. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/composables/useAsyncData.ts +69 -0
  223. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/index.ts +10 -0
  224. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/lib/utils.ts +6 -0
  225. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/main.ts +6 -0
  226. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/pages/AccountObjectDetailPage.vue +98 -0
  227. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/pages/Home.vue +20 -0
  228. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/pages/NotFound.vue +28 -0
  229. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/pages/SearchPage.vue +17 -0
  230. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/router-utils.ts +32 -0
  231. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/router.ts +51 -0
  232. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/styles/global.css +134 -0
  233. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/test/setup.ts +1 -0
  234. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/types/conversation.ts +54 -0
  235. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/types/globals.d.ts +13 -0
  236. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/src/utils/accountFields.ts +25 -0
  237. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/tsconfig.json +45 -0
  238. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/tsconfig.node.json +13 -0
  239. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/tsconfig.tsbuildinfo +1 -0
  240. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/ui-bundle.json +7 -0
  241. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/vite-env.d.ts +14 -0
  242. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/vite.config.ts +106 -0
  243. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/vitest-env.d.ts +2 -0
  244. package/lib/templates/project/vueinternalapp/_p_/_m_/_w_/_a_/vueinternalapp.uibundle-meta.xml +8 -0
  245. package/lib/templates/project/vueinternalapp/_p_/_m_/applications/vueinternalapp.app-meta.xml +17 -0
  246. package/lib/templates/project/vueinternalapp/config/project-scratch-def.json +13 -0
  247. package/lib/templates/project/vueinternalapp/eslint.config.js +7 -0
  248. package/lib/templates/project/vueinternalapp/jest.config.js +6 -0
  249. package/lib/templates/project/vueinternalapp/package.json +46 -0
  250. package/lib/templates/project/vueinternalapp/scripts/apex/hello.apex +10 -0
  251. package/lib/templates/project/vueinternalapp/scripts/gitignore-templates.json +4 -0
  252. package/lib/templates/project/vueinternalapp/scripts/graphql-search.sh +191 -0
  253. package/lib/templates/project/vueinternalapp/scripts/network-senders.mjs +84 -0
  254. package/lib/templates/project/vueinternalapp/scripts/org-setup-config-schema.mjs +132 -0
  255. package/lib/templates/project/vueinternalapp/scripts/org-setup-dev.mjs +114 -0
  256. package/lib/templates/project/vueinternalapp/scripts/org-setup-url.mjs +108 -0
  257. package/lib/templates/project/vueinternalapp/scripts/org-setup-utils.mjs +569 -0
  258. package/lib/templates/project/vueinternalapp/scripts/org-setup-xml.mjs +283 -0
  259. package/lib/templates/project/vueinternalapp/scripts/org-setup.config.json +9 -0
  260. package/lib/templates/project/vueinternalapp/scripts/org-setup.mjs +2545 -0
  261. package/lib/templates/project/vueinternalapp/scripts/sf-project-setup.mjs +103 -0
  262. package/lib/templates/project/vueinternalapp/scripts/soql/account.soql +6 -0
  263. package/lib/templates/project/vueinternalapp/scripts/validate-org-setup-config.mjs +38 -0
  264. package/lib/templates/project/vueinternalapp/sfdx-project.json +12 -0
  265. package/lib/templates/uiBundles/angularbasic/package-lock.json +165 -165
  266. package/lib/templates/uiBundles/reactbasic/eslint.config.js +8 -12
  267. package/lib/templates/uiBundles/reactbasic/package-lock.json +136 -136
  268. package/lib/templates/uiBundles/reactbasic/package.json +5 -5
  269. package/lib/templates/uiBundles/reactbasic/src/components/ui/button.tsx +1 -1
  270. package/lib/templates/uiBundles/reactbasic/src/components/ui/sonner.tsx +7 -1
  271. package/lib/templates/uiBundles/vuebasic/eslint.config.js +6 -1
  272. package/lib/templates/uiBundles/vuebasic/package-lock.json +143 -143
  273. package/lib/templates/uiBundles/vuebasic/package.json +5 -5
  274. package/lib/utils/types.d.ts +1 -1
  275. package/lib/utils/uiBundleTemplateUtils.js +5 -0
  276. package/lib/utils/uiBundleTemplateUtils.js.map +1 -1
  277. package/package.json +7 -6
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "base-vue-app",
3
+ "private": true,
4
+ "version": "1.59.0",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=22"
8
+ },
9
+ "scripts": {
10
+ "dev": "vite",
11
+ "dev:design": "vite --mode design",
12
+ "build": "vue-tsc -b && vite build",
13
+ "build:e2e": "npm run build && node scripts/rewrite-e2e-assets.mjs",
14
+ "lint": "eslint .",
15
+ "preview": "vite preview",
16
+ "test": "vitest",
17
+ "graphql:codegen": "graphql-codegen",
18
+ "graphql:schema": "node scripts/get-graphql-schema.mjs"
19
+ },
20
+ "dependencies": {
21
+ "@lucide/vue": "^1.47.0",
22
+ "@salesforce/platform-sdk": "^12.14.0",
23
+ "@salesforce/ui-bundle": "^12.14.0",
24
+ "@tailwindcss/vite": "^4.1.17",
25
+ "class-variance-authority": "^0.7.1",
26
+ "clsx": "^2.1.1",
27
+ "date-fns": "^4.1.0",
28
+ "reka-ui": "^2.5.0",
29
+ "tailwind-merge": "^3.5.0",
30
+ "tailwindcss": "^4.1.17",
31
+ "tw-animate-css": "^1.4.0",
32
+ "vue": "^3.5.13",
33
+ "vue-router": "^4.5.0",
34
+ "vue-sonner": "^2.0.0",
35
+ "@salesforce/agentforce-conversation-client": "^12.0.0"
36
+ },
37
+ "devDependencies": {
38
+ "@eslint/js": "^9.39.1",
39
+ "@graphql-codegen/cli": "^6.1.0",
40
+ "@graphql-codegen/typescript": "^5.0.6",
41
+ "@graphql-codegen/typescript-operations": "^5.0.6",
42
+ "@graphql-eslint/eslint-plugin": "^4.1.0",
43
+ "@graphql-tools/utils": "^11.0.0",
44
+ "@playwright/test": "^1.49.0",
45
+ "@salesforce/graphiti": "^12.14.0",
46
+ "@salesforce/vite-plugin-ui-bundle": "^12.14.0",
47
+ "@testing-library/jest-dom": "^6.6.3",
48
+ "@testing-library/user-event": "^14.5.2",
49
+ "@testing-library/vue": "^8.1.0",
50
+ "@types/node": "^24.10.1",
51
+ "@vitejs/plugin-vue": "^6.0.0",
52
+ "@vitest/ui": "^4.1.0",
53
+ "@vue/eslint-config-typescript": "^14.1.4",
54
+ "@vue/test-utils": "^2.4.6",
55
+ "eslint": "^9.39.1",
56
+ "eslint-plugin-vue": "^9.32.0",
57
+ "globals": "^16.5.0",
58
+ "graphql": "^16.11.0",
59
+ "graphql-codegen-typescript-operation-types": "^2.0.2",
60
+ "jsdom": "^25.0.1",
61
+ "serve": "^14.2.5",
62
+ "typescript": "~5.9.3",
63
+ "typescript-eslint": "^8.71.2",
64
+ "vite": "^7.2.4",
65
+ "vite-plugin-graphql-codegen": "^3.6.3",
66
+ "vitest": "^4.1.0",
67
+ "vue-tsc": "^2.2.0"
68
+ },
69
+ "overrides": {
70
+ "lodash": "^4.18.1",
71
+ "undici": "^7.28.0"
72
+ }
73
+ }
@@ -0,0 +1,24 @@
1
+ import { defineConfig, devices } from '@playwright/test';
2
+
3
+ const E2E_PORT = 5177;
4
+
5
+ export default defineConfig({
6
+ testDir: './e2e',
7
+ fullyParallel: true,
8
+ forbidOnly: !!process.env.CI,
9
+ retries: process.env.CI ? 2 : 0,
10
+ workers: process.env.CI ? 1 : undefined,
11
+ reporter: 'html',
12
+ use: {
13
+ baseURL: `http://localhost:${E2E_PORT}`,
14
+ trace: 'on-first-retry',
15
+ },
16
+ projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
17
+ webServer: {
18
+ // Serve built dist/ with static server so e2e works in CI without SF org (vite preview runs plugin and can fail)
19
+ command: `npx serve dist -l ${E2E_PORT}`,
20
+ url: `http://localhost:${E2E_PORT}`,
21
+ reuseExistingServer: !process.env.CI,
22
+ timeout: process.env.CI ? 120_000 : 60_000,
23
+ },
24
+ });
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Downloads the full GraphQL schema from a connected Salesforce org via introspection.
3
+ *
4
+ * Usage:
5
+ * npm run graphql:schema
6
+ * node scripts/get-graphql-schema.mjs [output-path]
7
+ *
8
+ * The default output path matches the schema location expected by codegen.yml
9
+ * and .graphqlrc.yml so that codegen and IDE tooling resolve it automatically.
10
+ */
11
+ import { writeFileSync } from 'node:fs';
12
+ import { resolve } from 'node:path';
13
+ import { getOrgInfo } from '@salesforce/ui-bundle/app';
14
+ import { buildClientSchema, getIntrospectionQuery, printSchema } from 'graphql';
15
+ import { pruneSchema } from '@graphql-tools/utils';
16
+
17
+ const DEFAULT_SCHEMA_PATH = '../../../../../schema.graphql';
18
+ const TARGET_ORG = process.env.SF_TARGET_ORG || undefined;
19
+
20
+ async function executeSalesforceGraphQLQuery(query, variables, operationName) {
21
+ const orgInfo = await getOrgInfo(TARGET_ORG);
22
+ if (!orgInfo) {
23
+ throw new Error(
24
+ 'Could not resolve a Salesforce org. Set SF_TARGET_ORG or a default org.'
25
+ );
26
+ }
27
+ const { rawInstanceUrl: instanceUrl, apiVersion, accessToken } = orgInfo;
28
+
29
+ const targetUrl = `${instanceUrl}/services/data/v${apiVersion}/graphql`;
30
+
31
+ console.log(`Executing introspection query against ${targetUrl}`);
32
+ const response = await fetch(targetUrl, {
33
+ method: 'POST',
34
+ headers: {
35
+ Authorization: `Bearer ${accessToken}`,
36
+ 'Content-Type': 'application/json',
37
+ 'X-Chatter-Entity-Encoding': 'false',
38
+ },
39
+ body: JSON.stringify({ query, variables, operationName }),
40
+ });
41
+
42
+ if (!response.ok) {
43
+ const errorText = await response.text();
44
+ throw new Error(
45
+ `Salesforce GraphQL request failed: ${response.status} ${response.statusText} - ${errorText}`
46
+ );
47
+ }
48
+
49
+ return response.json();
50
+ }
51
+
52
+ try {
53
+ const outputPath = resolve(process.argv[2] || DEFAULT_SCHEMA_PATH);
54
+
55
+ const introspectionResult = await executeSalesforceGraphQLQuery(
56
+ getIntrospectionQuery(),
57
+ {},
58
+ 'IntrospectionQuery'
59
+ );
60
+
61
+ const schema = buildClientSchema(introspectionResult.data);
62
+ const prunedSchema = pruneSchema(schema);
63
+ const sdl = printSchema(prunedSchema);
64
+
65
+ writeFileSync(outputPath, sdl);
66
+
67
+ console.log(`Schema saved to ${outputPath}`);
68
+ } catch (error) {
69
+ console.error('Error:', error.message);
70
+ process.exit(1);
71
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Prepares dist/ for e2e: root-relative asset paths + SPA fallback for serve.
3
+ */
4
+ import { readFileSync, writeFileSync } from 'node:fs';
5
+ import { join, dirname } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ const distDir = join(__dirname, '..', 'dist');
10
+
11
+ // Rewrite index.html so asset paths are root-relative (/assets/...)
12
+ const indexPath = join(distDir, 'index.html');
13
+ let html = readFileSync(indexPath, 'utf8');
14
+ html = html.replace(/(src|href)="[^"]*\/assets\//g, '$1="/assets/');
15
+ writeFileSync(indexPath, html);
16
+
17
+ // SPA fallback so /about, /non-existent-route etc. serve index.html
18
+ writeFileSync(
19
+ join(distDir, 'serve.json'),
20
+ JSON.stringify({
21
+ rewrites: [{ source: '**', destination: '/index.html' }],
22
+ })
23
+ );
@@ -0,0 +1,5 @@
1
+ <script setup lang="ts"></script>
2
+
3
+ <template>
4
+ <RouterView />
5
+ </template>
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Self-contained CMS Connect REST helpers (URL builder + authenticated GET).
3
+ */
4
+
5
+ import { createDataSDK } from "@salesforce/platform-sdk";
6
+
7
+ // The build injects the org's API version via `__SF_API_VERSION__`; the literal
8
+ // is only a fallback for environments (e.g. unit tests) where it is undefined.
9
+ declare const __SF_API_VERSION__: string;
10
+
11
+ /** Project-standard API version, mirroring `@salesforce/ui-bundle/api`'s clients. */
12
+ const API_VERSION: string = typeof __SF_API_VERSION__ !== "undefined" ? __SF_API_VERSION__ : "65.0";
13
+
14
+ /**
15
+ * The bundle's build-time API version as a number (e.g. `68.0`), i.e. the
16
+ * version the SDK's GraphQL/REST requests are actually issued at. `0` when it
17
+ * cannot be parsed. Distinct from the org's advertised max version.
18
+ */
19
+ export function getBuildApiVersion(): number {
20
+ const v = Number.parseFloat(API_VERSION);
21
+ return Number.isFinite(v) ? v : 0;
22
+ }
23
+
24
+ /**
25
+ * Percent-encodes a single path segment (SSRF / path-injection guard). Mirrors
26
+ * the reference `safeEncodePath`.
27
+ */
28
+ export function safeEncodePath(segment: string): string {
29
+ return encodeURIComponent(segment);
30
+ }
31
+
32
+ /** Builds an absolute Connect REST URL from a version-relative sub-path. */
33
+ export function connectUrl(subPath: string): string {
34
+ const normalized = subPath.startsWith("/") ? subPath : `/${subPath}`;
35
+ return `/services/data/v${API_VERSION}/connect${normalized}`;
36
+ }
37
+
38
+ /**
39
+ * GETs a Connect JSON resource via the platform SDK's authenticated `fetch`
40
+ * and returns the parsed body. Throws on SDK-init failure or a non-OK response
41
+ * (the caller decides how a whole-request failure surfaces).
42
+ */
43
+ export async function connectGetJson<T = unknown>(url: string): Promise<T> {
44
+ const sdk = await createDataSDK();
45
+ if (!sdk?.fetch) {
46
+ throw new Error("Failed to initialize data SDK for CMS REST call");
47
+ }
48
+ const response = await sdk.fetch(url, {
49
+ method: "GET",
50
+ headers: { Accept: "application/json" },
51
+ });
52
+ if (!response.ok) {
53
+ throw new Error(`CMS REST request failed: ${response.status} ${response.statusText}`);
54
+ }
55
+ return (await response.json()) as T;
56
+ }
@@ -0,0 +1,37 @@
1
+ /** Service to gate CMS search on the bundle's build-time API version. */
2
+
3
+ import { getBuildApiVersion } from "./apiUtils";
4
+
5
+ /** Minimum API version that supports CMS search. */
6
+ export const MIN_CMS_API_VERSION = 68;
7
+
8
+ /**
9
+ * Whether CMS search can run — the synchronous core of the gate. Gated on the
10
+ * bundle's build-time API version (`__SF_API_VERSION__`, injected from the
11
+ * resolved org's API version at build time) being >= {@link MIN_CMS_API_VERSION}.
12
+ *
13
+ * The build version is the version every SDK GraphQL/REST request is actually
14
+ * issued at, so it already reflects a concrete, org-supported API version: a
15
+ * bundle built against an org resolves that org's max version, and a bundle
16
+ * built below v68 sends its CMS query to `/services/data/v67.0/graphql` where
17
+ * the v68 CMS backend is unavailable. Gating on the build version alone is
18
+ * therefore both necessary and sufficient — and, unlike a runtime probe of the
19
+ * bare `/services/data/` version-listing endpoint (which is not routed on guest
20
+ * Experience Sites and would fail-close there), it works on every surface.
21
+ *
22
+ * Because it reads only the baked build version, it is safe to call during
23
+ * render — e.g. to hide the CMS source's scope entry and result section when
24
+ * the backend isn't available (see `ScopeSelector` and `SearchResults`).
25
+ */
26
+ export function isCmsSearchSupported(): boolean {
27
+ return getBuildApiVersion() >= MIN_CMS_API_VERSION;
28
+ }
29
+
30
+ /**
31
+ * Resolves whether CMS search can run. Thin async wrapper over
32
+ * {@link isCmsSearchSupported} that preserves the existing fetch-path call-site
33
+ * contract (the fetch effect awaits it alongside the async UIBundle-id lookup).
34
+ */
35
+ export function getOrgSupportsCmsSearch(): Promise<boolean> {
36
+ return Promise.resolve(isCmsSearchSupported());
37
+ }
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Connect REST caller for a CMS channel's searchable content types:
3
+ * GET /connect/cms/channels/{channelId}/searchable-content-types?page=N&pageSize=25
4
+ * Returns the validated content types (FQN + server label) that populate the
5
+ * content-type scope entries.
6
+ */
7
+
8
+ import { connectGetJson, connectUrl, safeEncodePath } from "./apiUtils";
9
+ import {
10
+ isValidCmsFqn,
11
+ formatContentTypeLabel,
12
+ type DiscoveredContentType,
13
+ } from "../contentTypeUtils";
14
+
15
+ /** Hard-coded REST page size. NEVER user input. */
16
+ const REST_PAGE_SIZE = 25;
17
+
18
+ /** Safety cap so a misbehaving backend cannot spin an unbounded loop. */
19
+ const MAX_PAGES = 100;
20
+
21
+ /** Channel id shape guard — `0ap` + 12–15 alphanumerics — validates the discovery channel id before the searchable-content-types REST call. */
22
+ export const CHANNEL_ID_PATTERN = /^0ap[a-zA-Z0-9]{12,15}$/;
23
+
24
+ /** One raw entry from the Connect response. */
25
+ interface RawContentTypeEntry {
26
+ /** FQN with the `sfdc_cms__` prefix (e.g. "sfdc_cms__news"). */
27
+ id?: string;
28
+ /** Server-provided display name (e.g. "News"). */
29
+ label?: string;
30
+ /** Bare developer name without the prefix (e.g. "news"). */
31
+ name?: string;
32
+ // Legacy/alternate FQN field names kept as fallbacks.
33
+ contentType?: string;
34
+ fqn?: string;
35
+ developerName?: string;
36
+ isSearchable?: boolean;
37
+ searchable?: boolean;
38
+ }
39
+
40
+ /** Loose shape of one page of the Connect response. */
41
+ interface RawContentTypesPage {
42
+ items?: RawContentTypeEntry[];
43
+ contentTypes?: RawContentTypeEntry[];
44
+ }
45
+
46
+ /** Reads the entries array from a page payload (array or object form). */
47
+ function pageItems(payload: unknown): RawContentTypeEntry[] {
48
+ if (Array.isArray(payload)) return payload as RawContentTypeEntry[];
49
+ const obj = payload as RawContentTypesPage | null;
50
+ if (obj?.items && Array.isArray(obj.items)) return obj.items;
51
+ if (obj?.contentTypes && Array.isArray(obj.contentTypes)) return obj.contentTypes;
52
+ return [];
53
+ }
54
+
55
+ /** True when the entry is not explicitly marked non-searchable. */
56
+ function isSearchable(entry: RawContentTypeEntry): boolean {
57
+ if (typeof entry.isSearchable === "boolean") return entry.isSearchable;
58
+ if (typeof entry.searchable === "boolean") return entry.searchable;
59
+ // No flag present → do not exclude on searchability (FQN validity still gates).
60
+ return true;
61
+ }
62
+
63
+ /** ManagedContentType key prefix — the `id` custom content types carry instead of an FQN. */
64
+ const CONTENT_TYPE_RECORD_ID_PATTERN = /^0T1[a-zA-Z0-9]{12,15}$/;
65
+
66
+ /** Extracts the prefixed FQN from an entry, rebuilding `c__<name>` when `id` is a custom-type record id. */
67
+ function entryFqn(entry: RawContentTypeEntry): string | undefined {
68
+ // Custom types put a ManagedContentType id in `id`; the FQN the GraphQL filter
69
+ // accepts is `c__<name>`.
70
+ if (entry.id && CONTENT_TYPE_RECORD_ID_PATTERN.test(entry.id)) {
71
+ return entry.name ? `c__${entry.name}` : undefined;
72
+ }
73
+ return entry.id ?? entry.contentType ?? entry.fqn ?? entry.developerName;
74
+ }
75
+
76
+ /**
77
+ * Fetches every searchable content type for `channelId`, paginating the
78
+ * Connect endpoint until a short page is returned, then filtering to searchable
79
+ * entries whose FQN passes `isValidCmsFqn`. Each result carries the FQN and the
80
+ * server-provided display `label` (falling back to a derived label only when
81
+ * the response omits one). De-duplicates by FQN, preserving first-seen order.
82
+ *
83
+ * @throws when `channelId` fails the shape guard (before any network call).
84
+ */
85
+ export async function fetchSearchableContentTypes(
86
+ channelId: string,
87
+ ): Promise<DiscoveredContentType[]> {
88
+ if (!CHANNEL_ID_PATTERN.test(channelId)) {
89
+ throw new Error(`Invalid CMS channel id "${channelId}".`);
90
+ }
91
+
92
+ const encoded = safeEncodePath(channelId);
93
+ const collected: DiscoveredContentType[] = [];
94
+ const seen = new Set<string>();
95
+
96
+ for (let page = 0; page < MAX_PAGES; page++) {
97
+ const url = connectUrl(
98
+ `/cms/channels/${encoded}/searchable-content-types?page=${page}&pageSize=${REST_PAGE_SIZE}`,
99
+ );
100
+ const payload = await connectGetJson(url);
101
+ const items = pageItems(payload);
102
+
103
+ for (const entry of items) {
104
+ const fqn = entryFqn(entry);
105
+ if (!fqn || !isSearchable(entry) || !isValidCmsFqn(fqn) || seen.has(fqn)) {
106
+ continue;
107
+ }
108
+ seen.add(fqn);
109
+ // Prefer the server label; fall back to a label derived from the FQN.
110
+ const label = entry.label?.trim() || formatContentTypeLabel(fqn);
111
+ collected.push({ fqn, label });
112
+ }
113
+
114
+ // Last page reached when fewer than a full page came back.
115
+ if (items.length < REST_PAGE_SIZE) break;
116
+ }
117
+
118
+ return collected;
119
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Extracts the CMS channel id from a search result so it can feed the
3
+ * searchable-content-types Connect endpoint. There is one CMS channel for the
4
+ * app, so any result carries it:
5
+ *
6
+ * nodes[0].managedContentChannelDeliveryDetails[0].managedContentChannelDetails.id
7
+ *
8
+ * Returns `null` when any hop is missing (empty nodes, missing delivery
9
+ * details, missing channel details / id) — the caller simply skips discovery.
10
+ */
11
+
12
+ import type { SourceResult } from "../../types";
13
+ import type { CmsSearchResponse, CmsSearchItem } from "./types";
14
+
15
+ /** Accepts either a normalised `SourceResult` or a raw `CmsSearchResponse`. */
16
+ type ChannelSource = SourceResult | CmsSearchResponse | null | undefined;
17
+
18
+ function getNodes(result: ChannelSource): unknown[] {
19
+ if (!result) return [];
20
+ // SourceResult carries `nodes`; CmsSearchResponse carries `items`.
21
+ if ("nodes" in result && Array.isArray(result.nodes)) return result.nodes;
22
+ if ("items" in result && Array.isArray(result.items)) return result.items;
23
+ return [];
24
+ }
25
+
26
+ /**
27
+ * Returns the channel id derived from the first result's delivery details, or
28
+ * `null` when any level is absent. Validate the returned id's shape before it
29
+ * enters a REST path (the discovery service does this).
30
+ */
31
+ export function resolveChannelId(result: ChannelSource): string | null {
32
+ const nodes = getNodes(result);
33
+ const first = nodes[0] as Partial<CmsSearchItem> | undefined;
34
+ if (!first) return null;
35
+
36
+ const delivery = first.managedContentChannelDeliveryDetails?.[0];
37
+ const id = delivery?.managedContentChannelDetails?.id;
38
+
39
+ return typeof id === "string" && id.length > 0 ? id : null;
40
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Builds the CMS `managed_content { search { searchContentInChannels … } }`
3
+ * fragment as a {@link QueryFragmentContribution} with `placement: "root"` — a
4
+ * sibling of the `uiapi` block in the combined document.
5
+ *
6
+ * The `$cmsContentTypeFQNs` declaration, its `contentTypeFQNs:` argument, and
7
+ * its variable value are all emitted only when a non-empty content-type list is
8
+ * passed; on the bootstrap call (no types known) they are omitted entirely. The
9
+ * `$cmsLanguage` pieces follow the same conditional pattern.
10
+ */
11
+
12
+ import type { QueryFragmentContribution } from "../types";
13
+
14
+ export interface BuildCmsFragmentParams {
15
+ /** Search keyword → `$cmsKeyword`. */
16
+ keyword: string;
17
+ /** UIBundle Id fed into the query → `$UIBundleId` (from `getUIBundleId()`). */
18
+ uiBundleId: string;
19
+ /** Pagination offset → `$cmsOffset` (already clamped `>= 0` by the caller). */
20
+ offset: number;
21
+ /** Page size → `$cmsLimit` (validated against `pageSizeOptions` upstream). */
22
+ limit: number;
23
+ /**
24
+ * Discovered/selected content-type FQNs → the schema's `contentTypeFQNs`
25
+ * argument (via `$cmsContentTypeFQNs`). When absent, null, or empty, those
26
+ * pieces are omitted entirely (bootstrap call). Callers pass ONLY
27
+ * `isValidCmsFqn`-validated values.
28
+ */
29
+ contentTypes?: string[] | null;
30
+ /** User language tag for the `language` argument; emitted only when a non-empty string. */
31
+ language?: string | null;
32
+ }
33
+
34
+ /**
35
+ * Builds the CMS fragment contribution. The content-type filter pieces are
36
+ * included only when `params.contentTypes` is a non-empty array.
37
+ */
38
+ export function buildCmsQueryFragment(params: BuildCmsFragmentParams): QueryFragmentContribution {
39
+ const { keyword, uiBundleId, offset, limit, contentTypes, language } = params;
40
+
41
+ const hasContentTypes = Array.isArray(contentTypes) && contentTypes.length > 0;
42
+ const hasLanguage = typeof language === "string" && language.length > 0;
43
+
44
+ const variableDeclarations = [
45
+ "$cmsKeyword: String!",
46
+ "$UIBundleId: ID!",
47
+ "$cmsOffset: Int!",
48
+ "$cmsLimit: Int!",
49
+ ];
50
+
51
+ const variables: Record<string, unknown> = {
52
+ cmsKeyword: keyword,
53
+ UIBundleId: uiBundleId,
54
+ cmsOffset: offset,
55
+ cmsLimit: limit,
56
+ };
57
+
58
+ // CONDITIONAL: declare + pass + supply the content-type filter only when a
59
+ // non-empty list is provided. Otherwise these three pieces are all absent.
60
+ // The schema argument is `contentTypeFQNs` (a list of content-type FQNs) —
61
+ // NOT `contentTypes`; the latter is rejected as an unknown field argument.
62
+ const contentTypesArg = hasContentTypes ? "\n contentTypeFQNs: $cmsContentTypeFQNs" : "";
63
+ if (hasContentTypes) {
64
+ variableDeclarations.push("$cmsContentTypeFQNs: [String!]");
65
+ variables.cmsContentTypeFQNs = contentTypes;
66
+ }
67
+
68
+ // Top-level `language: String` arg, emitted only when a non-empty tag is provided.
69
+ const languageArg = hasLanguage ? "\n language: $cmsLanguage" : "";
70
+ if (hasLanguage) {
71
+ variableDeclarations.push("$cmsLanguage: String");
72
+ variables.cmsLanguage = language;
73
+ }
74
+
75
+ const fragment = `managed_content {
76
+ search {
77
+ searchContentInChannels(
78
+ searchIdentifiers: { uibundleIds: [$UIBundleId] }
79
+ keyword: $cmsKeyword
80
+ pagination: { limit: $cmsLimit, offset: $cmsOffset }${languageArg}${contentTypesArg}
81
+ ) {
82
+ total
83
+ offset
84
+ pageSize
85
+ items {
86
+ managedContentId
87
+ managedContentKey
88
+ title
89
+ contentType
90
+ language
91
+ highlightedSnippet
92
+ managedContentChannelDeliveryDetails {
93
+ contentUrl
94
+ publishedDate
95
+ managedContentChannelDetails { id name type }
96
+ }
97
+ }
98
+ }
99
+ }
100
+ }`;
101
+
102
+ return {
103
+ variableDeclarations,
104
+ variables,
105
+ placement: "root",
106
+ fragment,
107
+ };
108
+ }