@mohasinac/appkit 3.0.4 → 3.1.0

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 (320) hide show
  1. package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +42 -0
  2. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +86 -0
  3. package/dist/_internal/client/features/lottery/LotteryAdminSlotView.d.ts +14 -0
  4. package/dist/_internal/client/features/lottery/LotteryAdminSlotView.js +46 -0
  5. package/dist/_internal/client/features/lottery/LotteryDetailView.d.ts +28 -0
  6. package/dist/_internal/client/features/lottery/LotteryDetailView.js +17 -0
  7. package/dist/_internal/client/features/lottery/LotteryEntriesView.d.ts +23 -0
  8. package/dist/_internal/client/features/lottery/LotteryEntriesView.js +58 -0
  9. package/dist/_internal/client/features/lottery/LotteryListView.d.ts +21 -0
  10. package/dist/_internal/client/features/lottery/LotteryListView.js +25 -0
  11. package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +19 -0
  12. package/dist/_internal/client/features/lottery/LotteryPullForm.js +65 -0
  13. package/dist/_internal/client/features/lottery/LotterySlotGrid.d.ts +13 -0
  14. package/dist/_internal/client/features/lottery/LotterySlotGrid.js +21 -0
  15. package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.d.ts +28 -0
  16. package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.js +18 -0
  17. package/dist/_internal/server/features/auctions/__tests__/service.test.d.ts +1 -0
  18. package/dist/_internal/server/features/auctions/__tests__/service.test.js +140 -0
  19. package/dist/_internal/server/features/auctions/data.d.ts +2 -4
  20. package/dist/_internal/server/features/auctions/data.js +3 -9
  21. package/dist/_internal/server/features/checkout/__tests__/actions.test.d.ts +1 -0
  22. package/dist/_internal/server/features/checkout/__tests__/actions.test.js +600 -0
  23. package/dist/_internal/server/features/classified/adapters.d.ts +2 -15
  24. package/dist/_internal/server/features/classified/adapters.js +2 -17
  25. package/dist/_internal/server/features/classified/data.d.ts +1 -6
  26. package/dist/_internal/server/features/classified/data.js +2 -10
  27. package/dist/_internal/server/features/digital-code/adapters.d.ts +2 -15
  28. package/dist/_internal/server/features/digital-code/adapters.js +2 -17
  29. package/dist/_internal/server/features/digital-code/data.d.ts +1 -6
  30. package/dist/_internal/server/features/digital-code/data.js +2 -10
  31. package/dist/_internal/server/features/live/adapters.d.ts +2 -15
  32. package/dist/_internal/server/features/live/adapters.js +2 -17
  33. package/dist/_internal/server/features/live/data.d.ts +1 -6
  34. package/dist/_internal/server/features/live/data.js +2 -10
  35. package/dist/_internal/server/features/lottery/actions.d.ts +43 -0
  36. package/dist/_internal/server/features/lottery/actions.js +243 -0
  37. package/dist/_internal/server/features/lottery/adapters.d.ts +9 -0
  38. package/dist/_internal/server/features/lottery/adapters.js +21 -0
  39. package/dist/_internal/server/features/lottery/data.d.ts +20 -0
  40. package/dist/_internal/server/features/lottery/data.js +41 -0
  41. package/dist/_internal/server/features/lottery/metadata.d.ts +6 -0
  42. package/dist/_internal/server/features/lottery/metadata.js +31 -0
  43. package/dist/_internal/server/features/pre-orders/data.d.ts +2 -4
  44. package/dist/_internal/server/features/pre-orders/data.js +3 -9
  45. package/dist/_internal/server/features/prize-draws/actions.d.ts +2 -0
  46. package/dist/_internal/server/features/prize-draws/actions.js +22 -0
  47. package/dist/_internal/server/features/prize-draws/data.d.ts +1 -17
  48. package/dist/_internal/server/features/prize-draws/data.js +2 -20
  49. package/dist/_internal/server/features/prize-draws/index.d.ts +3 -1
  50. package/dist/_internal/server/features/prize-draws/index.js +2 -0
  51. package/dist/_internal/server/features/prize-draws/service.d.ts +3 -0
  52. package/dist/_internal/server/features/prize-draws/service.js +19 -0
  53. package/dist/_internal/server/features/refunds/__tests__/actions.test.d.ts +1 -0
  54. package/dist/_internal/server/features/refunds/__tests__/actions.test.js +263 -0
  55. package/dist/_internal/server/features/shared/listing-adapters.d.ts +19 -0
  56. package/dist/_internal/server/features/shared/listing-adapters.js +20 -0
  57. package/dist/_internal/server/features/shared/listing-data-factory.d.ts +9 -0
  58. package/dist/_internal/server/features/shared/listing-data-factory.js +33 -0
  59. package/dist/_internal/server/features/stores/data.d.ts +3 -14
  60. package/dist/_internal/server/features/stores/data.js +6 -44
  61. package/dist/_internal/server/jobs/core/__tests__/auctionSettlement.test.d.ts +1 -0
  62. package/dist/_internal/server/jobs/core/__tests__/auctionSettlement.test.js +249 -0
  63. package/dist/_internal/server/jobs/core/__tests__/onBidPlaced.test.d.ts +1 -0
  64. package/dist/_internal/server/jobs/core/__tests__/onBidPlaced.test.js +201 -0
  65. package/dist/_internal/server/jobs/core/__tests__/onOrderCreate.test.d.ts +1 -0
  66. package/dist/_internal/server/jobs/core/__tests__/onOrderCreate.test.js +169 -0
  67. package/dist/_internal/server/jobs/core/__tests__/onOrderStatusChange.test.d.ts +1 -0
  68. package/dist/_internal/server/jobs/core/__tests__/onOrderStatusChange.test.js +148 -0
  69. package/dist/_internal/server/jobs/core/__tests__/onProductStockChange.test.d.ts +1 -0
  70. package/dist/_internal/server/jobs/core/__tests__/onProductStockChange.test.js +210 -0
  71. package/dist/_internal/server/jobs/core/__tests__/onReviewWrite.test.d.ts +1 -0
  72. package/dist/_internal/server/jobs/core/__tests__/onReviewWrite.test.js +190 -0
  73. package/dist/_internal/server/jobs/core/__tests__/onUserBanChange.test.d.ts +1 -0
  74. package/dist/_internal/server/jobs/core/__tests__/onUserBanChange.test.js +161 -0
  75. package/dist/_internal/server/jobs/core/__tests__/payoutBatch.test.d.ts +1 -0
  76. package/dist/_internal/server/jobs/core/__tests__/payoutBatch.test.js +302 -0
  77. package/dist/_internal/server/jobs/core/__tests__/triggerEventRaffle.test.d.ts +1 -0
  78. package/dist/_internal/server/jobs/core/__tests__/triggerEventRaffle.test.js +252 -0
  79. package/dist/_internal/server/jobs/core/__tests__/wrapJobHandler.test.d.ts +1 -0
  80. package/dist/_internal/server/jobs/core/__tests__/wrapJobHandler.test.js +129 -0
  81. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  82. package/dist/_internal/shared/actions/action-registry.js +46 -0
  83. package/dist/_internal/shared/features/cart/schema.d.ts +8 -8
  84. package/dist/_internal/shared/features/cart/schema.js +3 -0
  85. package/dist/_internal/shared/features/products/schema.d.ts +8 -8
  86. package/dist/_internal/shared/listing-types/_registry.js +2 -0
  87. package/dist/_internal/shared/listing-types/auction/schema.d.ts +3 -1
  88. package/dist/_internal/shared/listing-types/auction/schema.js +7 -1
  89. package/dist/_internal/shared/listing-types/classified/schema.d.ts +2 -1
  90. package/dist/_internal/shared/listing-types/classified/schema.js +3 -1
  91. package/dist/_internal/shared/listing-types/digital-code/schema.d.ts +3 -1
  92. package/dist/_internal/shared/listing-types/digital-code/schema.js +7 -1
  93. package/dist/_internal/shared/listing-types/live/schema.d.ts +2 -1
  94. package/dist/_internal/shared/listing-types/live/schema.js +3 -1
  95. package/dist/_internal/shared/listing-types/pre-order/schema.d.ts +3 -1
  96. package/dist/_internal/shared/listing-types/pre-order/schema.js +7 -1
  97. package/dist/_internal/shared/listing-types/prize-draw/schema.d.ts +3 -1
  98. package/dist/_internal/shared/listing-types/prize-draw/schema.js +13 -1
  99. package/dist/_internal/shared/listing-types/standard/schema.d.ts +2 -1
  100. package/dist/_internal/shared/listing-types/standard/schema.js +4 -1
  101. package/dist/_internal/shared/types/base-document.d.ts +15 -0
  102. package/dist/_internal/shared/types/base-document.js +1 -0
  103. package/dist/_internal/shared/types/index.d.ts +2 -0
  104. package/dist/_internal/shared/types/index.js +1 -0
  105. package/dist/client.d.ts +13 -4
  106. package/dist/client.js +11 -2
  107. package/dist/constants/api-endpoints.d.ts +12 -0
  108. package/dist/constants/api-endpoints.js +4 -0
  109. package/dist/constants/field-names.d.ts +5 -1
  110. package/dist/constants/field-names.js +5 -1
  111. package/dist/features/addresses/repository/__tests__/addresses.repository.test.d.ts +1 -0
  112. package/dist/features/addresses/repository/__tests__/addresses.repository.test.js +386 -0
  113. package/dist/features/addresses/schemas/firestore.d.ts +2 -4
  114. package/dist/features/admin/api/bids/route.d.ts +2 -2
  115. package/dist/features/admin/api/coupons/route.d.ts +2 -2
  116. package/dist/features/admin/api/products/route.d.ts +2 -2
  117. package/dist/features/admin/api/reviews/route.d.ts +2 -2
  118. package/dist/features/admin/components/AdminFulfillmentView.d.ts +6 -0
  119. package/dist/features/admin/components/AdminFulfillmentView.js +107 -0
  120. package/dist/features/admin/components/AdminProductEditorView.js +21 -9
  121. package/dist/features/admin/components/AdminProductsView.js +3 -0
  122. package/dist/features/admin/components/DataListingView.d.ts +1 -0
  123. package/dist/features/admin/components/index.d.ts +2 -0
  124. package/dist/features/admin/components/index.js +1 -0
  125. package/dist/features/admin/repository/__tests__/notification.repository.test.d.ts +1 -0
  126. package/dist/features/admin/repository/__tests__/notification.repository.test.js +356 -0
  127. package/dist/features/admin/repository/__tests__/site-settings.repository.test.d.ts +1 -0
  128. package/dist/features/admin/repository/__tests__/site-settings.repository.test.js +298 -0
  129. package/dist/features/admin/schemas/firestore.d.ts +3 -8
  130. package/dist/features/auctions/repository/__tests__/bid.repository.test.d.ts +1 -0
  131. package/dist/features/auctions/repository/__tests__/bid.repository.test.js +290 -0
  132. package/dist/features/auctions/schemas/firestore.d.ts +2 -4
  133. package/dist/features/auctions/schemas/index.d.ts +2 -2
  134. package/dist/features/auth/api/route.d.ts +2 -2
  135. package/dist/features/auth/repository/__tests__/session.repository.test.d.ts +1 -0
  136. package/dist/features/auth/repository/__tests__/session.repository.test.js +310 -0
  137. package/dist/features/auth/repository/__tests__/session.test.d.ts +1 -0
  138. package/dist/features/auth/repository/__tests__/session.test.js +146 -0
  139. package/dist/features/auth/repository/__tests__/sms-counter.repository.test.d.ts +1 -0
  140. package/dist/features/auth/repository/__tests__/sms-counter.repository.test.js +171 -0
  141. package/dist/features/auth/repository/__tests__/sms-counter.test.d.ts +1 -0
  142. package/dist/features/auth/repository/__tests__/sms-counter.test.js +88 -0
  143. package/dist/features/auth/repository/__tests__/token.repository.test.d.ts +1 -0
  144. package/dist/features/auth/repository/__tests__/token.repository.test.js +260 -0
  145. package/dist/features/auth/repository/__tests__/token.test.d.ts +1 -0
  146. package/dist/features/auth/repository/__tests__/token.test.js +140 -0
  147. package/dist/features/auth/repository/__tests__/user.repository.test.d.ts +1 -0
  148. package/dist/features/auth/repository/__tests__/user.repository.test.js +352 -0
  149. package/dist/features/auth/repository/__tests__/user.test.d.ts +1 -0
  150. package/dist/features/auth/repository/__tests__/user.test.js +226 -0
  151. package/dist/features/auth/repository/session.repository.js +10 -1
  152. package/dist/features/before-after/schemas/index.d.ts +2 -2
  153. package/dist/features/blog/schemas/firestore.d.ts +2 -4
  154. package/dist/features/blog/schemas/index.d.ts +4 -4
  155. package/dist/features/cart/repository/__tests__/cart.repository.test.d.ts +1 -0
  156. package/dist/features/cart/repository/__tests__/cart.repository.test.js +371 -0
  157. package/dist/features/cart/repository/cart.repository.js +2 -2
  158. package/dist/features/cart/schemas/firestore.d.ts +2 -4
  159. package/dist/features/categories/api/[id]/route.d.ts +4 -4
  160. package/dist/features/categories/api/route.d.ts +2 -2
  161. package/dist/features/categories/repository/__tests__/categories.repository.test.d.ts +1 -0
  162. package/dist/features/categories/repository/__tests__/categories.repository.test.js +813 -0
  163. package/dist/features/categories/schemas/firestore.d.ts +2 -4
  164. package/dist/features/categories/schemas/index.d.ts +4 -4
  165. package/dist/features/events/components/EventCard.js +1 -0
  166. package/dist/features/events/repository/__tests__/event.repository.test.d.ts +1 -0
  167. package/dist/features/events/repository/__tests__/event.repository.test.js +299 -0
  168. package/dist/features/events/schemas/firestore.d.ts +5 -4
  169. package/dist/features/events/schemas/index.d.ts +836 -16
  170. package/dist/features/events/schemas/index.js +80 -1
  171. package/dist/features/events/types/index.d.ts +4 -1
  172. package/dist/features/faq/repository/__tests__/faqs.repository.test.d.ts +1 -0
  173. package/dist/features/faq/repository/__tests__/faqs.repository.test.js +311 -0
  174. package/dist/features/faq/schemas/firestore.d.ts +2 -4
  175. package/dist/features/faq/schemas/index.d.ts +2 -2
  176. package/dist/features/grouped/schemas/index.d.ts +2 -2
  177. package/dist/features/history/repository/__tests__/user-history.repository.test.d.ts +1 -0
  178. package/dist/features/history/repository/__tests__/user-history.repository.test.js +180 -0
  179. package/dist/features/homepage/api/[id]/route.d.ts +4 -4
  180. package/dist/features/homepage/api/carousel/[id]/route.d.ts +4 -4
  181. package/dist/features/homepage/api/carousel/route.d.ts +2 -2
  182. package/dist/features/homepage/api/route.d.ts +2 -2
  183. package/dist/features/homepage/repository/__tests__/carousel.repository.test.d.ts +1 -0
  184. package/dist/features/homepage/repository/__tests__/carousel.repository.test.js +182 -0
  185. package/dist/features/homepage/repository/__tests__/homepage-sections.repository.test.d.ts +1 -0
  186. package/dist/features/homepage/repository/__tests__/homepage-sections.repository.test.js +185 -0
  187. package/dist/features/homepage/schemas/firestore.d.ts +4 -12
  188. package/dist/features/homepage/schemas/index.d.ts +2 -2
  189. package/dist/features/lottery/repository/lottery-entry.repository.d.ts +33 -0
  190. package/dist/features/lottery/repository/lottery-entry.repository.js +130 -0
  191. package/dist/features/lottery/schemas/firestore.d.ts +49 -0
  192. package/dist/features/lottery/schemas/firestore.js +25 -0
  193. package/dist/features/lottery/schemas/zod.d.ts +113 -0
  194. package/dist/features/lottery/schemas/zod.js +39 -0
  195. package/dist/features/lottery/types/index.d.ts +67 -0
  196. package/dist/features/lottery/types/index.js +55 -0
  197. package/dist/features/messages/repository/__tests__/conversations.repository.test.d.ts +1 -0
  198. package/dist/features/messages/repository/__tests__/conversations.repository.test.js +257 -0
  199. package/dist/features/messages/schemas/firestore.d.ts +2 -4
  200. package/dist/features/messages/schemas/index.d.ts +2 -2
  201. package/dist/features/orders/repository/__tests__/orders.repository.test.d.ts +1 -0
  202. package/dist/features/orders/repository/__tests__/orders.repository.test.js +211 -0
  203. package/dist/features/orders/repository/orders.repository.d.ts +5 -0
  204. package/dist/features/orders/repository/orders.repository.js +26 -0
  205. package/dist/features/orders/schemas/firestore.d.ts +9 -5
  206. package/dist/features/orders/schemas/firestore.js +3 -0
  207. package/dist/features/payments/repository/__tests__/payout.repository.test.d.ts +1 -0
  208. package/dist/features/payments/repository/__tests__/payout.repository.test.js +292 -0
  209. package/dist/features/payments/schemas/firestore.d.ts +2 -4
  210. package/dist/features/pre-orders/schemas/index.d.ts +2 -2
  211. package/dist/features/products/api/[id]/route.d.ts +4 -4
  212. package/dist/features/products/api/route.d.ts +2 -2
  213. package/dist/features/products/constants/action-defs.d.ts +16 -0
  214. package/dist/features/products/constants/action-defs.js +16 -0
  215. package/dist/features/products/constants/listing-tabs.d.ts +48 -0
  216. package/dist/features/products/constants/listing-tabs.js +12 -0
  217. package/dist/features/products/repository/__tests__/product-features.repository.test.d.ts +1 -0
  218. package/dist/features/products/repository/__tests__/product-features.repository.test.js +329 -0
  219. package/dist/features/products/repository/__tests__/products.repository.test.d.ts +1 -0
  220. package/dist/features/products/repository/__tests__/products.repository.test.js +607 -0
  221. package/dist/features/products/repository/products.repository.d.ts +37 -0
  222. package/dist/features/products/repository/products.repository.js +28 -1
  223. package/dist/features/products/schemas/firestore.d.ts +23 -7
  224. package/dist/features/products/schemas/firestore.js +2 -0
  225. package/dist/features/products/schemas/index.d.ts +18 -18
  226. package/dist/features/products/schemas/index.js +4 -2
  227. package/dist/features/products/types/index.d.ts +3 -1
  228. package/dist/features/promotions/repository/__tests__/claimed-coupons.repository.test.d.ts +1 -0
  229. package/dist/features/promotions/repository/__tests__/claimed-coupons.repository.test.js +219 -0
  230. package/dist/features/promotions/repository/__tests__/coupons.repository.test.d.ts +1 -0
  231. package/dist/features/promotions/repository/__tests__/coupons.repository.test.js +480 -0
  232. package/dist/features/promotions/schemas/firestore.d.ts +2 -4
  233. package/dist/features/promotions/schemas/index.d.ts +4 -4
  234. package/dist/features/reviews/api/[id]/route.d.ts +4 -4
  235. package/dist/features/reviews/api/route.d.ts +2 -2
  236. package/dist/features/reviews/repository/__tests__/reviews.repository.test.d.ts +1 -0
  237. package/dist/features/reviews/repository/__tests__/reviews.repository.test.js +327 -0
  238. package/dist/features/reviews/schemas/firestore.d.ts +2 -4
  239. package/dist/features/reviews/schemas/index.d.ts +2 -2
  240. package/dist/features/scams/repository/__tests__/scammer.repository.test.d.ts +1 -0
  241. package/dist/features/scams/repository/__tests__/scammer.repository.test.js +385 -0
  242. package/dist/features/scams/schemas/firestore.d.ts +2 -4
  243. package/dist/features/scams/schemas/index.d.ts +4 -4
  244. package/dist/features/search/repository/__tests__/search.repository.test.d.ts +1 -0
  245. package/dist/features/search/repository/__tests__/search.repository.test.js +369 -0
  246. package/dist/features/search/schemas/index.d.ts +2 -2
  247. package/dist/features/seller/api/coupons/route.d.ts +2 -2
  248. package/dist/features/seller/api/offers/route.d.ts +2 -2
  249. package/dist/features/seller/api/products/route.d.ts +2 -2
  250. package/dist/features/seller/api/store/route.d.ts +2 -2
  251. package/dist/features/seller/components/BarcodeField.d.ts +17 -0
  252. package/dist/features/seller/components/BarcodeField.js +30 -0
  253. package/dist/features/seller/components/FulfillmentView.d.ts +5 -0
  254. package/dist/features/seller/components/FulfillmentView.js +120 -0
  255. package/dist/features/seller/components/PrintCenterView.d.ts +3 -1
  256. package/dist/features/seller/components/PrintCenterView.js +52 -4
  257. package/dist/features/seller/components/SellerProductShell.d.ts +2 -0
  258. package/dist/features/seller/components/SellerProductShell.js +2 -1
  259. package/dist/features/seller/components/index.d.ts +4 -0
  260. package/dist/features/seller/components/index.js +2 -0
  261. package/dist/features/seller/repository/__tests__/offer.repository.test.d.ts +1 -0
  262. package/dist/features/seller/repository/__tests__/offer.repository.test.js +458 -0
  263. package/dist/features/seller/schemas/index.d.ts +2 -2
  264. package/dist/features/store-extensions/schemas/firestore.d.ts +12 -44
  265. package/dist/features/stores/components/StoreClassifiedsListing.d.ts +5 -0
  266. package/dist/features/stores/components/StoreClassifiedsListing.js +48 -0
  267. package/dist/features/stores/components/StoreClassifiedsPageView.d.ts +4 -0
  268. package/dist/features/stores/components/StoreClassifiedsPageView.js +20 -0
  269. package/dist/features/stores/components/StoreDetailLayoutView.js +23 -2
  270. package/dist/features/stores/components/StoreDigitalCodesListing.d.ts +5 -0
  271. package/dist/features/stores/components/StoreDigitalCodesListing.js +48 -0
  272. package/dist/features/stores/components/StoreDigitalCodesPageView.d.ts +4 -0
  273. package/dist/features/stores/components/StoreDigitalCodesPageView.js +20 -0
  274. package/dist/features/stores/components/StoreLiveItemsListing.d.ts +5 -0
  275. package/dist/features/stores/components/StoreLiveItemsListing.js +48 -0
  276. package/dist/features/stores/components/StoreLiveItemsPageView.d.ts +4 -0
  277. package/dist/features/stores/components/StoreLiveItemsPageView.js +20 -0
  278. package/dist/features/stores/components/index.d.ts +9 -0
  279. package/dist/features/stores/components/index.js +3 -0
  280. package/dist/features/stores/repository/__tests__/store.repository.test.d.ts +1 -0
  281. package/dist/features/stores/repository/__tests__/store.repository.test.js +261 -0
  282. package/dist/features/stores/schemas/firestore.d.ts +2 -4
  283. package/dist/features/stores/schemas/index.d.ts +4 -4
  284. package/dist/features/support/repository/__tests__/support.repository.test.d.ts +1 -0
  285. package/dist/features/support/repository/__tests__/support.repository.test.js +312 -0
  286. package/dist/features/support/schemas/firestore.d.ts +2 -4
  287. package/dist/features/support/schemas/index.d.ts +2 -2
  288. package/dist/features/wishlist/repository/__tests__/user-wishlist.repository.test.d.ts +1 -0
  289. package/dist/features/wishlist/repository/__tests__/user-wishlist.repository.test.js +139 -0
  290. package/dist/index.d.ts +14 -0
  291. package/dist/index.js +17 -0
  292. package/dist/next/api/routeHandler.d.ts +1 -1
  293. package/dist/next/routing/route-map.d.ts +24 -0
  294. package/dist/next/routing/route-map.js +11 -0
  295. package/dist/repositories/index.d.ts +1 -0
  296. package/dist/repositories/index.js +2 -0
  297. package/dist/schemas/registry.d.ts +24 -24
  298. package/dist/security/index.d.ts +1 -1
  299. package/dist/security/index.js +1 -1
  300. package/dist/security/pii-schemas.d.ts +2 -0
  301. package/dist/security/pii-schemas.js +6 -0
  302. package/dist/seed/actions/demo-seed-actions.d.ts +1 -1
  303. package/dist/seed/events-seed-data.js +85 -2
  304. package/dist/seed/grouped-listings-seed-data.d.ts +5 -2
  305. package/dist/seed/index.d.ts +1 -0
  306. package/dist/seed/index.js +1 -0
  307. package/dist/seed/lottery-entries-seed-data.d.ts +2 -0
  308. package/dist/seed/lottery-entries-seed-data.js +108 -0
  309. package/dist/seed/manifest.js +1 -0
  310. package/dist/seed/products-preorders-seed-data.d.ts +5 -2
  311. package/dist/seed/products-standard-seed-data.js +8 -0
  312. package/dist/server-entry.d.ts +3 -0
  313. package/dist/server-entry.js +3 -0
  314. package/dist/server.d.ts +10 -4
  315. package/dist/server.js +6 -0
  316. package/dist/styles.css +1 -1
  317. package/dist/tailwind-utilities.css +1 -1
  318. package/dist/utils/id-generators.d.ts +8 -0
  319. package/dist/utils/id-generators.js +14 -0
  320. package/package.json +2 -1
