@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
@@ -1,14 +1,15 @@
1
1
  /*
2
2
  * WHY: Authored six-part procedures for content-discovery/authenticity-checker.
3
- * WHAT: 13 cases, keyed by full checklist id.
3
+ * WHAT: 22 cases, keyed by full checklist id.
4
4
  *
5
5
  * 🛑 EVERY CASE IS WRITTEN AS BEFORE → AFTER, because this feature's whole
6
6
  * failure mode is a confident, plausible, wrong answer returned with HTTP 200. A
7
7
  * case reading "check the authenticity tool works" passes against all of these:
8
8
  *
9
- * 1. A two-state sign-in gate. `SessionProvider` mounts `initialUser={null}`,
10
- * so `loading` starts true for SIGNED-IN users too — the page flashed "sign
11
- * in to run a lookup" at an authenticated collector on every hard load.
9
+ * 1. A sign-in wall on the one surface whose whole purpose is being consulted
10
+ * while standing in front of the item — and before that, a two-state gate
11
+ * that flashed "sign in to run a lookup" at collectors who WERE signed in,
12
+ * because `SessionProvider` mounts `initialUser={null}`.
12
13
  * 2. A verdict with no hedge. The engine can match a midfake batch code; the
13
14
  * wrong version of that screen reads as an accusation against the seller.
14
15
  * 3. One date printed as fact. Two decodings of a Metal Fight box code
@@ -20,8 +21,12 @@
20
21
  * line as "Pegasis" while Hasbro prints "Pegasus", and an earlier draft of
21
22
  * the checker flagged the TT spelling as trademark evasion.
22
23
  * 7. Calling Funskool a fake. It is a licensed Indian importer.
23
- * 8. A sign-in wall with no public content, which is thin content to a crawler
24
- * and useless to someone who arrived from search.
24
+ * 8. Picking a side on a code two compilations flatly disagree about, where
25
+ * either answer costs a real person money.
26
+ * 9. Reporting a code its own source calls authentic as a reproduction,
27
+ * because it appears in a list whose subject is a TYPEFACE.
28
+ * 10. Grading a Burst bey against the Metal Fight checklist in silence,
29
+ * because the generation selector arrives pre-set and nobody touched it.
25
30
  *
26
31
  * So each case names what the wrong answer LOOKED like, and several assert the
27
32
  * presence of a hedge rather than the presence of a result.
@@ -39,13 +44,16 @@ export const authored = {
39
44
  startPage: "/tools/authenticity",
40
45
  steps: [
41
46
  "Open /tools/authenticity signed out.",
42
- "Confirm there is exactly ONE sign-in box, and it sits where the lookup form would be.",
47
+ "Confirm the LOOKUP FORM itself is there and usable — 'Which bey is it?', 'Code on the box', a collapsed 'More details' holding the name-as-printed, brand, generation and how-it-was-sold fields, and a 'Check it' button.",
48
+ "Confirm there is no sign-in card and no 'Sign in to run a lookup' text anywhere on the page.",
49
+ "Enter A0813 as the code and submit, still signed out.",
50
+ "Confirm a real assessment comes back rather than an error or a prompt to sign in.",
43
51
  "Scroll past it and confirm the generation table renders — six rows, each naming years, code prefixes and licensed marques.",
44
- "Confirm the 'Checks that apply whatever the generation' list renders with real text in each row.",
45
- "Answer no if the page is only a sign-in prompt, or if the reference content is missing.",
52
+ "Answer no if any part of the lookup asks for an account.",
46
53
  ],
47
- expectedBehaviour: "BEFORE: gating the page would have meant a signed-out visitor — and a crawler — getting nothing but 'please sign in', which is thin content and throws away the highest-intent query this site can answer. AFTER: only the LOOKUP is gated. The generation table, the box-code explanation, the universal checks and the licensed-marque list are server-rendered and public, identical for crawlers and humans.",
48
- expectedUiState: "One sign-in card reading 'Sign in to run a lookup', followed by the heading 'Which generation is it, and what does the box code mean' and six generation rows, then the universal checks, then 'Who is actually licensed'.",
54
+ inputs: { code: "A0813" },
55
+ expectedBehaviour: "BEFORE: the lookup required an account, so a buyer standing in a shop with the box in their hand was asked to register first — on the one surface whose whole purpose is being consulted at that moment, and the person most in need of it is the one least likely to have an account. AFTER: the entire page is public. What makes that safe is the shape of the output, not a gate: there is no 'counterfeit' verdict, the harshest outcome is 'likely reproduction', and every claim is about a production batch or a named brand rather than about a seller.",
56
+ expectedUiState: "The form, then (after submitting) a verdict block, then the heading 'Which generation is it, and what does the box code mean' with six generation rows, then the universal checks, then 'Who is actually licensed'. No sign-in card at any point.",
49
57
  expectedData: { generationRows: 6 },
50
58
  endResult: "Nothing is changed; this case only reads.",
51
59
  },
@@ -56,18 +64,18 @@ export const authored = {
56
64
  "Sign in as a buyer.",
57
65
  "Hard-reload /tools/authenticity (Ctrl+F5, not a soft navigation).",
58
66
  "Watch the lookup area through the first second of the load.",
59
- "Answer no if the words 'Sign in to run a lookup' appear at any point.",
67
+ "Answer no if the words 'Sign in to run a lookup' or 'Checking your session…' appear at any point, or if the form is not usable immediately.",
60
68
  ],
61
- expectedBehaviour: "BEFORE: a two-state gate (`!user` → prompt) showed the sign-in card to signed-in users on every hard load, because the session provider mounts with a null user and resolves asynchronously. AFTER: three states — while `loading` is true the area shows 'Checking your session…', and the prompt is reachable only once the session has resolved to no user.",
62
- expectedUiState: "Either 'Checking your session…' briefly, then the form. Never the sign-in card.",
69
+ expectedBehaviour: "BEFORE: a two-state gate (`!user` → prompt) showed the sign-in card to signed-in users on every hard load, because the session provider mounts with a null user and resolves asynchronously; a three-state gate with a 'Checking your session…' placeholder fixed the flash. AFTER: the component reads no session at all, so neither string exists and there is nothing to resolve — the form is interactive on first paint.",
70
+ expectedUiState: "The form, immediately. No session placeholder, no sign-in card.",
63
71
  endResult: "The form is present after reload. Nothing persists.",
64
72
  },
65
73
  "checklist-content-discovery-authenticity-checker-midfake-code-reads-as-batch-not-accusation": {
66
74
  roles: ["buyer"],
67
75
  startPage: "/tools/authenticity",
68
76
  steps: [
69
- "Sign in and open /tools/authenticity.",
70
- "Enter A0813 in 'Code on the box' and leave everything else empty.",
77
+ "Open /tools/authenticity (no account needed).",
78
+ "Enter A0813 in 'Code on the box' and leave the other fields as they are.",
71
79
  "Submit.",
72
80
  "Read the verdict block and the caveat directly beneath it.",
73
81
  "Answer no if the screen anywhere calls the item counterfeit, or accuses the seller, or omits the caveat.",
@@ -115,12 +123,12 @@ export const authored = {
115
123
  "Enter 'Storm Pegasus' as the model name and BB-105 as the code. Submit.",
116
124
  "Confirm the verdict is 'Discrepancy found' and a signal explains that Storm Pegasus is BB-28 while BB-105 is a different release.",
117
125
  "Confirm the signal tells you a Random Booster or multi-pack is an innocent explanation.",
118
- "Now tick 'This came in a Random Booster or a multi-pack' and submit again.",
126
+ "Now set 'How it was sold' to 'In a Random Booster or a multi-pack' and submit again.",
119
127
  "Confirm the verdict is no longer a discrepancy and a signal says a mismatch is EXPECTED for a Random Booster.",
120
128
  "Answer no if the second submission still reports a discrepancy.",
121
129
  ],
122
130
  inputs: { name: "Storm Pegasus", code: "BB-105", randomBooster: true },
123
- expectedBehaviour: "BEFORE: flagging every name/code mismatch would flag a large class of genuine product, because a Random Booster box carries the BOOSTER's code and not the code of the bey inside — the reference table records several, e.g. Pisces DF145BS under F2810 marked 'Random Booster TT'. AFTER: the mismatch is reported as a question with the Random Booster explanation offered, and ticking the box re-reads it as expected rather than suspicious.",
131
+ expectedBehaviour: "BEFORE: flagging every name/code mismatch would flag a large class of genuine product, because a Random Booster box carries the BOOSTER's code and not the code of the bey inside — the reference table records several, e.g. Pisces DF145BS under F2810 marked 'Random Booster TT'. AFTER: the mismatch is reported as a question with the Random Booster explanation offered, and answering 'How it was sold' re-reads it as expected rather than suspicious. 🛑 That control is a THREE-option dropdown, not the checkbox it used to be: a checkbox could only express 'yes' and silence, and silence was read as a firm 'no' from a buyer who may simply not have the box in front of them.",
124
132
  expectedUiState: "First submission: 'Discrepancy found' plus a row naming both codes. Second: not a discrepancy, plus a row reading 'Code does not match the model — but you said this is a Random Booster'.",
125
133
  expectedData: { firstVerdict: "discrepancy-found", secondVerdict: "inconclusive" },
126
134
  endResult: "Nothing is written either time.",
@@ -177,15 +185,15 @@ export const authored = {
177
185
  roles: ["buyer"],
178
186
  startPage: "/tools/authenticity",
179
187
  steps: [
180
- "Enter L9999 as the code and submit.",
181
- "If asked which generation, choose Metal Fight.",
188
+ "Enter L9999 as the code and submit (Generation is already on Metal Fight).",
182
189
  "Read the signal about the code.",
183
190
  "Confirm it says the code is NOT RECORDED and explicitly that this does not mean fake.",
191
+ "Confirm it also states the size of the tables it searched, so 'not recorded' is reported against a known coverage rather than as a bare miss.",
184
192
  "Answer no if an unrecorded code is presented as evidence of a fake.",
185
193
  ],
186
194
  inputs: { code: "L9999", generation: "mfb" },
187
- expectedBehaviour: "BEFORE: an absent lookup result reads as a negative result. The reference table is 90 distinct codes compiled by collectors from boxes they personally held, so it was never complete and new genuine codes surface regularly. AFTER: an unmatched code produces a neutral signal saying 'not recorded', with that reasoning stated.",
188
- expectedUiState: "A neutral signal row saying L9999 is not in either table and that an absent code means 'not recorded', not 'fake'. The verdict is 'Inconclusive', never 'Likely reproduction'.",
195
+ expectedBehaviour: "BEFORE: an absent lookup result reads as a negative result. The tables are collectors' compilations from boxes they personally held — thorough at 495 original and 18 midfake codes across 45 models and 6 regions, and still not complete; new genuine codes surface regularly and an unphotographed regional batch is simply missing. AFTER: an unmatched code produces a neutral signal saying 'not recorded', with that reasoning and the coverage stated.",
196
+ expectedUiState: "A neutral signal row saying L9999 is not in any of the tables, naming how many codes were searched, and saying an absent code means 'not recorded', not 'fake'. The verdict is 'Inconclusive', never 'Likely reproduction'.",
189
197
  expectedData: { verdict: "inconclusive" },
190
198
  endResult: "Nothing is changed.",
191
199
  },
@@ -193,15 +201,16 @@ export const authored = {
193
201
  roles: ["buyer"],
194
202
  startPage: "/tools/authenticity",
195
203
  steps: [
196
- "Submit the form with every field empty.",
197
- "Confirm the tool asks for something rather than returning a verdict.",
198
- "Now enter a name that matches nothing — 'zzzznope' — and submit.",
204
+ "Submit the form with every text field empty, leaving Generation on its Metal Fight default.",
205
+ "Confirm the tool asks you to say what you are looking at rather than returning a verdict about an item it knows nothing about.",
206
+ "Confirm it still shows the Metal Fight checklist, since that much IS known.",
207
+ "Now set Generation to 'Not sure / something else', enter a name that matches nothing — 'zzzznope' — and submit.",
199
208
  "Confirm a question appears asking which generation it is, with six options each naming years and example code prefixes.",
200
- "Answer no if the tool returns any verdict at all without being able to establish a generation.",
209
+ "Answer no if an empty submission returns a verdict such as 'Nothing contradicts it', or if the generation question never appears when no generation is set.",
201
210
  ],
202
211
  inputs: { name: "zzzznope" },
203
- expectedBehaviour: "BEFORE: guessing a generation hands the user a confidently wrong checklist — tri-wing screws mean something on Burst and nothing on a 2001 plastic bey, and the box date-code table only covers the Metal series. AFTER: with no generation established the verdict is 'Need a bit more' and a question is asked, with the reason for asking stated.",
204
- expectedUiState: "A 'Need a bit more' badge and a question headed 'Which generation is it?' with six buttons, each labelled with the generation, its years and an example code shape.",
212
+ expectedBehaviour: "BEFORE: guessing a generation hands the user a confidently wrong checklist — tri-wing screws mean something on Burst and nothing on a 2001 plastic bey, and the box date-code table only covers the Metal series. 🛑 And once the selector gained a Metal Fight default, every empty submission carried a generation, which was briefly enough to escape the 'nothing to go on' branch — so pressing Check with nothing typed answered 'Nothing contradicts it, and nothing confirms it either': a verdict, about no item, phrased as reassurance. AFTER: a generation alone is still nothing to go on, and the branch uses it for the checklist instead of treating it as an answer.",
213
+ expectedUiState: "Empty submission → a 'Need a bit more' badge, 'Tell me what you are looking at.', and the Metal Fight checks. With Generation cleared and an unmatched name → a question headed 'Which generation is it?' with six buttons.",
205
214
  expectedData: { verdict: "needs-input" },
206
215
  endResult: "Nothing is changed.",
207
216
  },
@@ -213,31 +222,369 @@ export const authored = {
213
222
  // A tester step is prose a human types into a devtools console: it cannot
214
223
  // import a constant, and calling the real URL signed out IS the case.
215
224
  // audit-hardcoded-api-routes-ok: literal URL inside a manual test procedure
216
- "Run: await fetch('/api/tools/authenticity', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code: 'A0813' }) }).then(r => r.status)",
217
- "Confirm the status is 401 (or 403) and not 200.",
218
- "Confirm the response body contains no assessment — no verdict, no signals.",
219
- "Answer no if a signed-out call returns an assessment.",
225
+ "Run: await fetch('/api/tools/authenticity', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code: 'A0813' }) }).then(r => r.json())",
226
+ "Confirm the response is a real assessment — it has a verdict and signals — and not a 401.",
227
+ // audit-hardcoded-api-routes-ok: literal URL inside a manual test procedure
228
+ "Now run the same fetch with an extra key the schema does not declare, e.g. body: JSON.stringify({ code: 'A0813', listingId: 'x' }), and read r.status.",
229
+ "Confirm THAT one is refused with a 400 rather than quietly ignoring the unknown key.",
230
+ "Answer no if the plain call is refused, or if the unknown-key call succeeds.",
220
231
  ],
221
232
  inputs: { code: "A0813" },
222
- expectedBehaviour: "BEFORE: gating only the UI leaves the endpoint open, and the endpoint is where the output actually comes from — 'this matches a batch recorded as a midfake' read off an anonymous API is a quotable claim with no account behind it. AFTER: `auth: true` on the route refuses the call before the engine runs.",
223
- expectedUiState: "The console shows 401 or 403. No verdict text appears anywhere in the response.",
224
- expectedData: { status: 401 },
225
- endResult: "Nothing is written; no assessment is produced.",
233
+ expectedBehaviour: "BEFORE: the route required `auth: true`, so the endpoint refused an anonymous caller. The reasoning was that 'this matches a batch recorded as a midfake' is a quotable claim that should have an account behind it; in practice it put a registration wall in front of someone standing in a shop. AFTER: the route is public and the protection is in the output's shape instead — no 'counterfeit' rung, nothing said about a seller. The `.strict()` schema is still enforced, so an undeclared key is a 400 and never a confident assessment of a different, emptier question.",
234
+ expectedUiState: "The console shows an object with a verdict and a signals array for the first call, and status 400 for the second.",
235
+ expectedData: { status: 200 },
236
+ endResult: "Nothing is written; the engine does zero I/O.",
226
237
  },
227
- "checklist-content-discovery-authenticity-checker-json-ld-declares-the-gate": {
238
+ "checklist-content-discovery-authenticity-checker-json-ld-declares-no-false-gate": {
228
239
  roles: ["guest"],
229
240
  startPage: "/tools/authenticity",
230
241
  steps: [
231
242
  "Signed out, view source on /tools/authenticity (Ctrl+U, not devtools' rendered DOM).",
232
243
  "Find the application/ld+json block with \"@type\": \"WebApplication\".",
233
- "Confirm it carries isAccessibleForFree true at the top level and a hasPart entry with isAccessibleForFree FALSE and a cssSelector.",
234
- "Copy that cssSelector value and run document.querySelectorAll(<selector>).length in the console.",
235
- "Confirm the count is 1 — the selector matches a real element on the page.",
236
- "Answer no if the selector matches nothing, or if the gate is undeclared.",
237
- ],
238
- expectedBehaviour: "BEFORE: a page with gated content and no declaration is a structured-data mismatch — and the alternative mistake, serving the reference content only to crawler user-agents, is cloaking. AFTER: the page declares the gate exactly as the guest-price gate does: the free reference content is marked free, and the lookup region is carved out by selector as not free. 🛑 Root Cause #93 is the record of the failure this case exists to catch — a class name and a selector living as two literals in two files, where a rename points the crawler at a selector matching nothing and looks identical to making no declaration at all.",
239
- expectedUiState: "A WebApplication JSON-LD block naming the tool, plus a breadcrumb block. The hasPart cssSelector resolves to exactly one element in the live DOM.",
240
- expectedData: { gatedSelectorMatches: 1 },
244
+ "Confirm it carries isAccessibleForFree true.",
245
+ "Confirm there is NO hasPart entry, and no isAccessibleForFree false anywhere in the block.",
246
+ "Search the page source for 'authenticity-gated-tool' and confirm it does not appear.",
247
+ "Answer no if the markup still declares a paywalled region.",
248
+ ],
249
+ expectedBehaviour: "BEFORE: the lookup needed an account, and the page declared that honestly — `isAccessibleForFree: true` overall plus a `hasPart` node carving out the lookup by cssSelector as not free (the same pattern the guest-price gate uses). AFTER: the gate is gone, so the declaration had to go with it. A paywall node a crawler can see is false is worse than no node at all, and the class it named was deleted rather than left in the DOM as a selector nothing references — 🛑 Root Cause #93 is exactly that pair drifting apart.",
250
+ expectedUiState: "A WebApplication JSON-LD block naming the tool with isAccessibleForFree true and no hasPart, plus a breadcrumb block.",
251
+ expectedData: { hasPartNodes: 0 },
241
252
  endResult: "Nothing is changed; this case only reads.",
242
253
  },
254
+ "checklist-content-discovery-authenticity-checker-defaults-to-metal-fight": {
255
+ roles: ["guest"],
256
+ startPage: "/tools/authenticity",
257
+ steps: [
258
+ "Open /tools/authenticity.",
259
+ "Read the Generation control WITHOUT touching it — confirm it already shows Metal Fight.",
260
+ "Enter L9999 as the code and submit without changing anything.",
261
+ "Confirm the result is produced immediately and is NOT a 'Which generation is it?' question.",
262
+ "Now enter B-174 as the code, leave Generation on Metal Fight, and submit.",
263
+ "Confirm a signal appears saying Metal Fight is selected but this looks like Burst, and that it tells you to change the selector.",
264
+ "Answer no if the default is absent, or if a Burst code is silently graded against the Metal Fight checklist with no mention of the mismatch.",
265
+ ],
266
+ inputs: { code: "B-174" },
267
+ expectedBehaviour: "BEFORE: the tool opened with no generation and asked for one before it would say anything, which made the commonest question — 'what does this code on my Metal Fight box mean' — cost a selection first. Everything the box-code tables know is Metal-series. AFTER: the selector is pre-set to Metal Fight. 🛑 The risk a pre-set default creates is handled rather than ignored: a supplied generation still wins, because the user can see the box and the engine cannot, but a code or name implying a different generation raises a `generation-mismatch` signal naming both. A default that cannot be questioned is the thing to avoid.",
268
+ expectedUiState: "The Generation control reads 'Metal Fight (Metal Fusion / Masters / Fury) — 2008–2012' on load. With B-174 submitted, a neutral signal headed 'You have Metal Fight … selected, but this looks like Burst'.",
269
+ expectedData: { defaultGeneration: "mfb" },
270
+ endResult: "Nothing is changed.",
271
+ },
272
+ "checklist-content-discovery-authenticity-checker-contested-code-reports-the-disagreement": {
273
+ roles: ["guest"],
274
+ startPage: "/tools/authenticity",
275
+ steps: [
276
+ "Enter I2211 as the code and submit.",
277
+ "Confirm the verdict badge reads 'Discrepancy found' and NOT 'Likely reproduction' and NOT 'Consistent with an original'.",
278
+ "Confirm the headline says the sources contradict each other about this code.",
279
+ "Confirm a signal names BOTH sides — which compilation records it on genuine boxes and which lists it as a midfake.",
280
+ "Answer no if the screen picks a side, or presents either reading as the answer.",
281
+ ],
282
+ inputs: { code: "I2211" },
283
+ expectedBehaviour: "BEFORE: merging three code compilations put four codes (I0511, I2211, I2611, J2712) in two tables at once, and whichever table the lookup scanned first would have won. That is a coin flip with a person's money on it in both directions — the midfake reading accuses whoever is holding a genuine box, the original reading reassures whoever is holding a fake. AFTER: contested codes live in their own table, are checked before the precedence scan, and are reported as the disagreement they are.",
284
+ expectedUiState: "A 'Discrepancy found' badge, the headline 'My sources contradict each other about this exact code.', and a signal row naming the model-by-model list and the 2021 reference post on one side and the midfake list on the other.",
285
+ expectedData: { verdict: "discrepancy-found" },
286
+ endResult: "Nothing is changed.",
287
+ },
288
+ "checklist-content-discovery-authenticity-checker-font-midfake-does-not-accuse": {
289
+ roles: ["guest"],
290
+ startPage: "/tools/authenticity",
291
+ steps: [
292
+ "Enter D0511 as the code and submit.",
293
+ "Confirm the verdict is 'Discrepancy found' — NOT 'Likely reproduction'.",
294
+ "Read the midfake signal and confirm it says the point is the TYPEFACE and that the source considers the code itself authentic.",
295
+ "Now enter H3011 instead and submit.",
296
+ "Confirm THAT one does reach 'Likely reproduction' and names the high-quality Sonokong fakes.",
297
+ "Answer no if D0511 is reported as a reproduction, or if H3011 is not.",
298
+ ],
299
+ inputs: { code: "D0511" },
300
+ expectedBehaviour: "BEFORE: every row in the midfake table drove the harshest verdict the engine has. Three of the Chinese rows carry their own author's words that *the code itself should be authentic* and only the printed font differs, so that verdict would have been the engine asserting something no source did, about someone's property, in the worst available direction. AFTER: a midfake row the source hedged on is still a concern — it lands on 'resolve this before you pay' with the font comparison spelled out — but only an unhedged row reaches 'likely reproduction'.",
301
+ expectedUiState: "D0511 → 'Discrepancy found', with a signal explaining the typeface comparison. H3011 → 'Likely reproduction', with a signal about Korean boxes reproducing the importer panel and a Safety block.",
302
+ expectedData: { d0511Verdict: "discrepancy-found", h3011Verdict: "likely-reproduction" },
303
+ endResult: "Nothing is written either time.",
304
+ },
305
+ "checklist-content-discovery-authenticity-checker-regional-code-reads-as-regional": {
306
+ roles: ["guest"],
307
+ startPage: "/tools/authenticity",
308
+ steps: [
309
+ "Enter C2811 as the code and submit.",
310
+ "Confirm the signal names the regions the code is recorded in, and that Korea (Sonokong) is among them.",
311
+ "Now enter K2510 and submit.",
312
+ "Confirm that one names Middle East (NewBoy), and that its source note says the 2021 post could only call it 'seems legit' while the later list records it as a genuine New Boy release.",
313
+ "Answer no if a regional code comes back with no region, or is presented as unrecorded.",
314
+ ],
315
+ inputs: { code: "C2811" },
316
+ expectedBehaviour: "BEFORE: the table was 90 Japanese-centric codes with no region data, so a buyer holding a Korean Sonokong or Middle-East New Boy box got 'not recorded' — which reads as a warning about a perfectly genuine regional release. AFTER: 495 codes across 45 models and 6 regions, and the signal says which region the code is recorded in. The five New Boy codes the 2021 post could only hedge about are resolved by the later list, carrying that history in their notes rather than being quietly upgraded.",
317
+ expectedUiState: "C2811 → a supporting signal reading 'Recorded on … releases' naming Korea (Sonokong). K2510 → the same shape naming Middle East (NewBoy), plus the 'seems legit' history.",
318
+ endResult: "Nothing is changed.",
319
+ },
320
+ "checklist-content-discovery-authenticity-checker-series-window-names-the-production-run": {
321
+ roles: ["guest"],
322
+ startPage: "/tools/authenticity",
323
+ steps: [
324
+ "Enter C2312 as the code and submit (Generation stays on Metal Fight).",
325
+ "Confirm a signal says the date falls inside a NAMED production window, and names it 'Metal Fury' with a start and end date.",
326
+ "Confirm the signal says this is about the CALENDAR DATE, not about the code itself.",
327
+ "Now enter A0813 instead and submit.",
328
+ "Confirm there is NO series-window signal for A0813 — its derived date (8 January 2013) is after every window the source states ends (Shogun Steel, 23 December 2012).",
329
+ "Answer no if the window signal is missing for C2312, or present for A0813.",
330
+ ],
331
+ inputs: { code: "C2312" },
332
+ expectedBehaviour: "BEFORE: the reference post's own 'Additional Information' section — the only place any source states a MANUFACTURING WINDOW rather than one code's date (Metal Fusion/Masters/Fury, Shogun Steel) — was parsed for nothing; the tool could only ever say what a specific code decoded to. AFTER: every derivable date is checked against those four windows and named when it falls inside one. This is a fact about the calendar, so it fires even for C2312, which matches no code in any table.",
333
+ expectedUiState: "C2312 → a signal reading 'Falls inside the Metal Fury production window', naming '3 April 2011 – 1 April 2012'. A0813 → no such signal at all.",
334
+ endResult: "Nothing is changed.",
335
+ },
336
+ "checklist-content-discovery-authenticity-checker-temporal-context-for-an-unmatched-code": {
337
+ roles: ["guest"],
338
+ startPage: "/tools/authenticity",
339
+ steps: [
340
+ "Enter C2312 as the code and submit.",
341
+ "Confirm the main 'not in any of the tables' signal is still present and still says not-recorded is not fake.",
342
+ "Find a SEPARATE signal headed 'What the tables record from March 2012'.",
343
+ "Confirm it lists several genuine releases from that month BY NAME, and explicitly says 'Midfake batches: none recorded' rather than omitting that half.",
344
+ "Confirm the detail text explicitly says this is NOT a match for the code and your item matching none of the names is NOT evidence of anything.",
345
+ "Answer no if the month-context signal is missing, or if it reads as though it identifies the item.",
346
+ ],
347
+ inputs: { code: "C2312" },
348
+ expectedBehaviour: "BEFORE: a code that matched nothing got exactly one sentence — 'not recorded, not fake' — and nothing else, even though the tables know a great deal about what else was moving through production the same month. AFTER: for a well-formed code that matches no table row, the engine also reports every genuine and midfake product name recorded from the SAME CALENDAR MONTH, explicitly framed as context rather than a match. 🛑 This only fires for an UNMATCHED code — a code that DID match something already has its own, richer product list, and piling month-context on top of that would dilute the one thing that actually matters (the match itself, or for a contested code, the disagreement).",
349
+ expectedUiState: "A signal titled 'What the tables record from March 2012' listing names like Diablo Nemesis X:D, Fang Leone 130W²D, Kreis Cygnus, Phantom Orion and others, ending '… and N more', followed by 'Midfake batches: none recorded.' and the context/not-evidence sentence.",
350
+ endResult: "Nothing is changed.",
351
+ },
352
+ };
353
+ /*
354
+ * ── Guards for the near-miss work (2026-10-19) ──────────────────────────
355
+ *
356
+ * 🛑 All four protect behaviour that is ALREADY CORRECT, and that is the
357
+ * point of them. A "did you mean" affordance is precisely the change that
358
+ * could turn an absent code into an accusation, and this tool's third rule
359
+ * is that a false counterfeit accusation against a real seller is worse
360
+ * than a missed one.
361
+ *
362
+ * Appended as a second export rather than merged into the map above, so the
363
+ * diff shows what the near-miss change added and the original 21 cases stay
364
+ * byte-identical.
365
+ */
366
+ export const authoredNearMiss = {
367
+ "checklist-content-discovery-authenticity-checker-authenticity-unknown-code-is-not-fake": {
368
+ roles: ["guest"],
369
+ startPage: "/tools/authenticity",
370
+ steps: [
371
+ "Open /tools/authenticity.",
372
+ "Leave the model name empty.",
373
+ "Enter Z9999 in the box-code field — well-formed (one letter, four digits) but in no table.",
374
+ "Pick any generation.",
375
+ "Run the check.",
376
+ "Read the verdict chip, the headline and the caveat word for word.",
377
+ "Read every row under 'What I checked' and note each row's direction.",
378
+ ],
379
+ inputs: { code: "Z9999" },
380
+ expectedBehaviour: "An unmatched code is NOT evidence in either direction. New genuine codes appear regularly, and counterfeiters use real codes anyway — this project's own crawl found 516 real B-xx codes on one counterfeit catalogue.",
381
+ expectedUiState: "The verdict is 'Inconclusive'. The code signal reads NEUTRAL, and its text says the code is not recorded rather than that the item is fake. No wording anywhere implies counterfeit, and no verdict more severe than inconclusive appears. Either of those is the failure.",
382
+ endResult: "Read-only; nothing persists.",
383
+ },
384
+ "checklist-content-discovery-authenticity-checker-authenticity-near-miss-suggests": {
385
+ roles: ["guest"],
386
+ startPage: "/tools/authenticity",
387
+ steps: [
388
+ "Open /tools/authenticity.",
389
+ "Find a code the tool recognises — A0813 is a known one.",
390
+ "Enter A0831 instead: the same characters with one pair transposed.",
391
+ "Run the check.",
392
+ "Read the suggestion line and note where on the page it sits.",
393
+ "Read every row under 'What I checked'.",
394
+ "Write down the verdict.",
395
+ "Run the check again with the transposition removed and compare the verdict.",
396
+ ],
397
+ inputs: { nearMiss: "A0831", realCode: "A0813" },
398
+ expectedBehaviour: "A near-miss is INPUT CORRECTION, never evidence. It is ambiguous between a typo and a batch deliberately mimicking a real code, and an ambiguous signal must not become an accusation.",
399
+ expectedUiState: "A neutral line offers 'did you mean A0813?'. It sits OUTSIDE the 'What I checked' list and does NOT appear as a signal row. No evasion or counterfeit warning fires. The verdict is identical to what the same input produced before the suggestion existed.",
400
+ endResult: "Read-only; nothing persists.",
401
+ },
402
+ "checklist-content-discovery-authenticity-checker-authenticity-inconclusive-asks-for-more": {
403
+ roles: ["guest"],
404
+ startPage: "/tools/authenticity",
405
+ steps: [
406
+ "Open /tools/authenticity.",
407
+ "Leave the model name EMPTY.",
408
+ "Enter a plausible box code and pick a generation.",
409
+ "Run the check.",
410
+ "Read the caveat under the verdict word for word.",
411
+ "Now fill in a model name as well and run it again.",
412
+ "Compare the two caveats.",
413
+ ],
414
+ expectedBehaviour: "Name-against-code agreement is one of the engine's strongest signals and cannot be computed with only one of the two. The tool already asks which generation and already asks which model on an ambiguous name; asking for the missing half is the same mechanism one input over.",
415
+ expectedUiState: "With no name, the caveat says that supplying the model name lets the tool cross-check it against the code, and offers asking the seller for a photo of the box bottom and the screws. With the name supplied, that prompt is gone. The VERDICT is 'Inconclusive' in both cases — only the caveat differs.",
416
+ endResult: "Read-only; nothing persists.",
417
+ },
418
+ "checklist-content-discovery-authenticity-checker-authenticity-evasion-warning-survives": {
419
+ roles: ["guest"],
420
+ startPage: "/tools/authenticity",
421
+ steps: [
422
+ "Open /tools/authenticity.",
423
+ "Type Pegasis in the model name field — note the spelling, 'sis' not 'sus'.",
424
+ "If a typeahead offers canonical names, read them but do NOT pick one yet.",
425
+ "Run the check and read every row under 'What I checked'.",
426
+ "Note whether a trademark-evasion or fuzzy-spelling signal fired.",
427
+ "Now clear the field, type Pegasis again, and this time PICK 'Storm Pegasus' from the typeahead.",
428
+ "Run the check again and compare the signals.",
429
+ ],
430
+ inputs: { evasionSpelling: "Pegasis", canonical: "Storm Pegasus" },
431
+ expectedBehaviour: "This is the hazard the whole separation exists for. The site search matcher deliberately treats 'Pegasis' as a match for 'Storm Pegasus' — its alias table is derived FROM a counterfeit seller's 1,277 titles, on the insight that an evasion spelling is the same string a buyer mistypes. This tool treats that same spelling as EVIDENCE. If picking a canonical name replaced what the user typed, the warning would be silenced by construction and the tool would confirm a fake using the exact spelling that indicates one.",
432
+ expectedUiState: "The evasion/fuzzy signal fires in BOTH runs. A case asserting only 'the typeahead found it' would pass against precisely the bug this guards, so the second run is the one that matters.",
433
+ endResult: "Read-only; nothing persists.",
434
+ },
435
+ };
436
+ /*
437
+ * ── The name typeahead (2026-10-19) ──────────────────────────────────────
438
+ *
439
+ * `EntityTypeahead` is wired into the model-name field with
440
+ * `fillOnPick={false}` for exactly the reason `authenticity-name-typeahead`
441
+ * below checks: picking a suggestion must never replace what the engine
442
+ * analyses, or the evasion signal `authenticity-evasion-warning-survives`
443
+ * guards would be silenced by the very feature meant to help discovery.
444
+ */
445
+ export const authoredTypeahead = {
446
+ "checklist-content-discovery-authenticity-checker-authenticity-name-typeahead": {
447
+ roles: ["guest"],
448
+ startPage: "/tools/authenticity",
449
+ steps: [
450
+ "Open /tools/authenticity.",
451
+ "Type a partial model name into the model-name field, e.g. Drago.",
452
+ "Read the suggestions offered beneath the field.",
453
+ "Click one of the suggestions.",
454
+ "Read the model-name field's actual text after clicking.",
455
+ "Read any confirmation line that appeared.",
456
+ "Run the check and read the result.",
457
+ ],
458
+ inputs: { partial: "Drago" },
459
+ expectedBehaviour: "The typeahead is discovery only. Clicking a suggestion must NOT overwrite the field — if it did, the engine would analyse the canonical name instead of what the user actually typed, which is the mechanism `authenticity-evasion-warning-survives` exists to catch happening the other way.",
460
+ expectedUiState: "Canonical releases appear as the user types. After clicking one, the field still shows exactly what was typed (e.g. 'Drago'), and a muted confirmation line names the matched release separately. The check result is consistent with the typed text, not the clicked suggestion.",
461
+ endResult: "Read-only; nothing persists.",
462
+ },
463
+ "checklist-content-discovery-authenticity-checker-authenticity-box-code-still-instant": {
464
+ roles: ["guest"],
465
+ startPage: "/tools/authenticity",
466
+ steps: [
467
+ "Open /tools/authenticity and open DevTools → Network.",
468
+ "Clear the network log.",
469
+ "Enter a known box date code (e.g. A0813) in the code field, leaving the name field empty.",
470
+ "Run the check.",
471
+ "Read the network log for any request to a search endpoint or the search artifact.",
472
+ ],
473
+ inputs: { code: "A0813" },
474
+ expectedBehaviour: "GUARD CASE. The box date-code table is bundled locally and resolves with zero network requests — this was true before the typeahead work and must stay true, because the two code paths (date-code lookup vs. name typeahead) are independent.",
475
+ expectedUiState: "The result renders immediately. The network log shows no fetch to the search artifact or any /api/search* endpoint.",
476
+ endResult: "Read-only; nothing persists.",
477
+ },
478
+ /* ── The model picker and the per-model code list (2026-10-20) ──────────
479
+ * 🛑 The reported bug these exist for: typing "l drago" returned
480
+ * "Inconclusive — Nothing contradicts it, and nothing confirms it either",
481
+ * while the tool held 33 recorded box codes for Lightning L-Drago, 35 for
482
+ * Meteo and 27 for Destructor and had no way to reach any of them. Every
483
+ * table was keyed by CODE, so naming a bey could never surface them.
484
+ *
485
+ * So each case compares the answer against data the tool DEMONSTRABLY HAS.
486
+ * "Check the picker works" passes against the bug; "picking Lightning
487
+ * L-Drago must list its 33 codes" does not.
488
+ */
489
+ "checklist-content-discovery-authenticity-checker-model-picker-finds-l-drago": {
490
+ roles: ["guest"],
491
+ startPage: "/tools/authenticity",
492
+ steps: [
493
+ "Open /tools/authenticity.",
494
+ "Confirm the FIRST field is 'Which bey is it?' and is a searchable select, not a plain text box.",
495
+ "Open it and type: l drago",
496
+ "Confirm all three L-Drago releases are offered — Lightning L-Drago 100HF, Meteo L-Drago LW105LF and L-Drago Destructor F:S.",
497
+ "Confirm each offered row states how many box codes are on file for it.",
498
+ "Clear the search and type: pegasis",
499
+ "Confirm Storm Pegasus still appears, even though the list spells it Pegasus.",
500
+ "Answer no if 'l drago' offers nothing, or if the field is a free-text input with no list.",
501
+ ],
502
+ inputs: { search: "l drago" },
503
+ expectedBehaviour: "BEFORE: the first field was a free-text 'Model name'. Typing 'l drago' matched four releases, so the engine refused to pick one and returned an inconclusive verdict — correct, and useless, because the three L-Dragos it declined to choose between have 95 recorded box codes between them and the screen never said so. AFTER: the picker resolves exactly one bey, which is what unlocks the per-model code list. It searches aliases as well as labels, because Takara Tomy romanises the line Pegasis and Hasbro prints Pegasus — a label-only filter would find neither from the other.",
504
+ expectedUiState: "A searchable select labelled 'Which bey is it?' listing three L-Drago rows, each annotated with a box-code count. Beys with codes on file are offered before beys without.",
505
+ endResult: "Nothing is written. The picker only reads a bundled list.",
506
+ },
507
+ "checklist-content-discovery-authenticity-checker-picked-model-lists-its-box-codes": {
508
+ roles: ["guest"],
509
+ startPage: "/tools/authenticity",
510
+ steps: [
511
+ "Open /tools/authenticity.",
512
+ "In 'Which bey is it?', search for and pick: Lightning L-Drago 100HF",
513
+ "Leave 'Code on the box' EMPTY.",
514
+ "Press 'Check it'.",
515
+ "Read the verdict line, then look for a panel headed 'Box codes recorded for Lightning L-Drago 100HF'.",
516
+ "Count the codes it lists.",
517
+ "Answer no if the verdict is 'Inconclusive — Nothing contradicts it, and nothing confirms it either', or if no code list appears.",
518
+ ],
519
+ inputs: { modelSlug: "lightning-l-drago-100hf" },
520
+ expectedBehaviour: "BEFORE: naming this bey with no code produced 'Inconclusive — Nothing contradicts it, and nothing confirms it either' and a physical checklist. True, in that nothing had been checked — and it withheld the 33 box codes the tables record for this exact bey, which is the single most useful thing the tool holds and the one thing the user had asked for. AFTER: the verdict is 'I know this one. Now find the code stamped on the box.' — still not a judgement, because nothing has been checked, but with the next step named and the 33 codes listed to check against.",
521
+ expectedUiState: "Verdict badge 'Need a bit more', headline 'I know this one. Now find the code stamped on the box.', a caveat naming the count, and a panel listing 33 codes as badges. A link reading 'Everything we hold on Lightning L-Drago 100HF' points at the model's own category page.",
522
+ expectedData: { recordedCodes: 33 },
523
+ endResult: "Read-only.",
524
+ },
525
+ "checklist-content-discovery-authenticity-checker-picked-model-plus-its-own-code": {
526
+ roles: ["guest"],
527
+ startPage: "/tools/authenticity",
528
+ steps: [
529
+ "Pick 'Lightning L-Drago 100HF' in 'Which bey is it?'.",
530
+ "Enter A0910 in 'Code on the box' — a code recorded against this exact bey.",
531
+ "Press 'Check it' and read the verdict.",
532
+ "Expand 'How I got there' and find the row about the code.",
533
+ "Press 'Start over'. Pick Lightning L-Drago 100HF again, this time enter A0310 — a real code recorded against a DIFFERENT bey.",
534
+ "Read the verdict and expand the working-out again.",
535
+ "Answer no if the second case reads as a mismatch, a discrepancy, or anything implying the item is wrong.",
536
+ ],
537
+ inputs: { modelSlug: "lightning-l-drago-100hf", recordedCode: "A0910", otherBeyCode: "A0310" },
538
+ expectedBehaviour: "A0910 is in this bey's own list, so the check reads as agreement — the strongest thing the tool can say from data, and it still says so is not proof. A0310 is recorded, but on Earth Aquila: that is WEAK SUPPORT, not a mismatch, because a production date covers whatever was boxed that day. The distinction is the whole point. Reporting 'that code belongs to a different bey' as a discrepancy would manufacture suspicion out of how the tables are organised, and the panel highlights the code you entered inside the list so you can see where it sits.",
539
+ expectedUiState: "First run: 'Consistent with an original', and a row reading that the code is recorded on genuine boxes of this exact bey. Second run: still not adverse — a neutral row saying the code is recorded but not against this bey, with the phrase 'weak support rather than a mismatch'.",
540
+ endResult: "Read-only.",
541
+ },
542
+ "checklist-content-discovery-authenticity-checker-missing-code-is-not-an-accusation": {
543
+ roles: ["guest"],
544
+ startPage: "/tools/authenticity",
545
+ steps: [
546
+ "Pick 'Lightning L-Drago 100HF' in 'Which bey is it?'.",
547
+ "Enter Z9999 in 'Code on the box' — well-formed, and in none of the tables.",
548
+ "Press 'Check it'.",
549
+ "Read the verdict, the code panel and the working-out.",
550
+ "Answer no if any sentence implies the item is fake, counterfeit or a reproduction, or if the verdict is more severe than 'Inconclusive'.",
551
+ ],
552
+ inputs: { modelSlug: "lightning-l-drago-100hf", code: "Z9999" },
553
+ expectedBehaviour: "A code absent from a model's list means UNRECORDED, never wrong. These tables are what collectors photographed, not a manufacturing record: 38 of 221 canonical beys have any recorded code at all, new genuine codes turn up regularly, and counterfeiters print real codes anyway. The panel must say so where a user will actually read it, not only inside the collapsed working-out — the reading 'my code is not in the list, so it is fake' is the one this whole feature has to defend against.",
554
+ expectedUiState: "Verdict 'Inconclusive'. A neutral row reading 'Not among the 33 codes recorded for Lightning L-Drago 100HF' whose text says 'unrecorded, not wrong'. The code panel carries a line beginning 'A code missing from this list means unrecorded, not fake.'",
555
+ endResult: "Read-only.",
556
+ },
557
+ "checklist-content-discovery-authenticity-checker-working-out-is-collapsed-by-default": {
558
+ roles: ["guest"],
559
+ startPage: "/tools/authenticity",
560
+ steps: [
561
+ "Pick 'Lightning L-Drago 100HF' and enter A0910. Press 'Check it'.",
562
+ "WITHOUT expanding anything, confirm you can read the verdict badge, the headline, the caveat and the recorded-code list.",
563
+ "Confirm the reasoning is behind a single collapsed control reading 'How I got there — N checks'.",
564
+ "Expand it and confirm every individual check is still there, each with its own confidence chip.",
565
+ "Press 'Start over'. Now enter the code A0813 with no model picked — a code the sources record as a midfake batch.",
566
+ "Confirm the working-out is ALREADY OPEN this time, without being clicked.",
567
+ "Answer no if the reasoning is expanded by default on the clean run, or collapsed on the midfake run.",
568
+ ],
569
+ inputs: { modelSlug: "lightning-l-drago-100hf", cleanCode: "A0910", midfakeCode: "A0813" },
570
+ expectedBehaviour: "BEFORE: every check rendered expanded under a 'What I checked' heading — up to eleven rows of reasoning between the buyer and the answer they asked for. AFTER: it is collapsed, not deleted, because 'why should I believe this' has to stay one tap away for a tool whose entire posture is that it hedges and cites. A CONCERN forces it open: the one case where the reasoning IS the answer is the case where something is wrong, and the user who never expands it must not be the user who never finds out.",
571
+ expectedUiState: "Clean run: verdict block, code panel, a collapsed 'How I got there — N checks', then the physical checks. Midfake run: the same control, already open.",
572
+ endResult: "Read-only.",
573
+ },
574
+ "checklist-content-discovery-authenticity-checker-result-scrolls-into-view": {
575
+ roles: ["guest"],
576
+ startPage: "/tools/authenticity",
577
+ steps: [
578
+ "Resize the browser to a phone viewport (about 390 x 740) or use device emulation.",
579
+ "Pick 'Lightning L-Drago 100HF' and enter A0910.",
580
+ "Scroll so the 'Check it' button is visible, and note what is on screen.",
581
+ "Press 'Check it'.",
582
+ "Without scrolling yourself, confirm the verdict badge and headline are now on screen.",
583
+ "Answer no if the page does not move and the verdict is below the fold.",
584
+ ],
585
+ inputs: { viewport: "390x740", modelSlug: "lightning-l-drago-100hf", code: "A0910" },
586
+ expectedBehaviour: "BEFORE: the form is taller than a phone viewport, so the answer rendered below the fold and pressing 'Check it' looked like it had done nothing — at the one moment this tool must not be ambiguous. AFTER: the result is scrolled into view on every fresh assessment, including the re-run a clarifying question triggers, and anchored at the top of the viewport rather than the middle so the badge and headline land where the eye already is.",
587
+ expectedUiState: "After submitting, the verdict badge and headline are visible without manual scrolling.",
588
+ endResult: "Read-only.",
589
+ },
243
590
  };