@mohasinac/appkit 4.53.0 → 4.53.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (500) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/client/scaffolds/AppShell.js +1 -1
  3. package/dist/_internal/server/features/auctions/og.d.ts +3 -1
  4. package/dist/_internal/server/features/auctions/og.js +3 -1
  5. package/dist/_internal/server/features/bundles/actions.js +1 -1
  6. package/dist/_internal/server/features/business-documents/download.d.ts +41 -0
  7. package/dist/_internal/server/features/business-documents/download.js +91 -0
  8. package/dist/_internal/server/features/categories/adapters.d.ts +1 -1
  9. package/dist/_internal/server/features/categories/adapters.js +10 -0
  10. package/dist/_internal/server/features/checkout/actions.d.ts +14 -0
  11. package/dist/_internal/server/features/checkout/actions.js +106 -12
  12. package/dist/_internal/server/features/checkout/bundle-expansion.js +17 -0
  13. package/dist/_internal/server/features/checkout/data.d.ts +22 -0
  14. package/dist/_internal/server/features/checkout/group-rule-revalidation.d.ts +34 -0
  15. package/dist/_internal/server/features/checkout/group-rule-revalidation.js +124 -0
  16. package/dist/_internal/server/features/checkout/supplier-identity.d.ts +55 -0
  17. package/dist/_internal/server/features/checkout/supplier-identity.js +81 -0
  18. package/dist/_internal/server/features/classified/metadata.js +1 -1
  19. package/dist/_internal/server/features/classified/og.d.ts +3 -1
  20. package/dist/_internal/server/features/classified/og.js +3 -1
  21. package/dist/_internal/server/features/digital-code/metadata.js +1 -1
  22. package/dist/_internal/server/features/digital-code/og.d.ts +3 -1
  23. package/dist/_internal/server/features/digital-code/og.js +3 -1
  24. package/dist/_internal/server/features/digital-code/pool.d.ts +19 -4
  25. package/dist/_internal/server/features/digital-code/pool.js +19 -4
  26. package/dist/_internal/server/features/live/metadata.js +1 -1
  27. package/dist/_internal/server/features/live/og.d.ts +3 -1
  28. package/dist/_internal/server/features/live/og.js +3 -1
  29. package/dist/_internal/server/features/messages/actions.js +1 -1
  30. package/dist/_internal/server/features/parts/og.js +1 -1
  31. package/dist/_internal/server/features/pre-orders/og.d.ts +3 -1
  32. package/dist/_internal/server/features/pre-orders/og.js +3 -1
  33. package/dist/_internal/server/features/price-index/search.d.ts +18 -0
  34. package/dist/_internal/server/features/price-index/search.js +47 -7
  35. package/dist/_internal/server/features/prize-draws/og.d.ts +3 -1
  36. package/dist/_internal/server/features/prize-draws/og.js +3 -1
  37. package/dist/_internal/server/features/products/group-members.d.ts +26 -0
  38. package/dist/_internal/server/features/products/group-members.js +7 -1
  39. package/dist/_internal/server/features/products/og.d.ts +3 -1
  40. package/dist/_internal/server/features/products/og.js +3 -1
  41. package/dist/_internal/server/features/search/scoped-sources.d.ts +26 -0
  42. package/dist/_internal/server/features/search/scoped-sources.js +252 -0
  43. package/dist/_internal/server/features/search/search-everything.d.ts +57 -0
  44. package/dist/_internal/server/features/search/search-everything.js +70 -20
  45. package/dist/_internal/server/features/seo/sitemap.js +6 -0
  46. package/dist/_internal/server/features/shared/listing-adapters.js +4 -2
  47. package/dist/_internal/server/features/sticker-trace/adapters.d.ts +11 -0
  48. package/dist/_internal/server/features/sticker-trace/adapters.js +1 -0
  49. package/dist/_internal/server/features/sticker-trace/promote-to-listings.d.ts +26 -0
  50. package/dist/_internal/server/features/sticker-trace/promote-to-listings.js +264 -0
  51. package/dist/_internal/server/features/sticker-trace/sheet-actions.d.ts +3 -1
  52. package/dist/_internal/server/features/sticker-trace/sheet-actions.js +32 -0
  53. package/dist/_internal/server/features/sticker-trace/storage.d.ts +20 -1
  54. package/dist/_internal/server/features/sticker-trace/storage.js +20 -1
  55. package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
  56. package/dist/_internal/server/features/stores/adapters.js +16 -0
  57. package/dist/_internal/server/features/stores/index.d.ts +1 -0
  58. package/dist/_internal/server/features/stores/index.js +1 -0
  59. package/dist/_internal/server/features/stores/tabs.d.ts +25 -0
  60. package/dist/_internal/server/features/stores/tabs.js +122 -0
  61. package/dist/_internal/shared/checkout/rules/_defaults.js +0 -2
  62. package/dist/_internal/shared/checkout/rules/_limits.d.ts +14 -1
  63. package/dist/_internal/shared/checkout/rules/_limits.js +14 -1
  64. package/dist/_internal/shared/checkout/rules/_registry.js +2 -0
  65. package/dist/_internal/shared/checkout/rules/auction.rule.js +9 -1
  66. package/dist/_internal/shared/checkout/rules/bundle.rule.js +13 -0
  67. package/dist/_internal/shared/checkout/rules/index.d.ts +2 -0
  68. package/dist/_internal/shared/checkout/rules/index.js +9 -0
  69. package/dist/_internal/shared/checkout/rules/offer.rule.js +0 -1
  70. package/dist/_internal/shared/checkout/rules/payment-gate.d.ts +26 -0
  71. package/dist/_internal/shared/checkout/rules/payment-gate.js +133 -0
  72. package/dist/_internal/shared/checkout/rules/prize-draw.rule.js +29 -8
  73. package/dist/_internal/shared/checkout/rules/spares.rule.d.ts +16 -0
  74. package/dist/_internal/shared/checkout/rules/spares.rule.js +5 -0
  75. package/dist/_internal/shared/checkout/rules/types.d.ts +26 -2
  76. package/dist/_internal/shared/config/schema.d.ts +122 -0
  77. package/dist/_internal/shared/features/cart/schema.js +3 -2
  78. package/dist/_internal/shared/features/products/group-rules.d.ts +75 -0
  79. package/dist/_internal/shared/features/products/group-rules.js +142 -0
  80. package/dist/_internal/shared/listing-types/_registry.d.ts +24 -0
  81. package/dist/_internal/shared/listing-types/_registry.js +39 -1
  82. package/dist/_internal/shared/listing-types/art/config.d.ts +1 -0
  83. package/dist/_internal/shared/listing-types/art/config.js +2 -0
  84. package/dist/_internal/shared/listing-types/auction/config.d.ts +1 -0
  85. package/dist/_internal/shared/listing-types/auction/config.js +2 -0
  86. package/dist/_internal/shared/listing-types/availability.js +48 -0
  87. package/dist/_internal/shared/listing-types/capabilities.js +19 -0
  88. package/dist/_internal/shared/listing-types/classified/config.d.ts +1 -0
  89. package/dist/_internal/shared/listing-types/classified/config.js +2 -0
  90. package/dist/_internal/shared/listing-types/digital-code/config.d.ts +1 -0
  91. package/dist/_internal/shared/listing-types/digital-code/config.js +2 -0
  92. package/dist/_internal/shared/listing-types/feature-flags.js +1 -0
  93. package/dist/_internal/shared/listing-types/live/config.d.ts +1 -0
  94. package/dist/_internal/shared/listing-types/live/config.js +2 -0
  95. package/dist/_internal/shared/listing-types/pre-order/config.d.ts +1 -0
  96. package/dist/_internal/shared/listing-types/pre-order/config.js +2 -0
  97. package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +1 -0
  98. package/dist/_internal/shared/listing-types/prize-draw/config.js +2 -0
  99. package/dist/_internal/shared/listing-types/spares/config.d.ts +74 -0
  100. package/dist/_internal/shared/listing-types/spares/config.js +60 -0
  101. package/dist/_internal/shared/listing-types/spares/ctas.d.ts +1 -0
  102. package/dist/_internal/shared/listing-types/spares/ctas.js +4 -0
  103. package/dist/_internal/shared/listing-types/spares/og.js +1 -0
  104. package/dist/_internal/shared/listing-types/spares/schema.d.ts +2 -0
  105. package/dist/_internal/shared/listing-types/spares/schema.js +3 -0
  106. package/dist/_internal/shared/listing-types/spares/seed-factory.d.ts +1 -0
  107. package/dist/_internal/shared/listing-types/spares/seed-factory.js +1 -0
  108. package/dist/_internal/shared/listing-types/standard/config.d.ts +1 -0
  109. package/dist/_internal/shared/listing-types/standard/config.js +2 -0
  110. package/dist/_internal/shared/listing-types/stickers/config.d.ts +1 -0
  111. package/dist/_internal/shared/listing-types/stickers/config.js +2 -0
  112. package/dist/_internal/shared/search/match.d.ts +11 -0
  113. package/dist/_internal/shared/search/match.js +2 -1
  114. package/dist/_internal/shared/search/to-groups.d.ts +52 -0
  115. package/dist/_internal/shared/search/to-groups.js +141 -0
  116. package/dist/client.d.ts +8 -2
  117. package/dist/client.js +4 -1
  118. package/dist/constants/api-endpoints.d.ts +39 -15
  119. package/dist/constants/api-endpoints.js +28 -5
  120. package/dist/constants/cache-invalidation.js +3 -1
  121. package/dist/constants/field-names.d.ts +6 -0
  122. package/dist/constants/field-names.js +6 -0
  123. package/dist/features/about/components/PolicyPageView.d.ts +22 -2
  124. package/dist/features/about/components/PolicyPageView.js +14 -2
  125. package/dist/features/about/components/PublicProfileView.js +2 -1
  126. package/dist/features/about/components/ShippingPolicyView.js +42 -2
  127. package/dist/features/account/components/NotificationBell.js +1 -1
  128. package/dist/features/account/hooks/useProfileStats.js +5 -1
  129. package/dist/features/addresses/repository/addresses.repository.d.ts +16 -0
  130. package/dist/features/addresses/repository/addresses.repository.js +21 -0
  131. package/dist/features/admin/actions/admin-actions.js +16 -1
  132. package/dist/features/admin/components/AdminCategoryEditorView.js +19 -2
  133. package/dist/features/admin/components/AdminNavigationView.js +1 -1
  134. package/dist/features/admin/components/AdminPageHeader.js +1 -1
  135. package/dist/features/admin/components/AdminProductEditorView.js +17 -2
  136. package/dist/features/admin/components/AdminSidebar.js +1 -1
  137. package/dist/features/admin/components/AdminSiteSettingsView.js +39 -8
  138. package/dist/features/admin/components/AdminSparesView.d.ts +12 -0
  139. package/dist/features/admin/components/AdminSparesView.js +30 -0
  140. package/dist/features/admin/components/AdminUsersView.js +9 -7
  141. package/dist/features/admin/components/index.d.ts +2 -0
  142. package/dist/features/admin/components/index.js +1 -0
  143. package/dist/features/admin/components/sections/adminSectionsTypes.js +7 -0
  144. package/dist/features/admin/constants/filter-tabs.d.ts +3 -0
  145. package/dist/features/admin/constants/filter-tabs.js +1 -0
  146. package/dist/features/admin/schemas/firestore.d.ts +26 -2
  147. package/dist/features/auctions/components/AuctionDetailPageView.js +4 -11
  148. package/dist/features/auth/repository/user.repository.d.ts +13 -0
  149. package/dist/features/auth/repository/user.repository.js +28 -0
  150. package/dist/features/auth/schemas/firestore.d.ts +2 -0
  151. package/dist/features/auth/schemas/firestore.js +2 -0
  152. package/dist/features/business-documents/repository/business-documents.repository.d.ts +58 -0
  153. package/dist/features/business-documents/repository/business-documents.repository.js +54 -0
  154. package/dist/features/business-documents/schemas/firestore.d.ts +105 -0
  155. package/dist/features/business-documents/schemas/firestore.js +75 -0
  156. package/dist/features/cart/actions/cart-actions.js +83 -6
  157. package/dist/features/cart/repository/cart.repository.js +32 -4
  158. package/dist/features/cart/schemas/firestore.d.ts +20 -0
  159. package/dist/features/cart/schemas/index.d.ts +2 -2
  160. package/dist/features/catalogue/components/PublicCatalogueItemDetailView.js +1 -1
  161. package/dist/features/categories/api/[id]/price-guidance/route.d.ts +42 -0
  162. package/dist/features/categories/api/[id]/price-guidance/route.js +113 -0
  163. package/dist/features/categories/components/BrandDetailPageView.js +19 -3
  164. package/dist/features/categories/components/BrandDetailTabs.js +10 -0
  165. package/dist/features/categories/components/CategoriesIndexListing.js +23 -8
  166. package/dist/features/categories/components/CategoryDetailPageView.js +141 -14
  167. package/dist/features/categories/components/CategoryDetailTabs.js +12 -0
  168. package/dist/features/categories/components/CategoryGrid.js +10 -1
  169. package/dist/features/categories/components/PriceGuidanceHint.d.ts +29 -0
  170. package/dist/features/categories/components/PriceGuidanceHint.js +61 -0
  171. package/dist/features/categories/components/SubcategoryCardRow.d.ts +27 -0
  172. package/dist/features/categories/components/SubcategoryCardRow.js +98 -0
  173. package/dist/features/categories/constants/category-tabs.d.ts +20 -25
  174. package/dist/features/categories/constants/category-tabs.js +22 -39
  175. package/dist/features/categories/repository/categories.repository.d.ts +12 -0
  176. package/dist/features/categories/repository/categories.repository.js +20 -0
  177. package/dist/features/categories/schemas/firestore.d.ts +22 -1
  178. package/dist/features/categories/schemas/firestore.js +10 -0
  179. package/dist/features/categories/schemas/index.d.ts +6 -0
  180. package/dist/features/categories/schemas/index.js +2 -0
  181. package/dist/features/categories/types/index.d.ts +8 -0
  182. package/dist/features/categories/utils/taxonomy-visual.d.ts +16 -3
  183. package/dist/features/categories/utils/taxonomy-visual.js +26 -10
  184. package/dist/features/classified/components/ClassifiedDetailPageView.js +4 -9
  185. package/dist/features/contact/components/ContactPageView.d.ts +33 -1
  186. package/dist/features/contact/components/ContactPageView.js +58 -28
  187. package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +4 -9
  188. package/dist/features/grouped/components/GroupedListingDetailView.js +9 -1
  189. package/dist/features/grouped/schemas/firestore.d.ts +5 -0
  190. package/dist/features/homepage/actions/homepage-section-actions.d.ts +3 -3
  191. package/dist/features/homepage/components/CollectionCardsSection.d.ts +28 -6
  192. package/dist/features/homepage/components/CollectionCardsSection.js +43 -8
  193. package/dist/features/homepage/components/HowItWorksSection.d.ts +23 -6
  194. package/dist/features/homepage/components/HowItWorksSection.js +4 -4
  195. package/dist/features/homepage/components/PromoGrid.d.ts +31 -2
  196. package/dist/features/homepage/components/PromoGrid.js +12 -3
  197. package/dist/features/homepage/components/TestimonialsCarousel.d.ts +34 -2
  198. package/dist/features/homepage/components/TestimonialsCarousel.js +1 -1
  199. package/dist/features/homepage/components/index.d.ts +0 -20
  200. package/dist/features/homepage/components/index.js +0 -11
  201. package/dist/features/homepage/index.d.ts +0 -2
  202. package/dist/features/homepage/index.js +0 -2
  203. package/dist/features/homepage/lib/section-renderer.d.ts +9 -0
  204. package/dist/features/homepage/lib/section-renderer.js +202 -58
  205. package/dist/features/homepage/repository/homepage-sections.repository.d.ts +35 -3
  206. package/dist/features/homepage/repository/homepage-sections.repository.js +58 -8
  207. package/dist/features/homepage/schemas/firestore.d.ts +106 -3
  208. package/dist/features/homepage/schemas/firestore.js +32 -2
  209. package/dist/features/homepage/schemas/index.d.ts +36 -7
  210. package/dist/features/homepage/schemas/index.js +39 -27
  211. package/dist/features/homepage/server.d.ts +0 -3
  212. package/dist/features/homepage/server.js +0 -3
  213. package/dist/features/homepage/types/index.d.ts +0 -47
  214. package/dist/features/homepage/types/index.js +49 -0
  215. package/dist/features/layout/AppLayoutShell.js +1 -1
  216. package/dist/features/layout/BottomNavItem.js +1 -1
  217. package/dist/features/layout/BottomSheet.js +1 -1
  218. package/dist/features/layout/TitleBarLayout.js +1 -1
  219. package/dist/features/live/components/LiveItemDetailPageView.js +4 -9
  220. package/dist/features/media/hooks/useMedia.js +17 -0
  221. package/dist/features/media/repository/media-assets.repository.d.ts +46 -0
  222. package/dist/features/media/repository/media-assets.repository.js +31 -0
  223. package/dist/features/media/trust-signals.d.ts +85 -0
  224. package/dist/features/media/trust-signals.js +126 -0
  225. package/dist/features/parts/components/PartDetailPageView.d.ts +14 -1
  226. package/dist/features/parts/components/PartDetailPageView.js +2 -2
  227. package/dist/features/parts/components/PartsIndexPageView.d.ts +11 -2
  228. package/dist/features/parts/components/PartsIndexPageView.js +2 -2
  229. package/dist/features/parts/components/PartsSearchBox.d.ts +9 -0
  230. package/dist/features/parts/components/PartsSearchBox.js +57 -0
  231. package/dist/features/parts/schemas/firestore.d.ts +149 -3
  232. package/dist/features/parts/schemas/firestore.js +184 -0
  233. package/dist/features/payments/repository/payout.repository.d.ts +15 -0
  234. package/dist/features/payments/repository/payout.repository.js +21 -0
  235. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +4 -11
  236. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +1 -1
  237. package/dist/features/price-index/components/PriceIndexToolView.d.ts +3 -1
  238. package/dist/features/price-index/components/PriceIndexToolView.js +92 -13
  239. package/dist/features/products/components/CompareOverlay.d.ts +18 -1
  240. package/dist/features/products/components/CompareOverlay.js +21 -2
  241. package/dist/features/products/components/CustomSectionTabContent.d.ts +31 -0
  242. package/dist/features/products/components/CustomSectionTabContent.js +29 -3
  243. package/dist/features/products/components/GroupMemberPicker.d.ts +19 -1
  244. package/dist/features/products/components/GroupMemberPicker.js +46 -8
  245. package/dist/features/products/components/GroupSettingsPanel.d.ts +11 -1
  246. package/dist/features/products/components/GroupSettingsPanel.js +91 -3
  247. package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
  248. package/dist/features/products/components/PrizeDrawEntryActions.d.ts +15 -1
  249. package/dist/features/products/components/PrizeDrawEntryActions.js +70 -8
  250. package/dist/features/products/components/ProductDetailPageView.js +74 -15
  251. package/dist/features/products/components/ProductForm.d.ts +13 -1
  252. package/dist/features/products/components/ProductForm.js +16 -2
  253. package/dist/features/products/components/ProductsIndexListing.d.ts +16 -1
  254. package/dist/features/products/components/ProductsIndexListing.js +3 -3
  255. package/dist/features/products/components/ShowGroupSection.js +15 -2
  256. package/dist/features/products/components/SparesListView.d.ts +7 -0
  257. package/dist/features/products/components/SparesListView.js +39 -0
  258. package/dist/features/products/constants/action-defs.d.ts +1 -0
  259. package/dist/features/products/constants/action-defs.js +1 -0
  260. package/dist/features/products/repository/products.repository.d.ts +98 -0
  261. package/dist/features/products/repository/products.repository.js +235 -17
  262. package/dist/features/products/schemas/firestore.d.ts +97 -4
  263. package/dist/features/products/schemas/firestore.js +104 -0
  264. package/dist/features/products/schemas/index.d.ts +10 -19
  265. package/dist/features/products/schemas/index.js +23 -6
  266. package/dist/features/products/types/index.d.ts +45 -1
  267. package/dist/features/products/utils/group-member-image.d.ts +39 -0
  268. package/dist/features/products/utils/group-member-image.js +3 -0
  269. package/dist/features/products/utils/listing-badge-variant.js +4 -0
  270. package/dist/features/reviews/components/ReviewsList.js +1 -1
  271. package/dist/features/scams/components/ScamProfileView.js +1 -1
  272. package/dist/features/search/components/EntityTypeahead.d.ts +44 -0
  273. package/dist/features/search/components/EntityTypeahead.js +92 -0
  274. package/dist/features/search/components/Search.d.ts +12 -1
  275. package/dist/features/search/components/Search.js +47 -3
  276. package/dist/features/search/components/SearchFacetRail.d.ts +11 -0
  277. package/dist/features/search/components/SearchFacetRail.js +92 -0
  278. package/dist/features/search/components/SearchResultsClient.d.ts +33 -0
  279. package/dist/features/search/components/SearchResultsClient.js +103 -0
  280. package/dist/features/search/components/SearchResultsPageView.d.ts +15 -1
  281. package/dist/features/search/components/SearchResultsPageView.js +2 -2
  282. package/dist/features/search/components/index.d.ts +0 -6
  283. package/dist/features/search/components/index.js +0 -3
  284. package/dist/features/search/hooks/useNavSuggestions.d.ts +13 -0
  285. package/dist/features/search/hooks/useScopedSearch.d.ts +34 -0
  286. package/dist/features/search/hooks/useScopedSearch.js +114 -0
  287. package/dist/features/search/hooks/useSiteSearch.d.ts +26 -0
  288. package/dist/features/search/hooks/useSiteSearch.js +149 -0
  289. package/dist/features/search/index.d.ts +0 -4
  290. package/dist/features/search/index.js +0 -4
  291. package/dist/features/search/manifest.d.ts +12 -0
  292. package/dist/features/search/manifest.js +17 -2
  293. package/dist/features/search/server.d.ts +0 -3
  294. package/dist/features/search/server.js +0 -3
  295. package/dist/features/seller/components/SellerListingTypeView.d.ts +1 -1
  296. package/dist/features/seller/components/SellerListingTypeView.js +20 -0
  297. package/dist/features/seller/components/SellerPayoutSettingsView.js +35 -14
  298. package/dist/features/seller/components/SellerProductShell.d.ts +1 -1
  299. package/dist/features/seller/components/SellerProductShell.js +2 -1
  300. package/dist/features/seller/components/SellerSidebar.js +1 -1
  301. package/dist/features/seller/components/SellerSparesView.d.ts +17 -0
  302. package/dist/features/seller/components/SellerSparesView.js +6 -0
  303. package/dist/features/seller/components/index.d.ts +2 -0
  304. package/dist/features/seller/components/index.js +1 -0
  305. package/dist/features/shell/CommandPalette.js +1 -1
  306. package/dist/features/sticker-trace/schemas/firestore.d.ts +19 -0
  307. package/dist/features/stores/components/StoreDetailLayoutView.d.ts +1 -1
  308. package/dist/features/stores/components/StoreDetailLayoutView.js +7 -82
  309. package/dist/features/stores/components/StoreHeader.js +1 -2
  310. package/dist/features/stores/components/StoreSparesPageView.d.ts +27 -0
  311. package/dist/features/stores/components/StoreSparesPageView.js +48 -0
  312. package/dist/features/stores/schemas/firestore.d.ts +19 -0
  313. package/dist/features/tester/seed-data/authored/buying__spares-and-groups.d.ts +2 -0
  314. package/dist/features/tester/seed-data/authored/buying__spares-and-groups.js +96 -0
  315. package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.d.ts +2 -0
  316. package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.js +280 -45
  317. package/dist/features/tester/seed-data/authored/content-discovery__category-tabs-franchise-bundles.js +54 -32
  318. package/dist/features/tester/seed-data/authored/content-discovery__parts-database.js +3 -2
  319. package/dist/features/tester/seed-data/authored/content-discovery__price-index-and-compare.d.ts +2 -0
  320. package/dist/features/tester/seed-data/authored/content-discovery__price-index-and-compare.js +73 -0
  321. package/dist/features/tester/seed-data/authored/content-discovery__site-wide-index.d.ts +2 -0
  322. package/dist/features/tester/seed-data/authored/content-discovery__site-wide-index.js +340 -0
  323. package/dist/features/tester/seed-data/authored/index.js +16 -0
  324. package/dist/features/tester/seed-data/authored/search-and-nav__header-search.d.ts +1 -0
  325. package/dist/features/tester/seed-data/authored/search-and-nav__header-search.js +57 -0
  326. package/dist/features/tester/seed-data/authored/selling__spares-and-group-editing.d.ts +2 -0
  327. package/dist/features/tester/seed-data/authored/selling__spares-and-group-editing.js +77 -0
  328. package/dist/features/tester/seed-data/authored/tools__sticker-promote-to-listings.d.ts +2 -0
  329. package/dist/features/tester/seed-data/authored/tools__sticker-promote-to-listings.js +58 -0
  330. package/dist/features/tester/seed-data/authored/tools__sticker-tracer.js +5 -4
  331. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +372 -18
  332. package/dist/index.d.ts +17 -57
  333. package/dist/index.js +35 -96
  334. package/dist/next/routing/route-map.d.ts +42 -1
  335. package/dist/next/routing/route-map.js +45 -0
  336. package/dist/providers/db-firebase/base.repository.d.ts +22 -0
  337. package/dist/providers/db-firebase/base.repository.js +36 -0
  338. package/dist/schemas/registry.d.ts +15 -6
  339. package/dist/security/blind-index-tokens.d.ts +54 -0
  340. package/dist/security/blind-index-tokens.js +152 -0
  341. package/dist/security/index.d.ts +1 -1
  342. package/dist/security/index.js +1 -1
  343. package/dist/security/settings-encryption.d.ts +35 -0
  344. package/dist/security/settings-encryption.js +91 -0
  345. package/dist/seed/_helpers/brand-rows.js +38 -14
  346. package/dist/seed/_helpers/category-forest.js +73 -7
  347. package/dist/seed/_helpers/category-images-generated.d.ts +25 -6
  348. package/dist/seed/_helpers/category-images-generated.js +81 -6
  349. package/dist/seed/_helpers/category-models.d.ts +1 -1
  350. package/dist/seed/_helpers/category-models.js +7 -1
  351. package/dist/seed/_helpers/category-parts-glossary-map.js +8 -0
  352. package/dist/seed/_helpers/category-parts.d.ts +1 -1
  353. package/dist/seed/_helpers/category-parts.js +50 -2
  354. package/dist/seed/_helpers/character-features.d.ts +1 -1
  355. package/dist/seed/_helpers/character-features.js +2 -1
  356. package/dist/seed/_helpers/franchise-rows.js +10 -2
  357. package/dist/seed/_helpers/media.d.ts +47 -0
  358. package/dist/seed/_helpers/media.js +34 -0
  359. package/dist/seed/authenticity-knowledge-seed-data.d.ts +49 -5
  360. package/dist/seed/authenticity-knowledge-seed-data.js +10001 -549
  361. package/dist/seed/categories-seed-data.js +124 -20
  362. package/dist/seed/grouped-listings-seed-data.js +16 -0
  363. package/dist/seed/homepage-sections-seed-data.js +94 -5
  364. package/dist/seed/index.d.ts +2 -3
  365. package/dist/seed/index.js +2 -2
  366. package/dist/seed/manifest.js +3 -0
  367. package/dist/seed/media-assets-seed-data.d.ts +1 -1
  368. package/dist/seed/media-assets-seed-data.js +746 -196
  369. package/dist/seed/model-composition-seed-data.js +122 -6
  370. package/dist/seed/part-glossary-seed-data.d.ts +1 -1
  371. package/dist/seed/part-glossary-seed-data.js +37219 -463
  372. package/dist/seed/products-art-seed-data.js +7 -18
  373. package/dist/seed/products-auctions-seed-data.js +56 -47
  374. package/dist/seed/products-beyblade-x-seed-data.d.ts +2 -0
  375. package/dist/seed/products-beyblade-x-seed-data.js +441 -0
  376. package/dist/seed/products-catalogue-seed-data.d.ts +5 -5
  377. package/dist/seed/products-catalogue-seed-data.js +1500 -1240
  378. package/dist/seed/products-classifieds-seed-data.js +9 -27
  379. package/dist/seed/products-digital-codes-seed-data.js +9 -18
  380. package/dist/seed/products-live-items-seed-data.js +5 -13
  381. package/dist/seed/products-preorders-seed-data.js +67 -33
  382. package/dist/seed/products-prize-draws-seed-data.js +8 -17
  383. package/dist/seed/products-spares-seed-data.d.ts +2 -0
  384. package/dist/seed/products-spares-seed-data.js +368 -0
  385. package/dist/seed/products-standard-seed-data.js +39 -126
  386. package/dist/seed/products-stickers-seed-data.js +59 -15
  387. package/dist/seed/site-settings-seed-data.js +8 -0
  388. package/dist/seo/json-ld.d.ts +25 -0
  389. package/dist/seo/json-ld.js +28 -0
  390. package/dist/server-entry.d.ts +3 -1
  391. package/dist/server-entry.js +2 -2
  392. package/dist/server.d.ts +9 -9
  393. package/dist/server.js +27 -25
  394. package/dist/styles.css +202 -1
  395. package/dist/tailwind-input.css +31 -17
  396. package/dist/tailwind-utilities.css +1 -1
  397. package/dist/ui/components/Accordion.js +1 -1
  398. package/dist/ui/components/Breadcrumb.js +1 -1
  399. package/dist/ui/components/DetailPageGallery.js +0 -2
  400. package/dist/ui/components/DetailPageHero.js +0 -1
  401. package/dist/ui/components/DetailPageTabs.js +1 -1
  402. package/dist/ui/components/Divider.js +3 -3
  403. package/dist/ui/components/GatedPrice.js +5 -1
  404. package/dist/ui/components/HorizontalScroller.js +1 -1
  405. package/dist/ui/components/ListingLayout.js +3 -3
  406. package/dist/ui/components/MediaTabs.style.css +81 -0
  407. package/dist/ui/components/Pagination.js +1 -1
  408. package/dist/ui/components/PendingValue.d.ts +45 -0
  409. package/dist/ui/components/PendingValue.js +7 -0
  410. package/dist/ui/components/Radio.style.css +119 -0
  411. package/dist/ui/components/RatingDisplay.js +2 -2
  412. package/dist/ui/components/SettingsSection.js +0 -2
  413. package/dist/ui/components/SiteLogo.js +3 -0
  414. package/dist/ui/components/SiteMark.js +3 -0
  415. package/dist/ui/components/SortDropdown.js +1 -1
  416. package/dist/ui/components/SpecTable.d.ts +58 -0
  417. package/dist/ui/components/SpecTable.js +8 -0
  418. package/dist/ui/components/StarRating.js +1 -3
  419. package/dist/ui/components/SummaryCard.js +1 -1
  420. package/dist/ui/components/index.style.css +1 -0
  421. package/dist/ui/icons/icon-registry.d.ts +1 -0
  422. package/dist/ui/icons/icon-registry.js +9 -1
  423. package/dist/ui/index.d.ts +4 -0
  424. package/dist/ui/index.js +2 -0
  425. package/dist/utils/search-txt-query.d.ts +12 -0
  426. package/dist/utils/search-txt-query.js +12 -0
  427. package/package.json +1 -1
  428. package/scripts/dedupe-peer-deps.mjs +18 -0
  429. package/scripts/seed-cli.mjs +135 -26
  430. package/scripts/verify-css-build.mjs +118 -37
  431. package/dist/_internal/server/features/search/actions.d.ts +0 -7
  432. package/dist/_internal/server/features/search/actions.js +0 -21
  433. package/dist/_internal/server/features/search/data.d.ts +0 -15
  434. package/dist/_internal/server/features/search/data.js +0 -18
  435. package/dist/_internal/server/features/search/index.d.ts +0 -3
  436. package/dist/_internal/server/features/search/index.js +0 -2
  437. package/dist/features/homepage/api/[id]/route.d.ts +0 -23
  438. package/dist/features/homepage/api/[id]/route.js +0 -103
  439. package/dist/features/homepage/api/route.d.ts +0 -14
  440. package/dist/features/homepage/api/route.js +0 -140
  441. package/dist/features/homepage/api/sections/[id]/route.d.ts +0 -26
  442. package/dist/features/homepage/api/sections/[id]/route.js +0 -95
  443. package/dist/features/homepage/api/sections/route.d.ts +0 -13
  444. package/dist/features/homepage/api/sections/route.js +0 -82
  445. package/dist/features/homepage/components/AdvertisementBanner.d.ts +0 -14
  446. package/dist/features/homepage/components/AdvertisementBanner.js +0 -35
  447. package/dist/features/homepage/components/BeforeAfterCard.d.ts +0 -9
  448. package/dist/features/homepage/components/BeforeAfterCard.js +0 -73
  449. package/dist/features/homepage/components/FeaturedResultsSection.d.ts +0 -17
  450. package/dist/features/homepage/components/FeaturedResultsSection.js +0 -17
  451. package/dist/features/homepage/components/HeroBanner.d.ts +0 -7
  452. package/dist/features/homepage/components/HeroBanner.js +0 -43
  453. package/dist/features/homepage/components/HeroSection.d.ts +0 -7
  454. package/dist/features/homepage/components/HeroSection.js +0 -6
  455. package/dist/features/homepage/components/HomepageSkeleton.d.ts +0 -2
  456. package/dist/features/homepage/components/HomepageSkeleton.js +0 -21
  457. package/dist/features/homepage/components/HomepageView.d.ts +0 -25
  458. package/dist/features/homepage/components/HomepageView.js +0 -5
  459. package/dist/features/homepage/components/NewsletterBanner.d.ts +0 -23
  460. package/dist/features/homepage/components/NewsletterBanner.js +0 -37
  461. package/dist/features/homepage/components/SiteFeaturesSection.d.ts +0 -14
  462. package/dist/features/homepage/components/SiteFeaturesSection.js +0 -11
  463. package/dist/features/homepage/components/TrustBadges.d.ts +0 -6
  464. package/dist/features/homepage/components/TrustBadges.js +0 -46
  465. package/dist/features/homepage/components/TrustIndicatorsSection.d.ts +0 -12
  466. package/dist/features/homepage/components/TrustIndicatorsSection.js +0 -11
  467. package/dist/features/homepage/hooks/useHomepage.d.ts +0 -11
  468. package/dist/features/homepage/hooks/useHomepage.js +0 -17
  469. package/dist/features/homepage/hooks/useHomepageSections.d.ts +0 -2
  470. package/dist/features/homepage/hooks/useHomepageSections.js +0 -10
  471. package/dist/features/homepage/repository/homepage.repository.d.ts +0 -11
  472. package/dist/features/homepage/repository/homepage.repository.js +0 -25
  473. package/dist/features/search/actions/index.d.ts +0 -1
  474. package/dist/features/search/actions/index.js +0 -1
  475. package/dist/features/search/actions/search-actions.d.ts +0 -18
  476. package/dist/features/search/actions/search-actions.js +0 -43
  477. package/dist/features/search/api/route.d.ts +0 -22
  478. package/dist/features/search/api/route.js +0 -128
  479. package/dist/features/search/columns/index.d.ts +0 -17
  480. package/dist/features/search/columns/index.js +0 -49
  481. package/dist/features/search/components/SearchFiltersRow.d.ts +0 -22
  482. package/dist/features/search/components/SearchFiltersRow.js +0 -21
  483. package/dist/features/search/components/SearchResultsSection.d.ts +0 -53
  484. package/dist/features/search/components/SearchResultsSection.js +0 -35
  485. package/dist/features/search/components/SearchView.d.ts +0 -27
  486. package/dist/features/search/components/SearchView.js +0 -8
  487. package/dist/features/search/hooks/useSearch.d.ts +0 -21
  488. package/dist/features/search/hooks/useSearch.js +0 -33
  489. package/dist/features/search/repository/search.repository.d.ts +0 -14
  490. package/dist/features/search/repository/search.repository.js +0 -59
  491. package/dist/features/search/schemas/index.d.ts +0 -70
  492. package/dist/features/search/schemas/index.js +0 -36
  493. package/dist/features/search/types/index.d.ts +0 -48
  494. package/dist/features/stores/components/StoreScopedSearch.d.ts +0 -17
  495. package/dist/features/stores/components/StoreScopedSearch.js +0 -27
  496. package/dist/seed/defaults/homepage-sections.d.ts +0 -2
  497. package/dist/seed/defaults/homepage-sections.js +0 -60
  498. package/dist/seed/factories/homepage-section.factory.d.ts +0 -10
  499. package/dist/seed/factories/homepage-section.factory.js +0 -13
  500. /package/dist/{features/search/types/index.js → _internal/shared/listing-types/spares/og.d.ts} +0 -0
