@mohasinac/appkit 4.52.5 → 4.53.0
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 +4 -4
- package/dist/_internal/client/features/sticker-trace/canvas-codec.d.ts +2 -0
- package/dist/_internal/client/features/sticker-trace/canvas-codec.js +148 -0
- package/dist/_internal/server/features/auctions/actions.js +1 -1
- package/dist/_internal/server/features/categories/data.js +23 -7
- package/dist/_internal/server/features/checkout/actions.js +10 -3
- package/dist/_internal/server/features/checkout/data.d.ts +15 -0
- package/dist/_internal/server/features/checkout/data.js +20 -3
- package/dist/_internal/server/features/lottery/actions.js +9 -1
- package/dist/_internal/server/features/lottery/og.d.ts +40 -0
- package/dist/_internal/server/features/lottery/og.js +79 -0
- package/dist/_internal/server/features/messages/actions.js +4 -0
- package/dist/_internal/server/features/offers/adapters.js +17 -3
- package/dist/_internal/server/features/orders/actions.js +7 -2
- package/dist/_internal/server/features/raffle/actions.d.ts +19 -0
- package/dist/_internal/server/features/raffle/actions.js +25 -0
- package/dist/_internal/server/features/reviews/actions.js +1 -1
- package/dist/_internal/server/features/reviews/data.d.ts +1 -1
- package/dist/_internal/server/features/search/index-build/artifact-store.d.ts +89 -0
- package/dist/_internal/server/features/search/index-build/artifact-store.js +208 -0
- package/dist/_internal/server/features/search/index-build/build.d.ts +57 -0
- package/dist/_internal/server/features/search/index-build/build.js +285 -0
- package/dist/_internal/server/features/search/index-build/sources.d.ts +53 -0
- package/dist/_internal/server/features/search/index-build/sources.js +385 -0
- package/dist/_internal/server/features/search/search-sources.d.ts +0 -15
- package/dist/_internal/server/features/search/search-sources.js +142 -7
- package/dist/_internal/server/features/seo/robots.js +21 -1
- package/dist/_internal/server/features/seo/sitemap.js +1 -1
- package/dist/_internal/server/features/shared/listing-data-factory.js +16 -4
- package/dist/_internal/server/features/site-settings/adapters.d.ts +1 -1
- package/dist/_internal/server/features/site-settings/adapters.js +26 -0
- package/dist/_internal/server/features/sticker-trace/actions.d.ts +109 -0
- package/dist/_internal/server/features/sticker-trace/actions.js +172 -0
- package/dist/_internal/server/features/sticker-trace/adapters.d.ts +80 -0
- package/dist/_internal/server/features/sticker-trace/adapters.js +174 -0
- package/dist/_internal/server/features/sticker-trace/build-cut-file.d.ts +26 -0
- package/dist/_internal/server/features/sticker-trace/build-cut-file.js +127 -0
- package/dist/_internal/server/features/sticker-trace/coordinator.d.ts +27 -0
- package/dist/_internal/server/features/sticker-trace/coordinator.js +169 -0
- package/dist/_internal/server/features/sticker-trace/fanout.d.ts +43 -0
- package/dist/_internal/server/features/sticker-trace/fanout.js +180 -0
- package/dist/_internal/server/features/sticker-trace/finalizer.d.ts +25 -0
- package/dist/_internal/server/features/sticker-trace/finalizer.js +123 -0
- package/dist/_internal/server/features/sticker-trace/mosaic-inputs.d.ts +30 -0
- package/dist/_internal/server/features/sticker-trace/mosaic-inputs.js +112 -0
- package/dist/_internal/server/features/sticker-trace/progress.d.ts +81 -0
- package/dist/_internal/server/features/sticker-trace/progress.js +198 -0
- package/dist/_internal/server/features/sticker-trace/promote.d.ts +23 -0
- package/dist/_internal/server/features/sticker-trace/promote.js +193 -0
- package/dist/_internal/server/features/sticker-trace/shard.d.ts +50 -0
- package/dist/_internal/server/features/sticker-trace/shard.js +174 -0
- package/dist/_internal/server/features/sticker-trace/sheet-actions.d.ts +141 -0
- package/dist/_internal/server/features/sticker-trace/sheet-actions.js +279 -0
- package/dist/_internal/server/features/sticker-trace/storage.d.ts +135 -0
- package/dist/_internal/server/features/sticker-trace/storage.js +282 -0
- package/dist/_internal/server/functions/scheduled.d.ts +2 -1
- package/dist/_internal/server/functions/scheduled.js +25 -1
- package/dist/_internal/server/jobs/core/_event-ref.d.ts +51 -0
- package/dist/_internal/server/jobs/core/_event-ref.js +63 -0
- package/dist/_internal/server/jobs/core/assignSpinPrize.d.ts +18 -0
- package/dist/_internal/server/jobs/core/assignSpinPrize.js +77 -16
- package/dist/_internal/server/jobs/core/auctionSettlement.js +13 -4
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +44 -15
- package/dist/_internal/server/jobs/core/bundleStockSync.js +3 -4
- package/dist/_internal/server/jobs/core/cleanupRtdbEvents.js +44 -0
- package/dist/_internal/server/jobs/core/event-draw-reasons.d.ts +78 -0
- package/dist/_internal/server/jobs/core/event-draw-reasons.js +133 -0
- package/dist/_internal/server/jobs/core/listingProcessor.js +28 -8
- package/dist/_internal/server/jobs/core/onOrderCreate.js +12 -1
- package/dist/_internal/server/jobs/core/onScamReportCreate.js +13 -2
- package/dist/_internal/server/jobs/core/onScamReportRejected.js +8 -2
- package/dist/_internal/server/jobs/core/onScamReportVerified.js +8 -2
- package/dist/_internal/server/jobs/core/onStoreWrite.js +11 -4
- package/dist/_internal/server/jobs/core/prizeDrawAssignWinner.js +5 -4
- package/dist/_internal/server/jobs/core/prizeDrawExpiryReveal.js +1 -4
- package/dist/_internal/server/jobs/core/prizeDrawSoldOutReveal.js +1 -2
- package/dist/_internal/server/jobs/core/promotions.js +7 -4
- package/dist/_internal/server/jobs/core/searchIndexBuild.d.ts +7 -0
- package/dist/_internal/server/jobs/core/searchIndexBuild.js +211 -0
- package/dist/_internal/server/jobs/core/triggerEventRaffle.d.ts +35 -0
- package/dist/_internal/server/jobs/core/triggerEventRaffle.js +213 -31
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +20 -14
- package/dist/_internal/server/jobs/handlers/assignSpinPrize.d.ts +8 -0
- package/dist/_internal/server/jobs/handlers/assignSpinPrize.js +15 -15
- package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
- package/dist/_internal/server/jobs/handlers/index.js +1 -0
- package/dist/_internal/server/jobs/handlers/onCategoryWrite.js +18 -0
- package/dist/_internal/server/jobs/handlers/onProductWrite.js +18 -0
- package/dist/_internal/server/jobs/handlers/onReviewWrite.js +18 -0
- package/dist/_internal/server/jobs/handlers/onScamReportUpdate.js +18 -0
- package/dist/_internal/server/jobs/handlers/onStoreWrite.js +18 -0
- package/dist/_internal/server/jobs/handlers/searchIndexBuild.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/searchIndexBuild.js +2 -0
- package/dist/_internal/server/jobs/handlers/triggerEventRaffle.d.ts +8 -0
- package/dist/_internal/server/jobs/handlers/triggerEventRaffle.js +15 -6
- package/dist/_internal/shared/actions/action-registry.js +29 -0
- package/dist/_internal/shared/cache/revalidate-targets.d.ts +0 -1
- package/dist/_internal/shared/cache/revalidate-targets.js +10 -8
- package/dist/_internal/shared/features/events/config.d.ts +14 -0
- package/dist/_internal/shared/features/events/config.js +14 -0
- package/dist/_internal/shared/features/notifications/action-url.js +8 -0
- package/dist/_internal/shared/features/site-settings/types.d.ts +11 -0
- package/dist/_internal/shared/features/sticker-trace/codec.d.ts +81 -0
- package/dist/_internal/shared/features/sticker-trace/codec.js +30 -0
- package/dist/_internal/shared/features/sticker-trace/config.d.ts +81 -0
- package/dist/_internal/shared/features/sticker-trace/config.js +177 -0
- package/dist/_internal/shared/features/sticker-trace/cut-contour.d.ts +20 -0
- package/dist/_internal/shared/features/sticker-trace/cut-contour.js +60 -0
- package/dist/_internal/shared/features/sticker-trace/export/build-export-set.d.ts +65 -0
- package/dist/_internal/shared/features/sticker-trace/export/build-export-set.js +176 -0
- package/dist/_internal/shared/features/sticker-trace/export/cut-svg.d.ts +56 -0
- package/dist/_internal/shared/features/sticker-trace/export/cut-svg.js +120 -0
- package/dist/_internal/shared/features/sticker-trace/export/machine-profiles.d.ts +92 -0
- package/dist/_internal/shared/features/sticker-trace/export/machine-profiles.js +153 -0
- package/dist/_internal/shared/features/sticker-trace/export/nesting.d.ts +51 -0
- package/dist/_internal/shared/features/sticker-trace/export/nesting.js +110 -0
- package/dist/_internal/shared/features/sticker-trace/export/page-sizes.d.ts +68 -0
- package/dist/_internal/shared/features/sticker-trace/export/page-sizes.js +223 -0
- package/dist/_internal/shared/features/sticker-trace/export/part-tiling.d.ts +51 -0
- package/dist/_internal/shared/features/sticker-trace/export/part-tiling.js +178 -0
- package/dist/_internal/shared/features/sticker-trace/export/simplify-budget.d.ts +26 -0
- package/dist/_internal/shared/features/sticker-trace/export/simplify-budget.js +134 -0
- package/dist/_internal/shared/features/sticker-trace/export/vectorise-art.d.ts +40 -0
- package/dist/_internal/shared/features/sticker-trace/export/vectorise-art.js +195 -0
- package/dist/_internal/shared/features/sticker-trace/index.d.ts +2 -0
- package/dist/_internal/shared/features/sticker-trace/index.js +59 -0
- package/dist/_internal/shared/features/sticker-trace/math/catmull-rom.d.ts +11 -0
- package/dist/_internal/shared/features/sticker-trace/math/catmull-rom.js +66 -0
- package/dist/_internal/shared/features/sticker-trace/math/edt.d.ts +27 -0
- package/dist/_internal/shared/features/sticker-trace/math/edt.js +165 -0
- package/dist/_internal/shared/features/sticker-trace/math/label.d.ts +15 -0
- package/dist/_internal/shared/features/sticker-trace/math/label.js +156 -0
- package/dist/_internal/shared/features/sticker-trace/math/marching-squares.d.ts +16 -0
- package/dist/_internal/shared/features/sticker-trace/math/marching-squares.js +242 -0
- package/dist/_internal/shared/features/sticker-trace/math/morphology.d.ts +46 -0
- package/dist/_internal/shared/features/sticker-trace/math/morphology.js +168 -0
- package/dist/_internal/shared/features/sticker-trace/math/rasterize-path.d.ts +88 -0
- package/dist/_internal/shared/features/sticker-trace/math/rasterize-path.js +286 -0
- package/dist/_internal/shared/features/sticker-trace/math/rdp.d.ts +10 -0
- package/dist/_internal/shared/features/sticker-trace/math/rdp.js +74 -0
- package/dist/_internal/shared/features/sticker-trace/resume.d.ts +50 -0
- package/dist/_internal/shared/features/sticker-trace/resume.js +160 -0
- package/dist/_internal/shared/features/sticker-trace/trace/cluster.d.ts +20 -0
- package/dist/_internal/shared/features/sticker-trace/trace/cluster.js +132 -0
- package/dist/_internal/shared/features/sticker-trace/trace/cross-profile.d.ts +28 -0
- package/dist/_internal/shared/features/sticker-trace/trace/cross-profile.js +120 -0
- package/dist/_internal/shared/features/sticker-trace/trace/flood.d.ts +21 -0
- package/dist/_internal/shared/features/sticker-trace/trace/flood.js +104 -0
- package/dist/_internal/shared/features/sticker-trace/trace/geometry.d.ts +3 -0
- package/dist/_internal/shared/features/sticker-trace/trace/geometry.js +140 -0
- package/dist/_internal/shared/features/sticker-trace/trace/line-filter.d.ts +15 -0
- package/dist/_internal/shared/features/sticker-trace/trace/line-filter.js +138 -0
- package/dist/_internal/shared/features/sticker-trace/trace/merge.d.ts +27 -0
- package/dist/_internal/shared/features/sticker-trace/trace/merge.js +187 -0
- package/dist/_internal/shared/features/sticker-trace/trace/paper.d.ts +32 -0
- package/dist/_internal/shared/features/sticker-trace/trace/paper.js +147 -0
- package/dist/_internal/shared/features/sticker-trace/trace/prefilter.d.ts +30 -0
- package/dist/_internal/shared/features/sticker-trace/trace/prefilter.js +125 -0
- package/dist/_internal/shared/features/sticker-trace/trace/quantise.d.ts +10 -0
- package/dist/_internal/shared/features/sticker-trace/trace/quantise.js +153 -0
- package/dist/_internal/shared/features/sticker-trace/trace/ridge.d.ts +88 -0
- package/dist/_internal/shared/features/sticker-trace/trace/ridge.js +344 -0
- package/dist/_internal/shared/features/sticker-trace/trace/segment.d.ts +10 -0
- package/dist/_internal/shared/features/sticker-trace/trace/segment.js +251 -0
- package/dist/_internal/shared/features/sticker-trace/types.d.ts +197 -0
- package/dist/_internal/shared/features/sticker-trace/types.js +49 -0
- package/dist/_internal/shared/listing-types/_registry.d.ts +11 -0
- package/dist/_internal/shared/listing-types/art/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/art/config.js +1 -0
- package/dist/_internal/shared/listing-types/auction/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/auction/config.js +1 -0
- package/dist/_internal/shared/listing-types/classified/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/classified/config.js +1 -0
- package/dist/_internal/shared/listing-types/digital-code/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/digital-code/config.js +1 -0
- package/dist/_internal/shared/listing-types/feature-flags.d.ts +7 -0
- package/dist/_internal/shared/listing-types/feature-flags.js +9 -0
- package/dist/_internal/shared/listing-types/live/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/live/config.js +1 -0
- package/dist/_internal/shared/listing-types/pre-order/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/pre-order/config.js +1 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.js +1 -0
- package/dist/_internal/shared/listing-types/standard/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/standard/config.js +1 -0
- package/dist/_internal/shared/listing-types/stickers/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/stickers/config.js +1 -0
- package/dist/_internal/shared/media/limits.d.ts +12 -2
- package/dist/_internal/shared/media/limits.js +12 -2
- package/dist/_internal/shared/search/artifact-url.d.ts +35 -0
- package/dist/_internal/shared/search/artifact-url.js +119 -0
- package/dist/_internal/shared/search/index-record.d.ts +68 -0
- package/dist/_internal/shared/search/index-record.js +96 -0
- package/dist/_internal/shared/search/match.d.ts +65 -0
- package/dist/_internal/shared/search/match.js +263 -0
- package/dist/client.d.ts +8 -7
- package/dist/client.js +12 -5
- package/dist/configs/eslint.js +22 -2
- package/dist/constants/api-endpoints.d.ts +68 -0
- package/dist/constants/api-endpoints.js +32 -0
- package/dist/constants/field-names.d.ts +7 -1
- package/dist/constants/field-names.js +36 -1
- package/dist/contracts/repository.d.ts +8 -7
- package/dist/contracts/search.d.ts +2 -2
- package/dist/contracts/table.d.ts +3 -3
- package/dist/contracts/table.js +3 -3
- package/dist/core/contact-submissions.repository.js +2 -2
- package/dist/core/integration-keys.js +7 -1
- package/dist/core/newsletter.repository.js +2 -2
- package/dist/core/site-config.js +1 -1
- package/dist/errors/error-codes.d.ts +30 -0
- package/dist/errors/error-codes.js +30 -0
- package/dist/errors/error-display-map.js +40 -1
- package/dist/features/account/api/[userId]/addresses/route.js +1 -1
- package/dist/features/account/components/UserBidsView.js +1 -1
- package/dist/features/account/components/index.d.ts +0 -8
- package/dist/features/account/components/index.js +0 -4
- package/dist/features/addresses/repository/addresses.repository.d.ts +17 -6
- package/dist/features/addresses/repository/addresses.repository.js +65 -9
- package/dist/features/addresses/schemas/firestore.d.ts +17 -6
- package/dist/features/admin/actions/notification-actions.js +15 -6
- package/dist/features/admin/api/bids/route.js +1 -1
- package/dist/features/admin/api/coupons/route.js +1 -1
- package/dist/features/admin/api/products/route.js +1 -1
- package/dist/features/admin/api/reviews/route.js +1 -1
- package/dist/features/admin/components/AdminAdEditorView.js +19 -5
- package/dist/features/admin/components/AdminAuditLogView.js +1 -1
- package/dist/features/admin/components/AdminBlogEditorView.js +0 -1
- package/dist/features/admin/components/AdminBlogView.js +1 -1
- package/dist/features/admin/components/AdminBrandsView.js +17 -1
- package/dist/features/admin/components/AdminCategoryEditorView.js +0 -1
- package/dist/features/admin/components/AdminContactView.js +1 -1
- package/dist/features/admin/components/AdminEmployeeEditorView.js +1 -3
- package/dist/features/admin/components/AdminNewsletterView.js +1 -1
- package/dist/features/admin/components/AdminOrdersView.js +9 -2
- package/dist/features/admin/components/AdminPayoutsView.js +1 -1
- package/dist/features/admin/components/AdminPrizeDrawsView.js +11 -0
- package/dist/features/admin/components/AdminProductEditorView.d.ts +10 -1
- package/dist/features/admin/components/AdminProductEditorView.js +24 -37
- package/dist/features/admin/components/AdminProductsView.js +10 -3
- package/dist/features/admin/components/AdminScammersView.js +1 -1
- package/dist/features/admin/components/AdminSectionsView.js +21 -5
- package/dist/features/admin/components/AdminSessionsView.js +1 -1
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +22 -1
- package/dist/features/admin/components/AdminTeamView.js +1 -1
- package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +0 -1
- package/dist/features/admin/components/AdminUsersView.js +1 -1
- package/dist/features/admin/components/DataListingView.d.ts +2 -1
- package/dist/features/admin/components/DataListingView.js +9 -4
- package/dist/features/admin/components/analytics/AdminPageViewsReportView.js +0 -2
- package/dist/features/admin/hooks/useAdminListing.d.ts +1 -1
- package/dist/features/admin/repository/site-settings.repository.js +8 -3
- package/dist/features/admin/schemas/firestore.d.ts +62 -1
- package/dist/features/admin/schemas/firestore.js +32 -0
- package/dist/features/admin/types/index.d.ts +1 -1
- package/dist/features/auctions/actions/bid-actions.d.ts +2 -2
- package/dist/features/auctions/actions/bid-actions.js +16 -4
- package/dist/features/auctions/api/route.js +1 -1
- package/dist/features/auctions/components/AuctionBottomActions.js +4 -0
- package/dist/features/auctions/repository/bid.repository.d.ts +46 -4
- package/dist/features/auctions/repository/bid.repository.js +114 -13
- package/dist/features/auctions/schemas/firestore.d.ts +24 -4
- package/dist/features/auctions/schemas/firestore.js +1 -0
- package/dist/features/auth/actions/profile-actions.js +30 -4
- package/dist/features/auth/components/index.d.ts +0 -1
- package/dist/features/auth/components/index.js +0 -1
- package/dist/features/auth/repository/session.repository.d.ts +17 -3
- package/dist/features/auth/repository/session.repository.js +46 -5
- package/dist/features/auth/repository/token.repository.d.ts +27 -4
- package/dist/features/auth/repository/token.repository.js +65 -11
- package/dist/features/auth/repository/user.repository.d.ts +33 -7
- package/dist/features/auth/repository/user.repository.js +100 -15
- package/dist/features/auth/schemas/firestore.d.ts +63 -12
- package/dist/features/auth/schemas/firestore.js +11 -0
- package/dist/features/before-after/api/route.js +1 -1
- package/dist/features/blog/components/BlogListView.js +16 -3
- package/dist/features/blog/components/BlogPostView.js +1 -1
- package/dist/features/blog/schemas/index.d.ts +2 -2
- package/dist/features/cart/api/route.js +1 -1
- package/dist/features/catalogue/repository/catalogue.repository.d.ts +7 -7
- package/dist/features/catalogue/repository/catalogue.repository.js +28 -10
- package/dist/features/catalogue/schemas/firestore.d.ts +11 -0
- package/dist/features/categories/api/route.js +10 -10
- package/dist/features/categories/components/CategoryDetailPageView.js +35 -5
- package/dist/features/categories/components/CategorySelectorCreate.js +0 -1
- package/dist/features/categories/components/index.d.ts +0 -2
- package/dist/features/categories/components/index.js +0 -1
- package/dist/features/categories/repository/categories.repository.d.ts +1 -0
- package/dist/features/categories/repository/categories.repository.js +31 -1
- package/dist/features/categories/schemas/index.d.ts +2 -2
- package/dist/features/categories/utils/taxonomy-visual.d.ts +21 -0
- package/dist/features/categories/utils/taxonomy-visual.js +58 -0
- package/dist/features/checkout/actions/checkout-value-otp-actions.js +8 -1
- package/dist/features/collections/api/[slug]/route.js +3 -3
- package/dist/features/collections/api/route.js +1 -1
- package/dist/features/consultation/api/route.js +1 -1
- package/dist/features/consultation/repository/consultations.repository.js +1 -1
- package/dist/features/consultation/types/index.d.ts +1 -1
- package/dist/features/contact/components/ContactForm.js +1 -3
- package/dist/features/corporate/api/route.js +1 -1
- package/dist/features/corporate/repository/corporate.repository.js +1 -1
- package/dist/features/corporate/types/index.d.ts +1 -1
- package/dist/features/cron/jobs/auction-expiry.job.js +1 -1
- package/dist/features/cron/jobs/preorder-reminder.job.js +1 -1
- package/dist/features/events/actions/event-actions.d.ts +14 -0
- package/dist/features/events/actions/event-actions.js +82 -3
- package/dist/features/events/api/[id]/route.js +2 -2
- package/dist/features/events/components/AdminEventEditorView.js +22 -3
- package/dist/features/events/components/AdminEventsView.js +32 -8
- package/dist/features/events/components/EventRafflesSection.d.ts +34 -1
- package/dist/features/events/components/EventRafflesSection.js +52 -3
- package/dist/features/events/components/SpinWheelView.d.ts +2 -2
- package/dist/features/events/components/SpinWheelView.js +41 -4
- package/dist/features/events/repository/event-entry.repository.d.ts +14 -2
- package/dist/features/events/repository/event-entry.repository.js +41 -7
- package/dist/features/events/schemas/firestore.d.ts +17 -4
- package/dist/features/events/types/index.d.ts +37 -0
- package/dist/features/faq/repository/faqs.repository.js +1 -1
- package/dist/features/faq/schemas/index.d.ts +5 -2
- package/dist/features/faq/schemas/index.js +6 -0
- package/dist/features/faq/types/index.d.ts +0 -1
- package/dist/features/grouped/components/GroupedListingEditorView.js +17 -2
- package/dist/features/homepage/api/carousel/[id]/route.js +1 -1
- package/dist/features/homepage/api/carousel/route.js +4 -4
- package/dist/features/homepage/api/route.js +2 -2
- package/dist/features/homepage/api/sections/route.js +1 -1
- package/dist/features/homepage/components/AnnouncementBar.d.ts +1 -9
- package/dist/features/homepage/components/AnnouncementBar.js +13 -19
- package/dist/features/homepage/components/MarketplaceHomepageView.js +6 -1
- package/dist/features/homepage/components/index.d.ts +0 -2
- package/dist/features/homepage/components/index.js +0 -1
- package/dist/features/homepage/repository/homepage.repository.js +1 -1
- package/dist/features/homepage/schemas/firestore.d.ts +6 -0
- package/dist/features/homepage/schemas/index.d.ts +10 -10
- package/dist/features/layout/AppLayoutShell.d.ts +11 -0
- package/dist/features/layout/AppLayoutShell.js +64 -12
- package/dist/features/layout/BottomActionsContext.d.ts +16 -0
- package/dist/features/layout/BottomActionsContext.js +5 -0
- package/dist/features/layout/BottomChrome.js +48 -9
- package/dist/features/layout/BottomNavbar.d.ts +14 -0
- package/dist/features/layout/BottomNavbar.js +43 -3
- package/dist/features/layout/MainNavbar.d.ts +11 -0
- package/dist/features/layout/NavbarLayout.d.ts +3 -0
- package/dist/features/layout/NavbarLayout.js +88 -11
- package/dist/features/layout/SideActionDock.d.ts +1 -0
- package/dist/features/layout/SideActionDock.js +219 -0
- package/dist/features/layout/TitleBarLayout.js +1 -1
- package/dist/features/layout/cta-anchor.d.ts +31 -0
- package/dist/features/layout/cta-anchor.js +31 -0
- package/dist/features/layout/hooks/useBottomActions.d.ts +7 -0
- package/dist/features/layout/hooks/useBottomActions.js +6 -1
- package/dist/features/layout/index.d.ts +9 -1
- package/dist/features/layout/index.js +9 -1
- package/dist/features/layout/useCtaReveal.d.ts +24 -0
- package/dist/features/layout/useCtaReveal.js +82 -0
- package/dist/features/layout/useHoldToOpen.d.ts +41 -0
- package/dist/features/layout/useHoldToOpen.js +84 -0
- package/dist/features/lottery/repository/lottery-entry.repository.d.ts +18 -3
- package/dist/features/lottery/repository/lottery-entry.repository.js +48 -6
- package/dist/features/lottery/schemas/firestore.d.ts +33 -5
- package/dist/features/loyalty/api/balance/route.js +1 -1
- package/dist/features/loyalty/api/history/route.js +1 -1
- package/dist/features/media/MediaImage.js +0 -1
- package/dist/features/media/MediaPickerModal.js +7 -1
- package/dist/features/media/modals/ImageEditor.js +5 -1
- package/dist/features/media/modals/VideoThumbnailSelector.js +17 -1
- package/dist/features/media/repository/media-assets.repository.d.ts +19 -0
- package/dist/features/media/upload/CameraCapture.js +16 -1
- package/dist/features/media/upload/ImageUpload.js +42 -4
- package/dist/features/media/upload/MediaUploadField.js +69 -13
- package/dist/features/media/upload/downscale-image.d.ts +56 -0
- package/dist/features/media/upload/downscale-image.js +150 -0
- package/dist/features/messages/components/ConversationThreadView.js +45 -2
- package/dist/features/messages/components/MessageSellerButton.d.ts +15 -1
- package/dist/features/messages/components/MessageSellerButton.js +10 -2
- package/dist/features/messages/schemas/firestore.d.ts +15 -0
- package/dist/features/orders/hooks/useOrders.js +4 -2
- package/dist/features/orders/repository/orders.repository.d.ts +15 -4
- package/dist/features/orders/repository/orders.repository.js +52 -20
- package/dist/features/orders/schemas/firestore.d.ts +15 -4
- package/dist/features/orders/schemas/index.d.ts +8 -8
- package/dist/features/orders/types/index.d.ts +2 -2
- package/dist/features/payments/repository/payments.repository.js +2 -2
- package/dist/features/payments/repository/payout.repository.d.ts +23 -6
- package/dist/features/payments/repository/payout.repository.js +63 -23
- package/dist/features/payments/repository/saved-payment-methods.repository.d.ts +17 -6
- package/dist/features/payments/repository/saved-payment-methods.repository.js +59 -11
- package/dist/features/payments/schemas/firestore.d.ts +14 -3
- package/dist/features/payments/schemas/saved-methods-firestore.d.ts +12 -1
- package/dist/features/pre-orders/api/[slug]/route.js +1 -1
- package/dist/features/pre-orders/api/admin/route.js +1 -1
- package/dist/features/pre-orders/api/route.js +1 -1
- package/dist/features/pre-orders/repository/pre-orders.repository.d.ts +3 -3
- package/dist/features/pre-orders/repository/pre-orders.repository.js +6 -6
- package/dist/features/products/api/[id]/route.js +1 -1
- package/dist/features/products/components/ListingBottomActions.js +4 -0
- package/dist/features/products/components/ProductDetailPageView.js +2 -1
- package/dist/features/products/components/ProductGalleryClient.js +2 -2
- package/dist/features/products/components/ProductGrid.js +12 -1
- package/dist/features/products/components/index.d.ts +0 -2
- package/dist/features/products/components/index.js +0 -1
- package/dist/features/products/config/listing-type-listing-config.d.ts +0 -3
- package/dist/features/products/config/listing-type-listing-config.js +27 -2
- package/dist/features/products/constants/listing-tabs.d.ts +9 -0
- package/dist/features/products/constants/listing-tabs.js +3 -2
- package/dist/features/products/hooks/useProducts.js +2 -2
- package/dist/features/products/repository/products.repository.js +13 -1
- package/dist/features/products/schemas/index.d.ts +2 -2
- package/dist/features/products/types/index.d.ts +1 -1
- package/dist/features/promotions/api/route.js +1 -1
- package/dist/features/reviews/actions/review-actions.d.ts +6 -6
- package/dist/features/reviews/actions/review-actions.js +22 -8
- package/dist/features/reviews/components/ReviewDetailPageView.js +13 -1
- package/dist/features/reviews/components/ReviewsIndexPageView.js +21 -4
- package/dist/features/reviews/repository/reviews.repository.d.ts +19 -7
- package/dist/features/reviews/repository/reviews.repository.js +63 -12
- package/dist/features/reviews/schemas/firestore.d.ts +13 -2
- package/dist/features/reviews/schemas/index.d.ts +2 -2
- package/dist/features/scams/actions/scam-actions.js +16 -4
- package/dist/features/search/components/SearchResultsPageView.js +2 -1
- package/dist/features/search/hooks/useSearchIndex.d.ts +34 -0
- package/dist/features/search/hooks/useSearchIndex.js +172 -0
- package/dist/features/search/repository/search.repository.js +2 -2
- package/dist/features/seller/actions/offer-actions.d.ts +5 -5
- package/dist/features/seller/actions/offer-actions.js +38 -8
- package/dist/features/seller/actions/seller-actions.js +34 -7
- package/dist/features/seller/api/coupons/route.js +1 -1
- package/dist/features/seller/api/products/route.js +1 -1
- package/dist/features/seller/api/store/route.js +1 -1
- package/dist/features/seller/components/SellerBidsView.js +1 -1
- package/dist/features/seller/components/SellerCouponsView.js +1 -1
- package/dist/features/seller/components/SellerListingTypeView.js +9 -1
- package/dist/features/seller/components/SellerOrdersView.js +1 -1
- package/dist/features/seller/components/SellerPayoutSettingsView.js +0 -1
- package/dist/features/seller/components/SellerPayoutsView.js +1 -1
- package/dist/features/seller/components/SellerProductShell.js +0 -1
- package/dist/features/seller/components/SellerProductsView.js +1 -1
- package/dist/features/seller/components/SellerShippingView.js +0 -1
- package/dist/features/seller/components/SellerStorefrontView.js +0 -1
- package/dist/features/seller/components/index.d.ts +0 -4
- package/dist/features/seller/components/index.js +0 -2
- package/dist/features/seller/repository/offer.repository.d.ts +15 -3
- package/dist/features/seller/repository/offer.repository.js +43 -7
- package/dist/features/seller/schemas/firestore.d.ts +14 -3
- package/dist/features/seller/schemas/index.d.ts +4 -4
- package/dist/features/seller/schemas/order-forms.d.ts +39 -0
- package/dist/features/seller/schemas/order-forms.js +37 -0
- package/dist/features/shell/FormShell.js +6 -2
- package/dist/features/shell/QuickFormDrawer.js +15 -1
- package/dist/features/shipments/components/AdminShipmentEditorView.js +0 -1
- package/dist/features/shipments/components/AdminShipmentsView.js +1 -1
- package/dist/features/sticker-trace/components/StickerCommitPanel.d.ts +30 -0
- package/dist/features/sticker-trace/components/StickerCommitPanel.js +93 -0
- package/dist/features/sticker-trace/components/StickerPrintPanel.d.ts +33 -0
- package/dist/features/sticker-trace/components/StickerPrintPanel.js +104 -0
- package/dist/features/sticker-trace/components/StickerRetryPanel.d.ts +20 -0
- package/dist/features/sticker-trace/components/StickerRetryPanel.js +128 -0
- package/dist/features/sticker-trace/components/StickerRunHistoryPanel.d.ts +45 -0
- package/dist/features/sticker-trace/components/StickerRunHistoryPanel.js +84 -0
- package/dist/features/sticker-trace/components/StickerTraceStudio.d.ts +27 -0
- package/dist/features/sticker-trace/components/StickerTraceStudio.js +171 -0
- package/dist/features/sticker-trace/constants/help-content.d.ts +23 -0
- package/dist/features/sticker-trace/constants/help-content.js +172 -0
- package/dist/features/sticker-trace/hooks/useLiveTrace.d.ts +27 -0
- package/dist/features/sticker-trace/hooks/useLiveTrace.js +160 -0
- package/dist/features/sticker-trace/hooks/useStickerTraceProgress.d.ts +22 -0
- package/dist/features/sticker-trace/hooks/useStickerTraceProgress.js +106 -0
- package/dist/features/sticker-trace/repository/sticker-sheets.repository.d.ts +113 -0
- package/dist/features/sticker-trace/repository/sticker-sheets.repository.js +146 -0
- package/dist/features/sticker-trace/repository/sticker-trace-runs.repository.d.ts +132 -0
- package/dist/features/sticker-trace/repository/sticker-trace-runs.repository.js +174 -0
- package/dist/features/sticker-trace/schemas/firestore.d.ts +248 -0
- package/dist/features/sticker-trace/schemas/firestore.js +79 -0
- package/dist/features/sticker-trace/server.d.ts +8 -0
- package/dist/features/sticker-trace/server.js +51 -0
- package/dist/features/stores/actions/store-query-actions.d.ts +2 -2
- package/dist/features/stores/actions/store-query-actions.js +7 -2
- package/dist/features/stores/api/[storeSlug]/auctions/route.js +3 -3
- package/dist/features/stores/api/[storeSlug]/products/route.js +3 -3
- package/dist/features/stores/api/[storeSlug]/reviews/route.js +1 -1
- package/dist/features/stores/api/[storeSlug]/route.js +1 -1
- package/dist/features/stores/components/StoreNavTabs.js +17 -2
- package/dist/features/stores/manifest.js +8 -4
- package/dist/features/support/repository/support.repository.d.ts +14 -3
- package/dist/features/support/repository/support.repository.js +93 -13
- package/dist/features/support/schemas/firestore.d.ts +16 -5
- package/dist/features/tester/seed-data/authored/admin__events-raffles-spin.js +61 -19
- package/dist/features/tester/seed-data/authored/buying__browsing-search.js +19 -3
- 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 +243 -0
- package/dist/features/tester/seed-data/authored/content-discovery__events.js +36 -10
- package/dist/features/tester/seed-data/authored/content-discovery__overflowing-pill-rows.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/content-discovery__overflowing-pill-rows.js +209 -0
- package/dist/features/tester/seed-data/authored/design-ux__header-brand-mark-centring.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/design-ux__header-brand-mark-centring.js +90 -0
- package/dist/features/tester/seed-data/authored/index.js +8 -0
- package/dist/features/tester/seed-data/authored/selling__media-limits.js +19 -17
- package/dist/features/tester/seed-data/authored/selling__product-upload-details.js +10 -5
- package/dist/features/tester/seed-data/authored/tools__sticker-tracer.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/tools__sticker-tracer.js +283 -0
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +192 -22
- package/dist/index.d.ts +10 -23
- package/dist/index.js +27 -19
- package/dist/next/api/routeHandler.js +9 -0
- package/dist/next/routing/require-record.d.ts +13 -0
- package/dist/next/routing/require-record.js +62 -0
- package/dist/next/routing/route-map.d.ts +4 -4
- package/dist/next/routing/route-map.js +25 -3
- package/dist/providers/db-firebase/base.d.ts +4 -2
- package/dist/providers/db-firebase/base.js +21 -5
- package/dist/providers/db-firebase/base.repository.d.ts +11 -9
- package/dist/providers/db-firebase/base.repository.js +19 -0
- package/dist/providers/db-firebase/rtdb-paths.d.ts +12 -0
- package/dist/providers/db-firebase/rtdb-paths.js +12 -0
- package/dist/react/ErrorBoundary.js +6 -1
- package/dist/react/hooks/realtime-event-constants.d.ts +1 -0
- package/dist/react/hooks/realtime-event-constants.js +8 -0
- package/dist/react/hooks/useEntityDelete.js +5 -1
- package/dist/react/hooks/useMessage.js +5 -1
- package/dist/repositories/index.d.ts +2 -2
- package/dist/repositories/index.js +6 -1
- package/dist/schemas/registry.d.ts +13 -20
- package/dist/schemas/registry.js +31 -14
- package/dist/schemas/webhooks/google-oauth.d.ts +2 -2
- package/dist/security/encrypted.d.ts +117 -0
- package/dist/security/encrypted.js +100 -0
- package/dist/security/index.d.ts +5 -2
- package/dist/security/index.js +16 -2
- package/dist/security/pii-accessors.d.ts +71 -0
- package/dist/security/pii-accessors.js +150 -0
- package/dist/security/pii-encrypt.d.ts +0 -8
- package/dist/security/pii-encrypt.js +22 -15
- package/dist/security/pii-mask.d.ts +18 -0
- package/dist/security/pii-mask.js +27 -0
- package/dist/seed/_helpers/search-txt-wrappers.d.ts +6 -6
- package/dist/seed/_helpers/search-txt-wrappers.js +22 -0
- package/dist/seed/addresses-seed-data.d.ts +2 -2
- package/dist/seed/authenticity-knowledge-seed-data.d.ts +34 -0
- package/dist/seed/authenticity-knowledge-seed-data.js +1277 -0
- package/dist/seed/bids-seed-data.d.ts +2 -2
- package/dist/seed/bids-seed-data.js +23 -2
- package/dist/seed/events-seed-data.d.ts +2 -2
- package/dist/seed/events-seed-data.js +5 -0
- package/dist/seed/faq-seed-data.js +175 -5
- package/dist/seed/homepage-sections-seed-data.js +39 -23
- package/dist/seed/index.d.ts +1 -0
- package/dist/seed/index.js +1 -0
- package/dist/seed/lottery-entries-seed-data.d.ts +2 -2
- package/dist/seed/offers-seed-data.d.ts +2 -2
- package/dist/seed/offers-seed-data.js +1 -1
- package/dist/seed/orders-seed-data.d.ts +2 -2
- package/dist/seed/payouts-seed-data.d.ts +2 -2
- package/dist/seed/payouts-seed-data.js +1 -1
- package/dist/seed/reviews-seed-data.d.ts +1 -2
- package/dist/seed/seed-purpose.js +7 -0
- package/dist/seed/sessions-seed-data.d.ts +2 -2
- package/dist/seed/sessions-seed-data.js +5 -0
- package/dist/seed/site-settings-seed-data.js +1 -1
- package/dist/seed/support-tickets-seed-data.d.ts +2 -2
- package/dist/seed/support-tickets-seed-data.js +1 -1
- package/dist/seed/users-seed-data.d.ts +2 -2
- package/dist/seed/users-seed-data.js +1 -1
- package/dist/seo/json-ld.js +5 -1
- package/dist/server.d.ts +18 -3
- package/dist/server.js +59 -3
- package/dist/styles.css +237 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/Button.js +7 -1
- package/dist/ui/components/FilterChipGroup.d.ts +11 -0
- package/dist/ui/components/FilterChipGroup.js +1 -1
- package/dist/ui/components/ImageGallery.js +6 -1
- package/dist/ui/components/ListingLayout.js +14 -8
- package/dist/ui/components/OverflowRow.d.ts +92 -0
- package/dist/ui/components/OverflowRow.js +203 -0
- package/dist/ui/components/OverflowRow.style.css +235 -0
- package/dist/ui/components/RecordDetailModal.js +14 -1
- package/dist/ui/components/RowActionMenu.js +5 -1
- package/dist/ui/components/SideDrawer.js +5 -1
- package/dist/ui/components/SideModal.js +9 -1
- package/dist/ui/components/TabBarShell.d.ts +12 -1
- package/dist/ui/components/TabBarShell.js +3 -2
- package/dist/ui/components/Tabs.js +36 -24
- package/dist/ui/components/Toast.d.ts +3 -24
- package/dist/ui/components/Toast.js +2 -19
- package/dist/ui/components/TopBannerPortal.d.ts +15 -0
- package/dist/ui/components/TopBannerPortal.js +19 -0
- package/dist/ui/components/UnsavedChangesModal.js +5 -1
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/components/row-collapse.d.ts +15 -0
- package/dist/ui/components/row-collapse.js +127 -0
- package/dist/ui/components/toast-context.d.ts +63 -0
- package/dist/ui/components/toast-context.js +48 -0
- package/dist/ui/components/top-banner-slot.d.ts +37 -0
- package/dist/ui/components/top-banner-slot.js +45 -0
- package/dist/ui/icons/icon-registry.d.ts +6 -0
- package/dist/ui/icons/icon-registry.js +17 -1
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +1 -0
- package/dist/utils/array.helper.d.ts +2 -2
- package/dist/utils/array.helper.js +5 -5
- package/dist/utils/id-generators.d.ts +6 -0
- package/dist/utils/id-generators.js +10 -0
- package/dist/utils/media-url.js +0 -2
- package/dist/utils/search-txt-builders.d.ts +28 -4
- package/dist/utils/search-txt-builders.js +41 -1
- package/dist/validation/schemas.d.ts +8 -8
- package/package.json +6 -2
- package/scripts/seed-cli.mjs +3 -0
- package/dist/features/account/components/UserAddressesView.d.ts +0 -13
- package/dist/features/account/components/UserAddressesView.js +0 -5
- package/dist/features/account/components/UserNotificationsView.d.ts +0 -15
- package/dist/features/account/components/UserNotificationsView.js +0 -12
- package/dist/features/account/components/UserSettingsView.d.ts +0 -16
- package/dist/features/account/components/UserSettingsView.js +0 -13
- package/dist/features/account/components/UserSupportView.d.ts +0 -3
- package/dist/features/account/components/UserSupportView.js +0 -166
- package/dist/features/admin/components/DemoSeedView.d.ts +0 -11
- package/dist/features/admin/components/DemoSeedView.js +0 -5
- package/dist/features/auth/components/AuthStatusPanel.d.ts +0 -10
- package/dist/features/auth/components/AuthStatusPanel.js +0 -22
- package/dist/features/categories/components/CategoryProductsView.d.ts +0 -45
- package/dist/features/categories/components/CategoryProductsView.js +0 -21
- package/dist/features/homepage/components/HowItWorksInfoView.d.ts +0 -36
- package/dist/features/homepage/components/HowItWorksInfoView.js +0 -10
- package/dist/features/layout/BottomActions.d.ts +0 -1
- package/dist/features/layout/BottomActions.js +0 -311
- package/dist/features/products/components/ProductsView.d.ts +0 -6
- package/dist/features/products/components/ProductsView.js +0 -5
- package/dist/features/seller/components/SellerStoreView.d.ts +0 -8
- package/dist/features/seller/components/SellerStoreView.js +0 -8
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseRepository, getFirestoreCount, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase/index.js";
|
|
2
2
|
import { NotFoundError } from "../../../errors/index.js";
|
|
3
3
|
import { REVIEW_MAX_RATING, REVIEW_MIN_RATING } from "../../../_internal/shared/features/reviews/config.js";
|
|
4
|
-
import {
|
|
4
|
+
import { maskPii, revealPii, REVIEW_PII_FIELDS, REVIEW_PII_INDEX_MAP, } from "../../../security/index.js";
|
|
5
5
|
import { createReviewId, REVIEW_COLLECTION, } from "../schemas/index.js";
|
|
6
6
|
import { buildReviewSearchTxt } from "../../../utils/search-txt-builders.js";
|
|
7
7
|
import { planSearchTxt, beginSearchWindow, emptySearchResult, } from "../../../utils/search-txt-query.js";
|
|
@@ -35,12 +35,23 @@ class ReviewRepository extends BaseRepository {
|
|
|
35
35
|
this.piiFields = REVIEW_PII_FIELDS;
|
|
36
36
|
this.piiIndexMap = REVIEW_PII_INDEX_MAP;
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
/*
|
|
39
|
+
* 🛑 Was `decryptSieveResult`, an UNCONDITIONAL decrypt on the list path.
|
|
40
|
+
*
|
|
41
|
+
* Same defect as the `mapDoc` one and invisible to R3, which only looks at
|
|
42
|
+
* `mapDoc` — a repository can leak plaintext through any read path, and this
|
|
43
|
+
* one did it for all three list methods. Renamed rather than deleted because
|
|
44
|
+
* these three callers are public-facing and genuinely need a displayable
|
|
45
|
+
* name, so they now take the MASKED form: "R*** K***" rather than the full
|
|
46
|
+
* name a scraper could harvest alongside every purchase it describes.
|
|
47
|
+
*
|
|
48
|
+
* A caller that needs the real name calls `revealReview` on the row it
|
|
49
|
+
* actually needs, which is one row rather than a page of fifty.
|
|
50
|
+
*/
|
|
51
|
+
maskSieveResult(result) {
|
|
39
52
|
return {
|
|
40
53
|
...result,
|
|
41
|
-
items: result.items
|
|
42
|
-
...REVIEW_PII_FIELDS,
|
|
43
|
-
])),
|
|
54
|
+
items: this.maskedReviews(result.items),
|
|
44
55
|
};
|
|
45
56
|
}
|
|
46
57
|
mapDoc(snap) {
|
|
@@ -52,13 +63,53 @@ class ReviewRepository extends BaseRepository {
|
|
|
52
63
|
// forgotten call site away from a leak. `voteHelpful` reads the raw snapshot
|
|
53
64
|
// inside its transaction and is unaffected.
|
|
54
65
|
const { helpfulVoterIds: _voters, ...rest } = raw;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
66
|
+
/*
|
|
67
|
+
* 🛑 The DECRYPT is gone; the STRIP stays, and the distinction matters.
|
|
68
|
+
*
|
|
69
|
+
* This is the one R3 subject whose `mapDoc` did something besides decrypt,
|
|
70
|
+
* so the override survives — which is also why the audit's rule had to be
|
|
71
|
+
* scoped to mapDoc's own body rather than "a decrypt call anywhere in this
|
|
72
|
+
* file": the named accessors below call one, and a file-level test would
|
|
73
|
+
* report this correctly-converted repository as unconverted forever.
|
|
74
|
+
*
|
|
75
|
+
* `userName` is now ciphertext to every caller. `maskPublicReview` masks
|
|
76
|
+
* it for the public list, and it refuses ciphertext by design — so that
|
|
77
|
+
* path goes through `maskedReview` instead, which decrypts first.
|
|
78
|
+
*/
|
|
79
|
+
return rest;
|
|
58
80
|
}
|
|
59
|
-
|
|
81
|
+
/** Plaintext PII. **Opt-in and named** — shows up in review and in a grep. */
|
|
82
|
+
revealReview(doc) {
|
|
83
|
+
return revealPii(doc, REVIEW_PII_FIELDS);
|
|
84
|
+
}
|
|
85
|
+
/** As `revealReview`, for a list read. */
|
|
86
|
+
revealReviews(docs) {
|
|
87
|
+
return docs.map((d) => this.revealReview(d));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Masked PII — **the default for any public surface.** Decrypts first, then
|
|
91
|
+
* masks: `maskName` returns "Anonymous" for a ciphertext input by design, so
|
|
92
|
+
* handing it a stored `userName` would anonymise every reviewer.
|
|
93
|
+
*/
|
|
94
|
+
maskedReview(doc) {
|
|
95
|
+
return maskPii(doc, REVIEW_PII_FIELDS);
|
|
96
|
+
}
|
|
97
|
+
/** As `maskedReview`, for a list read. */
|
|
98
|
+
maskedReviews(docs) {
|
|
99
|
+
return docs.map((d) => this.maskedReview(d));
|
|
100
|
+
}
|
|
101
|
+
/*
|
|
102
|
+
* 🛑 NOT an `override`. `BaseRepository.create` promises to accept the
|
|
103
|
+
* CIPHERTEXT shape; this accepts PLAINTEXT and encrypts, and `string` vs
|
|
104
|
+
* `Encrypted<string>` is incompatible in both directions, so an override is a
|
|
105
|
+
* TS2416 either way — correctly, because the signatures disagree about what
|
|
106
|
+
* the caller holds. Returns `ReviewPlain`: it echoes the input rather than
|
|
107
|
+
* re-reading.
|
|
108
|
+
*/
|
|
109
|
+
async createReview(input) {
|
|
60
110
|
const firstName = input.userName.split(" ")[0] || input.userName;
|
|
61
111
|
const id = createReviewId(input.productTitle, firstName, new Date());
|
|
112
|
+
// Plain in, ciphertext out — the write hook encrypts below.
|
|
62
113
|
const reviewData = {
|
|
63
114
|
...input,
|
|
64
115
|
helpfulCount: 0,
|
|
@@ -334,7 +385,7 @@ class ReviewRepository extends BaseRepository {
|
|
|
334
385
|
defaultPageSize: 10,
|
|
335
386
|
maxPageSize: 50,
|
|
336
387
|
});
|
|
337
|
-
return this.
|
|
388
|
+
return this.maskSieveResult(result);
|
|
338
389
|
}
|
|
339
390
|
async listForStore(storeId, model) {
|
|
340
391
|
const baseQuery = this.getCollection().where(REVIEW_FIELDS.STORE_ID, "==", storeId);
|
|
@@ -343,7 +394,7 @@ class ReviewRepository extends BaseRepository {
|
|
|
343
394
|
defaultPageSize: 10,
|
|
344
395
|
maxPageSize: 100,
|
|
345
396
|
});
|
|
346
|
-
return this.
|
|
397
|
+
return this.maskSieveResult(result);
|
|
347
398
|
}
|
|
348
399
|
/**
|
|
349
400
|
* Derived on every write path via `applyWriteHooks`.
|
|
@@ -376,7 +427,7 @@ class ReviewRepository extends BaseRepository {
|
|
|
376
427
|
});
|
|
377
428
|
// Refine BEFORE decrypting: the refine reads `searchTxt`, which is never
|
|
378
429
|
// encrypted, and decrypting rows that are about to be dropped is waste.
|
|
379
|
-
return this.
|
|
430
|
+
return this.maskSieveResult(win.finish(result));
|
|
380
431
|
}
|
|
381
432
|
}
|
|
382
433
|
ReviewRepository.SIEVE_FIELDS = {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import type { ReviewStatus } from "../types";
|
|
5
5
|
import type { BaseDocument } from "../../../_internal/shared/types/base-document";
|
|
6
6
|
import type { ListingType } from "../../products/types/index";
|
|
7
|
+
import type { Encrypted, Plaintext } from "../../../security/encrypted";
|
|
7
8
|
/** Runtime-accessible review status values — use instead of bare string literals. */
|
|
8
9
|
export declare const ReviewStatusValues: {
|
|
9
10
|
readonly PENDING: "pending";
|
|
@@ -17,6 +18,16 @@ export interface ReviewVideoField {
|
|
|
17
18
|
trimStart?: number;
|
|
18
19
|
trimEnd?: number;
|
|
19
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* `ReviewDocument` with its PII as plain `string` — what the named repository
|
|
23
|
+
* accessors return, and what a WRITE path accepts (the repository encrypts).
|
|
24
|
+
*
|
|
25
|
+
* `Plaintext<T>` is the one definition of this shape; see `security/encrypted.ts`.
|
|
26
|
+
* It is deliberately NOT distinguishable from a MASKED value — `maskPii`
|
|
27
|
+
* returns plain strings too, and no type can express "these characters are
|
|
28
|
+
* asterisks". The distinction is which named method you called.
|
|
29
|
+
*/
|
|
30
|
+
export type ReviewPlain = Plaintext<ReviewDocument>;
|
|
20
31
|
export interface ReviewDocument extends BaseDocument {
|
|
21
32
|
/** Normalized edge-n-gram search field — see appkit/src/utils/search-txt.ts. */
|
|
22
33
|
searchTxt?: string[];
|
|
@@ -29,7 +40,7 @@ export interface ReviewDocument extends BaseDocument {
|
|
|
29
40
|
userId: string;
|
|
30
41
|
/** Firestore document ID of the reviewer (slug like `user-mohsin-c`). Populated on create; used to build profile links. */
|
|
31
42
|
userSlug?: string;
|
|
32
|
-
userName: string
|
|
43
|
+
userName: Encrypted<string>;
|
|
33
44
|
userAvatar?: string;
|
|
34
45
|
rating: number;
|
|
35
46
|
title: string;
|
|
@@ -67,7 +78,7 @@ export declare const REVIEW_INDEXED_FIELDS: readonly ["searchTxt", "productId",
|
|
|
67
78
|
export declare const DEFAULT_REVIEW_DATA: Partial<ReviewDocument>;
|
|
68
79
|
export declare const REVIEW_PUBLIC_FIELDS: readonly ["id", "productId", "userName", "userAvatar", "rating", "title", "comment", "helpfulCount", "verified", "images", "createdAt"];
|
|
69
80
|
export declare const REVIEW_UPDATABLE_FIELDS: readonly ["rating", "title", "comment", "images"];
|
|
70
|
-
export type ReviewCreateInput = Omit<
|
|
81
|
+
export type ReviewCreateInput = Omit<ReviewPlain, "id" | "createdAt" | "updatedAt" | "helpfulCount" | "reportCount" | "moderatorId" | "moderatorNote" | "approvedAt" | "rejectedAt">;
|
|
71
82
|
export type ReviewUpdateInput = Partial<Pick<ReviewDocument, (typeof REVIEW_UPDATABLE_FIELDS)[number]>>;
|
|
72
83
|
export type ReviewModerationInput = {
|
|
73
84
|
status: ReviewStatus;
|
|
@@ -367,20 +367,20 @@ export declare const reviewListParamsSchema: z.ZodObject<{
|
|
|
367
367
|
sort?: string | undefined;
|
|
368
368
|
status?: "pending" | "approved" | "rejected" | undefined;
|
|
369
369
|
page?: number | undefined;
|
|
370
|
-
perPage?: number | undefined;
|
|
371
370
|
storeId?: string | undefined;
|
|
372
371
|
rating?: number | undefined;
|
|
373
372
|
featured?: boolean | undefined;
|
|
374
373
|
productId?: string | undefined;
|
|
375
374
|
userId?: string | undefined;
|
|
375
|
+
perPage?: number | undefined;
|
|
376
376
|
}, {
|
|
377
377
|
sort?: string | undefined;
|
|
378
378
|
status?: "pending" | "approved" | "rejected" | undefined;
|
|
379
379
|
page?: number | undefined;
|
|
380
|
-
perPage?: number | undefined;
|
|
381
380
|
storeId?: string | undefined;
|
|
382
381
|
rating?: number | undefined;
|
|
383
382
|
featured?: boolean | undefined;
|
|
384
383
|
productId?: string | undefined;
|
|
385
384
|
userId?: string | undefined;
|
|
385
|
+
perPage?: number | undefined;
|
|
386
386
|
}>;
|
|
@@ -119,13 +119,25 @@ export async function getSellerTrustStatus(storeId) {
|
|
|
119
119
|
if (!owner.value)
|
|
120
120
|
return TRUST_STATUS_CLEAR;
|
|
121
121
|
const ownerDoc = owner.value;
|
|
122
|
+
/*
|
|
123
|
+
* 🛑 REVEALED — without this, THIS SECURITY CONTROL SILENTLY ALWAYS PASSES.
|
|
124
|
+
*
|
|
125
|
+
* `userRepository` is ciphertext-by-default, while the scam registry's
|
|
126
|
+
* `phones`/`emails` are PLAINTEXT on purpose (decision D1: a victim googles a
|
|
127
|
+
* UPI id before paying, so SEO is the feature). Comparing ciphertext against
|
|
128
|
+
* plaintext never matches, so every lookup returned zero rows and
|
|
129
|
+
* `sellerTrustStatus` answered CLEAR for every seller — including ones
|
|
130
|
+
* already in the registry. No error, no log, and the one check a buyer
|
|
131
|
+
* relies on degraded to a constant.
|
|
132
|
+
*/
|
|
133
|
+
const ownerPlain = userRepository.revealUser(ownerDoc);
|
|
122
134
|
const lookups = [];
|
|
123
|
-
if (
|
|
124
|
-
const phone =
|
|
135
|
+
if (ownerPlain.phoneNumber) {
|
|
136
|
+
const phone = ownerPlain.phoneNumber;
|
|
125
137
|
lookups.push(() => scammerRepository.findByContactField("phones", phone));
|
|
126
138
|
}
|
|
127
|
-
if (
|
|
128
|
-
const email =
|
|
139
|
+
if (ownerPlain.email) {
|
|
140
|
+
const email = ownerPlain.email;
|
|
129
141
|
lookups.push(() => scammerRepository.findByContactField("emails", email));
|
|
130
142
|
}
|
|
131
143
|
if (lookups.length === 0)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ROUTES } from "../../../next/routing/route-map.js";
|
|
2
3
|
import { Section, Container, Stack, Row, Div, Heading, Text, Span, TextLink } from "../../../ui/index.js";
|
|
3
4
|
export function SearchResultsPageView({ result, renderSearchInput, }) {
|
|
4
5
|
const { q, terms, groups, totalHits } = result;
|
|
@@ -17,5 +18,5 @@ export function SearchResultsPageView({ result, renderSearchInput, }) {
|
|
|
17
18
|
const normalisedAway = hasQuery && terms.length === 0;
|
|
18
19
|
return (_jsx(Section, { padding: "page", children: _jsx(Container, { children: _jsxs(Stack, { gap: "loose", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 1, children: hasQuery ? `Search: ${q}` : "Search" }), hasQuery && !normalisedAway && (_jsx(Text, { color: "muted", size: "sm", children: totalHits === 0
|
|
19
20
|
? "Nothing matched."
|
|
20
|
-
: `${totalHits}${groups.some((g) => g.truncated) ? "+" : ""} result${totalHits === 1 ? "" : "s"} across ${groups.length} section${groups.length === 1 ? "" : "s"}.` }))] }), renderSearchInput?.(), !hasQuery && (_jsx(Text, { color: "muted", children: "Search listings, categories, brands, stores, articles and help pages at once." })), normalisedAway && (_jsx(Text, { color: "muted", children: "That search had no searchable words in it. Try a product, part or category name." })), groups.map((group) => (_jsxs(Stack, { gap: "sm", children: [_jsxs(Row, { justify: "between", align: "center", wrap: true, gap: "sm", children: [_jsxs(Heading, { level: 2, size: "lg", children: [group.label, group.truncated && (_jsxs(Span, { size: "sm", color: "muted", children: [" ", "(", group.hits.length, "+)"] }))] }), _jsxs(TextLink, { href: group.moreHref, children: ["See all ", group.label.toLowerCase(), " \u2192"] })] }), _jsx(Stack, { gap: "xs", children: group.hits.map((hit) => (_jsxs(Div, { surface: "card", padding: "sm", rounded: "md", children: [_jsx(TextLink, { href: hit.href, children: hit.title }), hit.detail && (_jsx(Text, { size: "xs", color: "muted", children: hit.detail }))] }, `${group.key}-${hit.id}`))) })] }, group.key))), hasQuery && !normalisedAway && totalHits === 0 && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { color: "muted", children: ["No listings, categories, brands, stores, articles or help pages matched \u201C", q, "\u201D."] }), _jsx(TextLink, { href:
|
|
21
|
+
: `${totalHits}${groups.some((g) => g.truncated) ? "+" : ""} result${totalHits === 1 ? "" : "s"} across ${groups.length} section${groups.length === 1 ? "" : "s"}.` }))] }), renderSearchInput?.(), !hasQuery && (_jsx(Text, { color: "muted", children: "Search listings, categories, brands, stores, articles and help pages at once." })), normalisedAway && (_jsx(Text, { color: "muted", children: "That search had no searchable words in it. Try a product, part or category name." })), groups.map((group) => (_jsxs(Stack, { gap: "sm", children: [_jsxs(Row, { justify: "between", align: "center", wrap: true, gap: "sm", children: [_jsxs(Heading, { level: 2, size: "lg", children: [group.label, group.truncated && (_jsxs(Span, { size: "sm", color: "muted", children: [" ", "(", group.hits.length, "+)"] }))] }), _jsxs(TextLink, { href: group.moreHref, children: ["See all ", group.label.toLowerCase(), " \u2192"] })] }), _jsx(Stack, { gap: "xs", children: group.hits.map((hit) => (_jsxs(Div, { surface: "card", padding: "sm", rounded: "md", children: [_jsx(TextLink, { href: hit.href, children: hit.title }), hit.detail && (_jsx(Text, { size: "xs", color: "muted", children: hit.detail }))] }, `${group.key}-${hit.id}`))) })] }, group.key))), hasQuery && !normalisedAway && totalHits === 0 && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { color: "muted", children: ["No listings, categories, brands, stores, articles or help pages matched \u201C", q, "\u201D."] }), _jsx(TextLink, { href: ROUTES.PUBLIC.PRODUCTS, children: "Browse all listings \u2192" })] }))] }) }) }));
|
|
21
22
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type SearchIndexArtifact } from "../../../_internal/shared/search/index-record";
|
|
2
|
+
import { type PreparedIndex } from "../../../_internal/shared/search/match";
|
|
3
|
+
export type SearchIndexStatus = "idle" | "loading" | "ready" | "failed" | "disabled";
|
|
4
|
+
export interface LoadedSearchIndex {
|
|
5
|
+
readonly artifact: SearchIndexArtifact;
|
|
6
|
+
readonly prepared: PreparedIndex[];
|
|
7
|
+
readonly version: number;
|
|
8
|
+
readonly builtAt: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SearchIndexState {
|
|
11
|
+
readonly status: SearchIndexStatus;
|
|
12
|
+
readonly index: LoadedSearchIndex | null;
|
|
13
|
+
/** Present on `failed`. Short, for a log line — never rendered to a user. */
|
|
14
|
+
readonly reason: string | null;
|
|
15
|
+
}
|
|
16
|
+
/** Reset hook, for the verify script and for a kill-switch flip mid-session. */
|
|
17
|
+
export declare function __resetSearchIndexForTests(): void;
|
|
18
|
+
/** Start the load if it has not started. Idempotent; safe to call per render. */
|
|
19
|
+
export declare function ensureSearchIndex(): Promise<LoadedSearchIndex | null>;
|
|
20
|
+
export interface UseSearchIndexOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Flip true when the user shows intent — focus, or the first keypress.
|
|
23
|
+
* False keeps the hook in `idle` and issues no request at all.
|
|
24
|
+
*/
|
|
25
|
+
active: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* `siteSettings.search?.indexEnabled`. 🛑 Read as `!== false`, so an
|
|
28
|
+
* undefined settings object — a failed fetch, a stale cached response from
|
|
29
|
+
* before the field existed — leaves search WORKING. Only an explicit
|
|
30
|
+
* `false` disables it.
|
|
31
|
+
*/
|
|
32
|
+
indexEnabled?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare function useSearchIndex({ active, indexEnabled, }: UseSearchIndexOptions): SearchIndexState;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/*
|
|
3
|
+
* WHY: Fetch the public search artifact exactly once per page session and
|
|
4
|
+
* hand every caller the SAME prepared copy, so typing costs nothing.
|
|
5
|
+
* WHAT: A module-level singleton loader plus the hook that exposes its state.
|
|
6
|
+
*
|
|
7
|
+
* ## 🛑 ONE fetch and ONE `prepareIndex`, per page session, shared
|
|
8
|
+
*
|
|
9
|
+
* The artifact is ~420 KB and `prepareIndex` normalises ~3,700 titles and
|
|
10
|
+
* details. Both are cheap once and ruinous per component: the header
|
|
11
|
+
* dropdown, `/search`, `/parts` and two `/tools` surfaces all want the same
|
|
12
|
+
* index, and a hook that loaded per mount would download it five times and
|
|
13
|
+
* re-normalise 18,500 strings. The singleton lives at module scope — not in a
|
|
14
|
+
* React context — because a context would make every one of those consumers
|
|
15
|
+
* require a provider they cannot guarantee (Root Cause #20), and the data is
|
|
16
|
+
* immutable so there is nothing for a provider to coordinate.
|
|
17
|
+
*
|
|
18
|
+
* ## 🛑 Nothing loads until somebody intends to search
|
|
19
|
+
*
|
|
20
|
+
* `active` gates the fetch, and callers flip it on first focus or first
|
|
21
|
+
* keypress — never on mount. The action-index route states the rule this
|
|
22
|
+
* follows: one request per session, never per keystroke. Firing on mount
|
|
23
|
+
* would put 420 KB on the critical path of every page view for a feature
|
|
24
|
+
* most visits never touch, which is the opposite of the trade this whole
|
|
25
|
+
* design exists to make.
|
|
26
|
+
*
|
|
27
|
+
* ## 🛑 It NEVER stores the artifact in localStorage
|
|
28
|
+
*
|
|
29
|
+
* The service worker holds the bytes, against an `immutable` version-addressed
|
|
30
|
+
* URL. localStorage is synchronous, ~5 MB, shared with everything else the app
|
|
31
|
+
* keeps, and would have to be invalidated by hand on every build. The plan
|
|
32
|
+
* called for keeping the version integer there to skip the manifest hop; that
|
|
33
|
+
* is dropped deliberately, because the manifest is a ~100-byte 60s-cached
|
|
34
|
+
* document and a remembered version is wrong exactly when it matters — after
|
|
35
|
+
* a rebuild — at which point it costs a WASTED 420 KB download instead of
|
|
36
|
+
* saving a tiny one.
|
|
37
|
+
*
|
|
38
|
+
* ## Four states, and `loading` is not `empty`
|
|
39
|
+
*
|
|
40
|
+
* Same discipline as `GatedPrice`'s three: a caller must be able to tell "no
|
|
41
|
+
* matches" from "we have not loaded the haystack yet". Rendering an empty
|
|
42
|
+
* state while the index is in flight says "nothing matched" about a question
|
|
43
|
+
* nobody has asked yet.
|
|
44
|
+
*
|
|
45
|
+
* `failed` and `disabled` are also distinct, and both mean *fall back to the
|
|
46
|
+
* server path* rather than *show nothing*.
|
|
47
|
+
*
|
|
48
|
+
* @tag domain:search
|
|
49
|
+
* @tag layer:feature
|
|
50
|
+
* @tag pattern:hook
|
|
51
|
+
* @tag access:client
|
|
52
|
+
* @tag consumers:Search,SearchResultsPageView,/parts,/tools/price-index
|
|
53
|
+
* @tag sideEffects:network
|
|
54
|
+
*/
|
|
55
|
+
import * as React from "react";
|
|
56
|
+
import { SEARCH_INDEX_SCHEMA_VERSION, } from "../../../_internal/shared/search/index-record.js";
|
|
57
|
+
import { searchArtifactUrl, searchManifestUrl, } from "../../../_internal/shared/search/artifact-url.js";
|
|
58
|
+
import { prepareIndex } from "../../../_internal/shared/search/match.js";
|
|
59
|
+
import { normalizeError } from "../../../errors/normalize.js";
|
|
60
|
+
/**
|
|
61
|
+
* Read at module scope, exactly as `utils/media-url.ts` does for the same
|
|
62
|
+
* variable. Next inlines `NEXT_PUBLIC_*` at build time, so this is a constant
|
|
63
|
+
* in the bundle rather than a runtime lookup.
|
|
64
|
+
*/
|
|
65
|
+
const BUCKET = process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET ?? "";
|
|
66
|
+
/**
|
|
67
|
+
* The singleton. `undefined` until the first caller asks; thereafter the same
|
|
68
|
+
* promise forever, resolving to `null` on any failure.
|
|
69
|
+
*
|
|
70
|
+
* 🛑 Resolving to `null` rather than rejecting is deliberate: a rejected
|
|
71
|
+
* module-scope promise with no attached handler is an unhandled rejection in
|
|
72
|
+
* every tab where nothing happens to be awaiting it yet.
|
|
73
|
+
*/
|
|
74
|
+
let inflight;
|
|
75
|
+
let failureReason = null;
|
|
76
|
+
/** Reset hook, for the verify script and for a kill-switch flip mid-session. */
|
|
77
|
+
export function __resetSearchIndexForTests() {
|
|
78
|
+
inflight = undefined;
|
|
79
|
+
failureReason = null;
|
|
80
|
+
}
|
|
81
|
+
async function fetchJson(url, label) {
|
|
82
|
+
/*
|
|
83
|
+
* 🛑 `mode: "cors"`, explicitly, and never `no-cors`.
|
|
84
|
+
*
|
|
85
|
+
* A `no-cors` fetch of a cross-origin object yields an OPAQUE response: the
|
|
86
|
+
* body cannot be read, the status reads as 0, and a 404 is indistinguishable
|
|
87
|
+
* from a successful empty index — which is this feature's single favourite
|
|
88
|
+
* failure mode. The bucket carries a CORS entry for the app origins
|
|
89
|
+
* precisely so this can be `cors` and the status can be real.
|
|
90
|
+
*/
|
|
91
|
+
const res = await fetch(url, { mode: "cors", credentials: "omit" });
|
|
92
|
+
if (!res.ok)
|
|
93
|
+
throw new Error(`${label}: HTTP ${res.status}`);
|
|
94
|
+
return (await res.json());
|
|
95
|
+
}
|
|
96
|
+
async function load() {
|
|
97
|
+
try {
|
|
98
|
+
if (!BUCKET)
|
|
99
|
+
throw new Error("NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET is unset");
|
|
100
|
+
const manifest = await fetchJson(searchManifestUrl(BUCKET), "manifest");
|
|
101
|
+
/*
|
|
102
|
+
* 🛑 Refuse a schema this build does not understand, rather than render a
|
|
103
|
+
* half-understood artifact.
|
|
104
|
+
*
|
|
105
|
+
* The tuple is positional. A future `[typeIdx, title, detail, slug,
|
|
106
|
+
* facets, badge]` would parse without error in this build and simply be
|
|
107
|
+
* read wrong — wrong slugs, wrong hrefs, confidently. The version lives
|
|
108
|
+
* in the ENVELOPE and not in the URL for exactly this check; falling back
|
|
109
|
+
* to the server path is always better than being quietly wrong.
|
|
110
|
+
*/
|
|
111
|
+
if (manifest.schema !== SEARCH_INDEX_SCHEMA_VERSION) {
|
|
112
|
+
throw new Error(`schema ${manifest.schema} != client ${SEARCH_INDEX_SCHEMA_VERSION}`);
|
|
113
|
+
}
|
|
114
|
+
const artifact = await fetchJson(searchArtifactUrl(BUCKET, manifest.version), "artifact");
|
|
115
|
+
if (artifact.schema !== SEARCH_INDEX_SCHEMA_VERSION) {
|
|
116
|
+
throw new Error(`artifact schema ${artifact.schema} disagrees with its manifest`);
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
artifact,
|
|
120
|
+
// Once, here. See the header, and `prepareIndex`'s own 🛑.
|
|
121
|
+
prepared: [...prepareIndex(artifact)],
|
|
122
|
+
version: artifact.version,
|
|
123
|
+
builtAt: artifact.builtAt,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
const n = normalizeError(err);
|
|
128
|
+
/*
|
|
129
|
+
* normalizeError FIRST, before the body touches the value — the project-wide
|
|
130
|
+
* rule, and what audit-catch-normalize is strict-zero about. It also removes
|
|
131
|
+
* the hand-rolled `err instanceof Error ? … : String(err)` narrowing that
|
|
132
|
+
* every catch otherwise reinvents slightly differently, which is the whole
|
|
133
|
+
* reason the helper exists (Root Cause #31).
|
|
134
|
+
*/
|
|
135
|
+
failureReason = n.message;
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/** Start the load if it has not started. Idempotent; safe to call per render. */
|
|
140
|
+
export function ensureSearchIndex() {
|
|
141
|
+
inflight ?? (inflight = load());
|
|
142
|
+
return inflight;
|
|
143
|
+
}
|
|
144
|
+
export function useSearchIndex({ active, indexEnabled, }) {
|
|
145
|
+
const enabled = indexEnabled !== false;
|
|
146
|
+
const [index, setIndex] = React.useState(null);
|
|
147
|
+
const [settled, setSettled] = React.useState(false);
|
|
148
|
+
React.useEffect(() => {
|
|
149
|
+
if (!active || !enabled || settled)
|
|
150
|
+
return;
|
|
151
|
+
let alive = true;
|
|
152
|
+
void ensureSearchIndex().then((loaded) => {
|
|
153
|
+
if (!alive)
|
|
154
|
+
return;
|
|
155
|
+
setIndex(loaded);
|
|
156
|
+
setSettled(true);
|
|
157
|
+
});
|
|
158
|
+
return () => {
|
|
159
|
+
alive = false;
|
|
160
|
+
};
|
|
161
|
+
}, [active, enabled, settled]);
|
|
162
|
+
const status = !enabled
|
|
163
|
+
? "disabled"
|
|
164
|
+
: !active
|
|
165
|
+
? "idle"
|
|
166
|
+
: !settled
|
|
167
|
+
? "loading"
|
|
168
|
+
: index
|
|
169
|
+
? "ready"
|
|
170
|
+
: "failed";
|
|
171
|
+
return { status, index, reason: status === "failed" ? failureReason : null };
|
|
172
|
+
}
|
|
@@ -43,7 +43,7 @@ export class SearchRepository {
|
|
|
43
43
|
: "createdAt",
|
|
44
44
|
order: query.sort?.startsWith("-") ? "desc" : "asc",
|
|
45
45
|
page: query.page ?? 1,
|
|
46
|
-
|
|
46
|
+
pageSize: query.pageSize ?? 24,
|
|
47
47
|
};
|
|
48
48
|
const result = await this.repo.findAll(sieveQuery);
|
|
49
49
|
return {
|
|
@@ -51,7 +51,7 @@ export class SearchRepository {
|
|
|
51
51
|
q: query.q ?? "",
|
|
52
52
|
total: result.total,
|
|
53
53
|
page: result.page,
|
|
54
|
-
pageSize: result.
|
|
54
|
+
pageSize: result.pageSize,
|
|
55
55
|
totalPages: result.totalPages,
|
|
56
56
|
hasMore: result.page < result.totalPages,
|
|
57
57
|
};
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Business logic for the Make-an-Offer feature lifecycle.
|
|
5
5
|
* Auth, rate-limiting, and input validation are handled by the calling server action.
|
|
6
6
|
*/
|
|
7
|
-
import type { OfferDocument } from "../schemas";
|
|
7
|
+
import type { OfferDocument, OfferPlain } from "../schemas";
|
|
8
8
|
/**
|
|
9
9
|
* How long an accepted offer stays claimable at its locked price. Shared by the
|
|
10
10
|
* seller-accept path, the buyer-accepts-counter path, and the expiry sweep so
|
|
@@ -28,11 +28,11 @@ export interface BuyerCounterInput {
|
|
|
28
28
|
counterAmount: number;
|
|
29
29
|
buyerNote?: string;
|
|
30
30
|
}
|
|
31
|
-
export declare function makeOffer(userId: string, userEmail: string, input: MakeOfferInput): Promise<
|
|
31
|
+
export declare function makeOffer(userId: string, userEmail: string, input: MakeOfferInput): Promise<OfferPlain>;
|
|
32
32
|
export declare function respondToOffer(userId: string, input: RespondToOfferInput): Promise<OfferDocument>;
|
|
33
33
|
export declare function acceptCounterOffer(userId: string, offerId: string): Promise<OfferDocument>;
|
|
34
|
-
export declare function counterOfferByBuyer(userId: string, userEmail: string, input: BuyerCounterInput): Promise<
|
|
34
|
+
export declare function counterOfferByBuyer(userId: string, userEmail: string, input: BuyerCounterInput): Promise<OfferPlain>;
|
|
35
35
|
export declare function withdrawOffer(userId: string, offerId: string): Promise<void>;
|
|
36
|
-
export declare function listBuyerOffers(userId: string): Promise<
|
|
37
|
-
export declare function listSellerOffers(userId: string): Promise<
|
|
36
|
+
export declare function listBuyerOffers(userId: string): Promise<OfferPlain[]>;
|
|
37
|
+
export declare function listSellerOffers(userId: string): Promise<OfferPlain[]>;
|
|
38
38
|
export declare function checkoutOffer(userId: string, offerId: string): Promise<CartDocument>;
|
|
@@ -12,7 +12,6 @@ import { sendNotification } from "../../admin/actions/notification-actions.js";
|
|
|
12
12
|
import { userRepository } from "../../auth/repository/user.repository.js";
|
|
13
13
|
import { storeRepository } from "../../stores/repository/store.repository.js";
|
|
14
14
|
import { cartRepository } from "../../cart/repository/cart.repository.js";
|
|
15
|
-
import { maskOfferForSeller } from "../../../security/index.js";
|
|
16
15
|
import { ERROR_MESSAGES, AuthorizationError, ValidationError, NotFoundError, OFFER_ERROR_CODES, } from "../../../errors/index.js";
|
|
17
16
|
import { canMakeOffer } from "../../../_internal/shared/listing-types/capabilities.js";
|
|
18
17
|
import { normalizeListingType } from "../../products/utils/listing-type.js";
|
|
@@ -122,17 +121,27 @@ export async function makeOffer(userId, userEmail, input) {
|
|
|
122
121
|
if (alreadyActive)
|
|
123
122
|
throw new ValidationError(ERROR_MESSAGES.OFFER.ACTIVE_OFFER_EXISTS);
|
|
124
123
|
const profile = await userRepository.findById(userId);
|
|
124
|
+
/*
|
|
125
|
+
* 🛑 REVEALED. `createOffer` encrypts `buyerEmail` (`OFFER_PII_FIELDS`), so
|
|
126
|
+
* the ciphertext this read returns would have been encrypted a second time
|
|
127
|
+
* and become permanently unreadable — the seller could never see who made
|
|
128
|
+
* the offer. The sibling `bid-actions.ts:223,481` was fixed for exactly this;
|
|
129
|
+
* this line is the same shape, missed.
|
|
130
|
+
*
|
|
131
|
+
* `userEmail` (the parameter) is already plaintext, so it needs no unwrap.
|
|
132
|
+
*/
|
|
133
|
+
const profilePlain = profile ? userRepository.revealUser(profile) : null;
|
|
125
134
|
const buyerDisplayName = profile?.displayName ?? "Buyer";
|
|
126
135
|
if (!product.storeId)
|
|
127
136
|
throw new ValidationError("Product is not associated with a store.");
|
|
128
|
-
const offer = await offerRepository.
|
|
137
|
+
const offer = await offerRepository.createOffer({
|
|
129
138
|
productId,
|
|
130
139
|
productTitle: product.title,
|
|
131
140
|
productSlug: product.slug,
|
|
132
141
|
productImageUrl: product.mainImage,
|
|
133
142
|
buyerUid: userId,
|
|
134
143
|
buyerName: buyerDisplayName,
|
|
135
|
-
buyerEmail:
|
|
144
|
+
buyerEmail: profilePlain?.email ?? userEmail ?? "",
|
|
136
145
|
storeId: product.storeId,
|
|
137
146
|
storeName: product.storeName ?? "",
|
|
138
147
|
offerAmount,
|
|
@@ -284,14 +293,24 @@ export async function counterOfferByBuyer(userId, userEmail, input) {
|
|
|
284
293
|
// single-field equality served by the automatic index.
|
|
285
294
|
const chainRoot = offer.chainRootOfferId ?? offer.id;
|
|
286
295
|
const nextRound = (offer.counterRound ?? 1) + 1;
|
|
287
|
-
|
|
296
|
+
/*
|
|
297
|
+
* 🛑 A NINTH double-encryption site, found by the nominal brand.
|
|
298
|
+
*
|
|
299
|
+
* A buyer counter creates a NEW offer document carrying the previous round's
|
|
300
|
+
* buyer identity forward. `offer` came from a read, so those two fields are
|
|
301
|
+
* ciphertext — and `createOffer` encrypts `buyerName`/`buyerEmail` again
|
|
302
|
+
* (`OFFER_PII_FIELDS`), so round 2 onward stored a permanently unreadable
|
|
303
|
+
* buyer. Rounds 1 was fine, which is why nothing looked wrong.
|
|
304
|
+
*/
|
|
305
|
+
const offerPlain = offerRepository.revealOffer(offer);
|
|
306
|
+
const newOffer = await offerRepository.createOffer({
|
|
288
307
|
productId: offer.productId,
|
|
289
308
|
productTitle: offer.productTitle,
|
|
290
309
|
productSlug: offer.productSlug,
|
|
291
310
|
productImageUrl: offer.productImageUrl,
|
|
292
311
|
buyerUid: userId,
|
|
293
|
-
buyerName:
|
|
294
|
-
buyerEmail:
|
|
312
|
+
buyerName: offerPlain.buyerName,
|
|
313
|
+
buyerEmail: offerPlain.buyerEmail,
|
|
295
314
|
storeId: offer.storeId,
|
|
296
315
|
storeName: offer.storeName,
|
|
297
316
|
offerAmount: counterAmount,
|
|
@@ -349,14 +368,25 @@ export async function withdrawOffer(userId, offerId) {
|
|
|
349
368
|
// --- Read ------------------------------------------------------------------
|
|
350
369
|
export async function listBuyerOffers(userId) {
|
|
351
370
|
const result = await offerRepository.findByBuyer(userId);
|
|
352
|
-
|
|
371
|
+
/*
|
|
372
|
+
* REVEALED: these are the buyer's OWN offers, so showing them their own name
|
|
373
|
+
* and email in full is the canonical legitimate plaintext read. The seller's
|
|
374
|
+
* view of the same documents goes through `maskedOffer` instead.
|
|
375
|
+
*/
|
|
376
|
+
return result.items.map((o) => offerRepository.revealOffer(o));
|
|
353
377
|
}
|
|
354
378
|
export async function listSellerOffers(userId) {
|
|
355
379
|
const store = await storeRepository.findByOwnerId(userId);
|
|
356
380
|
if (!store)
|
|
357
381
|
return [];
|
|
358
382
|
const result = await offerRepository.findByStore(store.id);
|
|
359
|
-
|
|
383
|
+
/*
|
|
384
|
+
* `maskedOffer`, not `maskOfferForSeller`. The latter calls `maskName`,
|
|
385
|
+
* which refuses ciphertext by design and answers "Anonymous" — and
|
|
386
|
+
* `findByStore` returns raw documents. So the seller's own offers list
|
|
387
|
+
* showed every buyer as Anonymous. `maskedOffer` decrypts first, then masks.
|
|
388
|
+
*/
|
|
389
|
+
return result.items.map((o) => offerRepository.maskedOffer(o));
|
|
360
390
|
}
|
|
361
391
|
// --- Buyer: Checkout accepted offer ----------------------------------------
|
|
362
392
|
export async function checkoutOffer(userId, offerId) {
|
|
@@ -257,7 +257,7 @@ export async function requestPayout(userId, userName, userEmail, input) {
|
|
|
257
257
|
const earnings = await computeSellerEarnings(userId);
|
|
258
258
|
if (earnings.netAmount <= 0 || earnings.eligibleOrders.length === 0)
|
|
259
259
|
throw new ValidationError("No eligible earnings available for payout.");
|
|
260
|
-
const payout = await payoutRepository.
|
|
260
|
+
const payout = await payoutRepository.createPayout({
|
|
261
261
|
storeId: userId,
|
|
262
262
|
sellerName: userName,
|
|
263
263
|
sellerEmail: userEmail,
|
|
@@ -288,6 +288,16 @@ export async function bulkSellerOrder(userId, userRole, userDisplayName, userEma
|
|
|
288
288
|
const userDoc = await userRepository.findById(userId);
|
|
289
289
|
if (!userDoc)
|
|
290
290
|
throw new AuthorizationError("User not found");
|
|
291
|
+
/*
|
|
292
|
+
* 🛑 REVEALED before building a payout — the same active corruption the two
|
|
293
|
+
* payout crons had. `findById` is ciphertext-by-default and `createPayout`
|
|
294
|
+
* encrypts `upiId` again (`PAYOUT_PII_FIELDS`), so the seller-initiated path
|
|
295
|
+
* wrote a permanently unreadable payee address too.
|
|
296
|
+
*
|
|
297
|
+
* `payoutDetails` is encrypted by `encryptPayoutDetails`, a separate scheme
|
|
298
|
+
* from the `piiFields` path, which is why `revealUser` unwraps it explicitly.
|
|
299
|
+
*/
|
|
300
|
+
const userPlain = userRepository.revealUser(userDoc);
|
|
291
301
|
if (!userDoc.payoutDetails?.isConfigured) {
|
|
292
302
|
throw new ValidationError("Payout details are not set up. Please configure your payout method before requesting a payout.");
|
|
293
303
|
}
|
|
@@ -337,7 +347,7 @@ export async function bulkSellerOrder(userId, userRole, userDisplayName, userEma
|
|
|
337
347
|
const grossAmount = ordersTaxableValue(eligible);
|
|
338
348
|
const bulkSiteSettings = await siteSettingsRepository.getSingleton();
|
|
339
349
|
const bulkDeduction = computePayoutDeduction(grossAmount, bulkSiteSettings.commissions);
|
|
340
|
-
const payoutDoc = await payoutRepository.
|
|
350
|
+
const payoutDoc = await payoutRepository.createPayout({
|
|
341
351
|
storeId: userId,
|
|
342
352
|
sellerName: userDisplayName,
|
|
343
353
|
sellerEmail: userEmail,
|
|
@@ -351,14 +361,31 @@ export async function bulkSellerOrder(userId, userRole, userDisplayName, userEma
|
|
|
351
361
|
gstAmount: bulkDeduction.gstOnFee,
|
|
352
362
|
gstRate: bulkSiteSettings.commissions.gstPercent / 100,
|
|
353
363
|
currency: getDefaultCurrency(),
|
|
354
|
-
paymentMethod:
|
|
364
|
+
paymentMethod: userPlain.payoutDetails?.method === "upi"
|
|
355
365
|
? "upi"
|
|
356
366
|
: "bank_transfer",
|
|
357
|
-
upiId:
|
|
358
|
-
?
|
|
367
|
+
upiId: userPlain.payoutDetails?.method === "upi"
|
|
368
|
+
? userPlain.payoutDetails.upiId
|
|
359
369
|
: undefined,
|
|
360
|
-
|
|
361
|
-
|
|
370
|
+
/*
|
|
371
|
+
* 🛑 Only the MASKED subset, matching both payout crons.
|
|
372
|
+
*
|
|
373
|
+
* This used to copy the whole `bankAccount`, including the real
|
|
374
|
+
* `accountNumber` that `encryptPayoutDetails` had encrypted on the user
|
|
375
|
+
* document. `PAYOUT_PII_FIELDS` does not list it and
|
|
376
|
+
* `encryptPayoutBankAccount` is a no-op, so the ciphertext was stored
|
|
377
|
+
* verbatim on the payout where nothing would ever unwrap it — and
|
|
378
|
+
* revealing it instead would store a real account number in the clear.
|
|
379
|
+
* A payout does not need the full number; it needs to identify the account.
|
|
380
|
+
*/
|
|
381
|
+
bankAccount: userPlain.payoutDetails?.method === "bank_transfer" &&
|
|
382
|
+
userPlain.payoutDetails.bankAccount
|
|
383
|
+
? {
|
|
384
|
+
accountHolderName: userPlain.payoutDetails.bankAccount.accountHolderName,
|
|
385
|
+
accountNumberMasked: userPlain.payoutDetails.bankAccount.accountNumberMasked,
|
|
386
|
+
ifscCode: userPlain.payoutDetails.bankAccount.ifscCode,
|
|
387
|
+
bankName: userPlain.payoutDetails.bankAccount.bankName,
|
|
388
|
+
}
|
|
362
389
|
: undefined,
|
|
363
390
|
notes: `Payout request for ${eligible.length} custom-shipped delivered order(s)`,
|
|
364
391
|
});
|