@mohasinac/appkit 4.53.0 → 4.53.1
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/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/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/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/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 +25 -0
- package/dist/_internal/server/features/stores/tabs.js +122 -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 +2 -1
- 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 +39 -15
- package/dist/constants/api-endpoints.js +28 -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 +4 -11
- 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 +113 -0
- package/dist/features/categories/components/BrandDetailPageView.js +19 -3
- package/dist/features/categories/components/BrandDetailTabs.js +10 -0
- package/dist/features/categories/components/CategoriesIndexListing.js +23 -8
- package/dist/features/categories/components/CategoryDetailPageView.js +141 -14
- package/dist/features/categories/components/CategoryDetailTabs.js +12 -0
- 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 +4 -9
- 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 +4 -9
- 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 +4 -9
- 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 +4 -11
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +1 -1
- package/dist/features/price-index/components/PriceIndexToolView.d.ts +3 -1
- package/dist/features/price-index/components/PriceIndexToolView.js +92 -13
- package/dist/features/products/components/CompareOverlay.d.ts +18 -1
- package/dist/features/products/components/CompareOverlay.js +21 -2
- 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 +91 -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 +74 -15
- 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/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 +98 -0
- package/dist/features/products/repository/products.repository.js +235 -17
- 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 +39 -0
- package/dist/features/products/utils/group-member-image.js +3 -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/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/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 +280 -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 +372 -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 +441 -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 +9 -9
- package/dist/server.js +27 -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 +135 -26
- 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
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: A signed-in viewer should be able to find their own orders, offers
|
|
3
|
+
* and drafts from the same box — without that box ever being able to
|
|
4
|
+
* return somebody else's.
|
|
5
|
+
* WHAT: Tier 2. Viewer-scoped `SearchSource`s, built per request from the
|
|
6
|
+
* session, plus the budget check.
|
|
7
|
+
*
|
|
8
|
+
* ## 🛑 Filtered SERVER-side, never in the browser
|
|
9
|
+
*
|
|
10
|
+
* The same rule `projectActionIndexForViewer` states for the action index.
|
|
11
|
+
* Every source below carries an OWNER CLAUSE in its `where`, so the query
|
|
12
|
+
* itself cannot return another viewer's rows — a client-side filter over a
|
|
13
|
+
* wider result is one rendering bug away from a disclosure, and the bug is
|
|
14
|
+
* invisible in review because the UI looks right.
|
|
15
|
+
*
|
|
16
|
+
* ## 🛑 A guest makes ZERO authenticated requests
|
|
17
|
+
*
|
|
18
|
+
* `scopedSourcesFor(null)` returns `[]`, and the route refuses before
|
|
19
|
+
* touching Firestore. This is not an optimisation: a signed-out visitor
|
|
20
|
+
* whose search fires an authenticated request has a session the page does
|
|
21
|
+
* not know about, and the only honest way to prove the gate works is for
|
|
22
|
+
* there to be no request at all.
|
|
23
|
+
*
|
|
24
|
+
* ## The read budget, which is why the split exists
|
|
25
|
+
*
|
|
26
|
+
* guest 0 sources 0 docs 0 round trips
|
|
27
|
+
* buyer 6 ~120 1 (one Promise.all)
|
|
28
|
+
* seller +5 ~220 1
|
|
29
|
+
* admin +5 ~320 1
|
|
30
|
+
*
|
|
31
|
+
* Against `SCOPED_SCAN_BUDGET` = 400. Scoped sources use `scanLimit: 20`
|
|
32
|
+
* rather than the 60 the public tier uses, because an owner clause has
|
|
33
|
+
* already collapsed the candidate set — a 60-doc window would be 40 reads
|
|
34
|
+
* spent proving there was nothing else.
|
|
35
|
+
*
|
|
36
|
+
* @tag domain:search
|
|
37
|
+
* @tag layer:server
|
|
38
|
+
* @tag pattern:none
|
|
39
|
+
* @tag access:server
|
|
40
|
+
* @tag consumers:/api/search/scoped
|
|
41
|
+
* @tag sideEffects:none
|
|
42
|
+
*/
|
|
43
|
+
import { ROUTES } from "../../../../next/routing/route-map.js";
|
|
44
|
+
import { SCOPED_SCAN_BUDGET } from "./search-everything.js";
|
|
45
|
+
/** Scoped sources read a narrow window; the owner clause did the work. */
|
|
46
|
+
const SCOPED_SCAN = 20;
|
|
47
|
+
const str = (v) => typeof v === "string" && v.length > 0 ? v : undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Every term must appear somewhere in the named fields.
|
|
50
|
+
*
|
|
51
|
+
* 🛑 AND, matching the push-down path's `plan.rest.every(...)`. An OR here
|
|
52
|
+
* would make the two modes disagree about what a two-word query means, on
|
|
53
|
+
* the same page, in adjacent groups.
|
|
54
|
+
*
|
|
55
|
+
* 🛑 Lower-cased comparison only — NOT `normalizeSearchText`. That
|
|
56
|
+
* normaliser exists to make the WRITE path and the token query agree; this
|
|
57
|
+
* path has no tokens, and running it here would strip punctuation out of a
|
|
58
|
+
* tracking number or an order id, which are exactly the strings someone
|
|
59
|
+
* pastes into a scoped search.
|
|
60
|
+
*/
|
|
61
|
+
function fieldsContain(fields) {
|
|
62
|
+
return (data, terms) => {
|
|
63
|
+
if (terms.length === 0)
|
|
64
|
+
return false;
|
|
65
|
+
const hay = fields
|
|
66
|
+
.map((f) => str(data[f]) ?? "")
|
|
67
|
+
.join(" ")
|
|
68
|
+
.toLowerCase();
|
|
69
|
+
return terms.every((t) => hay.includes(t.toLowerCase()));
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The viewer's own records.
|
|
74
|
+
*
|
|
75
|
+
* 🛑 `mode: "bounded-memory"` for everything PII-bearing, and `search-txt`
|
|
76
|
+
* only where the collection genuinely carries tokens that are safe to have.
|
|
77
|
+
* `orders` is the interesting case: `buildOrderSearchTxt` already indexes
|
|
78
|
+
* productTitle / storeName / trackingNumber and already EXCLUDES the
|
|
79
|
+
* shipping address, so it keeps the push-down. `addresses` has no
|
|
80
|
+
* `searchTxt` and must never grow one.
|
|
81
|
+
*/
|
|
82
|
+
function selfSources(viewer) {
|
|
83
|
+
return [
|
|
84
|
+
{
|
|
85
|
+
key: "my-orders",
|
|
86
|
+
label: "Your orders",
|
|
87
|
+
collection: "orders",
|
|
88
|
+
// 🛑 `userId`, NOT `buyerId`. The field is `userId` and a query on
|
|
89
|
+
// `buyerId` returns ZERO while raising nothing — indistinguishable
|
|
90
|
+
// from "this buyer has no orders", which is a false-finding generator.
|
|
91
|
+
where: [["userId", viewer.uid]],
|
|
92
|
+
scanLimit: SCOPED_SCAN,
|
|
93
|
+
mode: "search-txt",
|
|
94
|
+
select: ["productTitle", "status", "trackingNumber"],
|
|
95
|
+
toHit: (id, d) => ({
|
|
96
|
+
id,
|
|
97
|
+
title: str(d.productTitle) ?? `Order ${id}`,
|
|
98
|
+
href: String(ROUTES.USER.ORDER_DETAIL(id)),
|
|
99
|
+
detail: str(d.status),
|
|
100
|
+
}),
|
|
101
|
+
moreHref: () => String(ROUTES.USER.ORDERS),
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
key: "my-offers",
|
|
105
|
+
label: "Your offers",
|
|
106
|
+
collection: "offers",
|
|
107
|
+
where: [["buyerId", viewer.uid]],
|
|
108
|
+
scanLimit: SCOPED_SCAN,
|
|
109
|
+
mode: "bounded-memory",
|
|
110
|
+
orderBy: ["createdAt", "desc"],
|
|
111
|
+
select: ["productTitle", "status", "productSlug"],
|
|
112
|
+
matches: fieldsContain(["productTitle", "status"]),
|
|
113
|
+
toHit: (id, d) => ({
|
|
114
|
+
id,
|
|
115
|
+
title: str(d.productTitle) ?? "Offer",
|
|
116
|
+
href: String(ROUTES.USER.OFFERS),
|
|
117
|
+
detail: str(d.status),
|
|
118
|
+
}),
|
|
119
|
+
moreHref: () => String(ROUTES.USER.OFFERS),
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
key: "my-catalogue",
|
|
123
|
+
label: "Your catalogue",
|
|
124
|
+
collection: "catalogueItems",
|
|
125
|
+
where: [["ownerId", viewer.uid]],
|
|
126
|
+
scanLimit: SCOPED_SCAN,
|
|
127
|
+
mode: "bounded-memory",
|
|
128
|
+
orderBy: ["createdAt", "desc"],
|
|
129
|
+
select: ["title", "listingStatus"],
|
|
130
|
+
matches: fieldsContain(["title"]),
|
|
131
|
+
toHit: (id, d) => ({
|
|
132
|
+
id,
|
|
133
|
+
title: str(d.title) ?? "Catalogue item",
|
|
134
|
+
href: String(ROUTES.USER.CATALOGUE),
|
|
135
|
+
detail: str(d.listingStatus),
|
|
136
|
+
}),
|
|
137
|
+
moreHref: () => String(ROUTES.USER.CATALOGUE),
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
/*
|
|
141
|
+
* 🛑 `bounded-memory`, and this is the source the mode exists for.
|
|
142
|
+
*
|
|
143
|
+
* `addresses` is PII — `fullName`, `phone`, `addressLine1` are all
|
|
144
|
+
* encrypted at rest and decrypted by the repository on read. It has
|
|
145
|
+
* no `searchTxt` and must never get one: a prefix-expanded plaintext
|
|
146
|
+
* token array over a person's name and street is precisely what
|
|
147
|
+
* § Searching PII refuses. The owner clause already bounds this to
|
|
148
|
+
* well under 25 rows, so an index would re-derive a bound we have
|
|
149
|
+
* while adding leak surface for nothing.
|
|
150
|
+
*/
|
|
151
|
+
key: "my-addresses",
|
|
152
|
+
label: "Your addresses",
|
|
153
|
+
collection: "addresses",
|
|
154
|
+
where: [
|
|
155
|
+
["ownerType", "user"],
|
|
156
|
+
["ownerId", viewer.uid],
|
|
157
|
+
],
|
|
158
|
+
scanLimit: SCOPED_SCAN,
|
|
159
|
+
mode: "bounded-memory",
|
|
160
|
+
select: ["label", "city", "state"],
|
|
161
|
+
// Only the non-sensitive half is matched AND projected. A label and a
|
|
162
|
+
// city are enough to pick an address out of a list of four; the name,
|
|
163
|
+
// phone and street line never leave the server.
|
|
164
|
+
matches: fieldsContain(["label", "city", "state"]),
|
|
165
|
+
toHit: (id, d) => ({
|
|
166
|
+
id,
|
|
167
|
+
title: str(d.label) ?? "Address",
|
|
168
|
+
href: String(ROUTES.USER.ADDRESSES),
|
|
169
|
+
detail: [str(d.city), str(d.state)].filter(Boolean).join(", ") || undefined,
|
|
170
|
+
}),
|
|
171
|
+
moreHref: () => String(ROUTES.USER.ADDRESSES),
|
|
172
|
+
},
|
|
173
|
+
];
|
|
174
|
+
}
|
|
175
|
+
/** The viewer's store. Requires a resolved `storeId`, never a uid. */
|
|
176
|
+
function sellerSources(viewer) {
|
|
177
|
+
if (!viewer.storeId)
|
|
178
|
+
return [];
|
|
179
|
+
const storeId = viewer.storeId;
|
|
180
|
+
return [
|
|
181
|
+
{
|
|
182
|
+
/*
|
|
183
|
+
* The seller's own listings INCLUDING drafts — which is the whole
|
|
184
|
+
* point, because a published one is already in the public artifact and
|
|
185
|
+
* the merge dedupes it away. What survives in this group is precisely
|
|
186
|
+
* "your drafts", and labelling it so is what makes the dedupe legible.
|
|
187
|
+
*/
|
|
188
|
+
key: "my-listings",
|
|
189
|
+
label: "Your listings (incl. drafts)",
|
|
190
|
+
collection: "products",
|
|
191
|
+
where: [["storeId", storeId]],
|
|
192
|
+
scanLimit: SCOPED_SCAN,
|
|
193
|
+
mode: "search-txt",
|
|
194
|
+
select: ["title", "slug", "status"],
|
|
195
|
+
toHit: (id, d) => ({
|
|
196
|
+
id,
|
|
197
|
+
title: str(d.title) ?? "Listing",
|
|
198
|
+
href: String(ROUTES.STORE.PRODUCTS_EDIT(str(d.slug) ?? id)),
|
|
199
|
+
detail: str(d.status),
|
|
200
|
+
}),
|
|
201
|
+
moreHref: () => String(ROUTES.STORE.PRODUCTS),
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
key: "store-orders",
|
|
205
|
+
label: "Orders in your store",
|
|
206
|
+
collection: "orders",
|
|
207
|
+
where: [["storeId", storeId]],
|
|
208
|
+
scanLimit: SCOPED_SCAN,
|
|
209
|
+
mode: "search-txt",
|
|
210
|
+
select: ["productTitle", "status", "trackingNumber"],
|
|
211
|
+
toHit: (id, d) => ({
|
|
212
|
+
id,
|
|
213
|
+
title: str(d.productTitle) ?? `Order ${id}`,
|
|
214
|
+
href: String(ROUTES.STORE.ORDER_DETAIL(id)),
|
|
215
|
+
detail: str(d.status),
|
|
216
|
+
}),
|
|
217
|
+
moreHref: () => String(ROUTES.STORE.ORDERS),
|
|
218
|
+
},
|
|
219
|
+
];
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Build the viewer's sources.
|
|
223
|
+
*
|
|
224
|
+
* 🛑 Returns `[]` for a guest, which is what makes "zero authenticated
|
|
225
|
+
* requests when signed out" structural rather than a caller's discipline.
|
|
226
|
+
*/
|
|
227
|
+
export function scopedSourcesFor(viewer) {
|
|
228
|
+
if (!viewer)
|
|
229
|
+
return [];
|
|
230
|
+
const sources = [...selfSources(viewer)];
|
|
231
|
+
if (viewer.isSeller || viewer.isAdmin)
|
|
232
|
+
sources.push(...sellerSources(viewer));
|
|
233
|
+
return sources;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Σ `scanLimit`, for the budget check.
|
|
237
|
+
*
|
|
238
|
+
* Exported so `audit-scoped-search-ownership` R4 can print the arithmetic
|
|
239
|
+
* rather than just a verdict: "over budget" without the per-source figures
|
|
240
|
+
* tells you nothing about which source to shrink.
|
|
241
|
+
*/
|
|
242
|
+
export function scopedScanTotal(sources) {
|
|
243
|
+
return sources.reduce((n, s) => n + (s.scanLimit ?? 60), 0);
|
|
244
|
+
}
|
|
245
|
+
export function assertWithinScopedBudget(sources) {
|
|
246
|
+
const total = scopedScanTotal(sources);
|
|
247
|
+
if (total <= SCOPED_SCAN_BUDGET)
|
|
248
|
+
return;
|
|
249
|
+
const breakdown = sources.map((s) => `${s.key}=${s.scanLimit ?? 60}`).join(" + ");
|
|
250
|
+
throw new Error(`Scoped search would read up to ${total} documents, over SCOPED_SCAN_BUDGET ` +
|
|
251
|
+
`(${SCOPED_SCAN_BUDGET}). ${breakdown} = ${total}.`);
|
|
252
|
+
}
|
|
@@ -43,7 +43,56 @@ export interface SearchSource {
|
|
|
43
43
|
moreHref: (q: string) => string;
|
|
44
44
|
/** Extra equality narrowing, e.g. `status == published`. */
|
|
45
45
|
where?: ReadonlyArray<readonly [string, unknown]>;
|
|
46
|
+
/**
|
|
47
|
+
* The bounded window this source reads before the in-memory refine.
|
|
48
|
+
*
|
|
49
|
+
* Defaults to `SCAN` (60). Scoped sources pass 20: an owner clause
|
|
50
|
+
* (`userId == uid`) has already collapsed the candidate set, so a 60-doc
|
|
51
|
+
* window is 40 reads spent proving there was nothing else.
|
|
52
|
+
*/
|
|
53
|
+
scanLimit?: number;
|
|
54
|
+
/**
|
|
55
|
+
* How this source FINDS rows. Discriminated so TypeScript forces every
|
|
56
|
+
* source to answer, which is what makes `audit-scoped-search-ownership`
|
|
57
|
+
* R2 checkable rather than advisory.
|
|
58
|
+
*
|
|
59
|
+
* `search-txt` the `array-contains` push-down. Requires the
|
|
60
|
+
* collection to carry `searchTxt`.
|
|
61
|
+
* `bounded-memory` read the owner's bounded window and filter in
|
|
62
|
+
* memory, with no token push-down at all.
|
|
63
|
+
*
|
|
64
|
+
* 🛑 A PII-bearing collection must be `bounded-memory`. `searchTxt` is
|
|
65
|
+
* plaintext, edge-n-gram-expanded and stored on the document — giving
|
|
66
|
+
* `addresses` a token push-down would write a prefix-expanded index over
|
|
67
|
+
* a person's name and street, which is the precise thing § Searching PII
|
|
68
|
+
* refuses (a 1-character prefix has 36 possible values; the frequency
|
|
69
|
+
* distribution unpicks from there). Decrypting a bounded owner-scoped
|
|
70
|
+
* window in memory re-derives a bound ownership already gives, and adds
|
|
71
|
+
* no leak surface.
|
|
72
|
+
*/
|
|
73
|
+
mode?: "search-txt" | "bounded-memory";
|
|
74
|
+
/**
|
|
75
|
+
* For `bounded-memory`: does this row match? Receives the DECRYPTED
|
|
76
|
+
* document, so a repository that decrypts on read is the intended caller.
|
|
77
|
+
* Every term must match (AND), consistent with the push-down path.
|
|
78
|
+
*/
|
|
79
|
+
matches?: (data: SearchDocData, terms: readonly string[]) => boolean;
|
|
80
|
+
/** Ordering for `bounded-memory`, since there is no token clause to sort by. */
|
|
81
|
+
orderBy?: readonly [string, "asc" | "desc"];
|
|
46
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* 🛑 The ceiling on what ONE viewer's scoped search may read.
|
|
85
|
+
*
|
|
86
|
+
* Σ `scanLimit` across the widest role, checked by
|
|
87
|
+
* `audit-scoped-search-ownership` R4 — which PRINTS THE ARITHMETIC on
|
|
88
|
+
* failure, because "the budget is exceeded" is useless without knowing by
|
|
89
|
+
* which source and how much.
|
|
90
|
+
*
|
|
91
|
+
* 400 against Rule #6's ~3-round-trip guidance is defensible because the
|
|
92
|
+
* sources run in ONE `Promise.all`: it is one round trip of fan-out, not
|
|
93
|
+
* twelve sequential ones. The number that matters is documents read.
|
|
94
|
+
*/
|
|
95
|
+
export declare const SCOPED_SCAN_BUDGET = 400;
|
|
47
96
|
/** A projected Firestore document's fields. */
|
|
48
97
|
export type SearchDocData = Record<string, FirestoreValue>;
|
|
49
98
|
/** A projected document — the only shape this module reads. */
|
|
@@ -55,6 +104,14 @@ interface SearchQueryRef {
|
|
|
55
104
|
where(field: string, op: string, value: unknown): SearchQueryRef;
|
|
56
105
|
select(...fields: string[]): SearchQueryRef;
|
|
57
106
|
limit(n: number): SearchQueryRef;
|
|
107
|
+
/**
|
|
108
|
+
* Only `bounded-memory` sources use this. The push-down path deliberately
|
|
109
|
+
* does not order: an `orderBy` beside an `array-contains` demands a
|
|
110
|
+
* composite index per source, and a missing one throws
|
|
111
|
+
* `FAILED_PRECONDITION` into the per-source catch, where it renders as an
|
|
112
|
+
* empty group — i.e. silently (Root Cause #59).
|
|
113
|
+
*/
|
|
114
|
+
orderBy(field: string, direction: "asc" | "desc"): SearchQueryRef;
|
|
58
115
|
get(): Promise<{
|
|
59
116
|
docs: SearchDoc[];
|
|
60
117
|
}>;
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* D2 Fix 4 — CROSS-RESOURCE search.
|
|
3
3
|
*
|
|
4
|
-
* 🛑
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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
|
|
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
|
|
36
|
-
* only through `/api/search`, whose own hook (`useSearch`)
|
|
37
|
-
* outside barrel re-exports.
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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 =
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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>;
|