@@ -0,0 +1,607 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { makeMockDb, makeSnap, makeQuerySnap } from "../../../../../tests/helpers/mock-firestore";
3
+ const { db, mockDocRef, mockCollection, mockQuery, mockBatch } = makeMockDb();
4
+ const { mockCacheManager } = vi.hoisted(() => ({
5
+ mockCacheManager: {
6
+ get: vi.fn().mockReturnValue(null),
7
+ set: vi.fn(),
8
+ delete: vi.fn(),
9
+ },
10
+ }));
11
+ vi.mock("../../../../providers/db-firebase/admin", () => ({
12
+ getAdminDb: () => db,
13
+ }));
14
+ vi.mock("../../../../providers/db-firebase", async (importOriginal) => {
15
+ const actual = await importOriginal();
16
+ return {
17
+ ...actual,
18
+ prepareForFirestore: (d) => d,
19
+ };
20
+ });
21
+ vi.mock("../../../../contracts/field-ops", () => ({
22
+ serverTimestamp: () => new Date(),
23
+ increment: (n) => n,
24
+ arrayUnion: (...args) => args,
25
+ arrayRemove: (...args) => args,
26
+ deleteField: () => null,
27
+ registerFieldOps: vi.fn(),
28
+ }));
29
+ vi.mock("../../../../core", () => ({
30
+ cacheManager: mockCacheManager,
31
+ }));
32
+ vi.mock("../../../../errors/normalize", () => ({ normalizeError: vi.fn() }));
33
+ vi.mock("../../../../monitoring", () => ({
34
+ serverLogger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
35
+ }));
36
+ // buildSearchTokens returns an array of tokens — mock as passthrough
37
+ vi.mock("../../../../utils", () => ({
38
+ generateUniqueId: vi.fn(async (gen, exists) => {
39
+ const candidate = gen(0);
40
+ const taken = await exists(candidate);
41
+ return taken ? gen(1) : candidate;
42
+ }),
43
+ slugify: (s) => s.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, ""),
44
+ buildSearchTokens: (...args) => args.flat().filter(Boolean),
45
+ tokenizeQuery: (q) => q.trim() ? q.trim().toLowerCase().split(/\s+/) : [],
46
+ generateBarcodeId: (id) => Promise.resolve(`barcode-${id}`),
47
+ }));
48
+ import { ProductRepository } from "../products.repository";
49
+ const repo = new ProductRepository();
50
+ function makeProductDoc(overrides = {}) {
51
+ return {
52
+ id: "product-charizard-psa9",
53
+ slug: "product-charizard-psa9",
54
+ title: "Charizard PSA 9",
55
+ description: "1st Edition base set Charizard PSA 9",
56
+ brand: "Pokemon",
57
+ brandSlug: "brand-pokemon",
58
+ storeId: "store-pokemon-palace",
59
+ categorySlug: "category-trading-cards",
60
+ categorySlugs: ["category-trading-cards", "category-pokemon"],
61
+ listingType: "standard",
62
+ status: "published",
63
+ price: 50000,
64
+ currency: "INR",
65
+ stockQuantity: 1,
66
+ availableQuantity: 1,
67
+ currentBid: 0,
68
+ bidCount: 0,
69
+ viewCount: 0,
70
+ searchTokens: ["charizard", "psa9"],
71
+ barcodeId: "barcode-product-charizard-psa9",
72
+ images: [],
73
+ tags: [],
74
+ features: [],
75
+ createdAt: new Date(),
76
+ updatedAt: new Date(),
77
+ ...overrides,
78
+ };
79
+ }
80
+ beforeEach(() => {
81
+ vi.clearAllMocks();
82
+ mockCacheManager.get.mockReturnValue(null);
83
+ db.collection.mockReturnValue(mockCollection);
84
+ mockCollection.doc.mockReturnValue(mockDocRef);
85
+ mockCollection.where.mockReturnValue(mockQuery);
86
+ mockCollection.get = vi.fn().mockResolvedValue(makeQuerySnap([]));
87
+ mockQuery.where.mockReturnValue(mockQuery);
88
+ mockQuery.orderBy.mockReturnValue(mockQuery);
89
+ mockQuery.limit.mockReturnValue(mockQuery);
90
+ mockQuery.offset = vi.fn().mockReturnValue(mockQuery);
91
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
92
+ mockQuery.count.mockReturnValue({
93
+ get: vi.fn().mockResolvedValue({ data: () => ({ count: 0 }) }),
94
+ });
95
+ mockDocRef.get.mockResolvedValue(makeSnap(makeProductDoc(), "product-charizard-psa9"));
96
+ mockDocRef.set.mockResolvedValue(undefined);
97
+ mockDocRef.update.mockResolvedValue(undefined);
98
+ mockDocRef.delete.mockResolvedValue(undefined);
99
+ db.batch.mockReturnValue(mockBatch);
100
+ db.getAll = vi.fn().mockResolvedValue([]);
101
+ });
102
+ // ---------------------------------------------------------------------------
103
+ // findById — caching
104
+ // ---------------------------------------------------------------------------
105
+ describe("ProductRepository.findById — caching", () => {
106
+ it("returns cached product without hitting Firestore on warm cache", async () => {
107
+ const cached = makeProductDoc();
108
+ mockCacheManager.get.mockReturnValue(cached);
109
+ const result = await repo.findById("product-charizard-psa9");
110
+ expect(result?.id).toBe("product-charizard-psa9");
111
+ expect(mockDocRef.get).not.toHaveBeenCalled();
112
+ });
113
+ it("fetches from Firestore on cache miss and caches result", async () => {
114
+ mockCacheManager.get.mockReturnValue(null);
115
+ const product = makeProductDoc();
116
+ mockDocRef.get.mockResolvedValue(makeSnap(product, "product-charizard-psa9"));
117
+ await repo.findById("product-charizard-psa9");
118
+ expect(mockDocRef.get).toHaveBeenCalledOnce();
119
+ expect(mockCacheManager.set).toHaveBeenCalledWith(expect.stringContaining("product-charizard-psa9"), expect.any(Object), expect.objectContaining({ ttl: 30000 }));
120
+ });
121
+ it("returns null when product does not exist (Firestore miss)", async () => {
122
+ mockCacheManager.get.mockReturnValue(null);
123
+ mockDocRef.get.mockResolvedValue(makeSnap(null));
124
+ const result = await repo.findById("nonexistent");
125
+ expect(result).toBeNull();
126
+ });
127
+ it("does NOT cache null results (product not found)", async () => {
128
+ mockCacheManager.get.mockReturnValue(null);
129
+ mockDocRef.get.mockResolvedValue(makeSnap(null));
130
+ await repo.findById("nonexistent");
131
+ expect(mockCacheManager.set).not.toHaveBeenCalled();
132
+ });
133
+ });
134
+ // ---------------------------------------------------------------------------
135
+ // delete — cache invalidation
136
+ // ---------------------------------------------------------------------------
137
+ describe("ProductRepository.delete — cache invalidation", () => {
138
+ it("invalidates cache entry before deleting", async () => {
139
+ await repo.delete("product-charizard-psa9");
140
+ expect(mockCacheManager.delete).toHaveBeenCalledWith(expect.stringContaining("product-charizard-psa9"));
141
+ // delete happens before or alongside cache invalidation
142
+ expect(mockDocRef.delete).toHaveBeenCalledOnce();
143
+ });
144
+ it("deletes the Firestore document", async () => {
145
+ await repo.delete("product-charizard-psa9");
146
+ expect(mockDocRef.delete).toHaveBeenCalledOnce();
147
+ });
148
+ });
149
+ // ---------------------------------------------------------------------------
150
+ // create
151
+ // ---------------------------------------------------------------------------
152
+ describe("ProductRepository.create", () => {
153
+ beforeEach(() => {
154
+ // After doc.set, findById is called to re-read — return the stored product
155
+ mockDocRef.get.mockResolvedValue(makeSnap(null));
156
+ });
157
+ it("uses title to generate the slug when no explicit slug provided", async () => {
158
+ await repo.create({
159
+ title: "Hot Wheels Redline Vintage",
160
+ storeId: "store-diecast-depot",
161
+ price: 5000,
162
+ currency: "INR",
163
+ stockQuantity: 3,
164
+ listingType: "standard",
165
+ });
166
+ // generateUniqueId calls gen(0) = slugify("Hot Wheels Redline Vintage")
167
+ expect(mockCollection.doc).toHaveBeenCalledWith("hot-wheels-redline-vintage");
168
+ });
169
+ it("uses explicit slug when provided", async () => {
170
+ await repo.create({
171
+ title: "Anything",
172
+ slug: "product-custom-slug",
173
+ storeId: "store-1",
174
+ price: 1000,
175
+ currency: "INR",
176
+ stockQuantity: 1,
177
+ listingType: "standard",
178
+ });
179
+ expect(mockCollection.doc).toHaveBeenCalledWith("product-custom-slug");
180
+ });
181
+ it("builds and stores searchTokens", async () => {
182
+ await repo.create({
183
+ title: "Charizard",
184
+ storeId: "store-1",
185
+ price: 1000,
186
+ currency: "INR",
187
+ stockQuantity: 1,
188
+ listingType: "standard",
189
+ });
190
+ const setArg = mockDocRef.set.mock.calls[0][0];
191
+ expect(Array.isArray(setArg.searchTokens)).toBe(true);
192
+ });
193
+ it("sets availableQuantity from stockQuantity", async () => {
194
+ await repo.create({
195
+ title: "Test Product",
196
+ storeId: "store-1",
197
+ price: 1000,
198
+ currency: "INR",
199
+ stockQuantity: 5,
200
+ listingType: "standard",
201
+ });
202
+ const setArg = mockDocRef.set.mock.calls[0][0];
203
+ expect(setArg.availableQuantity).toBe(5);
204
+ });
205
+ it("generates barcodeId when not provided", async () => {
206
+ await repo.create({
207
+ title: "New Product",
208
+ storeId: "store-1",
209
+ price: 1000,
210
+ currency: "INR",
211
+ stockQuantity: 1,
212
+ listingType: "standard",
213
+ });
214
+ const setArg = mockDocRef.set.mock.calls[0][0];
215
+ expect(setArg.barcodeId).toBeDefined();
216
+ expect(typeof setArg.barcodeId).toBe("string");
217
+ });
218
+ it("uses provided barcodeId when given", async () => {
219
+ await repo.create({
220
+ title: "New Product",
221
+ barcodeId: "custom-barcode-123",
222
+ storeId: "store-1",
223
+ price: 1000,
224
+ currency: "INR",
225
+ stockQuantity: 1,
226
+ listingType: "standard",
227
+ });
228
+ const setArg = mockDocRef.set.mock.calls[0][0];
229
+ expect(setArg.barcodeId).toBe("custom-barcode-123");
230
+ });
231
+ it("caches the newly created product", async () => {
232
+ await repo.create({
233
+ title: "Cache Me",
234
+ storeId: "store-1",
235
+ price: 1000,
236
+ currency: "INR",
237
+ stockQuantity: 1,
238
+ listingType: "standard",
239
+ });
240
+ expect(mockCacheManager.set).toHaveBeenCalled();
241
+ });
242
+ it("sets createdAt and updatedAt timestamps", async () => {
243
+ await repo.create({
244
+ title: "New Product",
245
+ storeId: "store-1",
246
+ price: 1000,
247
+ currency: "INR",
248
+ stockQuantity: 1,
249
+ listingType: "standard",
250
+ });
251
+ const setArg = mockDocRef.set.mock.calls[0][0];
252
+ expect(setArg.createdAt).toBeInstanceOf(Date);
253
+ expect(setArg.updatedAt).toBeInstanceOf(Date);
254
+ });
255
+ });
256
+ // ---------------------------------------------------------------------------
257
+ // updateBid
258
+ // ---------------------------------------------------------------------------
259
+ describe("ProductRepository.updateBid", () => {
260
+ it("updates currentBid field", async () => {
261
+ const product = makeProductDoc();
262
+ mockDocRef.get.mockResolvedValue(makeSnap(product, product.id));
263
+ await repo.updateBid("product-charizard-psa9", 75000, 5);
264
+ const updateArg = mockDocRef.update.mock.calls[0][0];
265
+ expect(updateArg.currentBid).toBe(75000);
266
+ });
267
+ it("updates bidCount field", async () => {
268
+ const product = makeProductDoc();
269
+ mockDocRef.get.mockResolvedValue(makeSnap(product, product.id));
270
+ await repo.updateBid("product-charizard-psa9", 75000, 5);
271
+ const updateArg = mockDocRef.update.mock.calls[0][0];
272
+ expect(updateArg.bidCount).toBe(5);
273
+ });
274
+ });
275
+ // ---------------------------------------------------------------------------
276
+ // startGroup
277
+ // ---------------------------------------------------------------------------
278
+ describe("ProductRepository.startGroup", () => {
279
+ it("sets groupId, isGroupParent: true, and groupChildSlugs: []", async () => {
280
+ await repo.startGroup("product-charizard-psa9", "group-pokemon-starter-bundle");
281
+ expect(mockDocRef.update).toHaveBeenCalledWith({
282
+ groupId: "group-pokemon-starter-bundle",
283
+ isGroupParent: true,
284
+ groupChildSlugs: [],
285
+ });
286
+ });
287
+ it("calls doc(productId) to target the correct product", async () => {
288
+ await repo.startGroup("product-abc", "group-xyz");
289
+ expect(mockCollection.doc).toHaveBeenCalledWith("product-abc");
290
+ });
291
+ });
292
+ // ---------------------------------------------------------------------------
293
+ // dissolveGroup
294
+ // ---------------------------------------------------------------------------
295
+ describe("ProductRepository.dissolveGroup", () => {
296
+ it("clears groupId and group fields from all members in a batch", async () => {
297
+ const members = [
298
+ makeProductDoc({ id: "product-1", groupId: "group-1" }),
299
+ makeProductDoc({ id: "product-2", groupId: "group-1" }),
300
+ ];
301
+ mockQuery.get.mockResolvedValue(makeQuerySnap(members.map((m) => ({ id: m.id, data: m }))));
302
+ await repo.dissolveGroup("group-1");
303
+ expect(mockBatch.update).toHaveBeenCalledTimes(2);
304
+ const calls = mockBatch.update.mock.calls;
305
+ expect(calls.every((c) => c[1].groupId === null)).toBe(true);
306
+ expect(mockBatch.commit).toHaveBeenCalledOnce();
307
+ });
308
+ it("does not call batch.update when group has no members", async () => {
309
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
310
+ await repo.dissolveGroup("group-empty");
311
+ expect(mockBatch.update).not.toHaveBeenCalled();
312
+ expect(mockBatch.commit).toHaveBeenCalledOnce();
313
+ });
314
+ it("clears all group fields (isGroupParent, groupParentSlug, groupChildSlugs, groupTitle)", async () => {
315
+ const parent = makeProductDoc({ id: "p-1", groupId: "group-1", isGroupParent: true });
316
+ mockQuery.get.mockResolvedValue(makeQuerySnap([{ id: "p-1", data: parent }]));
317
+ await repo.dissolveGroup("group-1");
318
+ const updateArg = mockBatch.update.mock.calls[0][1];
319
+ expect(updateArg.isGroupParent).toBeNull();
320
+ expect(updateArg.groupParentSlug).toBeNull();
321
+ expect(updateArg.groupChildSlugs).toBeNull();
322
+ expect(updateArg.groupTitle).toBeNull();
323
+ });
324
+ });
325
+ // ---------------------------------------------------------------------------
326
+ // linkChildToGroup
327
+ // ---------------------------------------------------------------------------
328
+ describe("ProductRepository.linkChildToGroup", () => {
329
+ it("batch-updates child with groupId, groupParentSlug", async () => {
330
+ const parent = makeProductDoc({
331
+ id: "product-parent",
332
+ groupId: "group-1",
333
+ groupChildSlugs: [],
334
+ slug: "product-parent",
335
+ });
336
+ const child = makeProductDoc({ id: "product-child", slug: "product-child" });
337
+ await repo.linkChildToGroup(parent, child);
338
+ const calls = mockBatch.update.mock.calls;
339
+ const childUpdate = calls.find((c) => c[1].groupId === "group-1");
340
+ expect(childUpdate).toBeDefined();
341
+ expect(childUpdate[1].groupParentSlug).toBe("product-parent");
342
+ expect(mockBatch.commit).toHaveBeenCalledOnce();
343
+ });
344
+ it("appends child id to parent's groupChildSlugs", async () => {
345
+ const parent = makeProductDoc({
346
+ id: "product-parent",
347
+ groupId: "group-1",
348
+ groupChildSlugs: ["product-existing"],
349
+ slug: "product-parent",
350
+ });
351
+ const child = makeProductDoc({ id: "product-new-child", slug: "product-new-child" });
352
+ await repo.linkChildToGroup(parent, child);
353
+ const calls = mockBatch.update.mock.calls;
354
+ const parentUpdate = calls.find((c) => Array.isArray(c[1].groupChildSlugs));
355
+ expect(parentUpdate[1].groupChildSlugs).toContain("product-new-child");
356
+ expect(parentUpdate[1].groupChildSlugs).toContain("product-existing");
357
+ });
358
+ });
359
+ // ---------------------------------------------------------------------------
360
+ // unlinkChildFromGroup
361
+ // ---------------------------------------------------------------------------
362
+ describe("ProductRepository.unlinkChildFromGroup", () => {
363
+ it("clears child's groupId and groupParentSlug", async () => {
364
+ const parent = makeProductDoc({ id: "p-parent", groupChildSlugs: ["p-child"], slug: "p-parent" });
365
+ const child = makeProductDoc({ id: "p-child", groupId: "group-1", groupParentSlug: "p-parent" });
366
+ await repo.unlinkChildFromGroup(parent, child);
367
+ const calls = mockBatch.update.mock.calls;
368
+ const childUpdate = calls.find((c) => c[1].groupId === null);
369
+ expect(childUpdate).toBeDefined();
370
+ expect(childUpdate[1].groupParentSlug).toBeNull();
371
+ expect(mockBatch.commit).toHaveBeenCalledOnce();
372
+ });
373
+ it("removes child from parent's groupChildSlugs", async () => {
374
+ const parent = makeProductDoc({
375
+ id: "p-parent",
376
+ groupChildSlugs: ["p-child", "p-other"],
377
+ slug: "p-parent",
378
+ });
379
+ const child = makeProductDoc({ id: "p-child", slug: "p-child" });
380
+ await repo.unlinkChildFromGroup(parent, child);
381
+ const calls = mockBatch.update.mock.calls;
382
+ const parentUpdate = calls.find((c) => Array.isArray(c[1].groupChildSlugs));
383
+ expect(parentUpdate[1].groupChildSlugs).not.toContain("p-child");
384
+ expect(parentUpdate[1].groupChildSlugs).toContain("p-other");
385
+ });
386
+ });
387
+ // ---------------------------------------------------------------------------
388
+ // getExpiredAuctions
389
+ // ---------------------------------------------------------------------------
390
+ describe("ProductRepository.getExpiredAuctions", () => {
391
+ it("queries listingType==auction, auctionEndDate < now, status==published", async () => {
392
+ const now = new Date();
393
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
394
+ await repo.getExpiredAuctions(now);
395
+ expect(mockCollection.where).toHaveBeenCalledWith(expect.stringContaining("listingType"), "==", "auction");
396
+ expect(mockQuery.where).toHaveBeenCalledWith(expect.stringContaining("auctionEndDate"), "<", now);
397
+ expect(mockQuery.where).toHaveBeenCalledWith(expect.stringContaining("status"), "==", "published");
398
+ });
399
+ it("limits to 500 documents", async () => {
400
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
401
+ await repo.getExpiredAuctions(new Date());
402
+ expect(mockQuery.limit).toHaveBeenCalledWith(500);
403
+ });
404
+ it("returns { id, ref, data } tuples", async () => {
405
+ const auction = makeProductDoc({ id: "auction-expired", listingType: "auction" });
406
+ mockQuery.get.mockResolvedValue(makeQuerySnap([{ id: "auction-expired", data: auction }]));
407
+ const result = await repo.getExpiredAuctions(new Date());
408
+ expect(result).toHaveLength(1);
409
+ expect(result[0].id).toBe("auction-expired");
410
+ expect(result[0].data).toBeDefined();
411
+ expect(result[0].ref).toBeDefined();
412
+ });
413
+ it("returns empty array when no expired auctions", async () => {
414
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
415
+ const result = await repo.getExpiredAuctions(new Date());
416
+ expect(result).toEqual([]);
417
+ });
418
+ });
419
+ // ---------------------------------------------------------------------------
420
+ // updateStatusInBatch
421
+ // ---------------------------------------------------------------------------
422
+ describe("ProductRepository.updateStatusInBatch", () => {
423
+ it("stages status update in the caller's batch without committing", () => {
424
+ const callerBatch = { update: vi.fn(), commit: vi.fn() };
425
+ repo.updateStatusInBatch(callerBatch, "product-1", "draft");
426
+ expect(callerBatch.update).toHaveBeenCalledOnce();
427
+ expect(callerBatch.update).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({ status: "draft" }));
428
+ expect(callerBatch.commit).not.toHaveBeenCalled();
429
+ });
430
+ it("sets updatedAt timestamp in the staged update", () => {
431
+ const callerBatch = { update: vi.fn(), commit: vi.fn() };
432
+ repo.updateStatusInBatch(callerBatch, "product-1", "suspended");
433
+ expect(callerBatch.update).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({ updatedAt: expect.any(Date) }));
434
+ });
435
+ });
436
+ // ---------------------------------------------------------------------------
437
+ // incrementBidCountInBatch
438
+ // ---------------------------------------------------------------------------
439
+ describe("ProductRepository.incrementBidCountInBatch", () => {
440
+ it("stages bid count update without committing", () => {
441
+ const callerBatch = { update: vi.fn(), commit: vi.fn() };
442
+ repo.incrementBidCountInBatch(callerBatch, "auction-1", 5000);
443
+ expect(callerBatch.update).toHaveBeenCalledOnce();
444
+ expect(callerBatch.commit).not.toHaveBeenCalled();
445
+ });
446
+ it("sets currentBid to the provided amount", () => {
447
+ const callerBatch = { update: vi.fn(), commit: vi.fn() };
448
+ repo.incrementBidCountInBatch(callerBatch, "auction-1", 7500);
449
+ expect(callerBatch.update).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({ currentBid: 7500 }));
450
+ });
451
+ it("sets bidsHaveStarted: true on first bid", () => {
452
+ const callerBatch = { update: vi.fn(), commit: vi.fn() };
453
+ repo.incrementBidCountInBatch(callerBatch, "auction-1", 1000);
454
+ expect(callerBatch.update).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({ bidsHaveStarted: true }));
455
+ });
456
+ it("includes leadingBidderId when provided", () => {
457
+ const callerBatch = { update: vi.fn(), commit: vi.fn() };
458
+ repo.incrementBidCountInBatch(callerBatch, "auction-1", 5000, "user-ravi");
459
+ expect(callerBatch.update).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({ leadingBidderId: "user-ravi" }));
460
+ });
461
+ it("does NOT include leadingBidderId when not provided", () => {
462
+ const callerBatch = { update: vi.fn(), commit: vi.fn() };
463
+ repo.incrementBidCountInBatch(callerBatch, "auction-1", 5000);
464
+ const updateArg = callerBatch.update.mock.calls[0][1];
465
+ expect(updateArg).not.toHaveProperty("leadingBidderId");
466
+ });
467
+ });
468
+ // ---------------------------------------------------------------------------
469
+ // FILTER_ALIASES — pure function tests
470
+ // ---------------------------------------------------------------------------
471
+ describe("ProductRepository.FILTER_ALIASES — listingType", () => {
472
+ const aliases = ProductRepository.FILTER_ALIASES;
473
+ it("canonical 'auction' → listingType==auction", () => {
474
+ expect(aliases.listingType("auction", "==")).toContain("auction");
475
+ });
476
+ it("legacy 'preorder' → normalises to pre-order", () => {
477
+ const result = aliases.listingType("preorder", "==");
478
+ expect(result).toContain("pre-order");
479
+ });
480
+ it("legacy 'product' → normalises to standard", () => {
481
+ const result = aliases.listingType("product", "==");
482
+ expect(result).toContain("standard");
483
+ });
484
+ it("'standard' → listingType==standard", () => {
485
+ const result = aliases.listingType("standard", "==");
486
+ expect(result).toContain("standard");
487
+ });
488
+ it("unknown value → returns empty string (dropped)", () => {
489
+ expect(aliases.listingType("unknown-type", "==")).toBe("");
490
+ });
491
+ it("!= operator produces listingType!=<value>", () => {
492
+ const result = aliases.listingType("auction", "!=");
493
+ expect(result).toContain("!=");
494
+ expect(result).toContain("auction");
495
+ });
496
+ it("unsupported operator → returns empty string", () => {
497
+ expect(aliases.listingType("auction", ">")).toBe("");
498
+ });
499
+ });
500
+ describe("ProductRepository.FILTER_ALIASES — scope", () => {
501
+ const aliases = ProductRepository.FILTER_ALIASES;
502
+ it("scope==publicProducts → status==published,listingType==standard", () => {
503
+ const result = aliases.scope("publicProducts", "==");
504
+ expect(result).toContain("status");
505
+ expect(result).toContain("published");
506
+ expect(result).toContain("standard");
507
+ });
508
+ it("scope==publicAuctions → status==published,listingType==auction", () => {
509
+ const result = aliases.scope("publicAuctions", "==");
510
+ expect(result).toContain("published");
511
+ expect(result).toContain("auction");
512
+ });
513
+ it("scope==published → status==published only", () => {
514
+ const result = aliases.scope("published", "==");
515
+ expect(result).toContain("published");
516
+ expect(result).not.toContain("listingType");
517
+ });
518
+ it("scope with != operator → returns empty string", () => {
519
+ expect(aliases.scope("published", "!=")).toBe("");
520
+ });
521
+ it("unknown scope value → returns empty string", () => {
522
+ expect(aliases.scope("someUnknownScope", "==")).toBe("");
523
+ });
524
+ });
525
+ describe("ProductRepository.FILTER_ALIASES — other aliases", () => {
526
+ const aliases = ProductRepository.FILTER_ALIASES;
527
+ it("promoted==true → status==published,isPromoted==true", () => {
528
+ const result = aliases.promoted("true", "==");
529
+ expect(result).toContain("published");
530
+ expect(result).toContain("isPromoted");
531
+ });
532
+ it("promoted==false → returns empty string (not supported)", () => {
533
+ expect(aliases.promoted("false", "==")).toBe("");
534
+ });
535
+ it("featuredPublic==true → status==published,isFeatured==true", () => {
536
+ const result = aliases.featuredPublic("true", "==");
537
+ expect(result).toContain("published");
538
+ expect(result).toContain("featured");
539
+ });
540
+ it("category==slug → expands to categorySlugs@=slug (array-contains)", () => {
541
+ const result = aliases.category("category-trading-cards", "==");
542
+ expect(result).toContain("categorySlugs");
543
+ expect(result).toContain("category-trading-cards");
544
+ expect(result).toContain("@=");
545
+ });
546
+ });
547
+ // ---------------------------------------------------------------------------
548
+ // findByCategory
549
+ // ---------------------------------------------------------------------------
550
+ describe("ProductRepository.findByCategory", () => {
551
+ it("queries categorySlugs array-contains the category slug", async () => {
552
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
553
+ await repo.findByCategory("category-trading-cards");
554
+ expect(mockCollection.where).toHaveBeenCalledWith(expect.stringContaining("categorySlugs"), "array-contains", "category-trading-cards");
555
+ });
556
+ });
557
+ // ---------------------------------------------------------------------------
558
+ // incrementViewCount
559
+ // ---------------------------------------------------------------------------
560
+ describe("ProductRepository.incrementViewCount", () => {
561
+ it("increments viewCount field (fire-and-forget)", async () => {
562
+ await repo.incrementViewCount("product-1");
563
+ expect(mockDocRef.update).toHaveBeenCalledWith(expect.objectContaining({ viewCount: 1 }));
564
+ });
565
+ it("does not throw when Firestore update fails", async () => {
566
+ mockDocRef.update.mockRejectedValue(new Error("Firestore error"));
567
+ await expect(repo.incrementViewCount("product-1")).resolves.toBeUndefined();
568
+ });
569
+ });
570
+ // ---------------------------------------------------------------------------
571
+ // deleteByStore
572
+ // ---------------------------------------------------------------------------
573
+ describe("ProductRepository.deleteByStore", () => {
574
+ it("returns 0 when no products exist for the store", async () => {
575
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
576
+ const count = await repo.deleteByStore("store-empty");
577
+ expect(count).toBe(0);
578
+ });
579
+ it("batch-deletes all products for the store and returns count", async () => {
580
+ const products = [
581
+ makeProductDoc({ id: "p-1", storeId: "store-1" }),
582
+ makeProductDoc({ id: "p-2", storeId: "store-1" }),
583
+ ];
584
+ mockQuery.get.mockResolvedValue(makeQuerySnap(products.map((p) => ({ id: p.id, data: p }))));
585
+ const count = await repo.deleteByStore("store-1");
586
+ expect(count).toBe(2);
587
+ expect(mockBatch.delete).toHaveBeenCalledTimes(2);
588
+ expect(mockBatch.commit).toHaveBeenCalledOnce();
589
+ });
590
+ });
591
+ // ---------------------------------------------------------------------------
592
+ // getStaleDraftRefs
593
+ // ---------------------------------------------------------------------------
594
+ describe("ProductRepository.getStaleDraftRefs", () => {
595
+ it("queries status==draft and updatedAt < cutoff", async () => {
596
+ const cutoff = new Date("2026-01-01");
597
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
598
+ await repo.getStaleDraftRefs(cutoff);
599
+ expect(mockCollection.where).toHaveBeenCalledWith(expect.stringContaining("status"), "==", "draft");
600
+ expect(mockQuery.where).toHaveBeenCalledWith(expect.stringContaining("updatedAt"), "<", cutoff);
601
+ });
602
+ it("limits to 200 documents", async () => {
603
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
604
+ await repo.getStaleDraftRefs(new Date());
605
+ expect(mockQuery.limit).toHaveBeenCalledWith(200);
606
+ });
607
+ });
@@ -18,6 +18,7 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
18
18
  findByStatus(status: ProductStatus): Promise<ProductDocument[]>;
