@mohasinac/appkit 4.53.0 → 4.53.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (520) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/client/scaffolds/AppShell.js +1 -1
  3. package/dist/_internal/client/theme/ThemeProvider.js +15 -1
  4. package/dist/_internal/server/features/auctions/og.d.ts +3 -1
  5. package/dist/_internal/server/features/auctions/og.js +3 -1
  6. package/dist/_internal/server/features/bundles/actions.js +1 -1
  7. package/dist/_internal/server/features/business-documents/download.d.ts +41 -0
  8. package/dist/_internal/server/features/business-documents/download.js +91 -0
  9. package/dist/_internal/server/features/categories/adapters.d.ts +1 -1
  10. package/dist/_internal/server/features/categories/adapters.js +10 -0
  11. package/dist/_internal/server/features/categories/price-guidance-resolve.d.ts +66 -0
  12. package/dist/_internal/server/features/categories/price-guidance-resolve.js +138 -0
  13. package/dist/_internal/server/features/checkout/actions.d.ts +14 -0
  14. package/dist/_internal/server/features/checkout/actions.js +106 -12
  15. package/dist/_internal/server/features/checkout/bundle-expansion.js +17 -0
  16. package/dist/_internal/server/features/checkout/data.d.ts +22 -0
  17. package/dist/_internal/server/features/checkout/group-rule-revalidation.d.ts +34 -0
  18. package/dist/_internal/server/features/checkout/group-rule-revalidation.js +124 -0
  19. package/dist/_internal/server/features/checkout/supplier-identity.d.ts +55 -0
  20. package/dist/_internal/server/features/checkout/supplier-identity.js +81 -0
  21. package/dist/_internal/server/features/classified/metadata.js +1 -1
  22. package/dist/_internal/server/features/classified/og.d.ts +3 -1
  23. package/dist/_internal/server/features/classified/og.js +3 -1
  24. package/dist/_internal/server/features/digital-code/metadata.js +1 -1
  25. package/dist/_internal/server/features/digital-code/og.d.ts +3 -1
  26. package/dist/_internal/server/features/digital-code/og.js +3 -1
  27. package/dist/_internal/server/features/digital-code/pool.d.ts +19 -4
  28. package/dist/_internal/server/features/digital-code/pool.js +19 -4
  29. package/dist/_internal/server/features/live/metadata.js +1 -1
  30. package/dist/_internal/server/features/live/og.d.ts +3 -1
  31. package/dist/_internal/server/features/live/og.js +3 -1
  32. package/dist/_internal/server/features/messages/actions.js +1 -1
  33. package/dist/_internal/server/features/parts/og.js +1 -1
  34. package/dist/_internal/server/features/pre-orders/og.d.ts +3 -1
  35. package/dist/_internal/server/features/pre-orders/og.js +3 -1
  36. package/dist/_internal/server/features/price-index/market.d.ts +57 -0
  37. package/dist/_internal/server/features/price-index/market.js +120 -0
  38. package/dist/_internal/server/features/price-index/search.d.ts +18 -0
  39. package/dist/_internal/server/features/price-index/search.js +47 -7
  40. package/dist/_internal/server/features/prize-draws/og.d.ts +3 -1
  41. package/dist/_internal/server/features/prize-draws/og.js +3 -1
  42. package/dist/_internal/server/features/products/group-members.d.ts +26 -0
  43. package/dist/_internal/server/features/products/group-members.js +7 -1
  44. package/dist/_internal/server/features/products/list-public.d.ts +12 -0
  45. package/dist/_internal/server/features/products/list-public.js +10 -1
  46. package/dist/_internal/server/features/products/listing-tab-counts.d.ts +25 -1
  47. package/dist/_internal/server/features/products/listing-tab-counts.js +128 -10
  48. package/dist/_internal/server/features/products/og.d.ts +3 -1
  49. package/dist/_internal/server/features/products/og.js +3 -1
  50. package/dist/_internal/server/features/search/scoped-sources.d.ts +26 -0
  51. package/dist/_internal/server/features/search/scoped-sources.js +252 -0
  52. package/dist/_internal/server/features/search/search-everything.d.ts +57 -0
  53. package/dist/_internal/server/features/search/search-everything.js +70 -20
  54. package/dist/_internal/server/features/seo/sitemap.js +6 -0
  55. package/dist/_internal/server/features/shared/listing-adapters.js +4 -2
  56. package/dist/_internal/server/features/sticker-trace/adapters.d.ts +11 -0
  57. package/dist/_internal/server/features/sticker-trace/adapters.js +1 -0
  58. package/dist/_internal/server/features/sticker-trace/promote-to-listings.d.ts +26 -0
  59. package/dist/_internal/server/features/sticker-trace/promote-to-listings.js +264 -0
  60. package/dist/_internal/server/features/sticker-trace/sheet-actions.d.ts +3 -1
  61. package/dist/_internal/server/features/sticker-trace/sheet-actions.js +32 -0
  62. package/dist/_internal/server/features/sticker-trace/storage.d.ts +20 -1
  63. package/dist/_internal/server/features/sticker-trace/storage.js +20 -1
  64. package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
  65. package/dist/_internal/server/features/stores/adapters.js +16 -0
  66. package/dist/_internal/server/features/stores/index.d.ts +1 -0
  67. package/dist/_internal/server/features/stores/index.js +1 -0
  68. package/dist/_internal/server/features/stores/tabs.d.ts +30 -0
  69. package/dist/_internal/server/features/stores/tabs.js +124 -0
  70. package/dist/_internal/shared/checkout/rules/_defaults.js +0 -2
  71. package/dist/_internal/shared/checkout/rules/_limits.d.ts +14 -1
  72. package/dist/_internal/shared/checkout/rules/_limits.js +14 -1
  73. package/dist/_internal/shared/checkout/rules/_registry.js +2 -0
  74. package/dist/_internal/shared/checkout/rules/auction.rule.js +9 -1
  75. package/dist/_internal/shared/checkout/rules/bundle.rule.js +13 -0
  76. package/dist/_internal/shared/checkout/rules/index.d.ts +2 -0
  77. package/dist/_internal/shared/checkout/rules/index.js +9 -0
  78. package/dist/_internal/shared/checkout/rules/offer.rule.js +0 -1
  79. package/dist/_internal/shared/checkout/rules/payment-gate.d.ts +26 -0
  80. package/dist/_internal/shared/checkout/rules/payment-gate.js +133 -0
  81. package/dist/_internal/shared/checkout/rules/prize-draw.rule.js +29 -8
  82. package/dist/_internal/shared/checkout/rules/spares.rule.d.ts +16 -0
  83. package/dist/_internal/shared/checkout/rules/spares.rule.js +5 -0
  84. package/dist/_internal/shared/checkout/rules/types.d.ts +26 -2
  85. package/dist/_internal/shared/config/schema.d.ts +122 -0
  86. package/dist/_internal/shared/features/cart/schema.js +3 -2
  87. package/dist/_internal/shared/features/products/group-rules.d.ts +75 -0
  88. package/dist/_internal/shared/features/products/group-rules.js +142 -0
  89. package/dist/_internal/shared/listing-types/_registry.d.ts +24 -0
  90. package/dist/_internal/shared/listing-types/_registry.js +39 -1
  91. package/dist/_internal/shared/listing-types/art/config.d.ts +1 -0
  92. package/dist/_internal/shared/listing-types/art/config.js +2 -0
  93. package/dist/_internal/shared/listing-types/auction/config.d.ts +1 -0
  94. package/dist/_internal/shared/listing-types/auction/config.js +2 -0
  95. package/dist/_internal/shared/listing-types/availability.js +48 -0
  96. package/dist/_internal/shared/listing-types/capabilities.js +19 -0
  97. package/dist/_internal/shared/listing-types/classified/config.d.ts +1 -0
  98. package/dist/_internal/shared/listing-types/classified/config.js +2 -0
  99. package/dist/_internal/shared/listing-types/digital-code/config.d.ts +1 -0
  100. package/dist/_internal/shared/listing-types/digital-code/config.js +2 -0
  101. package/dist/_internal/shared/listing-types/feature-flags.js +1 -0
  102. package/dist/_internal/shared/listing-types/live/config.d.ts +1 -0
  103. package/dist/_internal/shared/listing-types/live/config.js +2 -0
  104. package/dist/_internal/shared/listing-types/pre-order/config.d.ts +1 -0
  105. package/dist/_internal/shared/listing-types/pre-order/config.js +2 -0
  106. package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +1 -0
  107. package/dist/_internal/shared/listing-types/prize-draw/config.js +2 -0
  108. package/dist/_internal/shared/listing-types/spares/config.d.ts +74 -0
  109. package/dist/_internal/shared/listing-types/spares/config.js +60 -0
  110. package/dist/_internal/shared/listing-types/spares/ctas.d.ts +1 -0
  111. package/dist/_internal/shared/listing-types/spares/ctas.js +4 -0
  112. package/dist/_internal/shared/listing-types/spares/og.js +1 -0
  113. package/dist/_internal/shared/listing-types/spares/schema.d.ts +2 -0
  114. package/dist/_internal/shared/listing-types/spares/schema.js +3 -0
  115. package/dist/_internal/shared/listing-types/spares/seed-factory.d.ts +1 -0
  116. package/dist/_internal/shared/listing-types/spares/seed-factory.js +1 -0
  117. package/dist/_internal/shared/listing-types/standard/config.d.ts +1 -0
  118. package/dist/_internal/shared/listing-types/standard/config.js +2 -0
  119. package/dist/_internal/shared/listing-types/stickers/config.d.ts +1 -0
  120. package/dist/_internal/shared/listing-types/stickers/config.js +2 -0
  121. package/dist/_internal/shared/search/match.d.ts +11 -0
  122. package/dist/_internal/shared/search/match.js +30 -3
  123. package/dist/_internal/shared/search/to-groups.d.ts +52 -0
  124. package/dist/_internal/shared/search/to-groups.js +141 -0
  125. package/dist/client.d.ts +8 -2
  126. package/dist/client.js +4 -1
  127. package/dist/constants/api-endpoints.d.ts +46 -15
  128. package/dist/constants/api-endpoints.js +35 -5
  129. package/dist/constants/cache-invalidation.js +3 -1
  130. package/dist/constants/field-names.d.ts +6 -0
  131. package/dist/constants/field-names.js +6 -0
  132. package/dist/features/about/components/PolicyPageView.d.ts +22 -2
  133. package/dist/features/about/components/PolicyPageView.js +14 -2
  134. package/dist/features/about/components/PublicProfileView.js +2 -1
  135. package/dist/features/about/components/ShippingPolicyView.js +42 -2
  136. package/dist/features/account/components/NotificationBell.js +1 -1
  137. package/dist/features/account/hooks/useProfileStats.js +5 -1
  138. package/dist/features/addresses/repository/addresses.repository.d.ts +16 -0
  139. package/dist/features/addresses/repository/addresses.repository.js +21 -0
  140. package/dist/features/admin/actions/admin-actions.js +16 -1
  141. package/dist/features/admin/components/AdminCategoryEditorView.js +19 -2
  142. package/dist/features/admin/components/AdminNavigationView.js +1 -1
  143. package/dist/features/admin/components/AdminPageHeader.js +1 -1
  144. package/dist/features/admin/components/AdminProductEditorView.js +17 -2
  145. package/dist/features/admin/components/AdminSidebar.js +1 -1
  146. package/dist/features/admin/components/AdminSiteSettingsView.js +39 -8
  147. package/dist/features/admin/components/AdminSparesView.d.ts +12 -0
  148. package/dist/features/admin/components/AdminSparesView.js +30 -0
  149. package/dist/features/admin/components/AdminUsersView.js +9 -7
  150. package/dist/features/admin/components/index.d.ts +2 -0
  151. package/dist/features/admin/components/index.js +1 -0
  152. package/dist/features/admin/components/sections/adminSectionsTypes.js +7 -0
  153. package/dist/features/admin/constants/filter-tabs.d.ts +3 -0
  154. package/dist/features/admin/constants/filter-tabs.js +1 -0
  155. package/dist/features/admin/schemas/firestore.d.ts +26 -2
  156. package/dist/features/auctions/components/AuctionDetailPageView.js +12 -18
  157. package/dist/features/auth/repository/user.repository.d.ts +13 -0
  158. package/dist/features/auth/repository/user.repository.js +28 -0
  159. package/dist/features/auth/schemas/firestore.d.ts +2 -0
  160. package/dist/features/auth/schemas/firestore.js +2 -0
  161. package/dist/features/business-documents/repository/business-documents.repository.d.ts +58 -0
  162. package/dist/features/business-documents/repository/business-documents.repository.js +54 -0
  163. package/dist/features/business-documents/schemas/firestore.d.ts +105 -0
  164. package/dist/features/business-documents/schemas/firestore.js +75 -0
  165. package/dist/features/cart/actions/cart-actions.js +83 -6
  166. package/dist/features/cart/repository/cart.repository.js +32 -4
  167. package/dist/features/cart/schemas/firestore.d.ts +20 -0
  168. package/dist/features/cart/schemas/index.d.ts +2 -2
  169. package/dist/features/catalogue/components/PublicCatalogueItemDetailView.js +1 -1
  170. package/dist/features/categories/api/[id]/price-guidance/route.d.ts +42 -0
  171. package/dist/features/categories/api/[id]/price-guidance/route.js +90 -0
  172. package/dist/features/categories/components/BrandDetailPageView.js +32 -8
  173. package/dist/features/categories/components/BrandDetailTabs.d.ts +7 -1
  174. package/dist/features/categories/components/BrandDetailTabs.js +28 -3
  175. package/dist/features/categories/components/BundleCollage.js +3 -2
  176. package/dist/features/categories/components/BundleItemsPicker.js +3 -4
  177. package/dist/features/categories/components/CategoriesIndexListing.js +23 -8
  178. package/dist/features/categories/components/CategoryDetailPageView.js +157 -20
  179. package/dist/features/categories/components/CategoryDetailTabs.d.ts +7 -1
  180. package/dist/features/categories/components/CategoryDetailTabs.js +41 -7
  181. package/dist/features/categories/components/CategoryGrid.js +10 -1
  182. package/dist/features/categories/components/PriceGuidanceHint.d.ts +29 -0
  183. package/dist/features/categories/components/PriceGuidanceHint.js +61 -0
  184. package/dist/features/categories/components/SubcategoryCardRow.d.ts +27 -0
  185. package/dist/features/categories/components/SubcategoryCardRow.js +98 -0
  186. package/dist/features/categories/constants/category-tabs.d.ts +20 -25
  187. package/dist/features/categories/constants/category-tabs.js +22 -39
  188. package/dist/features/categories/repository/categories.repository.d.ts +12 -0
  189. package/dist/features/categories/repository/categories.repository.js +20 -0
  190. package/dist/features/categories/schemas/firestore.d.ts +22 -1
  191. package/dist/features/categories/schemas/firestore.js +10 -0
  192. package/dist/features/categories/schemas/index.d.ts +6 -0
  193. package/dist/features/categories/schemas/index.js +2 -0
  194. package/dist/features/categories/types/index.d.ts +8 -0
  195. package/dist/features/categories/utils/taxonomy-visual.d.ts +16 -3
  196. package/dist/features/categories/utils/taxonomy-visual.js +26 -10
  197. package/dist/features/classified/components/ClassifiedDetailPageView.js +14 -10
  198. package/dist/features/contact/components/ContactPageView.d.ts +33 -1
  199. package/dist/features/contact/components/ContactPageView.js +58 -28
  200. package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +14 -10
  201. package/dist/features/grouped/components/GroupedListingDetailView.js +9 -1
  202. package/dist/features/grouped/schemas/firestore.d.ts +5 -0
  203. package/dist/features/homepage/actions/homepage-section-actions.d.ts +3 -3
  204. package/dist/features/homepage/components/CollectionCardsSection.d.ts +28 -6
  205. package/dist/features/homepage/components/CollectionCardsSection.js +43 -8
  206. package/dist/features/homepage/components/HowItWorksSection.d.ts +23 -6
  207. package/dist/features/homepage/components/HowItWorksSection.js +4 -4
  208. package/dist/features/homepage/components/PromoGrid.d.ts +31 -2
  209. package/dist/features/homepage/components/PromoGrid.js +12 -3
  210. package/dist/features/homepage/components/TestimonialsCarousel.d.ts +34 -2
  211. package/dist/features/homepage/components/TestimonialsCarousel.js +1 -1
  212. package/dist/features/homepage/components/index.d.ts +0 -20
  213. package/dist/features/homepage/components/index.js +0 -11
  214. package/dist/features/homepage/index.d.ts +0 -2
  215. package/dist/features/homepage/index.js +0 -2
  216. package/dist/features/homepage/lib/section-renderer.d.ts +9 -0
  217. package/dist/features/homepage/lib/section-renderer.js +202 -58
  218. package/dist/features/homepage/repository/homepage-sections.repository.d.ts +35 -3
  219. package/dist/features/homepage/repository/homepage-sections.repository.js +58 -8
  220. package/dist/features/homepage/schemas/firestore.d.ts +106 -3
  221. package/dist/features/homepage/schemas/firestore.js +32 -2
  222. package/dist/features/homepage/schemas/index.d.ts +36 -7
  223. package/dist/features/homepage/schemas/index.js +39 -27
  224. package/dist/features/homepage/server.d.ts +0 -3
  225. package/dist/features/homepage/server.js +0 -3
  226. package/dist/features/homepage/types/index.d.ts +0 -47
  227. package/dist/features/homepage/types/index.js +49 -0
  228. package/dist/features/layout/AppLayoutShell.js +1 -1
  229. package/dist/features/layout/BottomNavItem.js +1 -1
  230. package/dist/features/layout/BottomSheet.js +1 -1
  231. package/dist/features/layout/TitleBarLayout.js +1 -1
  232. package/dist/features/live/components/LiveItemDetailPageView.js +14 -10
  233. package/dist/features/media/hooks/useMedia.js +17 -0
  234. package/dist/features/media/repository/media-assets.repository.d.ts +46 -0
  235. package/dist/features/media/repository/media-assets.repository.js +31 -0
  236. package/dist/features/media/trust-signals.d.ts +85 -0
  237. package/dist/features/media/trust-signals.js +126 -0
  238. package/dist/features/parts/components/PartDetailPageView.d.ts +14 -1
  239. package/dist/features/parts/components/PartDetailPageView.js +2 -2
  240. package/dist/features/parts/components/PartsIndexPageView.d.ts +11 -2
  241. package/dist/features/parts/components/PartsIndexPageView.js +2 -2
  242. package/dist/features/parts/components/PartsSearchBox.d.ts +9 -0
  243. package/dist/features/parts/components/PartsSearchBox.js +57 -0
  244. package/dist/features/parts/schemas/firestore.d.ts +149 -3
  245. package/dist/features/parts/schemas/firestore.js +184 -0
  246. package/dist/features/payments/repository/payout.repository.d.ts +15 -0
  247. package/dist/features/payments/repository/payout.repository.js +21 -0
  248. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +12 -18
  249. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +1 -1
  250. package/dist/features/price-index/components/PriceIndexToolView.d.ts +24 -1
  251. package/dist/features/price-index/components/PriceIndexToolView.js +299 -17
  252. package/dist/features/price-index/lookup-target.d.ts +48 -0
  253. package/dist/features/price-index/lookup-target.js +107 -0
  254. package/dist/features/products/components/AvailabilityScopeHint.d.ts +22 -0
  255. package/dist/features/products/components/AvailabilityScopeHint.js +27 -0
  256. package/dist/features/products/components/CompareOverlay.d.ts +20 -2
  257. package/dist/features/products/components/CompareOverlay.js +23 -3
  258. package/dist/features/products/components/CustomSectionTabContent.d.ts +31 -0
  259. package/dist/features/products/components/CustomSectionTabContent.js +29 -3
  260. package/dist/features/products/components/GroupMemberPicker.d.ts +19 -1
  261. package/dist/features/products/components/GroupMemberPicker.js +46 -8
  262. package/dist/features/products/components/GroupSettingsPanel.d.ts +11 -1
  263. package/dist/features/products/components/GroupSettingsPanel.js +92 -3
  264. package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
  265. package/dist/features/products/components/PrizeDrawEntryActions.d.ts +15 -1
  266. package/dist/features/products/components/PrizeDrawEntryActions.js +70 -8
  267. package/dist/features/products/components/ProductDetailPageView.js +81 -17
  268. package/dist/features/products/components/ProductForm.d.ts +13 -1
  269. package/dist/features/products/components/ProductForm.js +16 -2
  270. package/dist/features/products/components/ProductsIndexListing.d.ts +16 -1
  271. package/dist/features/products/components/ProductsIndexListing.js +3 -3
  272. package/dist/features/products/components/ShowGroupSection.js +15 -2
  273. package/dist/features/products/components/SparesListView.d.ts +7 -0
  274. package/dist/features/products/components/SparesListView.js +39 -0
  275. package/dist/features/products/components/SublistingCarouselSection.js +2 -1
  276. package/dist/features/products/constants/action-defs.d.ts +1 -0
  277. package/dist/features/products/constants/action-defs.js +1 -0
  278. package/dist/features/products/repository/products.repository.d.ts +99 -0
  279. package/dist/features/products/repository/products.repository.js +240 -18
  280. package/dist/features/products/schemas/firestore.d.ts +97 -4
  281. package/dist/features/products/schemas/firestore.js +104 -0
  282. package/dist/features/products/schemas/index.d.ts +10 -19
  283. package/dist/features/products/schemas/index.js +23 -6
  284. package/dist/features/products/types/index.d.ts +45 -1
  285. package/dist/features/products/utils/group-member-image.d.ts +46 -0
  286. package/dist/features/products/utils/group-member-image.js +4 -0
  287. package/dist/features/products/utils/listing-badge-variant.js +4 -0
  288. package/dist/features/reviews/components/ReviewsList.js +1 -1
  289. package/dist/features/scams/components/ScamProfileView.js +1 -1
  290. package/dist/features/search/components/EntityTypeahead.d.ts +44 -0
  291. package/dist/features/search/components/EntityTypeahead.js +92 -0
  292. package/dist/features/search/components/Search.d.ts +12 -1
  293. package/dist/features/search/components/Search.js +47 -3
  294. package/dist/features/search/components/SearchFacetRail.d.ts +11 -0
  295. package/dist/features/search/components/SearchFacetRail.js +92 -0
  296. package/dist/features/search/components/SearchResultsClient.d.ts +33 -0
  297. package/dist/features/search/components/SearchResultsClient.js +103 -0
  298. package/dist/features/search/components/SearchResultsPageView.d.ts +15 -1
  299. package/dist/features/search/components/SearchResultsPageView.js +2 -2
  300. package/dist/features/search/components/index.d.ts +0 -6
  301. package/dist/features/search/components/index.js +0 -3
  302. package/dist/features/search/hooks/useNavSuggestions.d.ts +13 -0
  303. package/dist/features/search/hooks/useScopedSearch.d.ts +34 -0
  304. package/dist/features/search/hooks/useScopedSearch.js +114 -0
  305. package/dist/features/search/hooks/useSiteSearch.d.ts +26 -0
  306. package/dist/features/search/hooks/useSiteSearch.js +149 -0
  307. package/dist/features/search/index.d.ts +0 -4
  308. package/dist/features/search/index.js +0 -4
  309. package/dist/features/search/manifest.d.ts +12 -0
  310. package/dist/features/search/manifest.js +17 -2
  311. package/dist/features/search/server.d.ts +0 -3
  312. package/dist/features/search/server.js +0 -3
  313. package/dist/features/seller/components/SellerListingTypeView.d.ts +1 -1
  314. package/dist/features/seller/components/SellerListingTypeView.js +20 -0
  315. package/dist/features/seller/components/SellerPayoutSettingsView.js +35 -14
  316. package/dist/features/seller/components/SellerProductShell.d.ts +1 -1
  317. package/dist/features/seller/components/SellerProductShell.js +2 -1
  318. package/dist/features/seller/components/SellerProductsView.js +3 -2
  319. package/dist/features/seller/components/SellerSidebar.js +1 -1
  320. package/dist/features/seller/components/SellerSparesView.d.ts +17 -0
  321. package/dist/features/seller/components/SellerSparesView.js +6 -0
  322. package/dist/features/seller/components/index.d.ts +2 -0
  323. package/dist/features/seller/components/index.js +1 -0
  324. package/dist/features/shell/CommandPalette.js +1 -1
  325. package/dist/features/sticker-trace/schemas/firestore.d.ts +19 -0
  326. package/dist/features/stores/components/StoreAuctionsListing.js +2 -1
  327. package/dist/features/stores/components/StoreDetailLayoutView.d.ts +1 -1
  328. package/dist/features/stores/components/StoreDetailLayoutView.js +7 -82
  329. package/dist/features/stores/components/StoreHeader.js +1 -2
  330. package/dist/features/stores/components/StoreSparesPageView.d.ts +27 -0
  331. package/dist/features/stores/components/StoreSparesPageView.js +48 -0
  332. package/dist/features/stores/schemas/firestore.d.ts +19 -0
  333. package/dist/features/tester/seed-data/authored/buying__spares-and-groups.d.ts +2 -0
  334. package/dist/features/tester/seed-data/authored/buying__spares-and-groups.js +96 -0
  335. package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.d.ts +2 -0
  336. package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.js +392 -45
  337. package/dist/features/tester/seed-data/authored/content-discovery__category-tabs-franchise-bundles.js +54 -32
  338. package/dist/features/tester/seed-data/authored/content-discovery__parts-database.js +3 -2
  339. package/dist/features/tester/seed-data/authored/content-discovery__price-index-and-compare.d.ts +2 -0
  340. package/dist/features/tester/seed-data/authored/content-discovery__price-index-and-compare.js +73 -0
  341. package/dist/features/tester/seed-data/authored/content-discovery__site-wide-index.d.ts +2 -0
  342. package/dist/features/tester/seed-data/authored/content-discovery__site-wide-index.js +340 -0
  343. package/dist/features/tester/seed-data/authored/index.js +16 -0
  344. package/dist/features/tester/seed-data/authored/search-and-nav__header-search.d.ts +1 -0
  345. package/dist/features/tester/seed-data/authored/search-and-nav__header-search.js +57 -0
  346. package/dist/features/tester/seed-data/authored/selling__spares-and-group-editing.d.ts +2 -0
  347. package/dist/features/tester/seed-data/authored/selling__spares-and-group-editing.js +77 -0
  348. package/dist/features/tester/seed-data/authored/tools__sticker-promote-to-listings.d.ts +2 -0
  349. package/dist/features/tester/seed-data/authored/tools__sticker-promote-to-listings.js +58 -0
  350. package/dist/features/tester/seed-data/authored/tools__sticker-tracer.js +5 -4
  351. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +384 -18
  352. package/dist/index.d.ts +17 -57
  353. package/dist/index.js +35 -96
  354. package/dist/next/routing/route-map.d.ts +42 -1
  355. package/dist/next/routing/route-map.js +45 -0
  356. package/dist/providers/db-firebase/base.repository.d.ts +22 -0
  357. package/dist/providers/db-firebase/base.repository.js +36 -0
  358. package/dist/schemas/registry.d.ts +15 -6
  359. package/dist/security/blind-index-tokens.d.ts +54 -0
  360. package/dist/security/blind-index-tokens.js +152 -0
  361. package/dist/security/index.d.ts +1 -1
  362. package/dist/security/index.js +1 -1
  363. package/dist/security/settings-encryption.d.ts +35 -0
  364. package/dist/security/settings-encryption.js +91 -0
  365. package/dist/seed/_helpers/brand-rows.js +38 -14
  366. package/dist/seed/_helpers/category-forest.js +73 -7
  367. package/dist/seed/_helpers/category-images-generated.d.ts +25 -6
  368. package/dist/seed/_helpers/category-images-generated.js +81 -6
  369. package/dist/seed/_helpers/category-models.d.ts +1 -1
  370. package/dist/seed/_helpers/category-models.js +7 -1
  371. package/dist/seed/_helpers/category-parts-glossary-map.js +8 -0
  372. package/dist/seed/_helpers/category-parts.d.ts +1 -1
  373. package/dist/seed/_helpers/category-parts.js +50 -2
  374. package/dist/seed/_helpers/character-features.d.ts +1 -1
  375. package/dist/seed/_helpers/character-features.js +2 -1
  376. package/dist/seed/_helpers/franchise-rows.js +10 -2
  377. package/dist/seed/_helpers/media.d.ts +47 -0
  378. package/dist/seed/_helpers/media.js +34 -0
  379. package/dist/seed/authenticity-knowledge-seed-data.d.ts +49 -5
  380. package/dist/seed/authenticity-knowledge-seed-data.js +10001 -549
  381. package/dist/seed/categories-seed-data.js +124 -20
  382. package/dist/seed/grouped-listings-seed-data.js +16 -0
  383. package/dist/seed/homepage-sections-seed-data.js +94 -5
  384. package/dist/seed/index.d.ts +2 -3
  385. package/dist/seed/index.js +2 -2
  386. package/dist/seed/manifest.js +3 -0
  387. package/dist/seed/media-assets-seed-data.d.ts +1 -1
  388. package/dist/seed/media-assets-seed-data.js +746 -196
  389. package/dist/seed/model-composition-seed-data.js +122 -6
  390. package/dist/seed/part-glossary-seed-data.d.ts +1 -1
  391. package/dist/seed/part-glossary-seed-data.js +37219 -463
  392. package/dist/seed/products-art-seed-data.js +7 -18
  393. package/dist/seed/products-auctions-seed-data.js +56 -47
  394. package/dist/seed/products-beyblade-x-seed-data.d.ts +2 -0
  395. package/dist/seed/products-beyblade-x-seed-data.js +476 -0
  396. package/dist/seed/products-catalogue-seed-data.d.ts +5 -5
  397. package/dist/seed/products-catalogue-seed-data.js +1500 -1240
  398. package/dist/seed/products-classifieds-seed-data.js +9 -27
  399. package/dist/seed/products-digital-codes-seed-data.js +9 -18
  400. package/dist/seed/products-live-items-seed-data.js +5 -13
  401. package/dist/seed/products-preorders-seed-data.js +67 -33
  402. package/dist/seed/products-prize-draws-seed-data.js +8 -17
  403. package/dist/seed/products-spares-seed-data.d.ts +2 -0
  404. package/dist/seed/products-spares-seed-data.js +368 -0
  405. package/dist/seed/products-standard-seed-data.js +39 -126
  406. package/dist/seed/products-stickers-seed-data.js +59 -15
  407. package/dist/seed/site-settings-seed-data.js +8 -0
  408. package/dist/seo/json-ld.d.ts +25 -0
  409. package/dist/seo/json-ld.js +28 -0
  410. package/dist/server-entry.d.ts +3 -1
  411. package/dist/server-entry.js +2 -2
  412. package/dist/server.d.ts +11 -9
  413. package/dist/server.js +35 -25
  414. package/dist/styles.css +202 -1
  415. package/dist/tailwind-input.css +31 -17
  416. package/dist/tailwind-utilities.css +1 -1
  417. package/dist/ui/components/Accordion.js +1 -1
  418. package/dist/ui/components/Breadcrumb.js +1 -1
  419. package/dist/ui/components/DetailPageGallery.js +0 -2
  420. package/dist/ui/components/DetailPageHero.js +0 -1
  421. package/dist/ui/components/DetailPageTabs.js +1 -1
  422. package/dist/ui/components/Divider.js +3 -3
  423. package/dist/ui/components/GatedPrice.js +5 -1
  424. package/dist/ui/components/HorizontalScroller.js +1 -1
  425. package/dist/ui/components/ListingLayout.js +3 -3
  426. package/dist/ui/components/MediaTabs.style.css +81 -0
  427. package/dist/ui/components/Pagination.js +1 -1
  428. package/dist/ui/components/PendingValue.d.ts +45 -0
  429. package/dist/ui/components/PendingValue.js +7 -0
  430. package/dist/ui/components/Radio.style.css +119 -0
  431. package/dist/ui/components/RatingDisplay.js +2 -2
  432. package/dist/ui/components/SettingsSection.js +0 -2
  433. package/dist/ui/components/SiteLogo.js +3 -0
  434. package/dist/ui/components/SiteMark.js +3 -0
  435. package/dist/ui/components/SortDropdown.js +1 -1
  436. package/dist/ui/components/SpecTable.d.ts +58 -0
  437. package/dist/ui/components/SpecTable.js +8 -0
  438. package/dist/ui/components/StarRating.js +1 -3
  439. package/dist/ui/components/SummaryCard.js +1 -1
  440. package/dist/ui/components/index.style.css +1 -0
  441. package/dist/ui/icons/icon-registry.d.ts +1 -0
  442. package/dist/ui/icons/icon-registry.js +9 -1
  443. package/dist/ui/index.d.ts +4 -0
  444. package/dist/ui/index.js +2 -0
  445. package/dist/utils/search-txt-query.d.ts +12 -0
  446. package/dist/utils/search-txt-query.js +12 -0
  447. package/package.json +1 -1
  448. package/scripts/dedupe-peer-deps.mjs +18 -0
  449. package/scripts/seed-cli.mjs +250 -35
  450. package/scripts/verify-css-build.mjs +118 -37
  451. package/dist/_internal/server/features/search/actions.d.ts +0 -7
  452. package/dist/_internal/server/features/search/actions.js +0 -21
  453. package/dist/_internal/server/features/search/data.d.ts +0 -15
  454. package/dist/_internal/server/features/search/data.js +0 -18
  455. package/dist/_internal/server/features/search/index.d.ts +0 -3
  456. package/dist/_internal/server/features/search/index.js +0 -2
  457. package/dist/features/homepage/api/[id]/route.d.ts +0 -23
  458. package/dist/features/homepage/api/[id]/route.js +0 -103
  459. package/dist/features/homepage/api/route.d.ts +0 -14
  460. package/dist/features/homepage/api/route.js +0 -140
  461. package/dist/features/homepage/api/sections/[id]/route.d.ts +0 -26
  462. package/dist/features/homepage/api/sections/[id]/route.js +0 -95
  463. package/dist/features/homepage/api/sections/route.d.ts +0 -13
  464. package/dist/features/homepage/api/sections/route.js +0 -82
  465. package/dist/features/homepage/components/AdvertisementBanner.d.ts +0 -14
  466. package/dist/features/homepage/components/AdvertisementBanner.js +0 -35
  467. package/dist/features/homepage/components/BeforeAfterCard.d.ts +0 -9
  468. package/dist/features/homepage/components/BeforeAfterCard.js +0 -73
  469. package/dist/features/homepage/components/FeaturedResultsSection.d.ts +0 -17
  470. package/dist/features/homepage/components/FeaturedResultsSection.js +0 -17
  471. package/dist/features/homepage/components/HeroBanner.d.ts +0 -7
  472. package/dist/features/homepage/components/HeroBanner.js +0 -43
  473. package/dist/features/homepage/components/HeroSection.d.ts +0 -7
  474. package/dist/features/homepage/components/HeroSection.js +0 -6
  475. package/dist/features/homepage/components/HomepageSkeleton.d.ts +0 -2
  476. package/dist/features/homepage/components/HomepageSkeleton.js +0 -21
  477. package/dist/features/homepage/components/HomepageView.d.ts +0 -25
  478. package/dist/features/homepage/components/HomepageView.js +0 -5
  479. package/dist/features/homepage/components/NewsletterBanner.d.ts +0 -23
  480. package/dist/features/homepage/components/NewsletterBanner.js +0 -37
  481. package/dist/features/homepage/components/SiteFeaturesSection.d.ts +0 -14
  482. package/dist/features/homepage/components/SiteFeaturesSection.js +0 -11
  483. package/dist/features/homepage/components/TrustBadges.d.ts +0 -6
  484. package/dist/features/homepage/components/TrustBadges.js +0 -46
  485. package/dist/features/homepage/components/TrustIndicatorsSection.d.ts +0 -12
  486. package/dist/features/homepage/components/TrustIndicatorsSection.js +0 -11
  487. package/dist/features/homepage/hooks/useHomepage.d.ts +0 -11
  488. package/dist/features/homepage/hooks/useHomepage.js +0 -17
  489. package/dist/features/homepage/hooks/useHomepageSections.d.ts +0 -2
  490. package/dist/features/homepage/hooks/useHomepageSections.js +0 -10
  491. package/dist/features/homepage/repository/homepage.repository.d.ts +0 -11
  492. package/dist/features/homepage/repository/homepage.repository.js +0 -25
  493. package/dist/features/search/actions/index.d.ts +0 -1
  494. package/dist/features/search/actions/index.js +0 -1
  495. package/dist/features/search/actions/search-actions.d.ts +0 -18
  496. package/dist/features/search/actions/search-actions.js +0 -43
  497. package/dist/features/search/api/route.d.ts +0 -22
  498. package/dist/features/search/api/route.js +0 -128
  499. package/dist/features/search/columns/index.d.ts +0 -17
  500. package/dist/features/search/columns/index.js +0 -49
  501. package/dist/features/search/components/SearchFiltersRow.d.ts +0 -22
  502. package/dist/features/search/components/SearchFiltersRow.js +0 -21
  503. package/dist/features/search/components/SearchResultsSection.d.ts +0 -53
  504. package/dist/features/search/components/SearchResultsSection.js +0 -35
  505. package/dist/features/search/components/SearchView.d.ts +0 -27
  506. package/dist/features/search/components/SearchView.js +0 -8
  507. package/dist/features/search/hooks/useSearch.d.ts +0 -21
  508. package/dist/features/search/hooks/useSearch.js +0 -33
  509. package/dist/features/search/repository/search.repository.d.ts +0 -14
  510. package/dist/features/search/repository/search.repository.js +0 -59
  511. package/dist/features/search/schemas/index.d.ts +0 -70
  512. package/dist/features/search/schemas/index.js +0 -36
  513. package/dist/features/search/types/index.d.ts +0 -48
  514. package/dist/features/stores/components/StoreScopedSearch.d.ts +0 -17
  515. package/dist/features/stores/components/StoreScopedSearch.js +0 -27
  516. package/dist/seed/defaults/homepage-sections.d.ts +0 -2
  517. package/dist/seed/defaults/homepage-sections.js +0 -60
  518. package/dist/seed/factories/homepage-section.factory.d.ts +0 -10
  519. package/dist/seed/factories/homepage-section.factory.js +0 -13
  520. /package/dist/{features/search/types/index.js → _internal/shared/listing-types/spares/og.d.ts} +0 -0
