@salla.sa/twilight-theme-engine 1.0.139 → 1.0.140

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 (220) hide show
  1. package/dist/{AddToCartForm-QGJ2XVCZ.js → AddToCartForm-EPTN5O3X.js} +3 -3
  2. package/dist/{AddToCartForm-QGJ2XVCZ.js.map → AddToCartForm-EPTN5O3X.js.map} +1 -1
  3. package/dist/{BlogAuthorPage-5QGP4RDK.js → BlogAuthorPage-PGFINJSP.js} +16 -16
  4. package/dist/{BlogAuthorPage-5QGP4RDK.js.map → BlogAuthorPage-PGFINJSP.js.map} +1 -1
  5. package/dist/{BlogCategoryPage-FH7ASETM.js → BlogCategoryPage-ZVRRHTKJ.js} +16 -16
  6. package/dist/{BlogCategoryPage-FH7ASETM.js.map → BlogCategoryPage-ZVRRHTKJ.js.map} +1 -1
  7. package/dist/{BlogPage-HNNR2Q4U.js → BlogPage-5SRGR7NK.js} +14 -14
  8. package/dist/{BlogPage-HNNR2Q4U.js.map → BlogPage-5SRGR7NK.js.map} +1 -1
  9. package/dist/{BlogSinglePage-OYXJCOJ2.js → BlogSinglePage-6A5J2BKK.js} +14 -14
  10. package/dist/{BlogSinglePage-OYXJCOJ2.js.map → BlogSinglePage-6A5J2BKK.js.map} +1 -1
  11. package/dist/{BlogTagPage-XEHXO4PE.js → BlogTagPage-DAA3ZO76.js} +16 -16
  12. package/dist/{BlogTagPage-XEHXO4PE.js.map → BlogTagPage-DAA3ZO76.js.map} +1 -1
  13. package/dist/{BrandsPage-QSTJTEAA.js → BrandsPage-JABKRWP2.js} +15 -15
  14. package/dist/{BrandsPage-QSTJTEAA.js.map → BrandsPage-JABKRWP2.js.map} +1 -1
  15. package/dist/{Breadcrumb-7QGTM5S5.js → Breadcrumb-SCJZW4RO.js} +14 -14
  16. package/dist/{Breadcrumb-7QGTM5S5.js.map → Breadcrumb-SCJZW4RO.js.map} +1 -1
  17. package/dist/{CartItem-7FNRMTQS.js → CartItem-3KFTPQVF.js} +14 -14
  18. package/dist/{CartItem-7FNRMTQS.js.map → CartItem-3KFTPQVF.js.map} +1 -1
  19. package/dist/{CartSummary-A32524GZ.js → CartSummary-I64SD4QX.js} +14 -14
  20. package/dist/{CartSummary-A32524GZ.js.map → CartSummary-I64SD4QX.js.map} +1 -1
  21. package/dist/{Copyright-PVSTODJF.js → Copyright-3TREUCAK.js} +3 -3
  22. package/dist/{Copyright-PVSTODJF.js.map → Copyright-3TREUCAK.js.map} +1 -1
  23. package/dist/{CustomerLayout-NOHKTFXG.js → CustomerLayout-BH74QWRI.js} +14 -14
  24. package/dist/{CustomerLayout-NOHKTFXG.js.map → CustomerLayout-BH74QWRI.js.map} +1 -1
  25. package/dist/{Footer-RDRSICZP.js → Footer-DSSOB3ZW.js} +4 -4
  26. package/dist/{Footer-RDRSICZP.js.map → Footer-DSSOB3ZW.js.map} +1 -1
  27. package/dist/{Header-YCS4HJCH.js → Header-UWUS34D6.js} +8 -8
  28. package/dist/{Header-YCS4HJCH.js.map → Header-UWUS34D6.js.map} +1 -1
  29. package/dist/{LatestProducts-JEOWAHON.js → LatestProducts-VLSTOKEB.js} +16 -16
  30. package/dist/{LatestProducts-JEOWAHON.js.map → LatestProducts-VLSTOKEB.js.map} +1 -1
  31. package/dist/{LoyaltyPage-TE5E3OJC.js → LoyaltyPage-3FAD5MJ6.js} +14 -14
  32. package/dist/{LoyaltyPage-TE5E3OJC.js.map → LoyaltyPage-3FAD5MJ6.js.map} +1 -1
  33. package/dist/{LoyaltyPointsPage-GI3EQOLY.js → LoyaltyPointsPage-A4CTI55P.js} +6 -6
  34. package/dist/{LoyaltyPointsPage-GI3EQOLY.js.map → LoyaltyPointsPage-A4CTI55P.js.map} +1 -1
  35. package/dist/{MainMenu-46II2DJE.js → MainMenu-C7TWXVL5.js} +6 -6
  36. package/dist/MainMenu-C7TWXVL5.js.map +1 -0
  37. package/dist/{MasterLayout-ZYWD2XZO.js → MasterLayout-MZY44RTA.js} +14 -14
  38. package/dist/{MasterLayout-ZYWD2XZO.js.map → MasterLayout-MZY44RTA.js.map} +1 -1
  39. package/dist/{MobileMenu-O7354R4E.js → MobileMenu-KNDN34HO.js} +15 -15
  40. package/dist/{MobileMenu-O7354R4E.js.map → MobileMenu-KNDN34HO.js.map} +1 -1
  41. package/dist/{NotificationsPage-OZKKZ2CC.js → NotificationsPage-LKUTYWPR.js} +14 -14
  42. package/dist/{NotificationsPage-OZKKZ2CC.js.map → NotificationsPage-LKUTYWPR.js.map} +1 -1
  43. package/dist/{OrderSinglePage-IVOIE2YE.js → OrderSinglePage-VDALCKJL.js} +15 -15
  44. package/dist/{OrderSinglePage-IVOIE2YE.js.map → OrderSinglePage-VDALCKJL.js.map} +1 -1
  45. package/dist/{OrdersPage-PMLHIHDV.js → OrdersPage-TOSCY6ML.js} +14 -14
  46. package/dist/{OrdersPage-PMLHIHDV.js.map → OrdersPage-TOSCY6ML.js.map} +1 -1
  47. package/dist/{PageSingle-C3NBFFHR.js → PageSingle-56SRDA4Q.js} +3 -3
  48. package/dist/{PageSingle-C3NBFFHR.js.map → PageSingle-56SRDA4Q.js.map} +1 -1
  49. package/dist/{ProductDetails-R7SFKXNC.js → ProductDetails-YYU7TJSU.js} +4 -4
  50. package/dist/{ProductDetails-R7SFKXNC.js.map → ProductDetails-YYU7TJSU.js.map} +1 -1
  51. package/dist/{ProfilePage-5FU4N6O7.js → ProfilePage-CTGBAD2A.js} +14 -14
  52. package/dist/{ProfilePage-5FU4N6O7.js.map → ProfilePage-CTGBAD2A.js.map} +1 -1
  53. package/dist/{ReviewsPage-M7QHDNCJ.js → ReviewsPage-RJ5VQVB2.js} +3 -3
  54. package/dist/{ReviewsPage-M7QHDNCJ.js.map → ReviewsPage-RJ5VQVB2.js.map} +1 -1
  55. package/dist/{SeoCartWidget-T6PF34G4.js → SeoCartWidget-VL5TD2FU.js} +14 -14
  56. package/dist/{SeoCartWidget-T6PF34G4.js.map → SeoCartWidget-VL5TD2FU.js.map} +1 -1
  57. package/dist/{SettingsPage-HBREYY5Q.js → SettingsPage-ZHDB3YZS.js} +14 -14
  58. package/dist/{SettingsPage-HBREYY5Q.js.map → SettingsPage-ZHDB3YZS.js.map} +1 -1
  59. package/dist/{StoreClosedNotification-QTKW4PQ2.js → StoreClosedNotification-VP2ACWD6.js} +15 -15
  60. package/dist/{StoreClosedNotification-QTKW4PQ2.js.map → StoreClosedNotification-VP2ACWD6.js.map} +1 -1
  61. package/dist/{TestimonialsPage-ESB4CQRS.js → TestimonialsPage-V2RWUVZD.js} +3 -3
  62. package/dist/{TestimonialsPage-ESB4CQRS.js.map → TestimonialsPage-V2RWUVZD.js.map} +1 -1
  63. package/dist/{ThankYouPage-BLYIJQNV.js → ThankYouPage-NJKASZ2R.js} +14 -14
  64. package/dist/{ThankYouPage-BLYIJQNV.js.map → ThankYouPage-NJKASZ2R.js.map} +1 -1
  65. package/dist/{WalletPage-7YOOVSXO.js → WalletPage-N4FDKDK3.js} +15 -15
  66. package/dist/{WalletPage-7YOOVSXO.js.map → WalletPage-N4FDKDK3.js.map} +1 -1
  67. package/dist/{WishlistPage-MXVTTJD6.js → WishlistPage-JCOG3KOV.js} +16 -16
  68. package/dist/{WishlistPage-MXVTTJD6.js.map → WishlistPage-JCOG3KOV.js.map} +1 -1
  69. package/dist/api/advertisement.js +2 -2
  70. package/dist/api/apps.js +2 -2
  71. package/dist/api/blog.js +3 -3
  72. package/dist/api/brands.js +3 -3
  73. package/dist/api/cart.js +3 -3
  74. package/dist/api/category.js +3 -3
  75. package/dist/api/client.js +1 -1
  76. package/dist/api/home.js +2 -2
  77. package/dist/api/loyalty.js +3 -3
  78. package/dist/api/menu.js +2 -2
  79. package/dist/api/notification.js +2 -2
  80. package/dist/api/order.js +3 -3
  81. package/dist/api/page.js +3 -3
  82. package/dist/api/product.js +3 -3
  83. package/dist/api/profile.js +1 -1
  84. package/dist/api/store.js +2 -2
  85. package/dist/api/translations.js +2 -2
  86. package/dist/api/user.js +2 -2
  87. package/dist/api/wallet.js +2 -2
  88. package/dist/api/wishlist.js +4 -4
  89. package/dist/{chunk-ZU7TSTT7.js → chunk-2AS6KREO.js} +3 -3
  90. package/dist/{chunk-ZU7TSTT7.js.map → chunk-2AS6KREO.js.map} +1 -1
  91. package/dist/{chunk-YF3U4WYG.js → chunk-33R2PXOK.js} +4 -4
  92. package/dist/{chunk-YF3U4WYG.js.map → chunk-33R2PXOK.js.map} +1 -1
  93. package/dist/{chunk-WWGGQY6N.js → chunk-3CKMMFUF.js} +4 -4
  94. package/dist/{chunk-WWGGQY6N.js.map → chunk-3CKMMFUF.js.map} +1 -1
  95. package/dist/{chunk-HGNR4XBD.js → chunk-444GCMTZ.js} +7 -7
  96. package/dist/{chunk-HGNR4XBD.js.map → chunk-444GCMTZ.js.map} +1 -1
  97. package/dist/{chunk-JI6ZNE7R.js → chunk-5NTD7WXX.js} +3 -3
  98. package/dist/{chunk-JI6ZNE7R.js.map → chunk-5NTD7WXX.js.map} +1 -1
  99. package/dist/{chunk-HUJARQI7.js → chunk-7DRXAR76.js} +3 -3
  100. package/dist/{chunk-HUJARQI7.js.map → chunk-7DRXAR76.js.map} +1 -1
  101. package/dist/{chunk-ISDSQORB.js → chunk-7PFJOTZS.js} +4 -4
  102. package/dist/{chunk-ISDSQORB.js.map → chunk-7PFJOTZS.js.map} +1 -1
  103. package/dist/{chunk-5KKQUH72.js → chunk-ABVHWJPY.js} +10 -3
  104. package/dist/chunk-ABVHWJPY.js.map +1 -0
  105. package/dist/{chunk-FG2B552D.js → chunk-AEM5UEH6.js} +3 -3
  106. package/dist/{chunk-FG2B552D.js.map → chunk-AEM5UEH6.js.map} +1 -1
  107. package/dist/{chunk-J4GUGMPX.js → chunk-ATL7CYF2.js} +3 -3
  108. package/dist/{chunk-J4GUGMPX.js.map → chunk-ATL7CYF2.js.map} +1 -1
  109. package/dist/{chunk-ONPV2TED.js → chunk-AZT4TALZ.js} +5 -5
  110. package/dist/{chunk-ONPV2TED.js.map → chunk-AZT4TALZ.js.map} +1 -1
  111. package/dist/{chunk-XETMTAZC.js → chunk-BPM6LSOV.js} +4 -4
  112. package/dist/{chunk-XETMTAZC.js.map → chunk-BPM6LSOV.js.map} +1 -1
  113. package/dist/{chunk-E7TU4RW4.js → chunk-CU2DV3Z4.js} +3 -3
  114. package/dist/{chunk-E7TU4RW4.js.map → chunk-CU2DV3Z4.js.map} +1 -1
  115. package/dist/{chunk-EPMM5ZWB.js → chunk-F3SXHQU4.js} +5 -5
  116. package/dist/{chunk-EPMM5ZWB.js.map → chunk-F3SXHQU4.js.map} +1 -1
  117. package/dist/{chunk-ECMDQQO2.js → chunk-GIYRCRS6.js} +3 -3
  118. package/dist/{chunk-ECMDQQO2.js.map → chunk-GIYRCRS6.js.map} +1 -1
  119. package/dist/{chunk-5J3HVOTR.js → chunk-IK7CQSEA.js} +3 -3
  120. package/dist/{chunk-5J3HVOTR.js.map → chunk-IK7CQSEA.js.map} +1 -1
  121. package/dist/{chunk-P4UZRDTR.js → chunk-J4EFCWMM.js} +14 -14
  122. package/dist/{chunk-P4UZRDTR.js.map → chunk-J4EFCWMM.js.map} +1 -1
  123. package/dist/{chunk-VZ342V4W.js → chunk-LB4U7GI3.js} +4 -4
  124. package/dist/{chunk-VZ342V4W.js.map → chunk-LB4U7GI3.js.map} +1 -1
  125. package/dist/{chunk-7CGTHJ6S.js → chunk-LCRHHSBP.js} +5 -5
  126. package/dist/{chunk-7CGTHJ6S.js.map → chunk-LCRHHSBP.js.map} +1 -1
  127. package/dist/{chunk-GCZ46XF6.js → chunk-LGXEXLX3.js} +12 -12
  128. package/dist/chunk-LGXEXLX3.js.map +1 -0
  129. package/dist/{chunk-XRX6VFSO.js → chunk-MBU4LSRZ.js} +4 -4
  130. package/dist/{chunk-XRX6VFSO.js.map → chunk-MBU4LSRZ.js.map} +1 -1
  131. package/dist/{chunk-JRWBBN3M.js → chunk-MJ3DLAC3.js} +4 -4
  132. package/dist/{chunk-JRWBBN3M.js.map → chunk-MJ3DLAC3.js.map} +1 -1
  133. package/dist/{chunk-N63CWDNI.js → chunk-MOAMYULW.js} +4 -4
  134. package/dist/{chunk-N63CWDNI.js.map → chunk-MOAMYULW.js.map} +1 -1
  135. package/dist/{chunk-NVPOH3CU.js → chunk-N36TVQP2.js} +4 -4
  136. package/dist/{chunk-NVPOH3CU.js.map → chunk-N36TVQP2.js.map} +1 -1
  137. package/dist/{chunk-ECFDJBTE.js → chunk-NRPYHHRQ.js} +7 -7
  138. package/dist/{chunk-ECFDJBTE.js.map → chunk-NRPYHHRQ.js.map} +1 -1
  139. package/dist/{chunk-TZCTCRGB.js → chunk-NUTURBAK.js} +3 -3
  140. package/dist/{chunk-TZCTCRGB.js.map → chunk-NUTURBAK.js.map} +1 -1
  141. package/dist/{chunk-XNBCPEH6.js → chunk-O2RXB3P3.js} +7 -7
  142. package/dist/{chunk-XNBCPEH6.js.map → chunk-O2RXB3P3.js.map} +1 -1
  143. package/dist/{chunk-FN4I3UDJ.js → chunk-OEM2SFJE.js} +3 -3
  144. package/dist/{chunk-FN4I3UDJ.js.map → chunk-OEM2SFJE.js.map} +1 -1
  145. package/dist/{chunk-2IGIQA2S.js → chunk-OJMG3NAF.js} +3 -3
  146. package/dist/{chunk-2IGIQA2S.js.map → chunk-OJMG3NAF.js.map} +1 -1
  147. package/dist/{chunk-Y6X3TVOI.js → chunk-QQWJNRSI.js} +3 -3
  148. package/dist/{chunk-Y6X3TVOI.js.map → chunk-QQWJNRSI.js.map} +1 -1
  149. package/dist/{chunk-RSHQ43OI.js → chunk-RUUQ5OVU.js} +3 -3
  150. package/dist/{chunk-RSHQ43OI.js.map → chunk-RUUQ5OVU.js.map} +1 -1
  151. package/dist/{chunk-C5UK27T6.js → chunk-SCBSKIBA.js} +4 -4
  152. package/dist/{chunk-C5UK27T6.js.map → chunk-SCBSKIBA.js.map} +1 -1
  153. package/dist/{chunk-PLPIAVLJ.js → chunk-SOLUKKTW.js} +3 -3
  154. package/dist/{chunk-PLPIAVLJ.js.map → chunk-SOLUKKTW.js.map} +1 -1
  155. package/dist/{chunk-BBMRL2QU.js → chunk-TDIENKPW.js} +3 -3
  156. package/dist/{chunk-BBMRL2QU.js.map → chunk-TDIENKPW.js.map} +1 -1
  157. package/dist/{chunk-J5OJ7JM2.js → chunk-UXJAZ5JP.js} +3 -3
  158. package/dist/{chunk-J5OJ7JM2.js.map → chunk-UXJAZ5JP.js.map} +1 -1
  159. package/dist/{chunk-LQ2PXFR4.js → chunk-VNZ6VI2O.js} +3 -3
  160. package/dist/{chunk-LQ2PXFR4.js.map → chunk-VNZ6VI2O.js.map} +1 -1
  161. package/dist/{chunk-ELQFU3RH.js → chunk-VXMWMGBQ.js} +4 -4
  162. package/dist/{chunk-ELQFU3RH.js.map → chunk-VXMWMGBQ.js.map} +1 -1
  163. package/dist/{chunk-AORRIQPD.js → chunk-XMSZSSTZ.js} +4 -4
  164. package/dist/{chunk-AORRIQPD.js.map → chunk-XMSZSSTZ.js.map} +1 -1
  165. package/dist/{chunk-ICQIH7F6.js → chunk-YRIMECFU.js} +3 -3
  166. package/dist/{chunk-ICQIH7F6.js.map → chunk-YRIMECFU.js.map} +1 -1
  167. package/dist/{chunk-62FNNS7U.js → chunk-ZNA3SWRP.js} +7 -7
  168. package/dist/{chunk-62FNNS7U.js.map → chunk-ZNA3SWRP.js.map} +1 -1
  169. package/dist/components/cart/index.js +1 -1
  170. package/dist/components/common/index.js +1 -1
  171. package/dist/components/drawer/index.js +12 -12
  172. package/dist/components/home/index.js +17 -17
  173. package/dist/components/layout/index.js +4 -4
  174. package/dist/components/navigation/MainMenu.d.ts.map +1 -1
  175. package/dist/components/product/index.js +14 -14
  176. package/dist/components/store/index.js +13 -13
  177. package/dist/dev/editor/index.js +1 -1
  178. package/dist/dev/index.js +1 -1
  179. package/dist/{document-class-LR4X2CH5.js → document-class-B4WBGWSA.js} +11 -11
  180. package/dist/{document-class-LR4X2CH5.js.map → document-class-B4WBGWSA.js.map} +1 -1
  181. package/dist/hooks/index.js +12 -12
  182. package/dist/hooks/useGtm.js +12 -12
  183. package/dist/hooks/useMoney.js +12 -12
  184. package/dist/hooks/useStore.js +12 -12
  185. package/dist/hooks/useUser.js +3 -3
  186. package/dist/index.js +12 -12
  187. package/dist/{navigation-OQ2ZT3ID.js → navigation-MBQKDV5V.js} +5 -5
  188. package/dist/{navigation-OQ2ZT3ID.js.map → navigation-MBQKDV5V.js.map} +1 -1
  189. package/dist/{navigation-5PCMFALH.js → navigation-VPRCR725.js} +4 -4
  190. package/dist/{navigation-5PCMFALH.js.map → navigation-VPRCR725.js.map} +1 -1
  191. package/dist/{not-found-YHQVBI7H.js → not-found-OIZGDR7G.js} +15 -15
  192. package/dist/{not-found-YHQVBI7H.js.map → not-found-OIZGDR7G.js.map} +1 -1
  193. package/dist/providers/index.js +12 -12
  194. package/dist/routes/account/orders.js +4 -4
  195. package/dist/routes/account.js +13 -13
  196. package/dist/routes/blog.js +8 -8
  197. package/dist/routes/brands.js +4 -4
  198. package/dist/routes/cart.js +21 -21
  199. package/dist/routes/home.js +18 -18
  200. package/dist/routes/index.js +3 -3
  201. package/dist/routes/loyalty.js +5 -5
  202. package/dist/routes/page.js +4 -4
  203. package/dist/routes/product-listing.js +19 -19
  204. package/dist/routes/product.js +17 -17
  205. package/dist/routes/root/beforeLoad.d.ts.map +1 -1
  206. package/dist/routes/seo-redirects.js +3 -3
  207. package/dist/routes/testimonials.js +2 -2
  208. package/dist/routes/thank-you.js +4 -4
  209. package/dist/skeleton.js +18 -18
  210. package/dist/{store-lookup-NJCTSPFL.js → store-lookup-HMU7QMSC.js} +8 -8
  211. package/dist/{store-lookup-NJCTSPFL.js.map → store-lookup-HMU7QMSC.js.map} +1 -1
  212. package/dist/tanstack/link.d.ts.map +1 -1
  213. package/dist/tanstack.js +11 -11
  214. package/dist/utils/index.js +2 -2
  215. package/package.json +1 -1
  216. package/tailwind/components/navigation/MainMenu.tsx.txt +2 -1
  217. package/tailwind/routes/root/beforeLoad.ts.txt +4 -2
  218. package/dist/MainMenu-46II2DJE.js.map +0 -1
  219. package/dist/chunk-5KKQUH72.js.map +0 -1
  220. package/dist/chunk-GCZ46XF6.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/providers/NavigationProvider.ts"],"names":[],"mappings":"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","file":"chunk-PLPIAVLJ.js","sourcesContent":["import {\n createElement,\n createContext,\n useContext,\n useCallback,\n useRef,\n useEffect,\n useSyncExternalStore,\n type ReactNode,\n} from 'react';\nimport { useTwilight } from './twilight-context';\nimport { getTwilightContext, subscribeTwilight, type TwilightLocation } from '../twilight/context';\nimport { RouteId } from '../router/route-ids';\nimport { withoutStoreUrlBase } from '../utils/store-base';\nimport { extractScopeFromUrl } from '../utils/cookies';\nimport { getActiveScope } from '../api/client';\nimport { openNativeBrowser } from '../native-app';\nimport { SUPPORTED_LOCALES } from './locale-constants';\n\n// Re-export RouteId from centralized location\nexport { RouteId } from '../router/route-ids';\nexport type { RouteIdType } from '../router/route-ids';\n\n// Re-export TwilightLocation as Location for convenience\nexport type { TwilightLocation as Location } from '../twilight/context';\n\nexport type NavigateOptions = { replace?: boolean };\nexport type NavigateFn = (to: string, opts?: NavigateOptions) => void;\n\n/** Redirect error - thrown to trigger a redirect */\nexport class RedirectError extends Error {\n constructor(\n public path: string,\n public replace = false\n ) {\n super(`Redirect to ${path}`);\n this.name = 'RedirectError';\n }\n}\n\n/** NotFound error - thrown to trigger 404 */\nexport class NotFoundError extends Error {\n constructor(message?: string) {\n super(message ?? 'Not Found');\n this.name = 'NotFoundError';\n }\n}\n\n/** Unauthorized error - thrown to trigger 401 */\nexport class UnauthorizedError extends Error {\n constructor(message?: string) {\n super(message ?? 'Unauthorized');\n this.name = 'UnauthorizedError';\n }\n}\n\nexport { SettingsError } from '../utils/errors';\n\nexport const defaultNavigate: NavigateFn = (to, opts) => {\n if (opts?.replace) window.location.replace(to);\n else window.location.href = to;\n};\n\nexport interface RedirectOptions {\n replace?: boolean;\n /** HTTP status of a server redirect, e.g. 301 for a URL that moved for good. Defaults to 307. */\n statusCode?: number;\n}\n\n/** Default redirect - throws RedirectError with browser navigation as fallback */\nexport const defaultRedirect = (path: string, opts?: RedirectOptions): never => {\n const replace = opts?.replace ?? false;\n\n // In browser, perform actual navigation before throwing\n if (typeof window !== 'undefined') {\n if (replace) window.location.replace(path);\n else window.location.href = path;\n }\n\n throw new RedirectError(path, replace);\n};\n\n/** Default notFound - throws generic error */\nexport const defaultNotFound = (message?: string): never => {\n throw new NotFoundError(message);\n};\n\n/** Default unauthorized - throws generic error */\nexport const defaultUnauthorized = (message?: string): never => {\n throw new UnauthorizedError(message);\n};\n\n// ─── Framework-agnostic functions for loaders (outside React context) ──────\n\ninterface NavigationHandlers {\n notFound: (message?: string) => never;\n redirect: (path: string, opts?: RedirectOptions) => never;\n unauthorized: (message?: string) => never;\n}\n\n// One set per isolate, so a loader in another copy of this module (a separate\n// package entry, a dev reload) still throws what the framework adapter configured.\nconst HANDLERS_KEY = Symbol.for('salla.twilight.navigation.handlers');\nconst registry = globalThis as { [HANDLERS_KEY]?: NavigationHandlers };\nconst handlers: NavigationHandlers = (registry[HANDLERS_KEY] ??= {\n notFound: defaultNotFound,\n redirect: defaultRedirect,\n unauthorized: defaultUnauthorized,\n});\n\n/**\n * Throw a notFound error. Use in route loaders.\n * Framework adapters will override this to throw framework-specific errors.\n *\n * @example\n * ```ts\n * // In loader\n * import { notFound } from '@salla.sa/twilight-theme-engine/providers';\n *\n * if (!data) throw notFound();\n * ```\n */\nexport function notFound(message?: string): never {\n return handlers.notFound(message);\n}\n\n/**\n * Throw an unauthorized error. Use in route loaders to protect authenticated routes.\n * Framework adapters will override this to throw framework-specific errors.\n *\n * @example\n * ```ts\n * import { unauthorized } from '@salla.sa/twilight-theme-engine/providers';\n *\n * if (!user) throw unauthorized();\n * ```\n */\nexport function unauthorized(message?: string): never {\n return handlers.unauthorized(message);\n}\n\n/**\n * Throw a redirect. Use in route loaders.\n * Framework adapters will override this to throw framework-specific redirects.\n *\n * @param path - Path to redirect to\n * @param opts - Redirect options\n *\n * @example\n * ```ts\n * // In loader\n * import { redirect } from '@salla.sa/twilight-theme-engine/providers';\n *\n * throw redirect('/ar/cart');\n * ```\n */\nexport function redirect(path: string, opts?: RedirectOptions): never {\n return handlers.redirect(path, opts);\n}\n\n/**\n * Configure navigation functions for a specific framework.\n * Called by framework adapters during setup.\n */\nexport function configureNavigation(config: {\n notFound?: (message?: string) => never;\n redirect?: (path: string, opts?: RedirectOptions) => never;\n unauthorized?: (_message?: string) => never;\n}): void {\n if (config.notFound) handlers.notFound = config.notFound;\n if (config.redirect) handlers.redirect = config.redirect;\n if (config.unauthorized) handlers.unauthorized = config.unauthorized;\n}\n\n// ─── Loader Error Handling Helpers ─────────────────────────────────────────────\n\n/**\n * Wraps an API promise with error handling for route loaders.\n * On a 404/410 response throws notFound(); other errors propagate.\n *\n * @example\n * ```ts\n * findOrThrow: (id, locale) => orThrow(product.find(id, locale)),\n * const data = await product.findOrThrow(id, locale);\n * ```\n */\nexport async function orThrow<T>(promise: Promise<T>): Promise<T> {\n try {\n return await promise;\n } catch (error) {\n console.debug('[loader error]', error);\n if (isMissingResource(error)) return notFound();\n throw error;\n }\n}\n\n/** The HTTP status an API error carries, if it is one. */\nexport function responseStatusOf(error: unknown): number | undefined {\n const status = (error as { response?: { status?: unknown } } | null)?.response?.status;\n return typeof status === 'number' ? status : undefined;\n}\n\n/** The API answers a missing resource with 410, sometimes 404. */\nexport function isMissingResource(error: unknown): boolean {\n const status = responseStatusOf(error);\n return status === 404 || status === 410;\n}\n\n/**\n * Wraps an API promise with error handling for authenticated route loaders.\n * On a 401/403 response throws unauthorized(); other errors propagate.\n *\n * @example\n * ```ts\n * const data = await orUnauthorized(order.find(id, locale));\n * ```\n */\nexport async function orUnauthorized<T>(promise: Promise<T>): Promise<T> {\n try {\n return await promise;\n } catch (error) {\n const status = (error as unknown as { response?: { status?: number } })?.response?.status;\n if (status === 401 || status === 403) {\n throw unauthorized();\n }\n throw error;\n }\n}\n\n// ─── Navigation Context ──────────────────────────────────────────────────────\n\ninterface NavigationContextValue {\n navigate: NavigateFn;\n}\n\nconst NavigationContext = createContext<NavigationContextValue>({\n navigate: defaultNavigate,\n});\n\nexport function NavigationProvider({\n navigate,\n children,\n}: {\n navigate: NavigateFn;\n children: ReactNode;\n}) {\n return createElement(NavigationContext.Provider, { value: { navigate } }, children);\n}\n\n// ─── Primary Hooks (High Compatibility) ───────────────────────────────────────\n\n/**\n * Get the navigate function.\n * Matches React Router, TanStack Router, and Remix naming convention.\n *\n * @returns Navigate function\n *\n * @example\n * ```tsx\n * const navigate = useNavigate();\n * navigate('/cart');\n * navigate('/cart', { replace: true });\n * ```\n */\nexport function useNavigate(): NavigateFn {\n return useContext(NavigationContext).navigate;\n}\n\n/**\n * Get the current route ID.\n * Used for route detection (isHome, isProduct, etc.)\n *\n * @returns Current route ID string\n *\n * @example\n * ```tsx\n * const routeId = useRouteId();\n * if (routeId === RouteId.INDEX) { ... }\n * ```\n */\nexport function useRouteId(): string {\n return useSyncExternalStore(\n subscribeTwilight,\n () => getTwilightContext().routeId,\n () => getTwilightContext().routeId\n );\n}\n\n/**\n * Get the current location (TanStack Router compatible).\n * Reads from the router context for SSR compatibility.\n *\n * @returns Location object matching TanStack's ParsedLocation\n *\n * @example\n * ```tsx\n * const location = useLocation();\n * console.log(location.pathname); // '/ar/products'\n * console.log(location.searchStr); // '?q=shoes&page=2'\n * console.log(location.search); // { q: 'shoes', page: '2' }\n * console.log(location.href); // 'https://store.com/ar/products?q=shoes'\n * console.log(location.hash); // '#section-1'\n * console.log(location.state); // { scrollY: 100 }\n * ```\n */\nexport function useLocation<TSearch = Record<string, unknown>>(): TwilightLocation<TSearch> {\n useTwilight();\n return getTwilightContext().location as TwilightLocation<TSearch>;\n}\n\n// ─── Selector Hooks ───────────────────────────────────────────────────────────\n\n/**\n * Check if current page is homepage */\nexport function useIsHome(): boolean {\n return useRouteId() === RouteId.INDEX;\n}\n\n/** Check if current page is product detail */\nexport function useIsProduct(): boolean {\n return useRouteId() === RouteId.PRODUCT_SINGLE;\n}\n\n/** Check if current page is cart */\nexport function useIsCart(): boolean {\n return useRouteId() === RouteId.CART;\n}\n\n/** Check if current page is in customer area */\nexport function useIsCustomer(): boolean {\n return useRouteId().startsWith('customer');\n}\n\n/** Check if current page is blog related */\nexport function useIsBlog(): boolean {\n const routeId = useRouteId();\n return routeId === RouteId.BLOG_INDEX || routeId === RouteId.BLOG_SINGLE;\n}\n\n/** Check if current page is brands related */\nexport function useIsBrands(): boolean {\n const routeId = useRouteId();\n return routeId === RouteId.BRANDS_INDEX || routeId === RouteId.BRANDS_SINGLE;\n}\n\n/** Check if current page is search */\nexport function useIsSearch(): boolean {\n return useRouteId() === RouteId.SEARCH;\n}\n\n// ─── Navigation Interceptor ──────────────────────────────────────────────────\n\n/**\n * NavigationInterceptor - Framework-agnostic SPA navigation interceptor.\n *\n * Uses navigate function from NavigationProvider context and store URL\n * from TwilightProvider to intercept same-origin navigations.\n */\nexport function NavigationInterceptor(): null {\n const navigate = useNavigate();\n const { store } = useTwilight();\n useNavigationInterceptor(navigate, store?.url, store?.username);\n return null;\n}\n\n/**\n * resolveRelativePath - Resolve href to store-relative path\n *\n * Returns `null` when the URL is not on the same origin as the store,\n * keeping this helper safe against open-redirect misuse.\n *\n * The path is the application's, not the address on Salla's host: a store served\n * under its username there (`https://demostore.salla.sa/ar/dev-…/`) hands out links\n * carrying that segment, and it is removed. On a host where the store is the root\n * of every URL, the router adds its own segment back when it navigates.\n *\n * @param href - The URL to resolve\n * @param storeUrl - The store's base URL\n * @param username - The store's username, which tells that segment apart from a\n * locale spelled the same way (see `withoutStoreUrlBase`)\n * @returns The relative path string, or null if the URL is external\n */\nexport function resolveRelativePath(\n href: string,\n storeUrl: string,\n username?: string | null\n): string | null {\n try {\n const target = new URL(href, window.location.href);\n const base = new URL(storeUrl);\n\n if (target.origin !== base.origin) return null;\n\n const pathname = withoutStoreUrlBase(target.pathname, base.pathname, username);\n const path = pathname + target.search + target.hash;\n return path || '/';\n } catch {\n return null;\n }\n}\n\n/**\n * Pages the Store app renders, not the theme: checkout, payment, digital orders, a\n * gift's page, the booking page (`cart/booking/<id>`, where \"book now\" leads), the\n * SDK's auth endpoints, and the order invoice. The storefront dispatcher sends a\n * document request for them to the app (`bypass.ts` in store-front-dispacher), so\n * only a page load reaches them; rendered in place, the router has no route and\n * shows its 404. Keep in step with the dispatcher's list.\n */\nconst APP_PATH_PREFIXES = [\n '/checkout',\n '/payment',\n '/orders/digital',\n '/gifts',\n '/cart/booking',\n '/auth/jwt',\n '/auth/refresh',\n];\nconst APP_PATH_PATTERNS = [/^\\/account\\/orders\\/[^/]+\\/print\\/?$/];\n\n/** Whether a store-relative path (`/ar/cart/booking/1?x`) is one of the app's pages. */\nexport function isAppPath(path: string): boolean {\n let pathname = path.split(/[?#]/)[0];\n const first = pathname.split('/')[1];\n if ((SUPPORTED_LOCALES as readonly string[]).includes(first)) {\n pathname = pathname.slice(first.length + 1) || '/';\n }\n return (\n APP_PATH_PREFIXES.some((prefix) => pathname === prefix || pathname.startsWith(`${prefix}/`)) ||\n APP_PATH_PATTERNS.some((pattern) => pattern.test(pathname))\n );\n}\n\n/**\n * EXTERNAL_TARGETS - Target attribute values that indicate external navigation\n */\nconst EXTERNAL_TARGETS = new Set(['_blank', '_parent', '_top']);\n\n/**\n * shouldIntercept - Determine if an anchor click should be intercepted by SPA router\n *\n * Follows browser conventions: modifier keys, external targets, downloads,\n * and non-HTTP(S) protocols are left to the browser's default behavior.\n *\n * @param anchor - The anchor element that was clicked\n * @param event - The click event\n * @returns true if the click should be intercepted by the router\n */\nexport function shouldIntercept(anchor: HTMLAnchorElement, event: MouseEvent): boolean {\n if (event.defaultPrevented) return false;\n\n if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return false;\n\n const targetAttr = anchor.getAttribute('target');\n if (targetAttr && EXTERNAL_TARGETS.has(targetAttr)) return false;\n\n if (anchor.hasAttribute('download')) return false;\n\n const rel = (anchor.getAttribute('rel') ?? '').split(/\\s+/);\n if (rel.includes('external')) return false;\n\n const href = anchor.getAttribute('href') ?? '';\n if (!href) return false;\n\n try {\n const url = new URL(href, window.location.href);\n if (url.protocol.startsWith('javascript:')) return false;\n } catch {\n return false;\n }\n\n return true;\n}\n\n/**\n * Whether `href` switches market: its `?scope=` names another scope than the active one,\n * or the same scope with another branch (`allocation_type` / `allocation_id`).\n *\n * Twig switches market with a full load (the SDK's scopes modal calls\n * `location.replace('?scope=<id>')`), and only a document request lets the server render\n * the new market and record it in the `scope` cookie. Left to the browser, not the router.\n */\nexport function switchesScope(href: string): boolean {\n let url: URL;\n try {\n url = new URL(href, window.location.href);\n } catch {\n return false;\n }\n const requested = extractScopeFromUrl(url.href);\n if (!requested) return false;\n const active = getActiveScope();\n if (requested.id !== active?.id) return true;\n return (\n requested.allocation_id != null &&\n (requested.allocation_id !== active.allocation_id ||\n requested.allocation_type !== active.allocation_type)\n );\n}\n\n/**\n * Whether an untouched tap on `anchor` would load a page off the store: an\n * http(s) link to neither this page's origin nor the store's. `mailto:`, `tel:`\n * and the like are the device's to handle, and are left alone. So are product\n * gallery slides (`data-fslightbox`): each links to its own image or video, and\n * the gallery opens it in the app's native gallery, from a click handler that\n * runs after this capture-phase check, so `event.defaultPrevented` is still false.\n */\nexport function leavesStore(\n anchor: HTMLAnchorElement,\n event: MouseEvent,\n storeUrl: string | undefined\n): boolean {\n if (event.defaultPrevented || anchor.hasAttribute('download')) return false;\n if (anchor.hasAttribute('data-fslightbox')) return false;\n let url: URL;\n try {\n url = new URL(anchor.getAttribute('href') ?? '', window.location.href);\n } catch {\n return false;\n }\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return false;\n if (url.origin === window.location.origin) return false;\n try {\n return !storeUrl || url.origin !== new URL(storeUrl).origin;\n } catch {\n return true;\n }\n}\n\nfunction isSamePage(href: string): boolean {\n const url = new URL(href, window.location.href);\n return url.pathname === window.location.pathname && url.search === window.location.search;\n}\n\nfunction closeEnclosingModal(path: EventTarget[]): void {\n const modal = path.find(\n (el): el is HTMLElement & { close?: () => unknown } =>\n el instanceof HTMLElement && el.localName === 'salla-modal'\n );\n void modal?.close?.();\n}\n\n// ─── Navigation Interceptor Hook (framework-agnostic) ─────────────────────────\n\n/**\n * Hook for navigation interception using framework-agnostic navigate function.\n * Framework adapters call this with their navigate implementation.\n */\nexport function useNavigationInterceptor(\n navigate: NavigateFn,\n storeUrl: string | undefined,\n username?: string\n): void {\n const storeUrlRef = useRef<string | undefined>(storeUrl);\n const usernameRef = useRef<string | undefined>(username);\n\n useEffect(() => {\n storeUrlRef.current = storeUrl;\n usernameRef.current = username;\n }, [storeUrl, username]);\n\n const tryNavigate = useCallback(\n (href: string, replace = false): boolean => {\n const currentStoreUrl = storeUrlRef.current;\n if (!currentStoreUrl || switchesScope(href)) return false;\n\n const relativePath = resolveRelativePath(href, currentStoreUrl, usernameRef.current);\n // The app's pages are left to the browser: only a page load reaches the app.\n if (!relativePath || isAppPath(relativePath)) return false;\n\n navigate(relativePath, { replace });\n return true;\n },\n [navigate]\n );\n\n useEffect(() => {\n // ── Pointer movement tracking ────────────────────────────────────────────\n // Detect drag/scroll gestures to avoid intercepting clicks from swipes.\n let pointerStartX = 0;\n let pointerStartY = 0;\n let pointerSwiped = false;\n const MOVE_THRESHOLD = 10;\n\n function handlePointerDown(event: PointerEvent): void {\n pointerStartX = event.clientX;\n pointerStartY = event.clientY;\n pointerSwiped = false;\n }\n\n function handlePointerUp(event: PointerEvent): void {\n const dx = Math.abs(event.clientX - pointerStartX);\n const dy = Math.abs(event.clientY - pointerStartY);\n pointerSwiped = dx > MOVE_THRESHOLD || dy > MOVE_THRESHOLD;\n }\n\n // ── Anchor click interception ────────────────────────────────────────────\n function handleClick(event: MouseEvent): void {\n // Ignore clicks that resulted from swipe/drag gestures.\n if (pointerSwiped) {\n pointerSwiped = false;\n return;\n }\n\n const path = event.composedPath();\n const anchor = path.find((el): el is HTMLAnchorElement => el instanceof HTMLAnchorElement);\n\n // Inside the Salla app, a link that leaves the store opens in the device's\n // browser; loaded in the app's webview it would strand the shopper there.\n if (\n anchor &&\n leavesStore(anchor, event, storeUrlRef.current) &&\n openNativeBrowser(anchor.href)\n ) {\n event.preventDefault();\n return;\n }\n\n if (!anchor || !shouldIntercept(anchor, event)) return;\n\n const samePage = isSamePage(anchor.href);\n if (tryNavigate(anchor.href, false)) {\n event.preventDefault();\n // A page load would close a web component's modal; an in-app navigation leaves it open.\n if (!samePage) closeEnclosingModal(path);\n }\n }\n\n // ── window.location.assign patch ─────────────────────────────────────────\n const origAssign = window.location.assign;\n function patchedAssign(url: string): void {\n if (!tryNavigate(url, false)) origAssign.call(window.location, url);\n }\n\n // ── window.location.replace patch ────────────────────────────────────────\n const origReplace = window.location.replace;\n function patchedReplace(url: string): void {\n if (!tryNavigate(url, true)) origReplace.call(window.location, url);\n }\n\n let assignPatched = false;\n let replacePatched = false;\n try {\n window.location.assign = patchedAssign;\n assignPatched = true;\n } catch {\n /* non-writable in this environment */\n }\n try {\n window.location.replace = patchedReplace;\n replacePatched = true;\n } catch {\n /* non-writable in this environment */\n }\n\n document.addEventListener('click', handleClick, { capture: true });\n document.addEventListener('pointerdown', handlePointerDown, { passive: true });\n document.addEventListener('pointerup', handlePointerUp, { passive: true });\n\n return () => {\n document.removeEventListener('click', handleClick, { capture: true });\n document.removeEventListener('pointerdown', handlePointerDown);\n document.removeEventListener('pointerup', handlePointerUp);\n if (assignPatched) {\n try {\n window.location.assign = origAssign;\n } catch {\n /* ignore */\n }\n }\n if (replacePatched) {\n try {\n window.location.replace = origReplace;\n } catch {\n /* ignore */\n }\n }\n };\n }, [tryNavigate]);\n}\n\n// ─── Client navigation config ─────────────────────────────────────────────────\n\nexport interface ClientNav {\n /** Framework adapter to use for client-side navigation. @default 'tanstack' */\n framework?: 'tanstack' | 'nextjs';\n /**\n * Intercept same-origin `<a>` clicks and `window.location` calls from web\n * components and route them through the framework router.\n * @default true\n */\n interceptLinks?: boolean;\n}\n\n// Framework adapter setups are registered in navigation-setups.ts to avoid circular imports.\n"]}
