@salesforce/ui-bundle-template-feature-angular-language-switcher 12.5.4

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 (253) hide show
  1. package/LICENSE.txt +82 -0
  2. package/README.md +193 -0
  3. package/dist/.forceignore +15 -0
  4. package/dist/.husky/pre-commit +4 -0
  5. package/dist/.prettierignore +11 -0
  6. package/dist/.prettierrc +17 -0
  7. package/dist/CHANGELOG.md +5238 -0
  8. package/dist/README.md +77 -0
  9. package/dist/config/project-scratch-def.json +13 -0
  10. package/dist/eslint.config.js +7 -0
  11. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/.forceignore +15 -0
  12. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/.graphqlrc.yml +2 -0
  13. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/.postcssrc.json +5 -0
  14. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/.prettierignore +8 -0
  15. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/.prettierrc +12 -0
  16. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/CHANGELOG.md +4 -0
  17. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/README.md +85 -0
  18. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/angular.json +98 -0
  19. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/codegen.yml +95 -0
  20. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/components.json +6 -0
  21. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/e2e/app.spec.ts +15 -0
  22. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/esbuild/api-version.mjs +17 -0
  23. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/esbuild/graphql-codegen.mjs +13 -0
  24. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/eslint-plugin-angular-graphql.js +148 -0
  25. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/eslint.config.js +76 -0
  26. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/feature-angular-language-switcher.uibundle-meta.xml +7 -0
  27. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/middleware/html.mjs +3 -0
  28. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/middleware/proxy.mjs +12 -0
  29. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/package.json +69 -0
  30. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/playwright.config.ts +25 -0
  31. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/public/favicon.ico +0 -0
  32. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/scripts/get-graphql-schema.mjs +69 -0
  33. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/scripts/rewrite-e2e-assets.mjs +30 -0
  34. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/api/data-client.service.spec.ts +184 -0
  35. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/api/data-client.service.ts +74 -0
  36. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/app.config.ts +20 -0
  37. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/app.html +1 -0
  38. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/app.routes.ts +26 -0
  39. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/app.spec.ts +18 -0
  40. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/app.ts +15 -0
  41. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/layout/app-layout/app-layout.html +51 -0
  42. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/layout/app-layout/app-layout.spec.ts +63 -0
  43. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/layout/app-layout/app-layout.ts +27 -0
  44. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/alert/alert.html +27 -0
  45. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/alert/alert.ts +41 -0
  46. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/button/button.html +27 -0
  47. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/button/button.ts +66 -0
  48. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/card/card.ts +100 -0
  49. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/collapsible/collapsible.html +38 -0
  50. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/collapsible/collapsible.ts +21 -0
  51. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/date-picker/date-picker.html +20 -0
  52. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/date-picker/date-picker.ts +49 -0
  53. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/date-range-picker/date-range-picker.html +20 -0
  54. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/date-range-picker/date-range-picker.ts +69 -0
  55. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/dialog/dialog.html +20 -0
  56. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/dialog/dialog.ts +40 -0
  57. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/dropdown-menu/dropdown-menu.html +20 -0
  58. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/dropdown-menu/dropdown-menu.ts +100 -0
  59. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/field/field.html +15 -0
  60. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/field/field.ts +28 -0
  61. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/field-appearance.ts +8 -0
  62. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/field-size.ts +8 -0
  63. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/icon/icon.ts +68 -0
  64. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/input/input.html +34 -0
  65. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/input/input.ts +79 -0
  66. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/label/label.ts +21 -0
  67. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/paginator/paginator.html +74 -0
  68. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/paginator/paginator.ts +93 -0
  69. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/paginator/types.ts +13 -0
  70. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/popover/popover.html +13 -0
  71. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/popover/popover.ts +37 -0
  72. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/select/select.html +36 -0
  73. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/select/select.ts +63 -0
  74. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/separator/separator.ts +31 -0
  75. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/skeleton/skeleton.ts +20 -0
  76. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/spinner/spinner.ts +28 -0
  77. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/features/language-switcher/__examples__/html-middleware-site-example.ts +41 -0
  78. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/features/language-switcher/__examples__/language-switcher-example.ts +41 -0
  79. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/features/language-switcher/build-language-url.ts +158 -0
  80. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/features/language-switcher/index.ts +28 -0
  81. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/features/language-switcher/language-switcher.html +22 -0
  82. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/features/language-switcher/language-switcher.ts +110 -0
  83. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/features/language-switcher/languages.ts +46 -0
  84. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/pages/home/home.html +6 -0
  85. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/pages/home/home.spec.ts +23 -0
  86. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/pages/home/home.ts +13 -0
  87. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/pages/not-found/not-found.html +12 -0
  88. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/pages/not-found/not-found.spec.ts +33 -0
  89. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/pages/not-found/not-found.ts +15 -0
  90. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/alert/hlm-alert-action.ts +14 -0
  91. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/alert/hlm-alert-description.ts +17 -0
  92. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/alert/hlm-alert-title.ts +17 -0
  93. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/alert/hlm-alert.ts +35 -0
  94. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/alert/index.ts +16 -0
  95. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/button/hlm-button.token.ts +22 -0
  96. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/button/hlm-button.ts +72 -0
  97. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/button/index.ts +6 -0
  98. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/calendar/hlm-calendar-multi.ts +195 -0
  99. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/calendar/hlm-calendar-range.ts +194 -0
  100. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/calendar/hlm-calendar.ts +189 -0
  101. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/calendar/hlm-month-year-calendar.ts +119 -0
  102. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/calendar/index.ts +16 -0
  103. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/hlm-card-action.ts +12 -0
  104. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/hlm-card-avatar.ts +14 -0
  105. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/hlm-card-content.ts +12 -0
  106. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/hlm-card-description.ts +12 -0
  107. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/hlm-card-footer.ts +12 -0
  108. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/hlm-card-header.ts +15 -0
  109. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/hlm-card-title.ts +12 -0
  110. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/hlm-card.token.ts +19 -0
  111. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/hlm-card.ts +22 -0
  112. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/index.ts +28 -0
  113. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/collapsible/hlm-collapsible-content.ts +14 -0
  114. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/collapsible/hlm-collapsible-trigger.ts +9 -0
  115. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/collapsible/hlm-collapsible.ts +15 -0
  116. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/collapsible/index.ts +13 -0
  117. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-multi-input.ts +104 -0
  118. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-picker-anchor.ts +23 -0
  119. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-picker-input.ts +118 -0
  120. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-picker-multi.token.ts +109 -0
  121. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-picker-multi.ts +220 -0
  122. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-picker-trigger.ts +104 -0
  123. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-picker.token.ts +69 -0
  124. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-picker.ts +203 -0
  125. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-range-input.ts +120 -0
  126. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-range-picker.token.ts +86 -0
  127. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-range-picker.ts +252 -0
  128. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-month-year-input.ts +105 -0
  129. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-month-year-picker.token.ts +89 -0
  130. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-month-year-picker.ts +196 -0
  131. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/index.ts +38 -0
  132. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog-close.ts +9 -0
  133. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog-content.ts +74 -0
  134. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog-description.ts +17 -0
  135. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog-footer.ts +15 -0
  136. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog-header.ts +12 -0
  137. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog-overlay.ts +27 -0
  138. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog-portal.ts +8 -0
  139. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog-title.ts +14 -0
  140. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog-trigger.ts +14 -0
  141. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog.service.ts +46 -0
  142. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog.ts +24 -0
  143. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/index.ts +35 -0
  144. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-checkbox-indicator.ts +21 -0
  145. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-checkbox.ts +54 -0
  146. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-focus-on-hover.ts +35 -0
  147. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-group.ts +14 -0
  148. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-item-sub-indicator.ts +19 -0
  149. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-item.ts +42 -0
  150. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-label.ts +20 -0
  151. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-radio-indicator.ts +21 -0
  152. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-radio.ts +49 -0
  153. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-separator.ts +12 -0
  154. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-shortcut.ts +15 -0
  155. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-sub-trigger.ts +52 -0
  156. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-sub.ts +53 -0
  157. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-token.ts +26 -0
  158. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-trigger.ts +46 -0
  159. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu.ts +57 -0
  160. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/index.ts +49 -0
  161. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field-content.ts +12 -0
  162. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field-description.ts +53 -0
  163. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field-error.ts +100 -0
  164. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field-group.ts +15 -0
  165. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field-label.ts +17 -0
  166. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field-legend.ts +19 -0
  167. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field-separator.ts +24 -0
  168. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field-set.ts +15 -0
  169. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field-title.ts +15 -0
  170. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field.ts +47 -0
  171. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/index.ts +34 -0
  172. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/input/hlm-input.ts +21 -0
  173. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/input/index.ts +5 -0
  174. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/input-group/hlm-input-group-addon.ts +40 -0
  175. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/input-group/hlm-input-group-button.ts +47 -0
  176. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/input-group/hlm-input-group-input.ts +17 -0
  177. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/input-group/hlm-input-group-text.ts +14 -0
  178. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/input-group/hlm-input-group.ts +18 -0
  179. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/input-group/index.ts +19 -0
  180. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/label/hlm-label.ts +17 -0
  181. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/label/index.ts +5 -0
  182. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/label/label.html +11 -0
  183. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/hlm-numbered-pagination-query-params.ts +191 -0
  184. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/hlm-numbered-pagination.ts +290 -0
  185. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/hlm-pagination-content.ts +12 -0
  186. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/hlm-pagination-ellipsis.ts +27 -0
  187. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/hlm-pagination-item.ts +7 -0
  188. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/hlm-pagination-link.ts +51 -0
  189. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/hlm-pagination-next.ts +65 -0
  190. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/hlm-pagination-previous.ts +65 -0
  191. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/hlm-pagination.ts +19 -0
  192. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/index.ts +31 -0
  193. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/popover/hlm-popover-content.ts +25 -0
  194. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/popover/hlm-popover-description.ts +12 -0
  195. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/popover/hlm-popover-header.ts +12 -0
  196. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/popover/hlm-popover-portal.ts +8 -0
  197. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/popover/hlm-popover-title.ts +12 -0
  198. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/popover/hlm-popover-trigger.ts +14 -0
  199. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/popover/hlm-popover.ts +23 -0
  200. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/popover/index.ts +25 -0
  201. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-content.ts +44 -0
  202. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-group.ts +14 -0
  203. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-item.ts +36 -0
  204. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-label.ts +14 -0
  205. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-multiple.ts +37 -0
  206. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-placeholder.ts +17 -0
  207. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-portal.ts +8 -0
  208. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-scroll-down.ts +22 -0
  209. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-scroll-up.ts +22 -0
  210. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-separator.ts +14 -0
  211. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-trigger.ts +59 -0
  212. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-value-template.ts +5 -0
  213. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-value.ts +18 -0
  214. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-values-content.ts +9 -0
  215. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-values.ts +5 -0
  216. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select.ts +37 -0
  217. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/index.ts +52 -0
  218. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/separator/hlm-separator.ts +19 -0
  219. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/separator/index.ts +5 -0
  220. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/skeleton/hlm-skeleton.ts +14 -0
  221. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/skeleton/index.ts +5 -0
  222. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/spinner/hlm-spinner.ts +26 -0
  223. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/spinner/index.ts +5 -0
  224. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/utils/hlm.ts +313 -0
  225. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/utils/index.ts +2 -0
  226. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/utils/provide-spartan-hlm.ts +34 -0
  227. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/utils/async-data.ts +99 -0
  228. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/index.html +13 -0
  229. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/main.ts +5 -0
  230. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/styles.css +139 -0
  231. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/types/sf-globals.d.ts +23 -0
  232. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/tsconfig.app.json +11 -0
  233. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/tsconfig.json +36 -0
  234. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/tsconfig.spec.json +10 -0
  235. package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/ui-bundle.json +7 -0
  236. package/dist/jest.config.js +6 -0
  237. package/dist/package-lock.json +12554 -0
  238. package/dist/package.json +46 -0
  239. package/dist/scripts/apex/hello.apex +10 -0
  240. package/dist/scripts/gitignore-templates.json +4 -0
  241. package/dist/scripts/graphql-search.sh +191 -0
  242. package/dist/scripts/org-setup-config-schema.mjs +132 -0
  243. package/dist/scripts/org-setup-dev.mjs +114 -0
  244. package/dist/scripts/org-setup-url.mjs +108 -0
  245. package/dist/scripts/org-setup-utils.mjs +569 -0
  246. package/dist/scripts/org-setup-xml.mjs +278 -0
  247. package/dist/scripts/org-setup.config.json +5 -0
  248. package/dist/scripts/org-setup.mjs +2537 -0
  249. package/dist/scripts/sf-project-setup.mjs +103 -0
  250. package/dist/scripts/soql/account.soql +6 -0
  251. package/dist/scripts/validate-org-setup-config.mjs +38 -0
  252. package/dist/sfdx-project.json +12 -0
  253. package/package.json +46 -0