@@ -2,6 +2,74 @@
2
2
  * Products Firestore Document Types & Constants
3
3
  */
4
4
  import { generateProductId, generateAuctionId, generatePreOrderId, } from "../../../utils/id-generators.js";
5
+ /**
6
+ * Whatever a document, a request body or a seed row happens to hold, as
7
+ * `ProductImage[]`.
8
+ *
9
+ * 🛑 This is called on the WRITE path (`ProductRepository.create`/`update`)
10
+ * **and** on the read path (`mapDoc`), and both are required. Root Cause #101
11
+ * is exactly this shape: `mapDoc` backfilled `categorySlugs` on read while the
12
+ * write schema stripped it, so the field was present on every object anyone
13
+ * inspected and absent from the stored document — invisible to every query,
14
+ * and latent until a human created a listing.
15
+ *
16
+ * Accepts `string`, `ProductImage`, and a mixed array of both, because a
17
+ * partially-migrated collection genuinely contains all three.
18
+ */
19
+ export function toProductImages(input) {
20
+ if (!Array.isArray(input))
21
+ return [];
22
+ const out = [];
23
+ for (const entry of input) {
24
+ if (typeof entry === "string") {
25
+ if (entry)
26
+ out.push({ url: entry });
27
+ continue;
28
+ }
29
+ if (entry && typeof entry === "object") {
30
+ /*
31
+ * `Partial<ProductImage>`, NOT `{ url?: unknown }` — one narrowing
32
+ * instead of two casts, and no `unknown` in a local binding
33
+ * (`audit-unknown-leakage` flagged the earlier spelling, correctly: an
34
+ * `unknown` here defeats the checking on every member read below it).
35
+ * Every member is still validated before use, so the assertion is
36
+ * narrowing a shape we are about to prove rather than asserting a fact.
37
+ */
38
+ const e = entry;
39
+ if (typeof e.url !== "string" || !e.url)
40
+ continue;
41
+ /*
42
+ * 🛑 EVERY optional member is carried through, not just the two A6
43
+ * added. This runs on the WRITE path, so a member it forgets is a
44
+ * member it DESTROYS on the next save — and `thumbnailUrl`/`order`
45
+ * predate this function. Adding a field to `ProductImage` means adding
46
+ * it here; the alternative is a normaliser that quietly truncates the
47
+ * type it claims to normalise.
48
+ */
49
+ out.push({
50
+ url: e.url,
51
+ ...(e.alt ? { alt: e.alt } : {}),
52
+ ...(e.thumbnailUrl ? { thumbnailUrl: e.thumbnailUrl } : {}),
53
+ ...(typeof e.order === "number" ? { order: e.order } : {}),
54
+ ...(typeof e.width === "number" ? { width: e.width } : {}),
55
+ ...(typeof e.height === "number" ? { height: e.height } : {}),
56
+ });
57
+ }
58
+ }
59
+ return out;
60
+ }
61
+ /**
62
+ * Just the URLs, in order — for the many call sites that only ever needed a
63
+ * `string[]` (a lightbox's source list, an OG image pick, a media-abort
64
+ * cleanup list).
65
+ *
66
+ * This is a PROJECTION, computed on demand, not a stored mirror — so it cannot
67
+ * drift from `images` the way a denormalised second field would (Root Cause
68
+ * #42). Takes `unknown` so it also works on a legacy `string[]`.
69
+ */
70
+ export function productImageUrls(input) {
71
+ return toProductImages(input).map((i) => i.url);
72
+ }
5
73
  export const MAX_CUSTOM_FIELDS = 50;
