@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
@@ -218,6 +218,49 @@ const rawTesterChecklistItems = [
218
218
  },
219
219
  ]),
220
220
  ...group("buying", "Buying", [
221
+ {
222
+ /*
223
+ * Part Two B/C/D/E — spares, made-to-order, promoted sticker parts and
224
+ * the group discount, all together on the one page a buyer actually
225
+ * uses. Each case is an explicit BEFORE -> AFTER: a spare page returning
226
+ * 200 with plausible cards is not evidence any of this works.
227
+ */
228
+ pageKey: "spares-and-groups",
229
+ pageLabel: "Spares browse, made-to-order, and group selections",
230
+ href: "/spares",
231
+ cases: [
232
+ {
233
+ key: "spares-not-in-general-catalogue",
234
+ label: "A spare does NOT appear on /products, but does appear on /spares",
235
+ description: "BEFORE: there was no tenth listing type. AFTER: open /products and search for a known spare's title (e.g. \"Cross Dranzer\") — it must NOT appear, because `spares` sets `inGeneralCatalogue: false`. The same title DOES appear on /spares. If it appears on both, the exclusion broke; if it appears on neither, the nav/page wiring broke.",
236
+ },
237
+ {
238
+ key: "spares-nav-entry-present",
239
+ label: "The main nav's Products dropdown has a \"Spares\" child linking to /spares",
240
+ },
241
+ {
242
+ key: "made-to-order-buyable-at-zero-stock",
243
+ label: "A made-to-order listing at zero stock is buyable and says \"Made to order\", never \"Out of Stock\"",
244
+ description: "Open the seeded \"Custom Beyblade Sticker Sheet — Print on Demand\" (stockQuantity 0, madeToOrder true). It must show \"Made to order — printed when you order\" rather than an Out of Stock badge, Add to Cart must work, and checkout must complete. This is the exact case C exists for: stock must gate nothing on this listing.",
245
+ href: "/products/sticker-beyblade-custom-print-on-demand",
246
+ },
247
+ {
248
+ key: "group-picker-shows-rules-before-choosing",
249
+ label: "A group with selection rules states them BEFORE the buyer picks anything",
250
+ description: "Open the seeded \"Burst & X Attackers You Might Like\" group (via one of its member products' Group section). The picker must show its rules (min 2, max 3, max 2 of any one, 10% off) as text near the top — not only as a refusal after submitting.",
251
+ },
252
+ {
253
+ key: "group-picker-blocks-and-explains-invalid-selection",
254
+ label: "Picking an invalid combination (e.g. only 1 item, or 3 of one item) shows the reason and disables Add",
255
+ description: "On the same group, select only 1 item: the Add button must be disabled and a message naming the rule must appear (\"Pick at least 2…\"). Select 3 of one item: a different message naming the per-item cap must appear. Fixing the selection must re-enable Add without a page reload.",
256
+ },
257
+ {
258
+ key: "group-discount-applied-and-shown",
259
+ label: "The group discount is shown on the picker AND the cart line is actually discounted by that percentage",
260
+ description: "Select a valid combination (e.g. 2 different items) on the 10%-off group and note the total shown in the picker. Add to cart, then open the cart and the checkout review: the line total must be 10% below the sum of the two items' individual prices, to the paisa, in all three places (picker, cart, checkout).",
261
+ },
262
+ ],
263
+ },
221
264
  {
222
265
  /*
223
266
  * The post-purchase money surface. `/user/orders/[id]/` has five
@@ -2212,6 +2255,39 @@ const rawTesterChecklistItems = [
2212
2255
  },
2213
2256
  ]),
2214
2257
  ...group("selling", "Selling", [
2258
+ {
2259
+ /*
2260
+ * Part Two B/E — the seller side of spares, and the shared group-stock
2261
+ * editor. "Stock is independent per listing" is the whole premise, so
2262
+ * the editor-save-and-reload round trip is what actually proves that —
2263
+ * a shared pool bug would show up exactly here, as one listing's stock
2264
+ * silently changing another's.
2265
+ */
2266
+ pageKey: "spares-seller-and-group-editing",
2267
+ pageLabel: "Seller spares dashboard and group stock/rules editing",
2268
+ href: "/store/spares",
2269
+ cases: [
2270
+ {
2271
+ key: "seller-can-list-a-spare",
2272
+ label: "A seller can create, publish and see a spare on their own /store/spares list",
2273
+ },
2274
+ {
2275
+ key: "group-stock-edit-is-independent",
2276
+ label: "Editing one group member's stock does NOT change any other member's stock",
2277
+ description: "Open a group parent's editor, note two children's stock figures, change ONLY one of them in the shared stock editor, save, reload. The edited child shows the new figure; the other child is UNCHANGED. This is the premise the whole feature rests on — a shared pool would fail exactly this check.",
2278
+ },
2279
+ {
2280
+ key: "group-rules-save-and-reload",
2281
+ label: "Saved group rules (min/max/per-item cap/discount) survive a reload and reach the public picker",
2282
+ description: "In the group parent's editor, set min/max/per-item/discount to new values, save, reload the editor — the same values must still be there. Then open the group's public picker (as a buyer) and confirm the NEW rule text is what shows, not the old one.",
2283
+ },
2284
+ {
2285
+ key: "group-stock-partial-failure-reported",
2286
+ label: "Saving group stock for a member no longer in the group reports it, without losing the other saved rows",
2287
+ description: "Unlink a child from a group in one tab, then (without reloading) try to save that same child's stock from a second tab holding the stale member list. The response must name that row as rejected while the OTHER rows in the same save still succeed — not a blanket failure.",
2288
+ },
2289
+ ],
2290
+ },
2215
2291
  {
2216
2292
  /*
2217
2293
  * The UPDATE path. The create path has 57 cases on /store/products/new
@@ -3160,28 +3236,135 @@ const rawTesterChecklistItems = [
3160
3236
  * romanisation and a licensed Indian importer's name.
3161
3237
  */
3162
3238
  pageKey: "authenticity-checker",
3163
- pageLabel: "Authenticity checker — box codes, per-generation checks and the sign-in gate",
3239
+ pageLabel: "Authenticity checker — box codes, per-generation checks and the regional code tables",
3164
3240
  href: "/tools/authenticity",
3165
3241
  cases: [
3166
- { key: "guest-sees-reference-not-a-wall", label: "Signed out, the generation table and universal checks still render — only the lookup is gated" },
3167
- { key: "no-signin-flash-when-authenticated", label: "A signed-in user never sees 'Sign in to run a lookup' during a hard reload" },
3242
+ /* ── Guards for the near-miss work (2026-10-19) ──────────────────
3243
+ * All three protect behaviour that is ALREADY CORRECT. They exist
3244
+ * because a "did you mean" feature is precisely the change that
3245
+ * could turn an absent code into an accusation, and this tool's
3246
+ * third rule is that a false counterfeit accusation against a real
3247
+ * seller is worse than a missed one.
3248
+ */
3249
+ {
3250
+ key: "authenticity-unknown-code-is-not-fake",
3251
+ label: "An invented but well-formed box code returns NEUTRAL + inconclusive — never a counterfeit verdict",
3252
+ description: "Type a code that is well-formed (one letter, four digits) but not in any table — try Z9999. The verdict must be \"Inconclusive\", the signal must read neutral, and the text must say the code is NOT RECORDED rather than fake. Any wording that implies the item is counterfeit is the failure, and so is any verdict more severe than inconclusive. New genuine codes appear regularly, and counterfeiters use real codes anyway, so an absent code proves nothing in either direction.",
3253
+ href: "/tools/authenticity",
3254
+ },
3255
+ {
3256
+ key: "authenticity-near-miss-suggests",
3257
+ label: "A one-character-off code offers \"did you mean …?\" as a SUGGESTION, with no change to the verdict",
3258
+ description: "BEFORE: a transposed code said only \"not in either table\". AFTER: enter A0831 when A0813 is a known code — you get a neutral \"did you mean A0813?\" line. It must sit OUTSIDE the \"What I checked\" list, must not appear as a signal, must not fire any evasion or counterfeit warning, and the verdict must stay exactly what it was without the suggestion. A near-miss is ambiguous between a typo and a batch mimicking a real code; it is input correction, never evidence.",
3259
+ href: "/tools/authenticity",
3260
+ },
3261
+ {
3262
+ key: "authenticity-inconclusive-asks-for-more",
3263
+ label: "Code-only input prompts for the model name and offers the ask-the-seller next step",
3264
+ description: "Enter a code and a generation but NO model name. The inconclusive result must now say that supplying the name lets it cross-check name against code, and must offer asking the seller for a photo of the box bottom and the screws. BEFORE: you got the physical checklist and no indication that a second field would unlock a real check.",
3265
+ href: "/tools/authenticity",
3266
+ },
3267
+ {
3268
+ key: "authenticity-evasion-warning-survives",
3269
+ label: "Typing \"Pegasis\" still reaches the tool's own verdict with the evasion warning intact",
3270
+ description: "SECURITY-SHAPED, and it exists because of a specific hazard. The site search matcher deliberately treats \"Pegasis\" as a match for \"Storm Pegasus\" — its alias table is built FROM a counterfeit seller's spellings. This tool treats that same spelling as EVIDENCE of trademark evasion. Type Pegasis here: the fuzzy/evasion signal must still fire. If a name typeahead is offered and you pick a canonical name from it, the tool must STILL analyse what you typed — a case asserting only \"the typeahead found it\" would pass against exactly the bug this guards.",
3271
+ href: "/tools/authenticity",
3272
+ },
3273
+ {
3274
+ key: "authenticity-name-typeahead",
3275
+ label: "Typing a partial model name offers canonical releases, and picking one does NOT change what gets checked",
3276
+ description: "BEFORE: you had to already know the exact release name. AFTER: type a partial name (e.g. \"Drago\") and canonical releases appear beneath the field as suggestions. Click one — a confirmation line appears (\"We have a record close to this: …\"), but the TEXT YOU TYPED in the field must stay exactly as you typed it. Submit the check and confirm the result is consistent with what you typed, not with the suggestion you clicked.",
3277
+ href: "/tools/authenticity",
3278
+ },
3279
+ {
3280
+ key: "authenticity-box-code-still-instant",
3281
+ label: "A box date code (e.g. A0813) resolves instantly with no network request — it is NOT in the search index",
3282
+ description: "GUARD CASE. Open DevTools -> Network, clear it, and submit a known box date code. No fetch to the search artifact or any search endpoint should appear — the date-code table is bundled locally and always was. This case exists because the site-wide index work added a name typeahead to this same page, and the two must stay on separate code paths.",
3283
+ href: "/tools/authenticity",
3284
+ },
3285
+ { key: "guest-sees-reference-not-a-wall", label: "Signed out, the LOOKUP ITSELF runs — no sign-in card anywhere on the page" },
3286
+ { key: "no-signin-flash-when-authenticated", label: "No session placeholder and no sign-in card during a hard reload, signed in or out" },
3168
3287
  { key: "midfake-code-reads-as-batch-not-accusation", label: "A matched midfake code reports a batch, never a counterfeit accusation against the seller" },
3169
3288
  { key: "date-code-shows-both-readings", label: "A box date code shows BOTH circulating readings and says neither is official" },
3170
3289
  { key: "real-product-code-is-not-evidence", label: "A well-formed product code is marked neutral and states that real codes appear on fakes" },
3171
- { key: "name-code-mismatch-asks-rather-than-accuses", label: "A name/code mismatch is a question, and ticking Random Booster re-reads it as expected" },
3290
+ { key: "name-code-mismatch-asks-rather-than-accuses", label: "A name/code mismatch is a question, and answering 'How it was sold' re-reads it as expected" },
3172
3291
  { key: "ambiguous-name-asks-which-model", label: "An ambiguous name asks which release instead of confidently naming one" },
3173
3292
  { key: "takara-spelling-is-not-flagged", label: "'Storm Pegasis' (the Takara Tomy spelling) is not flagged, while 'Scythee' is" },
3174
3293
  { key: "funskool-is-not-a-fake-signal", label: "Funskool reads as a licensed importer; Rapidity reads as an unlicensed producer" },
3175
3294
  { key: "unknown-code-is-not-called-fake", label: "An unrecorded code says 'not recorded' and explicitly not 'fake'" },
3176
3295
  { key: "asks-generation-when-nothing-inferable", label: "With no generation inferable the tool asks instead of returning a verdict" },
3177
- { key: "api-refuses-signed-out", label: "POST /api/tools/authenticity returns 401/403 signed out and leaks no assessment" },
3178
- { key: "json-ld-declares-the-gate", label: "The WebApplication JSON-LD declares the gated region, and its cssSelector matches a real element" },
3296
+ { key: "api-refuses-signed-out", label: "POST /api/tools/authenticity answers a signed-out caller, and still 400s an undeclared key" },
3297
+ { key: "json-ld-declares-no-false-gate", label: "The WebApplication JSON-LD declares NO paywalled region, now that there is none" },
3298
+ { key: "defaults-to-metal-fight", label: "Generation opens on Metal Fight, and a Burst code against it raises a mismatch signal rather than passing silently" },
3299
+ { key: "contested-code-reports-the-disagreement", label: "A code two sources disagree about reports the disagreement instead of picking a side" },
3300
+ { key: "font-midfake-does-not-accuse", label: "A midfake row whose own source calls the code authentic does NOT reach 'likely reproduction'" },
3301
+ { key: "regional-code-reads-as-regional", label: "A Korean or Middle-East code names its region instead of coming back unrecorded" },
3302
+ { key: "series-window-names-the-production-run", label: "A derivable date names its Metal-series production window, or names none outside all four" },
3303
+ { key: "temporal-context-for-an-unmatched-code", label: "An unmatched code reports what else the tables record from the same month, framed as context not a match" },
3304
+ /* ── The model picker and the per-model code list (2026-10-20) ───
3305
+ * The reported bug: naming a bey the tool holds 33 recorded box
3306
+ * codes for produced "Nothing contradicts it, and nothing confirms
3307
+ * it either" and never mentioned them. Every case here compares the
3308
+ * answer against data the tool demonstrably has.
3309
+ */
3310
+ { key: "model-picker-finds-l-drago", label: "The model picker finds all three L-Dragos by partial name, and each shows how many box codes are on file" },
3311
+ { key: "picked-model-lists-its-box-codes", label: "Picking a model with no code returns the codes recorded for that exact bey, not a bare 'inconclusive'" },
3312
+ { key: "picked-model-plus-its-own-code", label: "A code recorded against the picked bey reads as agreement; one recorded against a different bey reads as weak support, not a mismatch" },
3313
+ { key: "missing-code-is-not-an-accusation", label: "A code absent from the picked model's list says 'unrecorded', never 'fake', and the verdict stays inconclusive" },
3314
+ { key: "working-out-is-collapsed-by-default", label: "The verdict is readable without expanding anything, and a concern auto-opens the working-out" },
3315
+ { key: "result-scrolls-into-view", label: "Submitting scrolls the verdict into view on a phone rather than leaving it below the fold" },
3316
+ ],
3317
+ },
3318
+ {
3319
+ pageKey: "price-index-tool",
3320
+ pageLabel: "What it sold for — realised prices",
3321
+ href: "/tools/price-index",
3322
+ cases: [
3323
+ {
3324
+ key: "price-index-resolves-locally",
3325
+ label: "Candidate names appear with no network delay while typing; only the bands themselves come from the server",
3326
+ description: "BEFORE: resolving a free-text query to a category leaf was a Firestore searchTxt query on every lookup. AFTER: open /tools/price-index, open DevTools -> Network, clear the log, and search a real model name. The suggestion/candidate step must feel instant and correspond to the local search artifact (a .json fetched once, not per search); only ONE further request — to the bands endpoint — should fire, carrying just the resolved id(s), and it must return real min/median/mean/max figures for a model with recorded sales.",
3327
+ href: "/tools/price-index",
3328
+ },
3329
+ {
3330
+ key: "price-index-falls-back-when-index-off",
3331
+ label: "Price lookup still works end-to-end if the search index is unavailable",
3332
+ description: "GUARD CASE for the fallback path. If the admin search-index kill switch is off (or the artifact fails to load), the price lookup must still return real results via the original query-based endpoint — the feature must not go blank just because the faster path is unavailable.",
3333
+ href: "/tools/price-index",
3334
+ needsReview: true,
3335
+ reviewNote: "Needs the kill switch flipped off to observe, which an ordinary run does not do — flagged for a human or admin-tooling pass.",
3336
+ },
3337
+ ],
3338
+ },
3339
+ {
3340
+ pageKey: "compare-overlay-add-item",
3341
+ pageLabel: "Compare overlay — add another item",
3342
+ href: "/products",
3343
+ cases: [
3344
+ {
3345
+ key: "compare-add-another-via-typeahead",
3346
+ label: "The compare overlay's \"Add another\" typeahead adds a real item without leaving the page",
3347
+ description: "BEFORE: the compare overlay only ever showed items selected via listing-page checkboxes before opening it — there was no way to add a further item once the overlay was open. AFTER: open the compare overlay with at least one item selected, find the 'Add another to compare' box, type a product name, and pick a suggestion. The picked item's full column (image, price, specs) appears in the overlay without closing it or returning to the listing grid.",
3348
+ href: "/products",
3349
+ },
3350
+ {
3351
+ key: "compare-add-hidden-at-column-limit",
3352
+ label: "The \"Add another\" box disappears once the compare overlay is full",
3353
+ description: "Add items via checkboxes (or the typeahead) until the overlay holds its maximum number of columns. The 'Add another' box must then be absent entirely — not merely disabled — since a picker that cannot add anything is worse than none.",
3354
+ href: "/products",
3355
+ },
3179
3356
  ],
3180
3357
  },
3181
3358
  {
3182
3359
  /*
3183
- * D12 — the parts database, the composition panel, and the part line in a
3184
- * seller's auto-filled description.
3360
+ * D12 — the Beyblade parts gallery, the composition panel, and the part
3361
+ * line in a seller's auto-filled description.
3362
+ *
3363
+ * 🛑 `pageKey` stays `parts-database` even though the surface is now
3364
+ * called the Beyblade parts gallery. It keys all nine case ids, every
3365
+ * `testerChecklistResponses` row already recorded against them, and
3366
+ * `audit-tester-checklist-hrefs`. A label is a label; a key is an
3367
+ * identifier.
3185
3368
  *
3186
3369
  * 🛑 Four defects this feature has already produced, every one of them a
3187
3370
  * 200 with plausible content: a read against a field the collection does
@@ -3192,7 +3375,7 @@ const rawTesterChecklistItems = [
3192
3375
  * ABSENT panel — indistinguishable from a model we have no data for.
3193
3376
  */
3194
3377
  pageKey: "parts-database",
3195
- pageLabel: "Parts database, composition panel and the part line in a description",
3378
+ pageLabel: "Beyblade parts gallery, composition panel and the part line in a description",
3196
3379
  href: "/parts",
3197
3380
  cases: [
3198
3381
  { key: "index-lists-every-classification", label: "The parts index lists all 20 classifications, labelled as names and not as URL slugs" },
@@ -3217,30 +3400,33 @@ const rawTesterChecklistItems = [
3217
3400
  *
3218
3401
  * 1. The index rendered all 1,185 rows flat. Nothing errored; it just
3219
3402
  * buried the six roots under 752 part leaves.
3220
- * 2. The Parts tab first matched 772 rows — it also caught the 20
3221
- * classification NODES, whose own parentIds pass through a
3222
- * `*-parts` node. A tab mixing "Attack Rings" with the attack rings
3223
- * inside it looks entirely reasonable.
3224
- * 3. Seven franchises have NO stock. Seeded active they would each be
3403
+ * 2. Seven franchises have NO stock. Seeded active they would each be
3225
3404
  * a card opening an empty shop; omitted entirely, seventeen
3226
3405
  * CharacterHotspot links stay 404. Both failure modes look fine
3227
3406
  * from one page.
3228
- * 4. Every bundle carries a placeholder cover image, so a collage
3407
+ * 3. Every bundle carries a placeholder cover image, so a collage
3229
3408
  * checked AFTER the cover would never render — and a card showing
3230
3409
  * one grey tile is not visibly broken.
3410
+ *
3411
+ * 🛑 There was a fifth tab, Parts, removed 2026-10-20 — parts belong to
3412
+ * `/parts` and the spares listing type, not to a flat tab over 752
3413
+ * leaves. Its own case went with it. The product-detail Parts
3414
+ * COMPOSITION tab is a different surface and still has cases, on the
3415
+ * `parts-database` page above.
3231
3416
  */
3232
3417
  pageKey: "category-tabs-franchise-bundles",
3233
3418
  pageLabel: "Categories index tabs, the franchise unit and the bundle collage",
3234
3419
  href: "/categories",
3235
3420
  cases: [
3236
3421
  { key: "index-shows-roots-only", label: "The Categories tab lists the six roots and NO deeper category" },
3237
- { key: "five-tabs-no-all", label: "There are exactly five tabs and none of them is 'All'" },
3238
- { key: "parts-tab-is-leaves-only", label: "The Parts tab lists part leaves and NOT the 20 classification nodes" },
3422
+ { key: "four-tabs-no-all-no-parts", label: "There are exactly four tabs — no 'All', and no 'Parts'" },
3239
3423
  { key: "tabs-do-not-overlap", label: "No single row appears on two tabs" },
3240
3424
  { key: "franchise-index-hides-unstocked", label: "The franchise index shows only stocked franchises, not all eight" },
3241
3425
  { key: "unstocked-franchise-page-resolves", label: "An unstocked franchise page loads and says so, rather than 404ing or showing an empty grid" },
3242
3426
  { key: "franchise-detail-lists-its-products", label: "The Beyblade franchise page lists its listings, with a nonsense slug as the control" },
3243
3427
  { key: "bundle-card-shows-a-collage", label: "A bundle card shows a collage of its members, not its placeholder cover" },
3428
+ { key: "tabs-collapse-on-narrow-viewport", label: "On a phone-width viewport the four tabs collapse into a searchable dropdown, not a row running off-screen" },
3429
+ { key: "tabs-hover-changes-ink", label: "On desktop, hovering an inactive tab visibly changes its text colour" },
3244
3430
  ],
3245
3431
  },
3246
3432
  {
@@ -3450,7 +3636,7 @@ const rawTesterChecklistItems = [
3450
3636
  {
3451
3637
  key: "search-typeahead-differs",
3452
3638
  label: "Typing \"dranzer\" and then \"dragoon\" in the header search gives DIFFERENT suggestions",
3453
- description: "WAS: the same 5 newest rows appeared for every keystroke, because no search term reached the query at all. If both terms give an identical list, the term is still being ignored.",
3639
+ description: "WAS: the same 5 newest rows appeared for every keystroke, because no search term reached the query at all. If both terms give an identical list, the term is still being ignored. UPDATED 2026-10-19: suggestions now come from the local index and span all 21 content types rather than the four the old endpoint could produce — so expect categories, parts and FAQs among the rows, not only products. ALSO CHECK, in DevTools -> Network: after the first index download there must be NO request per keystroke.",
3454
3640
  },
3455
3641
  {
3456
3642
  key: "search-typeahead-no-drafts",
@@ -3565,6 +3751,138 @@ const rawTesterChecklistItems = [
3565
3751
  description: "Write this one carefully. Every other case here passes on freshly seeded data while real production documents stay invisible, because search tokens are only written when a record is saved. Pick a listing that has NOT been edited recently and confirm it is findable; if it is not, the backfill did not cover it.",
3566
3752
  href: "/products",
3567
3753
  },
3754
+ /* ── The site-wide index (2026-10-19) ─────────────────────────────
3755
+ * Every case below is written as an explicit BEFORE -> AFTER,
3756
+ * because almost all of them describe behaviour that previously
3757
+ * returned HTTP 200 with plausible rows. A case reading "check
3758
+ * search works" passes AGAINST the bug it is meant to catch.
3759
+ */
3760
+ {
3761
+ key: "search-finds-every-type",
3762
+ label: "Searching \"dranzer\" from the header lands on /search and returns hits in SEVERAL sections",
3763
+ description: "BEFORE: the header sent you to /products?q=dranzer — one browse page — so you never learned there is also a category, a part, a blog post and an FAQ about it. AFTER: you land on /search and see grouped results across several sections. CONTROL, and it is the only assertion here that cannot pass by accident: search \"zzzznope\" and the page must say nothing matched, with ZERO result rows.",
3764
+ href: "/search",
3765
+ },
3766
+ {
3767
+ key: "search-finds-a-part",
3768
+ label: "Searching \"Rubber Flat\" and then \"RF\" both find the part",
3769
+ description: "BEFORE: zero results for either — all 752 parts had no search tokens at all, so they were reachable only by drilling /parts -> classification -> code. AFTER: both queries return the part, and the row shows its classification underneath the name.",
3770
+ href: "/search",
3771
+ },
3772
+ {
3773
+ key: "search-ambiguous-part-code",
3774
+ label: "Searching \"105\" returns SEVERAL parts, each showing a DIFFERENT classification",
3775
+ description: "BEFORE: there was no way to search parts at all. AFTER: \"105\" is deliberately ambiguous — it is a Metal Fight spin-track height, while a bare number in Burst is a disc — so the right answer is several rows, each naming its own classification and each linking to a different two-segment /parts/<classification>/<code> page. ONE row is the failure: it means something picked a winner.",
3776
+ href: "/search",
3777
+ },
3778
+ {
3779
+ key: "search-midword",
3780
+ label: "Searching \"ariza\" finds Charizard",
3781
+ description: "BEFORE: impossible. Firestore token search is word-PREFIX only, so a mid-word fragment matched nothing. AFTER: it matches, because the browser scans the whole index. CONTROL: \"zzzz\" must still return nothing — a matcher that returns everything for any fragment would also pass the first half.",
3782
+ href: "/search",
3783
+ },
3784
+ {
3785
+ key: "search-typo",
3786
+ label: "Searching \"dragon g\" finds Dragoon G, and \"Pegasis\" finds Storm Pegasus",
3787
+ description: "Misspellings are expanded at index time from a crawl of a counterfeit seller's real titles, on the basis that an evasion spelling is the same string a buyer mistypes. BEFORE: neither matched. AFTER: both do.",
3788
+ href: "/search",
3789
+ },
3790
+ {
3791
+ key: "search-normalised-away",
3792
+ label: "Searching \"!!!\" shows the \"no searchable words\" state — NOT every listing",
3793
+ description: "BEFORE (the inverse bug, on /api/products): a punctuation-only query produced no filter clause, so it returned the ENTIRE published catalogue as search results. AFTER: the page says the query had no searchable words in it. Note this is a different message from \"nothing matched\" and both must be distinguishable.",
3794
+ href: "/search",
3795
+ },
3796
+ {
3797
+ key: "search-zero-reads",
3798
+ label: "After the first search, typing produces NO further network requests",
3799
+ description: "Open DevTools -> Network, signed out. Type a query: you should see ONE fetch of the index artifact (a .json from firebasestorage, a few hundred KB) and nothing per keystroke thereafter. BEFORE: every keystroke hit /api/search/suggestions. A request per character is the failure even though the results look identical.",
3800
+ href: "/search",
3801
+ },
3802
+ {
3803
+ key: "search-no-prices",
3804
+ label: "No rupee amount appears anywhere in the header suggestions or /search results, signed out",
3805
+ description: "BEFORE: the header dropdown printed the product price as the suggestion subtitle, from a route cached PUBLIC for 60s — a live guest-price leak on every page. AFTER: suggestions show the brand and category instead, and no ₹ appears anywhere for a signed-out visitor.",
3806
+ href: "/search",
3807
+ },
3808
+ {
3809
+ key: "search-no-sandbox",
3810
+ label: "No tester-sandbox row appears in search results for a signed-out visitor",
3811
+ description: "The index is ONE shared file every browser downloads, with no per-viewer filter anywhere after it is built — so a sandbox row in it is published to the world for that version's lifetime. Search \"sandbox\" and \"tester\" signed out; nothing seeded as test data may appear.",
3812
+ href: "/search",
3813
+ },
3814
+ {
3815
+ key: "search-freshness",
3816
+ label: "A newly published listing becomes findable within ~15 minutes, and unpublishing removes it",
3817
+ description: "Publish a listing, note the time, and search its exact title. It should appear within about 15 minutes. Then unpublish it and search again — it must disappear within the same window. BEFORE: there was no index to go stale. A row that never appears, or never leaves, both mean the rebuild stopped.",
3818
+ href: "/search",
3819
+ },
3820
+ {
3821
+ key: "search-facet-counts",
3822
+ label: "Facet chips on /search show counts that match the rows actually displayed, and ticking one narrows BOTH",
3823
+ description: "Search a broad term. The rail shows chips like Type/Brand/Condition with a number on each. Tick one: the results narrow AND the other chips' numbers drop to match. A chip whose count does not change the result set, or a count that disagrees with the rows you can see, is the failure.",
3824
+ href: "/search",
3825
+ },
3826
+ {
3827
+ key: "search-guest-no-private",
3828
+ label: "Signed out, /search shows no account sections and makes ZERO authenticated requests",
3829
+ description: "SECURITY. In a private window with no session, search a broad term and watch DevTools -> Network. There must be no request to /api/search/scoped at all — not a 401, not an empty 200. An authenticated request firing for a guest means a session the page did not expect.",
3830
+ href: "/search",
3831
+ },
3832
+ {
3833
+ key: "search-buyer-isolation",
3834
+ label: "Buyer A cannot find Buyer B's order — by the UI AND by calling the API directly",
3835
+ description: "SECURITY, and the API half is the half that matters. Signed in as one buyer, search a term from another buyer's order. Then call /api/search/scoped?q=<term> directly in the same session and read the JSON. Neither may contain the other buyer's row. A UI that hides it while the API returns it is the failure this case exists for.",
3836
+ href: "/search",
3837
+ },
3838
+ {
3839
+ key: "search-seller-own-store",
3840
+ label: "A seller finds their own DRAFT listing; another seller's draft is absent",
3841
+ description: "Signed in as a seller, search a draft listing's title. It appears under \"Your listings (incl. drafts)\" — that section exists precisely because published listings are deduped away into the public results, so what is left is your drafts. Then confirm a different store's draft title returns nothing.",
3842
+ href: "/search",
3843
+ },
3844
+ {
3845
+ key: "search-listing-boxes-still-filter",
3846
+ label: "The per-collection search boxes still filter — one public, one seller, one admin",
3847
+ description: "REGRESSION GUARD. The box on /stores, the one on /store/orders and the one on /admin/users are a DIFFERENT system from site-wide search and must be untouched. For each: a real term narrows the list, a nonsense term returns zero, pagination still works, and the filter-drawer badge still counts correctly.",
3848
+ href: "/stores",
3849
+ },
3850
+ {
3851
+ key: "search-support-ticket-two-words",
3852
+ label: "A TWO-WORD support-ticket search narrows to tickets matching BOTH words",
3853
+ description: "BEFORE: the second word was discarded entirely, so the search returned everything matching the longer word and looked like it was working. AFTER: search two words you know appear in one ticket's subject but not together in others — only the matching ticket should remain.",
3854
+ href: "/admin/support-tickets",
3855
+ },
3856
+ {
3857
+ key: "search-parts-page-has-a-box",
3858
+ label: "/parts has a search box that finds a part by name or by code",
3859
+ description: "BEFORE: /parts was browse-only — 752 parts reachable only by clicking a classification then a code. AFTER: there is a search input above the classification grid. Type \"RF\" and the part appears with its classification; type \"zzzznope\" and it says nothing matches. The classification grid must still be there below it.",
3860
+ href: "/parts",
3861
+ },
3862
+ {
3863
+ key: "search-facet-carries-through",
3864
+ label: "\"See all →\" on a group with a facet ticked lands on the browse page WITH that facet still applied",
3865
+ description: "Search a broad term and tick a facet chip (e.g. a brand). Click the group's own \"See all →\" link. BEFORE: the link always went to the plain, unfiltered browse page, so refining on /search and then following through threw the refinement away. AFTER: the destination URL carries the same filter (e.g. ?f=brand==Takara-Tomy) and the browse page opens already narrowed.",
3866
+ href: "/search",
3867
+ },
3868
+ {
3869
+ key: "search-admin-person-exact",
3870
+ label: "Admin can find a user by typing just their FIRST name — but a prefix of it finds nothing",
3871
+ description: "Signed in as admin, open /admin/users and type a real user's first name only (not their full \"First Last\"). BEFORE: the search was an exact match on the WHOLE displayName string, so typing only the first name found nobody. AFTER: it is a whole-WORD match and finds them. Then type only the first 3-4 letters of that name (a prefix, not the whole word) — this must find NOTHING. That second half is the design, not a bug: the index is whole-word only so it cannot be unpicked by counting partial-match frequencies.",
3872
+ href: "/admin/users",
3873
+ },
3874
+ {
3875
+ key: "search-no-plaintext-pii",
3876
+ label: "The stored search-index values are HMAC digests, never a readable name",
3877
+ description: "For an admin or engineer with Firestore console access: open a `users` document and find `nameTokensIndex`. Every entry must be an opaque hex digest (prefixed `hmac:`), never the person's actual name or email in any form. Also confirm no such field exists on, or is reachable from, the public search artifact (the .json file fetched by a signed-out visitor) — a deterministic per-person identifier published to every browser is a disclosure even though it cannot be reversed.",
3878
+ href: "/admin/users",
3879
+ },
3880
+ {
3881
+ key: "faq-punctuation-search",
3882
+ label: "Searching \"!!!\" in the FAQ box shows the normalised-away state, not every FAQ",
3883
+ description: "GUARD CASE — this behaviour is already correct and this case exists to pin it so it cannot regress while the site-wide index work is in motion elsewhere. Open /faqs and search \"!!!\". It must say the search had no searchable words, and show zero FAQs — not the entire FAQ list.",
3884
+ href: "/faqs",
3885
+ },
3568
3886
  ],
3569
3887
  },
3570
3888
  {
@@ -6756,6 +7074,24 @@ const rawTesterChecklistItems = [
6756
7074
  pageLabel: "Header search",
6757
7075
  href: "/",
6758
7076
  cases: [
7077
+ {
7078
+ key: "header-search-submit-lands-on-search",
7079
+ label: "Submitting the header search lands on /search with GROUPED results — not on one browse page",
7080
+ description: "BEFORE: submitting sent you to the browse route for whichever type was selected in the dropdown — /products?q=dranzer — so a cross-resource search was unreachable and the /search page it should have used had no users at all. AFTER: you land on /search?q=dranzer and see results grouped by type, each group with its own \"See all … →\" link pointing at that browse page. The per-type destinations are not lost; they moved to where they belong.",
7081
+ href: "/",
7082
+ },
7083
+ {
7084
+ key: "header-search-type-is-a-filter",
7085
+ label: "The type dropdown NARROWS the results — it no longer decides where you land",
7086
+ description: "BEFORE: choosing \"Stores\" meant submitting took you to /stores. AFTER: choosing \"Stores\" narrows what you see, and the Stores group's own \"See all stores →\" link is what takes you to /stores. Also confirm an EMPTY query does nothing at all on submit — landing on a bare browse page you did not ask for is worse than staying put.",
7087
+ href: "/",
7088
+ },
7089
+ {
7090
+ key: "header-search-no-price-for-guest",
7091
+ label: "Signed out, no suggestion row shows a price",
7092
+ description: "BEFORE: the suggestion subtitle was the product's price, served from a route cached PUBLIC for 60 seconds — so every signed-out visitor saw prices in the header on every page. AFTER: the subtitle is the brand and category. Any ₹ in the dropdown while signed out is a FAIL.",
7093
+ href: "/",
7094
+ },
6759
7095
  {
6760
7096
  key: "search-finds-maintenance-toggle",
6761
7097
  label: "Searching 'maintenance' reaches the maintenance TOGGLE, not just Site Settings",
@@ -6880,6 +7216,36 @@ const rawTesterChecklistItems = [
6880
7216
  { key: "storage-meter-honest", label: "The storage meter reports object COUNT beside bytes, and says when it truncated" },
6881
7217
  ],
6882
7218
  },
7219
+ {
7220
+ /*
7221
+ * Part Two D — promoting a traced, accepted, promoted-to-full-res
7222
+ * sheet into real marketplace listings. Distinct from the tracer's
7223
+ * own "promote" (run -> sheet, full-res pixels); this is sheet ->
7224
+ * marketplace (real products). Needs a real traced sheet to reach, so
7225
+ * these cases follow the tracer's own happy path rather than
7226
+ * standing alone.
7227
+ */
7228
+ pageKey: "sticker-promote-to-listings",
7229
+ pageLabel: "Promoting a sheet to real listings",
7230
+ href: "/tools/sticker-tracer",
7231
+ cases: [
7232
+ {
7233
+ key: "promote-creates-one-group",
7234
+ label: "Promoting a sheet creates one parent listing plus one child per UNIQUE part, all in one group",
7235
+ description: "On a sheet with at least one part marked a duplicate of another, promote it to listings. The group must contain the parent plus exactly the UNIQUE parts — a duplicate part must NOT get its own listing — and the picker on the parent's product page must show every member, not just one (this is exactly the `linkChildToGroup` stale-append bug: linking N children in a loop used to leave only one).",
7236
+ },
7237
+ {
7238
+ key: "promoted-part-image-watermarked",
7239
+ label: "A promoted part's marketplace image is watermarked; the admin's original download is NOT",
7240
+ description: "Open the promoted CHILD listing's public page and note its image carries a visible watermark. Then, from the tracer's own UI, download that same part's original — it must be clean, with no watermark. Same pixels, same object, two different responses: that is the point.",
7241
+ },
7242
+ {
7243
+ key: "promote-refuses-before-full-res-render",
7244
+ label: "Promoting a sheet that has not been accepted-and-rendered is refused, naming the fix",
7245
+ description: "Attempt to promote a sheet whose active run has not had \"Accept & render\" run on it (or try on a fresh trace with no promoted run). The action must refuse with a message naming the required step (accept/render the run first) — not create partial listings.",
7246
+ },
7247
+ ],
7248
+ },
6883
7249
  ], { adminOnly: true }),
6884
7250
  ];
6885
7251
  const defaultPhases = assignDefaultPhases(rawTesterChecklistItems);