@faststore/core 3.99.1 → 4.0.0-dev.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/.turbo/turbo-generate.log +29 -0
- package/.turbo/turbo-test.log +47 -16
- package/@generated/cached-operations.json +24 -0
- package/@generated/gql.ts +134 -210
- package/@generated/graphql.ts +1628 -3037
- package/@generated/index.ts +1 -1
- package/@generated/persisted-documents.json +1 -1
- package/@generated/schema.graphql +1854 -1743
- package/CHANGELOG.md +28 -4
- package/README.md +2 -1
- package/api/index.ts +6 -7
- package/cms/faststore/base.jsonc +1 -1
- package/cms/faststore/components/cms_component__alert.jsonc +5 -1
- package/cms/faststore/components/cms_component__bannernewsletter.jsonc +5 -1
- package/cms/faststore/components/cms_component__bannertext.jsonc +5 -1
- package/cms/faststore/components/cms_component__breadcrumb.jsonc +16 -4
- package/cms/faststore/components/cms_component__cartsidebar.jsonc +21 -1
- package/cms/faststore/components/cms_component__children.jsonc +5 -1
- package/cms/faststore/components/cms_component__crosssellingshelf.jsonc +5 -1
- package/cms/faststore/components/cms_component__emptystate.jsonc +8 -3
- package/cms/faststore/components/cms_component__footer.jsonc +9 -3
- package/cms/faststore/components/cms_component__hero.jsonc +5 -1
- package/cms/faststore/components/cms_component__incentives.jsonc +5 -1
- package/cms/faststore/components/cms_component__navbar.jsonc +139 -1
- package/cms/faststore/components/cms_component__newsletter.jsonc +5 -1
- package/cms/faststore/components/cms_component__productdetails.jsonc +33 -1
- package/cms/faststore/components/cms_component__productgallery.jsonc +113 -15
- package/cms/faststore/components/cms_component__productshelf.jsonc +5 -1
- package/cms/faststore/components/cms_component__producttiles.jsonc +5 -1
- package/cms/faststore/components/cms_component__regionbar.jsonc +5 -1
- package/cms/faststore/components/cms_component__regionmodal.jsonc +5 -1
- package/cms/faststore/components/cms_component__regionpopover.jsonc +5 -1
- package/cms/faststore/components/cms_component__scrolltotopbutton.jsonc +29 -0
- package/cms/faststore/components/cms_component__search.jsonc +5 -1
- package/cms/faststore/content-types.json +115 -0
- package/cms/faststore/pages/cms_content_type__globalsections.jsonc +52 -0
- package/cms/faststore/pages/cms_content_type__home.jsonc +6 -0
- package/cms/faststore/pages/cms_content_type__landingpage.jsonc +8 -1
- package/cms/faststore/pages/cms_content_type__pdp.jsonc +6 -0
- package/cms/faststore/pages/cms_content_type__plp.jsonc +17 -0
- package/cms/faststore/pages/cms_content_type__search.jsonc +17 -0
- package/cms/faststore/schema.json +547 -43
- package/cms/faststore/sections.json +330 -21
- package/discovery.config.default.js +49 -3
- package/next-env.d.ts +2 -1
- package/next.config.js +48 -26
- package/package.json +47 -60
- package/public/icons.svg +5 -1
- package/scripts/i18n.js +76 -0
- package/src/components/account/MyAccountDrawer/OrganizationDrawer/OrganizationDrawer.tsx +2 -1
- package/src/components/account/MyAccountDrawer/OrganizationDrawer/section.module.scss +25 -24
- package/src/components/account/MyAccountDrawer/ProfileSummary/profile-summary.scss +8 -6
- package/src/components/account/MyAccountLayout/styles.scss +16 -12
- package/src/components/account/MyAccountMenu/styles.scss +21 -22
- package/src/components/account/MyAccountUserDetails/styles.module.scss +4 -2
- package/src/components/account/components/MyAccountAccordion/styles.scss +4 -2
- package/src/components/account/components/MyAccountHeader/styles.scss +3 -1
- package/src/components/account/components/MyAccountTable/styles.scss +6 -4
- package/src/components/account/orders/MyAccountListOrders/MyAccountFilterSlider/section.module.scss +21 -19
- package/src/components/account/orders/MyAccountListOrders/MyAccountListOrdersTable/MyAccountListOrdersTable.tsx +3 -1
- package/src/components/account/orders/MyAccountListOrders/MyAccountListOrdersTable/styles.scss +5 -3
- package/src/components/account/orders/MyAccountListOrders/styles.module.scss +25 -22
- package/src/components/account/orders/MyAccountOrderDetails/MyAccountBudgetsCard/styles.scss +7 -5
- package/src/components/account/orders/MyAccountOrderDetails/MyAccountDeliveryOptionAccordion/styles.scss +7 -5
- package/src/components/account/orders/MyAccountOrderDetails/MyAccountMoreInformationCard/styles.scss +3 -1
- package/src/components/account/orders/MyAccountOrderDetails/MyAccountOrderActionModal/styles.module.scss +8 -6
- package/src/components/account/orders/MyAccountOrderDetails/MyAccountOrderActions/styles.scss +3 -1
- package/src/components/account/orders/MyAccountOrderDetails/MyAccountStatusCard/styles.scss +8 -6
- package/src/components/account/orders/MyAccountOrderDetails/section.module.scss +36 -43
- package/src/components/account/profile/profile.module.scss +3 -1
- package/src/components/account/section.module.scss +3 -1
- package/src/components/account/security/styles.module.scss +14 -8
- package/src/components/cart/CartSidebar/CartSidebar.tsx +10 -1
- package/src/components/cart/CartSidebar/section.module.scss +19 -17
- package/src/components/cart/EmptyCart/EmptyCart.tsx +12 -4
- package/src/components/cms/GlobalSections.tsx +12 -12
- package/src/components/cms/RenderSections.tsx +10 -2
- package/src/components/cms/plp/Components.ts +9 -0
- package/src/components/common/Alert/section.module.scss +10 -5
- package/src/components/common/PreviewTag/section.module.scss +48 -40
- package/src/components/common/Toast/section.module.scss +4 -2
- package/src/components/localization/LocalizationSelector/LocalizationSelector.tsx +320 -0
- package/src/components/localization/LocalizationSelector/index.ts +1 -0
- package/src/components/localization/LocalizationSelector/section.module.scss +18 -0
- package/src/components/localization/index.ts +1 -0
- package/src/components/navigation/Navbar/Navbar.tsx +49 -1
- package/src/components/navigation/NavbarSlider/NavbarSlider.tsx +19 -1
- package/src/components/navigation/NavbarSlider/section.module.scss +12 -10
- package/src/components/product/OutOfStock/OutOfStock.tsx +6 -1
- package/src/components/product/ProductCard/ProductCard.tsx +1 -1
- package/src/components/region/RegionModal/RegionModal.tsx +4 -1
- package/src/components/region/RegionModal/section.module.scss +10 -8
- package/src/components/region/RegionPopover/RegionPopover.tsx +6 -5
- package/src/components/region/RegionPopover/section.module.scss +9 -7
- package/src/components/region/RegionSlider/RegionSlider.tsx +5 -2
- package/src/components/region/RegionSlider/section.module.scss +16 -13
- package/src/components/search/Filter/FilterDeliveryMethodFacet.tsx +5 -5
- package/src/components/search/Filter/FilterDesktop.tsx +13 -4
- package/src/components/search/Filter/FilterSlider.tsx +18 -6
- package/src/components/search/Filter/section.module.scss +21 -19
- package/src/components/search/SearchDropdown/SearchDropdown.tsx +9 -7
- package/src/components/search/SearchHistory/SearchHistory.tsx +7 -2
- package/src/components/search/SearchInput/SearchInput.tsx +21 -8
- package/src/components/search/SearchProductItem/SearchProductItem.tsx +10 -3
- package/src/components/search/SearchTop/SearchTop.tsx +30 -24
- package/src/components/search/Sort/Sort.tsx +17 -19
- package/src/components/sections/Alert/Alert.tsx +6 -1
- package/src/components/sections/BannerNewsletter/section.module.scss +6 -4
- package/src/components/sections/BannerText/section.module.scss +5 -3
- package/src/components/sections/Breadcrumb/Breadcrumb.tsx +6 -2
- package/src/components/sections/Breadcrumb/section.module.scss +7 -5
- package/src/components/sections/EmptyState/section.module.scss +5 -3
- package/src/components/sections/Footer/Footer.tsx +1 -5
- package/src/components/sections/Footer/section.module.scss +10 -8
- package/src/components/sections/Hero/Hero.tsx +3 -1
- package/src/components/sections/Hero/section.module.scss +6 -4
- package/src/components/sections/Incentives/section.module.scss +5 -3
- package/src/components/sections/Navbar/Navbar.tsx +45 -0
- package/src/components/sections/Navbar/section.module.scss +34 -33
- package/src/components/sections/Newsletter/section.module.scss +10 -8
- package/src/components/sections/ProductDetails/ProductDetails.tsx +23 -1
- package/src/components/sections/ProductDetails/section.module.scss +32 -30
- package/src/components/sections/ProductGallery/EmptyGallery.tsx +13 -16
- package/src/components/sections/ProductGallery/section.module.scss +42 -39
- package/src/components/sections/ProductShelf/section.module.scss +22 -17
- package/src/components/sections/ProductTiles/section.module.scss +15 -13
- package/src/components/sections/RegionBar/section.module.scss +5 -3
- package/src/components/sections/ScrollToTopButton/ScrollToTopButton.tsx +4 -3
- package/src/components/sections/ScrollToTopButton/section.module.scss +4 -2
- package/src/components/templates/LandingPage/LandingPage.tsx +22 -16
- package/src/components/templates/ProductListingPage/ProductListing.tsx +1 -13
- package/src/components/templates/ProductListingPage/ProductListingPage.tsx +8 -3
- package/src/components/ui/Breadcrumb/Breadcrumb.tsx +14 -11
- package/src/components/ui/Button/ButtonSignIn/ButtonSignIn.tsx +2 -1
- package/src/components/ui/Button/ButtonSignIn/ButtonSignInFallback/ButtonSignInFallback.tsx +3 -1
- package/src/components/ui/Link/Link.tsx +19 -2
- package/src/components/ui/LinkButton/LinkButton.tsx +28 -0
- package/src/components/ui/LinkButton/index.ts +2 -0
- package/src/components/ui/LocalizationButton/LocalizationButton.tsx +124 -0
- package/src/components/ui/LocalizationButton/index.ts +1 -0
- package/src/components/ui/PickupPoints/PickupPointCards.tsx +2 -4
- package/src/components/ui/ProductComparison/ProductComparisonSidebar.tsx +34 -17
- package/src/components/ui/ProductDescription/ProductDescription.tsx +8 -3
- package/src/components/ui/ProductDetails/AddToCartLoadingSkeleton.tsx +6 -2
- package/src/components/ui/ProductDetails/ProductDetailsSettings.tsx +17 -5
- package/src/components/ui/ProductGallery/ProductGallery.tsx +7 -3
- package/src/components/ui/ProductGallery/ProductGalleryPage.tsx +1 -3
- package/src/components/ui/ShippingSimulation/ShippingSimulation.tsx +6 -1
- package/src/customizations/src/styles/custom-mixins.scss +0 -2
- package/src/experimental/index.ts +26 -0
- package/src/experimental/myAccountServerSideProps.ts +11 -2
- package/src/experimental/searchServerSideFunctions/getServerSideProps.ts +13 -7
- package/src/experimental/searchServerSideFunctions/getStaticProps.ts +5 -4
- package/src/instrumentation.ts +20 -6
- package/src/pages/404.tsx +5 -4
- package/src/pages/500.tsx +5 -4
- package/src/pages/[...slug].tsx +7 -4
- package/src/pages/[slug]/p.tsx +26 -8
- package/src/pages/_app.tsx +9 -1
- package/src/pages/api/fs/logout.ts +3 -2
- package/src/pages/api/graphql.ts +7 -23
- package/src/pages/api/preview.ts +21 -8
- package/src/pages/checkout.tsx +15 -5
- package/src/pages/index.tsx +19 -10
- package/src/pages/login.tsx +5 -4
- package/src/pages/pvt/account/403.tsx +11 -2
- package/src/pages/pvt/account/404.tsx +13 -3
- package/src/pages/pvt/account/index.tsx +6 -4
- package/src/pages/pvt/account/orders/[id].tsx +11 -3
- package/src/pages/pvt/account/orders/index.tsx +11 -3
- package/src/pages/pvt/account/profile.tsx +11 -2
- package/src/pages/pvt/account/security.tsx +11 -2
- package/src/pages/pvt/account/user-details.tsx +11 -2
- package/src/pages/s.tsx +26 -6
- package/src/proxy.ts +154 -0
- package/src/sdk/account/refreshToken.ts +4 -4
- package/src/sdk/account/useReorder.ts +8 -2
- package/src/sdk/analytics/platform/vtex/search.ts +3 -2
- package/src/sdk/cart/redirectToCheckout.ts +14 -8
- package/src/sdk/cart/useCheckoutButton.ts +7 -1
- package/src/sdk/deliveryPromise/useDeliveryPromise.ts +10 -18
- package/src/sdk/graphql/request.ts +102 -50
- package/src/sdk/localization/bindingSelector.ts +91 -0
- package/src/sdk/localization/index.ts +4 -0
- package/src/sdk/localization/match-url.ts +64 -0
- package/src/sdk/localization/types.ts +31 -0
- package/src/sdk/localization/useBindingSelector.ts +183 -0
- package/src/sdk/localization/useLocaleValidation.ts +49 -0
- package/src/sdk/localization/useLocalizationConfig.tsx +160 -0
- package/src/sdk/offer/fetcher.ts +3 -2
- package/src/sdk/product/useLocalizedVariables.ts +1 -1
- package/src/sdk/product/usePageProductsQuery.ts +2 -2
- package/src/sdk/product/useProductGalleryQuery.ts +1 -2
- package/src/sdk/product/useProductLink.ts +3 -1
- package/src/sdk/search/formatSearchPath.ts +28 -3
- package/src/sdk/search/state.ts +22 -13
- package/src/sdk/search/useSearchBase.ts +10 -0
- package/src/sdk/search/useSearchHistory.ts +41 -7
- package/src/sdk/session/index.ts +21 -3
- package/src/sdk/shipping/useShippingSimulation.ts +10 -2
- package/src/sdk/tsconfig.json +6 -0
- package/src/sdk/ui/useLink.ts +48 -0
- package/src/server/cms/global.ts +40 -0
- package/src/server/cms/index.ts +18 -10
- package/src/server/cms/pdp.ts +1 -0
- package/src/server/cms/plp.ts +3 -0
- package/src/server/content/service.ts +36 -24
- package/src/server/content/types.ts +10 -3
- package/src/server/index.ts +27 -20
- package/src/server/options.ts +27 -2
- package/src/styles/global/index.scss +10 -7
- package/src/styles/main.scss +3 -3
- package/src/typings/locales.ts +41 -0
- package/src/utils/fetchProductGallerySSR.ts +3 -1
- package/src/utils/getBaseDomain.ts +2 -1
- package/src/utils/getRequestHostname.ts +26 -0
- package/src/utils/globalSettings.ts +11 -0
- package/src/utils/localization/bindingPaths.ts +337 -0
- package/src/utils/localization/validateLocaleForHostname.ts +69 -0
- package/src/utils/localization/withLocaleValidation.ts +36 -0
- package/src/utils/utilities.ts +22 -0
- package/test/pages/api/preview.test.ts +93 -0
- package/test/sdk/localization/bindingSelector.test.ts +246 -0
- package/test/sdk/localization/store-url.browser.test.ts +140 -0
- package/test/sdk/localization/store-url.test.ts +96 -0
- package/test/sdk/localization/useBindingSelector.test.tsx +219 -0
- package/test/sdk/search/useSearchHistory.test.ts +222 -0
- package/test/server/cms/index.test.ts +3 -4
- package/test/server/content/service.test.ts +98 -0
- package/test/server/index.test.ts +10 -22
- package/test/utils/cookieCacheBusting.test.ts +32 -24
- package/test/utils/getRequestHostname.test.ts +71 -0
- package/test/utils/localization/bindingPaths.test.ts +706 -0
- package/test/utils/match-url.test.ts +388 -0
- package/tsconfig.json +11 -3
- package/vitest.config.ts +61 -0
- package/.next/BUILD_ID +0 -1
- package/.next/build-manifest.json +0 -197
- package/.next/cache/.tsbuildinfo +0 -1
- package/.next/cache/config.json +0 -7
- package/.next/cache/webpack/client-production/0.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack +0 -0
- package/.next/cache/webpack/edge-server-production/0.pack +0 -0
- package/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/.next/cache/webpack/server-production/0.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack +0 -0
- package/.next/export-marker.json +0 -1
- package/.next/images-manifest.json +0 -1
- package/.next/next-minimal-server.js.nft.json +0 -1
- package/.next/next-server.js.nft.json +0 -1
- package/.next/package.json +0 -1
- package/.next/prerender-manifest.js +0 -1
- package/.next/prerender-manifest.json +0 -1
- package/.next/react-loadable-manifest.json +0 -478
- package/.next/required-server-files.json +0 -1
- package/.next/routes-manifest.json +0 -1
- package/.next/server/chunks/1280.js +0 -1
- package/.next/server/chunks/1454.js +0 -1
- package/.next/server/chunks/1554.js +0 -1
- package/.next/server/chunks/1607.js +0 -1
- package/.next/server/chunks/1780.js +0 -1
- package/.next/server/chunks/1917.js +0 -1
- package/.next/server/chunks/1972.js +0 -1
- package/.next/server/chunks/2230.js +0 -1
- package/.next/server/chunks/2249.js +0 -1
- package/.next/server/chunks/2430.js +0 -1
- package/.next/server/chunks/2445.js +0 -12
- package/.next/server/chunks/2570.js +0 -1
- package/.next/server/chunks/2792.js +0 -1
- package/.next/server/chunks/2903.js +0 -1
- package/.next/server/chunks/294.js +0 -1
- package/.next/server/chunks/2955.js +0 -1
- package/.next/server/chunks/3006.js +0 -1
- package/.next/server/chunks/3029.js +0 -1
- package/.next/server/chunks/3060.js +0 -1
- package/.next/server/chunks/3408.js +0 -1
- package/.next/server/chunks/3483.js +0 -1
- package/.next/server/chunks/3683.js +0 -1
- package/.next/server/chunks/3836.js +0 -1
- package/.next/server/chunks/3890.js +0 -494
- package/.next/server/chunks/3922.js +0 -1
- package/.next/server/chunks/3945.js +0 -1
- package/.next/server/chunks/3951.js +0 -7
- package/.next/server/chunks/416.js +0 -1
- package/.next/server/chunks/4194.js +0 -1
- package/.next/server/chunks/4365.js +0 -1
- package/.next/server/chunks/4451.js +0 -1
- package/.next/server/chunks/4803.js +0 -1
- package/.next/server/chunks/4913.js +0 -13
- package/.next/server/chunks/5402.js +0 -1
- package/.next/server/chunks/5723.js +0 -13
- package/.next/server/chunks/5796.js +0 -1
- package/.next/server/chunks/6393.js +0 -1
- package/.next/server/chunks/6457.js +0 -6
- package/.next/server/chunks/6698.js +0 -1
- package/.next/server/chunks/6804.js +0 -6
- package/.next/server/chunks/6886.js +0 -1
- package/.next/server/chunks/7098.js +0 -9
- package/.next/server/chunks/7169.js +0 -1
- package/.next/server/chunks/7228.js +0 -1
- package/.next/server/chunks/7275.js +0 -1
- package/.next/server/chunks/7371.js +0 -1
- package/.next/server/chunks/7692.js +0 -1
- package/.next/server/chunks/7799.js +0 -1
- package/.next/server/chunks/8068.js +0 -1
- package/.next/server/chunks/83.js +0 -1
- package/.next/server/chunks/831.js +0 -1
- package/.next/server/chunks/8563.js +0 -1
- package/.next/server/chunks/8569.js +0 -1
- package/.next/server/chunks/8687.js +0 -1
- package/.next/server/chunks/870.js +0 -1
- package/.next/server/chunks/8737.js +0 -1
- package/.next/server/chunks/8741.js +0 -1
- package/.next/server/chunks/9088.js +0 -1
- package/.next/server/chunks/9117.js +0 -1
- package/.next/server/chunks/9237.js +0 -1
- package/.next/server/chunks/948.js +0 -3
- package/.next/server/chunks/9563.js +0 -3
- package/.next/server/chunks/9570.js +0 -1
- package/.next/server/chunks/9740.js +0 -1
- package/.next/server/chunks/9853.js +0 -1
- package/.next/server/chunks/9984.js +0 -1
- package/.next/server/chunks/9985.js +0 -1
- package/.next/server/chunks/9990.js +0 -1
- package/.next/server/chunks/ButtonSignIn.js +0 -1
- package/.next/server/chunks/Dropdown.js +0 -1
- package/.next/server/chunks/DropdownButton.js +0 -1
- package/.next/server/chunks/DropdownItem.js +0 -1
- package/.next/server/chunks/DropdownMenu.js +0 -1
- package/.next/server/chunks/FilterSkeleton.js +0 -1
- package/.next/server/chunks/ScrollToTopButton.js +0 -1
- package/.next/server/chunks/UIBannerText.js +0 -1
- package/.next/server/chunks/UISKUMatrixSidebar.js +0 -1
- package/.next/server/chunks/font-manifest.json +0 -1
- package/.next/server/edge-instrumentation.js +0 -2
- package/.next/server/edge-instrumentation.js.map +0 -1
- package/.next/server/edge-runtime-webpack.js +0 -157
- package/.next/server/edge-runtime-webpack.js.map +0 -1
- package/.next/server/font-manifest.json +0 -1
- package/.next/server/functions-config-manifest.json +0 -1
- package/.next/server/instrumentation.js +0 -1
- package/.next/server/instrumentation.js.nft.json +0 -1
- package/.next/server/middleware-build-manifest.js +0 -1
- package/.next/server/middleware-manifest.json +0 -6
- package/.next/server/middleware-react-loadable-manifest.js +0 -1
- package/.next/server/next-font-manifest.js +0 -1
- package/.next/server/next-font-manifest.json +0 -1
- package/.next/server/pages/404.js +0 -1
- package/.next/server/pages/404.js.nft.json +0 -1
- package/.next/server/pages/500.js +0 -1
- package/.next/server/pages/500.js.nft.json +0 -1
- package/.next/server/pages/[...slug].js +0 -1
- package/.next/server/pages/[...slug].js.nft.json +0 -1
- package/.next/server/pages/[slug]/p.js +0 -1
- package/.next/server/pages/[slug]/p.js.nft.json +0 -1
- package/.next/server/pages/_app.js +0 -1
- package/.next/server/pages/_app.js.nft.json +0 -1
- package/.next/server/pages/_document.js +0 -1
- package/.next/server/pages/_document.js.nft.json +0 -1
- package/.next/server/pages/_error.js +0 -1
- package/.next/server/pages/_error.js.nft.json +0 -1
- package/.next/server/pages/api/fs/logout.js +0 -1
- package/.next/server/pages/api/fs/logout.js.nft.json +0 -1
- package/.next/server/pages/api/graphql.js +0 -3
- package/.next/server/pages/api/graphql.js.nft.json +0 -1
- package/.next/server/pages/api/health/live.js +0 -1
- package/.next/server/pages/api/health/live.js.nft.json +0 -1
- package/.next/server/pages/api/health/ready.js +0 -1
- package/.next/server/pages/api/health/ready.js.nft.json +0 -1
- package/.next/server/pages/api/preview.js +0 -1
- package/.next/server/pages/api/preview.js.nft.json +0 -1
- package/.next/server/pages/checkout.js +0 -1
- package/.next/server/pages/checkout.js.nft.json +0 -1
- package/.next/server/pages/en-US/404.html +0 -42
- package/.next/server/pages/en-US/404.json +0 -1
- package/.next/server/pages/en-US/500.html +0 -42
- package/.next/server/pages/en-US/500.json +0 -1
- package/.next/server/pages/en-US/checkout.html +0 -42
- package/.next/server/pages/en-US/checkout.json +0 -1
- package/.next/server/pages/en-US/login.html +0 -42
- package/.next/server/pages/en-US/login.json +0 -1
- package/.next/server/pages/en-US/s.html +0 -42
- package/.next/server/pages/en-US/s.json +0 -1
- package/.next/server/pages/en-US.html +0 -42
- package/.next/server/pages/en-US.json +0 -1
- package/.next/server/pages/index.js +0 -1
- package/.next/server/pages/index.js.nft.json +0 -1
- package/.next/server/pages/login.js +0 -1
- package/.next/server/pages/login.js.nft.json +0 -1
- package/.next/server/pages/pvt/account/403.js +0 -1
- package/.next/server/pages/pvt/account/403.js.nft.json +0 -1
- package/.next/server/pages/pvt/account/404.js +0 -1
- package/.next/server/pages/pvt/account/404.js.nft.json +0 -1
- package/.next/server/pages/pvt/account/[...unknown].js +0 -1
- package/.next/server/pages/pvt/account/[...unknown].js.nft.json +0 -1
- package/.next/server/pages/pvt/account/orders/[id].js +0 -1
- package/.next/server/pages/pvt/account/orders/[id].js.nft.json +0 -1
- package/.next/server/pages/pvt/account/orders.js +0 -1
- package/.next/server/pages/pvt/account/orders.js.nft.json +0 -1
- package/.next/server/pages/pvt/account/profile.js +0 -1
- package/.next/server/pages/pvt/account/profile.js.nft.json +0 -1
- package/.next/server/pages/pvt/account/security.js +0 -1
- package/.next/server/pages/pvt/account/security.js.nft.json +0 -1
- package/.next/server/pages/pvt/account/user-details.js +0 -1
- package/.next/server/pages/pvt/account/user-details.js.nft.json +0 -1
- package/.next/server/pages/pvt/account.js +0 -1
- package/.next/server/pages/pvt/account.js.nft.json +0 -1
- package/.next/server/pages/s.js +0 -1
- package/.next/server/pages/s.js.nft.json +0 -1
- package/.next/server/pages-manifest.json +0 -1
- package/.next/server/webpack-api-runtime.js +0 -1
- package/.next/server/webpack-runtime.js +0 -1
- package/.next/static/NAuY8lqaeBBZf9CMZuYI2/_buildManifest.js +0 -1
- package/.next/static/NAuY8lqaeBBZf9CMZuYI2/_ssgManifest.js +0 -1
- package/.next/static/chunks/227-f109b24190a008ab.js +0 -8
- package/.next/static/chunks/2284.185d834d9829b652.js +0 -1
- package/.next/static/chunks/2851.e68ed3c8d27a032a.js +0 -1
- package/.next/static/chunks/2927.5a79877943a6bf7c.js +0 -1
- package/.next/static/chunks/3155.7bc4c730a79d9ecb.js +0 -1
- package/.next/static/chunks/3166-0b30a3887c0c346d.js +0 -1
- package/.next/static/chunks/3399.017c5209b74b0d97.js +0 -1
- package/.next/static/chunks/3483.ef53d632a763da3f.js +0 -1
- package/.next/static/chunks/349.b3b38e1e9f719dda.js +0 -1
- package/.next/static/chunks/3802.5e8b0b689eacc0f8.js +0 -1
- package/.next/static/chunks/3836.620480018be48233.js +0 -1
- package/.next/static/chunks/4436.e7af66f0b654ee36.js +0 -1
- package/.next/static/chunks/4803.b1920b6e3a1a6304.js +0 -1
- package/.next/static/chunks/5796.4351370494d8b8b6.js +0 -1
- package/.next/static/chunks/6393.55f991b1a6bb4fe7.js +0 -1
- package/.next/static/chunks/6700.b23221a57514e737.js +0 -1
- package/.next/static/chunks/6789.960162355435a81d.js +0 -1
- package/.next/static/chunks/7191-befad64ba238a817.js +0 -1
- package/.next/static/chunks/7692.8d5bf4560341a2f6.js +0 -1
- package/.next/static/chunks/7861.7f9f1a124a43da30.js +0 -6
- package/.next/static/chunks/8068.43663fb64762f7b8.js +0 -1
- package/.next/static/chunks/83.e0c16f0299b364a5.js +0 -1
- package/.next/static/chunks/8325.dabe9aa22eaadeed.js +0 -1
- package/.next/static/chunks/8587.53c8ce185ed1a2cb.js +0 -1
- package/.next/static/chunks/9173-e89da04079c35360.js +0 -2
- package/.next/static/chunks/9237.7555603703f81bf7.js +0 -1
- package/.next/static/chunks/9298-1d1ed0a54133cb59.js +0 -28
- package/.next/static/chunks/9399.d116a7fd62024783.js +0 -1
- package/.next/static/chunks/941.3e2782ab9c490eb0.js +0 -1
- package/.next/static/chunks/9701.f9a0bfffba8807dc.js +0 -1
- package/.next/static/chunks/9909.787576b171184aa5.js +0 -1
- package/.next/static/chunks/9960.7ef77f8a8b046170.js +0 -1
- package/.next/static/chunks/9979.aedbc094bdc2c599.js +0 -1
- package/.next/static/chunks/BannerNewsletter.00328ad162109908.js +0 -1
- package/.next/static/chunks/BannerText.dbbec87cadb8a521.js +0 -1
- package/.next/static/chunks/ButtonSignIn.f6e210eaca873e48.js +0 -1
- package/.next/static/chunks/CartItem.b34e295377f6f94f.js +0 -1
- package/.next/static/chunks/CartSidebar.ac447d1ef6276da1.js +0 -1
- package/.next/static/chunks/Dropdown.38b48c749bad6b60.js +0 -1
- package/.next/static/chunks/DropdownButton.66fa6b25dddb3a07.js +0 -1
- package/.next/static/chunks/DropdownItem.38b8f5a07c7f422a.js +0 -1
- package/.next/static/chunks/DropdownMenu.48b11521a24c6efd.js +0 -1
- package/.next/static/chunks/EmptyCart.320e2f94a0d5e6b1.js +0 -1
- package/.next/static/chunks/FilterSkeleton.7ab99e28f92341c9.js +0 -1
- package/.next/static/chunks/Footer.166b02c2f13980c6.js +0 -1
- package/.next/static/chunks/Gift.c49ba26da2fbab5e.js +0 -1
- package/.next/static/chunks/Newsletter.fc406f6029057022.js +0 -1
- package/.next/static/chunks/OrderSummary.5957d69f990d9add.js +0 -1
- package/.next/static/chunks/PreviewTag.f4f0c8710a7bcad9.js +0 -1
- package/.next/static/chunks/ProductShelf.cc46f5cdeeaeaff6.js +0 -1
- package/.next/static/chunks/ProductTiles.9b804e51b0704d60.js +0 -1
- package/.next/static/chunks/RegionModal.319ad44250cb12d2.js +0 -1
- package/.next/static/chunks/RegionSlider.0e0e93369f4f9c92.js +0 -1
- package/.next/static/chunks/ScrollToTopButton.c15431f2e8bb93ef.js +0 -1
- package/.next/static/chunks/ShoppingAssistant.ba191eb4dbed67e5.js +0 -1
- package/.next/static/chunks/Toast.5f90f0436708be5e.js +0 -1
- package/.next/static/chunks/UIBannerText.7315b16af4457059.js +0 -1
- package/.next/static/chunks/UISKUMatrixSidebar.b4349b2682bbe183.js +0 -1
- package/.next/static/chunks/UIToast.19a8664c01a00d3a.js +0 -1
- package/.next/static/chunks/framework-d514426edf885c68.js +0 -33
- package/.next/static/chunks/main-ec03882c4375091d.js +0 -1
- package/.next/static/chunks/pages/404-e3dfbbac003b02a2.js +0 -1
- package/.next/static/chunks/pages/500-c7814e085081d7b8.js +0 -1
- package/.next/static/chunks/pages/[...slug]-dc8a395a43b081f1.js +0 -1
- package/.next/static/chunks/pages/[slug]/p-644f1132f5a0f736.js +0 -1
- package/.next/static/chunks/pages/_app-84aaff8a9d4de60e.js +0 -1
- package/.next/static/chunks/pages/_error-2b0148be56a716e9.js +0 -1
- package/.next/static/chunks/pages/checkout-2071467ad42dfb99.js +0 -1
- package/.next/static/chunks/pages/index-8774d5b4a8744eb2.js +0 -1
- package/.next/static/chunks/pages/login-e74747c9f9cb5b49.js +0 -1
- package/.next/static/chunks/pages/pvt/account/403-491e6b062487b30f.js +0 -1
- package/.next/static/chunks/pages/pvt/account/404-5ad472d9f2ee1980.js +0 -1
- package/.next/static/chunks/pages/pvt/account/[...unknown]-f80f645594d2740c.js +0 -1
- package/.next/static/chunks/pages/pvt/account/orders/[id]-64e42157c0974c48.js +0 -1
- package/.next/static/chunks/pages/pvt/account/orders-faab39f2aff05fae.js +0 -1
- package/.next/static/chunks/pages/pvt/account/profile-9672177ee2665bc0.js +0 -1
- package/.next/static/chunks/pages/pvt/account/security-03eb1ccab9a30563.js +0 -1
- package/.next/static/chunks/pages/pvt/account/user-details-7838e3f79781596c.js +0 -1
- package/.next/static/chunks/pages/pvt/account-65fefcc699344bdb.js +0 -1
- package/.next/static/chunks/pages/s-162ba789e6611175.js +0 -1
- package/.next/static/chunks/polyfills-c67a75d1b6f99dc8.js +0 -1
- package/.next/static/chunks/webpack-f621ff3e951d2982.js +0 -1
- package/.next/static/css/02259c549b2179f2.css +0 -1
- package/.next/static/css/02eaf202d3d11fd1.css +0 -1
- package/.next/static/css/033e241c58ba6fbb.css +0 -1
- package/.next/static/css/042d012bc1895b5c.css +0 -1
- package/.next/static/css/14176a4704ff97b2.css +0 -1
- package/.next/static/css/146a8678babc61bf.css +0 -1
- package/.next/static/css/16d65422bfa80f51.css +0 -1
- package/.next/static/css/1c4983f11b861ea0.css +0 -1
- package/.next/static/css/204754bbd8cc4bef.css +0 -1
- package/.next/static/css/24d91007fc96be10.css +0 -1
- package/.next/static/css/2980acad3f8e1028.css +0 -1
- package/.next/static/css/2c19433e38fd7533.css +0 -1
- package/.next/static/css/31380ebc6e671486.css +0 -1
- package/.next/static/css/42df884711219edb.css +0 -1
- package/.next/static/css/4f0597b0bf0ef2e1.css +0 -1
- package/.next/static/css/54cedf4b65e5863f.css +0 -1
- package/.next/static/css/6580147d7ab8fcca.css +0 -1
- package/.next/static/css/6819dddf2b1b5ea7.css +0 -1
- package/.next/static/css/7ef7762f412ed665.css +0 -1
- package/.next/static/css/82f3cd529b672812.css +0 -1
- package/.next/static/css/837662922091162f.css +0 -1
- package/.next/static/css/8e6ab59ace21e03b.css +0 -1
- package/.next/static/css/a852afa9be31e946.css +0 -1
- package/.next/static/css/b684b347c5cc6514.css +0 -1
- package/.next/static/css/bcdaef0a83277ef5.css +0 -1
- package/.next/static/css/c33615999008d3f9.css +0 -1
- package/.next/static/css/c34f6ea9953e2f63.css +0 -1
- package/.next/static/css/ea11d74a2848b093.css +0 -1
- package/.next/static/css/f16438bcf4508fb3.css +0 -1
- package/.next/static/css/f60366555f563deb.css +0 -1
- package/.next/trace +0 -143
- package/.turbo/turbo-build.log +0 -140
- package/jest.config.js +0 -17
- package/public/~partytown/debug/partytown-atomics.js +0 -556
- package/public/~partytown/debug/partytown-media.js +0 -374
- package/public/~partytown/debug/partytown-sandbox-sw.js +0 -543
- package/public/~partytown/debug/partytown-sw.js +0 -59
- package/public/~partytown/debug/partytown-ww-atomics.js +0 -1789
- package/public/~partytown/debug/partytown-ww-sw.js +0 -1781
- package/public/~partytown/debug/partytown.js +0 -72
- package/public/~partytown/partytown-atomics.js +0 -2
- package/public/~partytown/partytown-media.js +0 -2
- package/public/~partytown/partytown-sw.js +0 -2
- package/public/~partytown/partytown.js +0 -2
- package/src/middleware__DISABLED.ts +0 -65
- package/src/server/generator/generateGraphQLSchemaFile.ts +0 -3
- package/src/server/generator/schema.ts +0 -81
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { getClientCacheBustingValue } from 'src/utils/cookieCacheBusting'
|
|
2
2
|
|
|
3
3
|
export type RequestOptions = Omit<BaseRequestOptions, 'operation' | 'variables'>
|
|
4
|
+
|
|
4
5
|
export type Operation = {
|
|
5
6
|
__meta__?: Record<string, any>
|
|
6
7
|
}
|
|
7
8
|
|
|
8
|
-
export interface GraphQLResponse<
|
|
9
|
-
data
|
|
10
|
-
errors
|
|
9
|
+
export interface GraphQLResponse<Data = any> {
|
|
10
|
+
data?: Data
|
|
11
|
+
errors?: any[]
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export interface BaseRequestOptions<V = any> {
|
|
@@ -18,33 +19,12 @@ export interface BaseRequestOptions<V = any> {
|
|
|
18
19
|
value?: string | null
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
},
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export const request = async <Query = unknown, Variables = unknown>(
|
|
28
|
-
operation: Operation,
|
|
29
|
-
variables: Variables,
|
|
30
|
-
options?: RequestOptions
|
|
31
|
-
) => {
|
|
32
|
-
// Get cache busting value based on auth state and postal code
|
|
33
|
-
const value = getClientCacheBustingValue()
|
|
34
|
-
|
|
35
|
-
const { data, errors } = await baseRequest<Variables, Query>('/api/graphql', {
|
|
36
|
-
...options,
|
|
37
|
-
variables,
|
|
38
|
-
operation,
|
|
39
|
-
value,
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
if (errors?.length) {
|
|
43
|
-
throw errors[0]
|
|
44
|
-
}
|
|
22
|
+
const cachedOperations = import('../../../@generated/cached-operations.json')
|
|
23
|
+
.then((mod) => mod.default as string[])
|
|
24
|
+
.catch(() => [] as string[])
|
|
45
25
|
|
|
46
|
-
|
|
47
|
-
|
|
26
|
+
const MethodByOperation = async (operationName: string) =>
|
|
27
|
+
(await cachedOperations).includes(operationName) ? 'GET' : 'POST'
|
|
48
28
|
|
|
49
29
|
/* This piece of code was taken out of @faststore/graphql-utils */
|
|
50
30
|
const baseRequest = async <V = any, D = any>(
|
|
@@ -56,40 +36,76 @@ const baseRequest = async <V = any, D = any>(
|
|
|
56
36
|
// Uses method from fetchOptions.
|
|
57
37
|
// If no one is passed, figure out with via heuristic
|
|
58
38
|
const method =
|
|
59
|
-
fetchOptions?.method
|
|
60
|
-
|
|
61
|
-
: operationName.endsWith('Query')
|
|
62
|
-
? 'GET'
|
|
63
|
-
: 'POST'
|
|
39
|
+
fetchOptions?.method?.toUpperCase() ??
|
|
40
|
+
(await MethodByOperation(operationName))
|
|
64
41
|
|
|
42
|
+
const response = await (method === 'POST' ? POSTRequest : GETRequest)({
|
|
43
|
+
endpoint,
|
|
44
|
+
operation,
|
|
45
|
+
variables,
|
|
46
|
+
fetchOptions,
|
|
47
|
+
value,
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
return ParseInvalidRequest(response) ?? response.json()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const GETRequest = <Variables>({
|
|
54
|
+
operation,
|
|
55
|
+
variables,
|
|
56
|
+
fetchOptions,
|
|
57
|
+
endpoint,
|
|
58
|
+
value,
|
|
59
|
+
}: BaseRequestOptions<Variables> & { endpoint: string }) => {
|
|
60
|
+
const { operationName, operationHash } = operation['__meta__']
|
|
65
61
|
const params = new URLSearchParams({
|
|
66
62
|
operationName,
|
|
67
63
|
operationHash,
|
|
68
|
-
|
|
69
|
-
...(
|
|
64
|
+
variables: JSON.stringify(variables),
|
|
65
|
+
...(value && { v: value }),
|
|
70
66
|
})
|
|
71
67
|
|
|
72
|
-
const
|
|
73
|
-
method === 'POST'
|
|
74
|
-
? JSON.stringify({
|
|
75
|
-
operationName,
|
|
76
|
-
operationHash,
|
|
77
|
-
variables,
|
|
78
|
-
})
|
|
79
|
-
: undefined
|
|
68
|
+
const url = `${endpoint}?${params.toString()}`
|
|
80
69
|
|
|
70
|
+
return fetch(url, {
|
|
71
|
+
method: 'GET',
|
|
72
|
+
body: undefined,
|
|
73
|
+
...fetchOptions,
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const POSTRequest = <Variables>({
|
|
78
|
+
operation,
|
|
79
|
+
variables,
|
|
80
|
+
fetchOptions,
|
|
81
|
+
endpoint,
|
|
82
|
+
value,
|
|
83
|
+
}: BaseRequestOptions<Variables> & { endpoint: string }) => {
|
|
84
|
+
const { operationName, operationHash } = operation['__meta__']
|
|
85
|
+
const params = new URLSearchParams({
|
|
86
|
+
operationName,
|
|
87
|
+
operationHash,
|
|
88
|
+
})
|
|
81
89
|
const url = `${endpoint}?${params.toString()}`
|
|
82
90
|
|
|
83
|
-
|
|
84
|
-
method,
|
|
85
|
-
body
|
|
91
|
+
return fetch(url, {
|
|
92
|
+
method: 'POST',
|
|
93
|
+
body: JSON.stringify({
|
|
94
|
+
operationName,
|
|
95
|
+
operationHash,
|
|
96
|
+
variables,
|
|
97
|
+
}),
|
|
86
98
|
...fetchOptions,
|
|
87
99
|
headers: {
|
|
88
|
-
|
|
100
|
+
'Content-Type': 'application/json',
|
|
89
101
|
...fetchOptions?.headers,
|
|
90
102
|
},
|
|
91
103
|
})
|
|
104
|
+
}
|
|
92
105
|
|
|
106
|
+
const ParseInvalidRequest = <T>(
|
|
107
|
+
response: Awaited<ReturnType<typeof fetch>>
|
|
108
|
+
) => {
|
|
93
109
|
if (!response.ok) {
|
|
94
110
|
const statusText = response.statusText
|
|
95
111
|
return {
|
|
@@ -100,8 +116,44 @@ const baseRequest = async <V = any, D = any>(
|
|
|
100
116
|
},
|
|
101
117
|
],
|
|
102
118
|
data: undefined,
|
|
103
|
-
} as GraphQLResponse<
|
|
119
|
+
} as GraphQLResponse<T>
|
|
104
120
|
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export async function GraphqlRequest<Query = unknown, Variables = unknown>(
|
|
124
|
+
{ operation, variables }: { operation: Operation; variables: Variables },
|
|
125
|
+
options?: RequestInit
|
|
126
|
+
): Promise<GraphQLResponse<Query>> {
|
|
127
|
+
// Get cache busting value based on cookie changes
|
|
128
|
+
const value = getClientCacheBustingValue()
|
|
105
129
|
|
|
106
|
-
|
|
130
|
+
const response = await baseRequest<Variables, Query>('/api/graphql', {
|
|
131
|
+
variables,
|
|
132
|
+
operation,
|
|
133
|
+
fetchOptions: options,
|
|
134
|
+
value,
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
// in Order to keep the same behaviour of previous version (request) throwing error
|
|
138
|
+
if (response.errors?.length) {
|
|
139
|
+
throw response.errors[0]
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return response
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @description It only exists to backward compatibilities
|
|
147
|
+
*/
|
|
148
|
+
export async function request<Query = unknown, Variables = unknown>(
|
|
149
|
+
operation: Operation,
|
|
150
|
+
variables: Variables,
|
|
151
|
+
options?: RequestOptions
|
|
152
|
+
) {
|
|
153
|
+
return (
|
|
154
|
+
await GraphqlRequest<Query, Variables>(
|
|
155
|
+
{ operation, variables },
|
|
156
|
+
options?.fetchOptions
|
|
157
|
+
)
|
|
158
|
+
).data
|
|
107
159
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { Binding, Locale } from './types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Capitalizes the first character of a string (e.g. "italiano" → "Italiano").
|
|
5
|
+
*/
|
|
6
|
+
function capitalizeFirst(str: string): string {
|
|
7
|
+
if (!str) return str
|
|
8
|
+
return str.charAt(0).toUpperCase() + str.slice(1)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Builds language options as Record<string, string> for direct use with UISelectField.
|
|
13
|
+
* Uses native locale name with first letter capitalized; adds " (regionCode)" when multiple locales share the same name.
|
|
14
|
+
*
|
|
15
|
+
* @param locales - Record of locale objects from discovery.config
|
|
16
|
+
* @returns Record where key is locale code (e.g., "pt-BR") and value is display label (e.g., "Português (BR)")
|
|
17
|
+
*/
|
|
18
|
+
export function buildLanguageOptions(
|
|
19
|
+
locales: Record<string, Locale>
|
|
20
|
+
): Record<string, string> {
|
|
21
|
+
// Count occurrences of each name to determine disambiguation needs
|
|
22
|
+
const languageCount: Record<string, number> = {}
|
|
23
|
+
Object.values(locales).forEach((locale) => {
|
|
24
|
+
languageCount[locale.name] = (languageCount[locale.name] || 0) + 1
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// Build options with conditional disambiguation; format native name with first letter capitalized
|
|
28
|
+
const options: Record<string, string> = {}
|
|
29
|
+
Object.entries(locales).forEach(([code, locale]) => {
|
|
30
|
+
const label = capitalizeFirst(locale.name)
|
|
31
|
+
const needsDisambiguation = languageCount[locale.name] > 1
|
|
32
|
+
options[code] = needsDisambiguation
|
|
33
|
+
? `${label} (${locale.regionCode})`
|
|
34
|
+
: label
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
return options
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Extracts unique currency codes from a locale's bindings.
|
|
42
|
+
*
|
|
43
|
+
* @param locale - The locale object containing bindings
|
|
44
|
+
* @returns Array of unique currency codes (e.g., ["BRL", "USD"])
|
|
45
|
+
*/
|
|
46
|
+
export function getCurrenciesForLocale(locale: Locale): string[] {
|
|
47
|
+
const currencies = new Set<string>()
|
|
48
|
+
|
|
49
|
+
locale.bindings.forEach((binding) => {
|
|
50
|
+
currencies.add(binding.currencyCode)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
return Array.from(currencies)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Resolves the correct binding for a locale+currency combination.
|
|
58
|
+
* Applies isDefault tie-breaker when multiple bindings match.
|
|
59
|
+
*
|
|
60
|
+
* @param bindings - Array of bindings from a locale
|
|
61
|
+
* @param currencyCode - The selected currency code to match
|
|
62
|
+
* @returns The resolved binding, or null if no match found
|
|
63
|
+
*/
|
|
64
|
+
export function resolveBinding(
|
|
65
|
+
bindings: Binding[],
|
|
66
|
+
currencyCode: string
|
|
67
|
+
): Binding | null {
|
|
68
|
+
const matches = bindings.filter((b) => b.currencyCode === currencyCode)
|
|
69
|
+
|
|
70
|
+
if (matches.length === 0) return null
|
|
71
|
+
if (matches.length === 1) return matches[0]
|
|
72
|
+
|
|
73
|
+
// Multiple matches - apply isDefault tie-breaker
|
|
74
|
+
return matches.find((b) => b.isDefault) ?? matches[0]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Validates that a URL is non-empty and appears to be valid.
|
|
79
|
+
*
|
|
80
|
+
* @param url - The URL string to validate
|
|
81
|
+
* @returns True if the URL is valid, false otherwise
|
|
82
|
+
*/
|
|
83
|
+
export function isValidUrl(url: string): boolean {
|
|
84
|
+
if (!url || url.trim() === '') return false
|
|
85
|
+
try {
|
|
86
|
+
new URL(url)
|
|
87
|
+
return true
|
|
88
|
+
} catch {
|
|
89
|
+
return false
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import config from 'discovery.config'
|
|
2
|
+
import { matchesBindingPath } from 'src/utils/localization/bindingPaths'
|
|
3
|
+
|
|
4
|
+
function shouldSkipHostnameCheck(hostname: string): boolean {
|
|
5
|
+
const isDevelopment = process.env.NODE_ENV === 'development'
|
|
6
|
+
const isLocalhost = hostname === 'localhost' || hostname === '127.0.0.1'
|
|
7
|
+
const isPreviewUrl = hostname.endsWith('.vtex.app')
|
|
8
|
+
|
|
9
|
+
return isLocalhost || isPreviewUrl || isDevelopment
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function matchURLBinding(href: string) {
|
|
13
|
+
let binding
|
|
14
|
+
const matchedConfig = Object.entries(config.localization.locales).find(
|
|
15
|
+
([_, langConfig]: [string, any]) => {
|
|
16
|
+
const hostURLObject = new URL(href)
|
|
17
|
+
const skipHostname = shouldSkipHostnameCheck(hostURLObject.hostname)
|
|
18
|
+
|
|
19
|
+
binding = langConfig.bindings.find(({ url }: { url: string }) => {
|
|
20
|
+
const configURLObject = new URL(url)
|
|
21
|
+
|
|
22
|
+
const hostnameMatch =
|
|
23
|
+
skipHostname || hostURLObject.hostname === configURLObject.hostname
|
|
24
|
+
|
|
25
|
+
if (!hostnameMatch) {
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Verify protocol matches for security, but skip on environments
|
|
30
|
+
// where the hostname is already relaxed (localhost, preview deploys).
|
|
31
|
+
if (!skipHostname) {
|
|
32
|
+
const protocolMatch =
|
|
33
|
+
hostURLObject.protocol === configURLObject.protocol
|
|
34
|
+
|
|
35
|
+
if (!protocolMatch) {
|
|
36
|
+
return false
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// If the config pathname is the root, it is a subdomain binding
|
|
41
|
+
// and we don't need to check if pathname matches.
|
|
42
|
+
const configPathname =
|
|
43
|
+
configURLObject.pathname.replace(/\/$/, '') || '/'
|
|
44
|
+
const isSubdomainBinding = configPathname === '/'
|
|
45
|
+
|
|
46
|
+
// Subdomain bindings rely on hostname, so when the hostname check
|
|
47
|
+
// is skipped we can only match path-based bindings.
|
|
48
|
+
if (isSubdomainBinding) {
|
|
49
|
+
return !skipHostname
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Check if pathname matches (exact or prefix match)
|
|
53
|
+
return matchesBindingPath(
|
|
54
|
+
hostURLObject.pathname,
|
|
55
|
+
configURLObject.pathname
|
|
56
|
+
)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
return !!binding
|
|
60
|
+
}
|
|
61
|
+
)?.[1]
|
|
62
|
+
|
|
63
|
+
return { config: matchedConfig, binding }
|
|
64
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface Binding {
|
|
2
|
+
currencyCode: string
|
|
3
|
+
url: string
|
|
4
|
+
salesChannel: string
|
|
5
|
+
/**
|
|
6
|
+
* When true, this binding is preferred for its locale in two cases:
|
|
7
|
+
* (1) When the URL does not match any binding, the default locale's binding
|
|
8
|
+
* with isDefault is used for session/settings.
|
|
9
|
+
* (2) When resolving locale+currency and multiple bindings match (e.g. same
|
|
10
|
+
* currency, different URLs), the one with isDefault is chosen.
|
|
11
|
+
* Each locale should have exactly one binding with isDefault: true to avoid
|
|
12
|
+
* ambiguous resolution.
|
|
13
|
+
*/
|
|
14
|
+
isDefault: boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface Locale {
|
|
18
|
+
code: string
|
|
19
|
+
name: string
|
|
20
|
+
languageCode: string
|
|
21
|
+
languageName: string
|
|
22
|
+
script: string
|
|
23
|
+
textDirection: 'ltr' | 'rtl'
|
|
24
|
+
regionCode: string
|
|
25
|
+
bindings: Binding[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type BindingSelectorError =
|
|
29
|
+
| { type: 'no-binding-found'; locale: string; currency: string }
|
|
30
|
+
| { type: 'invalid-url'; url: string }
|
|
31
|
+
| { type: 'no-currencies'; locale: string }
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { useCallback, useMemo, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import storeConfig from '../../../discovery.config'
|
|
4
|
+
import { buildRedirectUrl } from '../../utils/localization/bindingPaths'
|
|
5
|
+
import { useSession } from '../session'
|
|
6
|
+
import {
|
|
7
|
+
buildLanguageOptions,
|
|
8
|
+
getCurrenciesForLocale,
|
|
9
|
+
isValidUrl,
|
|
10
|
+
resolveBinding,
|
|
11
|
+
} from './bindingSelector'
|
|
12
|
+
import type { BindingSelectorError, Locale } from './types'
|
|
13
|
+
|
|
14
|
+
interface Currency {
|
|
15
|
+
code: string
|
|
16
|
+
name: string
|
|
17
|
+
symbol: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface Region {
|
|
21
|
+
code: string
|
|
22
|
+
name: string
|
|
23
|
+
dateFormat: string
|
|
24
|
+
timeFormat: string
|
|
25
|
+
timeFormatMask: string
|
|
26
|
+
unitSystem: string
|
|
27
|
+
defaultTimezone: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface LocalizationConfig {
|
|
31
|
+
defaultLocale: string
|
|
32
|
+
regions: Record<string, Region>
|
|
33
|
+
locales: Record<string, Locale>
|
|
34
|
+
currencies: Record<string, Currency>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Hook return type for useBindingSelector.
|
|
39
|
+
*/
|
|
40
|
+
export interface UseBindingSelectorReturn {
|
|
41
|
+
/** Language options as { localeCode: languageName } for UISelectField */
|
|
42
|
+
languages: Record<string, string>
|
|
43
|
+
/** Currency options as { currencyCode: currencyCode } for UISelectField */
|
|
44
|
+
currencies: Record<string, string>
|
|
45
|
+
/** Selected locale code (e.g., "pt-BR") */
|
|
46
|
+
localeCode: string | null
|
|
47
|
+
/** Selected currency code (e.g., "BRL") */
|
|
48
|
+
currencyCode: string | null
|
|
49
|
+
/** True when both locale and currency are selected and no error */
|
|
50
|
+
isSaveEnabled: boolean
|
|
51
|
+
/** Current error state, if any */
|
|
52
|
+
error: BindingSelectorError | null
|
|
53
|
+
/** Action to set the locale code (e.g., "pt-BR") */
|
|
54
|
+
setLocaleCode: (code: string) => void
|
|
55
|
+
/** Action to set the currency code */
|
|
56
|
+
setCurrencyCode: (code: string) => void
|
|
57
|
+
/** Action to save selections and redirect */
|
|
58
|
+
save: () => void
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Hook that provides state and actions for the localization selector.
|
|
63
|
+
* Manages locale selection, currency filtering, and binding resolution.
|
|
64
|
+
*
|
|
65
|
+
* @returns Object with languages, currencies, selections, and actions
|
|
66
|
+
*/
|
|
67
|
+
export function useBindingSelector(): UseBindingSelectorReturn {
|
|
68
|
+
const { locale: currentLocale, currency: currentCurrency } = useSession()
|
|
69
|
+
const localizationConfig = storeConfig.localization as LocalizationConfig
|
|
70
|
+
|
|
71
|
+
// Pre-select current locale code and currency code from session
|
|
72
|
+
const [localeCode, setLocaleCode] = useState<string | null>(
|
|
73
|
+
() => currentLocale ?? null
|
|
74
|
+
)
|
|
75
|
+
const [currencyCode, setCurrencyCode] = useState<string | null>(
|
|
76
|
+
() => currentCurrency?.code ?? null
|
|
77
|
+
)
|
|
78
|
+
const [error, setError] = useState<BindingSelectorError | null>(null)
|
|
79
|
+
|
|
80
|
+
// Build language options with disambiguation - returns Record<localeCode, languageName>
|
|
81
|
+
const languages = useMemo(
|
|
82
|
+
() => buildLanguageOptions(localizationConfig.locales),
|
|
83
|
+
[localizationConfig.locales]
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
// Filter currencies based on selected locale
|
|
87
|
+
const currencies = useMemo(() => {
|
|
88
|
+
if (!localeCode || !localizationConfig.locales[localeCode]) {
|
|
89
|
+
return {}
|
|
90
|
+
}
|
|
91
|
+
const currencyList = getCurrenciesForLocale(
|
|
92
|
+
localizationConfig.locales[localeCode]
|
|
93
|
+
)
|
|
94
|
+
// Convert array to Record<string, string> for UISelectField
|
|
95
|
+
return Object.fromEntries(currencyList.map((code) => [code, code]))
|
|
96
|
+
}, [localeCode, localizationConfig.locales])
|
|
97
|
+
|
|
98
|
+
// Handle locale code change
|
|
99
|
+
const handleSetLocaleCode = useCallback(
|
|
100
|
+
(code: string) => {
|
|
101
|
+
setLocaleCode(code)
|
|
102
|
+
setError(null)
|
|
103
|
+
|
|
104
|
+
// Check if current currency is available in new locale
|
|
105
|
+
const locale = localizationConfig.locales[code]
|
|
106
|
+
if (locale) {
|
|
107
|
+
const availableCurrencies = getCurrenciesForLocale(locale)
|
|
108
|
+
|
|
109
|
+
if (availableCurrencies.length === 0) {
|
|
110
|
+
setError({ type: 'no-currencies', locale: code })
|
|
111
|
+
setCurrencyCode(null)
|
|
112
|
+
} else {
|
|
113
|
+
const currencyStillValid =
|
|
114
|
+
currencyCode && availableCurrencies.includes(currencyCode)
|
|
115
|
+
if (!currencyStillValid) {
|
|
116
|
+
// Auto-select first currency if only one available, otherwise clear
|
|
117
|
+
setCurrencyCode(
|
|
118
|
+
availableCurrencies.length === 1 ? availableCurrencies[0] : null
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
[localizationConfig.locales, currencyCode]
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
// Handle currency code change
|
|
128
|
+
const handleSetCurrencyCode = useCallback((code: string) => {
|
|
129
|
+
setCurrencyCode(code)
|
|
130
|
+
setError(null)
|
|
131
|
+
}, [])
|
|
132
|
+
|
|
133
|
+
// Save and redirect
|
|
134
|
+
const save = useCallback(() => {
|
|
135
|
+
if (!localeCode || !currencyCode) return
|
|
136
|
+
|
|
137
|
+
const locale = localizationConfig.locales[localeCode]
|
|
138
|
+
if (!locale) {
|
|
139
|
+
setError({
|
|
140
|
+
type: 'no-binding-found',
|
|
141
|
+
locale: localeCode,
|
|
142
|
+
currency: currencyCode,
|
|
143
|
+
})
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const binding = resolveBinding(locale.bindings, currencyCode)
|
|
148
|
+
|
|
149
|
+
if (!binding) {
|
|
150
|
+
setError({
|
|
151
|
+
type: 'no-binding-found',
|
|
152
|
+
locale: localeCode,
|
|
153
|
+
currency: currencyCode,
|
|
154
|
+
})
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!isValidUrl(binding.url)) {
|
|
159
|
+
setError({ type: 'invalid-url', url: binding.url })
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Redirect to binding URL, preserving the current page path and query string
|
|
164
|
+
window.location.href = buildRedirectUrl(
|
|
165
|
+
binding.url,
|
|
166
|
+
`${window.location.pathname}${window.location.search}${window.location.hash}`
|
|
167
|
+
)
|
|
168
|
+
}, [localeCode, currencyCode, localizationConfig.locales])
|
|
169
|
+
|
|
170
|
+
const isSaveEnabled = Boolean(localeCode && currencyCode && !error)
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
languages,
|
|
174
|
+
currencies,
|
|
175
|
+
localeCode,
|
|
176
|
+
currencyCode,
|
|
177
|
+
isSaveEnabled,
|
|
178
|
+
error,
|
|
179
|
+
setLocaleCode: handleSetLocaleCode,
|
|
180
|
+
setCurrencyCode: handleSetCurrencyCode,
|
|
181
|
+
save,
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useRouter } from 'next/router'
|
|
2
|
+
import { useEffect } from 'react'
|
|
3
|
+
|
|
4
|
+
import storeConfig from 'discovery.config'
|
|
5
|
+
import { validateLocaleForHostname } from 'src/utils/localization/validateLocaleForHostname'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Client-side validation hook for locale binding.
|
|
9
|
+
*
|
|
10
|
+
* This hook validates that the current locale matches the hostname binding
|
|
11
|
+
* configuration. It acts as a fallback for static pages that cannot perform
|
|
12
|
+
* server-side validation.
|
|
13
|
+
*
|
|
14
|
+
* If the locale is invalid for the current hostname, the user is redirected to the 404 page.
|
|
15
|
+
*
|
|
16
|
+
* Only runs if localization feature is enabled (localization.enabled === true).
|
|
17
|
+
*/
|
|
18
|
+
export function useLocaleValidation() {
|
|
19
|
+
const router = useRouter()
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
// Skip validation if localization feature is not enabled
|
|
23
|
+
if (!storeConfig.localization?.enabled) {
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Skip validation if we're already on the 404 page (from SSR notFound)
|
|
28
|
+
if (router.pathname === '/404') {
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const hostname = window.location.hostname
|
|
33
|
+
const locale = router.locale
|
|
34
|
+
|
|
35
|
+
if (!locale) {
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const isValid = validateLocaleForHostname(hostname, locale)
|
|
41
|
+
|
|
42
|
+
if (!isValid) {
|
|
43
|
+
window.location.href = `/404?from=${encodeURIComponent(`/${locale}${router.asPath}`)}`
|
|
44
|
+
}
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.warn('Client-side locale validation failed:', error)
|
|
47
|
+
}
|
|
48
|
+
}, [router.locale, router.asPath, router.defaultLocale, router.pathname])
|
|
49
|
+
}
|