6
74
  export const MAX_CUSTOM_SECTIONS = 3;
7
75
  /** Runtime-accessible product status values — use instead of bare string literals. */
@@ -171,6 +239,15 @@ export const PRODUCT_PUBLIC_FIELDS = [
171
239
  "isPromoted",
172
240
  "isOnSale",
173
241
  "isSold",
242
+ /* Public because the PDP has to SAY "made to order, printed when you order"
243
+ * instead of a stock line, and the group picker has to uncap that member's
244
+ * stepper. Both are client-side reads. */
245
+ "madeToOrder",
246
+ /* Public: provenance a buyer should see — "this is one cut off that sheet" —
247
+ * and neither value is a path or a private id. The sheet document itself is
248
+ * admin-only; this is just its id, same shape as `sourceCatalogueItemId`. */
249
+ "sourceStickerSheetId",
250
+ "sourceStickerPartOrdinal",
174
251
  "slug",
175
252
  "seoTitle",
176
253
  "seoDescription",
@@ -184,6 +261,14 @@ export const PRODUCT_PUBLIC_FIELDS = [
184
261
  "groupParentSlug",
185
262
  "groupChildSlugs",
186
263
  "groupTitle",
264
+ /* The group's own rules. Public because the PICKER enforces them too — a
265
+ * server-only rule reads as a broken button — and none of them is sensitive:
266
+ * they are merchandising limits a buyer is about to be told anyway. */
267
+ "groupMinMembers",
268
+ "groupMaxMembers",
269
+ "groupMaxUnitsPerMember",
270
+ "groupRequiredMemberIds",
271
+ "groupDiscountPercent",
187
272
  "createdAt",
188
273
  ];
189
274
  export const PRODUCT_UPDATABLE_FIELDS = [
@@ -250,6 +335,17 @@ export const PRODUCT_UPDATABLE_FIELDS = [
250
335
  "preOrderCancellable",
251
336
  "isOnSale",
252
337
  "isSold",
338
+ /* 🛑 Listed here or an update STRIPS it — the write schema has no
339
+ * `.passthrough()`, so a field absent from this list is dropped before it
340
+ * reaches Firestore while the write still returns 200 (Root Cause #101). The
341
+ * seller checkbox would appear to save and change nothing. */
342
+ "madeToOrder",
343
+ /* Updatable so `promoteStickerSheetToListings` can set them through the same
344
+ * `create`/`update` path every other write uses — and so a re-promote can
345
+ * correct them — rather than needing a raw collection write that would also
346
+ * skip `deriveTaxonomy`. */
347
+ "sourceStickerSheetId",
348
+ "sourceStickerPartOrdinal",
253
349
  "allowShipBeforeEmiComplete",
254
350
  /*
255
351
  * B3 — these three were MISSING, so the fields existed on the document
@@ -272,6 +368,14 @@ export const PRODUCT_UPDATABLE_FIELDS = [
272
368
  "groupParentSlug",
273
369
  "groupChildSlugs",
274
370
  "groupTitle",
371
+ /* The group's own rules. Public because the PICKER enforces them too — a
372
+ * server-only rule reads as a broken button — and none of them is sensitive:
373
+ * they are merchandising limits a buyer is about to be told anyway. */
374
+ "groupMinMembers",
375
+ "groupMaxMembers",
376
+ "groupMaxUnitsPerMember",
377
+ "groupRequiredMemberIds",
378
+ "groupDiscountPercent",
275
379
  "barcodeId",
276
380
  ];
277
381
  // ── SB-UNI-N 2026-05-15 — Digital-code pool subcollection ───────────────────
@@ -1,5 +1,6 @@
1
1
  export * from "./firestore";
2
2
  import { z } from "zod";
3
+ import type { ListingType } from "../types/index";
3
4
  export declare const productImageSchema: z.ZodObject<{
4
5
  url: z.ZodString;
5
6
  thumbnailUrl: z.ZodOptional<z.ZodString>;
@@ -46,19 +47,6 @@ export declare const printMetaSchema: z.ZodObject<{
46
47
  finish?: string | undefined;
47
48
  editionSize?: number | undefined;
48
49
  }>;
49
- /**
50
- * Base Zod schema for a product item.
51
- * Apps can extend this to add their own fields:
52
- *
53
- * @example
54
- * import { productItemSchema } from "@mohasinac/feat-products";
55
- *
56
- * const myProductSchema = productItemSchema.extend({
57
- * brand: z.string(),
58
- * auctionEndDate: z.string().optional(),
59
- * });
60
- * type MyProduct = z.infer<typeof myProductSchema>;
61
- */
62
50
  export declare const productItemSchema: z.ZodObject<{
63
51
  id: z.ZodString;
64
52
  title: z.ZodString;
@@ -130,7 +118,8 @@ export declare const productItemSchema: z.ZodObject<{
130
118
  sellerAvatar: z.ZodOptional<z.ZodString>;
131
119
  status: z.ZodEnum<["draft", "published", "in_review", "archived"]>;
132
120
  condition: z.ZodOptional<z.ZodEnum<["new", "like_new", "good", "fair", "poor", "used", "refurbished", "broken", "graded"]>>;
133
- listingType: z.ZodOptional<z.ZodEnum<["standard", "auction", "pre-order", "prize-draw", "classified", "digital-code", "live"]>>;
121
+ madeToOrder: z.ZodOptional<z.ZodBoolean>;
122
+ listingType: z.ZodOptional<z.ZodEnum<[ListingType, ...ListingType[]]>>;
134
123
  maxPerUser: z.ZodOptional<z.ZodNumber>;
135
124
  partOfBundleIds: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
136
125
  partOfBundleTitles: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
@@ -337,7 +326,7 @@ export declare const productItemSchema: z.ZodObject<{
337
326
  } | undefined;
338
327
  createdAt?: string | undefined;
339
328
  updatedAt?: string | undefined;
340
- listingType?: "classified" | "standard" | "auction" | "pre-order" | "prize-draw" | "digital-code" | "live" | undefined;
329
+ listingType?: ListingType | undefined;
341
330
  category?: string | undefined;
342
331
  categorySlug?: string | undefined;
343
332
  storeId?: string | undefined;
@@ -359,6 +348,7 @@ export declare const productItemSchema: z.ZodObject<{
359
348
  description?: string | undefined;
360
349
  subcategory?: string | undefined;
361
350
  availableQuantity?: number | undefined;
351
+ madeToOrder?: boolean | undefined;
362
352
  mainImage?: string | undefined;
363
353
  images?: string[] | undefined;
364
354
  featured?: boolean | undefined;
@@ -476,7 +466,7 @@ export declare const productItemSchema: z.ZodObject<{
476
466
  } | undefined;
477
467
  createdAt?: string | undefined;
478
468
  updatedAt?: string | undefined;
479
- listingType?: "classified" | "standard" | "auction" | "pre-order" | "prize-draw" | "digital-code" | "live" | undefined;
469
+ listingType?: ListingType | undefined;
480
470
  category?: string | undefined;
481
471
  categorySlug?: string | undefined;
482
472
  storeId?: string | undefined;
@@ -498,6 +488,7 @@ export declare const productItemSchema: z.ZodObject<{
498
488
  description?: string | undefined;
499
489
  subcategory?: string | undefined;
500
490
  availableQuantity?: number | undefined;
491
+ madeToOrder?: boolean | undefined;
501
492
  mainImage?: string | undefined;
502
493
  images?: string[] | undefined;
503
494
  featured?: boolean | undefined;
@@ -612,7 +603,7 @@ export declare const productListParamsSchema: z.ZodObject<{
612
603
  minPrice: z.ZodOptional<z.ZodNumber>;
613
604
  maxPrice: z.ZodOptional<z.ZodNumber>;
614
605
  inStock: z.ZodOptional<z.ZodBoolean>;
615
- listingType: z.ZodOptional<z.ZodEnum<["standard", "auction", "pre-order", "prize-draw", "classified", "digital-code", "live"]>>;
606
+ listingType: z.ZodOptional<z.ZodEnum<[ListingType, ...ListingType[]]>>;
616
607
  storeId: z.ZodOptional<z.ZodString>;
617
608
  sort: z.ZodOptional<z.ZodString>;
618
609
  page: z.ZodOptional<z.ZodNumber>;
@@ -623,7 +614,7 @@ export declare const productListParamsSchema: z.ZodObject<{
623
614
  status?: "published" | "draft" | "in_review" | "archived" | undefined;
624
615
  page?: number | undefined;
625
616
  q?: string | undefined;
626
- listingType?: "classified" | "standard" | "auction" | "pre-order" | "prize-draw" | "digital-code" | "live" | undefined;
617
+ listingType?: ListingType | undefined;
627
618
  category?: string | undefined;
628
619
  storeId?: string | undefined;
629
620
  featured?: boolean | undefined;
@@ -637,7 +628,7 @@ export declare const productListParamsSchema: z.ZodObject<{
637
628
  status?: "published" | "draft" | "in_review" | "archived" | undefined;
638
629
  page?: number | undefined;
639
630
  q?: string | undefined;
640
- listingType?: "classified" | "standard" | "auction" | "pre-order" | "prize-draw" | "digital-code" | "live" | undefined;
631
+ listingType?: ListingType | undefined;
641
632
  category?: string | undefined;
642
633
  storeId?: string | undefined;
643
634
  featured?: boolean | undefined;
@@ -1,6 +1,7 @@
1
1
  export * from "./firestore.js";
2
2
  import { z } from "zod";
3
3
  import { mediaFieldSchema } from "../../media/types/index.js";
4
+ import { ALL_LISTING_TYPES } from "../../../_internal/shared/listing-types/feature-flags.js";
4
5
  import { PRIZE_DRAW_DURATION_DAYS_MIN, PRIZE_DRAW_DURATION_DAYS_MAX, } from "../../../_internal/shared/checkout/rules/_limits.js";
5
6
  // --- Sub-schemas --------------------------------------------------------------
6
7
  export const productImageSchema = z.object({
@@ -35,6 +36,25 @@ export const printMetaSchema = z.object({
35
36
  * });
36
37
  * type MyProduct = z.infer<typeof myProductSchema>;
37
38
  */
39
+ /**
40
+ * The `ListingType` union as a Zod enum, DERIVED.
41
+ *
42
+ * 🛑 Two hand-written copies of this lived in this file and BOTH had drifted to
43
+ * seven of nine — `art` and `stickers` were missing from the item schema and
44
+ * from the query schema. Consequence: `productItemSchema.parse()` stripped
45
+ * `listingType` off every art and sticker row (the field is `.optional()`, so a
46
+ * value outside the enum is a parse FAILURE, and a caller using `safeParse` or
47
+ * `.partial()` loses it silently), and `?listingType=art` on any route
48
+ * validating against the query schema was rejected as an invalid value.
49
+ *
50
+ * `z.enum` needs a non-empty literal tuple, hence the cast — `ALL_LISTING_TYPES`
51
+ * is derived from `Record<ListingType, true>`, so its CONTENTS are
52
+ * compile-checked against the union even though its length is not.
53
+ *
54
+ * Registered in `audit-listing-type-tab-coverage`'s STAYS_DERIVED so nobody
55
+ * re-expands it into a literal for readability.
56
+ */
57
+ const LISTING_TYPE_ENUM = z.enum(ALL_LISTING_TYPES);
38
58
  export const productItemSchema = z.object({
39
59
  id: z.string(),
40
60
  title: z.string(),
@@ -78,9 +98,8 @@ export const productItemSchema = z.object({
78
98
  ])
79
99
  .optional(),
80
100
  // SB1-G Phase 4 — canonical discriminator (legacy `isAuction`/`isPreOrder` booleans removed).
81
- listingType: z
82
- .enum(["standard", "auction", "pre-order", "prize-draw", "classified", "digital-code", "live"])
83
- .optional(),
101
+ madeToOrder: z.boolean().optional(),
102
+ listingType: LISTING_TYPE_ENUM.optional(),
84
103
  // SB1-C — prize-draw + bundle fields. All optional.
85
104
  maxPerUser: z.number().int().positive().optional(),
86
105
  partOfBundleIds: z.array(z.string()).optional(),
@@ -229,9 +248,7 @@ export const productListParamsSchema = z.object({
229
248
  minPrice: z.coerce.number().optional(),
230
249
  maxPrice: z.coerce.number().optional(),
231
250
  inStock: z.coerce.boolean().optional(),
232
- listingType: z
233
- .enum(["standard", "auction", "pre-order", "prize-draw", "classified", "digital-code", "live"])
234
- .optional(),
251
+ listingType: LISTING_TYPE_ENUM.optional(),
235
252
  storeId: z.string().optional(),
236
253
  sort: z.string().optional(),
237
254
  page: z.coerce.number().optional(),
@@ -11,12 +11,34 @@ export type ProductCondition = "new" | "like_new" | "good" | "fair" | "poor" | "
11
11
  * accepts legacy alias tokens (`preorder` → `pre-order`, `product` → `standard`)
12
12
  * but stored values are always one of these five.
13
13
  */
14
- export type ListingType = "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live" | "art" | "stickers";
14
+ export type ListingType = "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live" | "art" | "stickers" | "spares";
15
+ /**
16
+ * One gallery image — THE single definition, used by both the Firestore
17
+ * document and the client shape.
18
+ *
19
+ * 🛑 This interface already existed here, barrel-exported as public API, with
20
+ * ZERO consumers anywhere and `Product.images` declared `string[]` beside it:
21
+ * a speculative type describing a shape nothing produced or read. Plan A6
22
+ * resurrected it rather than declaring a second `ProductImage` in
23
+ * `schemas/firestore.ts`, which is what the first draft did — two same-named
24
+ * types in one package is a defect even when both look plausible, and the
25
+ * barrel's choice between them is invisible at every call site (Root Cause
26
+ * #36). `schemas/firestore.ts` imports this one.
27
+ *
28
+ * `alt` is the point: measured across the fifteen-site competitor crawl,
29
+ * per-image alt coverage is ~0% (0/151, 0/306, 0/317; tcgrepublic's 37/63 is
30
+ * the best anyone manages). `width`/`height` let `next/image` reserve space so
31
+ * a gallery does not reflow as each image lands — optional because we do not
32
+ * always know them at write time, and a guessed aspect ratio is worse than
33
+ * none.
34
+ */
15
35
  export interface ProductImage {
16
36
  url: string;
17
37
  thumbnailUrl?: string;
18
38
  alt?: string;
19
39
  order?: number;
40
+ width?: number;
41
+ height?: number;
20
42
  }
21
43
  export interface ProductSeo {
22
44
  title?: string;
@@ -78,6 +100,28 @@ export interface ProductItem {
78
100
  sublistingIcon?: string;
79
101
  isOnSale?: boolean;
80
102
  isSold?: boolean;
103
+ /**
104
+ * Printed or built when ordered, so stock does not gate it. Per-LISTING, not
105
+ * implied by `listingType` — a seller can hold a finite run of pre-cut sheets
106
+ * and also offer a print-on-demand one.
107
+ *
108
+ * On the client this drives three things: the PDP says "made to order"
109
+ * instead of a stock line, the group picker uncaps that member's stepper, and
110
+ * `baseAvailable` keeps the row in the default scope at quantity 0.
111
+ */
112
+ madeToOrder?: boolean;
113
+ /**
114
+ * Self-assembled-selection rules, present only on a group PARENT. See
115
+ * `_internal/shared/features/products/group-rules.ts` for the shape, the
116
+ * resolver and the single enforcement point — these fields exist here only
117
+ * so the admin editor (`GroupSettingsPanel`'s `groupRules` prop) can seed its
118
+ * form from the product it is already showing, with no second fetch.
119
+ */
120
+ groupMinMembers?: number;
121
+ groupMaxMembers?: number;
122
+ groupMaxUnitsPerMember?: number;
123
+ groupRequiredMemberIds?: string[];
124
+ groupDiscountPercent?: number;
81
125
  inStock?: boolean;
82
126
  stockCount?: number;
83
127
  rating?: number;
@@ -0,0 +1,46 @@
1
+ import type { ProductImage } from "../schemas/firestore";
2
+ /**
3
+ * The thumbnail for one product-group member, from whichever shape the caller
4
+ * supplied.
5
+ *
6
+ * ── 🛑 WHY `image` IS TRIED FIRST ──────────────────────────────────────────
7
+ *
8
+ * `toPublicGroupMember` is an ALLOW-LIST projection: it emits the singular
9
+ * `image` (`images[0].url ?? mainImage`) and no `images[]` array at all. So a
10
+ * component reading only `images[0]` resolves `undefined` for every member
11
+ * served by the public endpoint and renders a placeholder tile.
12
+ *
13
+ * That was live on every product-group page — all seven BX-53 members showed
14
+ * a grey box while their images sat in Storage serving HTTP 200. Nothing
15
+ * errors, because an empty `src` is a legal `MediaImage`.
16
+ *
17
+ * The `images[0]` fallback stays for callers that pass a raw document rather
18
+ * than the projection.
19
+ *
20
+ * ── 🛑 WHY ITS OWN MODULE, AND NOT EITHER COMPONENT ────────────────────────
21
+ *
22
+ * `ShowGroupSection` renders `GroupMemberPicker`, so the import edge between
23
+ * them already runs one way. Putting the shared helper in either file and
24
+ * importing it from the other closes a CYCLE — which is exactly what
25
+ * `audit-import-cycles` caught when the first version of this fix exported it
26
+ * from `ShowGroupSection`.
27
+ *
28
+ * A helper used by two components belongs beside neither (Root Cause #18:
29
+ * import from the module that DEFINES the symbol). One definition, two
30
+ * importers, no edge between them — and one rule reachable by copy-paste is
31
+ * one that gets copied wrong (Root Cause #75), which is how the two spellings
32
+ * diverged in the first place.
33
+ */
34
+ export interface GroupMemberImageSource {
35
+ /** The public projection's single thumbnail. */
36
+ image?: string;
37
+ /**
38
+ * A raw document's gallery. Only read when `image` is absent.
39
+ *
40
+ * 🛑 `ProductImage[]` since A6 — a raw document holds objects, a legacy one
41
+ * holds strings, and the public projection holds neither. `productImageUrls`
42
+ * is the one normaliser that spans all three.
43
+ */
44
+ images?: readonly (string | ProductImage)[];
45
+ }
46
+ export declare function resolveGroupMemberImage(m: GroupMemberImageSource): string;
@@ -0,0 +1,4 @@
1
+ import { productImageUrls } from "../schemas/firestore.js";
2
+ export function resolveGroupMemberImage(m) {
3
+ return m.image || productImageUrls(m.images)[0] || "";
4
+ }
@@ -15,6 +15,10 @@ import { isListingType } from "./listing-type.js";
15
15
  */
16
16
  export const LISTING_BADGE_VARIANT = {
17
17
  standard: "default",
18
+ // Shares `primary` with art/stickers: all three are "a thing we make or
19
+ // salvage ourselves" rather than a mechanism (auction) or a promise
20
+ // (pre-order), and the badge LABEL is what distinguishes them.
21
+ spares: "primary",
18
22
  auction: "warning",
19
23
  "pre-order": "secondary",
20
24
  "prize-draw": "primary",
@@ -29,7 +29,7 @@ export function ReviewCard({ review, context = "general", className = "" }) {
29
29
  const showProductLink = context !== "listing" && !!productHref;
30
30
  const showProfileLink = !!profileHref;
31
31
  const hasFooter = showStoreLink || showProductLink || showProfileLink;
32
- return (_jsxs(Stack, { padding: "5", shadow: "hover-md", className: `group h-full border border-neutral-200 transition ${className}`, rounded: "xl", surface: "default", children: [_jsxs(Link, { href: reviewHref, className: "flex flex-col flex-1 min-h-0", children: [_jsxs(Row, { align: "start", gap: "3", children: [review.userAvatar ? (_jsx(Div, { className: "relative h-9 w-9 flex-shrink-0", rounded: "full", overflow: "hidden", children: _jsx(MediaImage, { src: review.userAvatar, alt: displayName, size: "avatar" }) })) : (_jsx(Row, { textWeight: "medium", textSize: "sm", className: "h-9 w-9 flex-shrink-0 bg-neutral-200 text-[var(--appkit-color-text-muted)]", align: "center", justify: "center", rounded: "full", children: initials })), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Row, { wrap: true, gap: "sm", children: [_jsx(Span, { color: "inverse", weight: "medium", className: "text-[var(--appkit-color-text)] dark:", children: displayName }), review.verified && (_jsx(Span, { color: "success", surface: "success-surface", size: "xs", weight: "medium", rounded: "full", padding: "pill-xs", children: "Verified" })), date && (_jsx(Span, { size: "xs", color: "muted", children: date }))] }), _jsx(Div, { className: "mt-1", children: _jsx(StarRating, { value: review.rating, size: "sm", readOnly: true }) })] })] }), review.title && (_jsx(Heading, { color: "inverse", level: 4, className: "mt-3 text-[var(--appkit-color-text)] dark:", weight: "semibold", children: review.title })), review.comment && (_jsx(RichText, { html: normalizeRichTextHtml(review.comment), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "mt-2 text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)] text-[var(--appkit-color-text-muted)]" })), review.images && review.images.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", className: "mt-3", children: review.images.map((img, i) => (_jsx(Div, { className: "relative h-16 w-16 flex-shrink-0 border border-neutral-100", rounded: "lg", overflow: "hidden", children: _jsx(MediaImage, { src: img, alt: `Review image ${i + 1}`, size: "thumbnail" }) }, i))) })), (review.helpfulCount ?? 0) > 0 && (_jsxs(Text, { className: "mt-3", color: "faint", size: "xs", children: [review.helpfulCount, " found this helpful"] }))] }), hasFooter && (_jsxs(Stack, { gap: "xs", className: "mt-3 border-t border-neutral-100", padding: "t-sm", children: [showStoreLink && (_jsxs(Link, { href: String(ROUTES.PUBLIC.STORE_DETAIL(review.storeSlug)), className: "flex items-center gap-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-medium text-primary hover:underline", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83C\uDFEA" }), _jsx(Span, { truncate: true, children: review.storeName })] })), showProductLink && (_jsxs(Link, { href: productHref, className: "flex items-center gap-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] text-[var(--appkit-color-text-muted)] hover:text-neutral-700 hover:text-[var(--appkit-color-text-muted)]", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83D\uDCE6" }), _jsx(Span, { truncate: true, children: review.productTitle ?? "View Product" }), _jsx(Span, { "aria-hidden": "true", className: "ml-auto text-primary group-hover:translate-x-0.5 transition-transform", children: "\u2192" })] })), showProfileLink && (_jsxs(Link, { href: profileHref, className: "flex items-center gap-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] text-[var(--appkit-color-text-muted)] hover:text-neutral-700 hover:text-[var(--appkit-color-text-muted)]", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83D\uDC64" }), _jsx(Span, { truncate: true, children: displayName })] }))] }))] }));
32
+ return (_jsxs(Stack, { padding: "5", shadow: "hover-md", className: `group h-full border border-neutral-200 transition ${className}`, rounded: "xl", surface: "default", children: [_jsxs(Link, { href: reviewHref, className: "flex flex-col flex-1 min-h-0", children: [_jsxs(Row, { align: "start", gap: "3", children: [review.userAvatar ? (_jsx(Div, { className: "relative h-9 w-9 flex-shrink-0", rounded: "full", overflow: "hidden", children: _jsx(MediaImage, { src: review.userAvatar, alt: displayName, size: "avatar" }) })) : (_jsx(Row, { textWeight: "medium", textSize: "sm", className: "h-9 w-9 flex-shrink-0 bg-neutral-200 text-[var(--appkit-color-text-muted)]", align: "center", justify: "center", rounded: "full", children: initials })), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Row, { wrap: true, gap: "sm", children: [_jsx(Span, { color: "inverse", weight: "medium", className: "text-[var(--appkit-color-text)] dark:", children: displayName }), review.verified && (_jsx(Span, { color: "success", surface: "success-surface", size: "xs", weight: "medium", rounded: "full", padding: "pill-xs", children: "Verified" })), date && (_jsx(Span, { size: "xs", color: "muted", children: date }))] }), _jsx(Div, { className: "mt-1", children: _jsx(StarRating, { value: review.rating, size: "sm", readOnly: true }) })] })] }), review.title && (_jsx(Heading, { color: "inverse", level: 4, className: "mt-3 text-[var(--appkit-color-text)] dark:", weight: "semibold", children: review.title })), review.comment && (_jsx(RichText, { html: normalizeRichTextHtml(review.comment), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "mt-2 text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)] text-[var(--appkit-color-text-muted)]" })), review.images && review.images.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", className: "mt-3", children: review.images.map((img, i) => (_jsx(Div, { className: "relative h-16 w-16 flex-shrink-0 border border-neutral-100", rounded: "lg", overflow: "hidden", children: _jsx(MediaImage, { src: img, alt: `Review image ${i + 1}`, size: "thumbnail" }) }, i))) })), (review.helpfulCount ?? 0) > 0 && (_jsxs(Text, { className: "mt-3", color: "faint", size: "xs", children: [review.helpfulCount, " found this helpful"] }))] }), hasFooter && (_jsxs(Stack, { gap: "xs", className: "mt-3 border-t border-neutral-100", padding: "t-sm", children: [showStoreLink && (_jsxs(Link, { href: String(ROUTES.PUBLIC.STORE_DETAIL(review.storeSlug)), className: "flex items-center gap-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-medium text-primary hover:underline", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83C\uDFEA" }), _jsx(Span, { truncate: true, children: review.storeName })] })), showProductLink && (_jsxs(Link, { href: productHref, className: "flex items-center gap-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text)]", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83D\uDCE6" }), _jsx(Span, { truncate: true, children: review.productTitle ?? "View Product" }), _jsx(Span, { "aria-hidden": "true", className: "ml-auto text-primary group-hover:translate-x-0.5 transition-transform", children: "\u2192" })] })), showProfileLink && (_jsxs(Link, { href: profileHref, className: "flex items-center gap-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text)]", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83D\uDC64" }), _jsx(Span, { truncate: true, children: displayName })] }))] }))] }));
33
33
  }
