@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,11 +1,21 @@
1
1
  /**
2
2
  * D2 Fix 4 — CROSS-RESOURCE search.
3
3
  *
4
- * 🛑 `/search` is a pure REDIRECT today: it maps each of 15
5
- * `SearchResourceType` values to a browse route and `redirect()`s, `noindex`.
6
- * So there is **no cross-resource search at all** — the dropdown picks which
7
- * single listing page to send you to, and a buyer searching "dranzer" cannot
8
- * see that we have a product, an auction, a blog post and an FAQ about it.
4
+ * 🛑 CORRECTED 2026-10-19 — this header asserted the opposite of the truth.
5
+ *
6
+ * It read: "`/search` is a pure REDIRECT today … there is no cross-resource
7
+ * search at all." That described the state this module was WRITTEN to fix,
8
+ * and it stayed on the file after the fix shipped — so the one document
9
+ * explaining the feature told every later reader the feature did not exist.
10
+ *
11
+ * What is true now: `/search` is a real grouped results page, the header
12
+ * reaches it, and this module is TIER 2 — the authenticated, server-side
13
+ * half. The public half is a prebuilt artifact searched in the browser at
14
+ * zero Firestore reads, and this one is what serves a viewer's OWN records
15
+ * (`scoped-sources.ts`) and what the kill switch falls back to when the
16
+ * artifact is unavailable.
17
+ *
18
+ * That fallback is the reason this file is not deleted.
9
19
  *
10
20
  * ── WHY THIS IS AFFORDABLE ─────────────────────────────────────────────────
11
21
  *
@@ -30,11 +40,15 @@
30
40
  *
31
41
  * ── 🛑 WHAT IT DOES NOT REUSE, AND WHY ─────────────────────────────────────
32
42
  *
33
- * NOT `SearchRepository`. That class builds `title_=${q}` — a case-sensitive
43
+ * NOT `SearchRepository`. That class built `title_=${q}` — a case-sensitive
34
44
  * STARTS-WITH on `title` alone — so "dranzer" would not find "Beyblade
35
- * Original Dranzer S" and "flat" would find nothing at all. It is reachable
36
- * only through `/api/search`, whose own hook (`useSearch`) has zero consumers
37
- * outside barrel re-exports. Recorded rather than deleted here; see F1.
45
+ * Original Dranzer S" and "flat" would find nothing at all. It was reachable
46
+ * only through `/api/search`, whose own hook (`useSearch`) had zero consumers
47
+ * outside barrel re-exports. The whole dead subsystem — `SearchRepository`,
48
+ * `/api/search`, `useSearch`, `SearchView`/`SearchFiltersRow`/
49
+ * `SearchResultsSection`, the legacy `search-actions.ts`/`data.ts`/
50
+ * `actions.ts` chain — was deleted in the Wave 10 cleanup, kept only as this
51
+ * historical note on why `searchEverything` never called it.
38
52
  */
39
53
  import { planSearchTxt } from "../../../../utils/search-txt-query.js";
40
54
  import { parseSearchTxtQuery, SEARCH_TXT_FIELD } from "../../../../utils/search-txt.js";
@@ -44,6 +58,19 @@ import { normalizeError } from "../../../../errors/normalize.js";
44
58
  const PER_GROUP = 6;
45
59
  /** The bounded window each push-down reads before the in-memory refine. */
46
60
  const SCAN = 60;
61
+ /**
62
+ * 🛑 The ceiling on what ONE viewer's scoped search may read.
63
+ *
64
+ * Σ `scanLimit` across the widest role, checked by
65
+ * `audit-scoped-search-ownership` R4 — which PRINTS THE ARITHMETIC on
66
+ * failure, because "the budget is exceeded" is useless without knowing by
67
+ * which source and how much.
68
+ *
69
+ * 400 against Rule #6's ~3-round-trip guidance is defensible because the
70
+ * sources run in ONE `Promise.all`: it is one round trip of fan-out, not
71
+ * twelve sequential ones. The number that matters is documents read.
72
+ */
73
+ export const SCOPED_SCAN_BUDGET = 400;
47
74
  /**
48
75
  * Run every source in parallel and group the results.
49
76
  *
@@ -75,20 +102,41 @@ export async function searchEverything(db, q, sources) {
75
102
  moreHref: src.moreHref(q),
76
103
  };
77
104
  try {
105
+ const scan = src.scanLimit ?? SCAN;
106
+ const boundedMemory = src.mode === "bounded-memory";
78
107
  let ref = db.collection(src.collection);
79
108
  for (const [field, value] of src.where ?? [])
80
109
  ref = ref.where(field, "==", value);
81
- const snap = await ref
82
- .where(SEARCH_TXT_FIELD, "array-contains", plan.head)
83
- .select(...src.select, SEARCH_TXT_FIELD)
84
- .limit(SCAN)
85
- .get();
110
+ /*
111
+ * 🛑 The two modes differ in ONE clause and in nothing else.
112
+ *
113
+ * `bounded-memory` omits the `array-contains` push-down and does not
114
+ * project `searchTxt` — because the collections that use it do not
115
+ * have one, deliberately (see `mode`). Everything downstream —
116
+ * `toHit`, the PER_GROUP slice, the `truncated` floor — is shared, so
117
+ * the two paths cannot drift in how they REPORT, only in how they
118
+ * find.
119
+ */
120
+ const snap = boundedMemory
121
+ ? await (src.orderBy
122
+ ? ref.orderBy(src.orderBy[0], src.orderBy[1])
123
+ : ref)
124
+ .select(...src.select)
125
+ .limit(scan)
126
+ .get()
127
+ : await ref
128
+ .where(SEARCH_TXT_FIELD, "array-contains", plan.head)
129
+ .select(...src.select, SEARCH_TXT_FIELD)
130
+ .limit(scan)
131
+ .get();
86
132
  // AND-refine the remaining terms in memory — one push-down per query is
