@mohasinac/appkit 4.53.0 → 4.53.2

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 (520) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/client/scaffolds/AppShell.js +1 -1
  3. package/dist/_internal/client/theme/ThemeProvider.js +15 -1
  4. package/dist/_internal/server/features/auctions/og.d.ts +3 -1
  5. package/dist/_internal/server/features/auctions/og.js +3 -1
  6. package/dist/_internal/server/features/bundles/actions.js +1 -1
  7. package/dist/_internal/server/features/business-documents/download.d.ts +41 -0
  8. package/dist/_internal/server/features/business-documents/download.js +91 -0
  9. package/dist/_internal/server/features/categories/adapters.d.ts +1 -1
  10. package/dist/_internal/server/features/categories/adapters.js +10 -0
  11. package/dist/_internal/server/features/categories/price-guidance-resolve.d.ts +66 -0
  12. package/dist/_internal/server/features/categories/price-guidance-resolve.js +138 -0
  13. package/dist/_internal/server/features/checkout/actions.d.ts +14 -0
  14. package/dist/_internal/server/features/checkout/actions.js +106 -12
  15. package/dist/_internal/server/features/checkout/bundle-expansion.js +17 -0
  16. package/dist/_internal/server/features/checkout/data.d.ts +22 -0
  17. package/dist/_internal/server/features/checkout/group-rule-revalidation.d.ts +34 -0
  18. package/dist/_internal/server/features/checkout/group-rule-revalidation.js +124 -0
  19. package/dist/_internal/server/features/checkout/supplier-identity.d.ts +55 -0
  20. package/dist/_internal/server/features/checkout/supplier-identity.js +81 -0
  21. package/dist/_internal/server/features/classified/metadata.js +1 -1
  22. package/dist/_internal/server/features/classified/og.d.ts +3 -1
  23. package/dist/_internal/server/features/classified/og.js +3 -1
  24. package/dist/_internal/server/features/digital-code/metadata.js +1 -1
  25. package/dist/_internal/server/features/digital-code/og.d.ts +3 -1
  26. package/dist/_internal/server/features/digital-code/og.js +3 -1
  27. package/dist/_internal/server/features/digital-code/pool.d.ts +19 -4
  28. package/dist/_internal/server/features/digital-code/pool.js +19 -4
  29. package/dist/_internal/server/features/live/metadata.js +1 -1
  30. package/dist/_internal/server/features/live/og.d.ts +3 -1
  31. package/dist/_internal/server/features/live/og.js +3 -1
  32. package/dist/_internal/server/features/messages/actions.js +1 -1
  33. package/dist/_internal/server/features/parts/og.js +1 -1
  34. package/dist/_internal/server/features/pre-orders/og.d.ts +3 -1
  35. package/dist/_internal/server/features/pre-orders/og.js +3 -1
  36. package/dist/_internal/server/features/price-index/market.d.ts +57 -0
  37. package/dist/_internal/server/features/price-index/market.js +120 -0
  38. package/dist/_internal/server/features/price-index/search.d.ts +18 -0
  39. package/dist/_internal/server/features/price-index/search.js +47 -7
  40. package/dist/_internal/server/features/prize-draws/og.d.ts +3 -1
  41. package/dist/_internal/server/features/prize-draws/og.js +3 -1
  42. package/dist/_internal/server/features/products/group-members.d.ts +26 -0
  43. package/dist/_internal/server/features/products/group-members.js +7 -1
  44. package/dist/_internal/server/features/products/list-public.d.ts +12 -0
  45. package/dist/_internal/server/features/products/list-public.js +10 -1
  46. package/dist/_internal/server/features/products/listing-tab-counts.d.ts +25 -1
  47. package/dist/_internal/server/features/products/listing-tab-counts.js +128 -10
  48. package/dist/_internal/server/features/products/og.d.ts +3 -1
  49. package/dist/_internal/server/features/products/og.js +3 -1
  50. package/dist/_internal/server/features/search/scoped-sources.d.ts +26 -0
  51. package/dist/_internal/server/features/search/scoped-sources.js +252 -0
  52. package/dist/_internal/server/features/search/search-everything.d.ts +57 -0
  53. package/dist/_internal/server/features/search/search-everything.js +70 -20
  54. package/dist/_internal/server/features/seo/sitemap.js +6 -0
  55. package/dist/_internal/server/features/shared/listing-adapters.js +4 -2
  56. package/dist/_internal/server/features/sticker-trace/adapters.d.ts +11 -0
  57. package/dist/_internal/server/features/sticker-trace/adapters.js +1 -0
  58. package/dist/_internal/server/features/sticker-trace/promote-to-listings.d.ts +26 -0
  59. package/dist/_internal/server/features/sticker-trace/promote-to-listings.js +264 -0
  60. package/dist/_internal/server/features/sticker-trace/sheet-actions.d.ts +3 -1
  61. package/dist/_internal/server/features/sticker-trace/sheet-actions.js +32 -0
  62. package/dist/_internal/server/features/sticker-trace/storage.d.ts +20 -1
  63. package/dist/_internal/server/features/sticker-trace/storage.js +20 -1
  64. package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
  65. package/dist/_internal/server/features/stores/adapters.js +16 -0
  66. package/dist/_internal/server/features/stores/index.d.ts +1 -0
  67. package/dist/_internal/server/features/stores/index.js +1 -0
  68. package/dist/_internal/server/features/stores/tabs.d.ts +30 -0
  69. package/dist/_internal/server/features/stores/tabs.js +124 -0
  70. package/dist/_internal/shared/checkout/rules/_defaults.js +0 -2
  71. package/dist/_internal/shared/checkout/rules/_limits.d.ts +14 -1
  72. package/dist/_internal/shared/checkout/rules/_limits.js +14 -1
  73. package/dist/_internal/shared/checkout/rules/_registry.js +2 -0
  74. package/dist/_internal/shared/checkout/rules/auction.rule.js +9 -1
  75. package/dist/_internal/shared/checkout/rules/bundle.rule.js +13 -0
  76. package/dist/_internal/shared/checkout/rules/index.d.ts +2 -0
  77. package/dist/_internal/shared/checkout/rules/index.js +9 -0
  78. package/dist/_internal/shared/checkout/rules/offer.rule.js +0 -1
  79. package/dist/_internal/shared/checkout/rules/payment-gate.d.ts +26 -0
  80. package/dist/_internal/shared/checkout/rules/payment-gate.js +133 -0
  81. package/dist/_internal/shared/checkout/rules/prize-draw.rule.js +29 -8
  82. package/dist/_internal/shared/checkout/rules/spares.rule.d.ts +16 -0
  83. package/dist/_internal/shared/checkout/rules/spares.rule.js +5 -0
  84. package/dist/_internal/shared/checkout/rules/types.d.ts +26 -2
  85. package/dist/_internal/shared/config/schema.d.ts +122 -0
  86. package/dist/_internal/shared/features/cart/schema.js +3 -2
  87. package/dist/_internal/shared/features/products/group-rules.d.ts +75 -0
  88. package/dist/_internal/shared/features/products/group-rules.js +142 -0
  89. package/dist/_internal/shared/listing-types/_registry.d.ts +24 -0
  90. package/dist/_internal/shared/listing-types/_registry.js +39 -1
  91. package/dist/_internal/shared/listing-types/art/config.d.ts +1 -0
  92. package/dist/_internal/shared/listing-types/art/config.js +2 -0
  93. package/dist/_internal/shared/listing-types/auction/config.d.ts +1 -0
  94. package/dist/_internal/shared/listing-types/auction/config.js +2 -0
  95. package/dist/_internal/shared/listing-types/availability.js +48 -0
  96. package/dist/_internal/shared/listing-types/capabilities.js +19 -0
  97. package/dist/_internal/shared/listing-types/classified/config.d.ts +1 -0
  98. package/dist/_internal/shared/listing-types/classified/config.js +2 -0
  99. package/dist/_internal/shared/listing-types/digital-code/config.d.ts +1 -0
  100. package/dist/_internal/shared/listing-types/digital-code/config.js +2 -0
  101. package/dist/_internal/shared/listing-types/feature-flags.js +1 -0
  102. package/dist/_internal/shared/listing-types/live/config.d.ts +1 -0
  103. package/dist/_internal/shared/listing-types/live/config.js +2 -0
  104. package/dist/_internal/shared/listing-types/pre-order/config.d.ts +1 -0
  105. package/dist/_internal/shared/listing-types/pre-order/config.js +2 -0
  106. package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +1 -0
  107. package/dist/_internal/shared/listing-types/prize-draw/config.js +2 -0
  108. package/dist/_internal/shared/listing-types/spares/config.d.ts +74 -0
  109. package/dist/_internal/shared/listing-types/spares/config.js +60 -0
  110. package/dist/_internal/shared/listing-types/spares/ctas.d.ts +1 -0
  111. package/dist/_internal/shared/listing-types/spares/ctas.js +4 -0
  112. package/dist/_internal/shared/listing-types/spares/og.js +1 -0
  113. package/dist/_internal/shared/listing-types/spares/schema.d.ts +2 -0
  114. package/dist/_internal/shared/listing-types/spares/schema.js +3 -0
  115. package/dist/_internal/shared/listing-types/spares/seed-factory.d.ts +1 -0
  116. package/dist/_internal/shared/listing-types/spares/seed-factory.js +1 -0
  117. package/dist/_internal/shared/listing-types/standard/config.d.ts +1 -0
  118. package/dist/_internal/shared/listing-types/standard/config.js +2 -0
  119. package/dist/_internal/shared/listing-types/stickers/config.d.ts +1 -0
  120. package/dist/_internal/shared/listing-types/stickers/config.js +2 -0
  121. package/dist/_internal/shared/search/match.d.ts +11 -0
  122. package/dist/_internal/shared/search/match.js +30 -3
  123. package/dist/_internal/shared/search/to-groups.d.ts +52 -0
  124. package/dist/_internal/shared/search/to-groups.js +141 -0
  125. package/dist/client.d.ts +8 -2
  126. package/dist/client.js +4 -1
  127. package/dist/constants/api-endpoints.d.ts +46 -15
  128. package/dist/constants/api-endpoints.js +35 -5
  129. package/dist/constants/cache-invalidation.js +3 -1
  130. package/dist/constants/field-names.d.ts +6 -0
  131. package/dist/constants/field-names.js +6 -0
  132. package/dist/features/about/components/PolicyPageView.d.ts +22 -2
  133. package/dist/features/about/components/PolicyPageView.js +14 -2
  134. package/dist/features/about/components/PublicProfileView.js +2 -1
  135. package/dist/features/about/components/ShippingPolicyView.js +42 -2
  136. package/dist/features/account/components/NotificationBell.js +1 -1
  137. package/dist/features/account/hooks/useProfileStats.js +5 -1
  138. package/dist/features/addresses/repository/addresses.repository.d.ts +16 -0
  139. package/dist/features/addresses/repository/addresses.repository.js +21 -0
  140. package/dist/features/admin/actions/admin-actions.js +16 -1
  141. package/dist/features/admin/components/AdminCategoryEditorView.js +19 -2
  142. package/dist/features/admin/components/AdminNavigationView.js +1 -1
  143. package/dist/features/admin/components/AdminPageHeader.js +1 -1
  144. package/dist/features/admin/components/AdminProductEditorView.js +17 -2
  145. package/dist/features/admin/components/AdminSidebar.js +1 -1
  146. package/dist/features/admin/components/AdminSiteSettingsView.js +39 -8
  147. package/dist/features/admin/components/AdminSparesView.d.ts +12 -0
  148. package/dist/features/admin/components/AdminSparesView.js +30 -0
  149. package/dist/features/admin/components/AdminUsersView.js +9 -7
  150. package/dist/features/admin/components/index.d.ts +2 -0
  151. package/dist/features/admin/components/index.js +1 -0
  152. package/dist/features/admin/components/sections/adminSectionsTypes.js +7 -0
  153. package/dist/features/admin/constants/filter-tabs.d.ts +3 -0
  154. package/dist/features/admin/constants/filter-tabs.js +1 -0
  155. package/dist/features/admin/schemas/firestore.d.ts +26 -2
  156. package/dist/features/auctions/components/AuctionDetailPageView.js +12 -18
  157. package/dist/features/auth/repository/user.repository.d.ts +13 -0
  158. package/dist/features/auth/repository/user.repository.js +28 -0
  159. package/dist/features/auth/schemas/firestore.d.ts +2 -0
  160. package/dist/features/auth/schemas/firestore.js +2 -0
  161. package/dist/features/business-documents/repository/business-documents.repository.d.ts +58 -0
  162. package/dist/features/business-documents/repository/business-documents.repository.js +54 -0
  163. package/dist/features/business-documents/schemas/firestore.d.ts +105 -0
  164. package/dist/features/business-documents/schemas/firestore.js +75 -0
  165. package/dist/features/cart/actions/cart-actions.js +83 -6
  166. package/dist/features/cart/repository/cart.repository.js +32 -4
  167. package/dist/features/cart/schemas/firestore.d.ts +20 -0
  168. package/dist/features/cart/schemas/index.d.ts +2 -2
  169. package/dist/features/catalogue/components/PublicCatalogueItemDetailView.js +1 -1
  170. package/dist/features/categories/api/[id]/price-guidance/route.d.ts +42 -0
  171. package/dist/features/categories/api/[id]/price-guidance/route.js +90 -0
  172. package/dist/features/categories/components/BrandDetailPageView.js +32 -8
  173. package/dist/features/categories/components/BrandDetailTabs.d.ts +7 -1
  174. package/dist/features/categories/components/BrandDetailTabs.js +28 -3
  175. package/dist/features/categories/components/BundleCollage.js +3 -2
  176. package/dist/features/categories/components/BundleItemsPicker.js +3 -4
  177. package/dist/features/categories/components/CategoriesIndexListing.js +23 -8
  178. package/dist/features/categories/components/CategoryDetailPageView.js +157 -20
  179. package/dist/features/categories/components/CategoryDetailTabs.d.ts +7 -1
  180. package/dist/features/categories/components/CategoryDetailTabs.js +41 -7
  181. package/dist/features/categories/components/CategoryGrid.js +10 -1
  182. package/dist/features/categories/components/PriceGuidanceHint.d.ts +29 -0
  183. package/dist/features/categories/components/PriceGuidanceHint.js +61 -0
  184. package/dist/features/categories/components/SubcategoryCardRow.d.ts +27 -0
  185. package/dist/features/categories/components/SubcategoryCardRow.js +98 -0
  186. package/dist/features/categories/constants/category-tabs.d.ts +20 -25
  187. package/dist/features/categories/constants/category-tabs.js +22 -39
  188. package/dist/features/categories/repository/categories.repository.d.ts +12 -0
  189. package/dist/features/categories/repository/categories.repository.js +20 -0
  190. package/dist/features/categories/schemas/firestore.d.ts +22 -1
  191. package/dist/features/categories/schemas/firestore.js +10 -0
  192. package/dist/features/categories/schemas/index.d.ts +6 -0
  193. package/dist/features/categories/schemas/index.js +2 -0
  194. package/dist/features/categories/types/index.d.ts +8 -0
  195. package/dist/features/categories/utils/taxonomy-visual.d.ts +16 -3
  196. package/dist/features/categories/utils/taxonomy-visual.js +26 -10
  197. package/dist/features/classified/components/ClassifiedDetailPageView.js +14 -10
  198. package/dist/features/contact/components/ContactPageView.d.ts +33 -1
  199. package/dist/features/contact/components/ContactPageView.js +58 -28
  200. package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +14 -10
  201. package/dist/features/grouped/components/GroupedListingDetailView.js +9 -1
  202. package/dist/features/grouped/schemas/firestore.d.ts +5 -0
  203. package/dist/features/homepage/actions/homepage-section-actions.d.ts +3 -3
  204. package/dist/features/homepage/components/CollectionCardsSection.d.ts +28 -6
  205. package/dist/features/homepage/components/CollectionCardsSection.js +43 -8
  206. package/dist/features/homepage/components/HowItWorksSection.d.ts +23 -6
  207. package/dist/features/homepage/components/HowItWorksSection.js +4 -4
  208. package/dist/features/homepage/components/PromoGrid.d.ts +31 -2
  209. package/dist/features/homepage/components/PromoGrid.js +12 -3
  210. package/dist/features/homepage/components/TestimonialsCarousel.d.ts +34 -2
  211. package/dist/features/homepage/components/TestimonialsCarousel.js +1 -1
  212. package/dist/features/homepage/components/index.d.ts +0 -20
  213. package/dist/features/homepage/components/index.js +0 -11
  214. package/dist/features/homepage/index.d.ts +0 -2
  215. package/dist/features/homepage/index.js +0 -2
  216. package/dist/features/homepage/lib/section-renderer.d.ts +9 -0
  217. package/dist/features/homepage/lib/section-renderer.js +202 -58
  218. package/dist/features/homepage/repository/homepage-sections.repository.d.ts +35 -3
  219. package/dist/features/homepage/repository/homepage-sections.repository.js +58 -8
  220. package/dist/features/homepage/schemas/firestore.d.ts +106 -3
  221. package/dist/features/homepage/schemas/firestore.js +32 -2
  222. package/dist/features/homepage/schemas/index.d.ts +36 -7
  223. package/dist/features/homepage/schemas/index.js +39 -27
  224. package/dist/features/homepage/server.d.ts +0 -3
  225. package/dist/features/homepage/server.js +0 -3
  226. package/dist/features/homepage/types/index.d.ts +0 -47
  227. package/dist/features/homepage/types/index.js +49 -0
  228. package/dist/features/layout/AppLayoutShell.js +1 -1
  229. package/dist/features/layout/BottomNavItem.js +1 -1
  230. package/dist/features/layout/BottomSheet.js +1 -1
  231. package/dist/features/layout/TitleBarLayout.js +1 -1
  232. package/dist/features/live/components/LiveItemDetailPageView.js +14 -10
  233. package/dist/features/media/hooks/useMedia.js +17 -0
  234. package/dist/features/media/repository/media-assets.repository.d.ts +46 -0
  235. package/dist/features/media/repository/media-assets.repository.js +31 -0
  236. package/dist/features/media/trust-signals.d.ts +85 -0
  237. package/dist/features/media/trust-signals.js +126 -0
  238. package/dist/features/parts/components/PartDetailPageView.d.ts +14 -1
  239. package/dist/features/parts/components/PartDetailPageView.js +2 -2
  240. package/dist/features/parts/components/PartsIndexPageView.d.ts +11 -2
  241. package/dist/features/parts/components/PartsIndexPageView.js +2 -2
  242. package/dist/features/parts/components/PartsSearchBox.d.ts +9 -0
  243. package/dist/features/parts/components/PartsSearchBox.js +57 -0
  244. package/dist/features/parts/schemas/firestore.d.ts +149 -3
  245. package/dist/features/parts/schemas/firestore.js +184 -0
  246. package/dist/features/payments/repository/payout.repository.d.ts +15 -0
  247. package/dist/features/payments/repository/payout.repository.js +21 -0
  248. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +12 -18
  249. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +1 -1
  250. package/dist/features/price-index/components/PriceIndexToolView.d.ts +24 -1
  251. package/dist/features/price-index/components/PriceIndexToolView.js +299 -17
  252. package/dist/features/price-index/lookup-target.d.ts +48 -0
  253. package/dist/features/price-index/lookup-target.js +107 -0
  254. package/dist/features/products/components/AvailabilityScopeHint.d.ts +22 -0
  255. package/dist/features/products/components/AvailabilityScopeHint.js +27 -0
  256. package/dist/features/products/components/CompareOverlay.d.ts +20 -2
  257. package/dist/features/products/components/CompareOverlay.js +23 -3
  258. package/dist/features/products/components/CustomSectionTabContent.d.ts +31 -0
  259. package/dist/features/products/components/CustomSectionTabContent.js +29 -3
  260. package/dist/features/products/components/GroupMemberPicker.d.ts +19 -1
  261. package/dist/features/products/components/GroupMemberPicker.js +46 -8
  262. package/dist/features/products/components/GroupSettingsPanel.d.ts +11 -1
  263. package/dist/features/products/components/GroupSettingsPanel.js +92 -3
  264. package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
  265. package/dist/features/products/components/PrizeDrawEntryActions.d.ts +15 -1
  266. package/dist/features/products/components/PrizeDrawEntryActions.js +70 -8
  267. package/dist/features/products/components/ProductDetailPageView.js +81 -17
  268. package/dist/features/products/components/ProductForm.d.ts +13 -1
  269. package/dist/features/products/components/ProductForm.js +16 -2
  270. package/dist/features/products/components/ProductsIndexListing.d.ts +16 -1
  271. package/dist/features/products/components/ProductsIndexListing.js +3 -3
  272. package/dist/features/products/components/ShowGroupSection.js +15 -2
  273. package/dist/features/products/components/SparesListView.d.ts +7 -0
  274. package/dist/features/products/components/SparesListView.js +39 -0
  275. package/dist/features/products/components/SublistingCarouselSection.js +2 -1
  276. package/dist/features/products/constants/action-defs.d.ts +1 -0
  277. package/dist/features/products/constants/action-defs.js +1 -0
  278. package/dist/features/products/repository/products.repository.d.ts +99 -0
  279. package/dist/features/products/repository/products.repository.js +240 -18
  280. package/dist/features/products/schemas/firestore.d.ts +97 -4
  281. package/dist/features/products/schemas/firestore.js +104 -0
  282. package/dist/features/products/schemas/index.d.ts +10 -19
  283. package/dist/features/products/schemas/index.js +23 -6
  284. package/dist/features/products/types/index.d.ts +45 -1
  285. package/dist/features/products/utils/group-member-image.d.ts +46 -0
  286. package/dist/features/products/utils/group-member-image.js +4 -0
  287. package/dist/features/products/utils/listing-badge-variant.js +4 -0
  288. package/dist/features/reviews/components/ReviewsList.js +1 -1
  289. package/dist/features/scams/components/ScamProfileView.js +1 -1
  290. package/dist/features/search/components/EntityTypeahead.d.ts +44 -0
  291. package/dist/features/search/components/EntityTypeahead.js +92 -0
  292. package/dist/features/search/components/Search.d.ts +12 -1
  293. package/dist/features/search/components/Search.js +47 -3
  294. package/dist/features/search/components/SearchFacetRail.d.ts +11 -0
  295. package/dist/features/search/components/SearchFacetRail.js +92 -0
  296. package/dist/features/search/components/SearchResultsClient.d.ts +33 -0
  297. package/dist/features/search/components/SearchResultsClient.js +103 -0
  298. package/dist/features/search/components/SearchResultsPageView.d.ts +15 -1
  299. package/dist/features/search/components/SearchResultsPageView.js +2 -2
  300. package/dist/features/search/components/index.d.ts +0 -6
  301. package/dist/features/search/components/index.js +0 -3
  302. package/dist/features/search/hooks/useNavSuggestions.d.ts +13 -0
  303. package/dist/features/search/hooks/useScopedSearch.d.ts +34 -0
  304. package/dist/features/search/hooks/useScopedSearch.js +114 -0
  305. package/dist/features/search/hooks/useSiteSearch.d.ts +26 -0
  306. package/dist/features/search/hooks/useSiteSearch.js +149 -0
  307. package/dist/features/search/index.d.ts +0 -4
  308. package/dist/features/search/index.js +0 -4
  309. package/dist/features/search/manifest.d.ts +12 -0
  310. package/dist/features/search/manifest.js +17 -2
  311. package/dist/features/search/server.d.ts +0 -3
  312. package/dist/features/search/server.js +0 -3
  313. package/dist/features/seller/components/SellerListingTypeView.d.ts +1 -1
  314. package/dist/features/seller/components/SellerListingTypeView.js +20 -0
  315. package/dist/features/seller/components/SellerPayoutSettingsView.js +35 -14
  316. package/dist/features/seller/components/SellerProductShell.d.ts +1 -1
  317. package/dist/features/seller/components/SellerProductShell.js +2 -1
  318. package/dist/features/seller/components/SellerProductsView.js +3 -2
  319. package/dist/features/seller/components/SellerSidebar.js +1 -1
  320. package/dist/features/seller/components/SellerSparesView.d.ts +17 -0
  321. package/dist/features/seller/components/SellerSparesView.js +6 -0
  322. package/dist/features/seller/components/index.d.ts +2 -0
  323. package/dist/features/seller/components/index.js +1 -0
  324. package/dist/features/shell/CommandPalette.js +1 -1
  325. package/dist/features/sticker-trace/schemas/firestore.d.ts +19 -0
  326. package/dist/features/stores/components/StoreAuctionsListing.js +2 -1
  327. package/dist/features/stores/components/StoreDetailLayoutView.d.ts +1 -1
  328. package/dist/features/stores/components/StoreDetailLayoutView.js +7 -82
  329. package/dist/features/stores/components/StoreHeader.js +1 -2
  330. package/dist/features/stores/components/StoreSparesPageView.d.ts +27 -0
  331. package/dist/features/stores/components/StoreSparesPageView.js +48 -0
  332. package/dist/features/stores/schemas/firestore.d.ts +19 -0
  333. package/dist/features/tester/seed-data/authored/buying__spares-and-groups.d.ts +2 -0
  334. package/dist/features/tester/seed-data/authored/buying__spares-and-groups.js +96 -0
  335. package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.d.ts +2 -0
  336. package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.js +392 -45
  337. package/dist/features/tester/seed-data/authored/content-discovery__category-tabs-franchise-bundles.js +54 -32
  338. package/dist/features/tester/seed-data/authored/content-discovery__parts-database.js +3 -2
  339. package/dist/features/tester/seed-data/authored/content-discovery__price-index-and-compare.d.ts +2 -0
  340. package/dist/features/tester/seed-data/authored/content-discovery__price-index-and-compare.js +73 -0
  341. package/dist/features/tester/seed-data/authored/content-discovery__site-wide-index.d.ts +2 -0
  342. package/dist/features/tester/seed-data/authored/content-discovery__site-wide-index.js +340 -0
  343. package/dist/features/tester/seed-data/authored/index.js +16 -0
  344. package/dist/features/tester/seed-data/authored/search-and-nav__header-search.d.ts +1 -0
  345. package/dist/features/tester/seed-data/authored/search-and-nav__header-search.js +57 -0
  346. package/dist/features/tester/seed-data/authored/selling__spares-and-group-editing.d.ts +2 -0
  347. package/dist/features/tester/seed-data/authored/selling__spares-and-group-editing.js +77 -0
  348. package/dist/features/tester/seed-data/authored/tools__sticker-promote-to-listings.d.ts +2 -0
  349. package/dist/features/tester/seed-data/authored/tools__sticker-promote-to-listings.js +58 -0
  350. package/dist/features/tester/seed-data/authored/tools__sticker-tracer.js +5 -4
  351. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +384 -18
  352. package/dist/index.d.ts +17 -57
  353. package/dist/index.js +35 -96
  354. package/dist/next/routing/route-map.d.ts +42 -1
  355. package/dist/next/routing/route-map.js +45 -0
  356. package/dist/providers/db-firebase/base.repository.d.ts +22 -0
  357. package/dist/providers/db-firebase/base.repository.js +36 -0
  358. package/dist/schemas/registry.d.ts +15 -6
  359. package/dist/security/blind-index-tokens.d.ts +54 -0
  360. package/dist/security/blind-index-tokens.js +152 -0
  361. package/dist/security/index.d.ts +1 -1
  362. package/dist/security/index.js +1 -1
  363. package/dist/security/settings-encryption.d.ts +35 -0
  364. package/dist/security/settings-encryption.js +91 -0
  365. package/dist/seed/_helpers/brand-rows.js +38 -14
  366. package/dist/seed/_helpers/category-forest.js +73 -7
  367. package/dist/seed/_helpers/category-images-generated.d.ts +25 -6
  368. package/dist/seed/_helpers/category-images-generated.js +81 -6
  369. package/dist/seed/_helpers/category-models.d.ts +1 -1
  370. package/dist/seed/_helpers/category-models.js +7 -1
  371. package/dist/seed/_helpers/category-parts-glossary-map.js +8 -0
  372. package/dist/seed/_helpers/category-parts.d.ts +1 -1
  373. package/dist/seed/_helpers/category-parts.js +50 -2
  374. package/dist/seed/_helpers/character-features.d.ts +1 -1
  375. package/dist/seed/_helpers/character-features.js +2 -1
  376. package/dist/seed/_helpers/franchise-rows.js +10 -2
  377. package/dist/seed/_helpers/media.d.ts +47 -0
  378. package/dist/seed/_helpers/media.js +34 -0
  379. package/dist/seed/authenticity-knowledge-seed-data.d.ts +49 -5
  380. package/dist/seed/authenticity-knowledge-seed-data.js +10001 -549
  381. package/dist/seed/categories-seed-data.js +124 -20
  382. package/dist/seed/grouped-listings-seed-data.js +16 -0
  383. package/dist/seed/homepage-sections-seed-data.js +94 -5
  384. package/dist/seed/index.d.ts +2 -3
  385. package/dist/seed/index.js +2 -2
  386. package/dist/seed/manifest.js +3 -0
  387. package/dist/seed/media-assets-seed-data.d.ts +1 -1
  388. package/dist/seed/media-assets-seed-data.js +746 -196
  389. package/dist/seed/model-composition-seed-data.js +122 -6
  390. package/dist/seed/part-glossary-seed-data.d.ts +1 -1
  391. package/dist/seed/part-glossary-seed-data.js +37219 -463
  392. package/dist/seed/products-art-seed-data.js +7 -18
  393. package/dist/seed/products-auctions-seed-data.js +56 -47
  394. package/dist/seed/products-beyblade-x-seed-data.d.ts +2 -0
  395. package/dist/seed/products-beyblade-x-seed-data.js +476 -0
  396. package/dist/seed/products-catalogue-seed-data.d.ts +5 -5
  397. package/dist/seed/products-catalogue-seed-data.js +1500 -1240
  398. package/dist/seed/products-classifieds-seed-data.js +9 -27
  399. package/dist/seed/products-digital-codes-seed-data.js +9 -18
  400. package/dist/seed/products-live-items-seed-data.js +5 -13
  401. package/dist/seed/products-preorders-seed-data.js +67 -33
  402. package/dist/seed/products-prize-draws-seed-data.js +8 -17
  403. package/dist/seed/products-spares-seed-data.d.ts +2 -0
  404. package/dist/seed/products-spares-seed-data.js +368 -0
  405. package/dist/seed/products-standard-seed-data.js +39 -126
  406. package/dist/seed/products-stickers-seed-data.js +59 -15
  407. package/dist/seed/site-settings-seed-data.js +8 -0
  408. package/dist/seo/json-ld.d.ts +25 -0
  409. package/dist/seo/json-ld.js +28 -0
  410. package/dist/server-entry.d.ts +3 -1
  411. package/dist/server-entry.js +2 -2
  412. package/dist/server.d.ts +11 -9
  413. package/dist/server.js +35 -25
  414. package/dist/styles.css +202 -1
  415. package/dist/tailwind-input.css +31 -17
  416. package/dist/tailwind-utilities.css +1 -1
  417. package/dist/ui/components/Accordion.js +1 -1
  418. package/dist/ui/components/Breadcrumb.js +1 -1
  419. package/dist/ui/components/DetailPageGallery.js +0 -2
  420. package/dist/ui/components/DetailPageHero.js +0 -1
  421. package/dist/ui/components/DetailPageTabs.js +1 -1
  422. package/dist/ui/components/Divider.js +3 -3
  423. package/dist/ui/components/GatedPrice.js +5 -1
  424. package/dist/ui/components/HorizontalScroller.js +1 -1
  425. package/dist/ui/components/ListingLayout.js +3 -3
  426. package/dist/ui/components/MediaTabs.style.css +81 -0
  427. package/dist/ui/components/Pagination.js +1 -1
  428. package/dist/ui/components/PendingValue.d.ts +45 -0
  429. package/dist/ui/components/PendingValue.js +7 -0
  430. package/dist/ui/components/Radio.style.css +119 -0
  431. package/dist/ui/components/RatingDisplay.js +2 -2
  432. package/dist/ui/components/SettingsSection.js +0 -2
  433. package/dist/ui/components/SiteLogo.js +3 -0
  434. package/dist/ui/components/SiteMark.js +3 -0
  435. package/dist/ui/components/SortDropdown.js +1 -1
  436. package/dist/ui/components/SpecTable.d.ts +58 -0
  437. package/dist/ui/components/SpecTable.js +8 -0
  438. package/dist/ui/components/StarRating.js +1 -3
  439. package/dist/ui/components/SummaryCard.js +1 -1
  440. package/dist/ui/components/index.style.css +1 -0
  441. package/dist/ui/icons/icon-registry.d.ts +1 -0
  442. package/dist/ui/icons/icon-registry.js +9 -1
  443. package/dist/ui/index.d.ts +4 -0
  444. package/dist/ui/index.js +2 -0
  445. package/dist/utils/search-txt-query.d.ts +12 -0
  446. package/dist/utils/search-txt-query.js +12 -0
  447. package/package.json +1 -1
  448. package/scripts/dedupe-peer-deps.mjs +18 -0
  449. package/scripts/seed-cli.mjs +250 -35
  450. package/scripts/verify-css-build.mjs +118 -37
  451. package/dist/_internal/server/features/search/actions.d.ts +0 -7
  452. package/dist/_internal/server/features/search/actions.js +0 -21
  453. package/dist/_internal/server/features/search/data.d.ts +0 -15
  454. package/dist/_internal/server/features/search/data.js +0 -18
  455. package/dist/_internal/server/features/search/index.d.ts +0 -3
  456. package/dist/_internal/server/features/search/index.js +0 -2
  457. package/dist/features/homepage/api/[id]/route.d.ts +0 -23
  458. package/dist/features/homepage/api/[id]/route.js +0 -103
  459. package/dist/features/homepage/api/route.d.ts +0 -14
  460. package/dist/features/homepage/api/route.js +0 -140
  461. package/dist/features/homepage/api/sections/[id]/route.d.ts +0 -26
  462. package/dist/features/homepage/api/sections/[id]/route.js +0 -95
  463. package/dist/features/homepage/api/sections/route.d.ts +0 -13
  464. package/dist/features/homepage/api/sections/route.js +0 -82
  465. package/dist/features/homepage/components/AdvertisementBanner.d.ts +0 -14
  466. package/dist/features/homepage/components/AdvertisementBanner.js +0 -35
  467. package/dist/features/homepage/components/BeforeAfterCard.d.ts +0 -9
  468. package/dist/features/homepage/components/BeforeAfterCard.js +0 -73
  469. package/dist/features/homepage/components/FeaturedResultsSection.d.ts +0 -17
  470. package/dist/features/homepage/components/FeaturedResultsSection.js +0 -17
  471. package/dist/features/homepage/components/HeroBanner.d.ts +0 -7
  472. package/dist/features/homepage/components/HeroBanner.js +0 -43
  473. package/dist/features/homepage/components/HeroSection.d.ts +0 -7
  474. package/dist/features/homepage/components/HeroSection.js +0 -6
  475. package/dist/features/homepage/components/HomepageSkeleton.d.ts +0 -2
  476. package/dist/features/homepage/components/HomepageSkeleton.js +0 -21
  477. package/dist/features/homepage/components/HomepageView.d.ts +0 -25
  478. package/dist/features/homepage/components/HomepageView.js +0 -5
  479. package/dist/features/homepage/components/NewsletterBanner.d.ts +0 -23
  480. package/dist/features/homepage/components/NewsletterBanner.js +0 -37
  481. package/dist/features/homepage/components/SiteFeaturesSection.d.ts +0 -14
  482. package/dist/features/homepage/components/SiteFeaturesSection.js +0 -11
  483. package/dist/features/homepage/components/TrustBadges.d.ts +0 -6
  484. package/dist/features/homepage/components/TrustBadges.js +0 -46
  485. package/dist/features/homepage/components/TrustIndicatorsSection.d.ts +0 -12
  486. package/dist/features/homepage/components/TrustIndicatorsSection.js +0 -11
  487. package/dist/features/homepage/hooks/useHomepage.d.ts +0 -11
  488. package/dist/features/homepage/hooks/useHomepage.js +0 -17
  489. package/dist/features/homepage/hooks/useHomepageSections.d.ts +0 -2
  490. package/dist/features/homepage/hooks/useHomepageSections.js +0 -10
  491. package/dist/features/homepage/repository/homepage.repository.d.ts +0 -11
  492. package/dist/features/homepage/repository/homepage.repository.js +0 -25
  493. package/dist/features/search/actions/index.d.ts +0 -1
  494. package/dist/features/search/actions/index.js +0 -1
  495. package/dist/features/search/actions/search-actions.d.ts +0 -18
  496. package/dist/features/search/actions/search-actions.js +0 -43
  497. package/dist/features/search/api/route.d.ts +0 -22
  498. package/dist/features/search/api/route.js +0 -128
  499. package/dist/features/search/columns/index.d.ts +0 -17
  500. package/dist/features/search/columns/index.js +0 -49
  501. package/dist/features/search/components/SearchFiltersRow.d.ts +0 -22
  502. package/dist/features/search/components/SearchFiltersRow.js +0 -21
  503. package/dist/features/search/components/SearchResultsSection.d.ts +0 -53
  504. package/dist/features/search/components/SearchResultsSection.js +0 -35
  505. package/dist/features/search/components/SearchView.d.ts +0 -27
  506. package/dist/features/search/components/SearchView.js +0 -8
  507. package/dist/features/search/hooks/useSearch.d.ts +0 -21
  508. package/dist/features/search/hooks/useSearch.js +0 -33
  509. package/dist/features/search/repository/search.repository.d.ts +0 -14
  510. package/dist/features/search/repository/search.repository.js +0 -59
  511. package/dist/features/search/schemas/index.d.ts +0 -70
  512. package/dist/features/search/schemas/index.js +0 -36
  513. package/dist/features/search/types/index.d.ts +0 -48
  514. package/dist/features/stores/components/StoreScopedSearch.d.ts +0 -17
  515. package/dist/features/stores/components/StoreScopedSearch.js +0 -27
  516. package/dist/seed/defaults/homepage-sections.d.ts +0 -2
  517. package/dist/seed/defaults/homepage-sections.js +0 -60
  518. package/dist/seed/factories/homepage-section.factory.d.ts +0 -10
  519. package/dist/seed/factories/homepage-section.factory.js +0 -13
  520. /package/dist/{features/search/types/index.js → _internal/shared/listing-types/spares/og.d.ts} +0 -0