19
19
  findFeatured(): Promise<ProductDocument[]>;
20
20
  findByCategory(category: string): Promise<ProductDocument[]>;
21
+ findByBarcodeId(barcodeId: string): Promise<ProductDocument | null>;
21
22
  findBySlug(slug: string): Promise<ProductDocument | undefined>;
22
23
  findByIdOrSlug(idOrSlug: string): Promise<ProductDocument | undefined>;
23
24
  /**
@@ -206,6 +207,42 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
206
207
  canFilter: boolean;
207
208
  canSort: boolean;
208
209
  };
210
+ "classified.meetupArea": {
211
+ canFilter: boolean;
212
+ canSort: boolean;
213
+ };
214
+ "classified.acceptsShipping": {
215
+ canFilter: boolean;
216
+ canSort: boolean;
217
+ };
218
+ "classified.negotiable": {
219
+ canFilter: boolean;
220
+ canSort: boolean;
221
+ };
222
+ "digitalCode.codeDeliveryMethod": {
223
+ canFilter: boolean;
224
+ canSort: boolean;
225
+ };
226
+ "digitalCode.codesAvailable": {
227
+ canFilter: boolean;
228
+ canSort: boolean;
229
+ };
230
+ "liveItem.species": {
231
+ canFilter: boolean;
232
+ canSort: boolean;
233
+ };
234
+ "liveItem.sex": {
235
+ canFilter: boolean;
236
+ canSort: boolean;
237
+ };
238
+ "liveItem.jurisdictionAllowed": {
239
+ canFilter: boolean;
240
+ canSort: boolean;
241
+ };
242
+ "liveItem.cites": {
243
+ canFilter: boolean;
244
+ canSort: boolean;
245
+ };
209
246
  };
210
247
  /**
211
248
  * Virtual filter aliases — callers can pass `f=listingType==auction` for