@faststore/core 3.99.1 → 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.
Files changed (543) hide show
  1. package/.turbo/turbo-generate.log +29 -0
  2. package/.turbo/turbo-test.log +47 -16
  3. package/@generated/cached-operations.json +24 -0
  4. package/@generated/gql.ts +134 -210
  5. package/@generated/graphql.ts +1628 -3037
  6. package/@generated/index.ts +1 -1
  7. package/@generated/persisted-documents.json +1 -1
  8. package/@generated/schema.graphql +1854 -1743
  9. package/CHANGELOG.md +11 -5
  10. package/README.md +2 -1
  11. package/api/index.ts +6 -7
  12. package/cms/faststore/base.jsonc +1 -1
  13. package/cms/faststore/components/cms_component__alert.jsonc +5 -1
  14. package/cms/faststore/components/cms_component__bannernewsletter.jsonc +5 -1
  15. package/cms/faststore/components/cms_component__bannertext.jsonc +5 -1
  16. package/cms/faststore/components/cms_component__breadcrumb.jsonc +16 -4
  17. package/cms/faststore/components/cms_component__cartsidebar.jsonc +21 -1
  18. package/cms/faststore/components/cms_component__children.jsonc +5 -1
  19. package/cms/faststore/components/cms_component__crosssellingshelf.jsonc +5 -1
  20. package/cms/faststore/components/cms_component__emptystate.jsonc +8 -3
  21. package/cms/faststore/components/cms_component__footer.jsonc +9 -3
  22. package/cms/faststore/components/cms_component__hero.jsonc +5 -1
  23. package/cms/faststore/components/cms_component__incentives.jsonc +5 -1
  24. package/cms/faststore/components/cms_component__navbar.jsonc +139 -1
  25. package/cms/faststore/components/cms_component__newsletter.jsonc +5 -1
  26. package/cms/faststore/components/cms_component__productdetails.jsonc +33 -1
  27. package/cms/faststore/components/cms_component__productgallery.jsonc +113 -15
  28. package/cms/faststore/components/cms_component__productshelf.jsonc +5 -1
  29. package/cms/faststore/components/cms_component__producttiles.jsonc +5 -1
  30. package/cms/faststore/components/cms_component__regionbar.jsonc +5 -1
  31. package/cms/faststore/components/cms_component__regionmodal.jsonc +5 -1
  32. package/cms/faststore/components/cms_component__regionpopover.jsonc +5 -1
  33. package/cms/faststore/components/cms_component__scrolltotopbutton.jsonc +29 -0
  34. package/cms/faststore/components/cms_component__search.jsonc +5 -1
  35. package/cms/faststore/content-types.json +115 -0
  36. package/cms/faststore/pages/cms_content_type__globalsections.jsonc +52 -0
  37. package/cms/faststore/pages/cms_content_type__home.jsonc +6 -0
  38. package/cms/faststore/pages/cms_content_type__landingpage.jsonc +8 -1
  39. package/cms/faststore/pages/cms_content_type__pdp.jsonc +6 -0
  40. package/cms/faststore/pages/cms_content_type__plp.jsonc +17 -0
  41. package/cms/faststore/pages/cms_content_type__search.jsonc +17 -0
  42. package/cms/faststore/schema.json +547 -43
  43. package/cms/faststore/sections.json +330 -21
  44. package/discovery.config.default.js +49 -3
  45. package/next-env.d.ts +2 -1
  46. package/next.config.js +48 -26
  47. package/package.json +47 -60
  48. package/public/icons.svg +5 -1
  49. package/scripts/i18n.js +76 -0
  50. package/src/components/account/MyAccountDrawer/OrganizationDrawer/OrganizationDrawer.tsx +2 -1
  51. package/src/components/account/MyAccountDrawer/OrganizationDrawer/section.module.scss +25 -24
  52. package/src/components/account/MyAccountDrawer/ProfileSummary/profile-summary.scss +8 -6
  53. package/src/components/account/MyAccountLayout/styles.scss +16 -12
  54. package/src/components/account/MyAccountMenu/styles.scss +21 -22
  55. package/src/components/account/MyAccountUserDetails/styles.module.scss +4 -2
  56. package/src/components/account/components/MyAccountAccordion/styles.scss +4 -2
  57. package/src/components/account/components/MyAccountHeader/styles.scss +3 -1
  58. package/src/components/account/components/MyAccountTable/styles.scss +6 -4
  59. package/src/components/account/orders/MyAccountListOrders/MyAccountFilterSlider/section.module.scss +21 -19
  60. package/src/components/account/orders/MyAccountListOrders/MyAccountListOrdersTable/MyAccountListOrdersTable.tsx +3 -1
  61. package/src/components/account/orders/MyAccountListOrders/MyAccountListOrdersTable/styles.scss +5 -3
  62. package/src/components/account/orders/MyAccountListOrders/styles.module.scss +25 -22
  63. package/src/components/account/orders/MyAccountOrderDetails/MyAccountBudgetsCard/styles.scss +7 -5
  64. package/src/components/account/orders/MyAccountOrderDetails/MyAccountDeliveryOptionAccordion/styles.scss +7 -5
  65. package/src/components/account/orders/MyAccountOrderDetails/MyAccountMoreInformationCard/styles.scss +3 -1
  66. package/src/components/account/orders/MyAccountOrderDetails/MyAccountOrderActionModal/styles.module.scss +8 -6
  67. package/src/components/account/orders/MyAccountOrderDetails/MyAccountOrderActions/styles.scss +3 -1
  68. package/src/components/account/orders/MyAccountOrderDetails/MyAccountStatusCard/styles.scss +8 -6
  69. package/src/components/account/orders/MyAccountOrderDetails/section.module.scss +36 -43
  70. package/src/components/account/profile/profile.module.scss +3 -1
  71. package/src/components/account/section.module.scss +3 -1
  72. package/src/components/account/security/styles.module.scss +14 -8
  73. package/src/components/cart/CartSidebar/CartSidebar.tsx +10 -1
  74. package/src/components/cart/CartSidebar/section.module.scss +19 -17
  75. package/src/components/cart/EmptyCart/EmptyCart.tsx +12 -4
  76. package/src/components/cms/GlobalSections.tsx +12 -12
  77. package/src/components/cms/RenderSections.tsx +10 -2
  78. package/src/components/cms/plp/Components.ts +9 -0
  79. package/src/components/common/Alert/section.module.scss +10 -5
  80. package/src/components/common/PreviewTag/section.module.scss +48 -40
  81. package/src/components/common/Toast/section.module.scss +4 -2
  82. package/src/components/localization/LocalizationSelector/LocalizationSelector.tsx +320 -0
  83. package/src/components/localization/LocalizationSelector/index.ts +1 -0
  84. package/src/components/localization/LocalizationSelector/section.module.scss +18 -0
  85. package/src/components/localization/index.ts +1 -0
  86. package/src/components/navigation/Navbar/Navbar.tsx +49 -1
  87. package/src/components/navigation/NavbarSlider/NavbarSlider.tsx +19 -1
  88. package/src/components/navigation/NavbarSlider/section.module.scss +12 -10
  89. package/src/components/product/OutOfStock/OutOfStock.tsx +6 -1
  90. package/src/components/product/ProductCard/ProductCard.tsx +1 -1
  91. package/src/components/region/RegionModal/RegionModal.tsx +4 -1
  92. package/src/components/region/RegionModal/section.module.scss +10 -8
  93. package/src/components/region/RegionPopover/RegionPopover.tsx +6 -5
  94. package/src/components/region/RegionPopover/section.module.scss +9 -7
  95. package/src/components/region/RegionSlider/RegionSlider.tsx +5 -2
  96. package/src/components/region/RegionSlider/section.module.scss +16 -13
  97. package/src/components/search/Filter/FilterDeliveryMethodFacet.tsx +5 -5
  98. package/src/components/search/Filter/FilterDesktop.tsx +13 -4
  99. package/src/components/search/Filter/FilterSlider.tsx +18 -6
  100. package/src/components/search/Filter/section.module.scss +21 -19
  101. package/src/components/search/SearchDropdown/SearchDropdown.tsx +9 -7
  102. package/src/components/search/SearchHistory/SearchHistory.tsx +7 -2
  103. package/src/components/search/SearchInput/SearchInput.tsx +21 -8
  104. package/src/components/search/SearchProductItem/SearchProductItem.tsx +10 -3
  105. package/src/components/search/SearchTop/SearchTop.tsx +30 -24
  106. package/src/components/search/Sort/Sort.tsx +17 -19
  107. package/src/components/sections/Alert/Alert.tsx +6 -1
  108. package/src/components/sections/BannerNewsletter/section.module.scss +6 -4
  109. package/src/components/sections/BannerText/section.module.scss +5 -3
  110. package/src/components/sections/Breadcrumb/Breadcrumb.tsx +6 -2
  111. package/src/components/sections/Breadcrumb/section.module.scss +7 -5
  112. package/src/components/sections/EmptyState/section.module.scss +5 -3
  113. package/src/components/sections/Footer/Footer.tsx +1 -5
  114. package/src/components/sections/Footer/section.module.scss +10 -8
  115. package/src/components/sections/Hero/Hero.tsx +3 -1
  116. package/src/components/sections/Hero/section.module.scss +6 -4
  117. package/src/components/sections/Incentives/section.module.scss +5 -3
  118. package/src/components/sections/Navbar/Navbar.tsx +45 -0
  119. package/src/components/sections/Navbar/section.module.scss +34 -33
  120. package/src/components/sections/Newsletter/section.module.scss +10 -8
  121. package/src/components/sections/ProductDetails/ProductDetails.tsx +23 -1
  122. package/src/components/sections/ProductDetails/section.module.scss +32 -30
  123. package/src/components/sections/ProductGallery/EmptyGallery.tsx +13 -16
  124. package/src/components/sections/ProductGallery/section.module.scss +42 -39
  125. package/src/components/sections/ProductShelf/section.module.scss +22 -17
  126. package/src/components/sections/ProductTiles/section.module.scss +15 -13
  127. package/src/components/sections/RegionBar/section.module.scss +5 -3
  128. package/src/components/sections/ScrollToTopButton/ScrollToTopButton.tsx +4 -3
  129. package/src/components/sections/ScrollToTopButton/section.module.scss +4 -2
  130. package/src/components/templates/LandingPage/LandingPage.tsx +22 -16
  131. package/src/components/templates/ProductListingPage/ProductListing.tsx +1 -13
  132. package/src/components/templates/ProductListingPage/ProductListingPage.tsx +8 -3
  133. package/src/components/ui/Breadcrumb/Breadcrumb.tsx +14 -11
  134. package/src/components/ui/Button/ButtonSignIn/ButtonSignIn.tsx +2 -1
  135. package/src/components/ui/Button/ButtonSignIn/ButtonSignInFallback/ButtonSignInFallback.tsx +3 -1
  136. package/src/components/ui/Link/Link.tsx +19 -2
  137. package/src/components/ui/LinkButton/LinkButton.tsx +28 -0
  138. package/src/components/ui/LinkButton/index.ts +2 -0
  139. package/src/components/ui/LocalizationButton/LocalizationButton.tsx +124 -0
  140. package/src/components/ui/LocalizationButton/index.ts +1 -0
  141. package/src/components/ui/PickupPoints/PickupPointCards.tsx +2 -4
  142. package/src/components/ui/ProductComparison/ProductComparisonSidebar.tsx +34 -17
  143. package/src/components/ui/ProductDescription/ProductDescription.tsx +8 -3
  144. package/src/components/ui/ProductDetails/AddToCartLoadingSkeleton.tsx +6 -2
  145. package/src/components/ui/ProductDetails/ProductDetailsSettings.tsx +17 -5
  146. package/src/components/ui/ProductGallery/ProductGallery.tsx +7 -3
  147. package/src/components/ui/ProductGallery/ProductGalleryPage.tsx +1 -3
  148. package/src/components/ui/ShippingSimulation/ShippingSimulation.tsx +6 -1
  149. package/src/customizations/src/styles/custom-mixins.scss +0 -2
  150. package/src/experimental/index.ts +26 -0
  151. package/src/experimental/myAccountServerSideProps.ts +11 -2
  152. package/src/experimental/searchServerSideFunctions/getServerSideProps.ts +13 -7
  153. package/src/experimental/searchServerSideFunctions/getStaticProps.ts +5 -4
  154. package/src/instrumentation.ts +20 -6
  155. package/src/pages/404.tsx +5 -4
  156. package/src/pages/500.tsx +5 -4
  157. package/src/pages/[...slug].tsx +7 -4
  158. package/src/pages/[slug]/p.tsx +26 -8
  159. package/src/pages/_app.tsx +9 -1
  160. package/src/pages/api/fs/logout.ts +3 -2
  161. package/src/pages/api/graphql.ts +7 -23
  162. package/src/pages/api/preview.ts +21 -8
  163. package/src/pages/checkout.tsx +15 -5
  164. package/src/pages/index.tsx +18 -9
  165. package/src/pages/login.tsx +5 -4
  166. package/src/pages/pvt/account/403.tsx +11 -2
  167. package/src/pages/pvt/account/404.tsx +13 -3
  168. package/src/pages/pvt/account/index.tsx +6 -4
  169. package/src/pages/pvt/account/orders/[id].tsx +11 -3
  170. package/src/pages/pvt/account/orders/index.tsx +11 -3
  171. package/src/pages/pvt/account/profile.tsx +11 -2
  172. package/src/pages/pvt/account/security.tsx +11 -2
  173. package/src/pages/pvt/account/user-details.tsx +11 -2
  174. package/src/pages/s.tsx +26 -6
  175. package/src/proxy.ts +154 -0
  176. package/src/sdk/account/refreshToken.ts +4 -4
  177. package/src/sdk/account/useReorder.ts +8 -2
  178. package/src/sdk/analytics/platform/vtex/search.ts +3 -2
  179. package/src/sdk/cart/redirectToCheckout.ts +14 -8
  180. package/src/sdk/cart/useCheckoutButton.ts +7 -1
  181. package/src/sdk/deliveryPromise/useDeliveryPromise.ts +10 -18
  182. package/src/sdk/graphql/request.ts +102 -50
  183. package/src/sdk/localization/bindingSelector.ts +91 -0
  184. package/src/sdk/localization/index.ts +4 -0
  185. package/src/sdk/localization/match-url.ts +64 -0
  186. package/src/sdk/localization/types.ts +31 -0
  187. package/src/sdk/localization/useBindingSelector.ts +183 -0
  188. package/src/sdk/localization/useLocaleValidation.ts +49 -0
  189. package/src/sdk/localization/useLocalizationConfig.tsx +160 -0
  190. package/src/sdk/offer/fetcher.ts +3 -2
  191. package/src/sdk/product/useLocalizedVariables.ts +1 -1
  192. package/src/sdk/product/usePageProductsQuery.ts +2 -2
  193. package/src/sdk/product/useProductGalleryQuery.ts +1 -2
  194. package/src/sdk/product/useProductLink.ts +3 -1
  195. package/src/sdk/search/formatSearchPath.ts +28 -3
  196. package/src/sdk/search/state.ts +22 -13
  197. package/src/sdk/search/useSearchBase.ts +10 -0
  198. package/src/sdk/search/useSearchHistory.ts +41 -7
  199. package/src/sdk/session/index.ts +21 -3
  200. package/src/sdk/shipping/useShippingSimulation.ts +10 -2
  201. package/src/sdk/tsconfig.json +6 -0
  202. package/src/sdk/ui/useLink.ts +48 -0
  203. package/src/server/cms/global.ts +40 -0
  204. package/src/server/cms/index.ts +18 -10
  205. package/src/server/cms/pdp.ts +1 -0
  206. package/src/server/cms/plp.ts +3 -0
  207. package/src/server/content/service.ts +36 -24
  208. package/src/server/content/types.ts +10 -3
  209. package/src/server/index.ts +27 -20
  210. package/src/server/options.ts +27 -2
  211. package/src/styles/global/index.scss +10 -7
  212. package/src/styles/main.scss +3 -3
  213. package/src/typings/locales.ts +41 -0
  214. package/src/utils/fetchProductGallerySSR.ts +3 -1
  215. package/src/utils/getBaseDomain.ts +2 -1
  216. package/src/utils/getRequestHostname.ts +26 -0
  217. package/src/utils/globalSettings.ts +11 -0
  218. package/src/utils/localization/bindingPaths.ts +337 -0
  219. package/src/utils/localization/validateLocaleForHostname.ts +69 -0
  220. package/src/utils/localization/withLocaleValidation.ts +36 -0
  221. package/src/utils/utilities.ts +22 -0
  222. package/test/pages/api/preview.test.ts +93 -0
  223. package/test/sdk/localization/bindingSelector.test.ts +246 -0
  224. package/test/sdk/localization/store-url.browser.test.ts +140 -0
  225. package/test/sdk/localization/store-url.test.ts +96 -0
  226. package/test/sdk/localization/useBindingSelector.test.tsx +219 -0
  227. package/test/sdk/search/useSearchHistory.test.ts +222 -0
  228. package/test/server/cms/index.test.ts +3 -4
  229. package/test/server/content/service.test.ts +98 -0
  230. package/test/server/index.test.ts +10 -22
  231. package/test/utils/cookieCacheBusting.test.ts +32 -24
  232. package/test/utils/getRequestHostname.test.ts +71 -0
  233. package/test/utils/localization/bindingPaths.test.ts +706 -0
  234. package/test/utils/match-url.test.ts +388 -0
  235. package/tsconfig.json +11 -3
  236. package/vitest.config.ts +61 -0
  237. package/.next/BUILD_ID +0 -1
  238. package/.next/build-manifest.json +0 -197
  239. package/.next/cache/.tsbuildinfo +0 -1
  240. package/.next/cache/config.json +0 -7
  241. package/.next/cache/webpack/client-production/0.pack +0 -0
  242. package/.next/cache/webpack/client-production/index.pack +0 -0
  243. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  244. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  245. package/.next/cache/webpack/server-production/0.pack +0 -0
  246. package/.next/cache/webpack/server-production/index.pack +0 -0
  247. package/.next/export-marker.json +0 -1
  248. package/.next/images-manifest.json +0 -1
  249. package/.next/next-minimal-server.js.nft.json +0 -1
  250. package/.next/next-server.js.nft.json +0 -1
  251. package/.next/package.json +0 -1
  252. package/.next/prerender-manifest.js +0 -1
  253. package/.next/prerender-manifest.json +0 -1
  254. package/.next/react-loadable-manifest.json +0 -478
  255. package/.next/required-server-files.json +0 -1
  256. package/.next/routes-manifest.json +0 -1
  257. package/.next/server/chunks/1280.js +0 -1
  258. package/.next/server/chunks/1454.js +0 -1
  259. package/.next/server/chunks/1554.js +0 -1
  260. package/.next/server/chunks/1607.js +0 -1
  261. package/.next/server/chunks/1780.js +0 -1
  262. package/.next/server/chunks/1917.js +0 -1
  263. package/.next/server/chunks/1972.js +0 -1
  264. package/.next/server/chunks/2230.js +0 -1
  265. package/.next/server/chunks/2249.js +0 -1
  266. package/.next/server/chunks/2430.js +0 -1
  267. package/.next/server/chunks/2445.js +0 -12
  268. package/.next/server/chunks/2570.js +0 -1
  269. package/.next/server/chunks/2792.js +0 -1
  270. package/.next/server/chunks/2903.js +0 -1
  271. package/.next/server/chunks/294.js +0 -1
  272. package/.next/server/chunks/2955.js +0 -1
  273. package/.next/server/chunks/3006.js +0 -1
  274. package/.next/server/chunks/3029.js +0 -1
  275. package/.next/server/chunks/3060.js +0 -1
  276. package/.next/server/chunks/3408.js +0 -1
  277. package/.next/server/chunks/3483.js +0 -1
  278. package/.next/server/chunks/3683.js +0 -1
  279. package/.next/server/chunks/3836.js +0 -1
  280. package/.next/server/chunks/3890.js +0 -494
  281. package/.next/server/chunks/3922.js +0 -1
  282. package/.next/server/chunks/3945.js +0 -1
  283. package/.next/server/chunks/3951.js +0 -7
  284. package/.next/server/chunks/416.js +0 -1
  285. package/.next/server/chunks/4194.js +0 -1
  286. package/.next/server/chunks/4365.js +0 -1
  287. package/.next/server/chunks/4451.js +0 -1
  288. package/.next/server/chunks/4803.js +0 -1
  289. package/.next/server/chunks/4913.js +0 -13
  290. package/.next/server/chunks/5402.js +0 -1
  291. package/.next/server/chunks/5723.js +0 -13
  292. package/.next/server/chunks/5796.js +0 -1
  293. package/.next/server/chunks/6393.js +0 -1
  294. package/.next/server/chunks/6457.js +0 -6
  295. package/.next/server/chunks/6698.js +0 -1
  296. package/.next/server/chunks/6804.js +0 -6
  297. package/.next/server/chunks/6886.js +0 -1
  298. package/.next/server/chunks/7098.js +0 -9
  299. package/.next/server/chunks/7169.js +0 -1
  300. package/.next/server/chunks/7228.js +0 -1
  301. package/.next/server/chunks/7275.js +0 -1
  302. package/.next/server/chunks/7371.js +0 -1
  303. package/.next/server/chunks/7692.js +0 -1
  304. package/.next/server/chunks/7799.js +0 -1
  305. package/.next/server/chunks/8068.js +0 -1
  306. package/.next/server/chunks/83.js +0 -1
  307. package/.next/server/chunks/831.js +0 -1
  308. package/.next/server/chunks/8563.js +0 -1
  309. package/.next/server/chunks/8569.js +0 -1
  310. package/.next/server/chunks/8687.js +0 -1
  311. package/.next/server/chunks/870.js +0 -1
  312. package/.next/server/chunks/8737.js +0 -1
  313. package/.next/server/chunks/8741.js +0 -1
  314. package/.next/server/chunks/9088.js +0 -1
  315. package/.next/server/chunks/9117.js +0 -1
  316. package/.next/server/chunks/9237.js +0 -1
  317. package/.next/server/chunks/948.js +0 -3
  318. package/.next/server/chunks/9563.js +0 -3
  319. package/.next/server/chunks/9570.js +0 -1
  320. package/.next/server/chunks/9740.js +0 -1
  321. package/.next/server/chunks/9853.js +0 -1
  322. package/.next/server/chunks/9984.js +0 -1
  323. package/.next/server/chunks/9985.js +0 -1
  324. package/.next/server/chunks/9990.js +0 -1
  325. package/.next/server/chunks/ButtonSignIn.js +0 -1
  326. package/.next/server/chunks/Dropdown.js +0 -1
  327. package/.next/server/chunks/DropdownButton.js +0 -1
  328. package/.next/server/chunks/DropdownItem.js +0 -1
  329. package/.next/server/chunks/DropdownMenu.js +0 -1
  330. package/.next/server/chunks/FilterSkeleton.js +0 -1
  331. package/.next/server/chunks/ScrollToTopButton.js +0 -1
  332. package/.next/server/chunks/UIBannerText.js +0 -1
  333. package/.next/server/chunks/UISKUMatrixSidebar.js +0 -1
  334. package/.next/server/chunks/font-manifest.json +0 -1
  335. package/.next/server/edge-instrumentation.js +0 -2
  336. package/.next/server/edge-instrumentation.js.map +0 -1
  337. package/.next/server/edge-runtime-webpack.js +0 -157
  338. package/.next/server/edge-runtime-webpack.js.map +0 -1
  339. package/.next/server/font-manifest.json +0 -1
  340. package/.next/server/functions-config-manifest.json +0 -1
  341. package/.next/server/instrumentation.js +0 -1
  342. package/.next/server/instrumentation.js.nft.json +0 -1
  343. package/.next/server/middleware-build-manifest.js +0 -1
  344. package/.next/server/middleware-manifest.json +0 -6
  345. package/.next/server/middleware-react-loadable-manifest.js +0 -1
  346. package/.next/server/next-font-manifest.js +0 -1
  347. package/.next/server/next-font-manifest.json +0 -1
  348. package/.next/server/pages/404.js +0 -1
  349. package/.next/server/pages/404.js.nft.json +0 -1
  350. package/.next/server/pages/500.js +0 -1
  351. package/.next/server/pages/500.js.nft.json +0 -1
  352. package/.next/server/pages/[...slug].js +0 -1
  353. package/.next/server/pages/[...slug].js.nft.json +0 -1
  354. package/.next/server/pages/[slug]/p.js +0 -1
  355. package/.next/server/pages/[slug]/p.js.nft.json +0 -1
  356. package/.next/server/pages/_app.js +0 -1
  357. package/.next/server/pages/_app.js.nft.json +0 -1
  358. package/.next/server/pages/_document.js +0 -1
  359. package/.next/server/pages/_document.js.nft.json +0 -1
  360. package/.next/server/pages/_error.js +0 -1
  361. package/.next/server/pages/_error.js.nft.json +0 -1
  362. package/.next/server/pages/api/fs/logout.js +0 -1
  363. package/.next/server/pages/api/fs/logout.js.nft.json +0 -1
  364. package/.next/server/pages/api/graphql.js +0 -3
  365. package/.next/server/pages/api/graphql.js.nft.json +0 -1
  366. package/.next/server/pages/api/health/live.js +0 -1
  367. package/.next/server/pages/api/health/live.js.nft.json +0 -1
  368. package/.next/server/pages/api/health/ready.js +0 -1
  369. package/.next/server/pages/api/health/ready.js.nft.json +0 -1
  370. package/.next/server/pages/api/preview.js +0 -1
  371. package/.next/server/pages/api/preview.js.nft.json +0 -1
  372. package/.next/server/pages/checkout.js +0 -1
  373. package/.next/server/pages/checkout.js.nft.json +0 -1
  374. package/.next/server/pages/en-US/404.html +0 -42
  375. package/.next/server/pages/en-US/404.json +0 -1
  376. package/.next/server/pages/en-US/500.html +0 -42
  377. package/.next/server/pages/en-US/500.json +0 -1
  378. package/.next/server/pages/en-US/checkout.html +0 -42
  379. package/.next/server/pages/en-US/checkout.json +0 -1
  380. package/.next/server/pages/en-US/login.html +0 -42
  381. package/.next/server/pages/en-US/login.json +0 -1
  382. package/.next/server/pages/en-US/s.html +0 -42
  383. package/.next/server/pages/en-US/s.json +0 -1
  384. package/.next/server/pages/en-US.html +0 -42
  385. package/.next/server/pages/en-US.json +0 -1
  386. package/.next/server/pages/index.js +0 -1
  387. package/.next/server/pages/index.js.nft.json +0 -1
  388. package/.next/server/pages/login.js +0 -1
  389. package/.next/server/pages/login.js.nft.json +0 -1
  390. package/.next/server/pages/pvt/account/403.js +0 -1
  391. package/.next/server/pages/pvt/account/403.js.nft.json +0 -1
  392. package/.next/server/pages/pvt/account/404.js +0 -1
  393. package/.next/server/pages/pvt/account/404.js.nft.json +0 -1
  394. package/.next/server/pages/pvt/account/[...unknown].js +0 -1
  395. package/.next/server/pages/pvt/account/[...unknown].js.nft.json +0 -1
  396. package/.next/server/pages/pvt/account/orders/[id].js +0 -1
  397. package/.next/server/pages/pvt/account/orders/[id].js.nft.json +0 -1
  398. package/.next/server/pages/pvt/account/orders.js +0 -1
  399. package/.next/server/pages/pvt/account/orders.js.nft.json +0 -1
  400. package/.next/server/pages/pvt/account/profile.js +0 -1
  401. package/.next/server/pages/pvt/account/profile.js.nft.json +0 -1
  402. package/.next/server/pages/pvt/account/security.js +0 -1
  403. package/.next/server/pages/pvt/account/security.js.nft.json +0 -1
  404. package/.next/server/pages/pvt/account/user-details.js +0 -1
  405. package/.next/server/pages/pvt/account/user-details.js.nft.json +0 -1
  406. package/.next/server/pages/pvt/account.js +0 -1
  407. package/.next/server/pages/pvt/account.js.nft.json +0 -1
  408. package/.next/server/pages/s.js +0 -1
  409. package/.next/server/pages/s.js.nft.json +0 -1
  410. package/.next/server/pages-manifest.json +0 -1
  411. package/.next/server/webpack-api-runtime.js +0 -1
  412. package/.next/server/webpack-runtime.js +0 -1
  413. package/.next/static/NAuY8lqaeBBZf9CMZuYI2/_buildManifest.js +0 -1
  414. package/.next/static/NAuY8lqaeBBZf9CMZuYI2/_ssgManifest.js +0 -1
  415. package/.next/static/chunks/227-f109b24190a008ab.js +0 -8
  416. package/.next/static/chunks/2284.185d834d9829b652.js +0 -1
  417. package/.next/static/chunks/2851.e68ed3c8d27a032a.js +0 -1
  418. package/.next/static/chunks/2927.5a79877943a6bf7c.js +0 -1
  419. package/.next/static/chunks/3155.7bc4c730a79d9ecb.js +0 -1
  420. package/.next/static/chunks/3166-0b30a3887c0c346d.js +0 -1
  421. package/.next/static/chunks/3399.017c5209b74b0d97.js +0 -1
  422. package/.next/static/chunks/3483.ef53d632a763da3f.js +0 -1
  423. package/.next/static/chunks/349.b3b38e1e9f719dda.js +0 -1
  424. package/.next/static/chunks/3802.5e8b0b689eacc0f8.js +0 -1
  425. package/.next/static/chunks/3836.620480018be48233.js +0 -1
  426. package/.next/static/chunks/4436.e7af66f0b654ee36.js +0 -1
  427. package/.next/static/chunks/4803.b1920b6e3a1a6304.js +0 -1
  428. package/.next/static/chunks/5796.4351370494d8b8b6.js +0 -1
  429. package/.next/static/chunks/6393.55f991b1a6bb4fe7.js +0 -1
  430. package/.next/static/chunks/6700.b23221a57514e737.js +0 -1
  431. package/.next/static/chunks/6789.960162355435a81d.js +0 -1
  432. package/.next/static/chunks/7191-befad64ba238a817.js +0 -1
  433. package/.next/static/chunks/7692.8d5bf4560341a2f6.js +0 -1
  434. package/.next/static/chunks/7861.7f9f1a124a43da30.js +0 -6
  435. package/.next/static/chunks/8068.43663fb64762f7b8.js +0 -1
  436. package/.next/static/chunks/83.e0c16f0299b364a5.js +0 -1
  437. package/.next/static/chunks/8325.dabe9aa22eaadeed.js +0 -1
  438. package/.next/static/chunks/8587.53c8ce185ed1a2cb.js +0 -1
  439. package/.next/static/chunks/9173-e89da04079c35360.js +0 -2
  440. package/.next/static/chunks/9237.7555603703f81bf7.js +0 -1
  441. package/.next/static/chunks/9298-1d1ed0a54133cb59.js +0 -28
  442. package/.next/static/chunks/9399.d116a7fd62024783.js +0 -1
  443. package/.next/static/chunks/941.3e2782ab9c490eb0.js +0 -1
  444. package/.next/static/chunks/9701.f9a0bfffba8807dc.js +0 -1
  445. package/.next/static/chunks/9909.787576b171184aa5.js +0 -1
  446. package/.next/static/chunks/9960.7ef77f8a8b046170.js +0 -1
  447. package/.next/static/chunks/9979.aedbc094bdc2c599.js +0 -1
  448. package/.next/static/chunks/BannerNewsletter.00328ad162109908.js +0 -1
  449. package/.next/static/chunks/BannerText.dbbec87cadb8a521.js +0 -1
  450. package/.next/static/chunks/ButtonSignIn.f6e210eaca873e48.js +0 -1
  451. package/.next/static/chunks/CartItem.b34e295377f6f94f.js +0 -1
  452. package/.next/static/chunks/CartSidebar.ac447d1ef6276da1.js +0 -1
  453. package/.next/static/chunks/Dropdown.38b48c749bad6b60.js +0 -1
  454. package/.next/static/chunks/DropdownButton.66fa6b25dddb3a07.js +0 -1
  455. package/.next/static/chunks/DropdownItem.38b8f5a07c7f422a.js +0 -1
  456. package/.next/static/chunks/DropdownMenu.48b11521a24c6efd.js +0 -1
  457. package/.next/static/chunks/EmptyCart.320e2f94a0d5e6b1.js +0 -1
  458. package/.next/static/chunks/FilterSkeleton.7ab99e28f92341c9.js +0 -1
  459. package/.next/static/chunks/Footer.166b02c2f13980c6.js +0 -1
  460. package/.next/static/chunks/Gift.c49ba26da2fbab5e.js +0 -1
  461. package/.next/static/chunks/Newsletter.fc406f6029057022.js +0 -1
  462. package/.next/static/chunks/OrderSummary.5957d69f990d9add.js +0 -1
  463. package/.next/static/chunks/PreviewTag.f4f0c8710a7bcad9.js +0 -1
  464. package/.next/static/chunks/ProductShelf.cc46f5cdeeaeaff6.js +0 -1
  465. package/.next/static/chunks/ProductTiles.9b804e51b0704d60.js +0 -1
  466. package/.next/static/chunks/RegionModal.319ad44250cb12d2.js +0 -1
  467. package/.next/static/chunks/RegionSlider.0e0e93369f4f9c92.js +0 -1
  468. package/.next/static/chunks/ScrollToTopButton.c15431f2e8bb93ef.js +0 -1
  469. package/.next/static/chunks/ShoppingAssistant.ba191eb4dbed67e5.js +0 -1
  470. package/.next/static/chunks/Toast.5f90f0436708be5e.js +0 -1
  471. package/.next/static/chunks/UIBannerText.7315b16af4457059.js +0 -1
  472. package/.next/static/chunks/UISKUMatrixSidebar.b4349b2682bbe183.js +0 -1
  473. package/.next/static/chunks/UIToast.19a8664c01a00d3a.js +0 -1
  474. package/.next/static/chunks/framework-d514426edf885c68.js +0 -33
  475. package/.next/static/chunks/main-ec03882c4375091d.js +0 -1
  476. package/.next/static/chunks/pages/404-e3dfbbac003b02a2.js +0 -1
  477. package/.next/static/chunks/pages/500-c7814e085081d7b8.js +0 -1
  478. package/.next/static/chunks/pages/[...slug]-dc8a395a43b081f1.js +0 -1
  479. package/.next/static/chunks/pages/[slug]/p-644f1132f5a0f736.js +0 -1
  480. package/.next/static/chunks/pages/_app-84aaff8a9d4de60e.js +0 -1
  481. package/.next/static/chunks/pages/_error-2b0148be56a716e9.js +0 -1
  482. package/.next/static/chunks/pages/checkout-2071467ad42dfb99.js +0 -1
  483. package/.next/static/chunks/pages/index-8774d5b4a8744eb2.js +0 -1
  484. package/.next/static/chunks/pages/login-e74747c9f9cb5b49.js +0 -1
  485. package/.next/static/chunks/pages/pvt/account/403-491e6b062487b30f.js +0 -1
  486. package/.next/static/chunks/pages/pvt/account/404-5ad472d9f2ee1980.js +0 -1
  487. package/.next/static/chunks/pages/pvt/account/[...unknown]-f80f645594d2740c.js +0 -1
  488. package/.next/static/chunks/pages/pvt/account/orders/[id]-64e42157c0974c48.js +0 -1
  489. package/.next/static/chunks/pages/pvt/account/orders-faab39f2aff05fae.js +0 -1
  490. package/.next/static/chunks/pages/pvt/account/profile-9672177ee2665bc0.js +0 -1
  491. package/.next/static/chunks/pages/pvt/account/security-03eb1ccab9a30563.js +0 -1
  492. package/.next/static/chunks/pages/pvt/account/user-details-7838e3f79781596c.js +0 -1
  493. package/.next/static/chunks/pages/pvt/account-65fefcc699344bdb.js +0 -1
  494. package/.next/static/chunks/pages/s-162ba789e6611175.js +0 -1
  495. package/.next/static/chunks/polyfills-c67a75d1b6f99dc8.js +0 -1
  496. package/.next/static/chunks/webpack-f621ff3e951d2982.js +0 -1
  497. package/.next/static/css/02259c549b2179f2.css +0 -1
  498. package/.next/static/css/02eaf202d3d11fd1.css +0 -1
  499. package/.next/static/css/033e241c58ba6fbb.css +0 -1
  500. package/.next/static/css/042d012bc1895b5c.css +0 -1
  501. package/.next/static/css/14176a4704ff97b2.css +0 -1
  502. package/.next/static/css/146a8678babc61bf.css +0 -1
  503. package/.next/static/css/16d65422bfa80f51.css +0 -1
  504. package/.next/static/css/1c4983f11b861ea0.css +0 -1
  505. package/.next/static/css/204754bbd8cc4bef.css +0 -1
  506. package/.next/static/css/24d91007fc96be10.css +0 -1
  507. package/.next/static/css/2980acad3f8e1028.css +0 -1
  508. package/.next/static/css/2c19433e38fd7533.css +0 -1
  509. package/.next/static/css/31380ebc6e671486.css +0 -1
  510. package/.next/static/css/42df884711219edb.css +0 -1
  511. package/.next/static/css/4f0597b0bf0ef2e1.css +0 -1
  512. package/.next/static/css/54cedf4b65e5863f.css +0 -1
  513. package/.next/static/css/6580147d7ab8fcca.css +0 -1
  514. package/.next/static/css/6819dddf2b1b5ea7.css +0 -1
  515. package/.next/static/css/7ef7762f412ed665.css +0 -1
  516. package/.next/static/css/82f3cd529b672812.css +0 -1
  517. package/.next/static/css/837662922091162f.css +0 -1
  518. package/.next/static/css/8e6ab59ace21e03b.css +0 -1
  519. package/.next/static/css/a852afa9be31e946.css +0 -1
  520. package/.next/static/css/b684b347c5cc6514.css +0 -1
  521. package/.next/static/css/bcdaef0a83277ef5.css +0 -1
  522. package/.next/static/css/c33615999008d3f9.css +0 -1
  523. package/.next/static/css/c34f6ea9953e2f63.css +0 -1
  524. package/.next/static/css/ea11d74a2848b093.css +0 -1
  525. package/.next/static/css/f16438bcf4508fb3.css +0 -1
  526. package/.next/static/css/f60366555f563deb.css +0 -1
  527. package/.next/trace +0 -143
  528. package/.turbo/turbo-build.log +0 -140
  529. package/jest.config.js +0 -17
  530. package/public/~partytown/debug/partytown-atomics.js +0 -556
  531. package/public/~partytown/debug/partytown-media.js +0 -374
  532. package/public/~partytown/debug/partytown-sandbox-sw.js +0 -543
  533. package/public/~partytown/debug/partytown-sw.js +0 -59
  534. package/public/~partytown/debug/partytown-ww-atomics.js +0 -1789
  535. package/public/~partytown/debug/partytown-ww-sw.js +0 -1781
  536. package/public/~partytown/debug/partytown.js +0 -72
  537. package/public/~partytown/partytown-atomics.js +0 -2
  538. package/public/~partytown/partytown-media.js +0 -2
  539. package/public/~partytown/partytown-sw.js +0 -2
  540. package/public/~partytown/partytown.js +0 -2
  541. package/src/middleware__DISABLED.ts +0 -65
  542. package/src/server/generator/generateGraphQLSchemaFile.ts +0 -3
  543. 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<D = any> {
9
- data: D
10
- errors: any[]
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 DEFAULT_HEADERS_BY_VERB: Record<string, Record<string, string>> = {
22
- POST: {
23
- 'Content-Type': 'application/json',
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
- return data
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 !== undefined
60
- ? fetchOptions.method.toUpperCase()
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
- ...(method === 'GET' && { variables: JSON.stringify(variables) }),
69
- ...(method === 'GET' && value && { v: value }),
64
+ variables: JSON.stringify(variables),
65
+ ...(value && { v: value }),
70
66
  })
71
67
 
72
- const body =
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
- const response = await fetch(url, {
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
- ...DEFAULT_HEADERS_BY_VERB[method],
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<D>
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
- return response.json()
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,4 @@
1
+ export type { Binding, BindingSelectorError, Locale } from './types'
2
+ export { useBindingSelector } from './useBindingSelector'
3
+ export type { UseBindingSelectorReturn } from './useBindingSelector'
4
+ export { useLocaleValidation } from './useLocaleValidation'
@@ -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
+ }