87
133
  // all Firestore gives, so the rest is a filter over a bounded window.
88
- const matched = snap.docs.filter((d) => {
89
- const txt = d.data()[SEARCH_TXT_FIELD] ?? [];
90
- return plan.rest.every((t) => txt.includes(t));
91
- });
134
+ const matched = boundedMemory
135
+ ? snap.docs.filter((d) => src.matches?.(d.data(), terms) ?? false)
136
+ : snap.docs.filter((d) => {
137
+ const txt = d.data()[SEARCH_TXT_FIELD] ?? [];
138
+ return plan.rest.every((t) => txt.includes(t));
139
+ });
92
140
  const hits = matched
93
141
  .map((d) => src.toHit(d.id, d.data()))
94
142
  .filter((h) => h !== null);
@@ -96,8 +144,10 @@ export async function searchEverything(db, q, sources) {
96
144
  ...base,
97
145
  hits: hits.slice(0, PER_GROUP),
98
146
  // Saturated window OR more matches than we show — either way the
99
- // count on screen is a floor.
100
- truncated: snap.docs.length >= SCAN || hits.length > PER_GROUP,
147
+ // count on screen is a floor. Compared against THIS source's own
148
+ // limit, not the module default: a scoped source scanning 20 that
149
+ // returned 20 is saturated even though 20 < SCAN.
150
+ truncated: snap.docs.length >= scan || hits.length > PER_GROUP,
101
151
  };
102
152
  }
