@faststore/core 3.99.0 → 4.0.0-dev.1
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 +12 -2
- 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 +18 -9
- 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/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.23bae2c79f0ac0f3.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.de15325248043ce5.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-dbc0d9f67d2f928f.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/static/es3Z4hhE6B4Nut0ZqqUuB/_buildManifest.js +0 -1
- package/.next/static/es3Z4hhE6B4Nut0ZqqUuB/_ssgManifest.js +0 -1
- package/.next/trace +0 -143
- package/.turbo/turbo-build.log +0 -134
- 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
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import storeConfig from 'discovery.config'
|
|
2
|
+
import type { LocalesSettings } from 'src/typings/locales'
|
|
3
|
+
import { filterChannel } from 'src/utils/utilities'
|
|
4
|
+
|
|
5
|
+
export type CustomPathInfo = {
|
|
6
|
+
path: string
|
|
7
|
+
locale: string
|
|
8
|
+
hostname?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type SubdomainBinding = {
|
|
12
|
+
hostname: string
|
|
13
|
+
locale: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type BindingsByType = {
|
|
17
|
+
customPaths: CustomPathInfo[]
|
|
18
|
+
subdomainBindings: SubdomainBinding[]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let bindingsByTypeCache: BindingsByType | null = null
|
|
22
|
+
|
|
23
|
+
/** Returns path only (strip query ? and hash #) for prefix detection and building */
|
|
24
|
+
function getPathOnly(pathOrLink: string): string {
|
|
25
|
+
return pathOrLink.split('?')[0].split('#')[0]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Checks if an incoming path matches a binding path.
|
|
30
|
+
* Supports exact match and prefix matching for paths with additional segments.
|
|
31
|
+
* @param incomingPath - The path from the URL being checked (e.g., '/europe/it/products')
|
|
32
|
+
* @param bindingPath - The path from the binding configuration (e.g., '/europe/it')
|
|
33
|
+
* @returns true if they match (exact or prefix match)
|
|
34
|
+
* @example
|
|
35
|
+
* matchesBindingPath('/europe/it', '/europe/it') // true (exact match)
|
|
36
|
+
* matchesBindingPath('/europe/it/products', '/europe/it') // true (prefix match)
|
|
37
|
+
* matchesBindingPath('/europe/item', '/europe/it') // false (not a match)
|
|
38
|
+
* matchesBindingPath('/pt-BR/office', '/pt-BR') // true (prefix match)
|
|
39
|
+
* matchesBindingPath('/', '/') // true (root match)
|
|
40
|
+
*/
|
|
41
|
+
export function matchesBindingPath(
|
|
42
|
+
incomingPath: string,
|
|
43
|
+
bindingPath: string
|
|
44
|
+
): boolean {
|
|
45
|
+
// Normalize paths (remove trailing slash, but keep '/' for root)
|
|
46
|
+
const normalizedIncoming =
|
|
47
|
+
incomingPath.replace(/\/$/, '').toLowerCase() || '/'
|
|
48
|
+
const normalizedBinding = bindingPath.replace(/\/$/, '').toLowerCase() || '/'
|
|
49
|
+
|
|
50
|
+
// Root path only matches exact root
|
|
51
|
+
if (normalizedBinding === '/') {
|
|
52
|
+
return normalizedIncoming === '/'
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// For non-root paths, match exact or prefix
|
|
56
|
+
return (
|
|
57
|
+
normalizedIncoming === normalizedBinding ||
|
|
58
|
+
normalizedIncoming.startsWith(`${normalizedBinding}/`)
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Checks whether a locale code exists in the localization configuration.
|
|
64
|
+
*/
|
|
65
|
+
export function isValidLocale(locale: string): boolean {
|
|
66
|
+
if (!storeConfig.localization?.enabled) {
|
|
67
|
+
return false
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return locale in (storeConfig.localization.locales || {})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Single pass over all locale bindings, classifying each as either a
|
|
75
|
+
* custom-path binding (has a non-root pathname) or a subdomain binding
|
|
76
|
+
* (root pathname — hostname alone determines the locale).
|
|
77
|
+
* Results are cached after the first call.
|
|
78
|
+
*/
|
|
79
|
+
function getBindingsByType(): BindingsByType {
|
|
80
|
+
if (bindingsByTypeCache !== null) {
|
|
81
|
+
return bindingsByTypeCache
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const customPaths: CustomPathInfo[] = []
|
|
85
|
+
const subdomainBindings: SubdomainBinding[] = []
|
|
86
|
+
|
|
87
|
+
if (!storeConfig.localization?.enabled) {
|
|
88
|
+
bindingsByTypeCache = { customPaths, subdomainBindings }
|
|
89
|
+
return bindingsByTypeCache
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const locales = (storeConfig.localization.locales ||
|
|
93
|
+
{}) as LocalesSettings['locales']
|
|
94
|
+
const localeCodes = new Set(Object.keys(locales))
|
|
95
|
+
|
|
96
|
+
for (const [localeCode, localeConfig] of Object.entries(locales)) {
|
|
97
|
+
if (!localeConfig?.bindings || !Array.isArray(localeConfig.bindings)) {
|
|
98
|
+
continue
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
for (const binding of localeConfig.bindings) {
|
|
102
|
+
if (!binding.url) {
|
|
103
|
+
continue
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const urlObj = new URL(binding.url)
|
|
108
|
+
const pathname = urlObj.pathname.replace(/\/$/, '') || '/'
|
|
109
|
+
|
|
110
|
+
if (pathname === '/') {
|
|
111
|
+
subdomainBindings.push({
|
|
112
|
+
hostname: urlObj.hostname,
|
|
113
|
+
locale: localeCode,
|
|
114
|
+
})
|
|
115
|
+
} else if (!localeCodes.has(pathname.slice(1))) {
|
|
116
|
+
customPaths.push({
|
|
117
|
+
path: pathname,
|
|
118
|
+
locale: localeCode,
|
|
119
|
+
hostname: urlObj.hostname,
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
} catch {
|
|
123
|
+
console.warn(
|
|
124
|
+
`[bindingPaths] Skipping invalid binding URL: "${binding.url}"`
|
|
125
|
+
)
|
|
126
|
+
continue
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
customPaths.sort((a, b) => b.path.length - a.path.length)
|
|
132
|
+
|
|
133
|
+
bindingsByTypeCache = { customPaths, subdomainBindings }
|
|
134
|
+
return bindingsByTypeCache
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Returns bindings that use a custom URL path (non-root pathname).
|
|
139
|
+
* Sorted by path length descending (most specific first).
|
|
140
|
+
*/
|
|
141
|
+
export function getCustomPathsFromBindings(): CustomPathInfo[] {
|
|
142
|
+
return getBindingsByType().customPaths
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Returns bindings where the hostname alone determines the locale
|
|
147
|
+
* (root pathname '/').
|
|
148
|
+
*/
|
|
149
|
+
export function getSubdomainBindings(): SubdomainBinding[] {
|
|
150
|
+
return getBindingsByType().subdomainBindings
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Extracts custom path prefix from current pathname
|
|
155
|
+
* @param pathname - Current pathname (e.g., '/europe/it/apparel')
|
|
156
|
+
* @returns Custom path prefix if found, null otherwise
|
|
157
|
+
*/
|
|
158
|
+
export function extractCustomPathPrefix(pathname: string): string | null {
|
|
159
|
+
const pathOnly = getPathOnly(pathname)
|
|
160
|
+
const customPaths = getCustomPathsFromBindings()
|
|
161
|
+
|
|
162
|
+
for (const { path } of customPaths) {
|
|
163
|
+
if (matchesBindingPath(pathOnly, path)) {
|
|
164
|
+
return path
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return null
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Adds custom path prefix to a link if needed
|
|
173
|
+
* @param link - The link href (e.g., '/apparel' or '/europe/it/apparel')
|
|
174
|
+
* @param currentPathname - Current pathname (e.g., '/europe/it/apparel')
|
|
175
|
+
* @returns Link with prefix if needed (e.g., '/europe/it/apparel')
|
|
176
|
+
*/
|
|
177
|
+
export function addCustomPathPrefix(
|
|
178
|
+
link: string,
|
|
179
|
+
currentPathname: string
|
|
180
|
+
): string {
|
|
181
|
+
if (!storeConfig.localization?.enabled) {
|
|
182
|
+
return link
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (!link.startsWith('/')) {
|
|
186
|
+
return link
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (extractCustomPathPrefix(link) !== null) {
|
|
190
|
+
return link
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const prefix = extractCustomPathPrefix(currentPathname)
|
|
194
|
+
|
|
195
|
+
if (!prefix) {
|
|
196
|
+
return link
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const pathOnly = getPathOnly(link)
|
|
200
|
+
const queryAndHash = link.slice(pathOnly.length)
|
|
201
|
+
return `${prefix}${pathOnly}${queryAndHash}`
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Collects all binding URL paths from the locales config (both locale-code
|
|
206
|
+
* paths like "/pt-BR" and custom paths like "/europe/it").
|
|
207
|
+
* Returns deduplicated paths sorted by length descending (most specific first).
|
|
208
|
+
*
|
|
209
|
+
* Unlike `getCustomPathsFromBindings`, this does NOT filter out locale-code paths.
|
|
210
|
+
*
|
|
211
|
+
* @returns Array of binding path strings sorted longest-first
|
|
212
|
+
*/
|
|
213
|
+
export function collectAllBindingPaths(): string[] {
|
|
214
|
+
if (!storeConfig.localization?.enabled) {
|
|
215
|
+
return []
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const locales = (storeConfig.localization.locales ||
|
|
219
|
+
{}) as LocalesSettings['locales']
|
|
220
|
+
const pathSet = new Set<string>()
|
|
221
|
+
|
|
222
|
+
for (const localeConfig of Object.values(locales)) {
|
|
223
|
+
if (!localeConfig?.bindings || !Array.isArray(localeConfig.bindings)) {
|
|
224
|
+
continue
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
for (const binding of localeConfig.bindings) {
|
|
228
|
+
if (!binding.url) {
|
|
229
|
+
continue
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
const pathname = new URL(binding.url).pathname.replace(/\/$/, '')
|
|
234
|
+
|
|
235
|
+
if (pathname && pathname !== '/') {
|
|
236
|
+
pathSet.add(pathname)
|
|
237
|
+
}
|
|
238
|
+
} catch {
|
|
239
|
+
console.warn(
|
|
240
|
+
`[bindingPaths] Skipping invalid binding URL: "${binding.url}"`
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return Array.from(pathSet).sort((a, b) => b.length - a.length)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Strips the current binding path prefix from `currentPathname`, returning the
|
|
251
|
+
* remaining "page path" (e.g. "/products/item").
|
|
252
|
+
*
|
|
253
|
+
* @param currentPathname - The full browser pathname (e.g., "/pt-BR/products/item")
|
|
254
|
+
* @returns The page path after stripping the matched prefix, or the full pathname if none matches
|
|
255
|
+
*/
|
|
256
|
+
export function getPagePath(currentPathname: string): string {
|
|
257
|
+
const pathOnly = getPathOnly(currentPathname)
|
|
258
|
+
const bindingPaths = collectAllBindingPaths()
|
|
259
|
+
|
|
260
|
+
for (const bp of bindingPaths) {
|
|
261
|
+
if (matchesBindingPath(pathOnly, bp)) {
|
|
262
|
+
const normalizedBp = bp.replace(/\/$/, '').toLowerCase()
|
|
263
|
+
const normalizedPath = pathOnly.replace(/\/$/, '').toLowerCase()
|
|
264
|
+
|
|
265
|
+
if (normalizedPath === normalizedBp) {
|
|
266
|
+
return ''
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const prefixEnd =
|
|
270
|
+
normalizedPath.indexOf(normalizedBp) === 0 ? bp.length : 0
|
|
271
|
+
|
|
272
|
+
return prefixEnd > 0 ? pathOnly.slice(prefixEnd) : pathOnly
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return pathOnly
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Resolves the channel string for a given locale by looking up its
|
|
281
|
+
* binding's salesChannel. Falls back to the default session channel when the
|
|
282
|
+
* locale is unknown or localization is disabled.
|
|
283
|
+
*
|
|
284
|
+
* The returned string is a JSON channel object (same format used by
|
|
285
|
+
* `storeConfig.session.channel`) with `hasOnlyDefaultSalesChannel` stripped so
|
|
286
|
+
* it can be passed directly as a `channel` facet/locator value.
|
|
287
|
+
*/
|
|
288
|
+
export function getChannelForLocale(locale: string | undefined): string {
|
|
289
|
+
const defaultChannel = storeConfig.session.channel
|
|
290
|
+
|
|
291
|
+
if (!locale || !storeConfig.localization?.enabled) {
|
|
292
|
+
return filterChannel(defaultChannel)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const localeConfig = (storeConfig.localization.locales ?? {})[locale]
|
|
296
|
+
|
|
297
|
+
if (!localeConfig?.bindings?.length) {
|
|
298
|
+
return filterChannel(defaultChannel)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const binding =
|
|
302
|
+
localeConfig.bindings.find((b) => b.isDefault) ?? localeConfig.bindings[0]
|
|
303
|
+
|
|
304
|
+
if (!binding?.salesChannel) {
|
|
305
|
+
return filterChannel(defaultChannel)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
try {
|
|
309
|
+
const channelObj = JSON.parse(defaultChannel)
|
|
310
|
+
const updatedChannel = { ...channelObj, salesChannel: binding.salesChannel }
|
|
311
|
+
return filterChannel(JSON.stringify(updatedChannel))
|
|
312
|
+
} catch {
|
|
313
|
+
console.warn('[bindingPaths] Failed to parse channel')
|
|
314
|
+
return filterChannel(defaultChannel)
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Builds a redirect URL by combining the target binding URL with the current
|
|
320
|
+
* page path and query string.
|
|
321
|
+
*
|
|
322
|
+
* @param bindingUrl - The new binding's base URL (e.g., "http://localhost:3000/en-US")
|
|
323
|
+
* @param currentUrl - The current page URL path including query string (e.g., "/pt-BR/products/item?color=red")
|
|
324
|
+
* @returns The full redirect URL preserving the page path and query string
|
|
325
|
+
*/
|
|
326
|
+
export function buildRedirectUrl(
|
|
327
|
+
bindingUrl: string,
|
|
328
|
+
currentUrl: string
|
|
329
|
+
): string {
|
|
330
|
+
const pathOnly = getPathOnly(currentUrl)
|
|
331
|
+
const suffix = currentUrl.slice(pathOnly.length)
|
|
332
|
+
|
|
333
|
+
const pagePath = getPagePath(pathOnly)
|
|
334
|
+
const baseUrl = bindingUrl.replace(/\/$/, '')
|
|
335
|
+
|
|
336
|
+
return `${baseUrl}${pagePath}${suffix}`
|
|
337
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import storeConfig from 'discovery.config.default.js'
|
|
2
|
+
import type { LocalesSettings } from '../../typings/locales'
|
|
3
|
+
import { getRequestHostname } from '../getRequestHostname'
|
|
4
|
+
|
|
5
|
+
function getLocalesSettings(): LocalesSettings {
|
|
6
|
+
return storeConfig.localization as LocalesSettings
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A binding is valid if the hostname matches AND the locale code matches.
|
|
11
|
+
* This works for both:
|
|
12
|
+
* - Path-based bindings (e.g., "https://brandless.fast.store/pt-BR")
|
|
13
|
+
* - Subdomain bindings (e.g., "https://canada.brandless.fast.store" for "fr-CA")
|
|
14
|
+
*
|
|
15
|
+
* @param hostname - The request hostname
|
|
16
|
+
* @param localeCode - The locale code to check
|
|
17
|
+
* @returns true if there's a valid binding for this hostname and locale, false otherwise
|
|
18
|
+
*/
|
|
19
|
+
function hasValidBindingForHostname(
|
|
20
|
+
hostname: string,
|
|
21
|
+
localeCode: string
|
|
22
|
+
): boolean {
|
|
23
|
+
const localesSettings = getLocalesSettings()
|
|
24
|
+
const localeData = localesSettings.locales[localeCode]
|
|
25
|
+
|
|
26
|
+
if (!localeData?.bindings) {
|
|
27
|
+
return false
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return localeData.bindings.some((binding) => {
|
|
31
|
+
if (!binding.url) return false
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const bindingUrl = new URL(binding.url)
|
|
35
|
+
|
|
36
|
+
return bindingUrl.hostname === hostname
|
|
37
|
+
} catch {
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Validates if a locale has a valid binding for the hostname.
|
|
45
|
+
*
|
|
46
|
+
* @param hostname - The request hostname
|
|
47
|
+
* @param locale - The locale code (e.g., "fr-CA")
|
|
48
|
+
* @returns true if the locale/hostname combination is valid, false otherwise
|
|
49
|
+
*/
|
|
50
|
+
export function validateLocaleForHostname(
|
|
51
|
+
hostname: string,
|
|
52
|
+
locale: string
|
|
53
|
+
): boolean {
|
|
54
|
+
const normalizedHostname = getRequestHostname(hostname)
|
|
55
|
+
|
|
56
|
+
if (!normalizedHostname) {
|
|
57
|
+
return false
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// For localhost, allow all locales (useful for development)
|
|
61
|
+
if (
|
|
62
|
+
normalizedHostname === 'localhost' ||
|
|
63
|
+
normalizedHostname === '127.0.0.1'
|
|
64
|
+
) {
|
|
65
|
+
return true
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return hasValidBindingForHostname(normalizedHostname, locale)
|
|
69
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { GetServerSidePropsContext, GetServerSidePropsResult } from 'next'
|
|
2
|
+
import { validateLocaleForHostname } from './validateLocaleForHostname'
|
|
3
|
+
|
|
4
|
+
type ServerPropsHandler<P> = (
|
|
5
|
+
context: GetServerSidePropsContext
|
|
6
|
+
) => Promise<GetServerSidePropsResult<P>>
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Higher Order Function that validates locale binding before executing getServerSideProps
|
|
10
|
+
* @param getServerSidePropsFn - The original getServerSideProps function
|
|
11
|
+
* @returns getServerSideProps function with locale binding validation
|
|
12
|
+
*/
|
|
13
|
+
export function withLocaleValidationSSR<P extends Record<string, any>>(
|
|
14
|
+
getServerSidePropsFn: ServerPropsHandler<P>
|
|
15
|
+
): ServerPropsHandler<P> {
|
|
16
|
+
return async (context: GetServerSidePropsContext) => {
|
|
17
|
+
const { locale, req } = context
|
|
18
|
+
|
|
19
|
+
const hostname = req.headers.host || ''
|
|
20
|
+
|
|
21
|
+
if (!locale) {
|
|
22
|
+
// If there's no locale in context, execute original function
|
|
23
|
+
return getServerSidePropsFn(context)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const isValid = validateLocaleForHostname(hostname, locale)
|
|
27
|
+
|
|
28
|
+
if (!isValid) {
|
|
29
|
+
return {
|
|
30
|
+
notFound: true,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return getServerSidePropsFn(context)
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/utils/utilities.ts
CHANGED
|
@@ -109,6 +109,28 @@ export const buildFormData = (
|
|
|
109
109
|
export const toArray = <T>(x: T[] | T | undefined) =>
|
|
110
110
|
Array.isArray(x) ? x : x ? [x] : []
|
|
111
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Filters the channel object by removing the hasOnlyDefaultSalesChannel key.
|
|
114
|
+
* This key is used only in the useAuth hook and is only required to send on the ValidateSession mutation,
|
|
115
|
+
* so we remove it from the session's channel object to avoid unnecessary cache invalidations and query executions
|
|
116
|
+
*
|
|
117
|
+
* @param channel - The channel string to filter.
|
|
118
|
+
* @returns The filtered channel string.
|
|
119
|
+
*/
|
|
120
|
+
export function filterChannel(channel: string): string {
|
|
121
|
+
if (!channel) {
|
|
122
|
+
return '{}'
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
const { hasOnlyDefaultSalesChannel, ...filteredChannel } =
|
|
126
|
+
JSON.parse(channel)
|
|
127
|
+
return JSON.stringify(filteredChannel)
|
|
128
|
+
} catch {
|
|
129
|
+
console.warn('[filterChannel] Invalid channel JSON:', channel)
|
|
130
|
+
return channel
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
112
134
|
/**
|
|
113
135
|
* Formats the file name to a more user-friendly version.
|
|
114
136
|
* @param fileName - The original file name.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { NextApiRequest, NextApiResponse } from 'next'
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import handler from '../../../src/pages/api/preview'
|
|
5
|
+
import { contentService } from '../../../src/server/content/service'
|
|
6
|
+
|
|
7
|
+
const createResponse = () => {
|
|
8
|
+
const res = {
|
|
9
|
+
clearPreviewData: vi.fn(),
|
|
10
|
+
setPreviewData: vi.fn(),
|
|
11
|
+
redirect: vi.fn(),
|
|
12
|
+
status: vi.fn().mockReturnThis(),
|
|
13
|
+
end: vi.fn(),
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return res as unknown as NextApiResponse & typeof res
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe('/api/preview', () => {
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
vi.restoreAllMocks()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('stores the requested locale in the preview session data', async () => {
|
|
25
|
+
vi.spyOn(contentService, 'getSingleContent').mockResolvedValue({} as never)
|
|
26
|
+
|
|
27
|
+
const req = {
|
|
28
|
+
query: {
|
|
29
|
+
contentType: 'home',
|
|
30
|
+
documentId: 'entry-1',
|
|
31
|
+
versionId: 'branch-1',
|
|
32
|
+
locale: 'pt-BR',
|
|
33
|
+
},
|
|
34
|
+
} as unknown as NextApiRequest
|
|
35
|
+
const res = createResponse()
|
|
36
|
+
|
|
37
|
+
await handler(req, res)
|
|
38
|
+
|
|
39
|
+
expect(contentService.getSingleContent).toHaveBeenCalledWith({
|
|
40
|
+
contentType: 'home',
|
|
41
|
+
documentId: 'entry-1',
|
|
42
|
+
versionId: 'branch-1',
|
|
43
|
+
previewData: {
|
|
44
|
+
contentType: 'home',
|
|
45
|
+
documentId: 'entry-1',
|
|
46
|
+
versionId: 'branch-1',
|
|
47
|
+
},
|
|
48
|
+
locale: 'pt-BR',
|
|
49
|
+
releaseId: undefined,
|
|
50
|
+
slug: undefined,
|
|
51
|
+
})
|
|
52
|
+
expect(res.setPreviewData).toHaveBeenCalledWith(
|
|
53
|
+
{
|
|
54
|
+
contentType: 'home',
|
|
55
|
+
documentId: 'entry-1',
|
|
56
|
+
versionId: 'branch-1',
|
|
57
|
+
locale: 'pt-BR',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
maxAge: 3600,
|
|
61
|
+
path: '/',
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
expect(res.redirect).toHaveBeenCalledWith('/')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('keeps preview session data locator-only when locale is absent', async () => {
|
|
68
|
+
vi.spyOn(contentService, 'getSingleContent').mockResolvedValue({} as never)
|
|
69
|
+
|
|
70
|
+
const req = {
|
|
71
|
+
query: {
|
|
72
|
+
contentType: 'home',
|
|
73
|
+
documentId: 'entry-1',
|
|
74
|
+
versionId: 'branch-1',
|
|
75
|
+
},
|
|
76
|
+
} as unknown as NextApiRequest
|
|
77
|
+
const res = createResponse()
|
|
78
|
+
|
|
79
|
+
await handler(req, res)
|
|
80
|
+
|
|
81
|
+
expect(res.setPreviewData).toHaveBeenCalledWith(
|
|
82
|
+
{
|
|
83
|
+
contentType: 'home',
|
|
84
|
+
documentId: 'entry-1',
|
|
85
|
+
versionId: 'branch-1',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
maxAge: 3600,
|
|
89
|
+
path: '/',
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
})
|
|
93
|
+
})
|