@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.
- package/LICENSE.txt +82 -0
- package/README.md +193 -0
- package/dist/.forceignore +15 -0
- package/dist/.husky/pre-commit +4 -0
- package/dist/.prettierignore +11 -0
- package/dist/.prettierrc +17 -0
- package/dist/CHANGELOG.md +5238 -0
- package/dist/README.md +77 -0
- package/dist/config/project-scratch-def.json +13 -0
- package/dist/eslint.config.js +7 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/.forceignore +15 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/.graphqlrc.yml +2 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/.postcssrc.json +5 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/.prettierignore +8 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/.prettierrc +12 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/CHANGELOG.md +4 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/README.md +85 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/angular.json +98 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/codegen.yml +95 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/components.json +6 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/e2e/app.spec.ts +15 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/esbuild/api-version.mjs +17 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/esbuild/graphql-codegen.mjs +13 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/eslint-plugin-angular-graphql.js +148 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/eslint.config.js +76 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/feature-angular-language-switcher.uibundle-meta.xml +7 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/middleware/html.mjs +3 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/middleware/proxy.mjs +12 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/package.json +69 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/playwright.config.ts +25 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/public/favicon.ico +0 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/scripts/get-graphql-schema.mjs +69 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/scripts/rewrite-e2e-assets.mjs +30 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/api/data-client.service.spec.ts +184 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/api/data-client.service.ts +74 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/app.config.ts +20 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/app.html +1 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/app.routes.ts +26 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/app.spec.ts +18 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/app.ts +15 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/layout/app-layout/app-layout.html +51 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/layout/app-layout/app-layout.spec.ts +63 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/layout/app-layout/app-layout.ts +27 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/alert/alert.html +27 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/alert/alert.ts +41 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/button/button.html +27 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/button/button.ts +66 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/card/card.ts +100 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/collapsible/collapsible.html +38 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/collapsible/collapsible.ts +21 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/date-picker/date-picker.html +20 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/date-picker/date-picker.ts +49 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/date-range-picker/date-range-picker.html +20 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/date-range-picker/date-range-picker.ts +69 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/dialog/dialog.html +20 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/dialog/dialog.ts +40 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/dropdown-menu/dropdown-menu.html +20 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/dropdown-menu/dropdown-menu.ts +100 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/field/field.html +15 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/field/field.ts +28 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/field-appearance.ts +8 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/field-size.ts +8 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/icon/icon.ts +68 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/input/input.html +34 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/input/input.ts +79 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/label/label.ts +21 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/paginator/paginator.html +74 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/paginator/paginator.ts +93 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/paginator/types.ts +13 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/popover/popover.html +13 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/popover/popover.ts +37 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/select/select.html +36 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/select/select.ts +63 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/separator/separator.ts +31 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/skeleton/skeleton.ts +20 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/components/ui/spinner/spinner.ts +28 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/features/language-switcher/__examples__/html-middleware-site-example.ts +41 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/features/language-switcher/__examples__/language-switcher-example.ts +41 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/features/language-switcher/build-language-url.ts +158 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/features/language-switcher/index.ts +28 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/features/language-switcher/language-switcher.html +22 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/features/language-switcher/language-switcher.ts +110 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/features/language-switcher/languages.ts +46 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/pages/home/home.html +6 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/pages/home/home.spec.ts +23 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/pages/home/home.ts +13 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/pages/not-found/not-found.html +12 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/pages/not-found/not-found.spec.ts +33 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/pages/not-found/not-found.ts +15 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/alert/hlm-alert-action.ts +14 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/alert/hlm-alert-description.ts +17 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/alert/hlm-alert-title.ts +17 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/alert/hlm-alert.ts +35 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/alert/index.ts +16 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/button/hlm-button.token.ts +22 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/button/hlm-button.ts +72 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/button/index.ts +6 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/calendar/hlm-calendar-multi.ts +195 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/calendar/hlm-calendar-range.ts +194 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/calendar/hlm-calendar.ts +189 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/calendar/hlm-month-year-calendar.ts +119 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/calendar/index.ts +16 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/hlm-card-action.ts +12 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/hlm-card-avatar.ts +14 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/hlm-card-content.ts +12 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/hlm-card-description.ts +12 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/hlm-card-footer.ts +12 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/hlm-card-header.ts +15 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/hlm-card-title.ts +12 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/hlm-card.token.ts +19 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/hlm-card.ts +22 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/card/index.ts +28 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/collapsible/hlm-collapsible-content.ts +14 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/collapsible/hlm-collapsible-trigger.ts +9 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/collapsible/hlm-collapsible.ts +15 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/collapsible/index.ts +13 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-multi-input.ts +104 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-picker-anchor.ts +23 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-picker-input.ts +118 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-picker-multi.token.ts +109 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-picker-multi.ts +220 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-picker-trigger.ts +104 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-picker.token.ts +69 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-picker.ts +203 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-range-input.ts +120 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-range-picker.token.ts +86 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-date-range-picker.ts +252 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-month-year-input.ts +105 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-month-year-picker.token.ts +89 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/hlm-month-year-picker.ts +196 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/date-picker/index.ts +38 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog-close.ts +9 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog-content.ts +74 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog-description.ts +17 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog-footer.ts +15 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog-header.ts +12 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog-overlay.ts +27 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog-portal.ts +8 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog-title.ts +14 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog-trigger.ts +14 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog.service.ts +46 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/hlm-dialog.ts +24 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dialog/index.ts +35 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-checkbox-indicator.ts +21 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-checkbox.ts +54 -0
- 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
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-group.ts +14 -0
- 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
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-item.ts +42 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-label.ts +20 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-radio-indicator.ts +21 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-radio.ts +49 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-separator.ts +12 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-shortcut.ts +15 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-sub-trigger.ts +52 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-sub.ts +53 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-token.ts +26 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu-trigger.ts +46 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/hlm-dropdown-menu.ts +57 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/dropdown-menu/index.ts +49 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field-content.ts +12 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field-description.ts +53 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field-error.ts +100 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field-group.ts +15 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field-label.ts +17 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field-legend.ts +19 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field-separator.ts +24 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field-set.ts +15 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field-title.ts +15 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/hlm-field.ts +47 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/field/index.ts +34 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/input/hlm-input.ts +21 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/input/index.ts +5 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/input-group/hlm-input-group-addon.ts +40 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/input-group/hlm-input-group-button.ts +47 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/input-group/hlm-input-group-input.ts +17 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/input-group/hlm-input-group-text.ts +14 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/input-group/hlm-input-group.ts +18 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/input-group/index.ts +19 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/label/hlm-label.ts +17 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/label/index.ts +5 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/label/label.html +11 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/hlm-numbered-pagination-query-params.ts +191 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/hlm-numbered-pagination.ts +290 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/hlm-pagination-content.ts +12 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/hlm-pagination-ellipsis.ts +27 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/hlm-pagination-item.ts +7 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/hlm-pagination-link.ts +51 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/hlm-pagination-next.ts +65 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/hlm-pagination-previous.ts +65 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/hlm-pagination.ts +19 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/pagination/index.ts +31 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/popover/hlm-popover-content.ts +25 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/popover/hlm-popover-description.ts +12 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/popover/hlm-popover-header.ts +12 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/popover/hlm-popover-portal.ts +8 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/popover/hlm-popover-title.ts +12 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/popover/hlm-popover-trigger.ts +14 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/popover/hlm-popover.ts +23 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/popover/index.ts +25 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-content.ts +44 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-group.ts +14 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-item.ts +36 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-label.ts +14 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-multiple.ts +37 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-placeholder.ts +17 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-portal.ts +8 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-scroll-down.ts +22 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-scroll-up.ts +22 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-separator.ts +14 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-trigger.ts +59 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-value-template.ts +5 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-value.ts +18 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-values-content.ts +9 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select-values.ts +5 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/hlm-select.ts +37 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/select/index.ts +52 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/separator/hlm-separator.ts +19 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/separator/index.ts +5 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/skeleton/hlm-skeleton.ts +14 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/skeleton/index.ts +5 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/spinner/hlm-spinner.ts +26 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/spinner/index.ts +5 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/utils/hlm.ts +313 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/utils/index.ts +2 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/shared/utils/provide-spartan-hlm.ts +34 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/app/utils/async-data.ts +99 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/index.html +13 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/main.ts +5 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/styles.css +139 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/src/types/sf-globals.d.ts +23 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/tsconfig.app.json +11 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/tsconfig.json +36 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/tsconfig.spec.json +10 -0
- package/dist/force-app/main/default/uiBundles/feature-angular-language-switcher/ui-bundle.json +7 -0
- package/dist/jest.config.js +6 -0
- package/dist/package-lock.json +12554 -0
- package/dist/package.json +46 -0
- package/dist/scripts/apex/hello.apex +10 -0
- package/dist/scripts/gitignore-templates.json +4 -0
- package/dist/scripts/graphql-search.sh +191 -0
- package/dist/scripts/org-setup-config-schema.mjs +132 -0
- package/dist/scripts/org-setup-dev.mjs +114 -0
- package/dist/scripts/org-setup-url.mjs +108 -0
- package/dist/scripts/org-setup-utils.mjs +569 -0
- package/dist/scripts/org-setup-xml.mjs +278 -0
- package/dist/scripts/org-setup.config.json +5 -0
- package/dist/scripts/org-setup.mjs +2537 -0
- package/dist/scripts/sf-project-setup.mjs +103 -0
- package/dist/scripts/soql/account.soql +6 -0
- package/dist/scripts/validate-org-setup-config.mjs +38 -0
- package/dist/sfdx-project.json +12 -0
- 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
|
+
}
|