103
153
  catch (err) {
@@ -141,6 +141,12 @@ function staticPages(baseUrl) {
141
141
  page(String(ROUTES.PUBLIC.DIGITAL_CODES), "daily", 0.6),
142
142
  page(String(ROUTES.PUBLIC.LIVE), "hourly", 0.7),
143
143
  page(String(ROUTES.PUBLIC.ART), "weekly", 0.6),
144
+ /*
145
+ * `daily`, matching the other one-of-a-kind types rather than `/art`'s
146
+ * weekly: most spares are quantity 1, so the page turns over as fast as
147
+ * classifieds do.
148
+ */
149
+ page(String(ROUTES.PUBLIC.SPARES), "daily", 0.6),
144
150
  page(String(ROUTES.PUBLIC.FEES), "monthly", 0.4),
145
151
  page(String(ROUTES.PUBLIC.HOW_AUCTIONS_WORK), "monthly", 0.4),
146
152
  page(String(ROUTES.PUBLIC.HOW_PRE_ORDERS_WORK), "monthly", 0.4),
@@ -1,3 +1,4 @@
1
+ import { productImageUrls } from "../../../../features/products/schemas/firestore.js";
1
2
  /** Maps the shared fields from a raw Firestore ProductDocument. */
2
3
  export function mapBaseListingFields(doc) {
3
4
  return {
@@ -10,8 +11,9 @@ export function mapBaseListingFields(doc) {
10
11
  // bypassing <GatedPrice> entirely — and it had zero readers, so it was
11
12
  // pure exposure. Clients format from `price` through the gate.
12
13
  currency: doc.currency ?? "INR",
13
- mainImage: doc.mainImage || doc.images?.[0] || null,
14
- images: doc.images ?? [],
14
+ mainImage: doc.mainImage || doc.images?.[0]?.url || null,
15
+ // A6: the client listing shape is still `string[]`; project, never mirror.
16
+ images: productImageUrls(doc.images),
15
17
  storeId: doc.storeId,
16
18
  storeName: doc.storeName ?? null,
17
19
  category: doc.categorySlugs?.[0] ?? doc.category ?? "",
@@ -15,6 +15,17 @@ export interface ClientSheetPart {
15
15
  areaMm2: number | null;
16
16
  /** Whether a full-resolution render exists, NOT where it is. */
17
17
  hasAsset: boolean;
18
+ /**
19
+ * The PUBLIC `/media/{shortId}` slug, when the part has been promoted.
20
+ *
21
+ * 🛑 Public on purpose and NOT a path. A `mediaShortId` is already a public
22
+ * URL by construction — a `mediaAssets` row IS what mints `/media/{id}`, and
23
+ * `/api/media/[...slug]` serves it to anyone, watermarked. So the thing it
24
+ * names is public whether or not this field is projected, and the admin
25
+ * review UI genuinely needs it to show the marketplace copy beside the clean
26
+ * one. `assetPath` stays private: that is the unwatermarked original.
27
+ */
28
+ mediaShortId?: string;
18
29
  duplicateOf?: number;
19
30
  }
20
31
  export interface ClientStickerSheet {
@@ -105,6 +105,7 @@ function toClientPart(part) {
105
105
  bboxMm: part.bboxMm,
106
106
  areaMm2: part.areaMm2,
107
107
  hasAsset: Boolean(part.assetPath),
108
+ ...(part.mediaShortId !== undefined ? { mediaShortId: part.mediaShortId } : {}),
108
109
  ...(part.duplicateOf !== undefined ? { duplicateOf: part.duplicateOf } : {}),
109
110
  };
110
111
  }
@@ -0,0 +1,26 @@
1
+ export interface PromoteToListingsInput {
2
+ sheetId: string;
3
+ /** Owning store. The admin picks it; there is no sensible default. */
4
+ storeId: string;
5
+ /**
6
+ * ONE price for every part, in rupees.
7
+ *
8
+ * 🛑 Deliberately flat rather than per-part or area-derived. A cut sticker
9
+ * costs about the same to print whatever its size, so a size-scaled price
10
+ * would be a fabricated precision — and a per-part form for a 60-part sheet
11
+ * is 60 inputs nobody fills in honestly. Each child is an ordinary product
12
+ * afterwards and repriceable in the normal editor.
13
+ */
14
+ partPrice: number;
15
+ /** The whole sheet's price, in rupees. */
16
+ sheetPrice: number;
17
+ /** Auth UID, for the `mediaAssets` row's `uploadedBy`. */
18
+ actorUid: string;
19
+ }
20
+ export interface PromoteToListingsResult {
21
+ parentId: string;
22
+ childIds: string[];
23
+ /** Parts skipped as confirmed duplicates of another part. */
24
+ skippedDuplicates: number;
25
+ }
26
+ export declare function promoteStickerSheetToListings(input: PromoteToListingsInput): Promise<PromoteToListingsResult>;
@@ -0,0 +1,264 @@
1
+ /*
2
+ * WHY: The tracer ends at extracted geometry and a promoted run's full-size
3
+ * part PNGs. This is what makes that worth doing — the whole sheet becomes
4
+ * one listing, each unique cut becomes its own, and they are grouped so a
5
+ * buyer takes any mix ("the sheet, no; six of B; one of D").
6
+ * WHAT: `promoteStickerSheetToListings` — sheet → one parent product + N child
7
+ * products in one product group.
8
+ *
9
+ * 🛑 DISTINCT FROM `promoteRun`, and the names are close enough to matter.
10
+ * · `POST .../runs/{runId}/promote` → `promoteRun`: run → SHEET. Renders
11
+ * full-res parts and exports, sets `activeRunId`. Stays inside the tracer.
12
+ * · `POST .../promote-to-listings` → this: SHEET → MARKETPLACE. Creates
13
+ * products. Runs once per sheet, after the above.
14
+ * Calling this without the first is refused, because `SheetPart.assetPath` is
15
+ * only written by the `mode: "full"` shard path.
16
+ *
17
+ * 🛑 ZERO BYTES ARE COPIED for the watermarked display image. A `mediaAssets`
18
+ * row pointing at the EXISTING private part PNG is enough: the row is what
19
+ * mints `/media/{shortId}`, and the proxy watermarks on read because the row is
20
+ * written with `watermarkedAt` absent. Clean original for the cut file,
21
+ * watermarked copy for the marketplace, one object. Object count is metered
22
+ * separately from bytes, which is the whole reason the thumb atlas exists.
23
+ *
24
+ * EXPORTS: PromoteToListingsInput, PromoteToListingsResult,
25
+ * promoteStickerSheetToListings
26
+ *
27
+ * @tag domain:sticker-trace,products
28
+ * @tag layer:server
29
+ * @tag pattern:none
30
+ * @tag access:server
31
+ * @tag consumers:api/admin/sticker-sheets
32
+ * @tag sideEffects:firestore
33
+ */
34
+ import { ValidationError } from "../../../../errors/validation-error.js";
35
+ import { serverLogger } from "../../../../monitoring/server-logger.js";
36
+ import { normalizeError } from "../../../../errors/normalize.js";
37
+ import { productRepository } from "../../../../repositories/index.js";
38
+ import { mediaAssetsRepository } from "../../../../features/media/repository/media-assets.repository.js";
39
+ import { stickerSheetsRepository } from "../../../../features/sticker-trace/repository/sticker-sheets.repository.js";
40
+ /** A part that becomes its own listing: unique, and actually rendered. */
41
+ function isListableUniquePart(part) {
42
+ /*
43
+ * `duplicateOf` is proposed by perceptual hash and CONFIRMED BY A HUMAN, so
44
+ * trusting it is safe by design — that confirmation step exists precisely so
45
+ * this decision can be mechanical. Two stickers that merely look alike at
46
+ * thumbnail size are common on these sheets.
47
+ */
48
+ return part.duplicateOf === undefined;
49
+ }
50
+ /**
51
+ * Refuse loudly, before writing anything, if the sheet is not ready.
52
+ *
53
+ * 🛑 A half-promoted sheet is the "wrong data, no error" failure this whole
54
+ * plan exists to avoid: some parts listed, some silently missing, and nothing
55
+ * anywhere saying so. Every precondition is therefore checked UP FRONT and the
56
+ * whole promotion refused — not checked per part inside the loop, which would
57
+ * leave the first N written.
58
+ */
59
+ function assertPromotable(sheet) {
60
+ if (!sheet.activeRunId) {
61
+ throw new ValidationError(`"${sheet.title}" has no accepted run yet. Accept a run first, then promote it to listings.`);
62
+ }
63
+ const parts = sheet.parts ?? [];
64
+ if (parts.length === 0) {
65
+ throw new ValidationError(`"${sheet.title}" has no parts. Trace it and accept a run first.`);
66
+ }
67
+ const unique = parts.filter(isListableUniquePart);
68
+ if (unique.length === 0) {
69
+ throw new ValidationError(`Every part on "${sheet.title}" is marked a duplicate of another, so there is nothing to list.`);
70
+ }
71
+ /*
72
+ * `assetPath` absent means the accepted run was never PROMOTED — the
73
+ * full-resolution PNGs do not exist. Naming the parts rather than saying
74
+ * "not ready": the admin's next action is different depending on whether it
75
+ * is all of them (promote the run) or a few (the shard that rendered those
76
+ * failed, so retry).
77
+ */
78
+ const unrendered = unique.filter((p) => !p.assetPath);
79
+ if (unrendered.length > 0) {
80
+ throw new ValidationError(`${unrendered.length} of ${unique.length} parts on "${sheet.title}" have no full-resolution image yet` +
81
+ ` (${unrendered.slice(0, 5).map((p) => `#${p.ordinal}`).join(", ")}${unrendered.length > 5 ? ", …" : ""}).` +
82
+ ` Accept & render the run before promoting it to listings.`);
83
+ }
84
+ return unique;
85
+ }
86
+ /**
87
+ * Register the public, WATERMARKED display copy of one part.
88
+ *
89
+ * Reuses an existing row when the part already has one, so re-promoting a sheet
90
+ * does not mint a second slug for the same object and orphan the first.
91
+ */
92
+ async function ensurePartMediaSlug(sheet, part, actorUid) {
93
+ if (part.mediaShortId)
94
+ return part.mediaShortId;
95
+ if (!part.assetPath)
96
+ return undefined;
97
+ try {
98
+ const asset = await mediaAssetsRepository.createAsset(
99
+ /*
100
+ * A content-derived slug, matching every other media slug in the app.
101
+ * `createAsset` appends a random suffix on collision, so two sheets with
102
+ * the same title cannot overwrite each other's parts.
103
+ */
104
+ `sticker-part-${sheet.id}-${String(part.ordinal).padStart(3, "0")}`, {
105
+ /*
106
+ * 🛑 The SAME path as the clean original. This is the no-byte-copy
107
+ * mechanism, and it is only safe because of the two fields below.
108
+ */
109
+ storagePath: part.assetPath,
110
+ uploadedBy: actorUid,
111
+ contentType: "image/png",
112
+ /*
113
+ * Not measured. The row's `size` is used by the media usage meter, not
114
+ * for serving, and reading the object's metadata here would cost a
115
+ * Storage round trip per part — 60 on a full sheet — for a number the
116
+ * sticker feature already reports from `measureSheetBytes`.
117
+ */
118
+ size: 0,
119
+ isPublic: true,
120
+ status: "finalized",
121
+ contextType: "sticker-part",
122
+ /*
123
+ * 🛑 `watermarkedAt` is DELIBERATELY ABSENT, and it is the whole design.
124
+ * The proxy skips its sharp composite only when that field is set — so
125
+ * leaving it unset means `/media/{shortId}` watermarks this object on
126
+ * every read, while `assetPath` behind a signed URL stays clean. Set it
127
+ * here and the marketplace would serve the unwatermarked printable
128
+ * original, which is the one outcome this must never produce.
129
+ */
130
+ });
131
+ return asset.id;
132
+ }
133
+ catch (err) {
134
+ const e = normalizeError(err);
135
+ /*
136
+ * Non-fatal. A part with no public slug still lists — it falls back to no
137
+ * image rather than blocking the whole promotion — and the admin can
138
+ * re-promote to fill it in. Failing the sheet over one media row would be
139
+ * the worse trade.
140
+ */
141
+ serverLogger.warn("sticker-promote: media row failed for part", {
142
+ sheetId: sheet.id,
143
+ ordinal: part.ordinal,
144
+ error: e.message,
145
+ });
146
+ return undefined;
147
+ }
148
+ }
149
+ export async function promoteStickerSheetToListings(input) {
150
+ const sheet = await stickerSheetsRepository.findByIdOrFail(input.sheetId);
151
+ const unique = assertPromotable(sheet);
152
+ const allParts = sheet.parts ?? [];
153
+ /*
154
+ * Media rows first, before any product write.
155
+ *
156
+ * The images are what a listing is FOR, so resolving them up front means a
157
+ * product is never created pointing at nothing. Sequential rather than
158
+ * `Promise.all`: `createAsset` does a read-then-write collision check, and 60
159
+ * of those in parallel is 120 concurrent operations against Rule #6's budget
160
+ * for an admin action that is not latency-sensitive.
161
+ */
162
+ const slugByOrdinal = new Map();
163
+ for (const part of unique) {
164
+ const slug = await ensurePartMediaSlug(sheet, part, input.actorUid);
165
+ if (slug)
166
+ slugByOrdinal.set(part.ordinal, slug);
167
+ }
168
+ const partImage = (part) => {
169
+ const slug = slugByOrdinal.get(part.ordinal);
170
+ return slug ? `/media/${slug}` : "";
171
+ };
172
+ /*
173
+ * The parent: the whole sheet.
174
+ *
175
+ * 🛑 `madeToOrder: true`. A sheet that has been cut into parts has no stock
176
+ * of its own — it is printed when ordered — so without the flag the parent is
177
+ * unbuyable at quantity 0 and vanishes from the default availability scope,
178
+ * taking the group's entry point with it.
179
+ */
180
+ const parent = (await productRepository.create({
181
+ title: sheet.title,
182
+ description: `The complete ${sheet.title} sheet. Individual stickers from this sheet are listed separately — ` +
183
+ `pick any mix from the group below.`,
184
+ categorySlugs: [],
185
+ price: input.sheetPrice,
186
+ currency: "INR",
187
+ stockQuantity: 0,
188
+ madeToOrder: true,
189
+ mainImage: unique[0] ? partImage(unique[0]) : "",
190
+ images: unique.slice(0, 4).map(partImage).filter(Boolean),
191
+ status: "draft",
192
+ storeId: input.storeId,
193
+ featured: false,
194
+ tags: ["stickers"],
195
+ condition: "new",
196
+ listingType: "stickers",
197
+ groupTitle: sheet.title,
198
+ sourceStickerSheetId: sheet.id,
199
+ }));
200
+ /*
201
+ * `startGroup` sets `groupId = slug`, so the group id IS the parent's slug —
202
+ * which is why the parent must exist before any child can name it.
203
+ */
204
+ const parentSlug = parent.slug ?? parent.id;
205
+ await productRepository.startGroup(parent.id, parentSlug);
206
+ const children = [];
207
+ for (const part of unique) {
208
+ const child = (await productRepository.create({
209
+ title: `${sheet.title} — ${part.name}`,
210
+ description: `A single kiss-cut sticker from the ${sheet.title} sheet.`,
211
+ categorySlugs: [],
212
+ price: input.partPrice,
213
+ currency: "INR",
214
+ stockQuantity: 0,
215
+ // Same reasoning as the parent: a cut sticker is printed on order.
216
+ madeToOrder: true,
217
+ mainImage: partImage(part),
218
+ images: [partImage(part)].filter(Boolean),
219
+ status: "draft",
220
+ storeId: input.storeId,
221
+ featured: false,
222
+ tags: ["stickers"],
223
+ condition: "new",
224
+ listingType: "stickers",
225
+ sourceStickerSheetId: sheet.id,
226
+ sourceStickerPartOrdinal: part.ordinal,
227
+ }));
228
+ children.push(child);
229
+ }
230
+ /*
231
+ * 🛑 ONE batch link, via `linkChildrenToGroup`. Calling `linkChildToGroup` in
232
+ * a loop was the bug this phase had to fix first: it spread the CALLER's
233
+ * in-memory `parent.groupChildSlugs`, so N children produced exactly one
234
+ * entry — every iteration computing `[...stale, child]` from the same
235
+ * pre-loop snapshot, last write winning. Each child still got its own
236
+ * `groupId`, so nothing errored and `findAllByGroupId` found them all while
237
+ * the parent claimed one member.
238
+ */
239
+ await productRepository.linkChildrenToGroup({ ...parent, groupId: parentSlug }, children);
240
+ /*
241
+ * Write the slugs back onto the sheet so a re-promote reuses them.
242
+ *
243
+ * 🛑 Mapped over ALL parts, keyed on `ordinal`, never on array index — and
244
+ * `assertPromotable` filtered to unique parts, so a plain `.map()` over
245
+ * `unique` here would DROP every duplicate part from the sheet. The duplicate
246
+ * rows carry `duplicateOf` and real geometry and must survive.
247
+ */
248
+ const updatedParts = allParts.map((p) => {
249
+ const slug = slugByOrdinal.get(p.ordinal);
250
+ return slug ? { ...p, mediaShortId: slug } : p;
251
+ });
252
+ await stickerSheetsRepository.update(sheet.id, { parts: updatedParts });
253
+ serverLogger.info("sticker-promote: sheet promoted to listings", {
254
+ sheetId: sheet.id,
255
+ parentId: parent.id,
256
+ children: children.length,
257
+ skippedDuplicates: allParts.length - unique.length,
258
+ });
259
+ return {
260
+ parentId: parent.id,
261
+ childIds: children.map((c) => c.id),
262
+ skippedDuplicates: allParts.length - unique.length,
263
+ };
264
+ }
@@ -88,7 +88,7 @@ export declare function attachStickerSheetScan(input: AttachScanInput): Promise<
88
88
  * property `PRIVATE_STICKER_SHEET_FIELDS` declares and
89
89
  * audit-public-projection-parity enforces.
90
90
  */
91
- export declare const STICKER_ASSET_KINDS: readonly ["scan", "preview", "overlay", "geometry", "cutSvg", "thumbAtlas"];
91
+ export declare const STICKER_ASSET_KINDS: readonly ["scan", "preview", "overlay", "geometry", "cutSvg", "thumbAtlas", "part"];
92
92
  export type StickerAssetKind = (typeof STICKER_ASSET_KINDS)[number];
93
93
  /**
94
94
  * Mint a short-lived read URL for one asset of one sheet or run.
@@ -105,6 +105,8 @@ export declare function resolveStickerAssetUrl(input: {
105
105
  sheetId: string;
106
106
  kind: StickerAssetKind;
107
107
  runId?: string;
108
+ /** Required for `kind: "part"`. Matched against `SheetPart.ordinal`. */
109
+ ordinal?: number;
108
110
  }): Promise<{
109
111
  url: string;
110
112
  expiresAt: Date;
@@ -176,6 +176,15 @@ export const STICKER_ASSET_KINDS = [
176
176
  "geometry",
177
177
  "cutSvg",
178
178
  "thumbAtlas",
179
+ /*
180
+ * One promoted part's full-resolution PNG, by `ordinal`.
181
+ *
182
+ * 🛑 Absent until the producing run is PROMOTED — `SheetPart.assetPath` is
183
+ * only written by the `mode: "full"` shard path. Resolving to `null` before
184
+ * then is correct and must not read as an error: it is the normal state of
185
+ * every traced-but-unpromoted sheet.
186
+ */
187
+ "part",
179
188
  ];
180
189
  /**
181
190
  * Mint a short-lived read URL for one asset of one sheet or run.
@@ -193,6 +202,29 @@ export async function resolveStickerAssetUrl(input) {
193
202
  if (input.kind === "scan") {
194
203
  return sheet.sourceScan?.assetPath ? signedReadUrl(sheet.sourceScan.assetPath) : null;
195
204
  }
205
+ if (input.kind === "part") {
206
+ if (typeof input.ordinal !== "number")
207
+ return null;
208
+ /*
209
+ * 🛑 Matched on `ordinal`, NEVER on array index — the same rule
210
+ * `updatePart` already states. `parts[]` is not guaranteed to be dense or
211
+ * ordered: a part can be removed, and a re-promote rewrites the array. An
212
+ * index lookup would silently serve a DIFFERENT sticker's PNG, which is
213
+ * indistinguishable from a correct response.
214
+ */
215
+ const part = (sheet.parts ?? []).find((pt) => pt.ordinal === input.ordinal);
216
+ if (!part?.assetPath)
217
+ return null;
218
+ /*
219
+ * Still a signed-URL mint, like every other kind here —
220
+ * `audit-sticker-trace-egress` forbids a route under this feature
221
+ * returning bytes, and this is the unwatermarked printable original, which
222
+ * is the one thing that must not be served through a public path.
223
+ */
224
+ return signedReadUrl(part.assetPath, {
225
+ downloadAs: `part-${String(input.ordinal).padStart(3, "0")}.png`,
226
+ });
227
+ }
196
228
  let path;
197
229
  if (input.runId) {
198
230
  const run = await stickerTraceRunsRepository.findByIdOrFail(input.runId);
@@ -1,5 +1,24 @@
1
1
  import type { CutPath, Rect } from "../../../shared/features/sticker-trace/types";
2
- /** Nothing under here is reachable without a signed URL minted by this module. */
2
+ /**
3
+ * Everything this feature writes lives under here.
4
+ *
5
+ * 🛑 The one-line claim this used to carry — "nothing under here is reachable
6
+ * without a signed URL minted by this module" — was FALSE, in the same way the
7
+ * identical claim on `DIGITAL_CONTENT_PREFIX` was: `/media/[...slug]`'s
8
+ * raw-path fallback resolved any multi-segment slug as a literal storage path,
9
+ * so `/media/private/sticker-sheets/...` served these objects, watermarked, to
10
+ * anyone. Unlike digital-content's `randomUUID()` names, a part is written as
11
+ * `part-003.png` — guessable on the first try.
12
+ *
13
+ * The proxy now refuses a raw `private/` path outright. What is true is
14
+ * narrower than the old sentence and worth stating precisely: **no public URL
15
+ * exists for one of these objects unless something deliberately mints a
16
+ * `mediaAssets` row for it.** A row is what creates a `/media/{shortId}` slug,
17
+ * and a resolved row is intentionally still allowed through the proxy — that is
18
+ * how a promoted part gets a watermarked marketplace copy with no byte
19
+ * duplicated. The unwatermarked printable original has no row and is reachable
20
+ * only by a signed URL from this module.
21
+ */
3
22
  export declare const SCAN_ROOT = "private/sticker-sheets";
4
23
  export declare function runPrefix(sheetId: string, runId: string): string;
5
24
  export interface SignedUpload {
@@ -45,7 +45,26 @@
45
45
  import { normalizeError } from "../../../../errors/normalize.js";
46
46
  import { serverLogger } from "../../../../monitoring/index.js";
47
47
  import { getAdminStorage } from "../../../../providers/db-firebase/index.js";
48
- /** Nothing under here is reachable without a signed URL minted by this module. */
48
+ /**
49
+ * Everything this feature writes lives under here.
50
+ *
51
+ * 🛑 The one-line claim this used to carry — "nothing under here is reachable
52
+ * without a signed URL minted by this module" — was FALSE, in the same way the
53
+ * identical claim on `DIGITAL_CONTENT_PREFIX` was: `/media/[...slug]`'s
54
+ * raw-path fallback resolved any multi-segment slug as a literal storage path,
55
+ * so `/media/private/sticker-sheets/...` served these objects, watermarked, to
56
+ * anyone. Unlike digital-content's `randomUUID()` names, a part is written as
57
+ * `part-003.png` — guessable on the first try.
58
+ *
59
+ * The proxy now refuses a raw `private/` path outright. What is true is
60
+ * narrower than the old sentence and worth stating precisely: **no public URL
61
+ * exists for one of these objects unless something deliberately mints a
62
+ * `mediaAssets` row for it.** A row is what creates a `/media/{shortId}` slug,
63
+ * and a resolved row is intentionally still allowed through the proxy — that is
64
+ * how a promoted part gets a watermarked marketplace copy with no byte
65
+ * duplicated. The unwatermarked printable original has no row and is reachable
66
+ * only by a signed URL from this module.
67
+ */
49
68
  export const SCAN_ROOT = "private/sticker-sheets";
50
69
  /** How long a read URL lives. Long enough to fetch, short enough to be useless if leaked. */
51
70
  const READ_TTL_MS = 5 * 60 * 1000;
@@ -27,7 +27,7 @@ import type { StoreDetail, StoreListItem } from "../../../../features/stores/typ
27
27
  */
28
28
  export declare const PUBLIC_STORE_FIELDS: readonly ["id", "storeSlug", "ownerId", "storeName", "storeDescription", "storeCategory", "storeLogoURL", "storeBannerURL", "status", "isPublic", "stats", "bio", "location", "website", "socialLinks", "returnPolicy", "shippingPolicy", "isVacationMode", "vacationMessage", "supportsShippingProtection", "supportsSaleProtection", "createdAt"];
29
29
  /** Everything else, with the reason it stays server-side. */
30
- export declare const PRIVATE_STORE_FIELDS: readonly ["searchTxt", "updatedAt", "whatsappConfig", "adminNotes", "statusHistory", "statusHistoryTruncated", "isVerified", "suspensionReason", "capabilities", "customCommissionRate", "googleReviews", "shippingConfig", "emiEnabled", "pricesIncludeGst", "shippingPricing", "returnsDirectToSeller", "isFeatured", "vacationReturnDate", "isTestData", "testDataExpiresAt"];
30
+ export declare const PRIVATE_STORE_FIELDS: readonly ["searchTxt", "updatedAt", "whatsappConfig", "adminNotes", "statusHistory", "statusHistoryTruncated", "isVerified", "suspensionReason", "capabilities", "customCommissionRate", "googleReviews", "shippingConfig", "emiEnabled", "pricesIncludeGst", "shippingPricing", "returnsDirectToSeller", "isFeatured", "vacationReturnDate", "isTestData", "testDataExpiresAt", "gstin", "legalName", "registeredAddress"];
31
31
  /**
32
32
  * Accepted input: a `StoreDocument` (the common case) or a loosely-typed
33
33
  * Firestore row. The legacy top-level counters below predate `stats` and are
@@ -91,6 +91,22 @@ export const PRIVATE_STORE_FIELDS = [
91
91
  "vacationReturnDate", // not rendered by StoreAboutView; add to PUBLIC when a reader exists
92
92
  "isTestData", // tester-sandbox tagging; filtered by filterTestDataForViewer
93
93
  "testDataExpiresAt", // ditto
94
+ /*
95
+ * Tax identity (2026-10-19). Read today by exactly two server-side
96
+ * consumers: `resolveSupplierIdentity` (checkout, to decide whose
97
+ * registration an order invoices under) and the seller's own authenticated
98
+ * payout-settings form. Neither is a public store-profile reader.
99
+ *
100
+ * A GSTIN is not secret in the way a bank account number is — it is
101
+ * legally published on an invoice — but "may be disclosed" and "has a
102
+ * reader that needs it published on /stores/{slug}" are different
103
+ * questions, and only the first is true today. Move to PUBLIC_STORE_FIELDS
104
+ * together, with the literal in `toStoreListItem()`, once a storefront
105
+ * surface actually needs to show a seller's registration.
106
+ */
107
+ "gstin",
108
+ "legalName",
109
+ "registeredAddress",
94
110
  ];
95
111
  /** `createdAt` is `Date` on the document and `string` on the client types. */
96
112
  function toIsoString(value) {
@@ -3,3 +3,4 @@ export { STORES_PAGE_SIZE, STORES_PRODUCTS_PAGE_SIZE, STORES_SITEMAP_LIMIT, STOR
3
3
  export { renderStoreOgImage, renderStoreOg, type StoreOgData } from "./og";
4
4
  export { resolveStoreFields, resolveStoreFieldsMany, type ProductStoreFields, } from "./denormalize";
5
5
  export { toStoreDetail, toStoreListItem, PUBLIC_STORE_FIELDS, PRIVATE_STORE_FIELDS, type ToStoreDetailOptions, type ToStoreListItemOptions, type StoreProjectionSource, } from "./adapters";
6
+ export { resolveStoreTabs, getDefaultStoreTabPath, type ResolvedStoreTabs, type StoreTabLink, } from "./tabs";
@@ -3,3 +3,4 @@ export { STORES_PAGE_SIZE, STORES_PRODUCTS_PAGE_SIZE, STORES_SITEMAP_LIMIT, STOR
3
3
  export { renderStoreOgImage, renderStoreOg } from "./og.js";
4
4
  export { resolveStoreFields, resolveStoreFieldsMany, } from "./denormalize.js";
5
5
  export { toStoreDetail, toStoreListItem, PUBLIC_STORE_FIELDS, PRIVATE_STORE_FIELDS, } from "./adapters.js";
6
+ export { resolveStoreTabs, getDefaultStoreTabPath, } from "./tabs.js";