@@ -0,0 +1,30 @@
1
+ import { isListingTypeEnabled } from "../../../shared/listing-types/feature-flags";
2
+ import { type ListingTabCountResult } from "../products/listing-tab-counts";
3
+ type SiteSettingsSnapshot = Parameters<typeof isListingTypeEnabled>[1];
4
+ export interface StoreTabLink {
5
+ value: string;
6
+ label: string;
7
+ href: string;
8
+ }
9
+ export interface ResolvedStoreTabs {
10
+ /** Listing-type tabs with something in them, in `STORE_PAGE_TABS` order. */
11
+ dropdownTabs: StoreTabLink[];
12
+ /** Coupons (if any), Reviews (if any), About — always non-empty. */
13
+ tabs: StoreTabLink[];
14
+ /**
15
+ * Both numbers, because they answer different questions: `available` is what
16
+ * each tab badges, `total` is what decides whether the tab exists at all.
17
+ * See `ListingTabCountResult`.
18
+ */
19
+ listingCounts: ListingTabCountResult;
20
+ couponsCount: number;
21
+ reviewsCount: number;
22
+ }
23
+ export declare function resolveStoreTabs(storeSlug: string, storeId: string | undefined, settings: SiteSettingsSnapshot): Promise<ResolvedStoreTabs>;
24
+ /**
25
+ * Where the bare `/stores/[slug]` route should land: the first listing-type
26
+ * tab holding anything, else Coupons, else Reviews, else About. `about` is
27
+ * always present in `tabs`, so this never falls through to nothing.
28
+ */
29
+ export declare function getDefaultStoreTabPath(storeSlug: string, storeId: string | undefined, settings: SiteSettingsSnapshot): Promise<string>;
30
+ export {};
@@ -0,0 +1,124 @@
1
+ /*
2
+ * 🛑 Deliberately NO `"use server"`. That directive marks every export as a
3
+ * client-callable RPC endpoint, and these are not — they are read helpers
4
+ * invoked from a Server Component (`StoreDetailLayoutView`) and a server
5
+ * page (`/stores/[slug]`). Declaring it anyway also obliges every export to
6
+ * return `ActionResult<T>` (`audit-server-action-envelope`), which would
7
+ * wrap a path string in an envelope nobody unwraps. The sibling helpers
8
+ * this file sits beside — `adapters.ts`, `denormalize.ts` and
9
+ * `listing-tab-counts.ts`, which it calls — carry no directive either.
10
+ *
11
+ * WHY: `StoreDetailLayoutView`'s nav bar and the bare `/stores/[slug]`
12
+ * redirect both need "which tabs does this store actually have
13
+ * something in, and in what order". Before this file the redirect
14
+ * didn't ask the question at all — it always sent a visitor to
15
+ * `/products`, even for a store with zero products (and nothing else),
16
+ * landing on a tab that isn't even in the nav bar. Computing the answer
17
+ * twice (once per caller) is exactly the duplicated-predicate shape
18
+ * Root Cause #73 warns about, so it lives in one place.
19
+ * WHAT: `resolveStoreTabs` — every visible tab for a store, in priority
20
+ * order (listing types in `STORE_PAGE_TABS` order, then Coupons, then
21
+ * Reviews, then About last). `getDefaultStoreTabPath` picks the first
22
+ * one — the correct landing page for a store with no products.
23
+ */
24
+ import { sieveAnd, sieveFilter, SIEVE_OP } from "../../../../utils/sieve-builder.js";
25
+ import { couponsRepository, reviewRepository } from "../../../../repositories/index.js";
26
+ import { ROUTES } from "../../../../next/routing/route-map.js";
27
+ import { STORE_PAGE_TABS } from "../../../../features/products/constants/listing-tabs.js";
28
+ import { isListingTypeEnabled, isCategoryTypeEnabled } from "../../../shared/listing-types/feature-flags.js";
29
+ import { pluginFor } from "../../../shared/listing-types/_registry.js";
30
+ import { listingTabCounts, } from "../products/listing-tab-counts.js";
31
+ function tabLabel(base, count) {
32
+ if (!count)
33
+ return base;
34
+ return `${base} (${count.toLocaleString()})`;
35
+ }
36
+ /**
37
+ * tabSlug -> route builder. Mirrors `ROUTES.PUBLIC.STORE_*` exactly.
38
+ *
39
+ * 🛑 `spares` was missing here until this file was added, which meant
40
+ * `visibleStoreTabs` could include a tab this map had no builder for the
41
+ * moment a store's spares count went above zero — `STORE_LISTING_HREF[tab.id]`
42
+ * would resolve to `undefined`, and calling it as a function would throw.
43
+ * `/stores/[storeSlug]/spares/page.tsx` is a real route; the href map just
44
+ * never named it.
45
+ */
46
+ const STORE_LISTING_HREF = {
47
+ products: (slug) => String(ROUTES.PUBLIC.STORE_PRODUCTS(slug)),
48
+ auctions: (slug) => String(ROUTES.PUBLIC.STORE_AUCTIONS(slug)),
49
+ "pre-orders": (slug) => String(ROUTES.PUBLIC.STORE_PRE_ORDERS(slug)),
50
+ "prize-draws": (slug) => String(ROUTES.PUBLIC.STORE_PRIZE_DRAWS(slug)),
51
+ bundles: (slug) => String(ROUTES.PUBLIC.STORE_BUNDLES(slug)),
52
+ classifieds: (slug) => String(ROUTES.PUBLIC.STORE_CLASSIFIEDS(slug)),
53
+ "digital-codes": (slug) => String(ROUTES.PUBLIC.STORE_DIGITAL_CODES(slug)),
54
+ live: (slug) => String(ROUTES.PUBLIC.STORE_LIVE(slug)),
55
+ // Combined art + stickers, mirroring the public /art page. This used to
56
+ // point at STORE_PRODUCTS, which filters to `standard` — so the tab showed
57
+ // a real non-zero count and landed on a page with none of those items.
58
+ art: (slug) => String(ROUTES.PUBLIC.STORE_ART(slug)),
59
+ spares: (slug) => String(ROUTES.PUBLIC.STORE_SPARES(slug)),
60
+ };
61
+ export async function resolveStoreTabs(storeSlug, storeId, settings) {
62
+ const [listingCounts, couponsCount, reviewsCount] = storeId
63
+ ? await Promise.all([
64
+ listingTabCounts(STORE_PAGE_TABS, { storeId }),
65
+ // audit-public-test-data-ok: a COUNT of the store's own coupons, and
66
+ // `CouponDocument` carries no isTestData field — there is nothing to strip.
67
+ couponsRepository
68
+ .list({ filters: sieveAnd(sieveFilter("storeId", SIEVE_OP.EQ, storeId), sieveFilter("validity.isActive", SIEVE_OP.EQ, "true")), page: 1, pageSize: 1 })
69
+ .then((r) => r.total)
70
+ .catch(() => 0),
71
+ reviewRepository
72
+ .listAll({ filters: sieveAnd(sieveFilter("storeId", SIEVE_OP.EQ, storeId), sieveFilter("status", SIEVE_OP.EQ, "approved")), page: 1, pageSize: 1 })
73
+ .then((r) => r.total)
74
+ .catch(() => 0),
75
+ ])
76
+ : [{ available: {}, total: {} }, 0, 0];
77
+ const visibleStoreTabs = STORE_PAGE_TABS.filter((tab) => {
78
+ if (tab.id === "bundles") {
79
+ if (!isCategoryTypeEnabled("bundle", settings))
80
+ return false;
81
+ }
82
+ else if (tab.id === pluginFor("art").tabSlug) {
83
+ // Combined tab — visible if either underlying listing type is enabled.
84
+ if (!(isListingTypeEnabled("art", settings) || isListingTypeEnabled("stickers", settings)))
85
+ return false;
86
+ }
87
+ else if (tab.listingType) {
88
+ if (!isListingTypeEnabled(tab.listingType, settings))
89
+ return false;
90
+ }
91
+ // A store with zero items of a given listing type shouldn't offer a tab
92
+ // that leads to an empty page. `undefined` means the count query FAILED,
93
+ // not zero, so the tab stays visible rather than hiding real stock
94
+ // behind a swallowed error (Root Cause #59).
95
+ // TOTAL, not the badge: a tab whose rows have all ended still has an
96
+ // archive behind the Ended/All scopes, and hiding it would strand that.
97
+ const count = listingCounts.total[tab.id];
98
+ return count === undefined || count > 0;
99
+ });
100
+ const dropdownTabs = visibleStoreTabs.map((tab) => ({
101
+ value: tab.id,
102
+ label: tabLabel(tab.label, listingCounts.available[tab.id]),
103
+ href: STORE_LISTING_HREF[tab.id](storeSlug),
104
+ }));
105
+ const tabs = [
106
+ ...(couponsCount > 0
107
+ ? [{ value: "coupons", label: tabLabel("Coupons", couponsCount), href: String(ROUTES.PUBLIC.STORE_COUPONS(storeSlug)) }]
108
+ : []),
109
+ ...(reviewsCount > 0
110
+ ? [{ value: "reviews", label: tabLabel("Reviews", reviewsCount), href: String(ROUTES.PUBLIC.STORE_REVIEWS(storeSlug)) }]
111
+ : []),
112
+ { value: "about", label: "About", href: String(ROUTES.PUBLIC.STORE_ABOUT(storeSlug)) },
113
+ ];
114
+ return { dropdownTabs, tabs, listingCounts, couponsCount, reviewsCount };
115
+ }
116
+ /**
117
+ * Where the bare `/stores/[slug]` route should land: the first listing-type
118
+ * tab holding anything, else Coupons, else Reviews, else About. `about` is
119
+ * always present in `tabs`, so this never falls through to nothing.
120
+ */
121
+ export async function getDefaultStoreTabPath(storeSlug, storeId, settings) {
122
+ const { dropdownTabs, tabs } = await resolveStoreTabs(storeSlug, storeId, settings);
123
+ return (dropdownTabs[0] ?? tabs[0]).href;
124
+ }
@@ -1,12 +1,10 @@
1
1
  /** Default ListingCheckoutRule hook implementations. */