@@ -0,0 +1,569 @@
1
+ /**
2
+ * Shared helpers for the org-setup scripts.
3
+ *
4
+ * Factored out of org-setup.mjs so BOTH `org-setup.mjs` and `org-setup-dev.mjs` import the
5
+ * same primitives instead of duplicating them:
6
+ * - process runners (`run`, `runAsync`, `spawnAsync`)
7
+ * - SFDX path + UI-bundle discovery (`resolveSfdxSource`, `discoverAllUIBundleDirs`,
8
+ * `discoverUIBundleDir` — with the multi-bundle acknowledgment)
9
+ * - the interactive `--target-org` fallback (`resolveTargetOrg`) and its
10
+ * single-select picker (`promptSelect`)
11
+ * - pure, unit-testable logic for the license gate, SOQL-name validation, org-list
12
+ * parsing, and the non-TTY multi-bundle warning
13
+ *
14
+ * IMPORTANT: this module is side-effect-free at import time — no top-level
15
+ * readFileSync / process.exit / CLI run — so it can be imported by tests and by
16
+ * org-setup-dev.mjs without triggering org-setup's main(). All I/O happens inside the
17
+ * exported functions, only when they are called. Errors are plain `Error`s (not
18
+ * org-setup's StepError) to avoid an import cycle; runStep handles both alike.
19
+ */
20
+
21
+ import { spawnSync } from 'node:child_process';
22
+ import { readdirSync, existsSync, readFileSync } from 'node:fs';
23
+ import { resolve } from 'node:path';
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Pure logic (no I/O) — unit-testable in isolation.
27
+ // ---------------------------------------------------------------------------
28
+
29
+ /**
30
+ * Validate that a config-authored identifier is safe to interpolate into a SOQL
31
+ * `WHERE <field> = '<value>'` clause (validate-and-fail with a strict whitelist,
32
+ * NOT an escaper). These values (role name, site name,
33
+ * self-reg profile name) are developer-authored identifiers the app ships, not
34
+ * end-user data, and legitimate identifiers are drawn from letters, digits,
35
+ * spaces, hyphens, and underscores. Anything outside that set — a `'`, `\`, `=`,
36
+ * `"`, `&`, `<`, `>`, punctuation, or a control char — is treated as a config
37
+ * mistake to surface loudly rather than a value to silently escape. The
38
+ * whitelist is deliberately broader than the injection characters alone so a
39
+ * single positive rule covers every SOQL/quoting hazard at once.
40
+ *
41
+ * NOTE: this is for config *identifiers*. Free-form display values that
42
+ * legitimately contain punctuation (e.g. an Account name like `O'Brien Rentals`)
43
+ * are escaped at their call site instead — see org-setup.mjs's Account path.
44
+ *
45
+ * @param {*} value the value to validate (coerced to string)
46
+ * @param {string} fieldLabel how to name the field in an error (e.g. "roleName")
47
+ * @param {object} [opts]
48
+ * @param {string} [opts.remediation] where the author fixes it, e.g.
49
+ * "org-setup.config.json" or "the networks/<siteName>.network-meta.xml filename".
50
+ * Defaults to org-setup.config.json.
51
+ * @returns {string} the value unchanged when valid; throws otherwise.
52
+ */
53
+ export function validateSoqlName(value, fieldLabel, opts = {}) {
54
+ const { remediation = 'org-setup.config.json' } = opts;
55
+ const s = String(value);
56
+ if (!/^[A-Za-z0-9 _-]+$/.test(s)) {
57
+ throw new Error(
58
+ `${fieldLabel} "${value}" contains an unsupported character ` +
59
+ `(only letters, digits, spaces, hyphens, and underscores are allowed). ` +
60
+ `Fix it in ${remediation}.`,
61
+ );
62
+ }
63
+ return s;
64
+ }
65
+
66
+ /**
67
+ * Back-compat wrapper for the self-reg profile name (the original call site).
68
+ * Delegates to the strict whitelist above; kept as a named export so
69
+ * existing importers and tests need no change beyond the tightened rule.
70
+ */
71
+ export function validateProfileNameForSoql(name) {
72
+ return validateSoqlName(name, 'selfRegProfile name');
73
+ }
74
+
75
+ /**
76
+ * Escape a free-form value for safe interpolation inside a single-quoted SOQL
77
+ * string literal. Unlike validateSoqlName, this is for display
78
+ * values that legitimately contain punctuation — e.g. an Account name like
79
+ * `O'Brien Rentals & Co.` — where rejecting the value would be wrong. Escapes
80
+ * the backslash FIRST (so an already-present `\` isn't double-counted) then the
81
+ * quote, matching SOQL's `\'` / `\\` escape rules. Returns the escaped inner
82
+ * string (no surrounding quotes — the caller supplies those).
83
+ */
84
+ export function escapeSoqlString(value) {
85
+ return String(value).replace(/\\/g, '\\\\').replace(/'/g, "\\'");
86
+ }
87
+
88
+ /**
89
+ * Validate a Salesforce API name (an SObject type or a field API name) that is
90
+ * interpolated as a bare identifier into generated Apex / dynamic SOQL — e.g.
91
+ * `new ${sobject}()`, `r.put('${field}', …)`, or `Database.query('SELECT Id FROM
92
+ * ${sobject} …')`. Unlike a WHERE value, an identifier can't be
93
+ * quoted-and-escaped, so the only safe treatment is a strict whitelist. Valid API
94
+ * names are letters, digits, and underscores only (the `__c` / `__r` custom and
95
+ * namespace suffixes are already covered by that set); anything else — a quote,
96
+ * space, dot, or `)` — would let a crafted seed-data field name or sobject break
97
+ * out of the surrounding statement, so it is rejected loudly. These identifiers
98
+ * come from the app-shipped data plan / seed JSON, so a violation is a fixture
99
+ * bug, not user input; this keeps the identifier hardening symmetric with the
100
+ * value escaping (apexLiteral / escapeSoqlString) already applied around them.
101
+ *
102
+ * @param {*} value the identifier to validate (coerced to string)
103
+ * @param {string} fieldLabel how to name it in an error (e.g. "sobject type")
104
+ * @returns {string} the value unchanged when valid; throws otherwise.
105
+ */
106
+ export function validateApiName(value, fieldLabel) {
107
+ const s = String(value);
108
+ if (!/^[A-Za-z0-9_]+$/.test(s)) {
109
+ throw new Error(
110
+ `${fieldLabel} "${value}" is not a valid Salesforce API name ` +
111
+ `(only letters, digits, and underscores are allowed). ` +
112
+ `Check the seed data plan (data-plan.json) and record field names.`,
113
+ );
114
+ }
115
+ return s;
116
+ }
117
+
118
+ /**
119
+ * Parse the single `SETUP_RESULT_JSON:` marker line that buildApexInsert emits
120
+ * from anonymous Apex. Replaces the old per-record REF:/ERR: debug
121
+ * scraping. The marker carries a JSON array of `{ ref, id? , err? }` — one entry
122
+ * per inserted record.
123
+ *
124
+ * Robustness is the whole point: a debug log can be truncated, so a missing
125
+ * marker or a payload that doesn't parse as the expected array is reported as a
126
+ * hard failure via `ok: false` rather than silently reading as "0 records, 0
127
+ * errors" (which would let a partial import look successful).
128
+ *
129
+ * @param {string} apexOut raw stdout from `sf apex run`
130
+ * @returns {{ ok: boolean, error: string|null,
131
+ * successes: Array<{ref:string, id:string}>,
132
+ * errors: Array<{ref:string, message:string}> }}
133
+ */
134
+ export function parseApexInsertResults(apexOut) {
135
+ const text = String(apexOut ?? '');
136
+
137
+ // Strategy: the actual System.debug output appears somewhere in the log as a JSON array.
138
+ // First, try to find it via the USER_DEBUG line format (most reliable).
139
+ // The line looks like: `...|USER_DEBUG|[line]|ERROR|SETUP_RESULT_JSON:[{"ref":"a","id":"001..."}]`.
140
+ // The array can be empty ([]) for zero records, so use .*? instead of .+? to allow empty arrays.
141
+ // Accept any log level (DEBUG, ERROR, etc) to handle both old and new logging configurations.
142
+ const userDebugPattern = /\|USER_DEBUG\|.*?\|(DEBUG|ERROR|INFO|WARN)\|SETUP_RESULT_JSON:(\[.*?\])(?:\n|$)/s;
143
+ let match = text.match(userDebugPattern);
144
+
145
+ if (match) {
146
+ return parseJsonPayload(match[2].trim());
147
+ }
148
+
149
+ // Fallback: look for the marker followed by a JSON array, but skip "Execute Anonymous:" lines
150
+ const markerIdx = text.indexOf('SETUP_RESULT_JSON:');
151
+ if (markerIdx === -1) {
152
+ return {
153
+ ok: false,
154
+ error: 'no SETUP_RESULT_JSON marker found in Apex output (log may be truncated or the insert did not run)',
155
+ successes: [],
156
+ errors: [],
157
+ };
158
+ }
159
+
160
+ // Check if this is the source code line, not the debug output
161
+ const lineStart = text.lastIndexOf('\n', markerIdx) + 1;
162
+ const line = text.slice(lineStart, Math.min(text.length, lineStart + 200));
163
+ if (line.includes('Execute Anonymous:')) {
164
+ return {
165
+ ok: false,
166
+ error: 'found SETUP_RESULT_JSON in source code but not in debug output (debug output may be missing or truncated)',
167
+ successes: [],
168
+ errors: [],
169
+ };
170
+ }
171
+
172
+ // Extract what follows the marker up to end of line. Pass it to parseJsonPayload
173
+ // which will validate it's proper JSON and an array, giving specific error messages.
174
+ const afterMarker = text.slice(markerIdx + 'SETUP_RESULT_JSON:'.length);
175
+ const payload = afterMarker.split('\n')[0].trim();
176
+
177
+ return parseJsonPayload(payload);
178
+ }
179
+
180
+ function parseJsonPayload(payload) {
181
+ let parsed;
182
+ try {
183
+ parsed = JSON.parse(payload);
184
+ } catch {
185
+ return {
186
+ ok: false,
187
+ error: 'SETUP_RESULT_JSON payload was not valid JSON (log line may be truncated)',
188
+ successes: [],
189
+ errors: [],
190
+ };
191
+ }
192
+ if (!Array.isArray(parsed)) {
193
+ return {
194
+ ok: false,
195
+ error: 'SETUP_RESULT_JSON payload was not a JSON array',
196
+ successes: [],
197
+ errors: [],
198
+ };
199
+ }
200
+
201
+ const successes = [];
202
+ const errors = [];
203
+ for (const entry of parsed) {
204
+ if (!entry || typeof entry !== 'object') continue;
205
+ const ref = entry.ref != null ? String(entry.ref) : '';
206
+ if (entry.err != null) {
207
+ errors.push({ ref, message: String(entry.err) });
208
+ } else if (entry.id != null) {
209
+ successes.push({ ref, id: String(entry.id) });
210
+ }
211
+ }
212
+ return { ok: true, error: null, successes, errors };
213
+ }
214
+
215
+ /**
216
+ * Group serialized-record sizes into batches whose combined Apex character count
217
+ * (fixed per-batch overhead + each record's rendered size) stays under
218
+ * `charLimit`, subject to `maxBatch` records per batch. Replaces the
219
+ * old `estCharsPerRec = 40 + fields*55` heuristic with a measurement the caller
220
+ * supplies via `recordSizes`.
221
+ *
222
+ * Guarantees at least one record per batch even when a single record's size (plus
223
+ * overhead) already exceeds `charLimit` — otherwise a large record would loop
224
+ * forever or emit an empty batch. That oversized record is placed alone in its
225
+ * own batch and left for Apex to accept or reject.
226
+ *
227
+ * @param {number[]} recordSizes rendered Apex char length of each record, in order
228
+ * @param {object} opts
229
+ * @param {number} opts.charLimit max total chars per batch (record chars + overhead)
230
+ * @param {number} opts.maxBatch max records per batch
231
+ * @param {number} [opts.overhead] fixed per-batch char cost (DML boilerplate); default 0
232
+ * @returns {Array<{start:number, count:number}>} batch slices over the input order
233
+ */
234
+ export function planApexBatches(recordSizes, { charLimit, maxBatch, overhead = 0 }) {
235
+ const batches = [];
236
+ let start = 0;
237
+ const n = recordSizes.length;
238
+ while (start < n) {
239
+ let count = 0;
240
+ let total = overhead;
241
+ while (start + count < n && count < maxBatch) {
242
+ const next = recordSizes[start + count];
243
+ // Always take at least one record, even if it alone busts the limit.
244
+ if (count > 0 && total + next > charLimit) break;
245
+ total += next;
246
+ count += 1;
247
+ }
248
+ batches.push({ start, count });
249
+ start += count;
250
+ }
251
+ return batches;
252
+ }
253
+
254
+ /**
255
+ * Decide whether the self-reg profile's UserLicense is satisfied, from the rows
256
+ * a `SELECT UserLicense.LicenseDefinitionKey, UserLicense.Name, UserLicense.Status,
257
+ * UserLicense.TotalLicenses, UserLicense.UsedLicenses FROM Profile WHERE Name = '…'`
258
+ * query returns. The seat math MUST be done here in JS — SOQL cannot
259
+ * compare two fields. Matches/report on the stable
260
+ * `LicenseDefinitionKey`, never the display Name.
261
+ *
262
+ * satisfied ⇔ Status === 'Active' && (total === -1 [unlimited sentinel] || total - used > 0).
263
+ * 0 rows / null UserLicense ⇒ not satisfied, with a reason naming the profile.
264
+ *
265
+ * @returns {{ satisfied: boolean, reason: string|null, license: {key,label,status,total,used}|null }}
266
+ */
267
+ export function evaluateLicenseRows(rows, profileName) {
268
+ const lic = Array.isArray(rows) ? rows[0]?.UserLicense : null;
269
+ if (!lic) {
270
+ return {
271
+ satisfied: false,
272
+ reason: `no UserLicense found for profile "${profileName}" — the profile is missing in the org or its name is misspelled`,
273
+ license: null,
274
+ };
275
+ }
276
+ const key = lic.LicenseDefinitionKey ?? null;
277
+ const label = lic.Name ?? key ?? 'unknown license';
278
+ const total = Number(lic.TotalLicenses);
279
+ const used = Number(lic.UsedLicenses);
280
+ const license = { key, label, status: lic.Status, total, used };
281
+
282
+ if (lic.Status !== 'Active') {
283
+ return { satisfied: false, reason: `license ${label} (${key}) is not Active (Status=${lic.Status})`, license };
284
+ }
285
+ // Seat counts should always be integers in practice, but guard against a
286
+ // missing/non-numeric field so the skip reason stays legible (not "NaN/NaN").
287
+ if (!Number.isFinite(total) || !Number.isFinite(used)) {
288
+ return { satisfied: false, reason: `could not read seat counts for license ${label} (${key})`, license };
289
+ }
290
+ // -1 is the documented "unlimited" sentinel (defensive — unverified live).
291
+ if (total === -1 || total - used > 0) {
292
+ return { satisfied: true, reason: null, license };
293
+ }
294
+ return {
295
+ satisfied: false,
296
+ reason: `license ${label} (${key}) has no available seats (${used}/${total} used)`,
297
+ license,
298
+ };
299
+ }
300
+
301
+ /**
302
+ * Parse `sf org list --json` defensively into a flat org list + the default org
303
+ * (mirrors isOrgConnected's try/catch tolerance). Collects
304
+ * every array under `result` (nonScratchOrgs, scratchOrgs, sandboxes, devHubs, …)
305
+ * so the picker sees all authenticated orgs; the default is the entry marked
306
+ * `isDefaultUsername`. Malformed / unexpected JSON ⇒ empty result (caller then
307
+ * falls back to the clear error).
308
+ *
309
+ * @returns {{ orgs: Array<{alias:string|null, username:string, isDefault:boolean}>, defaultOrg: string|null }}
310
+ */
311
+ export function parseOrgList(jsonText) {
312
+ let parsed;
313
+ try {
314
+ parsed = JSON.parse(jsonText);
315
+ } catch {
316
+ return { orgs: [], defaultOrg: null };
317
+ }
318
+ const result = parsed?.result;
319
+ if (!result || typeof result !== 'object') return { orgs: [], defaultOrg: null };
320
+
321
+ const orgs = [];
322
+ const seen = new Set();
323
+ for (const val of Object.values(result)) {
324
+ if (!Array.isArray(val)) continue;
325
+ for (const entry of val) {
326
+ if (!entry || typeof entry !== 'object' || !entry.username) continue;
327
+ if (seen.has(entry.username)) continue;
328
+ seen.add(entry.username);
329
+ orgs.push({
330
+ alias: entry.alias ?? null,
331
+ username: entry.username,
332
+ isDefault: entry.isDefaultUsername === true,
333
+ });
334
+ }
335
+ }
336
+ const def = orgs.find((o) => o.isDefault);
337
+ return { orgs, defaultOrg: def ? (def.alias ?? def.username) : null };
338
+ }
339
+
340
+ /**
341
+ * Multi-line warning for the non-TTY multi-bundle case: there is nothing to
342
+ * interactively acknowledge, so keep the deterministic
343
+ * `all[0]` pick but never do it silently — name every bundle, the chosen one, and
344
+ * the `--ui-bundle-name` remedy.
345
+ */
346
+ export function buildMultiBundleWarning(allBundleNames, chosen) {
347
+ return [
348
+ `⚠ Multiple UI bundles found under uiBundles/ (${allBundleNames.length}):`,
349
+ ...allBundleNames.map((n) => ` - ${n}${n === chosen ? ' ← using this one' : ''}`),
350
+ ` Using "${chosen}" (first alphabetically). To deploy a different bundle, re-run with:`,
351
+ ` --ui-bundle-name <name>`,
352
+ ].join('\n');
353
+ }
354
+
355
+ // ---------------------------------------------------------------------------
356
+ // Process runner (moved from org-setup.mjs; plain Error to avoid an import cycle).
357
+ // Only the synchronous `run` is shared — org-setup-dev.mjs runs its steps serially. The
358
+ // async spawn variants stay local to org-setup.mjs where the parallel permset
359
+ // path uses them, so this module exposes no caller-less surface.
360
+ // ---------------------------------------------------------------------------
361
+
362
+ export function run(name, cmd, args, opts = {}) {
363
+ const { cwd = process.cwd(), optional = false } = opts;
364
+ console.log('\n---', name, '---');
365
+ const result = spawnSync(cmd, args, {
366
+ cwd,
367
+ stdio: 'inherit',
368
+ shell: true,
369
+ ...(opts.env && { env: opts.env }),
370
+ ...(opts.timeout && { timeout: opts.timeout }),
371
+ });
372
+ if (result.status !== 0 && !optional) {
373
+ throw new Error(`${name} (exit ${result.status ?? 1})`);
374
+ }
375
+ return result;
376
+ }
377
+
378
+ // ---------------------------------------------------------------------------
379
+ // SFDX paths + UI-bundle discovery.
380
+ // ---------------------------------------------------------------------------
381
+
382
+ /**
383
+ * Resolve the first package directory's source root (main/default) from
384
+ * sfdx-project.json under `root`. Exits non-zero with a clear message when the
385
+ * project file or packageDirectories path is missing. Called explicitly by each
386
+ * entry script (not at import) so this module stays side-effect-free.
387
+ */
388
+ export function resolveSfdxSource(root) {
389
+ const sfdxPath = resolve(root, 'sfdx-project.json');
390
+ if (!existsSync(sfdxPath)) {
391
+ console.error('Error: sfdx-project.json not found at project root.');
392
+ process.exit(1);
393
+ }
394
+ const sfdxProject = JSON.parse(readFileSync(sfdxPath, 'utf8'));
395
+ const pkgDir = sfdxProject?.packageDirectories?.[0]?.path;
396
+ if (!pkgDir) {
397
+ console.error('Error: No packageDirectories[].path found in sfdx-project.json.');
398
+ process.exit(1);
399
+ }
400
+ return resolve(root, pkgDir, 'main', 'default');
401
+ }
402
+
403
+ /** Basename of a bundle dir path, cross-platform. */
404
+ function bundleName(dir) {
405
+ return dir.split(/[/\\]/).pop();
406
+ }
407
+
408
+ /**
409
+ * All UI bundle directories under `uiBundlesDir` (alphabetical, hidden dirs
410
+ * excluded). When `uiBundleName` is given, returns just that one (erroring if it
411
+ * doesn't exist). Exits non-zero when the uiBundles dir or a named bundle is
412
+ * missing.
413
+ */
414
+ export function discoverAllUIBundleDirs(uiBundlesDir, uiBundleName) {
415
+ if (!existsSync(uiBundlesDir)) {
416
+ console.error(`Error: uiBundles directory not found: ${uiBundlesDir}`);
417
+ process.exit(1);
418
+ }
419
+ const entries = readdirSync(uiBundlesDir, { withFileTypes: true });
420
+ const dirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith('.'));
421
+ if (dirs.length === 0) {
422
+ console.error(`Error: No UI bundle folder found under ${uiBundlesDir}`);
423
+ process.exit(1);
424
+ }
425
+ if (uiBundleName) {
426
+ const requested = dirs.find((d) => d.name === uiBundleName);
427
+ if (!requested) {
428
+ console.error(`Error: UI bundle directory not found: ${uiBundleName}`);
429
+ process.exit(1);
430
+ }
431
+ return [resolve(uiBundlesDir, requested.name)];
432
+ }
433
+ return dirs.map((d) => resolve(uiBundlesDir, d.name));
434
+ }
435
+
436
+ /**
437
+ * Resolve the single UI bundle to operate on, with the multi-bundle
438
+ * acknowledgment. When >1 bundle exists and no `--ui-bundle-name` was given:
439
+ * - TTY: present a single-select picker (all bundles, alphabetical all[0]
440
+ * preselected) — enter accepts all[0], arrows choose another.
441
+ * - non-TTY: keep the deterministic all[0] pick but emit the multi-line warning
442
+ * — never a silent pick.
443
+ * Explicit `--ui-bundle-name` short-circuits both (no prompt, no warning).
444
+ */
445
+ export async function discoverUIBundleDir(uiBundlesDir, uiBundleName) {
446
+ const all = discoverAllUIBundleDirs(uiBundlesDir, uiBundleName);
447
+ if (all.length > 1 && !uiBundleName) {
448
+ const names = all.map(bundleName);
449
+ if (process.stdin.isTTY) {
450
+ const idx = await promptSelect(names, {
451
+ prompt: 'Multiple UI bundles found — select which one to deploy:',
452
+ preselectedIndex: 0,
453
+ });
454
+ return all[idx];
455
+ }
456
+ console.warn(buildMultiBundleWarning(names, names[0]));
457
+ }
458
+ return all[0];
459
+ }
460
+
461
+ // ---------------------------------------------------------------------------
462
+ // Interactive single-select picker + --target-org fallback.
463
+ // ---------------------------------------------------------------------------
464
+
465
+ /**
466
+ * Interactive single-select: arrow keys navigate, enter confirms. Returns the
467
+ * chosen index. Falls through to `preselectedIndex` immediately when stdin is not
468
+ * a TTY. Built on the same raw-mode scaffolding as promptSteps (org-setup.mjs)
469
+ * but selects exactly one item.
470
+ */
471
+ export function promptSelect(options, { prompt = 'Select an option:', preselectedIndex = 0 } = {}) {
472
+ if (!process.stdin.isTTY || options.length === 0) return Promise.resolve(preselectedIndex);
473
+
474
+ let cursor = Math.min(Math.max(0, preselectedIndex), options.length - 1);
475
+ const RST = '\x1B[0m';
476
+ const CYAN = '\x1B[36m';
477
+ const GREEN = '\x1B[32m';
478
+
479
+ function render() {
480
+ return options.map((label, row) => {
481
+ const ptr = row === cursor ? `${CYAN}❯${RST}` : ' ';
482
+ const chk = row === cursor ? `${GREEN}●${RST}` : '○';
483
+ return `${ptr} ${chk} ${label}`;
484
+ });
485
+ }
486
+
487
+ return new Promise((resolvePromise) => {
488
+ process.stdin.setRawMode(true);
489
+ process.stdin.resume();
490
+ process.stdin.setEncoding('utf8');
491
+ process.stdout.write('\x1B[?25l');
492
+ console.log(`\n${prompt} (↑↓ move, enter confirm):\n`);
493
+ let prevRows = options.length;
494
+ process.stdout.write(render().join('\n') + '\n');
495
+
496
+ function redraw() {
497
+ process.stdout.write(`\x1B[${prevRows}A`);
498
+ const lines = render();
499
+ for (const line of lines) process.stdout.write(`\x1B[2K${line}\n`);
500
+ prevRows = lines.length;
501
+ }
502
+
503
+ process.stdin.on('data', (key) => {
504
+ if (key === '\x03') {
505
+ process.stdout.write('\x1B[?25h\n');
506
+ process.exit(0);
507
+ }
508
+ if (key === '\r' || key === '\n') {
509
+ process.stdout.write('\x1B[?25h');
510
+ process.stdin.setRawMode(false);
511
+ process.stdin.pause();
512
+ process.stdin.removeAllListeners('data');
513
+ console.log();
514
+ resolvePromise(cursor);
515
+ return;
516
+ }
517
+ if (key === '\x1B[A' || key === 'k') {
518
+ cursor = Math.max(0, cursor - 1);
519
+ redraw();
520
+ } else if (key === '\x1B[B' || key === 'j') {
521
+ cursor = Math.min(options.length - 1, cursor + 1);
522
+ redraw();
523
+ }
524
+ });
525
+ });
526
+ }
527
+
528
+ /**
529
+ * Resolve the target org. When `--target-org` is supplied it is returned
530
+ * verbatim with NO `sf org list` call (byte-for-byte the scripted path).
531
+ * Otherwise discover authenticated orgs via `sf org list --json` and:
532
+ * - TTY: present a picker with the default org at the top and preselected;
533
+ * enter selects the default.
534
+ * - non-TTY: use the default org if resolvable, else exit 1 with a clear
535
+ * message.
536
+ */
537
+ export async function resolveTargetOrg(parsed) {
538
+ if (parsed.targetOrg) return parsed.targetOrg;
539
+
540
+ const listResult = spawnSync('sf', ['org', 'list', '--json'], {
541
+ encoding: 'utf8',
542
+ shell: true,
543
+ });
544
+ const { orgs, defaultOrg } = parseOrgList(listResult.status === 0 ? listResult.stdout : '');
545
+
546
+ if (process.stdin.isTTY && orgs.length > 0) {
547
+ // Default org first + preselected; the rest keep sf's order.
548
+ const ordered = [...orgs].sort((a, b) => (b.isDefault ? 1 : 0) - (a.isDefault ? 1 : 0));
549
+ const labels = ordered.map((o) => {
550
+ const name = o.alias ?? o.username;
551
+ const suffix = o.alias && o.alias !== o.username ? ` (${o.username})` : '';
552
+ return `${name}${suffix}${o.isDefault ? ' [default]' : ''}`;
553
+ });
554
+ const idx = await promptSelect(labels, { prompt: 'Select the target org:', preselectedIndex: 0 });
555
+ const chosen = ordered[idx];
556
+ return chosen.alias ?? chosen.username;
557
+ }
558
+
559
+ if (defaultOrg) {
560
+ console.log(`No --target-org given; using the default org: ${defaultOrg}`);
561
+ return defaultOrg;
562
+ }
563
+
564
+ console.error(
565
+ 'Error: --target-org <alias> is required. No default org is set and this is not an ' +
566
+ 'interactive terminal. Set a default org (sf config set target-org=<alias>) or pass --target-org.',
567
+ );
568
+ process.exit(1);
569
+ }