@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.
- package/dist/.build-stamp.json +3 -3
- package/dist/_internal/client/scaffolds/AppShell.js +1 -1
- package/dist/_internal/client/theme/ThemeProvider.js +15 -1
- package/dist/_internal/server/features/auctions/og.d.ts +3 -1
- package/dist/_internal/server/features/auctions/og.js +3 -1
- package/dist/_internal/server/features/bundles/actions.js +1 -1
- package/dist/_internal/server/features/business-documents/download.d.ts +41 -0
- package/dist/_internal/server/features/business-documents/download.js +91 -0
- package/dist/_internal/server/features/categories/adapters.d.ts +1 -1
- package/dist/_internal/server/features/categories/adapters.js +10 -0
- package/dist/_internal/server/features/categories/price-guidance-resolve.d.ts +66 -0
- package/dist/_internal/server/features/categories/price-guidance-resolve.js +138 -0
- package/dist/_internal/server/features/checkout/actions.d.ts +14 -0
- package/dist/_internal/server/features/checkout/actions.js +106 -12
- package/dist/_internal/server/features/checkout/bundle-expansion.js +17 -0
- package/dist/_internal/server/features/checkout/data.d.ts +22 -0
- package/dist/_internal/server/features/checkout/group-rule-revalidation.d.ts +34 -0
- package/dist/_internal/server/features/checkout/group-rule-revalidation.js +124 -0
- package/dist/_internal/server/features/checkout/supplier-identity.d.ts +55 -0
- package/dist/_internal/server/features/checkout/supplier-identity.js +81 -0
- package/dist/_internal/server/features/classified/metadata.js +1 -1
- package/dist/_internal/server/features/classified/og.d.ts +3 -1
- package/dist/_internal/server/features/classified/og.js +3 -1
- package/dist/_internal/server/features/digital-code/metadata.js +1 -1
- package/dist/_internal/server/features/digital-code/og.d.ts +3 -1
- package/dist/_internal/server/features/digital-code/og.js +3 -1
- package/dist/_internal/server/features/digital-code/pool.d.ts +19 -4
- package/dist/_internal/server/features/digital-code/pool.js +19 -4
- package/dist/_internal/server/features/live/metadata.js +1 -1
- package/dist/_internal/server/features/live/og.d.ts +3 -1
- package/dist/_internal/server/features/live/og.js +3 -1
- package/dist/_internal/server/features/messages/actions.js +1 -1
- package/dist/_internal/server/features/parts/og.js +1 -1
- package/dist/_internal/server/features/pre-orders/og.d.ts +3 -1
- package/dist/_internal/server/features/pre-orders/og.js +3 -1
- package/dist/_internal/server/features/price-index/market.d.ts +57 -0
- package/dist/_internal/server/features/price-index/market.js +120 -0
- package/dist/_internal/server/features/price-index/search.d.ts +18 -0
- package/dist/_internal/server/features/price-index/search.js +47 -7
- package/dist/_internal/server/features/prize-draws/og.d.ts +3 -1
- package/dist/_internal/server/features/prize-draws/og.js +3 -1
- package/dist/_internal/server/features/products/group-members.d.ts +26 -0
- package/dist/_internal/server/features/products/group-members.js +7 -1
- package/dist/_internal/server/features/products/list-public.d.ts +12 -0
- package/dist/_internal/server/features/products/list-public.js +10 -1
- package/dist/_internal/server/features/products/listing-tab-counts.d.ts +25 -1
- package/dist/_internal/server/features/products/listing-tab-counts.js +128 -10
- package/dist/_internal/server/features/products/og.d.ts +3 -1
- package/dist/_internal/server/features/products/og.js +3 -1
- package/dist/_internal/server/features/search/scoped-sources.d.ts +26 -0
- package/dist/_internal/server/features/search/scoped-sources.js +252 -0
- package/dist/_internal/server/features/search/search-everything.d.ts +57 -0
- package/dist/_internal/server/features/search/search-everything.js +70 -20
- package/dist/_internal/server/features/seo/sitemap.js +6 -0
- package/dist/_internal/server/features/shared/listing-adapters.js +4 -2
- package/dist/_internal/server/features/sticker-trace/adapters.d.ts +11 -0
- package/dist/_internal/server/features/sticker-trace/adapters.js +1 -0
- package/dist/_internal/server/features/sticker-trace/promote-to-listings.d.ts +26 -0
- package/dist/_internal/server/features/sticker-trace/promote-to-listings.js +264 -0
- package/dist/_internal/server/features/sticker-trace/sheet-actions.d.ts +3 -1
- package/dist/_internal/server/features/sticker-trace/sheet-actions.js +32 -0
- package/dist/_internal/server/features/sticker-trace/storage.d.ts +20 -1
- package/dist/_internal/server/features/sticker-trace/storage.js +20 -1
- package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
- package/dist/_internal/server/features/stores/adapters.js +16 -0
- package/dist/_internal/server/features/stores/index.d.ts +1 -0
- package/dist/_internal/server/features/stores/index.js +1 -0
- package/dist/_internal/server/features/stores/tabs.d.ts +30 -0
- package/dist/_internal/server/features/stores/tabs.js +124 -0
- package/dist/_internal/shared/checkout/rules/_defaults.js +0 -2
- package/dist/_internal/shared/checkout/rules/_limits.d.ts +14 -1
- package/dist/_internal/shared/checkout/rules/_limits.js +14 -1
- package/dist/_internal/shared/checkout/rules/_registry.js +2 -0
- package/dist/_internal/shared/checkout/rules/auction.rule.js +9 -1
- package/dist/_internal/shared/checkout/rules/bundle.rule.js +13 -0
- package/dist/_internal/shared/checkout/rules/index.d.ts +2 -0
- package/dist/_internal/shared/checkout/rules/index.js +9 -0
- package/dist/_internal/shared/checkout/rules/offer.rule.js +0 -1
- package/dist/_internal/shared/checkout/rules/payment-gate.d.ts +26 -0
- package/dist/_internal/shared/checkout/rules/payment-gate.js +133 -0
- package/dist/_internal/shared/checkout/rules/prize-draw.rule.js +29 -8
- package/dist/_internal/shared/checkout/rules/spares.rule.d.ts +16 -0
- package/dist/_internal/shared/checkout/rules/spares.rule.js +5 -0
- package/dist/_internal/shared/checkout/rules/types.d.ts +26 -2
- package/dist/_internal/shared/config/schema.d.ts +122 -0
- package/dist/_internal/shared/features/cart/schema.js +3 -2
- package/dist/_internal/shared/features/products/group-rules.d.ts +75 -0
- package/dist/_internal/shared/features/products/group-rules.js +142 -0
- package/dist/_internal/shared/listing-types/_registry.d.ts +24 -0
- package/dist/_internal/shared/listing-types/_registry.js +39 -1
- package/dist/_internal/shared/listing-types/art/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/art/config.js +2 -0
- package/dist/_internal/shared/listing-types/auction/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/auction/config.js +2 -0
- package/dist/_internal/shared/listing-types/availability.js +48 -0
- package/dist/_internal/shared/listing-types/capabilities.js +19 -0
- package/dist/_internal/shared/listing-types/classified/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/classified/config.js +2 -0
- package/dist/_internal/shared/listing-types/digital-code/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/digital-code/config.js +2 -0
- package/dist/_internal/shared/listing-types/feature-flags.js +1 -0
- package/dist/_internal/shared/listing-types/live/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/live/config.js +2 -0
- package/dist/_internal/shared/listing-types/pre-order/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/pre-order/config.js +2 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.js +2 -0
- package/dist/_internal/shared/listing-types/spares/config.d.ts +74 -0
- package/dist/_internal/shared/listing-types/spares/config.js +60 -0
- package/dist/_internal/shared/listing-types/spares/ctas.d.ts +1 -0
- package/dist/_internal/shared/listing-types/spares/ctas.js +4 -0
- package/dist/_internal/shared/listing-types/spares/og.js +1 -0
- package/dist/_internal/shared/listing-types/spares/schema.d.ts +2 -0
- package/dist/_internal/shared/listing-types/spares/schema.js +3 -0
- package/dist/_internal/shared/listing-types/spares/seed-factory.d.ts +1 -0
- package/dist/_internal/shared/listing-types/spares/seed-factory.js +1 -0
- package/dist/_internal/shared/listing-types/standard/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/standard/config.js +2 -0
- package/dist/_internal/shared/listing-types/stickers/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/stickers/config.js +2 -0
- package/dist/_internal/shared/search/match.d.ts +11 -0
- package/dist/_internal/shared/search/match.js +30 -3
- package/dist/_internal/shared/search/to-groups.d.ts +52 -0
- package/dist/_internal/shared/search/to-groups.js +141 -0
- package/dist/client.d.ts +8 -2
- package/dist/client.js +4 -1
- package/dist/constants/api-endpoints.d.ts +46 -15
- package/dist/constants/api-endpoints.js +35 -5
- package/dist/constants/cache-invalidation.js +3 -1
- package/dist/constants/field-names.d.ts +6 -0
- package/dist/constants/field-names.js +6 -0
- package/dist/features/about/components/PolicyPageView.d.ts +22 -2
- package/dist/features/about/components/PolicyPageView.js +14 -2
- package/dist/features/about/components/PublicProfileView.js +2 -1
- package/dist/features/about/components/ShippingPolicyView.js +42 -2
- package/dist/features/account/components/NotificationBell.js +1 -1
- package/dist/features/account/hooks/useProfileStats.js +5 -1
- package/dist/features/addresses/repository/addresses.repository.d.ts +16 -0
- package/dist/features/addresses/repository/addresses.repository.js +21 -0
- package/dist/features/admin/actions/admin-actions.js +16 -1
- package/dist/features/admin/components/AdminCategoryEditorView.js +19 -2
- package/dist/features/admin/components/AdminNavigationView.js +1 -1
- package/dist/features/admin/components/AdminPageHeader.js +1 -1
- package/dist/features/admin/components/AdminProductEditorView.js +17 -2
- package/dist/features/admin/components/AdminSidebar.js +1 -1
- package/dist/features/admin/components/AdminSiteSettingsView.js +39 -8
- package/dist/features/admin/components/AdminSparesView.d.ts +12 -0
- package/dist/features/admin/components/AdminSparesView.js +30 -0
- package/dist/features/admin/components/AdminUsersView.js +9 -7
- package/dist/features/admin/components/index.d.ts +2 -0
- package/dist/features/admin/components/index.js +1 -0
- package/dist/features/admin/components/sections/adminSectionsTypes.js +7 -0
- package/dist/features/admin/constants/filter-tabs.d.ts +3 -0
- package/dist/features/admin/constants/filter-tabs.js +1 -0
- package/dist/features/admin/schemas/firestore.d.ts +26 -2
- package/dist/features/auctions/components/AuctionDetailPageView.js +12 -18
- package/dist/features/auth/repository/user.repository.d.ts +13 -0
- package/dist/features/auth/repository/user.repository.js +28 -0
- package/dist/features/auth/schemas/firestore.d.ts +2 -0
- package/dist/features/auth/schemas/firestore.js +2 -0
- package/dist/features/business-documents/repository/business-documents.repository.d.ts +58 -0
- package/dist/features/business-documents/repository/business-documents.repository.js +54 -0
- package/dist/features/business-documents/schemas/firestore.d.ts +105 -0
- package/dist/features/business-documents/schemas/firestore.js +75 -0
- package/dist/features/cart/actions/cart-actions.js +83 -6
- package/dist/features/cart/repository/cart.repository.js +32 -4
- package/dist/features/cart/schemas/firestore.d.ts +20 -0
- package/dist/features/cart/schemas/index.d.ts +2 -2
- package/dist/features/catalogue/components/PublicCatalogueItemDetailView.js +1 -1
- package/dist/features/categories/api/[id]/price-guidance/route.d.ts +42 -0
- package/dist/features/categories/api/[id]/price-guidance/route.js +90 -0
- package/dist/features/categories/components/BrandDetailPageView.js +32 -8
- package/dist/features/categories/components/BrandDetailTabs.d.ts +7 -1
- package/dist/features/categories/components/BrandDetailTabs.js +28 -3
- package/dist/features/categories/components/BundleCollage.js +3 -2
- package/dist/features/categories/components/BundleItemsPicker.js +3 -4
- package/dist/features/categories/components/CategoriesIndexListing.js +23 -8
- package/dist/features/categories/components/CategoryDetailPageView.js +157 -20
- package/dist/features/categories/components/CategoryDetailTabs.d.ts +7 -1
- package/dist/features/categories/components/CategoryDetailTabs.js +41 -7
- package/dist/features/categories/components/CategoryGrid.js +10 -1
- package/dist/features/categories/components/PriceGuidanceHint.d.ts +29 -0
- package/dist/features/categories/components/PriceGuidanceHint.js +61 -0
- package/dist/features/categories/components/SubcategoryCardRow.d.ts +27 -0
- package/dist/features/categories/components/SubcategoryCardRow.js +98 -0
- package/dist/features/categories/constants/category-tabs.d.ts +20 -25
- package/dist/features/categories/constants/category-tabs.js +22 -39
- package/dist/features/categories/repository/categories.repository.d.ts +12 -0
- package/dist/features/categories/repository/categories.repository.js +20 -0
- package/dist/features/categories/schemas/firestore.d.ts +22 -1
- package/dist/features/categories/schemas/firestore.js +10 -0
- package/dist/features/categories/schemas/index.d.ts +6 -0
- package/dist/features/categories/schemas/index.js +2 -0
- package/dist/features/categories/types/index.d.ts +8 -0
- package/dist/features/categories/utils/taxonomy-visual.d.ts +16 -3
- package/dist/features/categories/utils/taxonomy-visual.js +26 -10
- package/dist/features/classified/components/ClassifiedDetailPageView.js +14 -10
- package/dist/features/contact/components/ContactPageView.d.ts +33 -1
- package/dist/features/contact/components/ContactPageView.js +58 -28
- package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +14 -10
- package/dist/features/grouped/components/GroupedListingDetailView.js +9 -1
- package/dist/features/grouped/schemas/firestore.d.ts +5 -0
- package/dist/features/homepage/actions/homepage-section-actions.d.ts +3 -3
- package/dist/features/homepage/components/CollectionCardsSection.d.ts +28 -6
- package/dist/features/homepage/components/CollectionCardsSection.js +43 -8
- package/dist/features/homepage/components/HowItWorksSection.d.ts +23 -6
- package/dist/features/homepage/components/HowItWorksSection.js +4 -4
- package/dist/features/homepage/components/PromoGrid.d.ts +31 -2
- package/dist/features/homepage/components/PromoGrid.js +12 -3
- package/dist/features/homepage/components/TestimonialsCarousel.d.ts +34 -2
- package/dist/features/homepage/components/TestimonialsCarousel.js +1 -1
- package/dist/features/homepage/components/index.d.ts +0 -20
- package/dist/features/homepage/components/index.js +0 -11
- package/dist/features/homepage/index.d.ts +0 -2
- package/dist/features/homepage/index.js +0 -2
- package/dist/features/homepage/lib/section-renderer.d.ts +9 -0
- package/dist/features/homepage/lib/section-renderer.js +202 -58
- package/dist/features/homepage/repository/homepage-sections.repository.d.ts +35 -3
- package/dist/features/homepage/repository/homepage-sections.repository.js +58 -8
- package/dist/features/homepage/schemas/firestore.d.ts +106 -3
- package/dist/features/homepage/schemas/firestore.js +32 -2
- package/dist/features/homepage/schemas/index.d.ts +36 -7
- package/dist/features/homepage/schemas/index.js +39 -27
- package/dist/features/homepage/server.d.ts +0 -3
- package/dist/features/homepage/server.js +0 -3
- package/dist/features/homepage/types/index.d.ts +0 -47
- package/dist/features/homepage/types/index.js +49 -0
- package/dist/features/layout/AppLayoutShell.js +1 -1
- package/dist/features/layout/BottomNavItem.js +1 -1
- package/dist/features/layout/BottomSheet.js +1 -1
- package/dist/features/layout/TitleBarLayout.js +1 -1
- package/dist/features/live/components/LiveItemDetailPageView.js +14 -10
- package/dist/features/media/hooks/useMedia.js +17 -0
- package/dist/features/media/repository/media-assets.repository.d.ts +46 -0
- package/dist/features/media/repository/media-assets.repository.js +31 -0
- package/dist/features/media/trust-signals.d.ts +85 -0
- package/dist/features/media/trust-signals.js +126 -0
- package/dist/features/parts/components/PartDetailPageView.d.ts +14 -1
- package/dist/features/parts/components/PartDetailPageView.js +2 -2
- package/dist/features/parts/components/PartsIndexPageView.d.ts +11 -2
- package/dist/features/parts/components/PartsIndexPageView.js +2 -2
- package/dist/features/parts/components/PartsSearchBox.d.ts +9 -0
- package/dist/features/parts/components/PartsSearchBox.js +57 -0
- package/dist/features/parts/schemas/firestore.d.ts +149 -3
- package/dist/features/parts/schemas/firestore.js +184 -0
- package/dist/features/payments/repository/payout.repository.d.ts +15 -0
- package/dist/features/payments/repository/payout.repository.js +21 -0
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +12 -18
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +1 -1
- package/dist/features/price-index/components/PriceIndexToolView.d.ts +24 -1
- package/dist/features/price-index/components/PriceIndexToolView.js +299 -17
- package/dist/features/price-index/lookup-target.d.ts +48 -0
- package/dist/features/price-index/lookup-target.js +107 -0
- package/dist/features/products/components/AvailabilityScopeHint.d.ts +22 -0
- package/dist/features/products/components/AvailabilityScopeHint.js +27 -0
- package/dist/features/products/components/CompareOverlay.d.ts +20 -2
- package/dist/features/products/components/CompareOverlay.js +23 -3
- package/dist/features/products/components/CustomSectionTabContent.d.ts +31 -0
- package/dist/features/products/components/CustomSectionTabContent.js +29 -3
- package/dist/features/products/components/GroupMemberPicker.d.ts +19 -1
- package/dist/features/products/components/GroupMemberPicker.js +46 -8
- package/dist/features/products/components/GroupSettingsPanel.d.ts +11 -1
- package/dist/features/products/components/GroupSettingsPanel.js +92 -3
- package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
- package/dist/features/products/components/PrizeDrawEntryActions.d.ts +15 -1
- package/dist/features/products/components/PrizeDrawEntryActions.js +70 -8
- package/dist/features/products/components/ProductDetailPageView.js +81 -17
- package/dist/features/products/components/ProductForm.d.ts +13 -1
- package/dist/features/products/components/ProductForm.js +16 -2
- package/dist/features/products/components/ProductsIndexListing.d.ts +16 -1
- package/dist/features/products/components/ProductsIndexListing.js +3 -3
- package/dist/features/products/components/ShowGroupSection.js +15 -2
- package/dist/features/products/components/SparesListView.d.ts +7 -0
- package/dist/features/products/components/SparesListView.js +39 -0
- package/dist/features/products/components/SublistingCarouselSection.js +2 -1
- package/dist/features/products/constants/action-defs.d.ts +1 -0
- package/dist/features/products/constants/action-defs.js +1 -0
- package/dist/features/products/repository/products.repository.d.ts +99 -0
- package/dist/features/products/repository/products.repository.js +240 -18
- package/dist/features/products/schemas/firestore.d.ts +97 -4
- package/dist/features/products/schemas/firestore.js +104 -0
- package/dist/features/products/schemas/index.d.ts +10 -19
- package/dist/features/products/schemas/index.js +23 -6
- package/dist/features/products/types/index.d.ts +45 -1
- package/dist/features/products/utils/group-member-image.d.ts +46 -0
- package/dist/features/products/utils/group-member-image.js +4 -0
- package/dist/features/products/utils/listing-badge-variant.js +4 -0
- package/dist/features/reviews/components/ReviewsList.js +1 -1
- package/dist/features/scams/components/ScamProfileView.js +1 -1
- package/dist/features/search/components/EntityTypeahead.d.ts +44 -0
- package/dist/features/search/components/EntityTypeahead.js +92 -0
- package/dist/features/search/components/Search.d.ts +12 -1
- package/dist/features/search/components/Search.js +47 -3
- package/dist/features/search/components/SearchFacetRail.d.ts +11 -0
- package/dist/features/search/components/SearchFacetRail.js +92 -0
- package/dist/features/search/components/SearchResultsClient.d.ts +33 -0
- package/dist/features/search/components/SearchResultsClient.js +103 -0
- package/dist/features/search/components/SearchResultsPageView.d.ts +15 -1
- package/dist/features/search/components/SearchResultsPageView.js +2 -2
- package/dist/features/search/components/index.d.ts +0 -6
- package/dist/features/search/components/index.js +0 -3
- package/dist/features/search/hooks/useNavSuggestions.d.ts +13 -0
- package/dist/features/search/hooks/useScopedSearch.d.ts +34 -0
- package/dist/features/search/hooks/useScopedSearch.js +114 -0
- package/dist/features/search/hooks/useSiteSearch.d.ts +26 -0
- package/dist/features/search/hooks/useSiteSearch.js +149 -0
- package/dist/features/search/index.d.ts +0 -4
- package/dist/features/search/index.js +0 -4
- package/dist/features/search/manifest.d.ts +12 -0
- package/dist/features/search/manifest.js +17 -2
- package/dist/features/search/server.d.ts +0 -3
- package/dist/features/search/server.js +0 -3
- package/dist/features/seller/components/SellerListingTypeView.d.ts +1 -1
- package/dist/features/seller/components/SellerListingTypeView.js +20 -0
- package/dist/features/seller/components/SellerPayoutSettingsView.js +35 -14
- package/dist/features/seller/components/SellerProductShell.d.ts +1 -1
- package/dist/features/seller/components/SellerProductShell.js +2 -1
- package/dist/features/seller/components/SellerProductsView.js +3 -2
- package/dist/features/seller/components/SellerSidebar.js +1 -1
- package/dist/features/seller/components/SellerSparesView.d.ts +17 -0
- package/dist/features/seller/components/SellerSparesView.js +6 -0
- package/dist/features/seller/components/index.d.ts +2 -0
- package/dist/features/seller/components/index.js +1 -0
- package/dist/features/shell/CommandPalette.js +1 -1
- package/dist/features/sticker-trace/schemas/firestore.d.ts +19 -0
- package/dist/features/stores/components/StoreAuctionsListing.js +2 -1
- package/dist/features/stores/components/StoreDetailLayoutView.d.ts +1 -1
- package/dist/features/stores/components/StoreDetailLayoutView.js +7 -82
- package/dist/features/stores/components/StoreHeader.js +1 -2
- package/dist/features/stores/components/StoreSparesPageView.d.ts +27 -0
- package/dist/features/stores/components/StoreSparesPageView.js +48 -0
- package/dist/features/stores/schemas/firestore.d.ts +19 -0
- package/dist/features/tester/seed-data/authored/buying__spares-and-groups.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/buying__spares-and-groups.js +96 -0
- package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.js +392 -45
- package/dist/features/tester/seed-data/authored/content-discovery__category-tabs-franchise-bundles.js +54 -32
- package/dist/features/tester/seed-data/authored/content-discovery__parts-database.js +3 -2
- package/dist/features/tester/seed-data/authored/content-discovery__price-index-and-compare.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/content-discovery__price-index-and-compare.js +73 -0
- package/dist/features/tester/seed-data/authored/content-discovery__site-wide-index.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/content-discovery__site-wide-index.js +340 -0
- package/dist/features/tester/seed-data/authored/index.js +16 -0
- package/dist/features/tester/seed-data/authored/search-and-nav__header-search.d.ts +1 -0
- package/dist/features/tester/seed-data/authored/search-and-nav__header-search.js +57 -0
- package/dist/features/tester/seed-data/authored/selling__spares-and-group-editing.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__spares-and-group-editing.js +77 -0
- package/dist/features/tester/seed-data/authored/tools__sticker-promote-to-listings.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/tools__sticker-promote-to-listings.js +58 -0
- package/dist/features/tester/seed-data/authored/tools__sticker-tracer.js +5 -4
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +384 -18
- package/dist/index.d.ts +17 -57
- package/dist/index.js +35 -96
- package/dist/next/routing/route-map.d.ts +42 -1
- package/dist/next/routing/route-map.js +45 -0
- package/dist/providers/db-firebase/base.repository.d.ts +22 -0
- package/dist/providers/db-firebase/base.repository.js +36 -0
- package/dist/schemas/registry.d.ts +15 -6
- package/dist/security/blind-index-tokens.d.ts +54 -0
- package/dist/security/blind-index-tokens.js +152 -0
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/settings-encryption.d.ts +35 -0
- package/dist/security/settings-encryption.js +91 -0
- package/dist/seed/_helpers/brand-rows.js +38 -14
- package/dist/seed/_helpers/category-forest.js +73 -7
- package/dist/seed/_helpers/category-images-generated.d.ts +25 -6
- package/dist/seed/_helpers/category-images-generated.js +81 -6
- package/dist/seed/_helpers/category-models.d.ts +1 -1
- package/dist/seed/_helpers/category-models.js +7 -1
- package/dist/seed/_helpers/category-parts-glossary-map.js +8 -0
- package/dist/seed/_helpers/category-parts.d.ts +1 -1
- package/dist/seed/_helpers/category-parts.js +50 -2
- package/dist/seed/_helpers/character-features.d.ts +1 -1
- package/dist/seed/_helpers/character-features.js +2 -1
- package/dist/seed/_helpers/franchise-rows.js +10 -2
- package/dist/seed/_helpers/media.d.ts +47 -0
- package/dist/seed/_helpers/media.js +34 -0
- package/dist/seed/authenticity-knowledge-seed-data.d.ts +49 -5
- package/dist/seed/authenticity-knowledge-seed-data.js +10001 -549
- package/dist/seed/categories-seed-data.js +124 -20
- package/dist/seed/grouped-listings-seed-data.js +16 -0
- package/dist/seed/homepage-sections-seed-data.js +94 -5
- package/dist/seed/index.d.ts +2 -3
- package/dist/seed/index.js +2 -2
- package/dist/seed/manifest.js +3 -0
- package/dist/seed/media-assets-seed-data.d.ts +1 -1
- package/dist/seed/media-assets-seed-data.js +746 -196
- package/dist/seed/model-composition-seed-data.js +122 -6
- package/dist/seed/part-glossary-seed-data.d.ts +1 -1
- package/dist/seed/part-glossary-seed-data.js +37219 -463
- package/dist/seed/products-art-seed-data.js +7 -18
- package/dist/seed/products-auctions-seed-data.js +56 -47
- package/dist/seed/products-beyblade-x-seed-data.d.ts +2 -0
- package/dist/seed/products-beyblade-x-seed-data.js +476 -0
- package/dist/seed/products-catalogue-seed-data.d.ts +5 -5
- package/dist/seed/products-catalogue-seed-data.js +1500 -1240
- package/dist/seed/products-classifieds-seed-data.js +9 -27
- package/dist/seed/products-digital-codes-seed-data.js +9 -18
- package/dist/seed/products-live-items-seed-data.js +5 -13
- package/dist/seed/products-preorders-seed-data.js +67 -33
- package/dist/seed/products-prize-draws-seed-data.js +8 -17
- package/dist/seed/products-spares-seed-data.d.ts +2 -0
- package/dist/seed/products-spares-seed-data.js +368 -0
- package/dist/seed/products-standard-seed-data.js +39 -126
- package/dist/seed/products-stickers-seed-data.js +59 -15
- package/dist/seed/site-settings-seed-data.js +8 -0
- package/dist/seo/json-ld.d.ts +25 -0
- package/dist/seo/json-ld.js +28 -0
- package/dist/server-entry.d.ts +3 -1
- package/dist/server-entry.js +2 -2
- package/dist/server.d.ts +11 -9
- package/dist/server.js +35 -25
- package/dist/styles.css +202 -1
- package/dist/tailwind-input.css +31 -17
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/Accordion.js +1 -1
- package/dist/ui/components/Breadcrumb.js +1 -1
- package/dist/ui/components/DetailPageGallery.js +0 -2
- package/dist/ui/components/DetailPageHero.js +0 -1
- package/dist/ui/components/DetailPageTabs.js +1 -1
- package/dist/ui/components/Divider.js +3 -3
- package/dist/ui/components/GatedPrice.js +5 -1
- package/dist/ui/components/HorizontalScroller.js +1 -1
- package/dist/ui/components/ListingLayout.js +3 -3
- package/dist/ui/components/MediaTabs.style.css +81 -0
- package/dist/ui/components/Pagination.js +1 -1
- package/dist/ui/components/PendingValue.d.ts +45 -0
- package/dist/ui/components/PendingValue.js +7 -0
- package/dist/ui/components/Radio.style.css +119 -0
- package/dist/ui/components/RatingDisplay.js +2 -2
- package/dist/ui/components/SettingsSection.js +0 -2
- package/dist/ui/components/SiteLogo.js +3 -0
- package/dist/ui/components/SiteMark.js +3 -0
- package/dist/ui/components/SortDropdown.js +1 -1
- package/dist/ui/components/SpecTable.d.ts +58 -0
- package/dist/ui/components/SpecTable.js +8 -0
- package/dist/ui/components/StarRating.js +1 -3
- package/dist/ui/components/SummaryCard.js +1 -1
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/icons/icon-registry.d.ts +1 -0
- package/dist/ui/icons/icon-registry.js +9 -1
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.js +2 -0
- package/dist/utils/search-txt-query.d.ts +12 -0
- package/dist/utils/search-txt-query.js +12 -0
- package/package.json +1 -1
- package/scripts/dedupe-peer-deps.mjs +18 -0
- package/scripts/seed-cli.mjs +250 -35
- package/scripts/verify-css-build.mjs +118 -37
- package/dist/_internal/server/features/search/actions.d.ts +0 -7
- package/dist/_internal/server/features/search/actions.js +0 -21
- package/dist/_internal/server/features/search/data.d.ts +0 -15
- package/dist/_internal/server/features/search/data.js +0 -18
- package/dist/_internal/server/features/search/index.d.ts +0 -3
- package/dist/_internal/server/features/search/index.js +0 -2
- package/dist/features/homepage/api/[id]/route.d.ts +0 -23
- package/dist/features/homepage/api/[id]/route.js +0 -103
- package/dist/features/homepage/api/route.d.ts +0 -14
- package/dist/features/homepage/api/route.js +0 -140
- package/dist/features/homepage/api/sections/[id]/route.d.ts +0 -26
- package/dist/features/homepage/api/sections/[id]/route.js +0 -95
- package/dist/features/homepage/api/sections/route.d.ts +0 -13
- package/dist/features/homepage/api/sections/route.js +0 -82
- package/dist/features/homepage/components/AdvertisementBanner.d.ts +0 -14
- package/dist/features/homepage/components/AdvertisementBanner.js +0 -35
- package/dist/features/homepage/components/BeforeAfterCard.d.ts +0 -9
- package/dist/features/homepage/components/BeforeAfterCard.js +0 -73
- package/dist/features/homepage/components/FeaturedResultsSection.d.ts +0 -17
- package/dist/features/homepage/components/FeaturedResultsSection.js +0 -17
- package/dist/features/homepage/components/HeroBanner.d.ts +0 -7
- package/dist/features/homepage/components/HeroBanner.js +0 -43
- package/dist/features/homepage/components/HeroSection.d.ts +0 -7
- package/dist/features/homepage/components/HeroSection.js +0 -6
- package/dist/features/homepage/components/HomepageSkeleton.d.ts +0 -2
- package/dist/features/homepage/components/HomepageSkeleton.js +0 -21
- package/dist/features/homepage/components/HomepageView.d.ts +0 -25
- package/dist/features/homepage/components/HomepageView.js +0 -5
- package/dist/features/homepage/components/NewsletterBanner.d.ts +0 -23
- package/dist/features/homepage/components/NewsletterBanner.js +0 -37
- package/dist/features/homepage/components/SiteFeaturesSection.d.ts +0 -14
- package/dist/features/homepage/components/SiteFeaturesSection.js +0 -11
- package/dist/features/homepage/components/TrustBadges.d.ts +0 -6
- package/dist/features/homepage/components/TrustBadges.js +0 -46
- package/dist/features/homepage/components/TrustIndicatorsSection.d.ts +0 -12
- package/dist/features/homepage/components/TrustIndicatorsSection.js +0 -11
- package/dist/features/homepage/hooks/useHomepage.d.ts +0 -11
- package/dist/features/homepage/hooks/useHomepage.js +0 -17
- package/dist/features/homepage/hooks/useHomepageSections.d.ts +0 -2
- package/dist/features/homepage/hooks/useHomepageSections.js +0 -10
- package/dist/features/homepage/repository/homepage.repository.d.ts +0 -11
- package/dist/features/homepage/repository/homepage.repository.js +0 -25
- package/dist/features/search/actions/index.d.ts +0 -1
- package/dist/features/search/actions/index.js +0 -1
- package/dist/features/search/actions/search-actions.d.ts +0 -18
- package/dist/features/search/actions/search-actions.js +0 -43
- package/dist/features/search/api/route.d.ts +0 -22
- package/dist/features/search/api/route.js +0 -128
- package/dist/features/search/columns/index.d.ts +0 -17
- package/dist/features/search/columns/index.js +0 -49
- package/dist/features/search/components/SearchFiltersRow.d.ts +0 -22
- package/dist/features/search/components/SearchFiltersRow.js +0 -21
- package/dist/features/search/components/SearchResultsSection.d.ts +0 -53
- package/dist/features/search/components/SearchResultsSection.js +0 -35
- package/dist/features/search/components/SearchView.d.ts +0 -27
- package/dist/features/search/components/SearchView.js +0 -8
- package/dist/features/search/hooks/useSearch.d.ts +0 -21
- package/dist/features/search/hooks/useSearch.js +0 -33
- package/dist/features/search/repository/search.repository.d.ts +0 -14
- package/dist/features/search/repository/search.repository.js +0 -59
- package/dist/features/search/schemas/index.d.ts +0 -70
- package/dist/features/search/schemas/index.js +0 -36
- package/dist/features/search/types/index.d.ts +0 -48
- package/dist/features/stores/components/StoreScopedSearch.d.ts +0 -17
- package/dist/features/stores/components/StoreScopedSearch.js +0 -27
- package/dist/seed/defaults/homepage-sections.d.ts +0 -2
- package/dist/seed/defaults/homepage-sections.js +0 -60
- package/dist/seed/factories/homepage-section.factory.d.ts +0 -10
- package/dist/seed/factories/homepage-section.factory.js +0 -13
- /package/dist/{features/search/types/index.js → _internal/shared/listing-types/spares/og.d.ts} +0 -0
|
@@ -26,10 +26,25 @@ import { PRODUCT_COLLECTION, PRODUCT_CODES_SUBCOLLECTION, } from "../../../../fe
|
|
|
26
26
|
/**
|
|
27
27
|
* Where a delivered asset's bytes live.
|
|
28
28
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
29
|
+
* No `mediaAssets` row is ever created for one — a row is what mints a
|
|
30
|
+
* `/media/{shortId}` slug, and that slug is served without authentication. No
|
|
31
|
+
* row, no slug, no public URL. That half was, and remains, true.
|
|
32
|
+
*
|
|
33
|
+
* 🛑 CORRECTED 2026-10-19. This block used to open with "deliberately OUTSIDE
|
|
34
|
+
* every prefix the public media proxy knows how to resolve", and that was
|
|
35
|
+
* FALSE from the day the proxy's raw-path fallback shipped: `/media/[...slug]`
|
|
36
|
+
* treats any multi-segment slug it cannot resolve through `mediaAssets` as a
|
|
37
|
+
* literal storage path, so `/media/private/digital-content/...` resolved and
|
|
38
|
+
* served. It did watermark, so no pristine original leaked — but a paid asset
|
|
39
|
+
* was reachable by anyone who guessed the object name, and the only thing
|
|
40
|
+
* standing in the way was `randomUUID()`.
|
|
41
|
+
*
|
|
42
|
+
* The proxy now refuses a raw path under `private/` outright
|
|
43
|
+
* (`PRIVATE_PREFIX` in `src/app/api/media/[...slug]/route.ts`), so the
|
|
44
|
+
* statement is true again — but it is true because of a check, not because of
|
|
45
|
+
* the prefix, and that distinction is the whole reason this comment was wrong
|
|
46
|
+
* for so long. An unenforced invariant written as a fact is worse than no
|
|
47
|
+
* comment: it is the reason nobody looked.
|
|
33
48
|
*/
|
|
34
49
|
export const DIGITAL_CONTENT_PREFIX = "private/digital-content";
|
|
35
50
|
/**
|
|
@@ -29,7 +29,7 @@ export function buildLiveItemMetadata(product, opts) {
|
|
|
29
29
|
description,
|
|
30
30
|
url: canonical,
|
|
31
31
|
type: "website",
|
|
32
|
-
images: image ? [{ url: image }] : undefined,
|
|
32
|
+
images: image ? [{ url: typeof image === "string" ? image : image.url }] : undefined,
|
|
33
33
|
},
|
|
34
34
|
twitter: {
|
|
35
35
|
card: image ? "summary_large_image" : "summary",
|
|
@@ -10,7 +10,9 @@ interface LiveItemDocLike {
|
|
|
10
10
|
price?: number | null;
|
|
11
11
|
currency?: string | null;
|
|
12
12
|
mainImage?: string | null;
|
|
13
|
-
images?: (
|
|
13
|
+
images?: ({
|
|
14
|
+
url: string;
|
|
15
|
+
} | string | null | undefined)[] | null;
|
|
14
16
|
liveItem?: {
|
|
15
17
|
species?: string;
|
|
16
18
|
} | null;
|
|
@@ -4,7 +4,9 @@ export function renderLiveItemOg(doc, opts) {
|
|
|
4
4
|
return renderLiveItemOgImage({
|
|
5
5
|
title: doc?.title ?? "Live Listing",
|
|
6
6
|
species: doc?.liveItem?.species ?? null,
|
|
7
|
-
imageUrl: resolveOgImageUrl(doc?.mainImage ||
|
|
7
|
+
imageUrl: resolveOgImageUrl(doc?.mainImage ||
|
|
8
|
+
(typeof doc?.images?.[0] === "string" ? doc.images[0] : doc?.images?.[0]?.url) ||
|
|
9
|
+
null, opts.baseUrl),
|
|
8
10
|
}, opts.siteName);
|
|
9
11
|
}
|
|
10
12
|
export function renderLiveItemOgImage(data, siteName) {
|
|
@@ -160,7 +160,7 @@ export async function sendMessage(input) {
|
|
|
160
160
|
productRef = {
|
|
161
161
|
productId: product.id,
|
|
162
162
|
title: product.title,
|
|
163
|
-
image: product.images?.[0],
|
|
163
|
+
image: product.images?.[0]?.url,
|
|
164
164
|
price: product.price,
|
|
165
165
|
// Without this the card cannot pick the right detail route — see the
|
|
166
166
|
// field's own comment on `MessageProductRef`.
|
|
@@ -58,7 +58,7 @@ export function renderPartOg(part, usedByCount, opts) {
|
|
|
58
58
|
/** One classification's card. */
|
|
59
59
|
export function renderPartClassificationOg(slot, count, opts) {
|
|
60
60
|
const label = PART_SLOT_LABEL[slot] ?? slot;
|
|
61
|
-
return shell("
|
|
61
|
+
return shell("Beyblade parts gallery", label,
|
|
62
62
|
// `count` comes from the same bounded read the page renders, so the number
|
|
63
63
|
// on the card is the number on the page. A separate count query would be a
|
|
64
64
|
// second source that could disagree.
|
|
@@ -8,7 +8,9 @@ interface PreOrderDocLike {
|
|
|
8
8
|
title?: string | null;
|
|
9
9
|
preOrderReleaseDate?: Date | string | null;
|
|
10
10
|
mainImage?: string | null;
|
|
11
|
-
images?: (
|
|
11
|
+
images?: ({
|
|
12
|
+
url: string;
|
|
13
|
+
} | string | null | undefined)[] | null;
|
|
12
14
|
}
|
|
13
15
|
/** High-level OG renderer — accepts the raw pre-order document from `getPreOrderForDetail`. */
|
|
14
16
|
export declare function renderPreOrderOg(doc: PreOrderDocLike | null | undefined, opts: {
|
|
@@ -12,7 +12,9 @@ export function renderPreOrderOg(doc, opts) {
|
|
|
12
12
|
return renderPreOrderOgImage({
|
|
13
13
|
title: doc?.title ?? "Pre-Order",
|
|
14
14
|
releaseDateLabel,
|
|
15
|
-
imageUrl: resolveOgImageUrl(doc?.mainImage ||
|
|
15
|
+
imageUrl: resolveOgImageUrl(doc?.mainImage ||
|
|
16
|
+
(typeof doc?.images?.[0] === "string" ? doc.images[0] : doc?.images?.[0]?.url) ||
|
|
17
|
+
null, opts.baseUrl),
|
|
16
18
|
}, opts.siteName);
|
|
17
19
|
}
|
|
18
20
|
export function renderPreOrderOgImage(data, siteName) {
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { PRICE_GUIDANCE_TAX_BASIS } from "../../../shared/features/categories/price-guidance";
|
|
2
|
+
export interface PriceIndexMarketRow {
|
|
3
|
+
categoryId: string;
|
|
4
|
+
/**
|
|
5
|
+
* The typical ASK, not a transaction.
|
|
6
|
+
*
|
|
7
|
+
* 🛑 `median` is the only required figure, and the three optional ones are
|
|
8
|
+
* optional because the crawl genuinely did not measure them everywhere — Hot
|
|
9
|
+
* Wheels has a median over n=7,418 and a RANGE, never quartiles, and the
|
|
10
|
+
* Beyblade-top table carries no per-leaf `n` at all. An absent field must
|
|
11
|
+
* render as absent; filling one in to make the shape look complete is the
|
|
12
|
+
* single thing `price-bands.md` warns about most.
|
|
13
|
+
*/
|
|
14
|
+
median: number;
|
|
15
|
+
p25?: number;
|
|
16
|
+
p75?: number;
|
|
17
|
+
sampleSize?: number;
|
|
18
|
+
asOf?: string;
|
|
19
|
+
/**
|
|
20
|
+
* The share of competitor listings at this level that are SOLD OUT (0.85,
|
|
21
|
+
* measured across the five largest catalogues).
|
|
22
|
+
*
|
|
23
|
+
* Carried because it is what stops the band being read as a clearing price:
|
|
24
|
+
* most asks at this level did not convert. The renderer must say so.
|
|
25
|
+
*/
|
|
26
|
+
soldOutShare?: number;
|
|
27
|
+
/** Which crawl, and any caveat — pooling, a disagreeing source, a small n. */
|
|
28
|
+
source?: string;
|
|
29
|
+
/**
|
|
30
|
+
* `true` when the band came from an ANCESTOR rather than this leaf.
|
|
31
|
+
*
|
|
32
|
+
* 🛑 Load-bearing, not decoration. `MARKET_BANDS` is keyed on era and part
|
|
33
|
+
* leaves, never on the ~216 named-model leaves, so a model lookup resolves
|
|
34
|
+
* its generation's band — "typical for Burst DB" is a true statement and
|
|
35
|
+
* "typical for Rage Longinus" is not. The renderer names the source leaf so
|
|
36
|
+
* the reader can tell which they are being told.
|
|
37
|
+
*/
|
|
38
|
+
inherited: boolean;
|
|
39
|
+
/** The ancestor the band actually came from, when `inherited`. */
|
|
40
|
+
viaCategoryId?: string;
|
|
41
|
+
viaName?: string;
|
|
42
|
+
/** The basis every figure above is on. Always inclusive; stated, not assumed. */
|
|
43
|
+
taxBasis: typeof PRICE_GUIDANCE_TAX_BASIS;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Market bands for the leaves the client already resolved.
|
|
47
|
+
*
|
|
48
|
+
* Bounded by `PRICE_INDEX_SEARCH_LIMIT` — the same cap the public half uses, so
|
|
49
|
+
* a crafted request cannot demand an unbounded read, and the two halves of one
|
|
50
|
+
* lookup can never disagree about how many rows a lookup covers.
|
|
51
|
+
*
|
|
52
|
+
* A leaf with no band at any level is ABSENT from the result rather than
|
|
53
|
+
* present with a null band. "We have no market evidence for this" and "this id
|
|
54
|
+
* does not exist" are different answers and the caller renders them
|
|
55
|
+
* differently; collapsing them is how a missing band becomes a confident ₹0.
|
|
56
|
+
*/
|
|
57
|
+
export declare function getPriceIndexMarketForIds(rawIds: readonly string[]): Promise<PriceIndexMarketRow[]>;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: The realised-price tool rendered "No completed sales recorded here yet"
|
|
3
|
+
* for every single lookup, because the only data it read was OUR completed
|
|
4
|
+
* sales and we have none for most leaves. Meanwhile the competitor crawl
|
|
5
|
+
* measured ~60 real bands (`MARKET_BANDS` in
|
|
6
|
+
* `seed/_helpers/category-content.ts`, evidence in
|
|
7
|
+
* `docs/research/competitor-crawl-2026-10/price-bands.md`) which were
|
|
8
|
+
* seeded onto the taxonomy and then read by exactly one surface — the
|
|
9
|
+
* seller form's hint. The tool had a starting point the whole time and
|
|
10
|
+
* never looked at it.
|
|
11
|
+
* WHAT: The MARKET half of the tool, resolved by the shared nearest-first
|
|
12
|
+
* ancestor walk. Signed-in only — see the gate note below.
|
|
13
|
+
*
|
|
14
|
+
* EXPORTS:
|
|
15
|
+
* PriceIndexMarketRow — one leaf's market band, API shape
|
|
16
|
+
* getPriceIndexMarketForIds — N category ids -> their market bands
|
|
17
|
+
*
|
|
18
|
+
* ── 🛑 WHY THIS IS A SEPARATE, AUTHENTICATED ENDPOINT ───────────────────────
|
|
19
|
+
*
|
|
20
|
+
* The sold half (`searchPriceIndex`, `getPriceIndexBandsForIds`) is PUBLIC and
|
|
21
|
+
* edge-cached, deliberately: a day-stale aggregate of our own completed sales
|
|
22
|
+
* is the strongest content we can publish and is not what the guest price gate
|
|
23
|
+
* protects. The market half is the opposite call, and the reasoning is already
|
|
24
|
+
* written down at `categories/api/[id]/price-guidance/route.ts`: publishing a
|
|
25
|
+
* competitor-derived band on an unauthenticated, publicly-cacheable response
|
|
26
|
+
* "would be exactly the 'a neighbouring surface publishes the range' leak that
|
|
27
|
+
* gate exists to close".
|
|
28
|
+
*
|
|
29
|
+
* So the two halves cannot share a response. Merging `market` into
|
|
30
|
+
* `getPriceIndexBandsForIds` would either poison that public cache with
|
|
31
|
+
* viewer-dependent content — one signed-in lookup populating the entry every
|
|
32
|
+
* guest then reads, the exact failure `cachedPublicRead`'s rules exist to
|
|
33
|
+
* prevent — or force the public half to go private and lose its cache. A
|
|
34
|
+
* sibling endpoint keeps both postures correct, and it is the same split the
|
|
35
|
+
* codebase already uses for `description-template` (public) against
|
|
36
|
+
* `price-guidance` (gated).
|
|
37
|
+
*
|
|
38
|
+
* ── 🛑 WHAT IS NOT DONE HERE: the seller tax-basis conversion ───────────────
|
|
39
|
+
*
|
|
40
|
+
* The seller route restates the band on the caller's own `pricesIncludeGst`
|
|
41
|
+
* basis. This one does not, for two reasons. The figures are tax-inclusive
|
|
42
|
+
* (`PRICE_GUIDANCE_TAX_BASIS`) and so is every price a buyer sees anywhere on
|
|
43
|
+
* the site, so for the tool's audience no conversion is the correct answer
|
|
44
|
+
* rather than a missing feature. And the conversion needs a store read per
|
|
45
|
+
* lookup, which on a tool that resolves up to eight leaves at a time would be
|
|
46
|
+
* a per-request cost paid to restate a number most callers want as stored. The
|
|
47
|
+
* renderer labels the basis instead.
|
|
48
|
+
*
|
|
49
|
+
* @tag domain:price-index
|
|
50
|
+
* @tag layer:server
|
|
51
|
+
* @tag pattern:projection
|
|
52
|
+
* @tag access:server-only
|
|
53
|
+
* @tag consumers:api/tools/price-index/market
|
|
54
|
+
* @tag sideEffects:none
|
|
55
|
+
*/
|
|
56
|
+
import { resolveInheritedGuidanceMany } from "../categories/price-guidance-resolve.js";
|
|
57
|
+
import { PRICE_GUIDANCE_TAX_BASIS } from "../../../shared/features/categories/price-guidance.js";
|
|
58
|
+
import { PRICE_INDEX_SEARCH_LIMIT } from "./search.js";
|
|
59
|
+
/**
|
|
60
|
+
* Market bands for the leaves the client already resolved.
|
|
61
|
+
*
|
|
62
|
+
* Bounded by `PRICE_INDEX_SEARCH_LIMIT` — the same cap the public half uses, so
|
|
63
|
+
* a crafted request cannot demand an unbounded read, and the two halves of one
|
|
64
|
+
* lookup can never disagree about how many rows a lookup covers.
|
|
65
|
+
*
|
|
66
|
+
* A leaf with no band at any level is ABSENT from the result rather than
|
|
67
|
+
* present with a null band. "We have no market evidence for this" and "this id
|
|
68
|
+
* does not exist" are different answers and the caller renders them
|
|
69
|
+
* differently; collapsing them is how a missing band becomes a confident ₹0.
|
|
70
|
+
*/
|
|
71
|
+
export async function getPriceIndexMarketForIds(rawIds) {
|
|
72
|
+
const ids = [...new Set(rawIds.filter((id) => typeof id === "string" && id.length > 0))].slice(0, PRICE_INDEX_SEARCH_LIMIT);
|
|
73
|
+
if (ids.length === 0)
|
|
74
|
+
return [];
|
|
75
|
+
const guidanceById = await resolveInheritedGuidanceMany(ids);
|
|
76
|
+
const rows = [];
|
|
77
|
+
for (const id of ids) {
|
|
78
|
+
const resolved = guidanceById.get(id);
|
|
79
|
+
const market = resolved?.guidance.market;
|
|
80
|
+
// No band anywhere up the chain. Omitted, never zeroed.
|
|
81
|
+
if (!market || !resolved)
|
|
82
|
+
continue;
|
|
83
|
+
rows.push({
|
|
84
|
+
categoryId: id,
|
|
85
|
+
median: market.median,
|
|
86
|
+
...(market.p25 === undefined ? {} : { p25: market.p25 }),
|
|
87
|
+
...(market.p75 === undefined ? {} : { p75: market.p75 }),
|
|
88
|
+
...(market.sampleSize === undefined ? {} : { sampleSize: market.sampleSize }),
|
|
89
|
+
...(toIso(market.asOf) ? { asOf: toIso(market.asOf) } : {}),
|
|
90
|
+
...(market.soldOutShare === undefined ? {} : { soldOutShare: market.soldOutShare }),
|
|
91
|
+
...(market.source ? { source: market.source } : {}),
|
|
92
|
+
inherited: resolved.inherited,
|
|
93
|
+
/*
|
|
94
|
+
* Only when inherited. Naming the leaf as its own source would be noise
|
|
95
|
+
* on a card that is already headed by that leaf's name.
|
|
96
|
+
*/
|
|
97
|
+
...(resolved.inherited
|
|
98
|
+
? {
|
|
99
|
+
viaCategoryId: resolved.fromCategoryId,
|
|
100
|
+
...(resolved.fromName ? { viaName: resolved.fromName } : {}),
|
|
101
|
+
}
|
|
102
|
+
: {}),
|
|
103
|
+
taxBasis: PRICE_GUIDANCE_TAX_BASIS,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return rows;
|
|
107
|
+
}
|
|
108
|
+
/** Firestore `Timestamp` | `Date` | ISO string -> ISO string. */
|
|
109
|
+
function toIso(v) {
|
|
110
|
+
if (!v)
|
|
111
|
+
return undefined;
|
|
112
|
+
if (v instanceof Date)
|
|
113
|
+
return v.toISOString();
|
|
114
|
+
if (typeof v === "string")
|
|
115
|
+
return v;
|
|
116
|
+
const t = v;
|
|
117
|
+
if (typeof t.toDate === "function")
|
|
118
|
+
return t.toDate().toISOString();
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
@@ -117,3 +117,21 @@ export interface PriceIndexSearchResponse {
|
|
|
117
117
|
* Cost: one `searchTxt` push-down query plus ONE multi-get. Both bounded.
|
|
118
118
|
*/
|
|
119
119
|
export declare function searchPriceIndex(rawQuery: string): Promise<PriceIndexSearchResponse>;
|
|
120
|
+
/**
|
|
121
|
+
* Tier 1 adoption — the client already resolved candidate leaves LOCALLY via
|
|
122
|
+
* the public search artifact (`useSiteSearch({ types: ["model", "category"] })`),
|
|
123
|
+
* at zero Firestore reads. This is the ONLY query this path still needs: one
|
|
124
|
+
* multi-get, bounded by however many ids the client resolved (capped the
|
|
125
|
+
* same as the search path, so a crafted request cannot demand an unbounded
|
|
126
|
+
* `getAll`).
|
|
127
|
+
*
|
|
128
|
+
* 🛑 Deliberately NOT filtering brand/bundle categoryType here — the CALLER
|
|
129
|
+
* already did that at index-build time (`PUBLIC_INDEX_SOURCES`'s category
|
|
130
|
+
* source only emits real listing leaves under the "model"/"category" type
|
|
131
|
+
* keys), so re-deriving the same filter from a bare id list would be a
|
|
132
|
+
* second, independently-maintained copy of a rule that already lives in one
|
|
133
|
+
* place. If that assumption is ever wrong, the cost is a band shown for a
|
|
134
|
+
* brand/bundle row — cosmetic, not a leak, since no money figure is ever
|
|
135
|
+
* attached to a document that does not already have sales recorded.
|
|
136
|
+
*/
|
|
137
|
+
export declare function getPriceIndexBandsForIds(rawIds: readonly string[]): Promise<PriceIndexSearchResult[]>;
|
|
@@ -154,8 +154,22 @@ export async function searchPriceIndex(rawQuery) {
|
|
|
154
154
|
return !t || t === "category" || t === "sublisting";
|
|
155
155
|
});
|
|
156
156
|
const page = listingRows.slice(0, PRICE_INDEX_SEARCH_LIMIT);
|
|
157
|
-
const
|
|
158
|
-
|
|
157
|
+
const results = await buildBandsForLeaves(page);
|
|
158
|
+
return {
|
|
159
|
+
...base,
|
|
160
|
+
results,
|
|
161
|
+
truncated: listingRows.length > PRICE_INDEX_SEARCH_LIMIT,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* The ONE multi-get, shared by the search path above and the ids-only path
|
|
166
|
+
* below — the actual band-shaping logic (sort by sample size, omit an empty
|
|
167
|
+
* `bands` array, the "no sales recorded" vs "zero sales" distinction) must
|
|
168
|
+
* answer identically regardless of how the category list was resolved.
|
|
169
|
+
*/
|
|
170
|
+
async function buildBandsForLeaves(leaves) {
|
|
171
|
+
const bandsByLeaf = await priceIndexRepository.getManyForLeaves(leaves.map((c) => c.id));
|
|
172
|
+
return leaves.map((c) => {
|
|
159
173
|
const doc = bandsByLeaf.get(c.id);
|
|
160
174
|
const bands = doc
|
|
161
175
|
? Object.entries(doc.byListingType ?? {})
|
|
@@ -180,9 +194,35 @@ export async function searchPriceIndex(rawQuery) {
|
|
|
180
194
|
...(doc ? { windowDays: doc.windowDays, computedAt: toIso(doc.computedAt) } : {}),
|
|
181
195
|
};
|
|
182
196
|
});
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Tier 1 adoption — the client already resolved candidate leaves LOCALLY via
|
|
200
|
+
* the public search artifact (`useSiteSearch({ types: ["model", "category"] })`),
|
|
201
|
+
* at zero Firestore reads. This is the ONLY query this path still needs: one
|
|
202
|
+
* multi-get, bounded by however many ids the client resolved (capped the
|
|
203
|
+
* same as the search path, so a crafted request cannot demand an unbounded
|
|
204
|
+
* `getAll`).
|
|
205
|
+
*
|
|
206
|
+
* 🛑 Deliberately NOT filtering brand/bundle categoryType here — the CALLER
|
|
207
|
+
* already did that at index-build time (`PUBLIC_INDEX_SOURCES`'s category
|
|
208
|
+
* source only emits real listing leaves under the "model"/"category" type
|
|
209
|
+
* keys), so re-deriving the same filter from a bare id list would be a
|
|
210
|
+
* second, independently-maintained copy of a rule that already lives in one
|
|
211
|
+
* place. If that assumption is ever wrong, the cost is a band shown for a
|
|
212
|
+
* brand/bundle row — cosmetic, not a leak, since no money figure is ever
|
|
213
|
+
* attached to a document that does not already have sales recorded.
|
|
214
|
+
*/
|
|
215
|
+
export async function getPriceIndexBandsForIds(rawIds) {
|
|
216
|
+
const ids = [...new Set(rawIds.filter((id) => typeof id === "string" && id.length > 0))].slice(0, PRICE_INDEX_SEARCH_LIMIT);
|
|
217
|
+
if (ids.length === 0)
|
|
218
|
+
return [];
|
|
219
|
+
const docs = await categoriesRepository.getManyByIds(ids);
|
|
220
|
+
const leaves = docs.map((c) => ({
|
|
221
|
+
id: c.id,
|
|
222
|
+
name: c.name,
|
|
223
|
+
slug: c.slug,
|
|
224
|
+
path: c.path,
|
|
225
|
+
display: c.display,
|
|
226
|
+
}));
|
|
227
|
+
return buildBandsForLeaves(leaves);
|
|
188
228
|
}
|
|
@@ -17,7 +17,9 @@ interface PrizeDrawDocLike {
|
|
|
17
17
|
price?: number | null;
|
|
18
18
|
currency?: string | null;
|
|
19
19
|
mainImage?: string | null;
|
|
20
|
-
images?: (
|
|
20
|
+
images?: ({
|
|
21
|
+
url: string;
|
|
22
|
+
} | string | null | undefined)[] | null;
|
|
21
23
|
prizeRevealStatus?: "pending" | "open" | "closed" | null;
|
|
22
24
|
prizeMaxEntries?: number | null;
|
|
23
25
|
prizeCurrentEntries?: number | null;
|
|
@@ -19,7 +19,9 @@ export function renderPrizeDrawOg(doc, opts) {
|
|
|
19
19
|
return renderPrizeDrawOgImage({
|
|
20
20
|
title: doc?.title ?? doc?.name ?? "Prize Draw",
|
|
21
21
|
pricePerEntryLabel,
|
|
22
|
-
imageUrl: resolveOgImageUrl(doc?.mainImage ||
|
|
22
|
+
imageUrl: resolveOgImageUrl(doc?.mainImage ||
|
|
23
|
+
(typeof doc?.images?.[0] === "string" ? doc.images[0] : doc?.images?.[0]?.url) ||
|
|
24
|
+
null, opts.baseUrl),
|
|
23
25
|
revealStatus: doc?.prizeRevealStatus ?? null,
|
|
24
26
|
entriesLabel,
|
|
25
27
|
}, opts.siteName);
|
|
@@ -42,6 +42,16 @@ export interface PublicGroupMember {
|
|
|
42
42
|
groupTitle?: string;
|
|
43
43
|
/** Drives the stepper's `max`. */
|
|
44
44
|
availableQuantity: number;
|
|
45
|
+
/**
|
|
46
|
+
* Made to order — the stepper is UNCAPPED for this member.
|
|
47
|
+
*
|
|
48
|
+
* 🛑 This is an ALLOW-LIST projection: a field absent from this interface and
|
|
49
|
+
* from `toPublicGroupMember` below simply does not reach the picker, with no
|
|
50
|
+
* error anywhere. Without it the picker reads `availableQuantity: 0` on a
|
|
51
|
+
* print-on-demand member and caps the stepper at zero — the member renders,
|
|
52
|
+
* looks selectable, and cannot be chosen.
|
|
53
|
+
*/
|
|
54
|
+
madeToOrder?: boolean;
|
|
45
55
|
storeId: string;
|
|
46
56
|
storeName?: string;
|
|
47
57
|
status: string;
|
|
@@ -50,6 +60,22 @@ export interface PublicGroupMember {
|
|
|
50
60
|
* snapshot it for per-member GST proration at checkout. */
|
|
51
61
|
gstRate?: 0 | 5 | 12 | 18 | 28;
|
|
52
62
|
hsnCode?: string;
|
|
63
|
+
/**
|
|
64
|
+
* The group's OWN selection rules — present only on the PARENT row, since
|
|
65
|
+
* that is the only document that carries them.
|
|
66
|
+
*
|
|
67
|
+
* 🛑 Projected so `readGroupRules` has something real to read client-side.
|
|
68
|
+
* Without these the picker would silently see no rules on every page that
|
|
69
|
+
* derives them from this endpoint's own response (`ShowGroupSection`),
|
|
70
|
+
* which is a client-only regression of the exact kind E3 exists to close —
|
|
71
|
+
* a server-only rule reads as a broken button, and here it would be the
|
|
72
|
+
* reverse: a client that never finds out a rule exists at all.
|
|
73
|
+
*/
|
|
74
|
+
groupMinMembers?: number;
|
|
75
|
+
groupMaxMembers?: number;
|
|
76
|
+
groupMaxUnitsPerMember?: number;
|
|
77
|
+
groupRequiredMemberIds?: string[];
|
|
78
|
+
groupDiscountPercent?: number;
|
|
53
79
|
}
|
|
54
80
|
export declare function toPublicGroupMember(doc: ProductDocument): PublicGroupMember;
|
|
55
81
|
export declare function toPublicGroupMembers(docs: readonly ProductDocument[]): PublicGroupMember[];
|
|
@@ -22,18 +22,24 @@ export function toPublicGroupMember(doc) {
|
|
|
22
22
|
title: doc.title,
|
|
23
23
|
price: doc.price,
|
|
24
24
|
currency: doc.currency,
|
|
25
|
-
image: doc.images?.[0] ?? doc.mainImage,
|
|
25
|
+
image: doc.images?.[0]?.url ?? doc.mainImage,
|
|
26
26
|
condition: doc.condition,
|
|
27
27
|
listingType: normalizeListingType(doc),
|
|
28
28
|
isGroupParent: doc.isGroupParent,
|
|
29
29
|
groupTitle: doc.groupTitle,
|
|
30
30
|
availableQuantity: doc.availableQuantity ?? 0,
|
|
31
|
+
madeToOrder: doc.madeToOrder,
|
|
31
32
|
storeId: doc.storeId,
|
|
32
33
|
storeName: doc.storeName,
|
|
33
34
|
status: doc.status,
|
|
34
35
|
isSold: doc.isSold,
|
|
35
36
|
gstRate: doc.gstRate,
|
|
36
37
|
hsnCode: doc.hsnCode,
|
|
38
|
+
groupMinMembers: doc.groupMinMembers,
|
|
39
|
+
groupMaxMembers: doc.groupMaxMembers,
|
|
40
|
+
groupMaxUnitsPerMember: doc.groupMaxUnitsPerMember,
|
|
41
|
+
groupRequiredMemberIds: doc.groupRequiredMemberIds,
|
|
42
|
+
groupDiscountPercent: doc.groupDiscountPercent,
|
|
37
43
|
};
|
|
38
44
|
}
|
|
39
45
|
export function toPublicGroupMembers(docs) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ViewerLike } from "../tester/visibility";
|
|
2
2
|
import type { JsonValue } from "../../../../schemas/types";
|
|
3
|
+
import type { UnavailableClause } from "../../../shared/listing-types/availability";
|
|
3
4
|
import { type AvailabilityFilter } from "../../../../constants/field-names";
|
|
4
5
|
/** Vercel Hobby Fluid Compute ceiling (CLAUDE.md Rule #6) — never fetch more at once. */
|
|
5
6
|
export declare const PUBLIC_PRODUCT_MAX_PAGE_SIZE = 50;
|
|
@@ -250,6 +251,17 @@ export declare function parsePublicProductParams(params: URLSearchParams | Recor
|
|
|
250
251
|
*/
|
|
251
252
|
availability?: AvailabilityFilter;
|
|
252
253
|
}): PublicProductListInput;
|
|
254
|
+
/** Render one `UnavailableClause` as a Sieve clause against a fixed `now`. */
|
|
255
|
+
/**
|
|
256
|
+
* One unavailable clause → a Sieve fragment, with `"NOW"` bound to this
|
|
257
|
+
* query's timestamp.
|
|
258
|
+
*
|
|
259
|
+
* Exported so `listing-tab-counts` builds its clause queries with THIS
|
|
260
|
+
* function rather than a second copy. A rule reachable by copy-paste is one
|
|
261
|
+
* that gets copied wrong (Root Cause #75), and the two would only diverge the
|
|
262
|
+
* day someone adds a third `op`.
|
|
263
|
+
*/
|
|
264
|
+
export declare function clauseToSieve(clause: UnavailableClause, now: Date): string;
|
|
253
265
|
export declare function listPublicProducts(input: PublicProductListInput, opts?: PublicProductListOptions): Promise<PublicProductListResult | null>;
|
|
254
266
|
/**
|
|
255
267
|
* One store tab's worth of listings.
|
|
@@ -440,7 +440,16 @@ async function defaultExecutor(query) {
|
|
|
440
440
|
};
|
|
441
441
|
}
|
|
442
442
|
/** Render one `UnavailableClause` as a Sieve clause against a fixed `now`. */
|
|
443
|
-
|
|
443
|
+
/**
|
|
444
|
+
* One unavailable clause → a Sieve fragment, with `"NOW"` bound to this
|
|
445
|
+
* query's timestamp.
|
|
446
|
+
*
|
|
447
|
+
* Exported so `listing-tab-counts` builds its clause queries with THIS
|
|
448
|
+
* function rather than a second copy. A rule reachable by copy-paste is one
|
|
449
|
+
* that gets copied wrong (Root Cause #75), and the two would only diverge the
|
|
450
|
+
* day someone adds a third `op`.
|
|
451
|
+
*/
|
|
452
|
+
export function clauseToSieve(clause, now) {
|
|
444
453
|
const value = clause.value === "NOW" ? now.toISOString() : clause.value;
|
|
445
454
|
return clause.op === "lt"
|
|
446
455
|
? sieveFilter(clause.field, SIEVE_OP.LT, value)
|
|
@@ -31,6 +31,28 @@ export interface ListingTabScope {
|
|
|
31
31
|
* `undefined` as "keep the tab visible" (Root Cause #59).
|
|
32
32
|
*/
|
|
33
33
|
export type ListingTabCounts = Record<string, number | undefined>;
|
|
34
|
+
/**
|
|
35
|
+
* The two numbers a tab bar needs, and they are NOT the same number.
|
|
36
|
+
*
|
|
37
|
+
* 🛑 A badge showing `total` while the panel below it defaults to the
|
|
38
|
+
* AVAILABLE scope is a tab that advertises rows it will not render. That was
|
|
39
|
+
* live: a category holding one `prizeRevealStatus: "closed"` draw showed
|
|
40
|
+
* "Prize Draws 1" above "No prize draws found." — accurate counting of the
|
|
41
|
+
* wrong question, Root Cause #72's family.
|
|
42
|
+
*
|
|
43
|
+
* `available` — what the default scope will render. The BADGE.
|
|
44
|
+
* `total` — every published row in scope, across all availability
|
|
45
|
+
* scopes. The hide-at-zero decision, and the "N ended —
|
|
46
|
+
* view All" hint under an empty panel.
|
|
47
|
+
*
|
|
48
|
+
* 🛑 Hiding must stay keyed on `total`. A tab whose rows have all ended still
|
|
49
|
+
* has a browsable archive behind the Ended/All scopes; hiding it at
|
|
50
|
+
* `available === 0` would make that archive unreachable.
|
|
51
|
+
*/
|
|
52
|
+
export interface ListingTabCountResult {
|
|
53
|
+
available: ListingTabCounts;
|
|
54
|
+
total: ListingTabCounts;
|
|
55
|
+
}
|
|
34
56
|
/**
|
|
35
57
|
* Count every tab in `tabs` within `scope`, in one parallel batch.
|
|
36
58
|
*
|
|
@@ -41,4 +63,6 @@ export type ListingTabCounts = Record<string, number | undefined>;
|
|
|
41
63
|
* `stores` is not counted here: only the category page has that tab and it
|
|
42
64
|
* already resolves its store list eagerly to render the tab body.
|
|
43
65
|
*/
|
|
44
|
-
export declare function listingTabCounts(tabs: readonly ListingTab[], scope: ListingTabScope
|
|
66
|
+
export declare function listingTabCounts(tabs: readonly ListingTab[], scope: ListingTabScope, opts?: {
|
|
67
|
+
now?: Date;
|
|
68
|
+
}): Promise<ListingTabCountResult>;
|