@salla.sa/twilight-components 2.14.590 → 2.14.592

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 (390) hide show
  1. package/dist/cjs/{filepond-Dt4Nihdd.js → filepond-YeXzkn77.js} +1 -1
  2. package/dist/cjs/{filepond-plugin-file-poster-DwwSoJTP.js → filepond-plugin-file-poster-BEU99tEc.js} +1 -1
  3. package/dist/cjs/{filepond-plugin-file-validate-size-DsVYh3H7.js → filepond-plugin-file-validate-size-BSsf6VIk.js} +1 -1
  4. package/dist/cjs/{filepond-plugin-file-validate-type-D5SqBZyw.js → filepond-plugin-file-validate-type-BkxQQF1Y.js} +1 -1
  5. package/dist/cjs/{filepond-plugin-image-edit-CTTRxQHm.js → filepond-plugin-image-edit-h9C4LxoR.js} +1 -1
  6. package/dist/cjs/{filepond-plugin-image-exif-orientation-CN52oBp4.js → filepond-plugin-image-exif-orientation-nDfSwZEM.js} +1 -1
  7. package/dist/cjs/{filepond-plugin-image-preview-DgSWBJVB.js → filepond-plugin-image-preview-C-FDU_4k.js} +1 -1
  8. package/dist/cjs/{helpers-DBynEhOO.js → helpers-DSseNQ1K.js} +1 -1
  9. package/dist/cjs/{index-CgZ8Pldm.js → index-DI5yx1uj.js} +11 -15
  10. package/dist/cjs/{intlTelInput-DBBilzGk.js → intlTelInput-AgidDx18.js} +1 -1
  11. package/dist/cjs/loader.cjs.js +2 -2
  12. package/dist/cjs/salla-accordion-body_2.cjs.entry.js +1 -1
  13. package/dist/cjs/salla-accordion_6.cjs.entry.js +1 -1
  14. package/dist/cjs/salla-add-product-button_5.cjs.entry.js +37 -4
  15. package/dist/cjs/salla-advertisement.cjs.entry.js +1 -1
  16. package/dist/cjs/salla-alert_2.cjs.entry.js +1 -1
  17. package/dist/cjs/salla-app-install-alert.cjs.entry.js +1 -1
  18. package/dist/cjs/salla-applied-filters.cjs.entry.js +1 -1
  19. package/dist/cjs/salla-apps-icons.cjs.entry.js +1 -1
  20. package/dist/cjs/salla-attachment-view_9.cjs.entry.js +11 -11
  21. package/dist/cjs/salla-badge.cjs.entry.js +1 -1
  22. package/dist/cjs/salla-basket-gap.cjs.entry.js +1 -1
  23. package/dist/cjs/salla-bullet-delivery_3.cjs.entry.js +45 -34
  24. package/dist/cjs/salla-cart-coupons.cjs.entry.js +1 -1
  25. package/dist/cjs/salla-cart-item-offers_2.cjs.entry.js +1 -1
  26. package/dist/cjs/salla-cart-summary-card.cjs.entry.js +1 -1
  27. package/dist/cjs/salla-cashback-banner.cjs.entry.js +1 -1
  28. package/dist/cjs/salla-comment-factors.cjs.entry.js +1 -1
  29. package/dist/cjs/salla-comment-form_9.cjs.entry.js +3 -3
  30. package/dist/cjs/salla-conditional-offer.cjs.entry.js +2664 -106
  31. package/dist/cjs/salla-contacts.cjs.entry.js +1 -1
  32. package/dist/cjs/salla-cookies-bar.cjs.entry.js +1 -1
  33. package/dist/cjs/salla-count-down.cjs.entry.js +1 -1
  34. package/dist/cjs/salla-custom-fields.cjs.entry.js +1 -1
  35. package/dist/cjs/salla-delivery-promise.cjs.entry.js +2 -2
  36. package/dist/cjs/salla-edit-order-button.cjs.entry.js +1 -1
  37. package/dist/cjs/salla-filters-widget.cjs.entry.js +1 -1
  38. package/dist/cjs/salla-filters.cjs.entry.js +1 -1
  39. package/dist/cjs/salla-fulfillment-methods.cjs.entry.js +1 -1
  40. package/dist/cjs/{salla-gift-card-redeem.cjs.entry.js → salla-gift-card-redeem_2.cjs.entry.js} +36 -1
  41. package/dist/cjs/salla-gifting.cjs.entry.js +1 -1
  42. package/dist/cjs/salla-hook.cjs.entry.js +1 -1
  43. package/dist/cjs/salla-infinite-scroll.cjs.entry.js +1 -1
  44. package/dist/cjs/salla-installment.cjs.entry.js +1 -1
  45. package/dist/cjs/salla-list-tile.cjs.entry.js +1 -1
  46. package/dist/cjs/salla-localization-modal.cjs.entry.js +1 -1
  47. package/dist/cjs/salla-login-modal.cjs.entry.js +1 -1
  48. package/dist/cjs/salla-loyalty-banner_10.cjs.entry.js +1 -1
  49. package/dist/cjs/salla-loyalty-exchange-product-card.cjs.entry.js +1 -1
  50. package/dist/cjs/salla-loyalty-points-banner.cjs.entry.js +1 -1
  51. package/dist/cjs/salla-loyalty-points-history.cjs.entry.js +1 -1
  52. package/dist/cjs/salla-loyalty-prize-item.cjs.entry.js +1 -1
  53. package/dist/cjs/salla-loyalty.cjs.entry.js +1 -1
  54. package/dist/cjs/salla-maintenance-alert.cjs.entry.js +1 -1
  55. package/dist/cjs/salla-map_3.cjs.entry.js +1 -1
  56. package/dist/cjs/salla-menu.cjs.entry.js +1 -1
  57. package/dist/cjs/salla-metadata.cjs.entry.js +1 -1
  58. package/dist/cjs/salla-multiple-bundle-product-cart_2.cjs.entry.js +1 -1
  59. package/dist/cjs/salla-multiple-bundle-product-options-modal_2.cjs.entry.js +1 -1
  60. package/dist/cjs/salla-multiple-bundle-product.cjs.entry.js +1 -1
  61. package/dist/cjs/salla-next-order-coupon.cjs.entry.js +1 -1
  62. package/dist/cjs/salla-notification-item_2.cjs.entry.js +1 -1
  63. package/dist/cjs/salla-offer-modal.cjs.entry.js +1 -1
  64. package/dist/cjs/salla-offer.cjs.entry.js +1 -1
  65. package/dist/cjs/salla-order-branch.cjs.entry.js +1 -1
  66. package/dist/cjs/salla-order-cancel.cjs.entry.js +1 -1
  67. package/dist/cjs/salla-order-details-multiple-bundle-product.cjs.entry.js +1 -1
  68. package/dist/cjs/salla-order-details-options.cjs.entry.js +1 -1
  69. package/dist/cjs/salla-order-details.cjs.entry.js +1 -1
  70. package/dist/cjs/salla-order-edit-item.cjs.entry.js +1 -1
  71. package/dist/cjs/salla-order-edit-product-card.cjs.entry.js +1 -1
  72. package/dist/cjs/salla-order-edit.cjs.entry.js +1 -1
  73. package/dist/cjs/salla-order-options-modal.cjs.entry.js +1 -1
  74. package/dist/cjs/salla-order-shipments.cjs.entry.js +1 -1
  75. package/dist/cjs/salla-order-summary.cjs.entry.js +1 -1
  76. package/dist/cjs/salla-order-totals-card.cjs.entry.js +1 -1
  77. package/dist/cjs/salla-orders.cjs.entry.js +1 -1
  78. package/dist/cjs/salla-payments.cjs.entry.js +1 -1
  79. package/dist/cjs/salla-placeholder.cjs.entry.js +1 -1
  80. package/dist/cjs/salla-price-range.cjs.entry.js +1 -1
  81. package/dist/cjs/salla-product-card-embed.cjs.entry.js +1 -1
  82. package/dist/cjs/salla-product-card.cjs.entry.js +1 -1
  83. package/dist/cjs/salla-product-size-guide.cjs.entry.js +1 -1
  84. package/dist/cjs/salla-products-list.cjs.entry.js +1 -1
  85. package/dist/cjs/salla-products-slider.cjs.entry.js +1 -1
  86. package/dist/cjs/salla-quick-order.cjs.entry.js +1 -1
  87. package/dist/cjs/salla-rating-modal-v2.cjs.entry.js +1 -1
  88. package/dist/cjs/salla-rating-modal.cjs.entry.js +1 -1
  89. package/dist/cjs/salla-review-factors-tags.cjs.entry.js +2 -2
  90. package/dist/cjs/salla-review-order-item.cjs.entry.js +1 -1
  91. package/dist/cjs/salla-scopes.cjs.entry.js +1 -1
  92. package/dist/cjs/salla-search-product-card.cjs.entry.js +3 -3
  93. package/dist/cjs/salla-search.cjs.entry.js +2 -2
  94. package/dist/cjs/salla-skeleton.cjs.entry.js +2 -2
  95. package/dist/cjs/salla-social-share.cjs.entry.js +2 -2
  96. package/dist/cjs/salla-social.cjs.entry.js +2 -2
  97. package/dist/cjs/salla-tab-content_3.cjs.entry.js +4 -4
  98. package/dist/cjs/salla-tiered-offer.cjs.entry.js +1 -1
  99. package/dist/cjs/salla-trust-badges.cjs.entry.js +1 -1
  100. package/dist/cjs/salla-user-menu.cjs.entry.js +1 -1
  101. package/dist/cjs/salla-user-profile.cjs.entry.js +1 -1
  102. package/dist/cjs/salla-user-settings.cjs.entry.js +2 -2
  103. package/dist/cjs/salla-verify.cjs.entry.js +1 -1
  104. package/dist/cjs/salla-wallet.cjs.entry.js +1 -1
  105. package/dist/cjs/salla-wishlist-actions.cjs.entry.js +2 -2
  106. package/dist/cjs/salla-wishlist-summary.cjs.entry.js +1 -1
  107. package/dist/cjs/twilight.cjs.js +2 -2
  108. package/dist/cjs/{vanilla-picker-DMOjok7F.js → vanilla-picker-DLfPr_3c.js} +1 -1
  109. package/dist/collection/components/salla-add-product-button/salla-add-product-button.js +4 -1
  110. package/dist/collection/components/salla-bullet-delivery/helpers.js +1 -1
  111. package/dist/collection/components/salla-bullet-delivery/salla-bullet-delivery.js +39 -28
  112. package/dist/collection/components/salla-conditional-offer/cart-update-window.js +34 -0
  113. package/dist/collection/components/salla-conditional-offer/interfaces.js +1 -0
  114. package/dist/collection/components/salla-conditional-offer/salla-conditional-offer.css +456 -191
  115. package/dist/collection/components/salla-conditional-offer/salla-conditional-offer.js +1790 -106
  116. package/dist/collection/components/salla-quick-buy/salla-quick-buy.js +17 -4
  117. package/dist/collection/components/salla-review-card/salla-review-card.js +1 -1
  118. package/dist/collection/components/salla-review-factors-tags/salla-review-factors-tags.js +1 -1
  119. package/dist/collection/components/salla-reviews-page/salla-reviews-page.js +1 -1
  120. package/dist/collection/components/salla-search/salla-search.js +1 -1
  121. package/dist/collection/components/salla-search-product-card/salla-search-product-card.js +2 -2
  122. package/dist/collection/components/salla-searchable-dropdown/salla-searchable-dropdown.js +4 -4
  123. package/dist/collection/components/salla-skeleton/salla-skeleton.js +1 -1
  124. package/dist/collection/components/salla-social/salla-social.js +1 -1
  125. package/dist/collection/components/salla-social-share/salla-social-share.js +1 -1
  126. package/dist/collection/components/salla-tabs/salla-tab-content.js +1 -1
  127. package/dist/collection/components/salla-tabs/salla-tab-header.js +1 -1
  128. package/dist/collection/components/salla-tabs/salla-tabs.js +1 -1
  129. package/dist/collection/components/salla-tel-input/salla-tel-input.js +1 -1
  130. package/dist/collection/components/salla-tooltip/salla-tooltip.js +1 -1
  131. package/dist/collection/components/salla-user-settings/salla-user-settings.js +1 -1
  132. package/dist/collection/components/salla-wishlist-actions/salla-wishlist-actions.js +1 -1
  133. package/dist/collection/shared/cart-free-gifts.js +448 -0
  134. package/dist/collection/shared/offer-stages.js +453 -0
  135. package/dist/components/helpers.js +2 -2
  136. package/dist/components/index.js +3 -3
  137. package/dist/components/salla-add-product-button2.js +4 -1
  138. package/dist/components/salla-bullet-delivery.js +40 -29
  139. package/dist/components/salla-conditional-offer.js +2672 -116
  140. package/dist/components/salla-delivery-promise.js +1 -1
  141. package/dist/components/salla-quick-buy2.js +34 -4
  142. package/dist/components/salla-review-card2.js +1 -1
  143. package/dist/components/salla-review-factors-tags2.js +1 -1
  144. package/dist/components/salla-reviews-page.js +1 -1
  145. package/dist/components/salla-search-product-card2.js +2 -2
  146. package/dist/components/salla-search.js +1 -1
  147. package/dist/components/salla-searchable-dropdown2.js +4 -4
  148. package/dist/components/salla-skeleton2.js +1 -1
  149. package/dist/components/salla-social-share2.js +1 -1
  150. package/dist/components/salla-social.js +1 -1
  151. package/dist/components/salla-tab-content2.js +1 -1
  152. package/dist/components/salla-tab-header2.js +1 -1
  153. package/dist/components/salla-tabs2.js +1 -1
  154. package/dist/components/salla-tel-input2.js +1 -1
  155. package/dist/components/salla-tooltip2.js +1 -1
  156. package/dist/components/salla-user-settings.js +1 -1
  157. package/dist/components/salla-wishlist-actions.js +1 -1
  158. package/dist/docs.json +21 -8
  159. package/dist/esm/{filepond-wzh5HaPr.js → filepond-C3uLKJlo.js} +1 -1
  160. package/dist/esm/{filepond-plugin-file-poster-DInGlcq6.js → filepond-plugin-file-poster-BNO8nuwN.js} +1 -1
  161. package/dist/esm/{filepond-plugin-file-validate-size-BaLOl1L7.js → filepond-plugin-file-validate-size-CMMYF-rL.js} +1 -1
  162. package/dist/esm/{filepond-plugin-file-validate-type-D_DVE5xS.js → filepond-plugin-file-validate-type-DSYhKJ7B.js} +1 -1
  163. package/dist/esm/{filepond-plugin-image-edit-B9vWgHyi.js → filepond-plugin-image-edit-lJgRMQAo.js} +1 -1
  164. package/dist/esm/{filepond-plugin-image-exif-orientation-lJrZF6xG.js → filepond-plugin-image-exif-orientation-B0tYK1GE.js} +1 -1
  165. package/dist/esm/{filepond-plugin-image-preview-Bbk7m1Yt.js → filepond-plugin-image-preview-kWmnxhXu.js} +1 -1
  166. package/dist/esm/{helpers-BJxBnTgv.js → helpers-DgRxTSyJ.js} +2 -2
  167. package/dist/esm/{index-klFskvc_.js → index-Bk24FlDL.js} +11 -15
  168. package/dist/esm/{intlTelInput-pS6pc-iA.js → intlTelInput-nODQyuq0.js} +1 -1
  169. package/dist/esm/loader.js +3 -3
  170. package/dist/esm/salla-accordion-body_2.entry.js +1 -1
  171. package/dist/esm/salla-accordion_6.entry.js +1 -1
  172. package/dist/esm/salla-add-product-button_5.entry.js +37 -4
  173. package/dist/esm/salla-advertisement.entry.js +1 -1
  174. package/dist/esm/salla-alert_2.entry.js +1 -1
  175. package/dist/esm/salla-app-install-alert.entry.js +1 -1
  176. package/dist/esm/salla-applied-filters.entry.js +1 -1
  177. package/dist/esm/salla-apps-icons.entry.js +1 -1
  178. package/dist/esm/salla-attachment-view_9.entry.js +11 -11
  179. package/dist/esm/salla-badge.entry.js +1 -1
  180. package/dist/esm/salla-basket-gap.entry.js +1 -1
  181. package/dist/esm/salla-bullet-delivery_3.entry.js +45 -34
  182. package/dist/esm/salla-cart-coupons.entry.js +1 -1
  183. package/dist/esm/salla-cart-item-offers_2.entry.js +1 -1
  184. package/dist/esm/salla-cart-summary-card.entry.js +1 -1
  185. package/dist/esm/salla-cashback-banner.entry.js +1 -1
  186. package/dist/esm/salla-comment-factors.entry.js +1 -1
  187. package/dist/esm/salla-comment-form_9.entry.js +3 -3
  188. package/dist/esm/salla-conditional-offer.entry.js +2664 -106
  189. package/dist/esm/salla-contacts.entry.js +1 -1
  190. package/dist/esm/salla-cookies-bar.entry.js +1 -1
  191. package/dist/esm/salla-count-down.entry.js +1 -1
  192. package/dist/esm/salla-custom-fields.entry.js +1 -1
  193. package/dist/esm/salla-delivery-promise.entry.js +2 -2
  194. package/dist/esm/salla-edit-order-button.entry.js +1 -1
  195. package/dist/esm/salla-filters-widget.entry.js +1 -1
  196. package/dist/esm/salla-filters.entry.js +1 -1
  197. package/dist/esm/salla-fulfillment-methods.entry.js +1 -1
  198. package/dist/esm/{salla-gift-card-redeem.entry.js → salla-gift-card-redeem_2.entry.js} +36 -2
  199. package/dist/esm/salla-gifting.entry.js +1 -1
  200. package/dist/esm/salla-hook.entry.js +1 -1
  201. package/dist/esm/salla-infinite-scroll.entry.js +1 -1
  202. package/dist/esm/salla-installment.entry.js +1 -1
  203. package/dist/esm/salla-list-tile.entry.js +1 -1
  204. package/dist/esm/salla-localization-modal.entry.js +1 -1
  205. package/dist/esm/salla-login-modal.entry.js +1 -1
  206. package/dist/esm/salla-loyalty-banner_10.entry.js +1 -1
  207. package/dist/esm/salla-loyalty-exchange-product-card.entry.js +1 -1
  208. package/dist/esm/salla-loyalty-points-banner.entry.js +1 -1
  209. package/dist/esm/salla-loyalty-points-history.entry.js +1 -1
  210. package/dist/esm/salla-loyalty-prize-item.entry.js +1 -1
  211. package/dist/esm/salla-loyalty.entry.js +1 -1
  212. package/dist/esm/salla-maintenance-alert.entry.js +1 -1
  213. package/dist/esm/salla-map_3.entry.js +1 -1
  214. package/dist/esm/salla-menu.entry.js +1 -1
  215. package/dist/esm/salla-metadata.entry.js +1 -1
  216. package/dist/esm/salla-multiple-bundle-product-cart_2.entry.js +1 -1
  217. package/dist/esm/salla-multiple-bundle-product-options-modal_2.entry.js +1 -1
  218. package/dist/esm/salla-multiple-bundle-product.entry.js +1 -1
  219. package/dist/esm/salla-next-order-coupon.entry.js +1 -1
  220. package/dist/esm/salla-notification-item_2.entry.js +1 -1
  221. package/dist/esm/salla-offer-modal.entry.js +1 -1
  222. package/dist/esm/salla-offer.entry.js +1 -1
  223. package/dist/esm/salla-order-branch.entry.js +1 -1
  224. package/dist/esm/salla-order-cancel.entry.js +1 -1
  225. package/dist/esm/salla-order-details-multiple-bundle-product.entry.js +1 -1
  226. package/dist/esm/salla-order-details-options.entry.js +1 -1
  227. package/dist/esm/salla-order-details.entry.js +1 -1
  228. package/dist/esm/salla-order-edit-item.entry.js +1 -1
  229. package/dist/esm/salla-order-edit-product-card.entry.js +1 -1
  230. package/dist/esm/salla-order-edit.entry.js +1 -1
  231. package/dist/esm/salla-order-options-modal.entry.js +1 -1
  232. package/dist/esm/salla-order-shipments.entry.js +1 -1
  233. package/dist/esm/salla-order-summary.entry.js +1 -1
  234. package/dist/esm/salla-order-totals-card.entry.js +1 -1
  235. package/dist/esm/salla-orders.entry.js +1 -1
  236. package/dist/esm/salla-payments.entry.js +1 -1
  237. package/dist/esm/salla-placeholder.entry.js +1 -1
  238. package/dist/esm/salla-price-range.entry.js +1 -1
  239. package/dist/esm/salla-product-card-embed.entry.js +1 -1
  240. package/dist/esm/salla-product-card.entry.js +1 -1
  241. package/dist/esm/salla-product-size-guide.entry.js +1 -1
  242. package/dist/esm/salla-products-list.entry.js +1 -1
  243. package/dist/esm/salla-products-slider.entry.js +1 -1
  244. package/dist/esm/salla-quick-order.entry.js +1 -1
  245. package/dist/esm/salla-rating-modal-v2.entry.js +1 -1
  246. package/dist/esm/salla-rating-modal.entry.js +1 -1
  247. package/dist/esm/salla-review-factors-tags.entry.js +2 -2
  248. package/dist/esm/salla-review-order-item.entry.js +1 -1
  249. package/dist/esm/salla-scopes.entry.js +1 -1
  250. package/dist/esm/salla-search-product-card.entry.js +3 -3
  251. package/dist/esm/salla-search.entry.js +2 -2
  252. package/dist/esm/salla-skeleton.entry.js +2 -2
  253. package/dist/esm/salla-social-share.entry.js +2 -2
  254. package/dist/esm/salla-social.entry.js +2 -2
  255. package/dist/esm/salla-tab-content_3.entry.js +4 -4
  256. package/dist/esm/salla-tiered-offer.entry.js +1 -1
  257. package/dist/esm/salla-trust-badges.entry.js +1 -1
  258. package/dist/esm/salla-user-menu.entry.js +1 -1
  259. package/dist/esm/salla-user-profile.entry.js +1 -1
  260. package/dist/esm/salla-user-settings.entry.js +2 -2
  261. package/dist/esm/salla-verify.entry.js +1 -1
  262. package/dist/esm/salla-wallet.entry.js +1 -1
  263. package/dist/esm/salla-wishlist-actions.entry.js +2 -2
  264. package/dist/esm/salla-wishlist-summary.entry.js +1 -1
  265. package/dist/esm/twilight.js +3 -3
  266. package/dist/esm/{vanilla-picker-BhmFKXIB.js → vanilla-picker-B1bTUgHP.js} +1 -1
  267. package/dist/twilight/{p-482fee31.entry.js → p-02202feb.entry.js} +1 -1
  268. package/dist/twilight/{p-f6bae25a.entry.js → p-03dffb99.entry.js} +1 -1
  269. package/dist/twilight/{p-efeae895.entry.js → p-06643011.entry.js} +1 -1
  270. package/dist/twilight/p-07174244.entry.js +4 -0
  271. package/dist/twilight/{p-55f765dc.entry.js → p-08256e67.entry.js} +1 -1
  272. package/dist/twilight/{p-a0869ee6.entry.js → p-0dc90c55.entry.js} +1 -1
  273. package/dist/twilight/{p-dd775629.entry.js → p-0e122915.entry.js} +1 -1
  274. package/dist/twilight/{p-089f0d34.entry.js → p-1b1bebf6.entry.js} +1 -1
  275. package/dist/twilight/{p-6536466c.entry.js → p-1d05ff72.entry.js} +1 -1
  276. package/dist/twilight/{p-c33ed919.entry.js → p-21802082.entry.js} +1 -1
  277. package/dist/twilight/{p-317048c4.entry.js → p-2191d792.entry.js} +1 -1
  278. package/dist/twilight/{p-32ea6fff.entry.js → p-21b3877b.entry.js} +1 -1
  279. package/dist/twilight/{p-9897606c.entry.js → p-22135b7c.entry.js} +1 -1
  280. package/dist/twilight/{p-7836ad45.entry.js → p-250262a9.entry.js} +1 -1
  281. package/dist/twilight/{p-23ccf145.entry.js → p-2fc68bfd.entry.js} +1 -1
  282. package/dist/twilight/{p-027b5233.entry.js → p-320d6b04.entry.js} +1 -1
  283. package/dist/twilight/{p-8fbf8044.entry.js → p-377642ea.entry.js} +1 -1
  284. package/dist/twilight/{p-0b10b315.entry.js → p-38418821.entry.js} +1 -1
  285. package/dist/twilight/{p-21f2167e.entry.js → p-3bf37615.entry.js} +1 -1
  286. package/dist/twilight/{p-39325cfc.entry.js → p-3e6f2bf8.entry.js} +1 -1
  287. package/dist/twilight/{p-864959d8.entry.js → p-42150f97.entry.js} +1 -1
  288. package/dist/twilight/{p-a64f2fff.entry.js → p-42e02411.entry.js} +1 -1
  289. package/dist/twilight/{p-3acbfe00.entry.js → p-43c5ec34.entry.js} +1 -1
  290. package/dist/twilight/{p-7495249f.entry.js → p-43ca88d0.entry.js} +1 -1
  291. package/dist/twilight/{p-009048d0.entry.js → p-45035389.entry.js} +1 -1
  292. package/dist/twilight/{p-48f26c20.entry.js → p-46721e68.entry.js} +1 -1
  293. package/dist/twilight/{p-0446f8e3.entry.js → p-4f76e9e2.entry.js} +1 -1
  294. package/dist/twilight/p-4fd73440.entry.js +4 -0
  295. package/dist/twilight/{p-6ea4f06c.entry.js → p-523f9816.entry.js} +1 -1
  296. package/dist/twilight/{p-a4b9f482.entry.js → p-540029a5.entry.js} +1 -1
  297. package/dist/twilight/{p-c273ff37.entry.js → p-5435dbcd.entry.js} +1 -1
  298. package/dist/twilight/{p-23e45bd7.entry.js → p-5f6a5a4b.entry.js} +1 -1
  299. package/dist/twilight/{p-bd1dbd4b.entry.js → p-5fc75260.entry.js} +1 -1
  300. package/dist/twilight/{p-d266e9ad.entry.js → p-612b6d87.entry.js} +1 -1
  301. package/dist/twilight/{p-6fcf17d4.entry.js → p-658de43b.entry.js} +1 -1
  302. package/dist/twilight/{p-31b7fbdc.entry.js → p-6673a16f.entry.js} +1 -1
  303. package/dist/twilight/{p-5b65fffc.entry.js → p-6760ec19.entry.js} +1 -1
  304. package/dist/twilight/{p-a97a0636.entry.js → p-68411931.entry.js} +1 -1
  305. package/dist/twilight/{p-85f185cc.entry.js → p-6a76acd8.entry.js} +1 -1
  306. package/dist/twilight/{p-0f951af1.entry.js → p-7038e1af.entry.js} +1 -1
  307. package/dist/twilight/{p-d35c3f7d.entry.js → p-75f32b91.entry.js} +1 -1
  308. package/dist/twilight/{p-e28f4afc.entry.js → p-7750be26.entry.js} +1 -1
  309. package/dist/twilight/{p-d524d26e.entry.js → p-79676f24.entry.js} +1 -1
  310. package/dist/twilight/{p-36f4e851.entry.js → p-7b4a8237.entry.js} +1 -1
  311. package/dist/twilight/p-7ff0c546.entry.js +4 -0
  312. package/dist/twilight/{p-294f6c8e.entry.js → p-813a40cc.entry.js} +1 -1
  313. package/dist/twilight/{p-a0f0a176.entry.js → p-83ccddd5.entry.js} +1 -1
  314. package/dist/twilight/{p-ddb65e87.entry.js → p-872d6d68.entry.js} +1 -1
  315. package/dist/twilight/{p-1988457b.entry.js → p-9048520b.entry.js} +1 -1
  316. package/dist/twilight/{p-799a0589.entry.js → p-91a1baf1.entry.js} +1 -1
  317. package/dist/twilight/{p-d566b402.entry.js → p-9e6f4ec4.entry.js} +1 -1
  318. package/dist/twilight/{p-PcxCrM8A.js → p-B-LS1xDz.js} +1 -1
  319. package/dist/twilight/p-Bk24FlDL.js +5 -0
  320. package/dist/twilight/{p-Ba21M-o3.js → p-BnmP7F4y.js} +1 -1
  321. package/dist/twilight/{p-4asjbsmx.js → p-BvUmZYa9.js} +1 -1
  322. package/dist/twilight/{p-rKbQj-u0.js → p-CHqh7vb6.js} +1 -1
  323. package/dist/twilight/{p-HZVqfCuE.js → p-CXo-IH2o.js} +1 -1
  324. package/dist/twilight/{p-7foc7ca2.js → p-CcGfWKIx.js} +1 -1
  325. package/dist/twilight/{p-BHXx247M.js → p-Ct18qgaq.js} +1 -1
  326. package/dist/twilight/{p-ClbVN5Nv.js → p-DBjpz_eG.js} +1 -1
  327. package/dist/twilight/{p-BJxBnTgv.js → p-DgRxTSyJ.js} +1 -1
  328. package/dist/twilight/{p-BcMiEHs_.js → p-DvlAEiaH.js} +1 -1
  329. package/dist/twilight/{p-78ff082d.entry.js → p-a057f532.entry.js} +1 -1
  330. package/dist/twilight/{p-a1d43865.entry.js → p-a0f1b867.entry.js} +1 -1
  331. package/dist/twilight/{p-6e45ec66.entry.js → p-a75680ae.entry.js} +1 -1
  332. package/dist/twilight/{p-6ea8b4a8.entry.js → p-ad52dcf7.entry.js} +1 -1
  333. package/dist/twilight/{p-cb47fc80.entry.js → p-b009d316.entry.js} +1 -1
  334. package/dist/twilight/{p-45a13d19.entry.js → p-b1b096d7.entry.js} +1 -1
  335. package/dist/twilight/{p-f407afee.entry.js → p-b1b345d6.entry.js} +1 -1
  336. package/dist/twilight/{p-0de54240.entry.js → p-b2c41e36.entry.js} +1 -1
  337. package/dist/twilight/{p-0746fded.entry.js → p-b37ae926.entry.js} +1 -1
  338. package/dist/twilight/{p-52d1d467.entry.js → p-b5e774db.entry.js} +1 -1
  339. package/dist/twilight/{p-7ddae4df.entry.js → p-b6dc3e12.entry.js} +1 -1
  340. package/dist/twilight/{p-9950bd4f.entry.js → p-b95a6e20.entry.js} +1 -1
  341. package/dist/twilight/{p-655c7f90.entry.js → p-bb152b39.entry.js} +1 -1
  342. package/dist/twilight/{p-d21e8092.entry.js → p-be729ca2.entry.js} +1 -1
  343. package/dist/twilight/{p-788ffe88.entry.js → p-becb6580.entry.js} +1 -1
  344. package/dist/twilight/{p-3ba09a8e.entry.js → p-c1d18b73.entry.js} +1 -1
  345. package/dist/twilight/{p-2667efd6.entry.js → p-c1d815c9.entry.js} +1 -1
  346. package/dist/twilight/{p-a0f2b1b7.entry.js → p-c313bd11.entry.js} +1 -1
  347. package/dist/twilight/{p-3de1d01b.entry.js → p-c3a2696b.entry.js} +1 -1
  348. package/dist/twilight/{p-c62b3757.entry.js → p-c9ebe466.entry.js} +1 -1
  349. package/dist/twilight/{p-0fca7ee0.entry.js → p-cc74d38b.entry.js} +1 -1
  350. package/dist/twilight/{p-34b91d42.entry.js → p-cd432c49.entry.js} +1 -1
  351. package/dist/twilight/{p-94eddc5c.entry.js → p-ce6c709b.entry.js} +1 -1
  352. package/dist/twilight/{p-b98b2cda.entry.js → p-ced74b88.entry.js} +1 -1
  353. package/dist/twilight/{p-8c7614fb.entry.js → p-d601977e.entry.js} +1 -1
  354. package/dist/twilight/{p-1d8c6adc.entry.js → p-d73307c7.entry.js} +1 -1
  355. package/dist/twilight/{p-8bc3a449.entry.js → p-d9ccdd8f.entry.js} +1 -1
  356. package/dist/twilight/{p-945eec73.entry.js → p-dc51993c.entry.js} +1 -1
  357. package/dist/twilight/{p-56ce31f2.entry.js → p-dfd212d5.entry.js} +1 -1
  358. package/dist/twilight/{p-62b8c2cc.entry.js → p-e2aa0e72.entry.js} +1 -1
  359. package/dist/twilight/{p-1a1f04ac.entry.js → p-e58b7ef9.entry.js} +1 -1
  360. package/dist/twilight/p-e7127704.entry.js +4 -0
  361. package/dist/twilight/{p-da05ae96.entry.js → p-e7ca6d06.entry.js} +1 -1
  362. package/dist/twilight/{p-6b767b19.entry.js → p-eaae379c.entry.js} +1 -1
  363. package/dist/twilight/{p-0156dfe8.entry.js → p-ed6ea140.entry.js} +1 -1
  364. package/dist/twilight/{p-53f8972b.entry.js → p-f263bba9.entry.js} +1 -1
  365. package/dist/twilight/p-f3ffe84f.entry.js +4 -0
  366. package/dist/twilight/{p-f757ba41.entry.js → p-f4f7ed38.entry.js} +1 -1
  367. package/dist/twilight/{p-0ccf7bde.entry.js → p-f5c9e513.entry.js} +1 -1
  368. package/dist/twilight/{p-ce0a4802.entry.js → p-f74ccae4.entry.js} +1 -1
  369. package/dist/twilight/{p-45cc68ce.entry.js → p-f7ee4910.entry.js} +1 -1
  370. package/dist/twilight/{p-09300a8d.entry.js → p-f8f9c563.entry.js} +1 -1
  371. package/dist/twilight/{p-74ba0473.entry.js → p-f9f0a5ee.entry.js} +1 -1
  372. package/dist/twilight/{p-a7a64c45.entry.js → p-fbee9e50.entry.js} +1 -1
  373. package/dist/twilight/twilight.esm.js +1 -1
  374. package/dist/types/components/salla-bullet-delivery/salla-bullet-delivery.d.ts +12 -0
  375. package/dist/types/components/salla-conditional-offer/cart-update-window.d.ts +31 -0
  376. package/dist/types/components/salla-conditional-offer/interfaces.d.ts +70 -13
  377. package/dist/types/components/salla-conditional-offer/salla-conditional-offer.d.ts +656 -10
  378. package/dist/types/components/salla-search/search-response.d.ts +1 -0
  379. package/dist/types/shared/cart-free-gifts.d.ts +141 -0
  380. package/dist/types/shared/offer-stages.d.ts +288 -0
  381. package/package.json +5 -5
  382. package/dist/cjs/salla-tooltip.cjs.entry.js +0 -42
  383. package/dist/esm/salla-tooltip.entry.js +0 -40
  384. package/dist/twilight/p-0e206ed7.entry.js +0 -4
  385. package/dist/twilight/p-1a875a94.entry.js +0 -4
  386. package/dist/twilight/p-8f5929a9.entry.js +0 -4
  387. package/dist/twilight/p-917ce4f9.entry.js +0 -4
  388. package/dist/twilight/p-d4f3905f.entry.js +0 -4
  389. package/dist/twilight/p-e47f5ec9.entry.js +0 -4
  390. package/dist/twilight/p-klFskvc_.js +0 -5