@@ -0,0 +1,61 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useQuery } from "@tanstack/react-query";
4
+ import { PricesOnly, Stack, Text } from "../../../ui/index.js";
5
+ import { apiClient } from "../../../http/index.js";
6
+ import { CATEGORY_ENDPOINTS } from "../../../constants/api-endpoints.js";
7
+ import { formatCurrency } from "../../../utils/number.formatter.js";
8
+ function unwrap(res) {
9
+ const body = res?.data ?? res;
10
+ return body && typeof body === "object" && "guidance" in body ? body : null;
11
+ }
12
+ /**
13
+ * The seller-facing price hint `description-template/route.ts`'s own
14
+ * docstring names as "C1's job, from a surface that knows who is asking" —
15
+ * renders the inherited competitor-derived market band beside the price
16
+ * field, on the SELLER'S own tax basis (the `/price-guidance` endpoint does
17
+ * that conversion server-side; this component only formats what it returns).
18
+ *
19
+ * Mounted via `ProductForm`'s `renderPriceGuidance` slot rather than fetched
20
+ * unconditionally inside the form — `ProductForm` has no data-fetching of its
21
+ * own (every app-specific concern is a render-prop), and this component is
22
+ * seller/admin-only, not something the form itself should assume a caller
23
+ * wants.
24
+ *
25
+ * 🛑 NEVER mount this on a public page (category/brand detail). A
26
+ * competitor-derived market band is exactly the "a neighbouring surface
27
+ * publishes the range" leak the guest price gate exists to close (CLAUDE.md
28
+ * § "Guest price gating"), even though the figure is advisory rather than a
29
+ * real listing price — which is also why `/price-guidance` itself is
30
+ * seller/admin/moderator-gated, not a public, edge-cached endpoint like its
31
+ * `description-template` sibling.
32
+ */
33
+ export function PriceGuidanceHint({ categoryId, currency = "INR", className = "", }) {
34
+ const query = useQuery({
35
+ queryKey: ["category-price-guidance", categoryId],
36
+ enabled: categoryId.length > 0,
37
+ staleTime: 60 * 60 * 1000,
38
+ queryFn: async () => unwrap(await apiClient.get(CATEGORY_ENDPOINTS.PRICE_GUIDANCE(categoryId))),
39
+ });
40
+ const guidance = query.data?.guidance;
41
+ const basisNote = query.data?.basisNote;
42
+ if (!guidance?.market)
43
+ return null;
44
+ const { median, p25, p75 } = guidance.market;
45
+ const fmt = (n) => formatCurrency(n, currency);
46
+ // p25/p75 are independently optional (see CategoryPriceGuidance's own
47
+ // docstring) — a range renders only when BOTH are present, never a
48
+ // half-range that would misreport the band's shape.
49
+ const range = p25 !== undefined && p75 !== undefined ? ` (${fmt(p25)}–${fmt(p75)})` : "";
50
+ /*
51
+ * `<PricesOnly>` rather than `<GatedPrice>`: this is advisory guidance
52
+ * BESIDE the seller's own price input, not a listing's primary price, so a
53
+ * viewer who may not see prices should get nothing here — a "Sign in to see
54
+ * price" prompt inside a seller form would be nonsense. In practice it
55
+ * never renders for a guest anyway, because the data source
56
+ * (`/api/categories/[id]/price-guidance`) is seller/admin/moderator-gated
57
+ * and 403s before there is a number to format; the wrapper makes that
58
+ * guarantee visible at the render site instead of implied by the route.
59
+ */
60
+ return (_jsx(PricesOnly, { children: _jsxs(Stack, { gap: "xs", className: className, children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Market median:", " ", _jsx(Text, { as: "span", weight: "medium", color: "primary", children: fmt(median) }), range, basisNote?.suffix ? ` ${basisNote.suffix}` : ""] }), basisNote?.note && _jsx(Text, { size: "xs", color: "faint", children: basisNote.note })] }) }));
61
+ }
@@ -0,0 +1,27 @@
1
+ import React from "react";
2
+ export interface SubcategoryCardItem {
3
+ id: string;
4
+ name: string;
5
+ href: string;
6
+ /** Rollup listing count. Omitted when zero — a card reading "0" invites a dead click. */
7
+ count?: number;
8
+ /** A real picture, when the row resolved one. */
9
+ imageUrl?: string;
10
+ /** An emoji, when that is all the row has. Never passed to an `<img>`. */
11
+ emoji?: string;
12
+ /** Direct children's names — searched, never rendered. */
13
+ childNames?: readonly string[];
14
+ /**
15
+ * Depth in the forest: 0 is a root, 1 its child, and so on.
16
+ *
17
+ * Rendered as a small "L0 / L1 / L2" marker so a row of ancestors reads as a
18
+ * LADDER rather than as five unrelated cards. Omitted on the subcategory row
19
+ * itself, where every card is the same depth by construction and the marker
20
+ * would be noise repeated N times.
21
+ */
22
+ level?: number;
23
+ }
24
+ export declare function SubcategoryCardRow({ items, label, }: {
25
+ items: readonly SubcategoryCardItem[];
26
+ label?: string;
27
+ }): React.JSX.Element | null;
@@ -0,0 +1,98 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /*
4
+ * WHY: the Subcategories row was a pill per child in an `<OverflowRow>`, which
5
+ * collapses to a searchable dropdown when it does not fit. A pill carries
6
+ * only a name and a count, so on a tree this wide every child looked the
7
+ * same and the row read as a wall of words.
8
+ * WHAT: small cards in the shared `<HorizontalScroller>`, with a filter box
9
+ * that matches ONE LEVEL DEEPER than the cards themselves.
10
+ *
11
+ * EXPORTS: SubcategoryCardRow, SubcategoryCardItem
12
+ *
13
+ * ## 🛑 The filter matches grandchildren, and that is the point
14
+ *
15
+ * Searching "Dranzer" on Spinning Tops should surface *Beyblade Original* —
16
+ * the card you must click to get there — not nothing. So each card carries its
17
+ * own direct children's names (`childNames`) and the match runs over the card
18
+ * name PLUS that list. One level only: the full subtree of a root is ~1,200
19
+ * nodes, and matching all of it would surface a card because of a name six
20
+ * levels down that the visitor will never see on the next page either.
21
+ *
22
+ * `childNames` is built from data the page has ALREADY fetched
23
+ * (`getDescendants` is one array-contains query over `parentIds`), so the
24
+ * filter costs no request — it is pure in-browser narrowing of a list that is
25
+ * already in memory.
26
+ *
27
+ * ## 🛑 Why this does not reintroduce the bug `OverflowRow` was built for
28
+ *
29
+ * CLAUDE.md records a bare `overflow-x-auto` row as an anti-pattern, measured:
30
+ * "4 of 12 subcategories past the right edge with no scrollbar and no arrow".
31
+ * The discoverability problem is real and this row answers it twice over —
32
+ * `<HorizontalScroller showArrows>` renders real scroll affordances, and the
33
+ * filter box makes an off-screen card reachable by typing rather than by
34
+ * scrolling to it. What is given up is the collapse-to-dropdown behaviour,
35
+ * which was the fallback for exactly that problem.
36
+ *
37
+ * @tag domain:categories
38
+ * @tag layer:feature
39
+ * @tag pattern:none
40
+ * @tag access:client
41
+ * @tag sideEffects:none
42
+ */
43
+ import { useMemo, useState } from "react";
44
+ import { Div, HorizontalScroller, Row, Span, Stack, Text } from "../../../ui/index.js";
45
+ import { FieldInput } from "../../../ui/forms/index.js";
46
+ import { MediaImage } from "../../media/MediaImage.js";
47
+ import { TextLink } from "../../../ui/components/TextLink.js";
48
+ /**
49
+ * The filter appears once this many NAMES are searchable — cards plus the
50
+ * grandchildren they match on.
51
+ *
52
+ * 🛑 Counting cards alone was wrong, and wrong in the case that prompted this
53
+ * row. Spinning Tops shows FIVE cards, so a `cards >= 6` rule hid the filter
54
+ * on exactly the page the request came from — while that page actually has 26
55
+ * searchable names (5 cards + 21 grandchildren), which is precisely where
56
+ * typing beats scanning.
57
+ *
58
+ * The number of cards is not what the filter searches, so it is not what
59
+ * should decide whether the filter exists.
60
+ */
61
+ const FILTER_MIN_NAMES = 12;
62
+ /** Case- and whitespace-insensitive contains. */
63
+ function matches(item, needle) {
64
+ if (item.name.toLowerCase().includes(needle))
65
+ return true;
66
+ /*
67
+ * 🛑 `?? []`, not a non-null assertion. A card whose children were never
68
+ * resolved must still match on its OWN name — otherwise a transient fetch
69
+ * failure upstream silently removes it from every search, which looks like
70
+ * the category does not exist rather than like a degraded read.
71
+ */
72
+ return (item.childNames ?? []).some((n) => n.toLowerCase().includes(needle));
73
+ }
74
+ export function SubcategoryCardRow({ items, label = "Subcategories", }) {
75
+ const [query, setQuery] = useState("");
76
+ const needle = query.trim().toLowerCase();
77
+ const filtered = useMemo(() => (needle ? items.filter((i) => matches(i, needle)) : items), [items, needle]);
78
+ const searchableNames = useMemo(() => items.reduce((n, i) => n + 1 + (i.childNames?.length ?? 0), 0), [items]);
79
+ if (items.length === 0)
80
+ return null;
81
+ const showFilter = searchableNames >= FILTER_MIN_NAMES;
82
+ return (_jsxs(Stack, { gap: "xs", children: [_jsxs(Row, { justify: "between", align: "center", gap: "md", wrap: true, children: [_jsx(Text, { size: "xs", color: "muted", weight: "medium", children: label }), showFilter && (_jsx(Div, { className: "w-full sm:w-64", children: _jsx(FieldInput, { name: "subcategory-filter", type: "search", value: query, onChange: setQuery, placeholder: "Filter subcategories\u2026", "aria-label": "Filter subcategories" }) }))] }), filtered.length === 0 ? (
83
+ /*
84
+ * A real empty state rather than a blank strip. The row is otherwise
85
+ * indistinguishable from "this category has no subcategories", which
86
+ * is a different and much more alarming thing to tell someone.
87
+ */
88
+ _jsxs(Text, { size: "sm", color: "muted", children: ["No subcategory matches \u201C", query.trim(), "\u201D."] })) : (_jsx(HorizontalScroller, { showArrows: true, showScrollbar: false, gap: 12, minItemWidth: 150, items: filtered, keyExtractor: (item) => item.id, renderItem: (item) => (_jsx(TextLink, { href: item.href, className: "group block h-full no-underline", "aria-label": item.count !== undefined
89
+ ? `${item.name}, ${item.count} listings`
90
+ : item.name, children: _jsxs(Stack, { gap: "xs", surface: "card", rounded: "lg", border: "subtle", padding: "sm", className: "h-full transition-colors hover:bg-surface-hover", children: [_jsx(Div, { surface: "muted", rounded: "md", className: `relative aspect-square w-full overflow-hidden flex-shrink-0`, children: item.imageUrl ? (_jsx(MediaImage, { src: item.imageUrl, alt: "", size: "thumbnail", className: "transition-transform duration-300 group-hover:scale-105" })) : (
91
+ /*
92
+ * The emoji is the FALLBACK, never an overlay — same rule
93
+ * as `CategoryCard` and `resolveTaxonomyVisual`: an emoji
94
+ * is what you show when there is no picture, not something
95
+ * you paint on top of one.
96
+ */
97
+ _jsx(Row, { align: "center", justify: "center", textSize: "2xl", className: "absolute inset-0", children: item.emoji ?? "" })) }), item.level !== undefined && (_jsx(Span, { size: "xs", color: "muted", weight: "medium", children: item.level === 0 ? "L0 · root" : `L${item.level}` })), _jsx(Text, { size: "xs", weight: "medium", className: "line-clamp-2", children: item.name }), item.count !== undefined && (_jsx(Span, { size: "xs", color: "muted", children: item.count.toLocaleString() }))] }) })) }))] }));
98
+ }
@@ -10,29 +10,36 @@
10
10
  * this shape is browsed, and what the category detail page already supports
11
11
  * (it fetches every descendant and renders them tier-grouped).
12
12
  *
13
- * ## 🛑 Parts cannot be matched on `categoryType`
13
+ * ## 🛑 There is no Parts tab, and that is deliberate
14
14
  *
15
- * Brands, franchises and bundles each carry a discriminator. Parts deliberately
16
- * DO NOT — `gen-category-parts.mjs` omits `categoryType` because 92 files read
17
- * it and a new value opts a row out of every path that works. So a part is
18
- * identified by its ANCESTRY: a leaf whose chain passes through one of the four
19
- * `*-parts` nodes. That is a fact about where it lives, not a flag that can
20
- * drift from it.
15
+ * It was removed 2026-10-20. Parts have a better home than a flat tab over
16
+ * 752 leaves: `/parts/[classification]/[code]` is a purpose-built part
17
+ * catalogue with composition data, and the `spares` listing type sells loose
18
+ * parts. A fifth tab listing every part leaf duplicated the worse half of
19
+ * both.
20
+ *
21
+ * The part leaves themselves are untouched and still reachable by drilling
22
+ * down from their `*-parts` node, which is also why no `categoryType` was
23
+ * involved: a part is identified by ANCESTRY, never by a discriminator
24
+ * (`gen-category-parts.mjs` omits `categoryType` because 92 files read it and
25
+ * a new value opts a row out of every path that works).
26
+ *
27
+ * 🛑 Do not confuse this with the product-detail **Parts composition tab**
28
+ * ("What it is made of"), gated by `LISTING_TYPE_HAS_COMPOSITION`. That is a
29
+ * different feature on a different surface and is unaffected.
21
30
  *
22
31
  * ## Why the tabs partition ONE payload
23
32
  *
24
- * The listing already fetches `?flat=true` once. Deriving all five tabs from
33
+ * The listing already fetches `?flat=true` once. Deriving all four tabs from
25
34
  * that single cached response costs no extra request — a per-tab query would
26
- * turn one fetch into five for the same data.
35
+ * turn one fetch into four for the same data.
27
36
  *
28
37
  * @tag domain:categories
29
38
  * @tag layer:constants
30
39
  * @tag pattern:shared-literal
31
40
  * @tag sideEffects:none
32
41
  */
33
- /** The tier-2 nodes every part leaf hangs beneath. */
34
- export declare const PART_PARENT_IDS: readonly string[];
35
- export type CategoryTabKey = "categories" | "brands" | "franchises" | "parts" | "bundles";
42
+ export type CategoryTabKey = "categories" | "brands" | "franchises" | "bundles";
36
43
  export interface CategoryTabDef {
37
44
  key: CategoryTabKey;
38
45
  label: string;
@@ -40,7 +47,7 @@ export interface CategoryTabDef {
40
47
  /**
41
48
  * 🛑 No "All" tab. It was the default and it was the problem — it is what
42
49
  * rendered 1,185 undifferentiated rows. Every tab here answers a question
43
- * ("which parts do you have?"); "all" answers none of them.
50
+ * ("which brands do you carry?"); "all" answers none of them.
44
51
  */
45
52
  export declare const CATEGORY_TABS: readonly CategoryTabDef[];
46
53
  export declare const DEFAULT_CATEGORY_TAB: CategoryTabKey;
@@ -48,19 +55,7 @@ export declare const DEFAULT_CATEGORY_TAB: CategoryTabKey;
48
55
  interface TabbableCategory {
49
56
  categoryType?: string | null;
50
57
  tier?: number | null;
51
- parentIds?: readonly string[] | null;
52
- isLeaf?: boolean | null;
53
58
  }
54
- /**
55
- * A LEAF whose ancestry passes through a `*-parts` node.
56
- *
57
- * 🛑 `isLeaf` is required, not incidental. Without it the tab also matches the
58
- * 20 classification NODES — "Attack Rings", "Weight Disks" — because their own
59
- * `parentIds` include a `*-parts` node too, giving 772 entries that mix groups
60
- * with the parts inside them. A part is a leaf by itself; the classification is
61
- * a branch you reach by drilling down.
62
- */
63
- export declare function isPartCategory(c: TabbableCategory): boolean;
64
59
  /**
65
60
  * Does this row belong on the given tab?
66
61
  *
@@ -10,63 +10,48 @@
10
10
  * this shape is browsed, and what the category detail page already supports
11
11
  * (it fetches every descendant and renders them tier-grouped).
12
12
  *
13
- * ## 🛑 Parts cannot be matched on `categoryType`
13
+ * ## 🛑 There is no Parts tab, and that is deliberate
14
14
  *
15
- * Brands, franchises and bundles each carry a discriminator. Parts deliberately
16
- * DO NOT — `gen-category-parts.mjs` omits `categoryType` because 92 files read
17
- * it and a new value opts a row out of every path that works. So a part is
18
- * identified by its ANCESTRY: a leaf whose chain passes through one of the four
19
- * `*-parts` nodes. That is a fact about where it lives, not a flag that can
20
- * drift from it.
15
+ * It was removed 2026-10-20. Parts have a better home than a flat tab over
16
+ * 752 leaves: `/parts/[classification]/[code]` is a purpose-built part
17
+ * catalogue with composition data, and the `spares` listing type sells loose
18
+ * parts. A fifth tab listing every part leaf duplicated the worse half of
19
+ * both.
20
+ *
21
+ * The part leaves themselves are untouched and still reachable by drilling
22
+ * down from their `*-parts` node, which is also why no `categoryType` was
23
+ * involved: a part is identified by ANCESTRY, never by a discriminator
24
+ * (`gen-category-parts.mjs` omits `categoryType` because 92 files read it and
25
+ * a new value opts a row out of every path that works).
26
+ *
27
+ * 🛑 Do not confuse this with the product-detail **Parts composition tab**
28
+ * ("What it is made of"), gated by `LISTING_TYPE_HAS_COMPOSITION`. That is a
29
+ * different feature on a different surface and is unaffected.
21
30
  *
22
31
  * ## Why the tabs partition ONE payload
23
32
  *
24
- * The listing already fetches `?flat=true` once. Deriving all five tabs from
33
+ * The listing already fetches `?flat=true` once. Deriving all four tabs from
25
34
  * that single cached response costs no extra request — a per-tab query would
26
- * turn one fetch into five for the same data.
35
+ * turn one fetch into four for the same data.
27
36
  *
28
37
  * @tag domain:categories
29
38
  * @tag layer:constants
30
39
  * @tag pattern:shared-literal
31
40
  * @tag sideEffects:none
32
41
  */
33
- /** The tier-2 nodes every part leaf hangs beneath. */
34
- export const PART_PARENT_IDS = [
35
- "category-original-parts",
36
- "category-metal-parts",
37
- "category-burst-parts",
38
- "category-x-parts",
39
- ];
40
42
  /**
41
43
  * 🛑 No "All" tab. It was the default and it was the problem — it is what
42
44
  * rendered 1,185 undifferentiated rows. Every tab here answers a question
43
- * ("which parts do you have?"); "all" answers none of them.
45
+ * ("which brands do you carry?"); "all" answers none of them.
44
46
  */
45
47
  export const CATEGORY_TABS = [
46
48
  { key: "categories", label: "Categories" },
47
49
  { key: "brands", label: "Brands" },
48
50
  { key: "franchises", label: "Franchises" },
49
- { key: "parts", label: "Parts" },
50
51
  { key: "bundles", label: "Bundles" },
51
52
  ];
52
53
  export const DEFAULT_CATEGORY_TAB = "categories";
53
54
  const typeOf = (c) => String(c.categoryType ?? "");
54
- /**
55
- * A LEAF whose ancestry passes through a `*-parts` node.
56
- *
57
- * 🛑 `isLeaf` is required, not incidental. Without it the tab also matches the
58
- * 20 classification NODES — "Attack Rings", "Weight Disks" — because their own
59
- * `parentIds` include a `*-parts` node too, giving 772 entries that mix groups
60
- * with the parts inside them. A part is a leaf by itself; the classification is
61
- * a branch you reach by drilling down.
62
- */
63
- export function isPartCategory(c) {
64
- if (typeOf(c))
65
- return false; // brand / bundle / franchise / sublisting
66
- if (c.isLeaf !== true)
67
- return false;
68
- return (c.parentIds ?? []).some((p) => PART_PARENT_IDS.includes(String(p)));
69
- }
70
55
  /**
71
56
  * Does this row belong on the given tab?
72
57
  *
@@ -83,13 +68,11 @@ export function matchesCategoryTab(c, tab) {
83
68
  return type === "franchise";
84
69
  case "bundles":
85
70
  return type === "bundle";
86
- case "parts":
87
- return isPartCategory(c);
88
71
  case "categories":
89
72
  /*
90
- * A plain, top-level category. Parts are excluded by the type check
91
- * being empty AND tier 0 — no part leaf is tier 0 — but a reader should
92
- * not have to derive that, so the root test carries the meaning.
73
+ * A plain, top-level category. Part and model leaves are excluded by the
74
+ * tier test — no leaf is tier 0 — but a reader should not have to derive
75
+ * that, so the root test carries the meaning.
93
76
  */
94
77
  return type === "" && Number(c.tier ?? -1) === 0;
95
78
  default:
@@ -32,6 +32,18 @@ export declare class CategoriesRepository extends BaseRepository<CategoryDocumen
32
32
  * indistinguishable from a count of nothing (Root Cause #72).
33
33
  */
34
34
  liveTotalsFor(ids: readonly string[]): Promise<Map<string, number>>;
35
+ /**
36
+ * `db.getAll`, not N `findById` calls — same reasoning as `liveTotalsFor`
37
+ * just above. Built for the site-wide search index's local-resolution
38
+ * path: the client resolves candidate category ids from the public
39
+ * artifact at zero reads, and this is the one query the server still
40
+ * needs to turn those ids into real documents.
41
+ *
42
+ * A missing id is simply ABSENT from the returned array, never null —
43
+ * the caller filters, so there is nothing to distinguish a deleted
44
+ * category from one that never existed, and nothing needs to.
45
+ */
46
+ getManyByIds(ids: readonly string[]): Promise<CategoryDocument[]>;
35
47
  getCategoryBySlug(slug: string): Promise<CategoryDocument | null>;
36
48
  getRootCategories(): Promise<CategoryDocument[]>;
37
49
  getLeafCategories(): Promise<CategoryDocument[]>;
@@ -128,6 +128,26 @@ export class CategoriesRepository extends BaseRepository {
128
128
  const { total } = await getLiveCategoryCounts(this.db, ids);
129
129
  return total;
130
130
  }
131
+ /**
132
+ * `db.getAll`, not N `findById` calls — same reasoning as `liveTotalsFor`
133
+ * just above. Built for the site-wide search index's local-resolution
134
+ * path: the client resolves candidate category ids from the public
135
+ * artifact at zero reads, and this is the one query the server still
136
+ * needs to turn those ids into real documents.
137
+ *
138
+ * A missing id is simply ABSENT from the returned array, never null —
139
+ * the caller filters, so there is nothing to distinguish a deleted
140
+ * category from one that never existed, and nothing needs to.
141
+ */
142
+ async getManyByIds(ids) {
143
+ if (ids.length === 0)
144
+ return [];
145
+ const refs = ids.map((id) => this.getCollection().doc(id));
146
+ const snaps = await this.db.getAll(...refs);
147
+ return snaps
148
+ .filter((s) => s.exists)
149
+ .map((s) => this.mapDoc(s));
150
+ }
131
151
  async getCategoryBySlug(slug) {
132
152
  try {
133
153
  const snapshot = await this.db
@@ -126,6 +126,27 @@ export interface CategoryDocument extends BaseDocument {
126
126
  brandCountry?: string;
127
127
  /** Brand founding year — categoryType==="brand". */
128
128
  brandFounded?: number;
129
+ /**
130
+ * The Hasbro / international-market name for this generation or wave, where
131
+ * it genuinely differs from the Takara-Tomy one. Two real pairs exist and
132
+ * they are separate waves, not one renaming: TT **God** ships as Hasbro
133
+ * **Evolution**, and TT **Cho-Z** ships as Hasbro **Turbo**.
134
+ *
135
+ * 🛑 RENDERED — "Also known as" on the category detail page. That is the
136
+ * whole difference from `aliases` below, which feeds `searchTxt` and is
137
+ * rendered nowhere. A buyer who only knows the Hasbro season name needs to
138
+ * SEE the correspondence, not merely be found by it.
139
+ *
140
+ * Left unset wherever a divergence is not confirmed. An honest gap beats a
141
+ * guessed pairing — same precedent as the 25 of 220 models with no cover and
142
+ * the deliberately partial `FRANCHISE_BY_CATEGORY_ROOT`.
143
+ */
144
+ hasbroName?: string;
145
+ /**
146
+ * Cover for the Hasbro-named variant, shown beside the primary cover when
147
+ * both exist. Same `/media/<slug>` form as `display.coverImage`.
148
+ */
149
+ hasbroCoverImage?: string;
129
150
  /**
130
151
  * Short bullet points — "why shop this category/brand." Category and
131
152
  * brand detail pages only; renders nothing when absent/empty.
@@ -255,7 +276,7 @@ export declare const MIN_ITEMS_FOR_FEATURED: 8;
255
276
  export declare const MAX_FEATURED_CATEGORIES: 4;
256
277
  export declare const DEFAULT_CATEGORY_DATA: Partial<CategoryDocument>;
257
278
  export declare const CATEGORIES_PUBLIC_FIELDS: readonly ["id", "name", "slug", "description", "rootId", "parentIds", "childrenIds", "tier", "path", "order", "position", "subtreeSize", "isLeaf", "metrics.totalProductCount", "metrics.totalAuctionCount", "metrics.totalItemCount", "isFeatured", "featuredPriority", "isBrand", "seo", "display", "isActive", "ancestors", "highlights", "faqs", "contentBody", "descriptionTemplates", "productDefaults", "aliases"];
258
- export declare const CATEGORIES_UPDATABLE_FIELDS: readonly ["name", "slug", "description", "order", "isFeatured", "featuredPriority", "isBrand", "seo", "display", "isActive", "isSearchable", "highlights", "faqs", "contentBody", "descriptionTemplates", "productDefaults", "aliases"];
279
+ export declare const CATEGORIES_UPDATABLE_FIELDS: readonly ["name", "slug", "description", "order", "isFeatured", "featuredPriority", "isBrand", "seo", "display", "isActive", "isSearchable", "highlights", "faqs", "contentBody", "descriptionTemplates", "productDefaults", "aliases", "hasbroName", "hasbroCoverImage"];
259
280
  export type CategoryCreateInput = Omit<CategoryDocument, "id" | "createdAt" | "updatedAt" | "metrics" | "isLeaf" | "ancestors"> & {
260
281
  parentIds?: string[];
261
282
  parentId?: string | null;
@@ -123,6 +123,16 @@ export const CATEGORIES_UPDATABLE_FIELDS = [
123
123
  "descriptionTemplates",
124
124
  "productDefaults",
125
125
  "aliases",
126
+ /*
127
+ * The regional-naming pair. Updatable for the same reason the five above
128
+ * are: without it `CategoryUpdateInput` cannot express the field and the
129
+ * admin editor would save a 200 and write nothing. Wave names are editorial
130
+ * and get corrected — the one case this plan would not set (Burst Ultimate ↔
131
+ * QuadStrike) is exactly the kind a human may later confirm and want to fill
132
+ * in without a reseed.
133
+ */
134
+ "hasbroName",
135
+ "hasbroCoverImage",
126
136
  ];
127
137
  export const categoryQueryHelpers = {
128
138
  bySlug: (slug) => ["slug", "==", slug],
@@ -769,6 +769,8 @@ export declare const categoryFirestoreSchema: z.ZodObject<{
769
769
  brandWebsite: z.ZodOptional<z.ZodString>;
770
770
  brandCountry: z.ZodOptional<z.ZodString>;
771
771
  brandFounded: z.ZodOptional<z.ZodNumber>;
772
+ hasbroName: z.ZodOptional<z.ZodString>;
773
+ hasbroCoverImage: z.ZodOptional<z.ZodString>;
772
774
  highlights: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
773
775
  faqs: z.ZodOptional<z.ZodArray<z.ZodObject<{
774
776
  question: z.ZodString;
@@ -1363,6 +1365,8 @@ export declare const categoryFirestoreSchema: z.ZodObject<{
1363
1365
  brandWebsite?: string | undefined;
1364
1366
  brandCountry?: string | undefined;
1365
1367
  brandFounded?: number | undefined;
1368
+ hasbroName?: string | undefined;
1369
+ hasbroCoverImage?: string | undefined;
1366
1370
  highlights?: string[] | undefined;
1367
1371
  contentBody?: string | undefined;
1368
1372
  descriptionTemplates?: {
@@ -1534,6 +1538,8 @@ export declare const categoryFirestoreSchema: z.ZodObject<{
1534
1538
  brandWebsite?: string | undefined;
1535
1539
  brandCountry?: string | undefined;
1536
1540
  brandFounded?: number | undefined;
1541
+ hasbroName?: string | undefined;
1542
+ hasbroCoverImage?: string | undefined;
1537
1543
  highlights?: string[] | undefined;
1538
1544
  contentBody?: string | undefined;
1539
1545
  descriptionTemplates?: {
@@ -167,6 +167,8 @@ export const categoryFirestoreSchema = z.object({
167
167
  brandWebsite: z.string().optional(),
168
168
  brandCountry: z.string().optional(),
169
169
  brandFounded: z.number().int().optional(),
170
+ hasbroName: z.string().optional(),
171
+ hasbroCoverImage: z.string().optional(),
170
172
  highlights: z.array(z.string()).optional(),
171
173
  faqs: z.array(z.object({ question: z.string(), answer: z.string() })).optional(),
172
174
  aliases: z.array(z.string()).optional(),
@@ -49,6 +49,14 @@ export interface CategoryItem {
49
49
  brandCountry?: string;
50
50
  /** Brand founded year — only set when categoryType==="brand". */
51
51
  brandFounded?: number;
52
+ /**
53
+ * Hasbro / international-market name for this generation or wave (TT "God" →
54
+ * Hasbro "Evolution"; TT "Cho-Z" → Hasbro "Turbo"). Rendered as "Also known
55
+ * as" — unlike `aliases`, which only feeds search.
56
+ */
57
+ hasbroName?: string;
58
+ /** Cover for the Hasbro-named variant, shown beside the primary cover. */
59
+ hasbroCoverImage?: string;
52
60
  /** Short bullet points — "why shop this category/brand." */
53
61
  highlights?: string[];
54
62
  /** A handful of Q&A pairs shown on the category/brand detail page. */
@@ -14,8 +14,21 @@ export interface TaxonomyVisual {
14
14
  /**
15
15
  * Resolve the leading visual for a taxonomy row.
16
16
  *
17
- * Precedence: `display.icon` (as image when it is a URL, else as emoji) →
18
- * `display.coverImage`. Returns an empty object when the row has neither, so
19
- * the caller can fall back to an initial rather than reserving a blank box.
17
+ * Precedence: an `icon` that is a URL → `coverImage` → an `icon` that is an
18
+ * EMOJI. Returns an empty object when the row has none, so the caller can fall
19
+ * back to an initial rather than reserving a blank box.
20
+ *
21
+ * 🛑 The emoji ranks LAST, and that ordering changed 2026-10-20. It used to be
22
+ * "icon, whatever it holds, then cover", which meant a row carrying both a
23
+ * real photograph and a decorative emoji rendered the emoji — the twelve
24
+ * forest roots are exactly that shape. `brand-rows.ts` trap 2 already names
25
+ * this as the defect rather than the intent: the brand seed leaves `icon`
26
+ * unset *precisely because* an emoji there would beat a real logo. Honouring
27
+ * that reading everywhere is what lets a root keep its emoji for menus while
28
+ * its page shows the photograph.
29
+ *
30
+ * An icon that is a URL still wins — that is `BrandsSection`'s long-standing
31
+ * convention, where `icon` holds the brand logo and is the more specific
32
+ * choice than a generic cover.
20
33
  */
21
34
  export declare function resolveTaxonomyVisual(row: TaxonomyVisualSource | null | undefined): TaxonomyVisual;
@@ -7,10 +7,12 @@
7
7
  * either prints a raw URL as visible text, or hands an emoji to an
8
8
  * `<img src>` that then 404s into a broken-image glyph.
9
9
  *
10
- * On top of that, `icon` BEATS `coverImage` — `brand-rows.ts` trap 2
11
- * records why: the brand seed deliberately leaves `icon` unset precisely
12
- * because an emoji left there would win over a real logo. A renderer that
13
- * checks `coverImage` first silently inverts that rule.
10
+ * On top of that there is an ORDERING question between the two, and
11
+ * `brand-rows.ts` trap 2 settles it: the brand seed leaves `icon` unset
12
+ * *precisely because* an emoji there would win over a real logo. So an
13
+ * emoji must rank BELOW a real cover (it is decoration), while an icon
14
+ * that is a URL ranks above one (it is the more specific asset). That is
15
+ * the precedence implemented below, corrected 2026-10-20.
14
16
  *
15
17
  * Two decisions, both easy to get wrong, previously made at each call
16
18
  * site. This is the one place that makes them.
@@ -42,17 +44,31 @@ function looksLikeUrl(value) {
42
44
  /**
43
45
  * Resolve the leading visual for a taxonomy row.
44
46
  *
45
- * Precedence: `display.icon` (as image when it is a URL, else as emoji) →
46
- * `display.coverImage`. Returns an empty object when the row has neither, so
47
- * the caller can fall back to an initial rather than reserving a blank box.
47
+ * Precedence: an `icon` that is a URL → `coverImage` → an `icon` that is an
48
+ * EMOJI. Returns an empty object when the row has none, so the caller can fall
49
+ * back to an initial rather than reserving a blank box.
50
+ *
51
+ * 🛑 The emoji ranks LAST, and that ordering changed 2026-10-20. It used to be
52
+ * "icon, whatever it holds, then cover", which meant a row carrying both a
53
+ * real photograph and a decorative emoji rendered the emoji — the twelve
54
+ * forest roots are exactly that shape. `brand-rows.ts` trap 2 already names
55
+ * this as the defect rather than the intent: the brand seed leaves `icon`
56
+ * unset *precisely because* an emoji there would beat a real logo. Honouring
57
+ * that reading everywhere is what lets a root keep its emoji for menus while
58
+ * its page shows the photograph.
59
+ *
60
+ * An icon that is a URL still wins — that is `BrandsSection`'s long-standing
61
+ * convention, where `icon` holds the brand logo and is the more specific
62
+ * choice than a generic cover.
48
63
  */
49
64
  export function resolveTaxonomyVisual(row) {
50
65
  const icon = row?.display?.icon?.trim();
51
- if (icon) {
52
- return looksLikeUrl(icon) ? { imageUrl: icon } : { emoji: icon };
53
- }
66
+ if (icon && looksLikeUrl(icon))
67
+ return { imageUrl: icon };
54
68
  const cover = row?.display?.coverImage?.trim();
55
69
  if (cover && looksLikeUrl(cover))
56
70
  return { imageUrl: cover };
71
+ if (icon)
72
+ return { emoji: icon };
57
73
  return {};
58
74
  }