@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
|
@@ -38,11 +38,13 @@ import { CHECKOUT_DEFAULT_COMMISSIONS, CHECKOUT_DEFAULT_EMI_SETTINGS } from "../
|
|
|
38
38
|
import { checkEmiEligibility, computeEmiSchedule } from "../../../shared/features/emi/schedule.js";
|
|
39
39
|
import { formatShippingAddress } from "./data.js";
|
|
40
40
|
import { enforceMaxPerUserForCart } from "./prize-bundle-gates.js";
|
|
41
|
-
import { getListingRule, runSyncPreflight, } from "../../../shared/checkout/rules/index.js";
|
|
41
|
+
import { getListingRule, runSyncPreflight, assertPaymentMethodAllowed, } from "../../../shared/checkout/rules/index.js";
|
|
42
|
+
import { revalidateGroupLines } from "./group-rule-revalidation.js";
|
|
42
43
|
import { cartIsDigitalOnly } from "../../../shared/listing-types/cart-shipping.js";
|
|
43
44
|
import { processRefundAction } from "../refunds/actions.js";
|
|
44
45
|
import { sendNotification } from "../../../../features/admin/actions/notification-actions.js";
|
|
45
46
|
import { resolveDisplayedUpiId } from "./upi-resolution.js";
|
|
47
|
+
import { resolveSupplierIdentity } from "./supplier-identity.js";
|
|
46
48
|
import { PAYMENT_WINDOW_MS } from "../../../../features/orders/constants/payment-window.js";
|
|
47
49
|
import { isCheckoutValueOtpVerified } from "../../../../features/checkout/actions/checkout-value-otp-actions.js";
|
|
48
50
|
const AUDIT_LOG_FAIL_MSG = "checkout: audit log failed (non-critical)";
|
|
@@ -193,6 +195,9 @@ export async function resolveShippingCost(storeId) {
|
|
|
193
195
|
shippingPolicy: DEFAULT_STORE_SHIPPING_POLICY,
|
|
194
196
|
shippingUnavailable: false,
|
|
195
197
|
storeProtections: { shipping: false, sale: false },
|
|
198
|
+
// No store, so no seller registration — the caller falls back to the
|
|
199
|
+
// platform's via resolveSupplierIdentity.
|
|
200
|
+
storeTaxIdentity: undefined,
|
|
196
201
|
};
|
|
197
202
|
}
|
|
198
203
|
const store = await storeRepository.findById(storeId);
|
|
@@ -246,6 +251,25 @@ export async function resolveShippingCost(storeId) {
|
|
|
246
251
|
shipping: store?.supportsShippingProtection === true,
|
|
247
252
|
sale: store?.supportsSaleProtection === true,
|
|
248
253
|
},
|
|
254
|
+
/*
|
|
255
|
+
* The seller's own tax registration, returned from the store read this
|
|
256
|
+
* function ALREADY does.
|
|
257
|
+
*
|
|
258
|
+
* Deliberately not a second `storeRepository.findById` in
|
|
259
|
+
* `createOrderForGroup`: that would add one Firestore read per order group
|
|
260
|
+
* to the checkout path for data already sitting in this scope, against
|
|
261
|
+
* Rule #6's round-trip budget. Returning the three raw fields rather than
|
|
262
|
+
* the resolved identity keeps the decision in one place —
|
|
263
|
+
* `resolveSupplierIdentity` — instead of splitting it across a shipping
|
|
264
|
+
* helper and a checkout action.
|
|
265
|
+
*/
|
|
266
|
+
storeTaxIdentity: store
|
|
267
|
+
? {
|
|
268
|
+
gstin: store.gstin,
|
|
269
|
+
legalName: store.legalName,
|
|
270
|
+
registeredAddress: store.registeredAddress,
|
|
271
|
+
}
|
|
272
|
+
: undefined,
|
|
249
273
|
};
|
|
250
274
|
}
|
|
251
275
|
/**
|
|
@@ -475,7 +499,17 @@ async function createOrderForGroup(group, orderType, ctx) {
|
|
|
475
499
|
return expandCartLineToOrderRows(item, product, ctx.productById).map((baseLine) => itemRule.decorateOrderItem(baseLine, product));
|
|
476
500
|
});
|
|
477
501
|
const totalQuantity = group.reduce((sum, { item }) => sum + item.quantity, 0);
|
|
478
|
-
const { shippingFee, storeOwnerId, storeEmiEnabled, storeState, storeProtections } = await resolveShippingCost(firstItem.storeId);
|
|
502
|
+
const { shippingFee, storeOwnerId, storeEmiEnabled, storeState, storeProtections, storeTaxIdentity } = await resolveShippingCost(firstItem.storeId);
|
|
503
|
+
/*
|
|
504
|
+
* Who issues this invoice — the seller if they have their own GSTIN, the
|
|
505
|
+
* platform otherwise. ONE function decides, and it is the only place that
|
|
506
|
+
* knows the order (Root Cause #75: a money rule reachable by copy-paste
|
|
507
|
+
* gets copied, and the copies miss branches added later).
|
|
508
|
+
*
|
|
509
|
+
* Snapshot below and never recomputed: `OrderDocument`'s own comment is
|
|
510
|
+
* that these are the registration "as it stood at the moment of supply".
|
|
511
|
+
*/
|
|
512
|
+
const supplier = resolveSupplierIdentity(storeTaxIdentity, gstSettings ? { gst: gstSettings } : undefined);
|
|
479
513
|
// P-8 GST — sum per-line-item tax (each product may carry its own gstRate),
|
|
480
514
|
// then split the group total into a single cgst/sgst/igst breakdown for the
|
|
481
515
|
// order document. Skipped entirely when GST is off or the buyer's state is
|
|
@@ -671,12 +705,17 @@ async function createOrderForGroup(group, orderType, ctx) {
|
|
|
671
705
|
* `taxableAmount` so the taxed base stays the base the tax was charged on,
|
|
672
706
|
* and recorded here so the invoice's columns can reconcile. */
|
|
673
707
|
exemptAmount: gstBreakdown?.exemptAmount || undefined,
|
|
674
|
-
/*
|
|
675
|
-
* GSTIN is not a tax invoice, and the invoice page cannot read
|
|
676
|
-
* site settings — `gst` is in PRIVATE_SITE_FIELDS. See the schema.
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
708
|
+
/* The supplier of record AS IT STANDS NOW. A tax invoice without the
|
|
709
|
+
* supplier's GSTIN is not a tax invoice, and the invoice page cannot read
|
|
710
|
+
* it from site settings — `gst` is in PRIVATE_SITE_FIELDS. See the schema.
|
|
711
|
+
*
|
|
712
|
+
* 🛑 This was `gstSettings.gstin` unconditionally — the PLATFORM's
|
|
713
|
+
* registration on every order ever placed, with no way for a seller to
|
|
714
|
+
* invoice under their own, while the seeded FAQ told buyers otherwise.
|
|
715
|
+
* It now resolves seller-first via `resolveSupplierIdentity`. */
|
|
716
|
+
supplierGstin: supplier.gstin,
|
|
717
|
+
supplierLegalName: supplier.legalName,
|
|
718
|
+
supplierAddress: supplier.address,
|
|
680
719
|
/* Per-rate, for Rule 46. Computed by `sumGroupGst` rather than re-derived
|
|
681
720
|
* downstream — a mixed 5%+18% order cannot produce a compliant invoice
|
|
682
721
|
* from the aggregate triple alone. */
|
|
@@ -873,6 +912,23 @@ export async function createCheckoutOrderAction(input) {
|
|
|
873
912
|
preTxProductById.set(preTxLookup.productIds[i], snap.data());
|
|
874
913
|
}
|
|
875
914
|
}
|
|
915
|
+
/*
|
|
916
|
+
* Payment-method gate — before `enforceMaxPerUserForCart`, which runs count
|
|
917
|
+
* queries, because this answer needs no reads at all.
|
|
918
|
+
*
|
|
919
|
+
* 🛑 `preTxProductById` is the right map and `preTxPairs` below is NOT: that
|
|
920
|
+
* pairing keeps only `getCartItemMemberIds(item)[0]`, so a bundle or grouped
|
|
921
|
+
* line contributes one member and the rest are invisible. The gate walks
|
|
922
|
+
* every member of every line against this map.
|
|
923
|
+
*
|
|
924
|
+
* 🛑 This ONE call site is complete coverage, and that is worth stating so
|
|
925
|
+
* nobody adds a second. Only `cod`/`cash`/`emi` are ever blocked (auction,
|
|
926
|
+
* bundle, prize-draw) and all three arrive here; the Razorpay path is always
|
|
927
|
+
* `"online"`, which no rule blocks — and a throw there would land AFTER
|
|
928
|
+
* capture, taking the buyer's money and then refusing the order. Pre-capture
|
|
929
|
+
* is the only safe place for this, and the method is decided here.
|
|
930
|
+
*/
|
|
931
|
+
assertPaymentMethodAllowed(cartItems, preTxProductById, paymentMethod);
|
|
876
932
|
const preTxPairs = cartItems
|
|
877
933
|
.map((item) => {
|
|
878
934
|
const [firstMember] = getCartItemMemberIds(item);
|
|
@@ -982,6 +1038,28 @@ export async function createCheckoutOrderAction(input) {
|
|
|
982
1038
|
throw new ValidationError(ERROR_MESSAGES.CHECKOUT.INSUFFICIENT_STOCK);
|
|
983
1039
|
}
|
|
984
1040
|
const { coupons: appliedCoupons, dropped: droppedCoupons } = await revalidateAppliedCoupons(cart.appliedCoupons ?? [], available, uid);
|
|
1041
|
+
/*
|
|
1042
|
+
* The THIRD group-rule enforcement point: the picker mirrors the rules,
|
|
1043
|
+
* `addGroupLineToCart` enforces them at add time, and this re-checks them at
|
|
1044
|
+
* placement. Without it a rule edited after a line was already in a cart is
|
|
1045
|
+
* never re-checked at all, and a quoted discount percentage outlives the rule
|
|
1046
|
+
* it was copied from.
|
|
1047
|
+
*
|
|
1048
|
+
* 🛑 Runs AFTER the stock bucketing, over `available` rather than the whole
|
|
1049
|
+
* cart — a line already dropped for being out of stock does not need its
|
|
1050
|
+
* group rules adjudicated too, and reporting both about one line would be two
|
|
1051
|
+
* explanations for one outcome.
|
|
1052
|
+
*
|
|
1053
|
+
* A blocked line THROWS: its selection is no longer valid and no price is
|
|
1054
|
+
* right for it. A repriced one is surfaced, which is `droppedCoupons`'
|
|
1055
|
+
* discipline — the buyer is on the payment step and cannot fix a seller's
|
|
1056
|
+
* rule change from there.
|
|
1057
|
+
*/
|
|
1058
|
+
const groupRecheck = await revalidateGroupLines(available.map(({ item }) => item));
|
|
1059
|
+
if (groupRecheck.blocked.length > 0) {
|
|
1060
|
+
const first = groupRecheck.blocked[0];
|
|
1061
|
+
throw Object.assign(new ValidationError(`"${first.groupTitle}" can no longer be bought as selected. ${first.reason} Update that line in your cart and try again.`), { code: "GROUP_RULES_CHANGED" });
|
|
1062
|
+
}
|
|
985
1063
|
const orderGroups = splitCartIntoOrderGroups(available);
|
|
986
1064
|
// Admin-bypass orders are immediately paid and processing; generate a shared
|
|
987
1065
|
// transaction reference for all orders in this checkout batch.
|
|
@@ -1039,6 +1117,15 @@ export async function createCheckoutOrderAction(input) {
|
|
|
1039
1117
|
itemCount: orderIds.length,
|
|
1040
1118
|
...(unavailable.length > 0 ? { unavailableItems: unavailable } : {}),
|
|
1041
1119
|
...(droppedCoupons.length > 0 ? { droppedCoupons } : {}),
|
|
1120
|
+
...(groupRecheck.repriced.length > 0
|
|
1121
|
+
? {
|
|
1122
|
+
repricedGroupLines: groupRecheck.repriced.map((r) => ({
|
|
1123
|
+
groupTitle: r.groupTitle,
|
|
1124
|
+
previousPercent: r.previousPercent,
|
|
1125
|
+
currentPercent: r.currentPercent,
|
|
1126
|
+
})),
|
|
1127
|
+
}
|
|
1128
|
+
: {}),
|
|
1042
1129
|
};
|
|
1043
1130
|
}
|
|
1044
1131
|
const EMPTY_PRICING_PREVIEW = {
|
|
@@ -1387,7 +1474,14 @@ async function createPhonePeGroupOrder(group, orderType, ctx) {
|
|
|
1387
1474
|
// Reuses the same store/seller lookup as the COD/UPI path above instead
|
|
1388
1475
|
// of re-implementing it inline — was two sequential findById calls per
|
|
1389
1476
|
// seller group here, duplicated from resolveShippingCost.
|
|
1390
|
-
const { shippingFee, storeOwnerId, storeState } = await resolveShippingCost(firstItem.storeId);
|
|
1477
|
+
const { shippingFee, storeOwnerId, storeState, storeTaxIdentity } = await resolveShippingCost(firstItem.storeId);
|
|
1478
|
+
/*
|
|
1479
|
+
* Same supplier-of-record rule as `createOrderForGroup`, through the same
|
|
1480
|
+
* function. This path (PhonePe verify / webhook) is the SECOND place an
|
|
1481
|
+
* order is created, and the two have drifted before — add-on fees, and the
|
|
1482
|
+
* whole tax breakdown, were both missing here until C0c defect 1.
|
|
1483
|
+
*/
|
|
1484
|
+
const supplier = resolveSupplierIdentity(storeTaxIdentity, siteSettings ? { gst: siteSettings.gst } : undefined);
|
|
1391
1485
|
const groupLines = group.map(({ item, product }) => ({ itemId: item.itemId, lineTotal: lineTotalFor(item, product) }));
|
|
1392
1486
|
const { couponDiscount, appliedDiscounts } = computeGroupCouponDiscount(appliedCoupons, groupLines, groupTotal, cartSubtotal, firstItem.storeId);
|
|
1393
1487
|
// Mirrors createOrderForGroup — without this the online-payment path wrote
|
|
@@ -1543,9 +1637,9 @@ async function createPhonePeGroupOrder(group, orderType, ctx) {
|
|
|
1543
1637
|
exemptAmount: phonepeGst?.exemptAmount || undefined,
|
|
1544
1638
|
// Our registration at the moment of supply — see the schema. Mirrors the
|
|
1545
1639
|
// COD/UPI path so both produce an invoiceable order.
|
|
1546
|
-
supplierGstin:
|
|
1547
|
-
supplierLegalName:
|
|
1548
|
-
supplierAddress:
|
|
1640
|
+
supplierGstin: supplier.gstin,
|
|
1641
|
+
supplierLegalName: supplier.legalName,
|
|
1642
|
+
supplierAddress: supplier.address,
|
|
1549
1643
|
gstByRate: phonepeGst?.byRate.length ? phonepeGst.byRate : undefined,
|
|
1550
1644
|
gstAmount: (phonepeGst?.gstAmount ?? 0) + platformFeeGst || undefined,
|
|
1551
1645
|
cgst: phonepeGst?.cgst || undefined,
|
|
@@ -60,6 +60,23 @@ export function validateCartItemStock(item, productById, decrements) {
|
|
|
60
60
|
if (!product || product.status !== ProductStatusValues.PUBLISHED) {
|
|
61
61
|
return { productId: member.productId, availableQty: product?.availableQuantity ?? 0 };
|
|
62
62
|
}
|
|
63
|
+
/*
|
|
64
|
+
* 🛑 Made to order: skip the stock CHECK for this member.
|
|
65
|
+
*
|
|
66
|
+
* There is nothing to be short of — the thing is printed or built when the
|
|
67
|
+
* order arrives. `availableQuantity` on such a row goes negative by design
|
|
68
|
+
* and that negative IS the production queue, which is why the check is
|
|
69
|
+
* bypassed here and `getExpandedDecrements` above is deliberately left
|
|
70
|
+
* alone: it still counts, the decrement still lands, and
|
|
71
|
+
* `stock-restore.ts` can still mirror it exactly on a cancellation. Skip
|
|
72
|
+
* the decrement instead and a cancelled order restores stock that was
|
|
73
|
+
* never taken.
|
|
74
|
+
*
|
|
75
|
+
* Placed AFTER the published-status check on purpose: a draft or archived
|
|
76
|
+
* listing is unbuyable whatever its production model.
|
|
77
|
+
*/
|
|
78
|
+
if (product.madeToOrder === true)
|
|
79
|
+
continue;
|
|
63
80
|
// The fallback must be member-weighted too. Left as a bare `item.quantity`
|
|
64
81
|
// it would validate a line needing 3× of a member against demand for 1
|
|
65
82
|
// whenever the cumulative map didn't happen to carry that product.
|
|
@@ -35,4 +35,26 @@ export interface CheckoutOrderResult {
|
|
|
35
35
|
code: string;
|
|
36
36
|
reason: string;
|
|
37
37
|
}[];
|
|
38
|
+
/**
|
|
39
|
+
* Grouped lines whose group DISCOUNT had changed since they were added. The
|
|
40
|
+
* order is priced at the group's current rule and the change surfaced here,
|
|
41
|
+
* for the same reason `droppedCoupons` exists: honouring the stale percent
|
|
42
|
+
* would charge less than the seller now offers, and changing a total the
|
|
43
|
+
* buyer has read without telling them is worse than either.
|
|
44
|
+
*/
|
|
45
|
+
repricedGroupLines?: {
|
|
46
|
+
groupTitle: string;
|
|
47
|
+
previousPercent: number;
|
|
48
|
+
currentPercent: number;
|
|
49
|
+
}[];
|
|
50
|
+
/**
|
|
51
|
+
* Grouped lines whose selection no longer satisfies the group's rules — the
|
|
52
|
+
* seller tightened them after the line was added. REPORTED rather than
|
|
53
|
+
* repriced: "this group now requires item X" cannot be fixed by adjusting a
|
|
54
|
+
* number, so the line is not silently bought.
|
|
55
|
+
*/
|
|
56
|
+
blockedGroupLines?: {
|
|
57
|
+
groupTitle: string;
|
|
58
|
+
reason: string;
|
|
59
|
+
}[];
|
|
38
60
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { CartItemDocument } from "../../../../features/cart/schemas/firestore";
|
|
2
|
+
export interface GroupLineRepriced {
|
|
3
|
+
itemId: string;
|
|
4
|
+
groupTitle: string;
|
|
5
|
+
/** What the line was quoted at. */
|
|
6
|
+
previousPercent: number;
|
|
7
|
+
/** What the group's rule says now. */
|
|
8
|
+
currentPercent: number;
|
|
9
|
+
}
|
|
10
|
+
export interface GroupLineBlocked {
|
|
11
|
+
itemId: string;
|
|
12
|
+
groupTitle: string;
|
|
13
|
+
/** Buyer-facing, from `checkGroupRules`. */
|
|
14
|
+
reason: string;
|
|
15
|
+
}
|
|
16
|
+
export interface GroupRuleRevalidation {
|
|
17
|
+
repriced: GroupLineRepriced[];
|
|
18
|
+
blocked: GroupLineBlocked[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Re-validate every multi-member line in the checkout batch.
|
|
22
|
+
*
|
|
23
|
+
* 🛑 A null `rules` — the group is gone, or the read failed — is treated as
|
|
24
|
+
* UNRESTRICTED and the line is left exactly as it is. Failing open is right in
|
|
25
|
+
* both cases: a transient Firestore error must not block a payment, and a
|
|
26
|
+
* deleted group is not a reason to refuse a selection the buyer already made
|
|
27
|
+
* and already had priced.
|
|
28
|
+
*
|
|
29
|
+
* 🛑 Bundle lines are deliberately skipped. A bundle is all-or-nothing with its
|
|
30
|
+
* own locked price — `unitPriceFor`'s bundle branch runs BEFORE its members
|
|
31
|
+
* branch precisely so the discount that IS the bundle is not recomputed from
|
|
32
|
+
* list prices — so self-assembly rules have nothing to say about one.
|
|
33
|
+
*/
|
|
34
|
+
export declare function revalidateGroupLines(items: readonly CartItemDocument[]): Promise<GroupRuleRevalidation>;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: A group's rules and its discount are seller-editable, and a cart can sit
|
|
3
|
+
* for days. `addGroupLineToCart` checked both at add time and nothing
|
|
4
|
+
* re-checked them — so a rule tightened afterwards was never applied, and
|
|
5
|
+
* a line kept quoting a discount percentage the seller had since changed.
|
|
6
|
+
* WHAT: The third enforcement point — re-validate every multi-member line
|
|
7
|
+
* against its group's CURRENT rules, immediately before pricing.
|
|
8
|
+
*
|
|
9
|
+
* 🛑 IT REPORTS, IT DOES NOT SILENTLY RESOLVE. A changed discount is the
|
|
10
|
+
* delicate case and it has two wrong answers: honouring the stale percent
|
|
11
|
+
* charges less than the seller now offers, and re-deriving silently changes a
|
|
12
|
+
* total the buyer has already read and agreed to. So the line is repriced to
|
|
13
|
+
* the current rule AND the change is handed back to the caller to surface —
|
|
14
|
+
* exactly what `revalidateAppliedCoupons` does with a lapsed coupon, for the
|
|
15
|
+
* same reason: the buyer is on the payment step and cannot fix a seller's rule
|
|
16
|
+
* change from there, so failing the whole checkout is hostile.
|
|
17
|
+
*
|
|
18
|
+
* 🛑 A rule VIOLATION is different from a repriced discount and is treated
|
|
19
|
+
* differently. "This group now requires item X" cannot be fixed by adjusting a
|
|
20
|
+
* number — the selection itself is no longer valid — so that line is reported
|
|
21
|
+
* as blocked rather than quietly bought.
|
|
22
|
+
*
|
|
23
|
+
* EXPORTS: GroupLineRepriced, GroupLineBlocked, GroupRuleRevalidation,
|
|
24
|
+
* revalidateGroupLines
|
|
25
|
+
*
|
|
26
|
+
* @tag domain:checkout
|
|
27
|
+
* @tag layer:server
|
|
28
|
+
* @tag pattern:none
|
|
29
|
+
* @tag access:server
|
|
30
|
+
* @tag consumers:checkout/actions
|
|
31
|
+
* @tag sideEffects:firestore
|
|
32
|
+
*/
|
|
33
|
+
import { safeRead } from "../../../../errors/safe-read.js";
|
|
34
|
+
import { serverLogger } from "../../../../monitoring/server-logger.js";
|
|
35
|
+
import { productRepository } from "../../../../repositories/index.js";
|
|
36
|
+
import { groupedListingsRepository } from "../../../../features/grouped/repository/grouped-listings.repository.js";
|
|
37
|
+
import { checkGroupRules, readGroupRules, } from "../../../shared/features/products/group-rules.js";
|
|
38
|
+
import { isMultiMemberLine } from "../../../shared/checkout/line-members.js";
|
|
39
|
+
/** One group's rules, from whichever of the two homes owns them. */
|
|
40
|
+
async function rulesFor(item) {
|
|
41
|
+
const groupId = item.groupId;
|
|
42
|
+
if (!groupId)
|
|
43
|
+
return null;
|
|
44
|
+
if (item.groupSource === "grouped-listing") {
|
|
45
|
+
const doc = await safeRead(() => groupedListingsRepository.findById(groupId), {
|
|
46
|
+
route: "/checkout",
|
|
47
|
+
key: "groupedListings.rules",
|
|
48
|
+
fallback: null,
|
|
49
|
+
});
|
|
50
|
+
return doc ? readGroupRules(doc) : null;
|
|
51
|
+
}
|
|
52
|
+
/*
|
|
53
|
+
* A product group's rules live on the PARENT product, and `groupId` IS the
|
|
54
|
+
* parent's slug (`startGroup` sets `groupId = slug`, and products are pure
|
|
55
|
+
* slugs), so this resolves directly.
|
|
56
|
+
*/
|
|
57
|
+
const parent = await safeRead(() => productRepository.findById(groupId), {
|
|
58
|
+
route: "/checkout",
|
|
59
|
+
key: "products.groupParentRules",
|
|
60
|
+
fallback: null,
|
|
61
|
+
});
|
|
62
|
+
return parent ? readGroupRules(parent) : null;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Re-validate every multi-member line in the checkout batch.
|
|
66
|
+
*
|
|
67
|
+
* 🛑 A null `rules` — the group is gone, or the read failed — is treated as
|
|
68
|
+
* UNRESTRICTED and the line is left exactly as it is. Failing open is right in
|
|
69
|
+
* both cases: a transient Firestore error must not block a payment, and a
|
|
70
|
+
* deleted group is not a reason to refuse a selection the buyer already made
|
|
71
|
+
* and already had priced.
|
|
72
|
+
*
|
|
73
|
+
* 🛑 Bundle lines are deliberately skipped. A bundle is all-or-nothing with its
|
|
74
|
+
* own locked price — `unitPriceFor`'s bundle branch runs BEFORE its members
|
|
75
|
+
* branch precisely so the discount that IS the bundle is not recomputed from
|
|
76
|
+
* list prices — so self-assembly rules have nothing to say about one.
|
|
77
|
+
*/
|
|
78
|
+
export async function revalidateGroupLines(items) {
|
|
79
|
+
const repriced = [];
|
|
80
|
+
const blocked = [];
|
|
81
|
+
const lines = items.filter((i) => isMultiMemberLine(i) && !i.bundleCategorySlug && i.lineKind === "group");
|
|
82
|
+
if (lines.length === 0)
|
|
83
|
+
return { repriced, blocked };
|
|
84
|
+
for (const item of lines) {
|
|
85
|
+
const rules = await rulesFor(item);
|
|
86
|
+
if (!rules)
|
|
87
|
+
continue;
|
|
88
|
+
const members = item.groupMembers ?? [];
|
|
89
|
+
const violations = checkGroupRules(rules, members.map((m) => ({
|
|
90
|
+
productId: m.productId,
|
|
91
|
+
quantity: m.quantity,
|
|
92
|
+
title: m.title,
|
|
93
|
+
})));
|
|
94
|
+
if (violations.length > 0) {
|
|
95
|
+
blocked.push({
|
|
96
|
+
itemId: item.itemId,
|
|
97
|
+
groupTitle: item.groupTitle ?? item.productTitle,
|
|
98
|
+
reason: violations[0].message,
|
|
99
|
+
});
|
|
100
|
+
/*
|
|
101
|
+
* Reported and NOT repriced. A blocked line's price is irrelevant — the
|
|
102
|
+
* caller is about to refuse it — and repricing it first would leave a
|
|
103
|
+
* stored price for a line that never sold.
|
|
104
|
+
*/
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const quoted = item.groupDiscountPercent ?? 0;
|
|
108
|
+
const current = Math.min(100, Math.max(0, rules.groupDiscountPercent ?? 0));
|
|
109
|
+
if (quoted !== current) {
|
|
110
|
+
repriced.push({
|
|
111
|
+
itemId: item.itemId,
|
|
112
|
+
groupTitle: item.groupTitle ?? item.productTitle,
|
|
113
|
+
previousPercent: quoted,
|
|
114
|
+
currentPercent: current,
|
|
115
|
+
});
|
|
116
|
+
serverLogger.info("checkout: group discount changed since add-to-cart", {
|
|
117
|
+
itemId: item.itemId,
|
|
118
|
+
quoted,
|
|
119
|
+
current,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return { repriced, blocked };
|
|
124
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who is the supplier of record on this order's invoice.
|
|
3
|
+
*
|
|
4
|
+
* ── The rule ──────────────────────────────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* A seller WITH their own GSTIN invoices under it. A seller WITHOUT one
|
|
7
|
+
* invoices under LetItRip's. The official store carries its own, so it is the
|
|
8
|
+
* first case rather than a special case.
|
|
9
|
+
*
|
|
10
|
+
* ── Why this is one function ──────────────────────────────────────────────
|
|
11
|
+
*
|
|
12
|
+
* 🛑 It would be two lines to write `store?.gstin ?? settings.gst?.gstin` at
|
|
13
|
+
* the call site, and that is exactly how Root Cause #75 happened: a money rule
|
|
14
|
+
* described as "the single source of truth" that was reachable by copy-paste,
|
|
15
|
+
* got copied four times, and two copies never learned about a branch added
|
|
16
|
+
* later — so Razorpay captured the list price for an accepted offer. Grepping
|
|
17
|
+
* for a FUNCTION name finds every caller; grepping for the SHAPE of an
|
|
18
|
+
* expression does not.
|
|
19
|
+
*
|
|
20
|
+
* The three fields also have to move TOGETHER. A GSTIN printed under the wrong
|
|
21
|
+
* legal name is a worse invoice than no GSTIN at all, so this never mixes a
|
|
22
|
+
* seller's GSTIN with the platform's name — see `resolve` below.
|
|
23
|
+
*
|
|
24
|
+
* ── Why it is snapshot, not looked up ─────────────────────────────────────
|
|
25
|
+
*
|
|
26
|
+
* The caller writes the result onto `OrderDocument.supplierGstin` /
|
|
27
|
+
* `supplierLegalName` / `supplierAddress` at creation and never recomputes it.
|
|
28
|
+
* `OrderDocument`'s own comment is explicit that these are "OUR GST
|
|
29
|
+
* registration as it stood at the moment of supply" — an admin correcting a
|
|
30
|
+
* typo in the GSTIN must not silently reissue every invoice ever produced.
|
|
31
|
+
*/
|
|
32
|
+
import type { StoreDocument } from "../../../../features/stores/schemas/firestore";
|
|
33
|
+
import type { SiteSettingsDocument } from "../../../../features/admin/schemas/firestore";
|
|
34
|
+
export interface SupplierIdentity {
|
|
35
|
+
gstin?: string;
|
|
36
|
+
legalName?: string;
|
|
37
|
+
address?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Which party the invoice is issued by.
|
|
40
|
+
*
|
|
41
|
+
* Not decoration: the invoice renderers use it to decide whether to say
|
|
42
|
+
* "Tax Invoice", and an operator reading an order needs to know which
|
|
43
|
+
* registration was applied without re-deriving the rule.
|
|
44
|
+
*/
|
|
45
|
+
source: "seller" | "platform" | "none";
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the supplier identity for an order from `store`.
|
|
49
|
+
*
|
|
50
|
+
* Returns `source: "none"` when GST is disabled or the platform has no GSTIN
|
|
51
|
+
* configured and the seller has none either. A caller must treat that as "this
|
|
52
|
+
* is not a tax invoice" rather than as an error — GST is genuinely optional
|
|
53
|
+
* and `siteSettings.gst.enabled` defaults to false.
|
|
54
|
+
*/
|
|
55
|
+
export declare function resolveSupplierIdentity(store: Pick<StoreDocument, "gstin" | "legalName" | "registeredAddress"> | null | undefined, settings: Pick<SiteSettingsDocument, "gst"> | null | undefined): SupplierIdentity;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who is the supplier of record on this order's invoice.
|
|
3
|
+
*
|
|
4
|
+
* ── The rule ──────────────────────────────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* A seller WITH their own GSTIN invoices under it. A seller WITHOUT one
|
|
7
|
+
* invoices under LetItRip's. The official store carries its own, so it is the
|
|
8
|
+
* first case rather than a special case.
|
|
9
|
+
*
|
|
10
|
+
* ── Why this is one function ──────────────────────────────────────────────
|
|
11
|
+
*
|
|
12
|
+
* 🛑 It would be two lines to write `store?.gstin ?? settings.gst?.gstin` at
|
|
13
|
+
* the call site, and that is exactly how Root Cause #75 happened: a money rule
|
|
14
|
+
* described as "the single source of truth" that was reachable by copy-paste,
|
|
15
|
+
* got copied four times, and two copies never learned about a branch added
|
|
16
|
+
* later — so Razorpay captured the list price for an accepted offer. Grepping
|
|
17
|
+
* for a FUNCTION name finds every caller; grepping for the SHAPE of an
|
|
18
|
+
* expression does not.
|
|
19
|
+
*
|
|
20
|
+
* The three fields also have to move TOGETHER. A GSTIN printed under the wrong
|
|
21
|
+
* legal name is a worse invoice than no GSTIN at all, so this never mixes a
|
|
22
|
+
* seller's GSTIN with the platform's name — see `resolve` below.
|
|
23
|
+
*
|
|
24
|
+
* ── Why it is snapshot, not looked up ─────────────────────────────────────
|
|
25
|
+
*
|
|
26
|
+
* The caller writes the result onto `OrderDocument.supplierGstin` /
|
|
27
|
+
* `supplierLegalName` / `supplierAddress` at creation and never recomputes it.
|
|
28
|
+
* `OrderDocument`'s own comment is explicit that these are "OUR GST
|
|
29
|
+
* registration as it stood at the moment of supply" — an admin correcting a
|
|
30
|
+
* typo in the GSTIN must not silently reissue every invoice ever produced.
|
|
31
|
+
*/
|
|
32
|
+
const trimmed = (v) => {
|
|
33
|
+
const s = (v ?? "").trim();
|
|
34
|
+
return s.length > 0 ? s : undefined;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Resolve the supplier identity for an order from `store`.
|
|
38
|
+
*
|
|
39
|
+
* Returns `source: "none"` when GST is disabled or the platform has no GSTIN
|
|
40
|
+
* configured and the seller has none either. A caller must treat that as "this
|
|
41
|
+
* is not a tax invoice" rather than as an error — GST is genuinely optional
|
|
42
|
+
* and `siteSettings.gst.enabled` defaults to false.
|
|
43
|
+
*/
|
|
44
|
+
export function resolveSupplierIdentity(store, settings) {
|
|
45
|
+
const sellerGstin = trimmed(store?.gstin);
|
|
46
|
+
/*
|
|
47
|
+
* Seller branch. Taken on the GSTIN alone, and the legal name and address
|
|
48
|
+
* come from the SELLER too — falling back to the platform's for a missing
|
|
49
|
+
* name would print LetItRip's legal entity beside the seller's registration,
|
|
50
|
+
* which identifies neither party correctly.
|
|
51
|
+
*
|
|
52
|
+
* A seller with a GSTIN and no legal name is a data gap worth seeing as a
|
|
53
|
+
* blank on the invoice, not one worth papering over with the wrong name.
|
|
54
|
+
*/
|
|
55
|
+
if (sellerGstin) {
|
|
56
|
+
return {
|
|
57
|
+
gstin: sellerGstin,
|
|
58
|
+
legalName: trimmed(store?.legalName),
|
|
59
|
+
address: trimmed(store?.registeredAddress),
|
|
60
|
+
source: "seller",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/*
|
|
64
|
+
* Platform branch. Gated on `gst.enabled`, matching what the checkout
|
|
65
|
+
* already did before sellers could have a GSTIN at all — disabling GST must
|
|
66
|
+
* continue to produce plain invoices rather than tax invoices.
|
|
67
|
+
*/
|
|
68
|
+
const gst = settings?.gst;
|
|
69
|
+
if (gst?.enabled) {
|
|
70
|
+
const platformGstin = trimmed(gst.gstin);
|
|
71
|
+
if (platformGstin) {
|
|
72
|
+
return {
|
|
73
|
+
gstin: platformGstin,
|
|
74
|
+
legalName: trimmed(gst.legalName),
|
|
75
|
+
address: trimmed(gst.address),
|
|
76
|
+
source: "platform",
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return { source: "none" };
|
|
81
|
+
}
|
|
@@ -33,7 +33,7 @@ export function buildClassifiedMetadata(product, opts) {
|
|
|
33
33
|
description,
|
|
34
34
|
url: canonical,
|
|
35
35
|
type: "website",
|
|
36
|
-
images: image ? [{ url: image }] : undefined,
|
|
36
|
+
images: image ? [{ url: typeof image === "string" ? image : image.url }] : undefined,
|
|
37
37
|
},
|
|
38
38
|
twitter: {
|
|
39
39
|
card: image ? "summary_large_image" : "summary",
|
|
@@ -10,7 +10,9 @@ interface ClassifiedDocLike {
|
|
|
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
|
classified?: {
|
|
15
17
|
meetupArea?: {
|
|
16
18
|
city?: string;
|
|
@@ -8,7 +8,9 @@ export function renderClassifiedOg(doc, opts) {
|
|
|
8
8
|
return renderClassifiedOgImage({
|
|
9
9
|
title: doc?.title ?? "Classified Listing",
|
|
10
10
|
location,
|
|
11
|
-
imageUrl: resolveOgImageUrl(doc?.mainImage ||
|
|
11
|
+
imageUrl: resolveOgImageUrl(doc?.mainImage ||
|
|
12
|
+
(typeof doc?.images?.[0] === "string" ? doc.images[0] : doc?.images?.[0]?.url) ||
|
|
13
|
+
null, opts.baseUrl),
|
|
12
14
|
}, opts.siteName);
|
|
13
15
|
}
|
|
14
16
|
export function renderClassifiedOgImage(data, siteName) {
|
|
@@ -26,7 +26,7 @@ export function buildDigitalCodeMetadata(product, opts) {
|
|
|
26
26
|
description,
|
|
27
27
|
url: canonical,
|
|
28
28
|
type: "website",
|
|
29
|
-
images: image ? [{ url: image }] : undefined,
|
|
29
|
+
images: image ? [{ url: typeof image === "string" ? image : image.url }] : undefined,
|
|
30
30
|
},
|
|
31
31
|
twitter: {
|
|
32
32
|
card: image ? "summary_large_image" : "summary",
|
|
@@ -10,7 +10,9 @@ interface DigitalCodeDocLike {
|
|
|
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
|
digitalCode?: {
|
|
15
17
|
codeDeliveryMethod?: string;
|
|
16
18
|
} | null;
|
|
@@ -9,7 +9,9 @@ export function renderDigitalCodeOg(doc, opts) {
|
|
|
9
9
|
return renderDigitalCodeOgImage({
|
|
10
10
|
title: doc?.title ?? "Digital Code",
|
|
11
11
|
deliveryMethod,
|
|
12
|
-
imageUrl: resolveOgImageUrl(doc?.mainImage ||
|
|
12
|
+
imageUrl: resolveOgImageUrl(doc?.mainImage ||
|
|
13
|
+
(typeof doc?.images?.[0] === "string" ? doc.images[0] : doc?.images?.[0]?.url) ||
|
|
14
|
+
null, opts.baseUrl),
|
|
13
15
|
}, opts.siteName);
|
|
14
16
|
}
|
|
15
17
|
export function renderDigitalCodeOgImage(data, siteName) {
|
|
@@ -20,10 +20,25 @@ import { type ProductCodeContentKind } from "../../../../features/products/schem
|
|
|
20
20
|
/**
|
|
21
21
|
* Where a delivered asset's bytes live.
|
|
22
22
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
23
|
+
* No `mediaAssets` row is ever created for one — a row is what mints a
|
|
24
|
+
* `/media/{shortId}` slug, and that slug is served without authentication. No
|
|
25
|
+
* row, no slug, no public URL. That half was, and remains, true.
|
|
26
|
+
*
|
|
27
|
+
* 🛑 CORRECTED 2026-10-19. This block used to open with "deliberately OUTSIDE
|
|
28
|
+
* every prefix the public media proxy knows how to resolve", and that was
|
|
29
|
+
* FALSE from the day the proxy's raw-path fallback shipped: `/media/[...slug]`
|
|
30
|
+
* treats any multi-segment slug it cannot resolve through `mediaAssets` as a
|
|
31
|
+
* literal storage path, so `/media/private/digital-content/...` resolved and
|
|
32
|
+
* served. It did watermark, so no pristine original leaked — but a paid asset
|
|
33
|
+
* was reachable by anyone who guessed the object name, and the only thing
|
|
34
|
+
* standing in the way was `randomUUID()`.
|
|
35
|
+
*
|
|
36
|
+
* The proxy now refuses a raw path under `private/` outright
|
|
37
|
+
* (`PRIVATE_PREFIX` in `src/app/api/media/[...slug]/route.ts`), so the
|
|
38
|
+
* statement is true again — but it is true because of a check, not because of
|
|
39
|
+
* the prefix, and that distinction is the whole reason this comment was wrong
|
|
40
|
+
* for so long. An unenforced invariant written as a fact is worse than no
|
|
41
|
+
* comment: it is the reason nobody looked.
|
|
27
42
|
*/
|
|
28
43
|
export declare const DIGITAL_CONTENT_PREFIX = "private/digital-content";
|
|
29
44
|
/**
|