2
- import { STANDARD_MAX_QTY_PER_LINE } from "./_limits.js";
3
2
  export const DEFAULT_LISTING_RULE = {
4
3
  purchaseable: true,
5
4
  cartEligible: true,
6
5
  cartIneligibleHint: "This listing type cannot be added to the cart.",
7
6
  requiresShippingAddress: true,
8
7
  nonRefundable: false,
9
- maxQuantityPerLine: STANDARD_MAX_QTY_PER_LINE,
10
8
  canMergeWithSameProduct: true,
11
9
  maxLinesPerOrder: Infinity,
12
10
  refundPolicy: { full: true, partial: true, partialGranularity: "item" },
@@ -28,5 +28,18 @@ export declare const PRIZE_DRAW_DURATION_DAYS_MIN = 1;
28
28
  export declare const PRIZE_DRAW_DURATION_DAYS_MAX = 15;
29
29
  /** Maximum qty per bundle purchase (bundles are always single-shot direct buys). */
30
30
  export declare const BUNDLE_MAX_QTY_PER_TX = 1;
31
- /** Standard / pre-order max qty per cart line (enforced at addToCart). */
31
+ /**
32
+ * Max quantity on a single cart line.
33
+ *
34
+ * 🛑 It now IS the enforcement. It used to say "enforced at addToCart" while
35
+ * the actual limit was four hand-written `.max(99)` literals that never
36
+ * referenced it — so the constant was documentation nobody read and the rule
37
+ * was a magic number in four places. The cart schemas import it.
38
+ *
39
+ * Its other reader, `ListingCheckoutRule.maxQuantityPerLine`, was DELETED in
40
+ * the same change: that field was set by auction, offer and prize-draw and read
41
+ * by nothing at all, and it contradicted the feature it appeared to govern —
42
+ * prize-draw set it to 1 while `splitMultipleOrders` right beside it existed
43
+ * only to chunk a quantity of 7 into [3,3,1].
44
+ */
32
45
  export declare const STANDARD_MAX_QTY_PER_LINE = 99;
@@ -28,5 +28,18 @@ export const PRIZE_DRAW_DURATION_DAYS_MIN = 1;
28
28
  export const PRIZE_DRAW_DURATION_DAYS_MAX = 15;
29
29
  /** Maximum qty per bundle purchase (bundles are always single-shot direct buys). */
30
30
  export const BUNDLE_MAX_QTY_PER_TX = 1;
31
- /** Standard / pre-order max qty per cart line (enforced at addToCart). */
31
+ /**
32
+ * Max quantity on a single cart line.
33
+ *
34
+ * 🛑 It now IS the enforcement. It used to say "enforced at addToCart" while
35
+ * the actual limit was four hand-written `.max(99)` literals that never
36
+ * referenced it — so the constant was documentation nobody read and the rule
37
+ * was a magic number in four places. The cart schemas import it.
38
+ *
39
+ * Its other reader, `ListingCheckoutRule.maxQuantityPerLine`, was DELETED in
40
+ * the same change: that field was set by auction, offer and prize-draw and read
41
+ * by nothing at all, and it contradicted the feature it appeared to govern —
42
+ * prize-draw set it to 1 while `splitMultipleOrders` right beside it existed
43
+ * only to chunk a quantity of 7 into [3,3,1].
44
+ */
32
45
  export const STANDARD_MAX_QTY_PER_LINE = 99;
@@ -20,6 +20,7 @@ import { digitalCodeRule } from "./digital-code.rule.js";
20
20
  import { liveRule } from "./live.rule.js";
21
21
  import { artRule } from "./art.rule.js";
22
22
  import { stickersRule } from "./stickers.rule.js";
23
+ import { sparesRule } from "./spares.rule.js";
23
24
  export const CHECKOUT_RULES = {
24
25
  standard: standardRule,
25
26
  auction: auctionRule,
@@ -30,6 +31,7 @@ export const CHECKOUT_RULES = {
30
31
  live: liveRule,
31
32
  art: artRule,
32
33
  stickers: stickersRule,
34
+ spares: sparesRule,
33
35
  };
34
36
  export const CATEGORY_CHECKOUT_RULES = {
35
37
  bundle: bundleRule,
@@ -4,7 +4,15 @@ export const auctionRule = {
4
4
  orderType: "auction",
5
5
  cartEligible: false, // bid-only; add-to-cart is blocked at the action layer
6
6
  cartIneligibleHint: "Place a bid on the auction page instead.",
7
- maxQuantityPerLine: 1,
7
+ /*
8
+ * 🛑 cod + cash, NOT emi. An auction win is a committed sale of a unique lot:
9
+ * a buyer who never pays on delivery costs the seller the item and the
10
+ * auction both, and there is no second-highest bidder to fall back to once
11
+ * it has been marked sold. EMI is confirmed-at-order financing rather than a
12
+ * deferred payment, so it carries none of that risk and stays available.
13
+ */
14
+ blockedPaymentMethods: ["cod", "cash"],
15
+ blockedPaymentMessage: "An auction win cannot be paid for on delivery or in cash — the lot is one of a kind and is held for you from the moment the auction closes.",
8
16
  canMergeWithSameProduct: false,
9
17
  refundPolicy: { full: true, partial: false, partialGranularity: "none" },
10
18
  splitKey: (item) => `auction:${item.itemId}`,
@@ -3,4 +3,17 @@ export const bundleRule = {
3
3
  cartEligible: false, // direct-checkout-only; addBundleToCart has been removed
4
4
  orderType: "standard", // bundle orders resolve to standard orderType
5
5
  requiresShippingAddress: true,
6
+ /*
7
+ * 🛑 cod + cash, NOT emi. A bundle is priced BELOW the sum of its members —
8
+ * that discount is the whole product — so a no-show on delivery costs the
9
+ * seller several items at once, and the bundle has meanwhile been held out of
10
+ * every member's own availability. EMI is confirmed-at-order financing rather
11
+ * than a deferred payment, so it carries none of that and stays available.
12
+ *
13
+ * 🛑 Honoured by `assertPaymentMethodAllowed`, NOT by `runSyncPreflight` —
14
+ * which skips multi-member lines, and a bundle line is always one. A value
15
+ * here relying on preflight would be a silent no-op.
16
+ */
17
+ blockedPaymentMethods: ["cod", "cash"],
18
+ blockedPaymentMessage: "Bundles cannot be paid for on delivery or in cash — a bundle is priced below the sum of its parts and is reserved when you order.",
6
19
  };
@@ -1,5 +1,6 @@
1
1
  export type { ListingCheckoutRule, CategoryCheckoutRule, CartItemProductPair, OrderItemInput, RefundPolicy, } from "./types";
2
2
  export { CHECKOUT_RULES, CATEGORY_CHECKOUT_RULES, getListingRule, getCategoryRule, pickOrderType, getSplitKey, runSyncPreflight, } from "./_registry";
3
+ export { assertPaymentMethodAllowed, blockedMethodsForCart, type BlockedMethod, } from "./payment-gate";
3
4
  export { CART_MAX_ITEMS, CHECKOUT_MAX_ORDERS_PER_TX, PRIZE_DRAW_MAX_REVEALS_PER_ORDER, PRIZE_DRAW_DURATION_DAYS_MIN, PRIZE_DRAW_DURATION_DAYS_MAX, BUNDLE_MAX_QTY_PER_TX, STANDARD_MAX_QTY_PER_LINE, } from "./_limits";
4
5
  export { standardRule } from "./standard.rule";
5
6
  export { auctionRule } from "./auction.rule";
@@ -12,3 +13,4 @@ export { digitalCodeRule } from "./digital-code.rule";
12
13
  export { liveRule } from "./live.rule";
13
14
  export { artRule } from "./art.rule";
14
15
  export { stickersRule } from "./stickers.rule";
16
+ export { sparesRule } from "./spares.rule";
@@ -1,4 +1,12 @@
1
1
  export { CHECKOUT_RULES, CATEGORY_CHECKOUT_RULES, getListingRule, getCategoryRule, pickOrderType, getSplitKey, runSyncPreflight, } from "./_registry.js";
2
+ /*
3
+ * The payment gate is a SEPARATE module from `_registry`, on purpose: it is the
4
+ * one rule check that cannot be expressed as a per-listing-type pass over
5
+ * single-member lines, which is all `runSyncPreflight` can see. Keeping them
6
+ * apart is what stops someone folding it back into preflight, where it would
7
+ * silently stop covering bundles and grouped lines.
8
+ */
9
+ export { assertPaymentMethodAllowed, blockedMethodsForCart, } from "./payment-gate.js";
2
10
  export { CART_MAX_ITEMS, CHECKOUT_MAX_ORDERS_PER_TX, PRIZE_DRAW_MAX_REVEALS_PER_ORDER, PRIZE_DRAW_DURATION_DAYS_MIN, PRIZE_DRAW_DURATION_DAYS_MAX, BUNDLE_MAX_QTY_PER_TX, STANDARD_MAX_QTY_PER_LINE, } from "./_limits.js";
3
11
  // Individual rules — exported for tests and direct access.
4
12
  export { standardRule } from "./standard.rule.js";
@@ -12,3 +20,4 @@ export { digitalCodeRule } from "./digital-code.rule.js";
12
20
  export { liveRule } from "./live.rule.js";
13
21
  export { artRule } from "./art.rule.js";
14
22
  export { stickersRule } from "./stickers.rule.js";
23
+ export { sparesRule } from "./spares.rule.js";
@@ -2,7 +2,6 @@ import { DEFAULT_LISTING_RULE } from "./_defaults.js";
2
2
  export const offerRule = {
3
3
  ...DEFAULT_LISTING_RULE,
4
4
  orderType: "offer",
5
- maxQuantityPerLine: 1,
6
5
  canMergeWithSameProduct: false,
7
6
  refundPolicy: { full: true, partial: false, partialGranularity: "none" },
8
7
  splitKey: (item) => `offer:${item.itemId}`,
@@ -0,0 +1,26 @@
1
+ import type { CartItemDocument } from "../../../../features/cart/schemas/firestore";
2
+ import type { ProductDocument } from "../../../../features/products/schemas/firestore";
3
+ import { type CheckoutPaymentMethod } from "../../features/checkout/config";
4
+ /** Why one method is unavailable, and which listing said so. */
5
+ export interface BlockedMethod {
6
+ /** The listing's own wording when it has one, else a generated sentence. */
7
+ message: string;
8
+ /** For logs and for the generated wording. */
9
+ label: string;
10
+ }
11
+ /**
12
+ * Which methods this cart forbids.
13
+ *
14
+ * Returned rather than thrown so the same answer can also DISABLE a payment
15
+ * control — one source for the refusal and the affordance, instead of a
16
+ * client-side guess that drifts from the server.
17
+ */
18
+ export declare function blockedMethodsForCart(items: readonly CartItemDocument[], productById: ReadonlyMap<string, ProductDocument>): Map<CheckoutPaymentMethod, BlockedMethod>;
19
+ /**
20
+ * Throw when the chosen method is forbidden by anything in the cart.
21
+ *
22
+ * One error, not one per offending line: a buyer changes the payment method
23
+ * once, and listing every blocked item would be a wall of text about a single
24
+ * decision.
25
+ */
26
+ export declare function assertPaymentMethodAllowed(items: readonly CartItemDocument[], productById: ReadonlyMap<string, ProductDocument>, paymentMethod: CheckoutPaymentMethod | undefined): void;
@@ -0,0 +1,133 @@
1
+ /*
2
+ * WHY: Some listings must not be bought with a pay-later method. An auction win
3
+ * or a bundle is a committed sale — a buyer who never pays on delivery
4
+ * costs the seller the lot, and for a unique auction lot there is no
5
+ * second-highest bidder to fall back on once it is marked sold. The rule
6
+ * registry has a field for this (`blockedPaymentMethods`) and nothing
7
+ * reachable enforced it for every line.
8
+ * WHAT: One cart-wide payment check, run BEFORE the transaction.
9
+ *
10
+ * 🛑 `runSyncPreflight` STRUCTURALLY CANNOT DO THIS, and that is not a bug in
11
+ * preflight. It does `if (isMultiMemberLine(pair.item)) continue;` for a good
12
+ * reason: a bundle or grouped line's own `listingType` describes the WRAPPER,
13
+ * not its members, so a per-type rule applied there would be applied to the
14
+ * wrong thing. Both of its call sites repeat the skip independently, and the
15
+ * online path additionally reduces each line to `getCartItemMemberIds(item)[0]`
16
+ * — its FIRST member only. So a `blockedPaymentMethods` set on `bundleRule` and
17
+ * left to preflight would be a silent no-op: the field would be set, reviewed,
18
+ * and never read.
19
+ *
20
+ * This resolves every line to ALL its members, so a grouped line containing one
21
+ * blocked listing is caught.
22
+ *
23
+ * 🛑 IT RUNS OUTSIDE THE TRANSACTION. Preflight is invoked inside the
24
+ * transaction callback, so a payment-method rejection there spends a full
25
+ * transaction attempt to produce an answer that depends on nothing the
26
+ * transaction reads.
27
+ *
28
+ * EXPORTS: assertPaymentMethodAllowed, blockedMethodsForCart
29
+ *
30
+ * @tag domain:checkout
31
+ * @tag layer:shared
32
+ * @tag pattern:none
33
+ * @tag access:isomorphic
34
+ * @tag consumers:server/features/checkout
35
+ * @tag sideEffects:none
36
+ */
37
+ import { ValidationError } from "../../../../errors/index.js";
38
+ import { CHECKOUT_PAYMENT_METHODS, } from "../../features/checkout/config.js";
39
+ import { getCartItemMemberIds } from "../line-members.js";
40
+ import { getCategoryRule, getListingRule } from "./_registry.js";
41
+ /**
42
+ * Which methods this cart forbids.
43
+ *
44
+ * Returned rather than thrown so the same answer can also DISABLE a payment
45
+ * control — one source for the refusal and the affordance, instead of a
46
+ * client-side guess that drifts from the server.
47
+ */
48
+ export function blockedMethodsForCart(items, productById) {
49
+ const blocked = new Map();
50
+ const claim = (methods, label, message) => {
51
+ for (const m of methods ?? []) {
52
+ // First claimant wins, so the message names a real listing rather than
53
+ // whichever happened to be last in the cart.
54
+ if (blocked.has(m))
55
+ continue;
56
+ blocked.set(m, {
57
+ label,
58
+ message: message ??
59
+ `"${label}" cannot be paid for with ${PAYMENT_METHOD_LABEL[m] ?? m}.`,
60
+ });
61
+ }
62
+ };
63
+ for (const item of items) {
64
+ /*
65
+ * A bundle's restriction lives on its CategoryCheckoutRule, not on any
66
+ * listing type — its members are ordinary products whose own types would
67
+ * otherwise answer for it. Checked first for that reason.
68
+ */
69
+ if (item.bundleCategorySlug) {
70
+ const rule = getCategoryRule("bundle");
71
+ claim(rule?.blockedPaymentMethods, item.groupTitle ?? item.productTitle ?? "this bundle", rule?.blockedPaymentMessage);
72
+ }
73
+ /*
74
+ * EVERY member, not the first. `getCartItemMemberIds` returns the line's
75
+ * own product for a plain line and all members for a bundle or group, so
76
+ * one blocked member blocks the line.
77
+ */
78
+ for (const productId of getCartItemMemberIds(item)) {
79
+ const product = productById.get(productId);
80
+ /*
81
+ * An unresolved member is NOT treated as unrestricted here. It is left to
82
+ * the stock and existence checks that already run on the same cart and
83
+ * fail loudly — inventing a payment refusal for a missing product would
84
+ * report the wrong problem.
85
+ */
86
+ if (!product)
87
+ continue;
88
+ const type = (product.listingType ?? "standard");
89
+ const rule = getListingRule(type);
90
+ claim(rule.blockedPaymentMethods, product.title ?? type, rule.blockedPaymentMessage);
91
+ }
92
+ }
93
+ return blocked;
94
+ }
95
+ /** Human wording for the generated message. */
96
+ const PAYMENT_METHOD_LABEL = {
97
+ cod: "cash on delivery",
98
+ cash: "cash on pickup",
99
+ emi: "EMI",
100
+ online: "online payment",
101
+ upi_manual: "manual bank/UPI transfer",
102
+ };
103
+ /**
104
+ * Throw when the chosen method is forbidden by anything in the cart.
105
+ *
106
+ * One error, not one per offending line: a buyer changes the payment method
107
+ * once, and listing every blocked item would be a wall of text about a single
108
+ * decision.
109
+ */
110
+ export function assertPaymentMethodAllowed(items, productById, paymentMethod) {
111
+ if (!paymentMethod)
112
+ return;
113
+ /*
114
+ * 🛑 `admin_bypass` is exempt, deliberately. It exists so an admin can reach
115
+ * an order state without a real payment, is gated by a site-settings flag
116
+ * plus an admin role, and logs every use. Blocking it would make the one tool
117
+ * for testing these flows unable to test them.
118
+ */
119
+ if (paymentMethod === "admin_bypass")
120
+ return;
121
+ const blocked = blockedMethodsForCart(items, productById);
122
+ const offender = blocked.get(paymentMethod);
123
+ if (!offender)
124
+ return;
125
+ /*
126
+ * The methods that remain, derived from the union rather than a second list —
127
+ * a seventh payment method must not quietly go unmentioned here.
128
+ */
129
+ const allowed = CHECKOUT_PAYMENT_METHODS.filter((m) => m !== "admin_bypass" && !blocked.has(m)).map((m) => PAYMENT_METHOD_LABEL[m] ?? m);
130
+ throw Object.assign(new ValidationError(allowed.length > 0
131
+ ? `${offender.message} Available instead: ${allowed.join(", ")}.`
132
+ : `${offender.message} Nothing in this cart's combination can be paid for — remove the item and buy it on its own.`), { code: "PAYMENT_METHOD_BLOCKED" });
133
+ }
@@ -10,15 +10,26 @@ import { PRIZE_DRAW_MAX_REVEALS_PER_ORDER } from "./_limits.js";
10
10
  * "admin_bypass" stays usable for QA regardless.
11
11
  */
12
12
  const PRIZE_DRAW_BLOCKED_PAYMENT_METHODS = ["cod", "cash", "emi"];
13
+ /**
14
+ * The refusal, kept VERBATIM when this check moved out of `preflightChecks`
15
+ * into `assertPaymentMethodAllowed`.
16
+ *
17
+ * 🛑 The gate can generate a correct sentence on its own, and a generated one
18
+ * would have been a regression here: this wording names the REASON (winner
19
+ * assignment waits on confirmed payment) rather than just the refusal, and the
20
+ * reason is the thing a buyer needs in order to pick a different method rather
21
+ * than abandon the entry.
22
+ */
23
+ const PRIZE_DRAW_BLOCKED_PAYMENT_MESSAGE = "Prize-draw entries can only be paid by manual bank/UPI transfer or PhonePe — cash on delivery and EMI are not available for this listing type.";
13
24
  export const prizeDrawRule = {
14
25
  ...DEFAULT_LISTING_RULE,
15
26
  orderType: "prize-draw",
16
27
  nonRefundable: true,
17
- maxQuantityPerLine: 1,
18
28
  canMergeWithSameProduct: false,
19
29
  maxLinesPerOrder: PRIZE_DRAW_MAX_REVEALS_PER_ORDER,
20
30
  refundPolicy: { full: false, partial: false, partialGranularity: "none" },
21
31
  blockedPaymentMethods: PRIZE_DRAW_BLOCKED_PAYMENT_METHODS,
32
+ blockedPaymentMessage: PRIZE_DRAW_BLOCKED_PAYMENT_MESSAGE,
22
33
  splitKey: (item) => `prize-draw:${item.itemId}`,
23
34
  /**
24
35
  * Slice a high-quantity prize-draw entry into chunks of
@@ -43,13 +54,23 @@ export const prizeDrawRule = {
43
54
  }
44
55
  return chunks;
45
56
  },
46
- preflightChecks: (pairs, paymentMethod) => {
47
- if (paymentMethod && PRIZE_DRAW_BLOCKED_PAYMENT_METHODS.includes(paymentMethod)) {
48
- const hasPrizeDraw = pairs.some((p) => p.product.listingType === "prize-draw");
49
- if (hasPrizeDraw) {
50
- throw Object.assign(new ValidationError("Prize-draw entries can only be paid by manual bank/UPI transfer or PhonePe — cash on delivery and EMI are not available for this listing type."), { code: "PRIZE_DRAW_PAYMENT_METHOD_BLOCKED" });
51
- }
52
- }
57
+ preflightChecks: (pairs) => {
58
+ /*
59
+ * The payment-method check USED to live here and is now in
60
+ * `assertPaymentMethodAllowed`, which runs cart-wide before the
61
+ * transaction.
62
+ *
63
+ * 🛑 This is not a tidy-up. The copy here could only ever see
64
+ * single-member lines — `runSyncPreflight` skips a bundle or grouped line,
65
+ * and its online call site reduces each line to its FIRST member — so a
66
+ * prize-draw entry reachable through a grouped selection was never
67
+ * checked. Re-adding a copy here would be a second answer to one question
68
+ * (Root Cause #75), and the one that is easier to find is the one that
69
+ * cannot see every line.
70
+ *
71
+ * `blockedPaymentMethods` + `blockedPaymentMessage` on this rule are what
72
+ * the gate reads, so the wording is unchanged.
73
+ */
53
74
  for (const { item, product } of pairs) {
54
75
  if (product.listingType !== "prize-draw")
55
76
  continue;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Spares listing rule — one component off a disassembled item, standard-like
3
+ * checkout.
4
+ *
5
+ * No bespoke behaviour: cart/order grouping, stock decrement and refund policy
6
+ * all follow the standard defaults, and `pluginFor()` carries everything that
7
+ * differs (badge, browse route, seller form fields).
8
+ *
9
+ * 🛑 `orderType: "standard"` is deliberate rather than lazy. A new `OrderType`
10
+ * member fans out into the order-status tabs, the order filters and the
11
+ * splitter's own keys — and buys nothing here, because a spare is fulfilled,
12
+ * refunded and reported exactly like any other physical item. A buyer looking
13
+ * at their orders wants "the attack ring I bought", not a separate tab for it.
14
+ */
15
+ import type { ListingCheckoutRule } from "./types";
16
+ export declare const sparesRule: ListingCheckoutRule;
@@ -0,0 +1,5 @@
1
+ import { DEFAULT_LISTING_RULE } from "./_defaults.js";
2
+ export const sparesRule = {
3
+ ...DEFAULT_LISTING_RULE,
4
+ orderType: "standard",
5
+ };
@@ -62,8 +62,6 @@ export interface ListingCheckoutRule {
62
62
  * Set true for prize-draw entries.
63
63
  */
64
64
  nonRefundable: boolean;
65
- /** Hard qty cap per single cart line. Auction/offer/prize-draw-per-line = 1. */
66
- maxQuantityPerLine: number;
67
65
  /**
68
66
  * When false the checkout pipeline issues one order per cart line (even when
69
67
  * multiple lines share the same product). Auctions and offers set this to
@@ -83,8 +81,20 @@ export interface ListingCheckoutRule {
83
81
  * all methods allowed. Prize-draw blocks cod/cash/emi — winner assignment
84
82
  * depends on the order's payment actually being confirmed, which those
85
83
  * deferred-payment methods don't guarantee.
84
+ *
85
+ * 🛑 Enforced by `assertPaymentMethodAllowed`, NOT by `runSyncPreflight`. A
86
+ * rule may only trust preflight for a single-member line; a type that can
87
+ * appear inside a bundle or a grouped selection is skipped there entirely.
88
+ * The gate runs over every member of every line, once, before the
89
+ * transaction.
86
90
  */
87
91
  blockedPaymentMethods?: CheckoutPaymentMethod[];
92
+ /**
93
+ * The refusal, in this type's own words. Optional — the gate generates a
94
+ * correct sentence without it. Supply one when the REASON is worth saying,
95
+ * as prize-draw's does.
96
+ */
97
+ blockedPaymentMessage?: string;
88
98
  /**
89
99
  * Return the grouping key used by the order splitter. Items with the same
90
100
  * key land in the same OrderGroup and produce one order document.
@@ -150,4 +160,18 @@ export interface CategoryCheckoutRule {
150
160
  cartEligible: boolean;
151
161
  orderType: OrderType;
152
162
  requiresShippingAddress: boolean;
163
+ /**
164
+ * Payment methods this category cannot be paid with.
165
+ *
166
+ * 🛑 NOT enforced by `runSyncPreflight`. That function skips multi-member
167
+ * lines by design, and a bundle is always one — so a value here would be set,
168
+ * reviewed, and never read. `assertPaymentMethodAllowed` is what honours it,
169
+ * and it runs cart-wide before the transaction.
170
+ */
171
+ blockedPaymentMethods?: CheckoutPaymentMethod[];
172
+ /**
173
+ * The refusal, in this category's own words. Optional — the gate generates a
174
+ * correct sentence without it. Supply one when the REASON is worth saying.
175
+ */
176
+ blockedPaymentMessage?: string;
153
177
  }