1
+ {"version":3,"sources":["../src/providers/NavigationProvider.ts"],"names":[],"mappings":"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","file":"chunk-SOLUKKTW.js","sourcesContent":["import {\n createElement,\n createContext,\n useContext,\n useCallback,\n useRef,\n useEffect,\n useSyncExternalStore,\n type ReactNode,\n} from 'react';\nimport { useTwilight } from './twilight-context';\nimport { getTwilightContext, subscribeTwilight, type TwilightLocation } from '../twilight/context';\nimport { RouteId } from '../router/route-ids';\nimport { withoutStoreUrlBase } from '../utils/store-base';\nimport { extractScopeFromUrl } from '../utils/cookies';\nimport { getActiveScope } from '../api/client';\nimport { openNativeBrowser } from '../native-app';\nimport { SUPPORTED_LOCALES } from './locale-constants';\n\n// Re-export RouteId from centralized location\nexport { RouteId } from '../router/route-ids';\nexport type { RouteIdType } from '../router/route-ids';\n\n// Re-export TwilightLocation as Location for convenience\nexport type { TwilightLocation as Location } from '../twilight/context';\n\nexport type NavigateOptions = { replace?: boolean };\nexport type NavigateFn = (to: string, opts?: NavigateOptions) => void;\n\n/** Redirect error - thrown to trigger a redirect */\nexport class RedirectError extends Error {\n constructor(\n public path: string,\n public replace = false\n ) {\n super(`Redirect to ${path}`);\n this.name = 'RedirectError';\n }\n}\n\n/** NotFound error - thrown to trigger 404 */\nexport class NotFoundError extends Error {\n constructor(message?: string) {\n super(message ?? 'Not Found');\n this.name = 'NotFoundError';\n }\n}\n\n/** Unauthorized error - thrown to trigger 401 */\nexport class UnauthorizedError extends Error {\n constructor(message?: string) {\n super(message ?? 'Unauthorized');\n this.name = 'UnauthorizedError';\n }\n}\n\nexport { SettingsError } from '../utils/errors';\n\nexport const defaultNavigate: NavigateFn = (to, opts) => {\n if (opts?.replace) window.location.replace(to);\n else window.location.href = to;\n};\n\nexport interface RedirectOptions {\n replace?: boolean;\n /** HTTP status of a server redirect, e.g. 301 for a URL that moved for good. Defaults to 307. */\n statusCode?: number;\n}\n\n/** Default redirect - throws RedirectError with browser navigation as fallback */\nexport const defaultRedirect = (path: string, opts?: RedirectOptions): never => {\n const replace = opts?.replace ?? false;\n\n // In browser, perform actual navigation before throwing\n if (typeof window !== 'undefined') {\n if (replace) window.location.replace(path);\n else window.location.href = path;\n }\n\n throw new RedirectError(path, replace);\n};\n\n/** Default notFound - throws generic error */\nexport const defaultNotFound = (message?: string): never => {\n throw new NotFoundError(message);\n};\n\n/** Default unauthorized - throws generic error */\nexport const defaultUnauthorized = (message?: string): never => {\n throw new UnauthorizedError(message);\n};\n\n// ─── Framework-agnostic functions for loaders (outside React context) ──────\n\ninterface NavigationHandlers {\n notFound: (message?: string) => never;\n redirect: (path: string, opts?: RedirectOptions) => never;\n unauthorized: (message?: string) => never;\n}\n\n// One set per isolate, so a loader in another copy of this module (a separate\n// package entry, a dev reload) still throws what the framework adapter configured.\nconst HANDLERS_KEY = Symbol.for('salla.twilight.navigation.handlers');\nconst registry = globalThis as { [HANDLERS_KEY]?: NavigationHandlers };\nconst handlers: NavigationHandlers = (registry[HANDLERS_KEY] ??= {\n notFound: defaultNotFound,\n redirect: defaultRedirect,\n unauthorized: defaultUnauthorized,\n});\n\n/**\n * Throw a notFound error. Use in route loaders.\n * Framework adapters will override this to throw framework-specific errors.\n *\n * @example\n * ```ts\n * // In loader\n * import { notFound } from '@salla.sa/twilight-theme-engine/providers';\n *\n * if (!data) throw notFound();\n * ```\n */\nexport function notFound(message?: string): never {\n return handlers.notFound(message);\n}\n\n/**\n * Throw an unauthorized error. Use in route loaders to protect authenticated routes.\n * Framework adapters will override this to throw framework-specific errors.\n *\n * @example\n * ```ts\n * import { unauthorized } from '@salla.sa/twilight-theme-engine/providers';\n *\n * if (!user) throw unauthorized();\n * ```\n */\nexport function unauthorized(message?: string): never {\n return handlers.unauthorized(message);\n}\n\n/**\n * Throw a redirect. Use in route loaders.\n * Framework adapters will override this to throw framework-specific redirects.\n *\n * @param path - Path to redirect to\n * @param opts - Redirect options\n *\n * @example\n * ```ts\n * // In loader\n * import { redirect } from '@salla.sa/twilight-theme-engine/providers';\n *\n * throw redirect('/ar/cart');\n * ```\n */\nexport function redirect(path: string, opts?: RedirectOptions): never {\n return handlers.redirect(path, opts);\n}\n\n/**\n * Configure navigation functions for a specific framework.\n * Called by framework adapters during setup.\n */\nexport function configureNavigation(config: {\n notFound?: (message?: string) => never;\n redirect?: (path: string, opts?: RedirectOptions) => never;\n unauthorized?: (_message?: string) => never;\n}): void {\n if (config.notFound) handlers.notFound = config.notFound;\n if (config.redirect) handlers.redirect = config.redirect;\n if (config.unauthorized) handlers.unauthorized = config.unauthorized;\n}\n\n// ─── Loader Error Handling Helpers ─────────────────────────────────────────────\n\n/**\n * Wraps an API promise with error handling for route loaders.\n * On a 404/410 response throws notFound(); other errors propagate.\n *\n * @example\n * ```ts\n * findOrThrow: (id, locale) => orThrow(product.find(id, locale)),\n * const data = await product.findOrThrow(id, locale);\n * ```\n */\nexport async function orThrow<T>(promise: Promise<T>): Promise<T> {\n try {\n return await promise;\n } catch (error) {\n console.debug('[loader error]', error);\n if (isMissingResource(error)) return notFound();\n throw error;\n }\n}\n\n/** The HTTP status an API error carries, if it is one. */\nexport function responseStatusOf(error: unknown): number | undefined {\n const status = (error as { response?: { status?: unknown } } | null)?.response?.status;\n return typeof status === 'number' ? status : undefined;\n}\n\n/** The API answers a missing resource with 410, sometimes 404. */\nexport function isMissingResource(error: unknown): boolean {\n const status = responseStatusOf(error);\n return status === 404 || status === 410;\n}\n\n/**\n * Wraps an API promise with error handling for authenticated route loaders.\n * On a 401/403 response throws unauthorized(); other errors propagate.\n *\n * @example\n * ```ts\n * const data = await orUnauthorized(order.find(id, locale));\n * ```\n */\nexport async function orUnauthorized<T>(promise: Promise<T>): Promise<T> {\n try {\n return await promise;\n } catch (error) {\n const status = (error as unknown as { response?: { status?: number } })?.response?.status;\n if (status === 401 || status === 403) {\n throw unauthorized();\n }\n throw error;\n }\n}\n\n// ─── Navigation Context ──────────────────────────────────────────────────────\n\ninterface NavigationContextValue {\n navigate: NavigateFn;\n}\n\nconst NavigationContext = createContext<NavigationContextValue>({\n navigate: defaultNavigate,\n});\n\nexport function NavigationProvider({\n navigate,\n children,\n}: {\n navigate: NavigateFn;\n children: ReactNode;\n}) {\n return createElement(NavigationContext.Provider, { value: { navigate } }, children);\n}\n\n// ─── Primary Hooks (High Compatibility) ───────────────────────────────────────\n\n/**\n * Get the navigate function.\n * Matches React Router, TanStack Router, and Remix naming convention.\n *\n * @returns Navigate function\n *\n * @example\n * ```tsx\n * const navigate = useNavigate();\n * navigate('/cart');\n * navigate('/cart', { replace: true });\n * ```\n */\nexport function useNavigate(): NavigateFn {\n return useContext(NavigationContext).navigate;\n}\n\n/**\n * Get the current route ID.\n * Used for route detection (isHome, isProduct, etc.)\n *\n * @returns Current route ID string\n *\n * @example\n * ```tsx\n * const routeId = useRouteId();\n * if (routeId === RouteId.INDEX) { ... }\n * ```\n */\nexport function useRouteId(): string {\n return useSyncExternalStore(\n subscribeTwilight,\n () => getTwilightContext().routeId,\n () => getTwilightContext().routeId\n );\n}\n\n/**\n * Get the current location (TanStack Router compatible).\n * Reads from the router context for SSR compatibility.\n *\n * @returns Location object matching TanStack's ParsedLocation\n *\n * @example\n * ```tsx\n * const location = useLocation();\n * console.log(location.pathname); // '/ar/products'\n * console.log(location.searchStr); // '?q=shoes&page=2'\n * console.log(location.search); // { q: 'shoes', page: '2' }\n * console.log(location.href); // 'https://store.com/ar/products?q=shoes'\n * console.log(location.hash); // '#section-1'\n * console.log(location.state); // { scrollY: 100 }\n * ```\n */\nexport function useLocation<TSearch = Record<string, unknown>>(): TwilightLocation<TSearch> {\n useTwilight();\n return getTwilightContext().location as TwilightLocation<TSearch>;\n}\n\n// ─── Selector Hooks ───────────────────────────────────────────────────────────\n\n/**\n * Check if current page is homepage */\nexport function useIsHome(): boolean {\n return useRouteId() === RouteId.INDEX;\n}\n\n/** Check if current page is product detail */\nexport function useIsProduct(): boolean {\n return useRouteId() === RouteId.PRODUCT_SINGLE;\n}\n\n/** Check if current page is cart */\nexport function useIsCart(): boolean {\n return useRouteId() === RouteId.CART;\n}\n\n/** Check if current page is in customer area */\nexport function useIsCustomer(): boolean {\n return useRouteId().startsWith('customer');\n}\n\n/** Check if current page is blog related */\nexport function useIsBlog(): boolean {\n const routeId = useRouteId();\n return routeId === RouteId.BLOG_INDEX || routeId === RouteId.BLOG_SINGLE;\n}\n\n/** Check if current page is brands related */\nexport function useIsBrands(): boolean {\n const routeId = useRouteId();\n return routeId === RouteId.BRANDS_INDEX || routeId === RouteId.BRANDS_SINGLE;\n}\n\n/** Check if current page is search */\nexport function useIsSearch(): boolean {\n return useRouteId() === RouteId.SEARCH;\n}\n\n// ─── Navigation Interceptor ──────────────────────────────────────────────────\n\n/**\n * NavigationInterceptor - Framework-agnostic SPA navigation interceptor.\n *\n * Uses navigate function from NavigationProvider context and store URL\n * from TwilightProvider to intercept same-origin navigations.\n */\nexport function NavigationInterceptor(): null {\n const navigate = useNavigate();\n const { store } = useTwilight();\n useNavigationInterceptor(navigate, store?.url, store?.username);\n return null;\n}\n\n/**\n * resolveRelativePath - Resolve href to store-relative path\n *\n * Returns `null` when the URL is not on the same origin as the store,\n * keeping this helper safe against open-redirect misuse.\n *\n * The path is the application's, not the address on Salla's host: a store served\n * under its username there (`https://demostore.salla.sa/ar/dev-…/`) hands out links\n * carrying that segment, and it is removed. On a host where the store is the root\n * of every URL, the router adds its own segment back when it navigates.\n *\n * @param href - The URL to resolve\n * @param storeUrl - The store's base URL\n * @param username - The store's username, which tells that segment apart from a\n * locale spelled the same way (see `withoutStoreUrlBase`)\n * @returns The relative path string, or null if the URL is external\n */\nexport function resolveRelativePath(\n href: string,\n storeUrl: string,\n username?: string | null\n): string | null {\n try {\n const target = new URL(href, window.location.href);\n const base = new URL(storeUrl);\n\n if (target.origin !== base.origin) return null;\n\n const pathname = withoutStoreUrlBase(target.pathname, base.pathname, username);\n const path = pathname + target.search + target.hash;\n return path || '/';\n } catch {\n return null;\n }\n}\n\n/**\n * Pages the Store app renders, not the theme: checkout, payment, digital orders, a\n * gift's page, the booking page (`cart/booking/<id>`, where \"book now\" leads), the\n * SDK's auth endpoints, and the order invoice. The storefront dispatcher sends a\n * document request for them to the app (`bypass.ts` in store-front-dispacher), so\n * only a page load reaches them; rendered in place, the router has no route and\n * shows its 404. Keep in step with the dispatcher's list.\n */\nconst APP_PATH_PREFIXES = [\n '/checkout',\n '/payment',\n '/orders/digital',\n '/gifts',\n '/cart/booking',\n '/auth/jwt',\n '/auth/refresh',\n];\nconst APP_PATH_PATTERNS = [/^\\/account\\/orders\\/[^/]+\\/print\\/?$/];\n\n/** Whether a store-relative path (`/ar/cart/booking/1?x`) is one of the app's pages. */\nexport function isAppPath(path: string): boolean {\n let pathname = path.split(/[?#]/)[0];\n const first = pathname.split('/')[1];\n if ((SUPPORTED_LOCALES as readonly string[]).includes(first)) {\n pathname = pathname.slice(first.length + 1) || '/';\n }\n return (\n APP_PATH_PREFIXES.some((prefix) => pathname === prefix || pathname.startsWith(`${prefix}/`)) ||\n APP_PATH_PATTERNS.some((pattern) => pattern.test(pathname))\n );\n}\n\n/**\n * EXTERNAL_TARGETS - Target attribute values that indicate external navigation\n */\nconst EXTERNAL_TARGETS = new Set(['_blank', '_parent', '_top']);\n\n/**\n * shouldIntercept - Determine if an anchor click should be intercepted by SPA router\n *\n * Follows browser conventions: modifier keys, external targets, downloads,\n * and non-HTTP(S) protocols are left to the browser's default behavior.\n *\n * @param anchor - The anchor element that was clicked\n * @param event - The click event\n * @returns true if the click should be intercepted by the router\n */\nexport function shouldIntercept(anchor: HTMLAnchorElement, event: MouseEvent): boolean {\n if (event.defaultPrevented) return false;\n\n if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return false;\n\n const targetAttr = anchor.getAttribute('target');\n if (targetAttr && EXTERNAL_TARGETS.has(targetAttr)) return false;\n\n if (anchor.hasAttribute('download')) return false;\n\n const rel = (anchor.getAttribute('rel') ?? '').split(/\\s+/);\n if (rel.includes('external')) return false;\n\n const href = anchor.getAttribute('href') ?? '';\n if (!href) return false;\n\n try {\n const url = new URL(href, window.location.href);\n if (url.protocol.startsWith('javascript:')) return false;\n } catch {\n return false;\n }\n\n return true;\n}\n\n/**\n * Whether `href` switches market: its `?scope=` names another scope than the active one,\n * or the same scope with another branch (`allocation_type` / `allocation_id`).\n *\n * Twig switches market with a full load (the SDK's scopes modal calls\n * `location.replace('?scope=<id>')`), and only a document request lets the server render\n * the new market and record it in the `scope` cookie. Left to the browser, not the router.\n */\nexport function switchesScope(href: string): boolean {\n let url: URL;\n try {\n url = new URL(href, window.location.href);\n } catch {\n return false;\n }\n const requested = extractScopeFromUrl(url.href);\n if (!requested) return false;\n const active = getActiveScope();\n if (requested.id !== active?.id) return true;\n return (\n requested.allocation_id != null &&\n (requested.allocation_id !== active.allocation_id ||\n requested.allocation_type !== active.allocation_type)\n );\n}\n\n/**\n * Whether an untouched tap on `anchor` would load a page off the store: an\n * http(s) link to neither this page's origin nor the store's. `mailto:`, `tel:`\n * and the like are the device's to handle, and are left alone. So are product\n * gallery slides (`data-fslightbox`): each links to its own image or video, and\n * the gallery opens it in the app's native gallery, from a click handler that\n * runs after this capture-phase check, so `event.defaultPrevented` is still false.\n */\nexport function leavesStore(\n anchor: HTMLAnchorElement,\n event: MouseEvent,\n storeUrl: string | undefined\n): boolean {\n if (event.defaultPrevented || anchor.hasAttribute('download')) return false;\n if (anchor.hasAttribute('data-fslightbox')) return false;\n let url: URL;\n try {\n url = new URL(anchor.getAttribute('href') ?? '', window.location.href);\n } catch {\n return false;\n }\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return false;\n if (url.origin === window.location.origin) return false;\n try {\n return !storeUrl || url.origin !== new URL(storeUrl).origin;\n } catch {\n return true;\n }\n}\n\nfunction isSamePage(href: string): boolean {\n const url = new URL(href, window.location.href);\n return url.pathname === window.location.pathname && url.search === window.location.search;\n}\n\nfunction closeEnclosingModal(path: EventTarget[]): void {\n const modal = path.find(\n (el): el is HTMLElement & { close?: () => unknown } =>\n el instanceof HTMLElement && el.localName === 'salla-modal'\n );\n void modal?.close?.();\n}\n\n// ─── Navigation Interceptor Hook (framework-agnostic) ─────────────────────────\n\n/**\n * Hook for navigation interception using framework-agnostic navigate function.\n * Framework adapters call this with their navigate implementation.\n */\nexport function useNavigationInterceptor(\n navigate: NavigateFn,\n storeUrl: string | undefined,\n username?: string\n): void {\n const storeUrlRef = useRef<string | undefined>(storeUrl);\n const usernameRef = useRef<string | undefined>(username);\n\n useEffect(() => {\n storeUrlRef.current = storeUrl;\n usernameRef.current = username;\n }, [storeUrl, username]);\n\n const tryNavigate = useCallback(\n (href: string, replace = false): boolean => {\n const currentStoreUrl = storeUrlRef.current;\n if (!currentStoreUrl || switchesScope(href)) return false;\n\n const relativePath = resolveRelativePath(href, currentStoreUrl, usernameRef.current);\n // The app's pages are left to the browser: only a page load reaches the app.\n if (!relativePath || isAppPath(relativePath)) return false;\n\n navigate(relativePath, { replace });\n return true;\n },\n [navigate]\n );\n\n useEffect(() => {\n // ── Pointer movement tracking ────────────────────────────────────────────\n // Detect drag/scroll gestures to avoid intercepting clicks from swipes.\n let pointerStartX = 0;\n let pointerStartY = 0;\n let pointerSwiped = false;\n const MOVE_THRESHOLD = 10;\n\n function handlePointerDown(event: PointerEvent): void {\n pointerStartX = event.clientX;\n pointerStartY = event.clientY;\n pointerSwiped = false;\n }\n\n function handlePointerUp(event: PointerEvent): void {\n const dx = Math.abs(event.clientX - pointerStartX);\n const dy = Math.abs(event.clientY - pointerStartY);\n pointerSwiped = dx > MOVE_THRESHOLD || dy > MOVE_THRESHOLD;\n }\n\n // ── Anchor click interception ────────────────────────────────────────────\n function handleClick(event: MouseEvent): void {\n // Ignore clicks that resulted from swipe/drag gestures.\n if (pointerSwiped) {\n pointerSwiped = false;\n return;\n }\n\n const path = event.composedPath();\n const anchor = path.find((el): el is HTMLAnchorElement => el instanceof HTMLAnchorElement);\n\n // Inside the Salla app, a link that leaves the store opens in the device's\n // browser; loaded in the app's webview it would strand the shopper there.\n if (\n anchor &&\n leavesStore(anchor, event, storeUrlRef.current) &&\n openNativeBrowser(anchor.href)\n ) {\n event.preventDefault();\n return;\n }\n\n if (!anchor || !shouldIntercept(anchor, event)) return;\n\n const samePage = isSamePage(anchor.href);\n if (tryNavigate(anchor.href, false)) {\n event.preventDefault();\n // A page load would close a web component's modal; an in-app navigation leaves it open.\n if (!samePage) closeEnclosingModal(path);\n }\n }\n\n // ── window.location.assign patch ─────────────────────────────────────────\n const origAssign = window.location.assign;\n function patchedAssign(url: string): void {\n if (!tryNavigate(url, false)) origAssign.call(window.location, url);\n }\n\n // ── window.location.replace patch ────────────────────────────────────────\n const origReplace = window.location.replace;\n function patchedReplace(url: string): void {\n if (!tryNavigate(url, true)) origReplace.call(window.location, url);\n }\n\n let assignPatched = false;\n let replacePatched = false;\n try {\n window.location.assign = patchedAssign;\n assignPatched = true;\n } catch {\n /* non-writable in this environment */\n }\n try {\n window.location.replace = patchedReplace;\n replacePatched = true;\n } catch {\n /* non-writable in this environment */\n }\n\n document.addEventListener('click', handleClick, { capture: true });\n document.addEventListener('pointerdown', handlePointerDown, { passive: true });\n document.addEventListener('pointerup', handlePointerUp, { passive: true });\n\n return () => {\n document.removeEventListener('click', handleClick, { capture: true });\n document.removeEventListener('pointerdown', handlePointerDown);\n document.removeEventListener('pointerup', handlePointerUp);\n if (assignPatched) {\n try {\n window.location.assign = origAssign;\n } catch {\n /* ignore */\n }\n }\n if (replacePatched) {\n try {\n window.location.replace = origReplace;\n } catch {\n /* ignore */\n }\n }\n };\n }, [tryNavigate]);\n}\n\n// ─── Client navigation config ─────────────────────────────────────────────────\n\nexport interface ClientNav {\n /** Framework adapter to use for client-side navigation. @default 'tanstack' */\n framework?: 'tanstack' | 'nextjs';\n /**\n * Intercept same-origin `<a>` clicks and `window.location` calls from web\n * components and route them through the framework router.\n * @default true\n */\n interceptLinks?: boolean;\n}\n\n// Framework adapter setups are registered in navigation-setups.ts to avoid circular imports.\n"]}
@@ -6,7 +6,7 @@ export { BlogSkeleton, BreadcrumbSkeleton, CustomerLayoutSkeleton, CustomerPageS
6
6
 
7
7
  // src/components/common/lazy.ts
8
8
  var LazyBreadcrumb = lazyComponent(
9
- () => import('./Breadcrumb-7QGTM5S5.js').then((m) => ({ default: m.Breadcrumb }))
9
+ () => import('./Breadcrumb-SCJZW4RO.js').then((m) => ({ default: m.Breadcrumb }))
10
10
  );
11
11
  var LazyNoContent = lazyComponent(
12
12
  () => import('./NoContent-7A7OR2NZ.js').then((m) => ({ default: m.NoContent }))
@@ -155,5 +155,5 @@ function DeferredData({ promise, fallback, children }) {
155
155
  }
156
156
 
157
157
  export { DeferredData, Image, LazyBreadcrumb, LazyCurrencySymbol, LazyErrorPage, LazyNoContent, LazyRenderWhenVisible, resolveDeferred };
158
- //# sourceMappingURL=chunk-BBMRL2QU.js.map
159
- //# sourceMappingURL=chunk-BBMRL2QU.js.map
158
+ //# sourceMappingURL=chunk-TDIENKPW.js.map
159
+ //# sourceMappingURL=chunk-TDIENKPW.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/common/lazy.ts","../src/components/common/Image.tsx","../src/components/common/DeferredData.tsx"],"names":["Image","memo","DeferredRenderer","jsx"],"mappings":"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","file":"chunk-BBMRL2QU.js","sourcesContent":["import { lazyComponent } from '../../utils/lazy-component';\n\n/**\n * Lazy-loaded common components\n * Use these for code-splitting non-critical components\n *\n * NOTE: Skeleton components are sourced from @salla.sa/twilight-components-react\n * because they must render instantly during route transitions.\n */\nexport const LazyBreadcrumb = lazyComponent(() =>\n import('./Breadcrumb').then((m) => ({ default: m.Breadcrumb }))\n);\n\nexport const LazyNoContent = lazyComponent(() =>\n import('./NoContent').then((m) => ({ default: m.NoContent }))\n);\n\nexport const LazyRenderWhenVisible = lazyComponent(() =>\n import('./RenderWhenVisible').then((m) => ({ default: m.RenderWhenVisible }))\n);\n\nexport const LazyCurrencySymbol = lazyComponent(() =>\n import('./CurrencySymbol').then((m) => ({ default: m.CurrencySymbol }))\n);\n\nexport const LazyErrorPage = lazyComponent(() =>\n import('./ErrorPage').then((m) => ({ default: m.ErrorPage }))\n);\n","import { memo } from 'react';\nimport { getCdnImageSrcSet, getCdnImageUrl } from '../../utils/cdn-image';\n\nexport interface ImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {\n /** Image source URL */\n src?: string;\n /** Alt text for accessibility */\n alt: string;\n /** Whether this image is above the fold / LCP candidate */\n priority?: boolean;\n /** Aspect ratio for CLS prevention (e.g. \"16/9\", \"1/1\", \"4/3\") */\n aspectRatio?: string;\n /** Object fit mode */\n objectFit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down';\n /** Mobile image source for <picture> element */\n mobileSrc?: string;\n /** Mobile breakpoint for <source> media query (default: 768px) */\n mobileBreakpoint?: number;\n /** Skip the wrapper div */\n noWrapper?: boolean;\n /**\n * CDN widths for a density `srcset`. Pass `sizes` so the browser picks a\n * candidate close to the displayed CSS size.\n */\n srcSetWidths?: readonly number[];\n}\n\n/**\n * Optimized Image component with native browser lazy loading.\n *\n * - Uses `loading=\"lazy\"` + `decoding=\"async\"` by default for below-the-fold images.\n * - Uses `loading=\"eager\"` + `fetchPriority=\"high\"` for priority (above-the-fold / LCP) images.\n * - Supports responsive `<picture>` with mobile source.\n * - Provides `aspectRatio` for CLS prevention.\n * - When `srcSetWidths` + `sizes` are set, emits a sized CDN `srcset`.\n */\nexport const Image = memo(function Image({\n src,\n alt,\n priority = false,\n aspectRatio,\n objectFit = 'cover',\n mobileSrc,\n mobileBreakpoint = 768,\n noWrapper = false,\n className = '',\n style,\n width,\n height,\n sizes,\n srcSet: srcSetProp,\n srcSetWidths,\n ...rest\n}: ImageProps) {\n if (!src) return null;\n\n const generatedSrcSet =\n srcSetProp || (srcSetWidths?.length ? getCdnImageSrcSet(src, srcSetWidths) : undefined);\n const generatedMobileSrcSet =\n srcSetWidths?.length && mobileSrc ? getCdnImageSrcSet(mobileSrc, srcSetWidths) : undefined;\n const cdnWidth = srcSetWidths?.length\n ? width != null && srcSetWidths.includes(Number(width))\n ? Number(width)\n : srcSetWidths[0]\n : width;\n // Width-only CDN URLs so `src` matches a srcset candidate for preload.\n const cdnOptions = generatedSrcSet ? { width: cdnWidth } : { width: cdnWidth, height };\n\n const optimizedSrc = getCdnImageUrl(src, cdnOptions);\n const optimizedMobileSrc = getCdnImageUrl(mobileSrc, cdnOptions);\n\n const objectFitClass = `object-${objectFit}`;\n const imgClasses = [className, objectFitClass].filter(Boolean).join(' ');\n\n const loadingProps: React.ImgHTMLAttributes<HTMLImageElement> = priority\n ? { loading: 'eager' as const, fetchPriority: 'high' as const, decoding: 'sync' as const }\n : { loading: 'lazy' as const, decoding: 'async' as const };\n\n const hasWrapper = !noWrapper && !!aspectRatio;\n\n const img = (\n <img\n src={optimizedSrc}\n alt={alt}\n className={imgClasses}\n width={width}\n height={height}\n sizes={sizes}\n srcSet={generatedSrcSet}\n // With a wrapper, `style` goes to the wrapper; otherwise it must land on the <img>.\n style={hasWrapper ? undefined : style}\n {...loadingProps}\n {...rest}\n />\n );\n\n const imgElement = optimizedMobileSrc ? (\n <picture>\n <source\n media={`(max-width: ${mobileBreakpoint}px)`}\n srcSet={generatedMobileSrcSet || optimizedMobileSrc}\n sizes={generatedMobileSrcSet ? sizes : undefined}\n />\n {img}\n </picture>\n ) : (\n img\n );\n\n if (!hasWrapper) {\n return imgElement;\n }\n\n return (\n <div className=\"relative overflow-hidden\" style={{ aspectRatio, ...style }}>\n {imgElement}\n </div>\n );\n});\n","import { Suspense, memo, use, type ReactNode } from 'react';\n\nexport interface DeferredDataProps<T> {\n promise: Promise<T> | T;\n fallback: ReactNode;\n children: (data: T) => ReactNode;\n}\n\nconst DEFERRED_KEY = Symbol.for('twilight.deferred');\n\ninterface DeferredState<T> {\n status: 'pending' | 'success' | 'error';\n value?: T;\n error?: unknown;\n}\n\nexport interface DeferredPromise<T> extends Promise<T> {\n [DEFERRED_KEY]?: DeferredState<T>;\n}\n\nconst seenPromises = typeof WeakSet !== 'undefined' ? new WeakSet() : null;\n\nfunction defer<T>(promise: Promise<T>): DeferredPromise<T> {\n const existing = (promise as DeferredPromise<T>)[DEFERRED_KEY];\n if (existing) return promise as DeferredPromise<T>;\n\n const state: DeferredState<T> = { status: 'pending' };\n\n const deferred = promise as DeferredPromise<T>;\n deferred[DEFERRED_KEY] = state;\n\n promise.then(\n (value) => {\n state.status = 'success';\n state.value = value;\n },\n (error) => {\n state.status = 'error';\n state.error = error;\n }\n );\n\n return deferred;\n}\n\nconst DeferredRenderer = memo(function DeferredRenderer<T>({\n promise,\n children,\n}: {\n promise: DeferredPromise<T>;\n children: (data: T) => ReactNode;\n}) {\n const state = promise[DEFERRED_KEY];\n\n if (state?.status === 'success') {\n return children(state.value!);\n }\n\n if (state?.status === 'error') {\n throw state.error;\n }\n\n const data = use(promise);\n return children(data);\n}) as <T>(props: { promise: DeferredPromise<T>; children: (data: T) => ReactNode }) => ReactNode;\n\nfunction DeferredFallback<T>({\n promise,\n fallback,\n children,\n}: {\n promise: DeferredPromise<T>;\n fallback: ReactNode;\n children: (data: T) => ReactNode;\n}) {\n const state = promise[DEFERRED_KEY];\n if (state?.status === 'success') {\n return children(state.value!);\n }\n return <>{fallback}</>;\n}\n\nexport function resolveDeferred<T>(\n promiseOrValue: Promise<T> | T | undefined\n): T | Promise<T> | undefined {\n if (promiseOrValue == null || !(promiseOrValue instanceof Promise)) return promiseOrValue;\n const deferred = defer(promiseOrValue);\n const state = deferred[DEFERRED_KEY];\n if (state?.status === 'success') return state.value as T;\n return promiseOrValue;\n}\n\nexport function DeferredData<T>({ promise, fallback, children }: DeferredDataProps<T>) {\n if (!(promise instanceof Promise)) {\n return children(promise);\n }\n\n if (seenPromises && promise instanceof Promise) seenPromises.add(promise);\n\n const deferred = defer(promise);\n const state = deferred[DEFERRED_KEY];\n\n if (state?.status === 'success') {\n return children(state.value!);\n }\n\n if (state?.status === 'error') {\n throw state.error;\n }\n\n return (\n <Suspense\n fallback={\n <DeferredFallback promise={deferred} fallback={fallback}>\n {children}\n </DeferredFallback>\n }\n >\n <DeferredRenderer promise={deferred}>{children}</DeferredRenderer>\n </Suspense>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/components/common/lazy.ts","../src/components/common/Image.tsx","../src/components/common/DeferredData.tsx"],"names":["Image","memo","DeferredRenderer","jsx"],"mappings":"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","file":"chunk-TDIENKPW.js","sourcesContent":["import { lazyComponent } from '../../utils/lazy-component';\n\n/**\n * Lazy-loaded common components\n * Use these for code-splitting non-critical components\n *\n * NOTE: Skeleton components are sourced from @salla.sa/twilight-components-react\n * because they must render instantly during route transitions.\n */\nexport const LazyBreadcrumb = lazyComponent(() =>\n import('./Breadcrumb').then((m) => ({ default: m.Breadcrumb }))\n);\n\nexport const LazyNoContent = lazyComponent(() =>\n import('./NoContent').then((m) => ({ default: m.NoContent }))\n);\n\nexport const LazyRenderWhenVisible = lazyComponent(() =>\n import('./RenderWhenVisible').then((m) => ({ default: m.RenderWhenVisible }))\n);\n\nexport const LazyCurrencySymbol = lazyComponent(() =>\n import('./CurrencySymbol').then((m) => ({ default: m.CurrencySymbol }))\n);\n\nexport const LazyErrorPage = lazyComponent(() =>\n import('./ErrorPage').then((m) => ({ default: m.ErrorPage }))\n);\n","import { memo } from 'react';\nimport { getCdnImageSrcSet, getCdnImageUrl } from '../../utils/cdn-image';\n\nexport interface ImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {\n /** Image source URL */\n src?: string;\n /** Alt text for accessibility */\n alt: string;\n /** Whether this image is above the fold / LCP candidate */\n priority?: boolean;\n /** Aspect ratio for CLS prevention (e.g. \"16/9\", \"1/1\", \"4/3\") */\n aspectRatio?: string;\n /** Object fit mode */\n objectFit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down';\n /** Mobile image source for <picture> element */\n mobileSrc?: string;\n /** Mobile breakpoint for <source> media query (default: 768px) */\n mobileBreakpoint?: number;\n /** Skip the wrapper div */\n noWrapper?: boolean;\n /**\n * CDN widths for a density `srcset`. Pass `sizes` so the browser picks a\n * candidate close to the displayed CSS size.\n */\n srcSetWidths?: readonly number[];\n}\n\n/**\n * Optimized Image component with native browser lazy loading.\n *\n * - Uses `loading=\"lazy\"` + `decoding=\"async\"` by default for below-the-fold images.\n * - Uses `loading=\"eager\"` + `fetchPriority=\"high\"` for priority (above-the-fold / LCP) images.\n * - Supports responsive `<picture>` with mobile source.\n * - Provides `aspectRatio` for CLS prevention.\n * - When `srcSetWidths` + `sizes` are set, emits a sized CDN `srcset`.\n */\nexport const Image = memo(function Image({\n src,\n alt,\n priority = false,\n aspectRatio,\n objectFit = 'cover',\n mobileSrc,\n mobileBreakpoint = 768,\n noWrapper = false,\n className = '',\n style,\n width,\n height,\n sizes,\n srcSet: srcSetProp,\n srcSetWidths,\n ...rest\n}: ImageProps) {\n if (!src) return null;\n\n const generatedSrcSet =\n srcSetProp || (srcSetWidths?.length ? getCdnImageSrcSet(src, srcSetWidths) : undefined);\n const generatedMobileSrcSet =\n srcSetWidths?.length && mobileSrc ? getCdnImageSrcSet(mobileSrc, srcSetWidths) : undefined;\n const cdnWidth = srcSetWidths?.length\n ? width != null && srcSetWidths.includes(Number(width))\n ? Number(width)\n : srcSetWidths[0]\n : width;\n // Width-only CDN URLs so `src` matches a srcset candidate for preload.\n const cdnOptions = generatedSrcSet ? { width: cdnWidth } : { width: cdnWidth, height };\n\n const optimizedSrc = getCdnImageUrl(src, cdnOptions);\n const optimizedMobileSrc = getCdnImageUrl(mobileSrc, cdnOptions);\n\n const objectFitClass = `object-${objectFit}`;\n const imgClasses = [className, objectFitClass].filter(Boolean).join(' ');\n\n const loadingProps: React.ImgHTMLAttributes<HTMLImageElement> = priority\n ? { loading: 'eager' as const, fetchPriority: 'high' as const, decoding: 'sync' as const }\n : { loading: 'lazy' as const, decoding: 'async' as const };\n\n const hasWrapper = !noWrapper && !!aspectRatio;\n\n const img = (\n <img\n src={optimizedSrc}\n alt={alt}\n className={imgClasses}\n width={width}\n height={height}\n sizes={sizes}\n srcSet={generatedSrcSet}\n // With a wrapper, `style` goes to the wrapper; otherwise it must land on the <img>.\n style={hasWrapper ? undefined : style}\n {...loadingProps}\n {...rest}\n />\n );\n\n const imgElement = optimizedMobileSrc ? (\n <picture>\n <source\n media={`(max-width: ${mobileBreakpoint}px)`}\n srcSet={generatedMobileSrcSet || optimizedMobileSrc}\n sizes={generatedMobileSrcSet ? sizes : undefined}\n />\n {img}\n </picture>\n ) : (\n img\n );\n\n if (!hasWrapper) {\n return imgElement;\n }\n\n return (\n <div className=\"relative overflow-hidden\" style={{ aspectRatio, ...style }}>\n {imgElement}\n </div>\n );\n});\n","import { Suspense, memo, use, type ReactNode } from 'react';\n\nexport interface DeferredDataProps<T> {\n promise: Promise<T> | T;\n fallback: ReactNode;\n children: (data: T) => ReactNode;\n}\n\nconst DEFERRED_KEY = Symbol.for('twilight.deferred');\n\ninterface DeferredState<T> {\n status: 'pending' | 'success' | 'error';\n value?: T;\n error?: unknown;\n}\n\nexport interface DeferredPromise<T> extends Promise<T> {\n [DEFERRED_KEY]?: DeferredState<T>;\n}\n\nconst seenPromises = typeof WeakSet !== 'undefined' ? new WeakSet() : null;\n\nfunction defer<T>(promise: Promise<T>): DeferredPromise<T> {\n const existing = (promise as DeferredPromise<T>)[DEFERRED_KEY];\n if (existing) return promise as DeferredPromise<T>;\n\n const state: DeferredState<T> = { status: 'pending' };\n\n const deferred = promise as DeferredPromise<T>;\n deferred[DEFERRED_KEY] = state;\n\n promise.then(\n (value) => {\n state.status = 'success';\n state.value = value;\n },\n (error) => {\n state.status = 'error';\n state.error = error;\n }\n );\n\n return deferred;\n}\n\nconst DeferredRenderer = memo(function DeferredRenderer<T>({\n promise,\n children,\n}: {\n promise: DeferredPromise<T>;\n children: (data: T) => ReactNode;\n}) {\n const state = promise[DEFERRED_KEY];\n\n if (state?.status === 'success') {\n return children(state.value!);\n }\n\n if (state?.status === 'error') {\n throw state.error;\n }\n\n const data = use(promise);\n return children(data);\n}) as <T>(props: { promise: DeferredPromise<T>; children: (data: T) => ReactNode }) => ReactNode;\n\nfunction DeferredFallback<T>({\n promise,\n fallback,\n children,\n}: {\n promise: DeferredPromise<T>;\n fallback: ReactNode;\n children: (data: T) => ReactNode;\n}) {\n const state = promise[DEFERRED_KEY];\n if (state?.status === 'success') {\n return children(state.value!);\n }\n return <>{fallback}</>;\n}\n\nexport function resolveDeferred<T>(\n promiseOrValue: Promise<T> | T | undefined\n): T | Promise<T> | undefined {\n if (promiseOrValue == null || !(promiseOrValue instanceof Promise)) return promiseOrValue;\n const deferred = defer(promiseOrValue);\n const state = deferred[DEFERRED_KEY];\n if (state?.status === 'success') return state.value as T;\n return promiseOrValue;\n}\n\nexport function DeferredData<T>({ promise, fallback, children }: DeferredDataProps<T>) {\n if (!(promise instanceof Promise)) {\n return children(promise);\n }\n\n if (seenPromises && promise instanceof Promise) seenPromises.add(promise);\n\n const deferred = defer(promise);\n const state = deferred[DEFERRED_KEY];\n\n if (state?.status === 'success') {\n return children(state.value!);\n }\n\n if (state?.status === 'error') {\n throw state.error;\n }\n\n return (\n <Suspense\n fallback={\n <DeferredFallback promise={deferred} fallback={fallback}>\n {children}\n </DeferredFallback>\n }\n >\n <DeferredRenderer promise={deferred}>{children}</DeferredRenderer>\n </Suspense>\n );\n}\n"]}
@@ -7,7 +7,7 @@ import { createIsomorphicFn } from '@tanstack/react-start';
7
7
  import { getCookie } from '@tanstack/react-start/server';
8
8
 
9
9
  // src/utils/engine-version.ts
10
- var ENGINE_VERSION = "1.0.139" ;
10
+ var ENGINE_VERSION = "1.0.140" ;
11
11
  var DEV_SETTINGS_TTL_MS = 60 * 6e4;
12
12
  var HIT_HEADER = "x-twilight-dev-cache";
13
13
  var FETCHED_AT_HEADER = "x-twilight-dev-fetched-at";
@@ -242,5 +242,5 @@ var filesCdn = ky.extend({
242
242
  });
243
243
 
244
244
  export { ENGINE_VERSION, STORE_IDENTIFIER_HEADER, api, cdn, devSettingsFetchedAt, filesCdn, getActiveCurrency, getActiveScope, getActiveScopeId, getAuthToken };
245
- //# sourceMappingURL=chunk-J5OJ7JM2.js.map
246
- //# sourceMappingURL=chunk-J5OJ7JM2.js.map
245
+ //# sourceMappingURL=chunk-UXJAZ5JP.js.map
246
+ //# sourceMappingURL=chunk-UXJAZ5JP.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/engine-version.ts","../src/api/dev-settings-cache.ts","../src/api/client.ts"],"names":["entry","createIsomorphicFn","getCookie"],"mappings":"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","file":"chunk-J5OJ7JM2.js","sourcesContent":["/**\n * The engine's own build version, injected by tsup `define` when dist is\n * built. Falls back to 'dev' when the source runs unbundled (vitest, direct\n * src imports in the monorepo).\n *\n * Exposed at runtime as `salla.versions.engine` by `useTwilightInit` so a\n * storefront can always be asked which engine build rendered it.\n */\nexport const ENGINE_VERSION: string =\n typeof __TWILIGHT_ENGINE_VERSION__ !== 'undefined' ? __TWILIGHT_ENGINE_VERSION__ : 'dev';\n","import type { AfterResponseHook, BeforeRequestHook } from 'ky';\nimport { createIsomorphicFn } from '@tanstack/react-start';\nimport { getCookie } from '@tanstack/react-start/server';\nimport { DEV_SETTINGS_REFRESH_COOKIE } from '../utils/dev-settings-refresh';\n\n/**\n * A dev server reuses a store's settings for up to an hour.\n *\n * Every server render asks the API for `store/settings`, and a developer clicking\n * around a theme renders dozens of pages a minute. The API answers a burst of\n * settings requests for one store from one address with a challenge, which a\n * server-side fetch cannot pass, so the dev server rendered \"Store Unavailable\"\n * (6.05k challenged `(development; ssr)` calls in the 7 days to 2026-10-25). In\n * development, on the server, the same request within {@link DEV_SETTINGS_TTL_MS}\n * is answered from memory instead. An hour because the API counts settings\n * requests per hour, so one fetch an hour can never reach its limit; the theme\n * editor's \"Fetch fresh store data\" fetches sooner, and says how old the settings\n * are. A production build never runs it: `import.meta.env.DEV` is statically\n * false there.\n */\nexport const DEV_SETTINGS_TTL_MS = 60 * 60_000;\n\n/** Marks an answer from memory, so the store step does not file it again. */\nconst HIT_HEADER = 'x-twilight-dev-cache';\n\n/** When the answer was really fetched from the API (epoch ms), on fresh answers and reused ones alike. */\nconst FETCHED_AT_HEADER = 'x-twilight-dev-fetched-at';\n\n/**\n * When the dev server fetched these settings from the API, read off the answer the\n * cache handed back; `undefined` when the cache did not handle the call (a\n * production build, the browser, a test), so nothing new reaches the settings there.\n */\nexport function devSettingsFetchedAt(headers: Headers | undefined): number | undefined {\n const at = Number(headers?.get(FETCHED_AT_HEADER));\n return Number.isFinite(at) && at > 0 ? at : undefined;\n}\n\n/**\n * Not carried over to an answer from memory: the body is stored decoded, so its\n * original encoding and length no longer describe it.\n */\nconst DROPPED_HEADERS = new Set(['content-encoding', 'content-length']);\n\ninterface Entry {\n body: string;\n status: number;\n statusText: string;\n headers: [string, string][];\n fetchedAt: number;\n /** The refresh cookie's value when this answer was fetched. */\n refresh: string | null;\n}\n\nexport interface DevSettingsCacheOptions {\n /** Whether this call may use the cache at all. */\n enabled: () => boolean;\n /** The current value of {@link DEV_SETTINGS_REFRESH_COOKIE}, or null. */\n refreshToken: () => string | null;\n now?: () => number;\n ttlMs?: number;\n /** Called the first time an answer comes from memory. */\n onFirstHit?: () => void;\n}\n\nfunction isSettingsRequest(request: Request): boolean {\n if (request.method !== 'GET') return false;\n try {\n return new URL(request.url).pathname.endsWith('/store/settings');\n } catch {\n return false;\n }\n}\n\n/**\n * An answer `requestSettings` accepts: JSON carrying `data`. A 200 without it is\n * refused there as \"Store Unavailable\"; kept here, it would be refused for the next\n * hour too, after the API had recovered.\n */\nfunction hasSettings(body: string): boolean {\n try {\n return Boolean((JSON.parse(body) as { data?: unknown } | null)?.data);\n } catch {\n return false;\n }\n}\n\n/**\n * The request as the API receives it: method, URL and every header the client's\n * hooks set. Keyed on the request itself rather than on the inputs that build it,\n * so a header added to the client later cannot be missing from the key.\n */\nfunction keyOf(request: Request): string {\n const headers = [...request.headers].map(([name, value]) => `${name}: ${value}`).join('\\n');\n return `${request.method} ${request.url}\\n${headers}`;\n}\n\n/**\n * The two ky hooks behind the cache: `lookup` runs last among `beforeRequest`, once\n * every header is set, and `store` among `afterResponse`. Only an answer with\n * settings in it is kept, so a timeout, a 429, a challenge or an empty 200 is asked\n * again on the next render.\n */\nexport function createDevSettingsCache(options: DevSettingsCacheOptions) {\n const {\n enabled,\n refreshToken,\n now = Date.now,\n ttlMs = DEV_SETTINGS_TTL_MS,\n onFirstHit,\n } = options;\n const entries = new Map<string, Entry>();\n let announced = false;\n\n const isFresh = (entry: Entry): boolean =>\n now() - entry.fetchedAt < ttlMs && entry.refresh === refreshToken();\n\n const answerOf = (entry: Entry, hit: boolean): Response => {\n const headers = new Headers(entry.headers);\n headers.set(FETCHED_AT_HEADER, String(entry.fetchedAt));\n if (hit) headers.set(HIT_HEADER, 'hit');\n return new Response(entry.body, {\n status: entry.status,\n statusText: entry.statusText,\n headers,\n });\n };\n\n const lookup: BeforeRequestHook = ({ request }) => {\n if (!enabled() || !isSettingsRequest(request)) return;\n const key = keyOf(request);\n const entry = entries.get(key);\n if (!entry) return;\n if (!isFresh(entry)) {\n entries.delete(key);\n return;\n }\n if (!announced) {\n announced = true;\n onFirstHit?.();\n }\n return answerOf(entry, true);\n };\n\n const store: AfterResponseHook = async ({ request, response }) => {\n if (!enabled() || !isSettingsRequest(request) || !response.ok) return;\n if (response.headers.has(HIT_HEADER)) return;\n // ky hands each hook a clone, so reading it leaves the answer it returns intact.\n const body = await response.text();\n if (!hasSettings(body)) return;\n for (const [key, entry] of entries) {\n if (now() - entry.fetchedAt >= ttlMs) entries.delete(key);\n }\n const entry: Entry = {\n body,\n status: response.status,\n statusText: response.statusText,\n headers: [...response.headers].filter(([name]) => !DROPPED_HEADERS.has(name)),\n fetchedAt: now(),\n refresh: refreshToken(),\n };\n entries.set(keyOf(request), entry);\n // Handed on in place of the API's own answer, so it carries its fetch time too.\n return answerOf(entry, false);\n };\n\n return { lookup, store, clear: () => entries.clear() };\n}\n\nconst readRefreshCookie = createIsomorphicFn()\n .server((): string | null => {\n try {\n return getCookie(DEV_SETTINGS_REFRESH_COOKIE) ?? null;\n } catch {\n return null;\n }\n })\n .client((): string | null => null);\n\nexport const devSettingsCache = createDevSettingsCache({\n // A dev server's render only. A production build has `DEV` statically false,\n // the browser keeps the settings it was handed in its query cache, and vitest\n // runs in `test` mode, where every test wants the request it mocked.\n enabled: () =>\n import.meta.env.DEV && import.meta.env.MODE !== 'test' && typeof document === 'undefined',\n refreshToken: readRefreshCookie,\n onFirstHit: () =>\n console.warn(\n `[twilight] Store settings are reused for up to ${DEV_SETTINGS_TTL_MS / 60_000} minutes while you develop, to stay under the Salla API's rate limit. To see a change you made in the dashboard, press \"Fetch fresh store data\" in the theme editor's side panel: it also says when they were last fetched.`\n ),\n});\n","import ky from 'ky';\nimport { createIsomorphicFn } from '@tanstack/react-start';\nimport { getCookie } from '@tanstack/react-start/server';\nimport { getStoreIdentifier } from '../utils/sdk-helpers';\nimport { resolveThemeVersionId } from '../utils/theme-version';\nimport { getInjectedRuntimeEnv, readEnv } from '../utils/runtime-env';\nimport { getTwilightContext } from '../twilight/context';\nimport { ENGINE_VERSION } from '../utils/engine-version';\nimport { devSettingsCache } from './dev-settings-cache';\nimport {\n AUTH_TOKEN_COOKIE_NAME,\n CURRENCY_COOKIE_NAME,\n extractCurrencyFromCookies,\n extractScopeFromCookies,\n toCurrencyCode,\n extractTokenFromCookies,\n readSdkScope,\n type ActiveScope,\n} from '../utils/cookies';\n\n// The twilight module store doesn't reliably carry authToken into every scope: on the\n// SERVER workerd has no AsyncLocalStorage, and on BOTH sides this api client can live in a\n// different module instance than the one rootBeforeLoad populates (engine subpath import vs\n// engine-internal relative import). So `getTwilightContext().authToken` is often null even\n// for logged-in users → API calls (orders/customer/…) return `token_not_provided` (400).\n// Read the token directly from the request cookie (server) or document.cookie / salla.storage\n// (client) as a fallback. The server-only import is stripped from the client bundle by\n// createIsomorphicFn.\nconst readFallbackToken = createIsomorphicFn()\n .server((): string | null => {\n try {\n return getCookie(AUTH_TOKEN_COOKIE_NAME) ?? null;\n } catch {\n return null;\n }\n })\n .client((): string | null => {\n const fromCookie =\n typeof document !== 'undefined'\n ? extractTokenFromCookies(document.cookie, AUTH_TOKEN_COOKIE_NAME)\n : null;\n if (fromCookie) return fromCookie;\n const storage = typeof window !== 'undefined' ? window.salla?.storage : undefined;\n const fromStorage =\n typeof storage?.get === 'function' ? storage.get(AUTH_TOKEN_COOKIE_NAME) : null;\n if (typeof fromStorage === 'string' && fromStorage) return fromStorage;\n // `?token=` webview injection lands only on the SDK's axios instance\n const fromSdkApi = typeof window !== 'undefined' ? window.salla?.api?.token : undefined;\n return typeof fromSdkApi === 'string' && fromSdkApi ? fromSdkApi : null;\n });\n\n/**\n * The auth token as the api client sees it: twilight context first, cookie/storage fallback.\n * Use this (not `getTwilightContext().authToken`, which is often null for logged-in users —\n * see the note above) to decide whether an authenticated-only query should run.\n */\nexport const getAuthToken = (): string | null =>\n getTwilightContext().authToken ?? readFallbackToken();\n\nconst readRequestCurrency = createIsomorphicFn()\n .server((): string | null => {\n try {\n return toCurrencyCode(getCookie(CURRENCY_COOKIE_NAME));\n } catch {\n return null;\n }\n })\n .client((): string | null => null);\n\n/**\n * The currency the visitor chose, from the `s-curr` cookie the SDK writes: the API\n * answers in the store's own currency unless the request names one.\n */\nexport function getActiveCurrency(): string | null;\nexport function getActiveCurrency(fallback: string): string;\nexport function getActiveCurrency(fallback: string | null = null): string | null {\n const chosen =\n typeof document !== 'undefined'\n ? extractCurrencyFromCookies(document.cookie)\n : readRequestCurrency();\n return chosen ?? fallback;\n}\n\nconst API_HEADERS = {\n ACCEPT: 'accept',\n ACCEPT_LANGUAGE: 'accept-language',\n APP_OS: 's-app-os',\n APP_VERSION: 's-app-version',\n RAY: 's-ray',\n SOURCE: 's-source',\n THEME_TYPE: 's-theme-type',\n STORE_API_VERSION: 's-store-api-version',\n REQUESTED_WITH: 'x-requested-with',\n CF_PROXY_WHITELIST: 's-cf-proxy-whitelist-id',\n STORE_IDENTIFIER: 'store-identifier',\n CURRENCY: 'currency',\n AUTHORIZATION: 'Authorization',\n VERSION_ID: 's-version-id',\n SCOPE_ID: 's-scope-id',\n SCOPE_TYPE: 's-scope-type',\n SCOPE_ALLOCATION_TYPE: 's-scope-allocation-type',\n SCOPE_ALLOCATION_ID: 's-scope-allocation-id',\n USER_AGENT: 'user-agent',\n} as const;\n\n/**\n * The header every Salla API call is answered by store.\n *\n * Exported because a caller that already knows which store it is asking about\n * must be able to say so on the request itself — see `store.settings`. Without\n * it the only way to name a store is the ambient resolver, and \"the identifier\n * I checked\" and \"the identifier that was sent\" become two different things.\n */\nexport const STORE_IDENTIFIER_HEADER = API_HEADERS.STORE_IDENTIFIER;\n\n/**\n * Active scope: the market plus its branch allocation, as the SDK stores it. On the\n * client it comes from `salla.storage.get('scope')`; before the SDK hydrates storage\n * it falls back to the `scope` cookie the theme mirrors that selection into (see\n * writeScopeCookie), then to the scope twilightMiddleware seeded from the same\n * cookie on the server. Requests vary by the `s-scope-*` headers, so React Query\n * keys must include this to avoid serving another branch's cached categories/products.\n *\n * The cookie step matters for hydration: the SDK is not guaranteed to have\n * populated `salla.storage` by the time the client rebuilds a dehydrated query\n * key, and without it the client would compute null against a scoped SSR key.\n */\nexport const getActiveScope = (): ActiveScope | null => {\n // `!== undefined`, not `!= null`: a null here is the SDK authoritatively saying \"no branch\n // selected\", which must beat a cookie left over from the previous selection. Only a\n // not-yet-ready storage API (undefined) falls through to the cookie.\n const fromStorage = readSdkScope();\n if (fromStorage !== undefined) return fromStorage;\n\n if (typeof document !== 'undefined') {\n const fromCookie = extractScopeFromCookies(document.cookie);\n if (fromCookie != null) return fromCookie;\n }\n\n try {\n return getTwilightContext().scope;\n } catch {\n return null;\n }\n};\n\n/** {@link getActiveScope} reduced to the market id. */\nexport const getActiveScopeId = (): string | null => getActiveScope()?.id ?? null;\n\nconst sharedHeaders = (locale: string) => ({\n [API_HEADERS.ACCEPT]: 'application/json' as const,\n [API_HEADERS.ACCEPT_LANGUAGE]: locale,\n [API_HEADERS.APP_OS]: 'browser',\n [API_HEADERS.APP_VERSION]: '2.14.363',\n [API_HEADERS.RAY]: '50',\n [API_HEADERS.SOURCE]: 'twilight',\n [API_HEADERS.THEME_TYPE]: 'react',\n [API_HEADERS.STORE_API_VERSION]: 'swoole',\n [API_HEADERS.REQUESTED_WITH]: 'XMLHttpRequest',\n [API_HEADERS.CF_PROXY_WHITELIST]: '019a2f78-a64c-7edb-proxy-whitelist-id-9a3a-17afda90f03a',\n});\n\n/**\n * Who a server-rendered call says it is.\n *\n * Every theme worker reaches api.salla.dev from Cloudflare's one Workers address, so\n * salla.dev's per-IP rate limits counted every React store as a single client and\n * refused the calls each page makes (`store/settings` among them: \"Store Unavailable\").\n * The zone skips rate limiting for a User-Agent shaped `Salla-<Name>/<N.N.N> (<env>; <meta>)`\n * from Cloudflare's or AWS's networks, the convention Salla's other services already send.\n *\n * Built from fixed parts: the zone's pattern takes only digits and dots for the version,\n * so a prerelease suffix is dropped, and an unbundled engine (`dev`) reads as `0.0.0`.\n */\nconst SERVER_USER_AGENT = `Salla-TwilightReact/${\n /^\\d+(?:\\.\\d+)*/.exec(ENGINE_VERSION)?.[0] ?? '0.0.0'\n} (${import.meta.env.DEV ? 'development' : 'production'}; ssr)`;\n\n/**\n * Store API base. `VITE_API_URL` override points a preview at a branch backend; unset → prod\n * `api.salla.dev`. Resolution order matters: the value the SSR worker resolved (published into\n * the head, since worker `vars` are server-only `process.env` and never reach the browser\n * bundle) wins, so the hydrated client keeps talking to the backend that rendered the page.\n */\n\nconst STORE_API_BASE =\n getInjectedRuntimeEnv().apiBase || readEnv('VITE_API_URL') || 'https://api.salla.dev/store/v1';\n\nexport const api = ky.extend({\n prefix: STORE_API_BASE,\n timeout: 8000,\n hooks: {\n beforeRequest: [\n ({ request }) => {\n const { locale, authToken } = getTwilightContext();\n const headers = sharedHeaders(locale);\n for (const [key, value] of Object.entries(headers)) {\n request.headers.set(key, value);\n }\n // Only when the caller did not say. `headers.set` here would otherwise\n // overwrite a per-request identifier with the ambient one, which is a\n // silent way to answer a question about one store with another's data.\n if (!request.headers.get(API_HEADERS.STORE_IDENTIFIER)) {\n request.headers.set(API_HEADERS.STORE_IDENTIFIER, getStoreIdentifier());\n }\n\n // Server only. A browser call comes from the shopper's own address and is\n // counted against it, which is what the zone's limits are for.\n if (typeof document === 'undefined') {\n request.headers.set(API_HEADERS.USER_AGENT, SERVER_USER_AGENT);\n }\n\n const scope = getActiveScope();\n\n // The version the request asked for, else the one the settings named for\n // this scope. Absent means \"whatever is live\", which is what an ordinary\n // storefront visit wants — so a null is left off rather than sent empty.\n const versionId = resolveThemeVersionId(scope?.id ?? null);\n if (versionId) {\n request.headers.set(API_HEADERS.VERSION_ID, versionId);\n }\n\n const currency = getActiveCurrency();\n if (currency) request.headers.set(API_HEADERS.CURRENCY, currency);\n\n const token = authToken ?? readFallbackToken();\n if (token) {\n request.headers.set(API_HEADERS.AUTHORIZATION, `Bearer ${token}`);\n }\n\n // The same four the SDK's own client sends. The API resolves stock per\n // allocation: id alone asks about the market, not the branch the shopper chose.\n if (scope) {\n request.headers.set(API_HEADERS.SCOPE_ID, scope.id);\n if (scope.type) request.headers.set(API_HEADERS.SCOPE_TYPE, scope.type);\n if (scope.allocation_type)\n request.headers.set(API_HEADERS.SCOPE_ALLOCATION_TYPE, scope.allocation_type);\n if (scope.allocation_id)\n request.headers.set(API_HEADERS.SCOPE_ALLOCATION_ID, scope.allocation_id);\n }\n },\n // Last, so it keys on the request with every header above set.\n devSettingsCache.lookup,\n ],\n afterResponse: [devSettingsCache.store],\n },\n});\n\nexport const cdn = ky.extend({\n prefix: 'https://cdn.salla.network',\n timeout: 5000,\n});\n\n/**\n * The merchant uploader's public bucket, `admin-uploader-prod`.\n *\n * A different host from {@link cdn} and a different thing behind it: this one\n * is an R2 custom domain, so a read never passes through a worker. Storefront\n * translations live here, per language, under `translations/` — see\n * `api/translations.ts`.\n */\nexport const filesCdn = ky.extend({\n prefix: 'https://cdn.files.salla.network',\n timeout: 5000,\n});\n"]}
1
+ {"version":3,"sources":["../src/utils/engine-version.ts","../src/api/dev-settings-cache.ts","../src/api/client.ts"],"names":["entry","createIsomorphicFn","getCookie"],"mappings":";;;;;;;;;AAQO,IAAM,cAAA,GAC0C,SAAA;ACWhD,IAAM,sBAAsB,EAAA,GAAK,GAAA;AAGxC,IAAM,UAAA,GAAa,sBAAA;AAGnB,IAAM,iBAAA,GAAoB,2BAAA;AAOnB,SAAS,qBAAqB,OAAA,EAAkD;AACrF,EAAA,MAAM,EAAA,GAAK,MAAA,CAAO,OAAA,EAAS,GAAA,CAAI,iBAAiB,CAAC,CAAA;AACjD,EAAA,OAAO,OAAO,QAAA,CAAS,EAAE,CAAA,IAAK,EAAA,GAAK,IAAI,EAAA,GAAK,MAAA;AAC9C;AAMA,IAAM,kCAAkB,IAAI,GAAA,CAAI,CAAC,kBAAA,EAAoB,gBAAgB,CAAC,CAAA;AAuBtE,SAAS,kBAAkB,OAAA,EAA2B;AACpD,EAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,KAAA,EAAO,OAAO,KAAA;AACrC,EAAA,IAAI;AACF,IAAA,OAAO,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA,CAAE,QAAA,CAAS,SAAS,iBAAiB,CAAA;AAAA,EACjE,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAOA,SAAS,YAAY,IAAA,EAAuB;AAC1C,EAAA,IAAI;AACF,IAAA,OAAO,OAAA,CAAS,IAAA,CAAK,KAAA,CAAM,IAAI,GAAiC,IAAI,CAAA;AAAA,EACtE,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAOA,SAAS,MAAM,OAAA,EAA0B;AACvC,EAAA,MAAM,UAAU,CAAC,GAAG,QAAQ,OAAO,CAAA,CAAE,IAAI,CAAC,CAAC,MAAM,KAAK,CAAA,KAAM,GAAG,IAAI,CAAA,EAAA,EAAK,KAAK,CAAA,CAAE,CAAA,CAAE,KAAK,IAAI,CAAA;AAC1F,EAAA,OAAO,CAAA,EAAG,OAAA,CAAQ,MAAM,CAAA,CAAA,EAAI,QAAQ,GAAG;AAAA,EAAK,OAAO,CAAA,CAAA;AACrD;AAQO,SAAS,uBAAuB,OAAA,EAAkC;AACvE,EAAA,MAAM;AAAA,IACJ,OAAA;AAAA,IACA,YAAA;AAAA,IACA,MAAM,IAAA,CAAK,GAAA;AAAA,IACX,KAAA,GAAQ,mBAAA;AAAA,IACR;AAAA,GACF,GAAI,OAAA;AACJ,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAmB;AACvC,EAAA,IAAI,SAAA,GAAY,KAAA;AAEhB,EAAA,MAAM,OAAA,GAAU,CAAC,KAAA,KACf,GAAA,EAAI,GAAI,MAAM,SAAA,GAAY,KAAA,IAAS,KAAA,CAAM,OAAA,KAAY,YAAA,EAAa;AAEpE,EAAA,MAAM,QAAA,GAAW,CAAC,KAAA,EAAc,GAAA,KAA2B;AACzD,IAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ,KAAA,CAAM,OAAO,CAAA;AACzC,IAAA,OAAA,CAAQ,GAAA,CAAI,iBAAA,EAAmB,MAAA,CAAO,KAAA,CAAM,SAAS,CAAC,CAAA;AACtD,IAAA,IAAI,GAAA,EAAK,OAAA,CAAQ,GAAA,CAAI,UAAA,EAAY,KAAK,CAAA;AACtC,IAAA,OAAO,IAAI,QAAA,CAAS,KAAA,CAAM,IAAA,EAAM;AAAA,MAC9B,QAAQ,KAAA,CAAM,MAAA;AAAA,MACd,YAAY,KAAA,CAAM,UAAA;AAAA,MAClB;AAAA,KACD,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,MAAM,MAAA,GAA4B,CAAC,EAAE,OAAA,EAAQ,KAAM;AACjD,IAAA,IAAI,CAAC,OAAA,EAAQ,IAAK,CAAC,iBAAA,CAAkB,OAAO,CAAA,EAAG;AAC/C,IAAA,MAAM,GAAA,GAAM,MAAM,OAAO,CAAA;AACzB,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA;AAC7B,IAAA,IAAI,CAAC,KAAA,EAAO;AACZ,IAAA,IAAI,CAAC,OAAA,CAAQ,KAAK,CAAA,EAAG;AACnB,MAAA,OAAA,CAAQ,OAAO,GAAG,CAAA;AAClB,MAAA;AAAA,IACF;AACA,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,UAAA,IAAa;AAAA,IACf;AACA,IAAA,OAAO,QAAA,CAAS,OAAO,IAAI,CAAA;AAAA,EAC7B,CAAA;AAEA,EAAA,MAAM,KAAA,GAA2B,OAAO,EAAE,OAAA,EAAS,UAAS,KAAM;AAChE,IAAA,IAAI,CAAC,SAAQ,IAAK,CAAC,kBAAkB,OAAO,CAAA,IAAK,CAAC,QAAA,CAAS,EAAA,EAAI;AAC/D,IAAA,IAAI,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,UAAU,CAAA,EAAG;AAEtC,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AACjC,IAAA,IAAI,CAAC,WAAA,CAAY,IAAI,CAAA,EAAG;AACxB,IAAA,KAAA,MAAW,CAAC,GAAA,EAAKA,MAAK,CAAA,IAAK,OAAA,EAAS;AAClC,MAAA,IAAI,KAAI,GAAIA,MAAAA,CAAM,aAAa,KAAA,EAAO,OAAA,CAAQ,OAAO,GAAG,CAAA;AAAA,IAC1D;AACA,IAAA,MAAM,KAAA,GAAe;AAAA,MACnB,IAAA;AAAA,MACA,QAAQ,QAAA,CAAS,MAAA;AAAA,MACjB,YAAY,QAAA,CAAS,UAAA;AAAA,MACrB,OAAA,EAAS,CAAC,GAAG,QAAA,CAAS,OAAO,CAAA,CAAE,MAAA,CAAO,CAAC,CAAC,IAAI,CAAA,KAAM,CAAC,eAAA,CAAgB,GAAA,CAAI,IAAI,CAAC,CAAA;AAAA,MAC5E,WAAW,GAAA,EAAI;AAAA,MACf,SAAS,YAAA;AAAa,KACxB;AACA,IAAA,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,OAAO,CAAA,EAAG,KAAK,CAAA;AAEjC,IAAA,OAAO,QAAA,CAAS,OAAO,KAAK,CAAA;AAAA,EAC9B,CAAA;AAEA,EAAA,OAAO,EAAE,MAAA,EAAQ,KAAA,EAAO,OAAO,MAAM,OAAA,CAAQ,OAAM,EAAE;AACvD;AAEA,IAAM,iBAAA,GAAoB,kBAAA,EAAmB,CAC1C,MAAA,CAAO,MAAqB;AAC3B,EAAA,IAAI;AACF,IAAA,OAAO,SAAA,CAAU,2BAA2B,CAAA,IAAK,IAAA;AAAA,EACnD,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF,CAAC,CAAA,CACA,MAAA,CAAO,MAAqB,IAAI,CAAA;AAE5B,IAAM,mBAAmB,sBAAA,CAAuB;AAAA;AAAA;AAAA;AAAA,EAIrD,OAAA,EAAS,MACP,MAAA,CAAA,IAAA,CAAY,GAAA,CAAI,GAAA,IAAO,YAAY,GAAA,CAAI,IAAA,KAAS,MAAA,IAAU,OAAO,QAAA,KAAa,WAAA;AAAA,EAChF,YAAA,EAAc,iBAAA;AAAA,EACd,UAAA,EAAY,MACV,OAAA,CAAQ,IAAA;AAAA,IACN,CAAA,+CAAA,EAAkD,sBAAsB,GAAM,CAAA,2NAAA;AAAA;AAEpF,CAAC,CAAA;;;AClKD,IAAM,iBAAA,GAAoBC,kBAAAA,EAAmB,CAC1C,MAAA,CAAO,MAAqB;AAC3B,EAAA,IAAI;AACF,IAAA,OAAOC,SAAAA,CAAU,sBAAsB,CAAA,IAAK,IAAA;AAAA,EAC9C,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF,CAAC,CAAA,CACA,OAAO,MAAqB;AAC3B,EAAA,MAAM,UAAA,GACJ,OAAO,QAAA,KAAa,WAAA,GAChB,wBAAwB,QAAA,CAAS,MAAA,EAAQ,sBAAsB,CAAA,GAC/D,IAAA;AACN,EAAA,IAAI,YAAY,OAAO,UAAA;AACvB,EAAA,MAAM,UAAU,OAAO,MAAA,KAAW,WAAA,GAAc,MAAA,CAAO,OAAO,OAAA,GAAU,MAAA;AACxE,EAAA,MAAM,WAAA,GACJ,OAAO,OAAA,EAAS,GAAA,KAAQ,aAAa,OAAA,CAAQ,GAAA,CAAI,sBAAsB,CAAA,GAAI,IAAA;AAC7E,EAAA,IAAI,OAAO,WAAA,KAAgB,QAAA,IAAY,WAAA,EAAa,OAAO,WAAA;AAE3D,EAAA,MAAM,aAAa,OAAO,MAAA,KAAW,cAAc,MAAA,CAAO,KAAA,EAAO,KAAK,KAAA,GAAQ,MAAA;AAC9E,EAAA,OAAO,OAAO,UAAA,KAAe,QAAA,IAAY,UAAA,GAAa,UAAA,GAAa,IAAA;AACrE,CAAC,CAAA;AAOI,IAAM,YAAA,GAAe,MAC1B,kBAAA,EAAmB,CAAE,aAAa,iBAAA;AAEpC,IAAM,mBAAA,GAAsBD,kBAAAA,EAAmB,CAC5C,MAAA,CAAO,MAAqB;AAC3B,EAAA,IAAI;AACF,IAAA,OAAO,cAAA,CAAeC,SAAAA,CAAU,oBAAoB,CAAC,CAAA;AAAA,EACvD,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF,CAAC,CAAA,CACA,MAAA,CAAO,MAAqB,IAAI,CAAA;AAQ5B,SAAS,iBAAA,CAAkB,WAA0B,IAAA,EAAqB;AAC/E,EAAA,MAAM,MAAA,GACJ,OAAO,QAAA,KAAa,WAAA,GAChB,2BAA2B,QAAA,CAAS,MAAM,IAC1C,mBAAA,EAAoB;AAC1B,EAAA,OAAO,MAAA,IAAU,QAAA;AACnB;AAEA,IAAM,WAAA,GAAc;AAAA,EAClB,MAAA,EAAQ,QAAA;AAAA,EACR,eAAA,EAAiB,iBAAA;AAAA,EACjB,MAAA,EAAQ,UAAA;AAAA,EACR,WAAA,EAAa,eAAA;AAAA,EACb,GAAA,EAAK,OAAA;AAAA,EACL,MAAA,EAAQ,UAAA;AAAA,EACR,UAAA,EAAY,cAAA;AAAA,EACZ,iBAAA,EAAmB,qBAAA;AAAA,EACnB,cAAA,EAAgB,kBAAA;AAAA,EAChB,kBAAA,EAAoB,yBAAA;AAAA,EACpB,gBAAA,EAAkB,kBAAA;AAAA,EAClB,QAAA,EAAU,UAAA;AAAA,EACV,aAAA,EAAe,eAAA;AAAA,EACf,UAAA,EAAY,cAAA;AAAA,EACZ,QAAA,EAAU,YAAA;AAAA,EACV,UAAA,EAAY,cAAA;AAAA,EACZ,qBAAA,EAAuB,yBAAA;AAAA,EACvB,mBAAA,EAAqB,uBAAA;AAAA,EACrB,UAAA,EAAY;AACd,CAAA;AAUO,IAAM,0BAA0B,WAAA,CAAY;AAc5C,IAAM,iBAAiB,MAA0B;AAItD,EAAA,MAAM,cAAc,YAAA,EAAa;AACjC,EAAA,IAAI,WAAA,KAAgB,QAAW,OAAO,WAAA;AAEtC,EAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AACnC,IAAA,MAAM,UAAA,GAAa,uBAAA,CAAwB,QAAA,CAAS,MAAM,CAAA;AAC1D,IAAA,IAAI,UAAA,IAAc,MAAM,OAAO,UAAA;AAAA,EACjC;AAEA,EAAA,IAAI;AACF,IAAA,OAAO,oBAAmB,CAAE,KAAA;AAAA,EAC9B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAGO,IAAM,gBAAA,GAAmB,MAAqB,cAAA,EAAe,EAAG,EAAA,IAAM;AAE7E,IAAM,aAAA,GAAgB,CAAC,MAAA,MAAoB;AAAA,EACzC,CAAC,WAAA,CAAY,MAAM,GAAG,kBAAA;AAAA,EACtB,CAAC,WAAA,CAAY,eAAe,GAAG,MAAA;AAAA,EAC/B,CAAC,WAAA,CAAY,MAAM,GAAG,SAAA;AAAA,EACtB,CAAC,WAAA,CAAY,WAAW,GAAG,UAAA;AAAA,EAC3B,CAAC,WAAA,CAAY,GAAG,GAAG,IAAA;AAAA,EACnB,CAAC,WAAA,CAAY,MAAM,GAAG,UAAA;AAAA,EACtB,CAAC,WAAA,CAAY,UAAU,GAAG,OAAA;AAAA,EAC1B,CAAC,WAAA,CAAY,iBAAiB,GAAG,QAAA;AAAA,EACjC,CAAC,WAAA,CAAY,cAAc,GAAG,gBAAA;AAAA,EAC9B,CAAC,WAAA,CAAY,kBAAkB,GAAG;AACpC,CAAA,CAAA;AAcA,IAAM,iBAAA,GAAoB,CAAA,oBAAA,EACxB,gBAAA,CAAiB,IAAA,CAAK,cAAc,CAAA,GAAI,CAAC,CAAA,IAAK,OAChD,CAAA,EAAA,EAAK,MAAA,CAAA,IAAA,CAAY,GAAA,CAAI,GAAA,GAAM,gBAAgB,YAAY,CAAA,MAAA,CAAA;AASvD,IAAM,iBACJ,qBAAA,EAAsB,CAAE,OAAA,IAAW,OAAA,CAAQ,cAAc,CAAA,IAAK,gCAAA;AAEzD,IAAM,GAAA,GAAM,GAAG,MAAA,CAAO;AAAA,EAC3B,MAAA,EAAQ,cAAA;AAAA,EACR,OAAA,EAAS,GAAA;AAAA,EACT,KAAA,EAAO;AAAA,IACL,aAAA,EAAe;AAAA,MACb,CAAC,EAAE,OAAA,EAAQ,KAAM;AACf,QAAA,MAAM,EAAE,MAAA,EAAQ,SAAA,EAAU,GAAI,kBAAA,EAAmB;AACjD,QAAA,MAAM,OAAA,GAAU,cAAc,MAAM,CAAA;AACpC,QAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAA,EAAG;AAClD,UAAA,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AAAA,QAChC;AAIA,QAAA,IAAI,CAAC,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,WAAA,CAAY,gBAAgB,CAAA,EAAG;AACtD,UAAA,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,WAAA,CAAY,gBAAA,EAAkB,oBAAoB,CAAA;AAAA,QACxE;AAIA,QAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AACnC,UAAA,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,WAAA,CAAY,UAAA,EAAY,iBAAiB,CAAA;AAAA,QAC/D;AAEA,QAAA,MAAM,QAAQ,cAAA,EAAe;AAK7B,QAAA,MAAM,SAAA,GAAY,qBAAA,CAAsB,KAAA,EAAO,EAAA,IAAM,IAAI,CAAA;AACzD,QAAA,IAAI,SAAA,EAAW;AACb,UAAA,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,WAAA,CAAY,UAAA,EAAY,SAAS,CAAA;AAAA,QACvD;AAEA,QAAA,MAAM,WAAW,iBAAA,EAAkB;AACnC,QAAA,IAAI,UAAU,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,WAAA,CAAY,UAAU,QAAQ,CAAA;AAEhE,QAAA,MAAM,KAAA,GAAQ,aAAa,iBAAA,EAAkB;AAC7C,QAAA,IAAI,KAAA,EAAO;AACT,UAAA,OAAA,CAAQ,QAAQ,GAAA,CAAI,WAAA,CAAY,aAAA,EAAe,CAAA,OAAA,EAAU,KAAK,CAAA,CAAE,CAAA;AAAA,QAClE;AAIA,QAAA,IAAI,KAAA,EAAO;AACT,UAAA,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,WAAA,CAAY,QAAA,EAAU,MAAM,EAAE,CAAA;AAClD,UAAA,IAAI,KAAA,CAAM,MAAM,OAAA,CAAQ,OAAA,CAAQ,IAAI,WAAA,CAAY,UAAA,EAAY,MAAM,IAAI,CAAA;AACtE,UAAA,IAAI,KAAA,CAAM,eAAA;AACR,YAAA,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,WAAA,CAAY,qBAAA,EAAuB,MAAM,eAAe,CAAA;AAC9E,UAAA,IAAI,KAAA,CAAM,aAAA;AACR,YAAA,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,WAAA,CAAY,mBAAA,EAAqB,MAAM,aAAa,CAAA;AAAA,QAC5E;AAAA,MACF,CAAA;AAAA;AAAA,MAEA,gBAAA,CAAiB;AAAA,KACnB;AAAA,IACA,aAAA,EAAe,CAAC,gBAAA,CAAiB,KAAK;AAAA;AAE1C,CAAC;AAEM,IAAM,GAAA,GAAM,GAAG,MAAA,CAAO;AAAA,EAC3B,MAAA,EAAQ,2BAAA;AAAA,EACR,OAAA,EAAS;AACX,CAAC;AAUM,IAAM,QAAA,GAAW,GAAG,MAAA,CAAO;AAAA,EAChC,MAAA,EAAQ,iCAAA;AAAA,EACR,OAAA,EAAS;AACX,CAAC","file":"chunk-UXJAZ5JP.js","sourcesContent":["/**\n * The engine's own build version, injected by tsup `define` when dist is\n * built. Falls back to 'dev' when the source runs unbundled (vitest, direct\n * src imports in the monorepo).\n *\n * Exposed at runtime as `salla.versions.engine` by `useTwilightInit` so a\n * storefront can always be asked which engine build rendered it.\n */\nexport const ENGINE_VERSION: string =\n typeof __TWILIGHT_ENGINE_VERSION__ !== 'undefined' ? __TWILIGHT_ENGINE_VERSION__ : 'dev';\n","import type { AfterResponseHook, BeforeRequestHook } from 'ky';\nimport { createIsomorphicFn } from '@tanstack/react-start';\nimport { getCookie } from '@tanstack/react-start/server';\nimport { DEV_SETTINGS_REFRESH_COOKIE } from '../utils/dev-settings-refresh';\n\n/**\n * A dev server reuses a store's settings for up to an hour.\n *\n * Every server render asks the API for `store/settings`, and a developer clicking\n * around a theme renders dozens of pages a minute. The API answers a burst of\n * settings requests for one store from one address with a challenge, which a\n * server-side fetch cannot pass, so the dev server rendered \"Store Unavailable\"\n * (6.05k challenged `(development; ssr)` calls in the 7 days to 2026-10-25). In\n * development, on the server, the same request within {@link DEV_SETTINGS_TTL_MS}\n * is answered from memory instead. An hour because the API counts settings\n * requests per hour, so one fetch an hour can never reach its limit; the theme\n * editor's \"Fetch fresh store data\" fetches sooner, and says how old the settings\n * are. A production build never runs it: `import.meta.env.DEV` is statically\n * false there.\n */\nexport const DEV_SETTINGS_TTL_MS = 60 * 60_000;\n\n/** Marks an answer from memory, so the store step does not file it again. */\nconst HIT_HEADER = 'x-twilight-dev-cache';\n\n/** When the answer was really fetched from the API (epoch ms), on fresh answers and reused ones alike. */\nconst FETCHED_AT_HEADER = 'x-twilight-dev-fetched-at';\n\n/**\n * When the dev server fetched these settings from the API, read off the answer the\n * cache handed back; `undefined` when the cache did not handle the call (a\n * production build, the browser, a test), so nothing new reaches the settings there.\n */\nexport function devSettingsFetchedAt(headers: Headers | undefined): number | undefined {\n const at = Number(headers?.get(FETCHED_AT_HEADER));\n return Number.isFinite(at) && at > 0 ? at : undefined;\n}\n\n/**\n * Not carried over to an answer from memory: the body is stored decoded, so its\n * original encoding and length no longer describe it.\n */\nconst DROPPED_HEADERS = new Set(['content-encoding', 'content-length']);\n\ninterface Entry {\n body: string;\n status: number;\n statusText: string;\n headers: [string, string][];\n fetchedAt: number;\n /** The refresh cookie's value when this answer was fetched. */\n refresh: string | null;\n}\n\nexport interface DevSettingsCacheOptions {\n /** Whether this call may use the cache at all. */\n enabled: () => boolean;\n /** The current value of {@link DEV_SETTINGS_REFRESH_COOKIE}, or null. */\n refreshToken: () => string | null;\n now?: () => number;\n ttlMs?: number;\n /** Called the first time an answer comes from memory. */\n onFirstHit?: () => void;\n}\n\nfunction isSettingsRequest(request: Request): boolean {\n if (request.method !== 'GET') return false;\n try {\n return new URL(request.url).pathname.endsWith('/store/settings');\n } catch {\n return false;\n }\n}\n\n/**\n * An answer `requestSettings` accepts: JSON carrying `data`. A 200 without it is\n * refused there as \"Store Unavailable\"; kept here, it would be refused for the next\n * hour too, after the API had recovered.\n */\nfunction hasSettings(body: string): boolean {\n try {\n return Boolean((JSON.parse(body) as { data?: unknown } | null)?.data);\n } catch {\n return false;\n }\n}\n\n/**\n * The request as the API receives it: method, URL and every header the client's\n * hooks set. Keyed on the request itself rather than on the inputs that build it,\n * so a header added to the client later cannot be missing from the key.\n */\nfunction keyOf(request: Request): string {\n const headers = [...request.headers].map(([name, value]) => `${name}: ${value}`).join('\\n');\n return `${request.method} ${request.url}\\n${headers}`;\n}\n\n/**\n * The two ky hooks behind the cache: `lookup` runs last among `beforeRequest`, once\n * every header is set, and `store` among `afterResponse`. Only an answer with\n * settings in it is kept, so a timeout, a 429, a challenge or an empty 200 is asked\n * again on the next render.\n */\nexport function createDevSettingsCache(options: DevSettingsCacheOptions) {\n const {\n enabled,\n refreshToken,\n now = Date.now,\n ttlMs = DEV_SETTINGS_TTL_MS,\n onFirstHit,\n } = options;\n const entries = new Map<string, Entry>();\n let announced = false;\n\n const isFresh = (entry: Entry): boolean =>\n now() - entry.fetchedAt < ttlMs && entry.refresh === refreshToken();\n\n const answerOf = (entry: Entry, hit: boolean): Response => {\n const headers = new Headers(entry.headers);\n headers.set(FETCHED_AT_HEADER, String(entry.fetchedAt));\n if (hit) headers.set(HIT_HEADER, 'hit');\n return new Response(entry.body, {\n status: entry.status,\n statusText: entry.statusText,\n headers,\n });\n };\n\n const lookup: BeforeRequestHook = ({ request }) => {\n if (!enabled() || !isSettingsRequest(request)) return;\n const key = keyOf(request);\n const entry = entries.get(key);\n if (!entry) return;\n if (!isFresh(entry)) {\n entries.delete(key);\n return;\n }\n if (!announced) {\n announced = true;\n onFirstHit?.();\n }\n return answerOf(entry, true);\n };\n\n const store: AfterResponseHook = async ({ request, response }) => {\n if (!enabled() || !isSettingsRequest(request) || !response.ok) return;\n if (response.headers.has(HIT_HEADER)) return;\n // ky hands each hook a clone, so reading it leaves the answer it returns intact.\n const body = await response.text();\n if (!hasSettings(body)) return;\n for (const [key, entry] of entries) {\n if (now() - entry.fetchedAt >= ttlMs) entries.delete(key);\n }\n const entry: Entry = {\n body,\n status: response.status,\n statusText: response.statusText,\n headers: [...response.headers].filter(([name]) => !DROPPED_HEADERS.has(name)),\n fetchedAt: now(),\n refresh: refreshToken(),\n };\n entries.set(keyOf(request), entry);\n // Handed on in place of the API's own answer, so it carries its fetch time too.\n return answerOf(entry, false);\n };\n\n return { lookup, store, clear: () => entries.clear() };\n}\n\nconst readRefreshCookie = createIsomorphicFn()\n .server((): string | null => {\n try {\n return getCookie(DEV_SETTINGS_REFRESH_COOKIE) ?? null;\n } catch {\n return null;\n }\n })\n .client((): string | null => null);\n\nexport const devSettingsCache = createDevSettingsCache({\n // A dev server's render only. A production build has `DEV` statically false,\n // the browser keeps the settings it was handed in its query cache, and vitest\n // runs in `test` mode, where every test wants the request it mocked.\n enabled: () =>\n import.meta.env.DEV && import.meta.env.MODE !== 'test' && typeof document === 'undefined',\n refreshToken: readRefreshCookie,\n onFirstHit: () =>\n console.warn(\n `[twilight] Store settings are reused for up to ${DEV_SETTINGS_TTL_MS / 60_000} minutes while you develop, to stay under the Salla API's rate limit. To see a change you made in the dashboard, press \"Fetch fresh store data\" in the theme editor's side panel: it also says when they were last fetched.`\n ),\n});\n","import ky from 'ky';\nimport { createIsomorphicFn } from '@tanstack/react-start';\nimport { getCookie } from '@tanstack/react-start/server';\nimport { getStoreIdentifier } from '../utils/sdk-helpers';\nimport { resolveThemeVersionId } from '../utils/theme-version';\nimport { getInjectedRuntimeEnv, readEnv } from '../utils/runtime-env';\nimport { getTwilightContext } from '../twilight/context';\nimport { ENGINE_VERSION } from '../utils/engine-version';\nimport { devSettingsCache } from './dev-settings-cache';\nimport {\n AUTH_TOKEN_COOKIE_NAME,\n CURRENCY_COOKIE_NAME,\n extractCurrencyFromCookies,\n extractScopeFromCookies,\n toCurrencyCode,\n extractTokenFromCookies,\n readSdkScope,\n type ActiveScope,\n} from '../utils/cookies';\n\n// The twilight module store doesn't reliably carry authToken into every scope: on the\n// SERVER workerd has no AsyncLocalStorage, and on BOTH sides this api client can live in a\n// different module instance than the one rootBeforeLoad populates (engine subpath import vs\n// engine-internal relative import). So `getTwilightContext().authToken` is often null even\n// for logged-in users → API calls (orders/customer/…) return `token_not_provided` (400).\n// Read the token directly from the request cookie (server) or document.cookie / salla.storage\n// (client) as a fallback. The server-only import is stripped from the client bundle by\n// createIsomorphicFn.\nconst readFallbackToken = createIsomorphicFn()\n .server((): string | null => {\n try {\n return getCookie(AUTH_TOKEN_COOKIE_NAME) ?? null;\n } catch {\n return null;\n }\n })\n .client((): string | null => {\n const fromCookie =\n typeof document !== 'undefined'\n ? extractTokenFromCookies(document.cookie, AUTH_TOKEN_COOKIE_NAME)\n : null;\n if (fromCookie) return fromCookie;\n const storage = typeof window !== 'undefined' ? window.salla?.storage : undefined;\n const fromStorage =\n typeof storage?.get === 'function' ? storage.get(AUTH_TOKEN_COOKIE_NAME) : null;\n if (typeof fromStorage === 'string' && fromStorage) return fromStorage;\n // `?token=` webview injection lands only on the SDK's axios instance\n const fromSdkApi = typeof window !== 'undefined' ? window.salla?.api?.token : undefined;\n return typeof fromSdkApi === 'string' && fromSdkApi ? fromSdkApi : null;\n });\n\n/**\n * The auth token as the api client sees it: twilight context first, cookie/storage fallback.\n * Use this (not `getTwilightContext().authToken`, which is often null for logged-in users —\n * see the note above) to decide whether an authenticated-only query should run.\n */\nexport const getAuthToken = (): string | null =>\n getTwilightContext().authToken ?? readFallbackToken();\n\nconst readRequestCurrency = createIsomorphicFn()\n .server((): string | null => {\n try {\n return toCurrencyCode(getCookie(CURRENCY_COOKIE_NAME));\n } catch {\n return null;\n }\n })\n .client((): string | null => null);\n\n/**\n * The currency the visitor chose, from the `s-curr` cookie the SDK writes: the API\n * answers in the store's own currency unless the request names one.\n */\nexport function getActiveCurrency(): string | null;\nexport function getActiveCurrency(fallback: string): string;\nexport function getActiveCurrency(fallback: string | null = null): string | null {\n const chosen =\n typeof document !== 'undefined'\n ? extractCurrencyFromCookies(document.cookie)\n : readRequestCurrency();\n return chosen ?? fallback;\n}\n\nconst API_HEADERS = {\n ACCEPT: 'accept',\n ACCEPT_LANGUAGE: 'accept-language',\n APP_OS: 's-app-os',\n APP_VERSION: 's-app-version',\n RAY: 's-ray',\n SOURCE: 's-source',\n THEME_TYPE: 's-theme-type',\n STORE_API_VERSION: 's-store-api-version',\n REQUESTED_WITH: 'x-requested-with',\n CF_PROXY_WHITELIST: 's-cf-proxy-whitelist-id',\n STORE_IDENTIFIER: 'store-identifier',\n CURRENCY: 'currency',\n AUTHORIZATION: 'Authorization',\n VERSION_ID: 's-version-id',\n SCOPE_ID: 's-scope-id',\n SCOPE_TYPE: 's-scope-type',\n SCOPE_ALLOCATION_TYPE: 's-scope-allocation-type',\n SCOPE_ALLOCATION_ID: 's-scope-allocation-id',\n USER_AGENT: 'user-agent',\n} as const;\n\n/**\n * The header every Salla API call is answered by store.\n *\n * Exported because a caller that already knows which store it is asking about\n * must be able to say so on the request itself — see `store.settings`. Without\n * it the only way to name a store is the ambient resolver, and \"the identifier\n * I checked\" and \"the identifier that was sent\" become two different things.\n */\nexport const STORE_IDENTIFIER_HEADER = API_HEADERS.STORE_IDENTIFIER;\n\n/**\n * Active scope: the market plus its branch allocation, as the SDK stores it. On the\n * client it comes from `salla.storage.get('scope')`; before the SDK hydrates storage\n * it falls back to the `scope` cookie the theme mirrors that selection into (see\n * writeScopeCookie), then to the scope twilightMiddleware seeded from the same\n * cookie on the server. Requests vary by the `s-scope-*` headers, so React Query\n * keys must include this to avoid serving another branch's cached categories/products.\n *\n * The cookie step matters for hydration: the SDK is not guaranteed to have\n * populated `salla.storage` by the time the client rebuilds a dehydrated query\n * key, and without it the client would compute null against a scoped SSR key.\n */\nexport const getActiveScope = (): ActiveScope | null => {\n // `!== undefined`, not `!= null`: a null here is the SDK authoritatively saying \"no branch\n // selected\", which must beat a cookie left over from the previous selection. Only a\n // not-yet-ready storage API (undefined) falls through to the cookie.\n const fromStorage = readSdkScope();\n if (fromStorage !== undefined) return fromStorage;\n\n if (typeof document !== 'undefined') {\n const fromCookie = extractScopeFromCookies(document.cookie);\n if (fromCookie != null) return fromCookie;\n }\n\n try {\n return getTwilightContext().scope;\n } catch {\n return null;\n }\n};\n\n/** {@link getActiveScope} reduced to the market id. */\nexport const getActiveScopeId = (): string | null => getActiveScope()?.id ?? null;\n\nconst sharedHeaders = (locale: string) => ({\n [API_HEADERS.ACCEPT]: 'application/json' as const,\n [API_HEADERS.ACCEPT_LANGUAGE]: locale,\n [API_HEADERS.APP_OS]: 'browser',\n [API_HEADERS.APP_VERSION]: '2.14.363',\n [API_HEADERS.RAY]: '50',\n [API_HEADERS.SOURCE]: 'twilight',\n [API_HEADERS.THEME_TYPE]: 'react',\n [API_HEADERS.STORE_API_VERSION]: 'swoole',\n [API_HEADERS.REQUESTED_WITH]: 'XMLHttpRequest',\n [API_HEADERS.CF_PROXY_WHITELIST]: '019a2f78-a64c-7edb-proxy-whitelist-id-9a3a-17afda90f03a',\n});\n\n/**\n * Who a server-rendered call says it is.\n *\n * Every theme worker reaches api.salla.dev from Cloudflare's one Workers address, so\n * salla.dev's per-IP rate limits counted every React store as a single client and\n * refused the calls each page makes (`store/settings` among them: \"Store Unavailable\").\n * The zone skips rate limiting for a User-Agent shaped `Salla-<Name>/<N.N.N> (<env>; <meta>)`\n * from Cloudflare's or AWS's networks, the convention Salla's other services already send.\n *\n * Built from fixed parts: the zone's pattern takes only digits and dots for the version,\n * so a prerelease suffix is dropped, and an unbundled engine (`dev`) reads as `0.0.0`.\n */\nconst SERVER_USER_AGENT = `Salla-TwilightReact/${\n /^\\d+(?:\\.\\d+)*/.exec(ENGINE_VERSION)?.[0] ?? '0.0.0'\n} (${import.meta.env.DEV ? 'development' : 'production'}; ssr)`;\n\n/**\n * Store API base. `VITE_API_URL` override points a preview at a branch backend; unset → prod\n * `api.salla.dev`. Resolution order matters: the value the SSR worker resolved (published into\n * the head, since worker `vars` are server-only `process.env` and never reach the browser\n * bundle) wins, so the hydrated client keeps talking to the backend that rendered the page.\n */\n\nconst STORE_API_BASE =\n getInjectedRuntimeEnv().apiBase || readEnv('VITE_API_URL') || 'https://api.salla.dev/store/v1';\n\nexport const api = ky.extend({\n prefix: STORE_API_BASE,\n timeout: 8000,\n hooks: {\n beforeRequest: [\n ({ request }) => {\n const { locale, authToken } = getTwilightContext();\n const headers = sharedHeaders(locale);\n for (const [key, value] of Object.entries(headers)) {\n request.headers.set(key, value);\n }\n // Only when the caller did not say. `headers.set` here would otherwise\n // overwrite a per-request identifier with the ambient one, which is a\n // silent way to answer a question about one store with another's data.\n if (!request.headers.get(API_HEADERS.STORE_IDENTIFIER)) {\n request.headers.set(API_HEADERS.STORE_IDENTIFIER, getStoreIdentifier());\n }\n\n // Server only. A browser call comes from the shopper's own address and is\n // counted against it, which is what the zone's limits are for.\n if (typeof document === 'undefined') {\n request.headers.set(API_HEADERS.USER_AGENT, SERVER_USER_AGENT);\n }\n\n const scope = getActiveScope();\n\n // The version the request asked for, else the one the settings named for\n // this scope. Absent means \"whatever is live\", which is what an ordinary\n // storefront visit wants — so a null is left off rather than sent empty.\n const versionId = resolveThemeVersionId(scope?.id ?? null);\n if (versionId) {\n request.headers.set(API_HEADERS.VERSION_ID, versionId);\n }\n\n const currency = getActiveCurrency();\n if (currency) request.headers.set(API_HEADERS.CURRENCY, currency);\n\n const token = authToken ?? readFallbackToken();\n if (token) {\n request.headers.set(API_HEADERS.AUTHORIZATION, `Bearer ${token}`);\n }\n\n // The same four the SDK's own client sends. The API resolves stock per\n // allocation: id alone asks about the market, not the branch the shopper chose.\n if (scope) {\n request.headers.set(API_HEADERS.SCOPE_ID, scope.id);\n if (scope.type) request.headers.set(API_HEADERS.SCOPE_TYPE, scope.type);\n if (scope.allocation_type)\n request.headers.set(API_HEADERS.SCOPE_ALLOCATION_TYPE, scope.allocation_type);\n if (scope.allocation_id)\n request.headers.set(API_HEADERS.SCOPE_ALLOCATION_ID, scope.allocation_id);\n }\n },\n // Last, so it keys on the request with every header above set.\n devSettingsCache.lookup,\n ],\n afterResponse: [devSettingsCache.store],\n },\n});\n\nexport const cdn = ky.extend({\n prefix: 'https://cdn.salla.network',\n timeout: 5000,\n});\n\n/**\n * The merchant uploader's public bucket, `admin-uploader-prod`.\n *\n * A different host from {@link cdn} and a different thing behind it: this one\n * is an R2 custom domain, so a read never passes through a worker. Storefront\n * translations live here, per language, under `translations/` — see\n * `api/translations.ts`.\n */\nexport const filesCdn = ky.extend({\n prefix: 'https://cdn.files.salla.network',\n timeout: 5000,\n});\n"]}
@@ -1,7 +1,7 @@
1
1
  import { MadeWithSalla } from './chunk-OQIIJRAS.js';
2
2
  import { HookSlot, useIsClient } from './chunk-5XDPAKF5.js';
3
3
  import { useTranslation } from './chunk-TYNZ5B2B.js';
4
- import { Image } from './chunk-BBMRL2QU.js';
4
+ import { Image } from './chunk-TDIENKPW.js';
5
5
  import { Link } from './chunk-AXW67SY7.js';
6
6
  import { LazyImageModal } from './chunk-KYMWEUN2.js';
7
7
  import { useTwilight } from './chunk-DF6FBKDN.js';
@@ -281,5 +281,5 @@ function Footer({ inSheet = false } = {}) {
281
281
  }
282
282
 
283
283
  export { Footer };
284
- //# sourceMappingURL=chunk-LQ2PXFR4.js.map
285
- //# sourceMappingURL=chunk-LQ2PXFR4.js.map
284
+ //# sourceMappingURL=chunk-VNZ6VI2O.js.map
285
+ //# sourceMappingURL=chunk-VNZ6VI2O.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/layout/Footer.tsx"],"names":[],"mappings":"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","file":"chunk-LQ2PXFR4.js","sourcesContent":["import { useState } from 'react';\nimport { SallaAppsIcons } from '@salla.sa/twilight-components-react/apps-icons';\nimport { SallaContacts } from '@salla.sa/twilight-components-react/contacts';\nimport { SallaSocial } from '@salla.sa/twilight-components-react/social';\nimport { SallaMenu } from '@salla.sa/twilight-components-react/menu';\nimport { SallaTrustBadges } from '@salla.sa/twilight-components-react/trust-badges';\nimport { SallaPayments } from '@salla.sa/twilight-components-react/payments';\nimport { useTwilight } from '../../providers/twilight-context';\nimport { useTranslation } from '../../providers/I18nProvider';\nimport { HookSlot } from '../../hooks/HookSlot';\nimport { useIsClient } from '../../hooks/useIsClient';\nimport { Link, Image } from '../common';\nimport { ImageModal } from '../modal';\nimport { MadeWithSalla } from './MadeWithSalla';\n\n/** Salla's value-added-tax badge icon (70×70, CDN-resized) — a platform\n * asset shared by every store, not specific to any one theme. */\nconst TAX_ICON_URL =\n 'https://cdn.salla.network/cdn-cgi/image/fit=scale-down,width=70,height=70,onerror=redirect,format=auto/images/tax.png?v=2.0.5';\n\n/**\n * The store's value-added-tax registration: the tax number, plus the certificate as\n * the tax icon that opens the certificate image full size in a modal. Nothing shows\n * when the store has no tax number.\n */\nfunction FooterTax({ dark }: { dark: boolean }) {\n const { store } = useTwilight();\n const { t } = useTranslation();\n const [open, setOpen] = useState(false);\n const number = store.settings?.tax?.number;\n const certificate = store.settings?.tax?.certificate;\n if (!number) return null;\n\n return (\n <div\n data-testid=\"store-footer-tax\"\n className=\"store-footer__tax flex items-end gap-2 rtl:space-x-reverse\"\n >\n {certificate && (\n <>\n <button\n type=\"button\"\n data-testid=\"store-footer-tax-certificate\"\n className=\"store-footer__tax-cert shrink-0\"\n aria-label={t('common.elements.tax_certificate', 'Tax Certificate')}\n aria-haspopup=\"dialog\"\n onClick={() => setOpen(true)}\n >\n <img\n src={TAX_ICON_URL}\n alt={t('common.elements.tax_certificate', 'Tax Certificate')}\n width={40}\n height={50}\n loading=\"lazy\"\n decoding=\"async\"\n className=\"w-10 rounded-sm transition-opacity hover:opacity-80\"\n />\n </button>\n <ImageModal\n isOpen={open}\n onClose={() => setOpen(false)}\n src={certificate}\n alt={t('common.elements.tax_certificate', 'Tax Certificate')}\n title={t('common.elements.tax_certificate', 'Tax Certificate')}\n />\n </>\n )}\n <div>\n <p className={`mb-1 text-sm opacity-70 ${dark ? 'text-white' : ''}`}>\n {t('common.elements.tax_number', 'Tax Number')}\n </p>\n <b className=\"text-sm\">{number}</b>\n </div>\n </div>\n );\n}\n\n/** Saudi Business Center (SBC) logo, CDN-versioned like the tax icon -- also a\n * platform asset, not theme-specific. */\nconst SBC_ICON_URL = 'https://cdn.salla.network/images/sbc.png?v=2.0.5';\n\n/**\n * The store's Saudi Business Center certificate: a \"Trusted by Business Platform\" badge that\n * opens the certificate's page on the SBC site so visitors can verify the merchant. Nothing\n * shows when the store has no certificate.\n */\nfunction FooterSbc({ dark }: { dark: boolean }) {\n const { store } = useTwilight();\n const { t } = useTranslation();\n const id = store.settings?.certificate?.id;\n if (!id) return null;\n\n return (\n <a\n href={`https://eauthenticate.saudibusiness.gov.sa/certificate-details/${encodeURIComponent(id)}`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-testid=\"store-footer-sbc\"\n className=\"store-footer__sbc flex items-center gap-2.5 transition-opacity hover:opacity-75\"\n >\n <img\n src={SBC_ICON_URL}\n alt={`SBC ${id}`}\n width={56}\n height={40}\n loading=\"lazy\"\n decoding=\"async\"\n className=\"h-10 w-14 rounded-sm\"\n />\n <div>\n <p className={`text-xs opacity-70 ${dark ? 'text-white' : ''}`}>\n {t('blocks.footer.sbc_trusted_by', 'Trusted by')}\n </p>\n <b className=\"text-sm\">{t('blocks.footer.sbc_platform', 'Business Platform')}</b>\n </div>\n </a>\n );\n}\n\n/** Trust badges, the tax registration and the SBC badge, side by side (wrapping on narrow columns). */\nfunction FooterTrust({ dark, centered }: { dark: boolean; centered: boolean }) {\n return (\n <div\n data-testid=\"store-footer-trust\"\n className={`store-footer__trust flex flex-wrap items-end gap-x-6 gap-y-3${centered ? ' justify-center' : ''}`}\n >\n <FooterTax dark={dark} />\n <FooterSbc dark={dark} />\n <SallaTrustBadges {...(dark ? { dark: true } : {})} />\n </div>\n );\n}\n\n/**\n * The footer's own menu of (\"important\") links -- skipped entirely when\n * `inSheet`, for a theme that opens it from somewhere else of its own (a\n * mobile account sheet, a dedicated links screen, …) instead; showing it\n * again down here would just be a redundant second copy. `heading` wraps it\n * in the grid layout's own `.store-footer__links` column; the centered\n * layout's inline row has none.\n */\nfunction FooterLinksMenu({\n inSheet,\n className,\n heading,\n dark,\n}: {\n inSheet: boolean;\n className: string;\n heading?: string;\n dark: boolean;\n}) {\n if (inSheet) return null;\n\n const menu = <SallaMenu source=\"footer\" className={className} suppressHydrationWarning />;\n if (!heading) return menu;\n\n return (\n <div className=\"store-footer__links\">\n <h3 className={`mb-3 text-lg font-bold lg:mb-5 ${dark ? 'text-white' : 'text-gray-900'}`}>\n {heading}\n </h3>\n {menu}\n </div>\n );\n}\n\n/** App-store / Google-Play buttons. Nothing shows -- not even the wrapping\n * div -- when the store has neither link set. */\nfunction FooterApps({ className }: { className: string }) {\n const { store } = useTwilight();\n const { t } = useTranslation();\n // The web component fills its light DOM before React hydrates the footer, a hydration error.\n const isClient = useIsClient();\n if (!store.apps?.appstore && !store.apps?.googleplay) return null;\n\n return (\n <div data-testid=\"store-footer-apps\" className={`store-footer__apps ${className}`}>\n {isClient && (\n <SallaAppsIcons\n apps={store.apps}\n appsTitle={t('blocks.footer.download_apps', 'Get our apps')}\n />\n )}\n </div>\n );\n}\n\n/** The logo link, same shape in both layouts -- only its own spacing differs. */\nfunction FooterLogo({ className }: { className: string }) {\n const { store } = useTwilight();\n return (\n <Link\n to=\"/\"\n className={`store-footer__logo inline-flex items-center m-0 transition-opacity hover:opacity-75 ${className}`}\n >\n <Image\n src={store.logo}\n alt={store.name || 'Store'}\n width={150}\n height={44}\n objectFit=\"contain\"\n className=\"h-11 w-auto max-w-[180px]\"\n />\n </Link>\n );\n}\n\n/** Merchant-authored description HTML -- mirrors the Twig theme's\n * `store.description|raw`. Renders nothing when the store has none set. */\nfunction FooterDescription({ className }: { className: string }) {\n const { store } = useTwilight();\n if (!store.description) return null;\n return (\n <div\n className={`store-footer__desc opacity-80 ${className}`}\n dangerouslySetInnerHTML={{ __html: store.description }}\n />\n );\n}\n\n/** The store's phone and email as one \"Contact us: …\" row under the description\n * (`<salla-contacts>`'s compact `isHeader` layout, which shows only those two).\n * Renders nothing when the store has neither. */\nfunction FooterContacts({ className }: { className: string }) {\n const { store } = useTwilight();\n const { t } = useTranslation();\n // The web component fills its light DOM once the SDK is ready, which can come before\n // React hydrates the footer's Suspense boundary: a hydration error (React #418).\n const isClient = useIsClient();\n if (!store.contacts?.phone && !store.contacts?.email) return null;\n return (\n <div className={`store-footer__contacts ${className}`}>\n {isClient && (\n <SallaContacts\n contacts={store.contacts}\n contactsTitle={t('blocks.footer.social', 'Contact us')}\n isHeader\n />\n )}\n </div>\n );\n}\n\n/** Centered layout: logo, description, contacts, social, footer menu, trust\n * badges and app buttons stacked as one centered column. */\nfunction FooterCenteredBody({ inSheet, isDark }: { inSheet: boolean; isDark: boolean }) {\n return (\n <div className=\"container mx-auto flex max-w-2xl flex-col items-center gap-6 text-center\">\n <FooterLogo className=\"\" />\n <FooterDescription className=\"\" />\n <FooterContacts className=\"\" />\n <SallaSocial />\n\n <FooterLinksMenu\n inSheet={inSheet}\n className=\"store-footer__menu store-footer__menu--inline\"\n dark={isDark}\n />\n\n <FooterTrust dark={isDark} centered />\n\n <FooterApps className=\"flex flex-col items-center gap-3\" />\n </div>\n );\n}\n\n/** Columns layout: brand (logo / description / contacts / social / trust) and app\n * buttons side by side, with the footer menu as its own labeled column. */\nfunction FooterColumnsBody({ inSheet, isDark }: { inSheet: boolean; isDark: boolean }) {\n const { t } = useTranslation();\n\n return (\n <div className=\"container grid grid-cols-1 gap-10 sm:grid-cols-2 lg:grid-cols-4 lg:gap-16\">\n <div className=\"store-footer__brand rtl:lg:pl-16 ltr:lg:pr-16 sm:col-span-2\">\n <FooterLogo className=\"mb-5\" />\n <FooterDescription className=\"max-w-sm leading-6 my-5\" />\n <FooterContacts className=\"mb-6\" />\n <SallaSocial className=\"block mb-6\" />\n <FooterTrust dark={isDark} centered={false} />\n </div>\n\n <FooterLinksMenu\n inSheet={inSheet}\n className=\"store-footer__menu store-footer__menu--stacked\"\n heading={t('blocks.footer.important_links', 'Important links')}\n dark={isDark}\n />\n\n <FooterApps className=\"\" />\n </div>\n );\n}\n\nexport interface FooterProps {\n /**\n * Renders as a plain `<div>` (no second `<footer>` landmark) with the\n * `footer:start` / `footer:end` hook slots skipped -- for a theme that\n * repeats these widgets somewhere other than the page's own footer (a\n * mobile sheet, for example) rather than rendering the real page footer a\n * second time.\n */\n inSheet?: boolean;\n}\n\n/**\n * Storefront footer.\n *\n * Two layouts, chosen by the `footer_layout` theme setting:\n * - **columns** (default): logo / description / social / trust badges, the\n * footer menu (\"important links\"), and app-store buttons, side by side.\n * - **centered**: the same pieces stacked as one centered column, with the\n * footer menu laid out as an inline, pipe-separated row.\n *\n * Styling for the handful of pieces rendered by web components this owns no\n * markup for (`<salla-social>`'s `.social-link` icons, `<salla-menu>`'s\n * `.s-menu-footer-item` rows, `<salla-payments>`'s `.s-payments-list-item`\n * icons) is the consuming theme's own responsibility, the same way\n * `inSheet`'s own presentation is -- this component owns structure and data,\n * not every pixel.\n */\nexport function Footer({ inSheet = false }: FooterProps = {}) {\n const { store, theme } = useTwilight();\n const { t } = useTranslation();\n const year = new Date().getFullYear();\n const isCentered = theme.settings?.footer_layout === 'centered';\n const isDark = !!theme.settings?.footer_is_dark;\n\n const Root = inSheet ? 'div' : 'footer';\n\n return (\n <Root\n className={`store-footer store-footer--${isCentered ? 'centered' : 'columns'}${\n inSheet ? ' store-footer--sheet' : ''\n } relative mt-8 sm:mt-16 ${isDark ? 'bg-[#0E0F0F] text-white' : 'bg-[#F4F7F2] text-gray-600'}`}\n suppressHydrationWarning\n >\n {!inSheet && <HookSlot name=\"footer:start\" />}\n\n <div\n aria-label=\"footer\"\n className={`store-footer__inner border-b py-8 lg:py-16 ${\n isDark ? 'bg-[#1D1F1F] border-dashed border-white/10' : 'bg-transparent border-gray-200'\n }`}\n >\n {isCentered ? (\n <FooterCenteredBody inSheet={inSheet} isDark={isDark} />\n ) : (\n <FooterColumnsBody inSheet={inSheet} isDark={isDark} />\n )}\n </div>\n\n <div className=\"store-footer__bottom container md:flex items-center justify-between gap-4 py-4 lg:py-7 text-center\">\n <span className={`text-sm copyright-text ${isDark ? 'text-gray-400' : 'text-gray-700'}`}>\n <HookSlot\n name=\"copyright\"\n context={{ storeName: store.name }}\n fallback={\n <>\n {t('blocks.footer.copyright', 'All rights reserved')}{' '}\n <Link to=\"/\" className=\"transition-opacity hover:opacity-75 hover:text-primary\">\n {store.name}\n </Link>{' '}\n © {year}\n <MadeWithSalla className=\"ms-2\" />\n </>\n }\n />\n </span>\n\n <SallaPayments />\n </div>\n\n {!inSheet && <HookSlot name=\"footer:end\" />}\n </Root>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/components/layout/Footer.tsx"],"names":[],"mappings":"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","file":"chunk-VNZ6VI2O.js","sourcesContent":["import { useState } from 'react';\nimport { SallaAppsIcons } from '@salla.sa/twilight-components-react/apps-icons';\nimport { SallaContacts } from '@salla.sa/twilight-components-react/contacts';\nimport { SallaSocial } from '@salla.sa/twilight-components-react/social';\nimport { SallaMenu } from '@salla.sa/twilight-components-react/menu';\nimport { SallaTrustBadges } from '@salla.sa/twilight-components-react/trust-badges';\nimport { SallaPayments } from '@salla.sa/twilight-components-react/payments';\nimport { useTwilight } from '../../providers/twilight-context';\nimport { useTranslation } from '../../providers/I18nProvider';\nimport { HookSlot } from '../../hooks/HookSlot';\nimport { useIsClient } from '../../hooks/useIsClient';\nimport { Link, Image } from '../common';\nimport { ImageModal } from '../modal';\nimport { MadeWithSalla } from './MadeWithSalla';\n\n/** Salla's value-added-tax badge icon (70×70, CDN-resized) — a platform\n * asset shared by every store, not specific to any one theme. */\nconst TAX_ICON_URL =\n 'https://cdn.salla.network/cdn-cgi/image/fit=scale-down,width=70,height=70,onerror=redirect,format=auto/images/tax.png?v=2.0.5';\n\n/**\n * The store's value-added-tax registration: the tax number, plus the certificate as\n * the tax icon that opens the certificate image full size in a modal. Nothing shows\n * when the store has no tax number.\n */\nfunction FooterTax({ dark }: { dark: boolean }) {\n const { store } = useTwilight();\n const { t } = useTranslation();\n const [open, setOpen] = useState(false);\n const number = store.settings?.tax?.number;\n const certificate = store.settings?.tax?.certificate;\n if (!number) return null;\n\n return (\n <div\n data-testid=\"store-footer-tax\"\n className=\"store-footer__tax flex items-end gap-2 rtl:space-x-reverse\"\n >\n {certificate && (\n <>\n <button\n type=\"button\"\n data-testid=\"store-footer-tax-certificate\"\n className=\"store-footer__tax-cert shrink-0\"\n aria-label={t('common.elements.tax_certificate', 'Tax Certificate')}\n aria-haspopup=\"dialog\"\n onClick={() => setOpen(true)}\n >\n <img\n src={TAX_ICON_URL}\n alt={t('common.elements.tax_certificate', 'Tax Certificate')}\n width={40}\n height={50}\n loading=\"lazy\"\n decoding=\"async\"\n className=\"w-10 rounded-sm transition-opacity hover:opacity-80\"\n />\n </button>\n <ImageModal\n isOpen={open}\n onClose={() => setOpen(false)}\n src={certificate}\n alt={t('common.elements.tax_certificate', 'Tax Certificate')}\n title={t('common.elements.tax_certificate', 'Tax Certificate')}\n />\n </>\n )}\n <div>\n <p className={`mb-1 text-sm opacity-70 ${dark ? 'text-white' : ''}`}>\n {t('common.elements.tax_number', 'Tax Number')}\n </p>\n <b className=\"text-sm\">{number}</b>\n </div>\n </div>\n );\n}\n\n/** Saudi Business Center (SBC) logo, CDN-versioned like the tax icon -- also a\n * platform asset, not theme-specific. */\nconst SBC_ICON_URL = 'https://cdn.salla.network/images/sbc.png?v=2.0.5';\n\n/**\n * The store's Saudi Business Center certificate: a \"Trusted by Business Platform\" badge that\n * opens the certificate's page on the SBC site so visitors can verify the merchant. Nothing\n * shows when the store has no certificate.\n */\nfunction FooterSbc({ dark }: { dark: boolean }) {\n const { store } = useTwilight();\n const { t } = useTranslation();\n const id = store.settings?.certificate?.id;\n if (!id) return null;\n\n return (\n <a\n href={`https://eauthenticate.saudibusiness.gov.sa/certificate-details/${encodeURIComponent(id)}`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-testid=\"store-footer-sbc\"\n className=\"store-footer__sbc flex items-center gap-2.5 transition-opacity hover:opacity-75\"\n >\n <img\n src={SBC_ICON_URL}\n alt={`SBC ${id}`}\n width={56}\n height={40}\n loading=\"lazy\"\n decoding=\"async\"\n className=\"h-10 w-14 rounded-sm\"\n />\n <div>\n <p className={`text-xs opacity-70 ${dark ? 'text-white' : ''}`}>\n {t('blocks.footer.sbc_trusted_by', 'Trusted by')}\n </p>\n <b className=\"text-sm\">{t('blocks.footer.sbc_platform', 'Business Platform')}</b>\n </div>\n </a>\n );\n}\n\n/** Trust badges, the tax registration and the SBC badge, side by side (wrapping on narrow columns). */\nfunction FooterTrust({ dark, centered }: { dark: boolean; centered: boolean }) {\n return (\n <div\n data-testid=\"store-footer-trust\"\n className={`store-footer__trust flex flex-wrap items-end gap-x-6 gap-y-3${centered ? ' justify-center' : ''}`}\n >\n <FooterTax dark={dark} />\n <FooterSbc dark={dark} />\n <SallaTrustBadges {...(dark ? { dark: true } : {})} />\n </div>\n );\n}\n\n/**\n * The footer's own menu of (\"important\") links -- skipped entirely when\n * `inSheet`, for a theme that opens it from somewhere else of its own (a\n * mobile account sheet, a dedicated links screen, …) instead; showing it\n * again down here would just be a redundant second copy. `heading` wraps it\n * in the grid layout's own `.store-footer__links` column; the centered\n * layout's inline row has none.\n */\nfunction FooterLinksMenu({\n inSheet,\n className,\n heading,\n dark,\n}: {\n inSheet: boolean;\n className: string;\n heading?: string;\n dark: boolean;\n}) {\n if (inSheet) return null;\n\n const menu = <SallaMenu source=\"footer\" className={className} suppressHydrationWarning />;\n if (!heading) return menu;\n\n return (\n <div className=\"store-footer__links\">\n <h3 className={`mb-3 text-lg font-bold lg:mb-5 ${dark ? 'text-white' : 'text-gray-900'}`}>\n {heading}\n </h3>\n {menu}\n </div>\n );\n}\n\n/** App-store / Google-Play buttons. Nothing shows -- not even the wrapping\n * div -- when the store has neither link set. */\nfunction FooterApps({ className }: { className: string }) {\n const { store } = useTwilight();\n const { t } = useTranslation();\n // The web component fills its light DOM before React hydrates the footer, a hydration error.\n const isClient = useIsClient();\n if (!store.apps?.appstore && !store.apps?.googleplay) return null;\n\n return (\n <div data-testid=\"store-footer-apps\" className={`store-footer__apps ${className}`}>\n {isClient && (\n <SallaAppsIcons\n apps={store.apps}\n appsTitle={t('blocks.footer.download_apps', 'Get our apps')}\n />\n )}\n </div>\n );\n}\n\n/** The logo link, same shape in both layouts -- only its own spacing differs. */\nfunction FooterLogo({ className }: { className: string }) {\n const { store } = useTwilight();\n return (\n <Link\n to=\"/\"\n className={`store-footer__logo inline-flex items-center m-0 transition-opacity hover:opacity-75 ${className}`}\n >\n <Image\n src={store.logo}\n alt={store.name || 'Store'}\n width={150}\n height={44}\n objectFit=\"contain\"\n className=\"h-11 w-auto max-w-[180px]\"\n />\n </Link>\n );\n}\n\n/** Merchant-authored description HTML -- mirrors the Twig theme's\n * `store.description|raw`. Renders nothing when the store has none set. */\nfunction FooterDescription({ className }: { className: string }) {\n const { store } = useTwilight();\n if (!store.description) return null;\n return (\n <div\n className={`store-footer__desc opacity-80 ${className}`}\n dangerouslySetInnerHTML={{ __html: store.description }}\n />\n );\n}\n\n/** The store's phone and email as one \"Contact us: …\" row under the description\n * (`<salla-contacts>`'s compact `isHeader` layout, which shows only those two).\n * Renders nothing when the store has neither. */\nfunction FooterContacts({ className }: { className: string }) {\n const { store } = useTwilight();\n const { t } = useTranslation();\n // The web component fills its light DOM once the SDK is ready, which can come before\n // React hydrates the footer's Suspense boundary: a hydration error (React #418).\n const isClient = useIsClient();\n if (!store.contacts?.phone && !store.contacts?.email) return null;\n return (\n <div className={`store-footer__contacts ${className}`}>\n {isClient && (\n <SallaContacts\n contacts={store.contacts}\n contactsTitle={t('blocks.footer.social', 'Contact us')}\n isHeader\n />\n )}\n </div>\n );\n}\n\n/** Centered layout: logo, description, contacts, social, footer menu, trust\n * badges and app buttons stacked as one centered column. */\nfunction FooterCenteredBody({ inSheet, isDark }: { inSheet: boolean; isDark: boolean }) {\n return (\n <div className=\"container mx-auto flex max-w-2xl flex-col items-center gap-6 text-center\">\n <FooterLogo className=\"\" />\n <FooterDescription className=\"\" />\n <FooterContacts className=\"\" />\n <SallaSocial />\n\n <FooterLinksMenu\n inSheet={inSheet}\n className=\"store-footer__menu store-footer__menu--inline\"\n dark={isDark}\n />\n\n <FooterTrust dark={isDark} centered />\n\n <FooterApps className=\"flex flex-col items-center gap-3\" />\n </div>\n );\n}\n\n/** Columns layout: brand (logo / description / contacts / social / trust) and app\n * buttons side by side, with the footer menu as its own labeled column. */\nfunction FooterColumnsBody({ inSheet, isDark }: { inSheet: boolean; isDark: boolean }) {\n const { t } = useTranslation();\n\n return (\n <div className=\"container grid grid-cols-1 gap-10 sm:grid-cols-2 lg:grid-cols-4 lg:gap-16\">\n <div className=\"store-footer__brand rtl:lg:pl-16 ltr:lg:pr-16 sm:col-span-2\">\n <FooterLogo className=\"mb-5\" />\n <FooterDescription className=\"max-w-sm leading-6 my-5\" />\n <FooterContacts className=\"mb-6\" />\n <SallaSocial className=\"block mb-6\" />\n <FooterTrust dark={isDark} centered={false} />\n </div>\n\n <FooterLinksMenu\n inSheet={inSheet}\n className=\"store-footer__menu store-footer__menu--stacked\"\n heading={t('blocks.footer.important_links', 'Important links')}\n dark={isDark}\n />\n\n <FooterApps className=\"\" />\n </div>\n );\n}\n\nexport interface FooterProps {\n /**\n * Renders as a plain `<div>` (no second `<footer>` landmark) with the\n * `footer:start` / `footer:end` hook slots skipped -- for a theme that\n * repeats these widgets somewhere other than the page's own footer (a\n * mobile sheet, for example) rather than rendering the real page footer a\n * second time.\n */\n inSheet?: boolean;\n}\n\n/**\n * Storefront footer.\n *\n * Two layouts, chosen by the `footer_layout` theme setting:\n * - **columns** (default): logo / description / social / trust badges, the\n * footer menu (\"important links\"), and app-store buttons, side by side.\n * - **centered**: the same pieces stacked as one centered column, with the\n * footer menu laid out as an inline, pipe-separated row.\n *\n * Styling for the handful of pieces rendered by web components this owns no\n * markup for (`<salla-social>`'s `.social-link` icons, `<salla-menu>`'s\n * `.s-menu-footer-item` rows, `<salla-payments>`'s `.s-payments-list-item`\n * icons) is the consuming theme's own responsibility, the same way\n * `inSheet`'s own presentation is -- this component owns structure and data,\n * not every pixel.\n */\nexport function Footer({ inSheet = false }: FooterProps = {}) {\n const { store, theme } = useTwilight();\n const { t } = useTranslation();\n const year = new Date().getFullYear();\n const isCentered = theme.settings?.footer_layout === 'centered';\n const isDark = !!theme.settings?.footer_is_dark;\n\n const Root = inSheet ? 'div' : 'footer';\n\n return (\n <Root\n className={`store-footer store-footer--${isCentered ? 'centered' : 'columns'}${\n inSheet ? ' store-footer--sheet' : ''\n } relative mt-8 sm:mt-16 ${isDark ? 'bg-[#0E0F0F] text-white' : 'bg-[#F4F7F2] text-gray-600'}`}\n suppressHydrationWarning\n >\n {!inSheet && <HookSlot name=\"footer:start\" />}\n\n <div\n aria-label=\"footer\"\n className={`store-footer__inner border-b py-8 lg:py-16 ${\n isDark ? 'bg-[#1D1F1F] border-dashed border-white/10' : 'bg-transparent border-gray-200'\n }`}\n >\n {isCentered ? (\n <FooterCenteredBody inSheet={inSheet} isDark={isDark} />\n ) : (\n <FooterColumnsBody inSheet={inSheet} isDark={isDark} />\n )}\n </div>\n\n <div className=\"store-footer__bottom container md:flex items-center justify-between gap-4 py-4 lg:py-7 text-center\">\n <span className={`text-sm copyright-text ${isDark ? 'text-gray-400' : 'text-gray-700'}`}>\n <HookSlot\n name=\"copyright\"\n context={{ storeName: store.name }}\n fallback={\n <>\n {t('blocks.footer.copyright', 'All rights reserved')}{' '}\n <Link to=\"/\" className=\"transition-opacity hover:opacity-75 hover:text-primary\">\n {store.name}\n </Link>{' '}\n © {year}\n <MadeWithSalla className=\"ms-2\" />\n </>\n }\n />\n </span>\n\n <SallaPayments />\n </div>\n\n {!inSheet && <HookSlot name=\"footer:end\" />}\n </Root>\n );\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { orThrow } from './chunk-PLPIAVLJ.js';
2
- import { api } from './chunk-J5OJ7JM2.js';
1
+ import { orThrow } from './chunk-SOLUKKTW.js';
2
+ import { api } from './chunk-UXJAZ5JP.js';
3
3
  import { queryOptions } from '@tanstack/react-query';
4
4
 
5
5
  var brand = {
@@ -25,5 +25,5 @@ var brand = {
25
25
  };
26
26
 
27
27
  export { brand };
28
- //# sourceMappingURL=chunk-ELQFU3RH.js.map
29
- //# sourceMappingURL=chunk-ELQFU3RH.js.map
28
+ //# sourceMappingURL=chunk-VXMWMGBQ.js.map
29
+ //# sourceMappingURL=chunk-VXMWMGBQ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/api/brands.ts"],"names":[],"mappings":";;;;AAMO,IAAM,KAAA,GAAQ;AAAA,EACnB,IAAA,EAAM,OAAO,IAAA,KAAmD;AAC9D,IAAA,MAAM,EAAE,IAAA,EAAM,MAAA,EAAO,GAAI,MAAM,GAAA,CAC5B,GAAA,CAAI,IAAA,GAAO,CAAA,YAAA,EAAe,IAAI,CAAA,CAAA,GAAK,QAAQ,EAC3C,IAAA,EAAoD;AACvD,IAAA,OAAO,EAAE,OAAO,IAAA,IAAQ,IAAI,IAAA,EAAM,MAAA,EAAQ,QAAQ,IAAA,EAAK;AAAA,EACzD,CAAA;AAAA,EAEA,IAAA,EAAM,OAAO,EAAA,KAA+B;AAC1C,IAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,GAAA,CAAI,IAAI,CAAA,OAAA,EAAU,EAAE,CAAA,CAAE,CAAA,CAAE,IAAA,EAAsB;AACrE,IAAA,OAAO,IAAA;AAAA,EACT,CAAA;AAAA,EAEA,aAAa,CAAC,EAAA,KAA+B,QAAQ,KAAA,CAAM,IAAA,CAAK,EAAE,CAAC,CAAA;AAAA,EAEnE,OAAA,EAAS;AAAA,IACP,IAAA,EAAM,CAAC,IAAA,KACL,YAAA,CAAa;AAAA,MACX,QAAA,EAAU,CAAC,QAAA,EAAU,MAAA,EAAQ,QAAQ,CAAC,CAAA;AAAA,MACtC,OAAA,EAAS,MAAM,KAAA,CAAM,IAAA,CAAK,IAAI;AAAA,KAC/B,CAAA;AAAA,IACH,MAAA,EAAQ,CAAC,EAAA,KACP,YAAA,CAAa;AAAA,MACX,QAAA,EAAU,CAAC,QAAA,EAAU,QAAA,EAAU,EAAE,CAAA;AAAA,MACjC,OAAA,EAAS,MAAM,KAAA,CAAM,IAAA,CAAK,EAAE;AAAA,KAC7B;AAAA;AAEP","file":"chunk-ELQFU3RH.js","sourcesContent":["import { queryOptions } from '@tanstack/react-query';\nimport { api } from './client';\nimport type { Brand } from '../routes/brands/types';\nimport type { PaginatedResult, Pagination } from './types';\nimport { orThrow } from '../providers/NavigationProvider';\n\nexport const brand = {\n list: async (page?: number): Promise<PaginatedResult<Brand>> => {\n const { data, cursor } = await api\n .get(page ? `brands?page=${page}` : 'brands')\n .json<{ data: Brand[] | null; cursor?: Pagination }>();\n return { items: data ?? [], next: cursor?.next ?? null };\n },\n\n find: async (id: string): Promise<Brand> => {\n const { data } = await api.get(`brands/${id}`).json<{ data: Brand }>();\n return data;\n },\n\n findOrThrow: (id: string): Promise<Brand> => orThrow(brand.find(id)),\n\n queries: {\n list: (page?: number) =>\n queryOptions({\n queryKey: ['brands', 'list', page ?? 1],\n queryFn: () => brand.list(page),\n }),\n detail: (id: string) =>\n queryOptions({\n queryKey: ['brands', 'detail', id],\n queryFn: () => brand.find(id),\n }),\n },\n};\n"]}
1
+ {"version":3,"sources":["../src/api/brands.ts"],"names":[],"mappings":";;;;AAMO,IAAM,KAAA,GAAQ;AAAA,EACnB,IAAA,EAAM,OAAO,IAAA,KAAmD;AAC9D,IAAA,MAAM,EAAE,IAAA,EAAM,MAAA,EAAO,GAAI,MAAM,GAAA,CAC5B,GAAA,CAAI,IAAA,GAAO,CAAA,YAAA,EAAe,IAAI,CAAA,CAAA,GAAK,QAAQ,EAC3C,IAAA,EAAoD;AACvD,IAAA,OAAO,EAAE,OAAO,IAAA,IAAQ,IAAI,IAAA,EAAM,MAAA,EAAQ,QAAQ,IAAA,EAAK;AAAA,EACzD,CAAA;AAAA,EAEA,IAAA,EAAM,OAAO,EAAA,KAA+B;AAC1C,IAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,GAAA,CAAI,IAAI,CAAA,OAAA,EAAU,EAAE,CAAA,CAAE,CAAA,CAAE,IAAA,EAAsB;AACrE,IAAA,OAAO,IAAA;AAAA,EACT,CAAA;AAAA,EAEA,aAAa,CAAC,EAAA,KAA+B,QAAQ,KAAA,CAAM,IAAA,CAAK,EAAE,CAAC,CAAA;AAAA,EAEnE,OAAA,EAAS;AAAA,IACP,IAAA,EAAM,CAAC,IAAA,KACL,YAAA,CAAa;AAAA,MACX,QAAA,EAAU,CAAC,QAAA,EAAU,MAAA,EAAQ,QAAQ,CAAC,CAAA;AAAA,MACtC,OAAA,EAAS,MAAM,KAAA,CAAM,IAAA,CAAK,IAAI;AAAA,KAC/B,CAAA;AAAA,IACH,MAAA,EAAQ,CAAC,EAAA,KACP,YAAA,CAAa;AAAA,MACX,QAAA,EAAU,CAAC,QAAA,EAAU,QAAA,EAAU,EAAE,CAAA;AAAA,MACjC,OAAA,EAAS,MAAM,KAAA,CAAM,IAAA,CAAK,EAAE;AAAA,KAC7B;AAAA;AAEP","file":"chunk-VXMWMGBQ.js","sourcesContent":["import { queryOptions } from '@tanstack/react-query';\nimport { api } from './client';\nimport type { Brand } from '../routes/brands/types';\nimport type { PaginatedResult, Pagination } from './types';\nimport { orThrow } from '../providers/NavigationProvider';\n\nexport const brand = {\n list: async (page?: number): Promise<PaginatedResult<Brand>> => {\n const { data, cursor } = await api\n .get(page ? `brands?page=${page}` : 'brands')\n .json<{ data: Brand[] | null; cursor?: Pagination }>();\n return { items: data ?? [], next: cursor?.next ?? null };\n },\n\n find: async (id: string): Promise<Brand> => {\n const { data } = await api.get(`brands/${id}`).json<{ data: Brand }>();\n return data;\n },\n\n findOrThrow: (id: string): Promise<Brand> => orThrow(brand.find(id)),\n\n queries: {\n list: (page?: number) =>\n queryOptions({\n queryKey: ['brands', 'list', page ?? 1],\n queryFn: () => brand.list(page),\n }),\n detail: (id: string) =>\n queryOptions({\n queryKey: ['brands', 'detail', id],\n queryFn: () => brand.find(id),\n }),\n },\n};\n"]}