34
34
  export function ReviewsList({ reviews, context = "general", isLoading, totalPages = 1, currentPage = 1, onPageChange, emptyLabel = "No reviews yet", }) {
35
35
  if (isLoading) {
@@ -51,7 +51,7 @@ export function ScamProfileView({ scammer, isAuthenticated, incidents = [], comm
51
51
  const registryHref = String(ROUTES.PUBLIC.SCAMS);
52
52
  const contestLoginHref = `${String(ROUTES.AUTH.LOGIN)}?redirect=${encodeURIComponent(`/scams/${scammer.id}`)}`;
53
53
  const scamTypeDef = getScamType(scammer.scamType);
54
- return (_jsxs(Main, { children: [_jsx(Div, { className: "border-b appkit-breadcrumb-strip", children: _jsx(Container, { size: "xl", padding: "y-sm", children: _jsx(Breadcrumb, { items: [
54
+ return (_jsxs(Main, { children: [_jsx(Div, { className: "border-b", children: _jsx(Container, { size: "xl", padding: "y-sm", children: _jsx(Breadcrumb, { items: [
55
55
  { label: "Home", href: "/" },
56
56
  { label: "Scam Registry", href: registryHref },
57
57
  { label: scammer.displayNames[0] ?? "Profile" },
@@ -0,0 +1,44 @@
1
+ import React from "react";
2
+ export interface EntityTypeaheadPick {
3
+ /** The canonical entity's title, as the index holds it. */
4
+ readonly title: string;
5
+ /** Its destination, which encodes the slug. */
6
+ readonly href: string;
7
+ /** The index type it came from. */
8
+ readonly typeKey: string;
9
+ /**
10
+ * 🛑 What the USER typed, verbatim. See the header — a consumer that
11
+ * analyses the resolved title instead of this is throwing away the signal
12
+ * it was built to detect.
13
+ */
14
+ readonly rawQuery: string;
15
+ }
16
+ export interface EntityTypeaheadProps {
17
+ /** Index type keys to search. `["model"]`, `["category"]`, `["part"]`… */
18
+ types: readonly string[];
19
+ onPick: (pick: EntityTypeaheadPick) => void;
20
+ /** `siteSettings.search?.indexEnabled`. Read as `!== false`. */
21
+ indexEnabled?: boolean;
22
+ label: string;
23
+ placeholder?: string;
24
+ /** Mirrors the field the consumer owns, so the two cannot disagree. */
25
+ value?: string;
26
+ onChange?: (value: string) => void;
27
+ limit?: number;
28
+ /**
29
+ * Default `true`: picking a suggestion fills the field with the
30
+ * canonical title, as every ordinary typeahead does.
31
+ *
32
+ * 🛑 Set `false` when the consumer's DOWNSTREAM LOGIC treats a mismatch
33
+ * between what was typed and the canonical form as a signal rather than
34
+ * noise — `/tools/authenticity` is the one caller that needs this. Its
35
+ * engine fires a trademark-evasion warning on a FUZZY match, and that
36
+ * warning is exactly what filling the field would silence: the engine
37
+ * would receive "Storm Pegasus" and match it exactly, never learning the
38
+ * user typed "Pegasis" at all. With this false, a pick still calls
39
+ * `onPick` (with `rawQuery` intact) but leaves the field exactly as
40
+ * typed — discovery without consequence.
41
+ */
42
+ fillOnPick?: boolean;
43
+ }
44
+ export declare function EntityTypeahead({ types, onPick, indexEnabled, label, placeholder, value, onChange, limit, fillOnPick, }: EntityTypeaheadProps): React.JSX.Element;
@@ -0,0 +1,92 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /*
4
+ * WHY: Three tools need "turn what the user typed into a canonical entity",
5
+ * and each wants a different slice. `resolveEntity`'s `types` parameter
6
+ * exists for exactly this; this is its UI.
7
+ * WHAT: A generic index-backed typeahead. Offers canonical names, reports
8
+ * BOTH the chosen entity and the raw string the user typed.
9
+ *
10
+ * ## 🛑 `onPick` hands back the RAW query as well as the entity
11
+ *
12
+ * This is the one thing about the component that is not a convenience.
13
+ *
14
+ * `/tools/authenticity` treats a fuzzy name match as EVIDENCE — a near-miss
15
+ * fires a trademark-evasion warning, and its own source records the case:
16
+ * "`Pegasis` … resolves as an `alias` hit, and the evasion-spelling warning
17
+ * (which only fires on a `fuzzy` hit) stays silent." Meanwhile this
18
+ * component's matcher derives its alias table from a crawl of a counterfeit
19
+ * seller's 1,277 titles, on the stated insight that "a counterfeiter's
20
+ * evasion spellings are the same strings a real buyer mistypes."
21
+ *
22
+ * Both systems consume the same corpus with OPPOSITE intent: search treats a
23
+ * counterfeiter's spelling as something to match through, authenticity
24
+ * treats it as the signal. So if someone types `Pegasis`, picks "Storm
25
+ * Pegasus" from this dropdown, and the consumer forwards only the resolved
26
+ * slug, the engine never sees `Pegasis` and the evasion warning is silenced
27
+ * BY CONSTRUCTION — the tool would confirm a fake using the exact spelling
28
+ * that indicates one.
29
+ *
30
+ * Hence: this component PROPOSES an entity; the consumer must keep passing
31
+ * what the user actually typed. Enforced from the other side by
32
+ * `audit-entity-resolver-consumers` R2, which forbids any module under
33
+ * `features/authenticity/` from importing the matcher at all — which is why
34
+ * this lives here and reaches that tool as a slot.
35
+ *
36
+ * @tag domain:search
37
+ * @tag layer:feature
38
+ * @tag pattern:none
39
+ * @tag access:client
40
+ * @tag consumers:/tools/authenticity,/tools/price-index
41
+ * @tag sideEffects:network
42
+ */
43
+ import React from "react";
44
+ import { Button, Div, Input, Stack, Text } from "../../../ui/index.js";
45
+ import { useSiteSearch } from "../hooks/useSiteSearch.js";
46
+ /** Stable identity — a `useMemo` dependency inside the hook. */
47
+ const passthroughLabel = (typeKey) => typeKey;
48
+ export function EntityTypeahead({ types, onPick, indexEnabled, label, placeholder, value, onChange, limit = 6, fillOnPick = true, }) {
49
+ const controlled = value !== undefined;
50
+ const [internal, setInternal] = React.useState("");
51
+ const query = controlled ? value : internal;
52
+ const [active, setActive] = React.useState(false);
53
+ const [dismissed, setDismissed] = React.useState(false);
54
+ const { status, result } = useSiteSearch(query, {
55
+ active,
56
+ indexEnabled,
57
+ types,
58
+ label: passthroughLabel,
59
+ });
60
+ const hits = result?.groups.flatMap((g) => g.hits) ?? [];
61
+ const show = !dismissed && status === "ready" && hits.length > 0 && query.trim().length > 0;
62
+ const setQuery = (next) => {
63
+ setDismissed(false);
64
+ if (controlled)
65
+ onChange?.(next);
66
+ else
67
+ setInternal(next);
68
+ };
69
+ return (_jsxs(Stack, { gap: "xs", className: "min-w-0", children: [_jsx(Input, { type: "search", value: query, "aria-label": label, placeholder: placeholder, onFocus: () => setActive(true), onChange: (event) => {
70
+ setActive(true);
71
+ setQuery(event.target.value);
72
+ } }), show && (_jsxs(Stack, { gap: "xs", className: "min-w-0", children: [_jsx(Text, { size: "xs", color: "muted", children: "Did you mean one of these?" }), hits.slice(0, limit).map((hit) => (_jsxs(Div, { surface: "card", padding: "xs", rounded: "md", border: "subtle", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => {
73
+ /*
74
+ * 🛑 `rawQuery: query` — BEFORE the field is overwritten
75
+ * with the canonical title. Reading it after would hand the
76
+ * consumer the canonical name as "what the user typed",
77
+ * which is the silent version of the bug this whole
78
+ * component's header is about.
79
+ */
80
+ onPick({
81
+ title: hit.title,
82
+ href: hit.href,
83
+ typeKey: result?.groups.find((g) => g.hits.includes(hit))?.key ?? "",
84
+ rawQuery: query,
85
+ });
86
+ // See `fillOnPick`'s 🛑 — skipping this is the whole
87
+ // reason the prop exists, not an inconsistency.
88
+ if (fillOnPick)
89
+ setQuery(hit.title);
90
+ setDismissed(true);
91
+ }, children: hit.title }), hit.detail && (_jsx(Text, { size: "xs", color: "muted", children: hit.detail }))] }, hit.id)))] }))] }));
92
+ }
@@ -47,5 +47,16 @@ export interface SearchProps {
47
47
  resourceTypes?: SearchResourceTypeOption[];
48
48
  defaultResourceType?: SearchResourceType;
49
49
  storageKey?: string;
50
+ /**
51
+ * `siteSettings.search?.indexEnabled`. Read as `!== false`, so an absent
52
+ * settings object leaves the local index in play.
53
+ *
54
+ * When the index answers, the typeahead spends ZERO network requests per
55
+ * keystroke and spans all 21 indexed types instead of the four
56
+ * `/api/search/suggestions` can produce. When it is off or has failed, the
57
+ * component falls back to that endpoint unchanged — which is why
58
+ * `useNavSuggestions` is still called rather than deleted.
59
+ */
60
+ searchIndexEnabled?: boolean;
50
61
  }
51
- export declare function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, placeholder, deferred, debounceMs, onClear, className, router, quickLinks, labels, suggestionTypeIcons, suggestionTypeBadges, resourceTypes, defaultResourceType, storageKey, }: SearchProps): React.JSX.Element | null;
62
+ export declare function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, placeholder, deferred, debounceMs, onClear, className, router, quickLinks, labels, suggestionTypeIcons, suggestionTypeBadges, resourceTypes, defaultResourceType, storageKey, searchIndexEnabled, }: SearchProps): React.JSX.Element | null;