@@ -0,0 +1,448 @@
1
+ /*!
2
+ * Crafted with ❤ by Salla
3
+ */
4
+ /**
5
+ * Shared free-gift lookup for the cart.
6
+ *
7
+ * The gift feature is twilight-only: no theme passes `added-by-offer` /
8
+ * `free-gift-quantity` attributes. `salla-conditional-offer` resolves the gift LINES it
9
+ * lists (read-only, as information) and the cart line count from here, never from theme
10
+ * attributes.
11
+ *
12
+ * Data source: `salla.api.cart.details()` — it hits the `api.cart.show`
13
+ * route, which IS in `CartPresenter::$routesWithItems`, so the response
14
+ * carries `data.cart.items[]` with the per-item keys `added_by_offer`
15
+ * (bool), `offer_id` (int|null) and `free_gift_quantity` (int) set by
16
+ * `CartItemTransformer.php`. Those three names are a pinned
17
+ * contract — do not rename them here or on the components that consume them.
18
+ *
19
+ * We deliberately never read these fields off a `cart::updated` payload:
20
+ * that event fires from `latest()` too, and `api.cart.latest` is NOT in
21
+ * `$routesWithItems`, so a `cart::updated` payload legitimately carries no
22
+ * `items` at all. Callers must fetch on mount and call
23
+ * `refreshCartFreeGifts()` on the event — this module never derives from
24
+ * event payloads.
25
+ *
26
+ * The banner asks for the gift lines and the cart line count on the same tick (initial
27
+ * mount, and again on every `cart::updated`). Concurrent callers share a
28
+ * single in-flight promise, mirroring `_pendingOffersRequest` in
29
+ * `twilight/packages/twilight/src/api/cart.js:831-846`.
30
+ */
31
+ const EMPTY_GIFT_ITEMS = Object.freeze([]);
32
+ /**
33
+ * The gift LINES from the last parsed response. `null` is the single "nothing has loaded
34
+ * yet" flag shared with `countCache`, which keeps the two from ever disagreeing about
35
+ * which response they came from.
36
+ */
37
+ let itemsCache = null;
38
+ /**
39
+ * The cart's line count, published in the SAME pass as `itemsCache`. Only meaningful
40
+ * while `itemsCache` is non-null.
41
+ */
42
+ let countCache = null;
43
+ /**
44
+ * Whether what sits in the caches above came from a response we actually PARSED,
45
+ * as opposed to the graceful fallback a rejected lookup publishes.
46
+ *
47
+ * THIS IS THE CACHE'S WARM/COLD FLAG, and it is separate from the values on purpose.
48
+ *
49
+ * A failed lookup still has to hand its in-flight callers something usable, so the
50
+ * `.catch` below publishes `gifts: []` / `count: null`. But `[]` is TRUTHY, so a cache
51
+ * populated that way used to read as WARM: every later `getCartItemCount()` /
52
+ * `getCartFreeGiftItems()` short-circuited on it and returned the FAILURE's values
53
+ * without ever re-asking. One rejected `details()` therefore pinned the count at
54
+ * "unknown" (or, for `CartNotCreated`, at a hard `0`) for the whole page view, and
55
+ * `salla-conditional-offer` — which renders nothing until it is POSITIVELY told the cart
56
+ * holds items — vanished on a populated cart with a live offer. Nothing corrected it:
57
+ * only `refreshCartFreeGifts()` clears the cache, and that runs from `cart::updated`,
58
+ * which a shopper who does not touch the cart never emits.
59
+ *
60
+ * So a failure now leaves the cache COLD: its fallback values are still handed to the
61
+ * callers already waiting, and the next caller re-asks. Concurrent callers still share
62
+ * one request through `pendingRequest`, so the retry costs one lookup per wave, not one
63
+ * per consumer — and the cheapest failure of all, `CartNotCreated`, never reaches the
64
+ * network at all (`details()` rejects before it requests).
65
+ */
66
+ let isAuthoritative = false;
67
+ let pendingRequest = null;
68
+ // De-dupes concurrent `refreshCartFreeGifts()` callers — see that function.
69
+ let pendingRefresh = null;
70
+ // Bumped by `refreshCartFreeGifts()`. A fetch only gets to publish into
71
+ // the caches if the generation it started under is still current when it
72
+ // resolves — otherwise a request that was already in flight when the cart
73
+ // mutated would win the race and cache pre-mutation data as if it were
74
+ // fresh. Mirrors `salla.event.on('cart::updated', () => { Salla.api.cart._pendingOffersRequest = null; })`
75
+ // in `twilight/packages/twilight/src/api/cart.js:81`, which abandons its
76
+ // in-flight promise on cart update so a post-mutation caller gets a fresh
77
+ // request — the same idea, expressed as a generation check instead of
78
+ // nulling the promise (nulling it here would defeat the same-tick dedupe
79
+ // that `refreshCartFreeGifts()` relies on).
80
+ let generation = 0;
81
+ /**
82
+ * The cart identity the caches (and any in-flight request) belong to. `undefined` until
83
+ * the first lookup. See `syncCartIdentity`.
84
+ */
85
+ let cacheIdentity;
86
+ /**
87
+ * The SDK's own name for "this visitor has no cart at all".
88
+ *
89
+ * `salla.api.cart.details()` resolves its id through
90
+ * `getCurrentCartId(false, …, allowCreate = false)`, which returns `null` for a guest with
91
+ * no stored cart id rather than creating one server-side; `details()` then throws this
92
+ * (twilight/src/api/cart.js:192-199). It is a STATEMENT ABOUT THE CART, not a failure to
93
+ * reach the server — no cart means no items — so it is the one rejection that resolves to
94
+ * a hard 0 instead of "unknown".
95
+ *
96
+ * Matched on `name`, never on message text: the wording is not a contract, the name is.
97
+ */
98
+ export const CART_NOT_CREATED = 'CartNotCreated';
99
+ export function isCartNotCreated(error) {
100
+ return error?.name === CART_NOT_CREATED;
101
+ }
102
+ /**
103
+ * The cart id as the SDK knows it right now, synchronously, or `null` when there is none.
104
+ * Same sources `salla.api.cart.getStoredCartId()` reads (the in-memory latest cart, then
105
+ * storage), with a storage fallback for an SDK that predates it.
106
+ */
107
+ function readCartIdentity() {
108
+ try {
109
+ const id = salla.api?.cart?.getStoredCartId
110
+ ? salla.api.cart.getStoredCartId()
111
+ : salla.storage?.get?.('cart.id');
112
+ return id === undefined || id === null || id === '' ? null : String(id);
113
+ }
114
+ catch (_) {
115
+ return null;
116
+ }
117
+ }
118
+ /**
119
+ * Drops everything cached for a previous cart when the cart id has changed.
120
+ *
121
+ * The caches are otherwise only invalidated by `refreshCartFreeGifts()`, i.e. by
122
+ * `cart::updated`. A silent cross-store login writes the merged cart id directly and
123
+ * emits nothing, so without this a mounted banner would keep serving the previous
124
+ * cart's line count and gifts. Bumping `generation` also stops a request that is still
125
+ * in flight for the old cart from publishing, and the in-flight slot is released so the
126
+ * caller asks again under the new identity.
127
+ */
128
+ function syncCartIdentity() {
129
+ const current = readCartIdentity();
130
+ if (cacheIdentity !== undefined && cacheIdentity !== current) {
131
+ generation++;
132
+ itemsCache = null;
133
+ countCache = null;
134
+ isAuthoritative = false;
135
+ pendingRequest = null;
136
+ }
137
+ cacheIdentity = current;
138
+ }
139
+ function toNumber(v) {
140
+ if (v === null || v === undefined)
141
+ return 0;
142
+ if (typeof v === 'number')
143
+ return isFinite(v) ? v : 0;
144
+ if (typeof v === 'string')
145
+ return parseFloat(v) || 0;
146
+ return 0;
147
+ }
148
+ function toNullableNumber(v) {
149
+ if (v === null || v === undefined || v === '')
150
+ return null;
151
+ const n = toNumber(v);
152
+ return isFinite(n) ? n : null;
153
+ }
154
+ /**
155
+ * `details()`'s envelope nests items under `data.cart.items`.
156
+ *
157
+ * Returns `null` — NOT `[]` — when the response carried no items array at all. The two
158
+ * are different facts: `[]` is the server saying "this cart is empty", `null` is "this
159
+ * response cannot answer the question". `getCartItemCount()` is built on that
160
+ * distinction; see `extractCartCount`.
161
+ */
162
+ function findItemsArray(res) {
163
+ const items = res?.data?.cart?.items ?? res?.data?.items ?? res?.cart?.items ?? res?.items;
164
+ return Array.isArray(items) ? items : null;
165
+ }
166
+ /**
167
+ * How many LINES the cart holds, or `null` when the response could not say.
168
+ *
169
+ * `null` is load-bearing and must never be collapsed into `0`: a consumer that hides
170
+ * itself on an empty cart has to be able to tell "the cart is empty" apart from "the
171
+ * cart has not loaded yet", or it flickers (or vanishes) on a populated cart while the
172
+ * first `details()` request is still in flight.
173
+ *
174
+ * The items array is the PRIMARY signal because `api.cart.show` is in
175
+ * `CartPresenter::$routesWithItems` and therefore always emits it. `cart.count` is only
176
+ * consulted when the array is missing entirely, and it is a QUANTITY total rather than a
177
+ * line count — close enough for the only question anyone asks of it (is this zero?), and
178
+ * never reached on a well-formed response.
179
+ */
180
+ function extractCartCount(res, items) {
181
+ if (items !== null)
182
+ return items.length;
183
+ const raw = res?.data?.cart?.count ?? res?.data?.count ?? res?.cart?.count ?? res?.count;
184
+ const count = toNullableNumber(raw);
185
+ return count !== null && count >= 0 ? count : null;
186
+ }
187
+ /**
188
+ * The price a gift line would have cost.
189
+ *
190
+ * Tries the keys in the order `CartItemTransformer` fills them, and accepts both the
191
+ * `{amount, currency}` shape a serialized `Money` produces and a bare number/string.
192
+ * A zero is treated as "nothing usable": the gift's own charged price IS zero, so a 0
193
+ * here means we read the wrong key, and rendering a struck-through "0" would be worse
194
+ * than rendering no price at all.
195
+ */
196
+ function extractOriginalPrice(item) {
197
+ for (const raw of [item?.original_price, item?.product_price, item?.price]) {
198
+ const value = raw && typeof raw === 'object' ? raw.amount : raw;
199
+ const amount = toNullableNumber(value);
200
+ if (amount !== null && amount > 0)
201
+ return amount;
202
+ }
203
+ return null;
204
+ }
205
+ /**
206
+ * How many units of this line the offer granted, or `0` when the answer is "none".
207
+ *
208
+ * The two absent-ish shapes are NOT the same fact and must not collapse:
209
+ *
210
+ * • the key is MISSING (`undefined`/`null`/`''`) — an older payload, or a
211
+ * transformer that did not fill it. Nothing contradicts "the whole line is
212
+ * the gift" for an unknown line quantity, so fall back to the line.
213
+ * • the key is PRESENT and `0` — the server stating there are no free units on
214
+ * this line. That is an answer, and it must be believed.
215
+ */
216
+ function resolveFreeQuantity(item, lineQuantity) {
217
+ const raw = item?.free_gift_quantity;
218
+ const stated = raw !== undefined && raw !== null && raw !== '';
219
+ if (stated)
220
+ return toNumber(raw);
221
+ return lineQuantity > 0 ? lineQuantity : 0;
222
+ }
223
+ /**
224
+ * The gift LINES, in cart order. Only lines the server flagged `added_by_offer` with at
225
+ * least one granted free unit qualify.
226
+ */
227
+ function buildGiftItems(items) {
228
+ const gifts = [];
229
+ for (const item of items) {
230
+ if (item?.id === undefined || item?.id === null)
231
+ continue;
232
+ if (!item.added_by_offer)
233
+ continue;
234
+ const lineQuantity = toNumber(item.quantity);
235
+ const freeQuantity = resolveFreeQuantity(item, lineQuantity);
236
+ // No free units ⇒ not a gift LINE, whatever the flag says. Listing it anyway
237
+ // would put a line the shopper PAID for in full into the gifts card, labelled
238
+ // "free" — the card states what the offer granted, so a non-positive free count
239
+ // means it granted nothing here.
240
+ if (freeQuantity <= 0)
241
+ continue;
242
+ gifts.push({
243
+ id: String(item.id),
244
+ offerId: toNullableNumber(item.offer_id),
245
+ quantity: freeQuantity,
246
+ freeQuantity,
247
+ lineQuantity,
248
+ name: String(item.product_name ?? item.name ?? ''),
249
+ imageUrl: String(item.product_image?.url ?? item.product_image ?? item.image?.url ?? ''),
250
+ imageAlt: String(item.product_image?.alt ?? item.image?.alt ?? item.product_name ?? ''),
251
+ url: String(item.url ?? ''),
252
+ originalPrice: extractOriginalPrice(item),
253
+ });
254
+ }
255
+ return gifts;
256
+ }
257
+ function fetchAndCache() {
258
+ if (pendingRequest)
259
+ return pendingRequest;
260
+ // Snapshot the generation this request started under — see the
261
+ // `generation` doc comment above for why.
262
+ const startedAtGeneration = generation;
263
+ const request = Promise.resolve()
264
+ .then(() => salla.api?.cart?.details?.())
265
+ .then((res) => {
266
+ const found = findItemsArray(res);
267
+ const items = found ?? [];
268
+ // Both projections come out of the SAME response, in one pass.
269
+ return {
270
+ gifts: buildGiftItems(items),
271
+ count: extractCartCount(res, found),
272
+ // The response was reached and parsed — these values are the cart, so the cache
273
+ // they land in may be short-circuited on. See `isAuthoritative`.
274
+ ok: true,
275
+ };
276
+ })
277
+ // A failed fetch shouldn't throw into a badge/lock render path — fail
278
+ // closed to "no gift info" (or whatever we already knew) rather than
279
+ // breaking the rest of the cart UI.
280
+ //
281
+ // The count splits the failures in two. `CartNotCreated` is the SDK
282
+ // stating there is no cart, which is a definitive EMPTY and resolves to
283
+ // 0 — without it, the ordinary first-time guest sits on "unknown"
284
+ // forever, because no request will ever succeed and no `cart::updated`
285
+ // is coming to correct it. Everything else (network, 5xx) says nothing
286
+ // about the contents and still fails OPEN to `null`, so a flaky
287
+ // connection can never claim the cart is empty.
288
+ .catch((error) => ({
289
+ gifts: itemsCache ?? [],
290
+ count: isCartNotCreated(error) ? 0 : countCache,
291
+ /**
292
+ * NOT authoritative, `CartNotCreated` included.
293
+ *
294
+ * `0` stays the right ANSWER for it — no cart means no items, and a first-time
295
+ * guest must be told "empty" rather than sitting on "unknown" — but it is no longer
296
+ * a FINAL one. The very first cart lookup on a page is fired before
297
+ * `salla.onReady()` has settled, so it can be answered by an SDK that has not yet
298
+ * installed the cart id (or, earlier still, not yet installed `salla.config`, whose
299
+ * `isGuest()` the id resolution calls unguarded). Both spell a cart that is merely
300
+ * unknown as one that is empty. Leaving the cache cold lets the next lookup — from
301
+ * a bootstrapped SDK — say so, and on a cart that genuinely is empty the retry
302
+ * rejects again without a request.
303
+ */
304
+ ok: false,
305
+ }))
306
+ .then(({ gifts, count, ok }) => {
307
+ // Only publish if nothing invalidated us while we were in flight.
308
+ // Otherwise this is pre-mutation data racing a newer, authoritative
309
+ // request — publishing it would make the lookups short-circuit on stale
310
+ // data until the NEXT mutation happens to come along.
311
+ if (startedAtGeneration === generation) {
312
+ itemsCache = gifts;
313
+ countCache = count;
314
+ isAuthoritative = ok;
315
+ }
316
+ })
317
+ .finally(() => {
318
+ // Only release OUR slot: an identity change may already have replaced it.
319
+ if (pendingRequest === request)
320
+ pendingRequest = null;
321
+ });
322
+ pendingRequest = request;
323
+ return request;
324
+ }
325
+ /**
326
+ * Every free-gift LINE currently in the cart, in cart order.
327
+ *
328
+ * Shares the cache, the in-flight promise and the generation guard with
329
+ * `getCartItemCount()` — the banner asking for both still issues exactly ONE
330
+ * `details()` request per cart mutation.
331
+ */
332
+ export function getCartFreeGiftItems() {
333
+ syncCartIdentity();
334
+ // `isAuthoritative`, not `itemsCache` alone — a failed lookup leaves `itemsCache` a
335
+ // truthy `[]`, which is not an answer. See `isAuthoritative`.
336
+ if (isAuthoritative && itemsCache) {
337
+ return Promise.resolve(itemsCache);
338
+ }
339
+ return fetchAndCache().then(() => itemsCache ?? EMPTY_GIFT_ITEMS);
340
+ }
341
+ /**
342
+ * Whether the cached cart picture came from a response that was actually reached and
343
+ * parsed, rather than from the fallback a rejected lookup publishes.
344
+ *
345
+ * For the ONE consumer that has to tell "the cart is empty" apart from "the lookup could
346
+ * not say, and guessed empty": `salla-conditional-offer` stops before it even asks for the
347
+ * offer when the count is `0`, which is unrecoverable if that `0` was a guess. Everything
348
+ * else should just read the values — they are already the best available answer.
349
+ *
350
+ * Synchronous and side-effect free: it reports on what is cached NOW, so a caller that
351
+ * wants a fresh answer must still await one of the lookups above.
352
+ */
353
+ export function hasAuthoritativeCartData() {
354
+ syncCartIdentity();
355
+ return isAuthoritative;
356
+ }
357
+ /**
358
+ * How many LINES the cart holds — `null` while that is genuinely UNKNOWN (nothing has
359
+ * loaded yet, or the lookup failed), never `0` for either.
360
+ *
361
+ * The distinction is the whole point: `salla-conditional-offer` hides its banner on an
362
+ * empty cart, and a `0` that really meant "still loading" would make the banner flicker
363
+ * or disappear on a populated one. Shares the cache, the in-flight promise and the
364
+ * generation guard with `getCartFreeGiftItems()`, so asking for the count costs no
365
+ * request of its own.
366
+ */
367
+ export function getCartItemCount() {
368
+ syncCartIdentity();
369
+ // THE ONE THAT HID THE BANNER. `itemsCache` alone read a failed lookup's `[]` as a warm
370
+ // cache and handed back its `null`/`0` count for ever — see `isAuthoritative`.
371
+ if (isAuthoritative && itemsCache) {
372
+ return Promise.resolve(countCache);
373
+ }
374
+ return fetchAndCache().then(() => countCache);
375
+ }
376
+ /**
377
+ * Invalidates the cache and re-fetches — call this from a `cart::updated`
378
+ * handler, ignoring the event payload entirely.
379
+ *
380
+ * Bumps `generation` first so that any request already in flight (started
381
+ * before this invalidation) can no longer publish into `cache` when it
382
+ * settles — mirrors `salla.event.on('cart::updated', () => { Salla.api.cart._pendingOffersRequest = null; })`
383
+ * in `twilight/packages/twilight/src/api/cart.js:81`, which abandons its
384
+ * own in-flight promise on cart update for the same reason.
385
+ *
386
+ * Then chains a fresh fetch BEHIND whatever was already in flight (rather
387
+ * than firing a second request in parallel): once that prior request
388
+ * settles — its result now guaranteed to be discarded by the generation
389
+ * check above — `fetchAndCache()` starts a request under the new
390
+ * generation, which is free to publish.
391
+ *
392
+ * `pendingRefresh` de-dupes concurrent callers of THIS function the same
393
+ * way `pendingRequest` de-dupes concurrent fetches: N components all
394
+ * invalidating within the same synchronous `cart::updated` dispatch share
395
+ * one refresh chain and therefore, ultimately, one fresh HTTP request.
396
+ *
397
+ * That sharing window is deliberately NARROW — it closes the instant the
398
+ * fresh request goes out, NOT when it comes back. Holding it open for the
399
+ * whole round trip silently coalesced two DIFFERENT cart mutations into one
400
+ * refresh: the second `cart::updated` got handed the first one's promise, so
401
+ * it never bumped `generation` and never issued a request, and the response
402
+ * that eventually published had been asked for BEFORE the second mutation
403
+ * happened. The cart then sat on pre-mutation gift data — a merged line
404
+ * showing yesterday's paid quantity beside yesterday's corrected total —
405
+ * until some later mutation happened to come along. A shopper clicking "+"
406
+ * twice inside one round trip is all it took.
407
+ */
408
+ export function refreshCartFreeGifts() {
409
+ if (pendingRefresh)
410
+ return pendingRefresh;
411
+ generation++;
412
+ itemsCache = null;
413
+ countCache = null;
414
+ isAuthoritative = false;
415
+ // This invalidation already covers whatever id the cart has now.
416
+ cacheIdentity = readCartIdentity();
417
+ const priorRequest = pendingRequest ?? Promise.resolve();
418
+ const startFreshRequest = () => {
419
+ // Sharing ends here: everything that invalidated up to this point is
420
+ // described by the request about to go out, and everything after it is not.
421
+ if (pendingRefresh === refresh)
422
+ pendingRefresh = null;
423
+ return fetchAndCache();
424
+ };
425
+ // `.then(f, f)` — a prior request that REJECTED still has to be waited out
426
+ // before the replacement is issued, or the two overlap.
427
+ const refresh = priorRequest
428
+ .then(startFreshRequest, startFreshRequest)
429
+ .finally(() => {
430
+ // Only ever clear OUR slot. A later refresh may already own it (it can
431
+ // only have taken it after `startFreshRequest` released it), and nulling
432
+ // that one would un-dedupe its same-tick callers.
433
+ if (pendingRefresh === refresh)
434
+ pendingRefresh = null;
435
+ });
436
+ pendingRefresh = refresh;
437
+ return refresh;
438
+ }
439
+ /** @internal exported for tests only — resets module-level state between specs. */
440
+ export function __resetCartFreeGiftsForTests() {
441
+ itemsCache = null;
442
+ countCache = null;
443
+ isAuthoritative = false;
444
+ pendingRequest = null;
445
+ pendingRefresh = null;
446
+ generation = 0;
447
+ cacheIdentity = undefined;
448
+ }