@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
|
@@ -4,9 +4,9 @@ import { normalizeError } from "../../../errors/normalize.js";
|
|
|
4
4
|
import { DatabaseError } from "../../../errors/index.js";
|
|
5
5
|
import { SUPPORT_TICKET_FIELDS } from "../../../constants/field-names.js";
|
|
6
6
|
import { buildSearchTxt } from "../../../utils/search-txt.js";
|
|
7
|
-
import { planSearchTxt, emptySearchResult } from "../../../utils/search-txt-query.js";
|
|
7
|
+
import { planSearchTxt, emptySearchResult, beginSearchWindow, } from "../../../utils/search-txt-query.js";
|
|
8
8
|
import { sieveAnd, sieveFilter, SIEVE_OP } from "../../../utils/sieve-builder.js";
|
|
9
|
-
import {
|
|
9
|
+
import { maskPii, revealPii } from "../../../security/pii-accessors.js";
|
|
10
10
|
import { SUPPORT_TICKET_COLLECTION, SUPPORT_TICKET_PII_FIELDS, SUPPORT_TICKET_TRACKED_FIELDS, TicketStatusValues, ACTIVE_TICKET_STATUSES, } from "../schemas/firestore.js";
|
|
11
11
|
import { withHistory } from "../../../_internal/shared/history/index.js";
|
|
12
12
|
export class SupportRepository extends BaseRepository {
|
|
@@ -35,9 +35,38 @@ export class SupportRepository extends BaseRepository {
|
|
|
35
35
|
return subject ? buildSearchTxt([subject]) : null;
|
|
36
36
|
}
|
|
37
37
|
/** Decrypt on read so callers see the shape they wrote. */
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
/*
|
|
39
|
+
* 🛑 `mapDoc` is NOT overridden, and that absence IS the change.
|
|
40
|
+
*
|
|
41
|
+
* It used to decrypt on every read, so ciphertext never left the database
|
|
42
|
+
* and every caller handled plaintext without ever saying so. Following
|
|
43
|
+
* `SiteSettingsRepository`: reads return what Firestore holds, and plaintext
|
|
44
|
+
* requires one of the two named methods below.
|
|
45
|
+
*
|
|
46
|
+
* A convention would not hold this. `SupportTicketDocument`'s PII fields are
|
|
47
|
+
* `Encrypted<string>`, assignable to nothing that wants a plain `string`, so
|
|
48
|
+
* a reader that skips both accessors is a compile error naming file and line.
|
|
49
|
+
*/
|
|
50
|
+
/** Plaintext PII. **Opt-in and named** — shows up in review and in a grep. */
|
|
51
|
+
revealTicket(doc) {
|
|
52
|
+
return revealPii(doc, SUPPORT_TICKET_PII_FIELDS);
|
|
53
|
+
}
|
|
54
|
+
/** As `revealTicket`, for a list read. */
|
|
55
|
+
revealTickets(docs) {
|
|
56
|
+
return docs.map((d) => this.revealTicket(d));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Masked PII — **the default for any surface that only needs to identify a
|
|
60
|
+
* row to a human.** Decrypts first, then masks: the mask helpers refuse
|
|
61
|
+
* ciphertext by design, so passing a stored field straight in would return
|
|
62
|
+
* "Anonymous" for every row.
|
|
63
|
+
*/
|
|
64
|
+
maskedTicket(doc) {
|
|
65
|
+
return maskPii(doc, SUPPORT_TICKET_PII_FIELDS);
|
|
66
|
+
}
|
|
67
|
+
/** As `maskedTicket`, for a list read. */
|
|
68
|
+
maskedTickets(docs) {
|
|
69
|
+
return docs.map((d) => this.maskedTicket(d));
|
|
41
70
|
}
|
|
42
71
|
async createTicket(input) {
|
|
43
72
|
try {
|
|
@@ -50,6 +79,9 @@ export class SupportRepository extends BaseRepository {
|
|
|
50
79
|
createdAt: now,
|
|
51
80
|
updatedAt: now,
|
|
52
81
|
};
|
|
82
|
+
// Plaintext in; `create` encrypts via the write hook. This used to be
|
|
83
|
+
// `as unknown as SupportTicketDocument` — a double cast that claimed the
|
|
84
|
+
// freshly-built literal was already ciphertext.
|
|
53
85
|
return this.create(data);
|
|
54
86
|
}
|
|
55
87
|
catch (err) {
|
|
@@ -77,9 +109,9 @@ export class SupportRepository extends BaseRepository {
|
|
|
77
109
|
/*
|
|
78
110
|
* The term is tokenised the same way the index was built, and only the
|
|
79
111
|
* FIRST token is pushed down — Firestore allows one `array-contains` per
|
|
80
|
-
* query. The rest are
|
|
81
|
-
* is what keeps a two-word search from
|
|
82
|
-
* alone (the partial-pushdown trap in Root Cause #62).
|
|
112
|
+
* query. The rest are AND-refined in memory by `beginSearchWindow` /
|
|
113
|
+
* `finish` below, which is what keeps a two-word search from matching on
|
|
114
|
+
* one word alone (the partial-pushdown trap in Root Cause #62).
|
|
83
115
|
*/
|
|
84
116
|
const clauses = [
|
|
85
117
|
sieveFilter(SUPPORT_TICKET_FIELDS.STORE_ID, SIEVE_OP.EQ, storeId),
|
|
@@ -101,13 +133,29 @@ export class SupportRepository extends BaseRepository {
|
|
|
101
133
|
if (plan.head) {
|
|
102
134
|
baseQuery = baseQuery.where(SUPPORT_TICKET_FIELDS.SEARCH_TXT, "array-contains", plan.head);
|
|
103
135
|
}
|
|
136
|
+
/*
|
|
137
|
+
* 🛑 The comment above used to claim `sieveQuery` applied the remaining
|
|
138
|
+
* terms "in memory by its own pass". It does not and never did — nothing
|
|
139
|
+
* read `plan.rest`, so a two-word search returned every ticket matching
|
|
140
|
+
* the LONGEST word, with `truncated` unset so the pager also claimed the
|
|
141
|
+
* result was complete. The claim was the only thing standing where the
|
|
142
|
+
* refine should have been (Root Cause #50's shape: a named guarantee with
|
|
143
|
+
* no implementation behind it).
|
|
144
|
+
*
|
|
145
|
+
* `beginSearchWindow` is the three-part conversion, and all three parts
|
|
146
|
+
* have to happen together — fetch a window, RAISE `maxPageSize` to it, and
|
|
147
|
+
* hand `finish` the page to slice. Doing one and three without two leaves
|
|
148
|
+
* Sieve clamping the window back to a page, which reads exactly like a
|
|
149
|
+
* working conversion and silently no-ops.
|
|
150
|
+
*/
|
|
151
|
+
const win = beginSearchWindow({ page, pageSize }, plan);
|
|
104
152
|
const model = {
|
|
105
153
|
filters: sieveAnd(...clauses.slice(1)),
|
|
106
154
|
sorts: "-createdAt",
|
|
107
|
-
|
|
108
|
-
pageSize,
|
|
155
|
+
...win.model,
|
|
109
156
|
};
|
|
110
|
-
|
|
157
|
+
const result = await this.sieveQuery(model, SupportRepository.SIEVE_FIELDS, { defaultPageSize: pageSize, maxPageSize: win.maxPageSize(50), baseQuery });
|
|
158
|
+
return win.finish(result);
|
|
111
159
|
}
|
|
112
160
|
async getTicketById(ticketId) {
|
|
113
161
|
return this.findById(ticketId);
|
|
@@ -158,6 +206,8 @@ export class SupportRepository extends BaseRepository {
|
|
|
158
206
|
*/
|
|
159
207
|
async updateTicketStatus(ticketId, update, ctx, prior) {
|
|
160
208
|
const current = prior !== undefined ? prior : await this.getTicketById(ticketId);
|
|
209
|
+
// Plain in, ciphertext out: `this.update` runs the write hook over
|
|
210
|
+
// `piiFields`, which is what encrypts `assignedToName` on the way down.
|
|
161
211
|
const patch = {
|
|
162
212
|
...update,
|
|
163
213
|
...this.resolutionStamps(current, update.status),
|
|
@@ -177,6 +227,12 @@ export class SupportRepository extends BaseRepository {
|
|
|
177
227
|
* Never fabricated for a ticket resolved before this existed — that one
|
|
178
228
|
* renders an em-dash, the same rule the offer timeline follows.
|
|
179
229
|
*/
|
|
230
|
+
/*
|
|
231
|
+
* Returns the PLAIN shape. It only ever emits two Date fields, neither of
|
|
232
|
+
* them PII — but it is SPREAD into a plaintext patch, and a return typed
|
|
233
|
+
* against the document would poison that patch's inferred type with
|
|
234
|
+
* `Encrypted<string>` members it never actually sets.
|
|
235
|
+
*/
|
|
180
236
|
resolutionStamps(current, next) {
|
|
181
237
|
if (!next || next === current?.status)
|
|
182
238
|
return {};
|
|
@@ -188,6 +244,14 @@ export class SupportRepository extends BaseRepository {
|
|
|
188
244
|
return {};
|
|
189
245
|
}
|
|
190
246
|
/** Append a timeline entry when the patch touches a tracked field. */
|
|
247
|
+
/*
|
|
248
|
+
* Takes and returns the PLAIN shape.
|
|
249
|
+
*
|
|
250
|
+
* It only threads a patch through `withHistory` and casts both sides to
|
|
251
|
+
* `FirestoreDocument` anyway, so it is indifferent to whether the PII in it
|
|
252
|
+
* is ciphertext — and its callers hold plaintext, because encryption happens
|
|
253
|
+
* in `this.update`'s write hook downstream.
|
|
254
|
+
*/
|
|
191
255
|
withTicketHistory(current, patch, ctx, defaultTrigger) {
|
|
192
256
|
const withEntry = withHistory(current, patch, {
|
|
193
257
|
tracked: SUPPORT_TICKET_TRACKED_FIELDS,
|
|
@@ -233,9 +297,25 @@ export class SupportRepository extends BaseRepository {
|
|
|
233
297
|
throw new DatabaseError("Failed to add message to ticket", err);
|
|
234
298
|
}
|
|
235
299
|
}
|
|
236
|
-
async assignTicket(ticketId, assignedTo,
|
|
300
|
+
async assignTicket(ticketId, assignedTo,
|
|
301
|
+
/*
|
|
302
|
+
* PLAINTEXT. `BaseRepository.update` runs `encryptPiiFields` over
|
|
303
|
+
* `piiFields`, which includes `assignedToName`, so the value is encrypted
|
|
304
|
+
* on the way down — the parameter is honest about what the caller holds.
|
|
305
|
+
* `asEncrypted` here would be the misuse its own docstring forbids:
|
|
306
|
+
* labelling plaintext as ciphertext.
|
|
307
|
+
*/
|
|
308
|
+
assignedToName, ctx, prior) {
|
|
237
309
|
const current = prior !== undefined ? prior : await this.getTicketById(ticketId);
|
|
238
|
-
return this.update(ticketId, this.withTicketHistory(current, {
|
|
310
|
+
return this.update(ticketId, this.withTicketHistory(current, {
|
|
311
|
+
assignedTo,
|
|
312
|
+
// No cast: `withTicketHistory` takes the PLAIN shape, and this
|
|
313
|
+
// parameter is plaintext (the write hook encrypts downstream). The
|
|
314
|
+
// cast that used to sit here existed only because the payload type
|
|
315
|
+
// claimed ciphertext.
|
|
316
|
+
assignedToName,
|
|
317
|
+
updatedAt: new Date(),
|
|
318
|
+
}, ctx, "assignTicket"));
|
|
239
319
|
}
|
|
240
320
|
async listAll(model) {
|
|
241
321
|
return this.sieveQuery(model, SupportRepository.SIEVE_FIELDS, { defaultPageSize: 25, maxPageSize: 100 });
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import type { BaseDocument } from "../../../_internal/shared/types/base-document";
|
|
18
18
|
import type { StatusChangeEntry } from "../../../_internal/shared/history/types";
|
|
19
|
+
import type { Encrypted, Plaintext } from "../../../security/encrypted";
|
|
19
20
|
export declare const TicketCategoryValues: {
|
|
20
21
|
readonly ORDER_ISSUE: "order_issue";
|
|
21
22
|
readonly BILLING_PAYMENT: "billing_payment";
|
|
@@ -72,12 +73,22 @@ export interface TicketRelatedParties {
|
|
|
72
73
|
productId?: string;
|
|
73
74
|
bidId?: string;
|
|
74
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* `SupportTicketDocument` with its PII as plain `string` — what the named repository
|
|
78
|
+
* accessors return, and what a WRITE path accepts (the repository encrypts).
|
|
79
|
+
*
|
|
80
|
+
* `Plaintext<T>` is the one definition of this shape; see `security/encrypted.ts`.
|
|
81
|
+
* It is deliberately NOT distinguishable from a MASKED value — `maskPii`
|
|
82
|
+
* returns plain strings too, and no type can express "these characters are
|
|
83
|
+
* asterisks". The distinction is which named method you called.
|
|
84
|
+
*/
|
|
85
|
+
export type SupportTicketPlain = Plaintext<SupportTicketDocument>;
|
|
75
86
|
export interface SupportTicketDocument extends BaseDocument {
|
|
76
87
|
userId: string;
|
|
77
88
|
/** Denormalized — for admin table display. */
|
|
78
|
-
userEmail: string
|
|
89
|
+
userEmail: Encrypted<string>;
|
|
79
90
|
/** Denormalized — for admin table display. */
|
|
80
|
-
userDisplayName: string
|
|
91
|
+
userDisplayName: Encrypted<string>;
|
|
81
92
|
category: TicketCategory;
|
|
82
93
|
subject: string;
|
|
83
94
|
/** Initial description written by the user when opening the ticket. */
|
|
@@ -109,7 +120,7 @@ export interface SupportTicketDocument extends BaseDocument {
|
|
|
109
120
|
/** UID of the assigned employee. */
|
|
110
121
|
assignedTo?: string;
|
|
111
122
|
/** Denormalized display name of the assignee. */
|
|
112
|
-
assignedToName?: string
|
|
123
|
+
assignedToName?: Encrypted<string>;
|
|
113
124
|
/**
|
|
114
125
|
* Internal-only notes visible only to admins/employees, never shown to the ticket author.
|
|
115
126
|
* Append-only — never overwrite, always append with timestamp.
|
|
@@ -166,8 +177,8 @@ export declare const SUPPORT_TICKET_TRACKED_FIELDS: readonly ["status", "priorit
|
|
|
166
177
|
* (`assignedTo`) and never the name.
|
|
167
178
|
*/
|
|
168
179
|
export declare const SUPPORT_TICKET_PII_FIELDS: readonly ["userEmail", "userDisplayName", "assignedToName"];
|
|
169
|
-
export type SupportTicketCreateInput = Pick<
|
|
170
|
-
export type SupportTicketUpdateInput = Partial<Pick<
|
|
180
|
+
export type SupportTicketCreateInput = Pick<SupportTicketPlain, "userId" | "userEmail" | "userDisplayName" | "category" | "subject" | "description" | "orderId" | "storeId">;
|
|
181
|
+
export type SupportTicketUpdateInput = Partial<Pick<SupportTicketPlain, "status" | "priority" | "assignedTo" | "assignedToName" | "internalNotes" | "relatedParties">>;
|
|
171
182
|
export declare const SUPPORT_TICKET_COLLECTION: "supportTickets";
|
|
172
183
|
export declare const SUPPORT_TICKET_ID_PREFIX: "ticket-";
|
|
173
184
|
export declare const SUPPORT_TICKET_INDEXED_FIELDS: readonly ["userId", "storeId", "searchTxt", "status", "category", "priority", "assignedTo", "orderId", "createdAt", "updatedAt"];
|
|
@@ -77,17 +77,32 @@ export const authored = {
|
|
|
77
77
|
startPage: "/admin/events",
|
|
78
78
|
steps: [
|
|
79
79
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
80
|
-
"Open 'QA Event raffle-open' in the admin and read the entry list.",
|
|
81
|
-
"Trigger
|
|
80
|
+
"Open 'QA Event raffle-open' in the admin editor and read the entry list and the 'Pool size' readout in the raffle panel.",
|
|
81
|
+
"Press 'Trigger Raffle Now'. A CONFIRMATION DIALOG must appear first — read it, then confirm.",
|
|
82
|
+
"Read the winner named and the pool size reported.",
|
|
82
83
|
"RELOAD and read the winner again.",
|
|
83
84
|
"Read whether the winner's name and entry are both recorded on the event.",
|
|
84
|
-
"Sign in as the winning buyer and
|
|
85
|
-
"Sign back in as admin and
|
|
85
|
+
"Sign in as the winning buyer and open the notification bell; read the notification and follow its link.",
|
|
86
|
+
"Sign back in as admin, reopen the event, and read the button's label and enabled state. Then attempt a second draw.",
|
|
86
87
|
],
|
|
87
|
-
expectedBehaviour: "The draw picks once with real randomness, records the winner on the event, and notifies them. A second draw must be refused — redrawing replaces a winner who has already been told they won, and the notification cannot be recalled.",
|
|
88
|
-
expectedUiState: "The winner is named and survives the reload. The event records both the winner and their entry. The buyer has
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
expectedBehaviour: "The draw picks once with real randomness, records the winner on the event, and notifies them. A second draw must be refused — redrawing replaces a winner who has already been told they won, and the notification cannot be recalled. A pool size of 0 on an event that visibly has entries is itself the failure: it means the eligibility query and the entry writer disagree about which field marks an entry eligible.",
|
|
89
|
+
expectedUiState: "A confirmation dialog gates the draw. The winner is named and survives the reload. The event records both the winner and their entry. The buyer has one notification that deep-links to the event's winner page. On reopening, the button reads 'Already triggered' and is disabled; forcing a second draw is refused naming the existing winner, never with a generic failure.",
|
|
90
|
+
/*
|
|
91
|
+
* `drawsPermitted: 1` was already here and had never been exercised. The
|
|
92
|
+
* other three are the preconditions that made it unexercisable:
|
|
93
|
+
* poolSizeNotZero — the draw queried `status == "CONFIRMED"`, which
|
|
94
|
+
* nothing writes at entry time, so EVERY draw
|
|
95
|
+
* returned "no eligible entries"
|
|
96
|
+
* winnerNotifications — the winner was recorded and never told
|
|
97
|
+
* confirmationDialogs — an irreversible action with no confirmation
|
|
98
|
+
*/
|
|
99
|
+
expectedData: {
|
|
100
|
+
drawsPermitted: 1,
|
|
101
|
+
poolSizeNotZero: true,
|
|
102
|
+
winnerNotifications: 1,
|
|
103
|
+
confirmationDialogs: 1,
|
|
104
|
+
},
|
|
105
|
+
endResult: "BEFORE: there was no confirmation dialog, the draw answered 'no eligible entries' on an event with entries (so no winner could ever be drawn), and on the rare path it did write a winner that person was never notified. AFTER: the dialog gates it, one winner is drawn and notified, and the second attempt is refused naming the existing winner. Delete the event afterwards. A second draw succeeding is the most serious failure on this page.",
|
|
91
106
|
},
|
|
92
107
|
"checklist-admin-events-raffles-spin-spin-wheel-create": {
|
|
93
108
|
roles: ["admin", "buyer"],
|
|
@@ -98,13 +113,25 @@ export const authored = {
|
|
|
98
113
|
"Define three prizes with labels and weights, set a maximum of 2 spins per user, and set a spin window covering today.",
|
|
99
114
|
"Save, RELOAD, and read every prize, weight, the maximum and the window.",
|
|
100
115
|
"Open the event's public page at /events/{its id} and read the wheel and its segments.",
|
|
101
|
-
"
|
|
116
|
+
"View SOURCE (or the network payload) for /events/{its id}/participate and search the response text for the string 'weight'. Then search it for 'couponId'.",
|
|
102
117
|
],
|
|
103
118
|
inputs: { title: "QA Event spin-create", prizeCount: 3, maxPerUser: 2 },
|
|
104
119
|
expectedBehaviour: "Prizes, weights, the per-user maximum and the window all round-trip. Weights are the odds and belong server-side — a public wheel that renders them tells every visitor which segment is worthless.",
|
|
105
|
-
expectedUiState: "After the reload all four groups of settings hold. The public wheel shows three labelled segments
|
|
106
|
-
|
|
107
|
-
|
|
120
|
+
expectedUiState: "After the reload all four groups of settings hold. The public wheel shows three labelled segments.",
|
|
121
|
+
/*
|
|
122
|
+
* 🛑 The last step searches the PAYLOAD, not the rendered wheel, and that
|
|
123
|
+
* is the whole case.
|
|
124
|
+
*
|
|
125
|
+
* It previously read "check the wheel does NOT disclose their weights" —
|
|
126
|
+
* and the rendered wheel has never shown a weight, so a tester looked at
|
|
127
|
+
* three labelled segments and passed. Meanwhile the participate page cast
|
|
128
|
+
* the stored prize array straight into a client prop, so every weight AND
|
|
129
|
+
* every internal coupon document id was sitting in the RSC payload of a
|
|
130
|
+
* public page. The case asserted `weightsInPublicSource: 0` and could not
|
|
131
|
+
* see the thing it was counting.
|
|
132
|
+
*/
|
|
133
|
+
expectedData: { weightsInPublicSource: 0, couponIdsInPublicSource: 0 },
|
|
134
|
+
endResult: "BEFORE: the payload contained 'weight: 15', 'weight: 25', 'weight: 10' and a 'couponId' for any prize carrying a coupon. AFTER: neither string appears anywhere in it, while the three labels still render. Leave the event in place — the limits case reads it.",
|
|
108
135
|
},
|
|
109
136
|
"checklist-admin-events-raffles-spin-spin-wheel-limits-enforced": {
|
|
110
137
|
roles: ["admin", "buyer"],
|
|
@@ -113,16 +140,31 @@ export const authored = {
|
|
|
113
140
|
"Sign in as rehan.sheikh@gmail.com / TempPass123! and open 'QA Event spin-create'.",
|
|
114
141
|
"Spin once and read the prize and the remaining count.",
|
|
115
142
|
"Spin a second time and read the remaining count.",
|
|
116
|
-
"Attempt a third spin and read the refusal.",
|
|
117
|
-
"RELOAD the page and
|
|
143
|
+
"Attempt a third spin and read the refusal WORD FOR WORD.",
|
|
144
|
+
"RELOAD the page and read the Spin button's state before clicking anything.",
|
|
145
|
+
"An event is reachable by two URL spellings — its SLUG and its DOCUMENT ID. Note which one you have been using, then open the OTHER one as the same account and attempt a spin.",
|
|
118
146
|
"Sign out and sign in as admin@letitrip.in / TempPass123!, then narrow the spin window so it is closed.",
|
|
119
|
-
"Sign back in as the buyer and attempt a spin, reading
|
|
147
|
+
"Sign back in as the buyer and attempt a spin, reading that refusal word for word too.",
|
|
120
148
|
],
|
|
121
149
|
inputs: { maxPerUser: 2 },
|
|
122
|
-
expectedBehaviour: "The per-user cap and the window are both enforced server-side
|
|
123
|
-
expectedUiState: "The
|
|
124
|
-
|
|
125
|
-
|
|
150
|
+
expectedBehaviour: "The per-user cap and the window are both enforced server-side, the cap is scoped to the PERSON rather than to the URL spelling, and the two refusals read differently — 'no spins left' and 'the window is closed' are different facts and a single generic message tells the buyer neither.",
|
|
151
|
+
expectedUiState: "The third attempt is refused naming the limit. After the reload the button is already disabled with the spent state shown, without a round trip. The other URL spelling is refused identically. With the window closed the refusal names the window instead.",
|
|
152
|
+
/*
|
|
153
|
+
* Three invariants, each of which was broken in a different way:
|
|
154
|
+
* spinsAfterReload — the cap itself
|
|
155
|
+
* spinsViaOtherUrlForm — the guest/identity hash was scoped to the
|
|
156
|
+
* SLUG while enterEvent scoped it to the doc id,
|
|
157
|
+
* so the two URL spellings were two identities
|
|
158
|
+
* and the other one handed out a fresh allowance
|
|
159
|
+
* distinctRefusalMessages — both refusals were the same sentence
|
|
160
|
+
*/
|
|
161
|
+
expectedData: {
|
|
162
|
+
maxPerUser: 2,
|
|
163
|
+
spinsAfterReload: 0,
|
|
164
|
+
spinsViaOtherUrlForm: 0,
|
|
165
|
+
distinctRefusalMessages: 2,
|
|
166
|
+
},
|
|
167
|
+
endResult: "BEFORE: every refusal on this page read 'Spin failed. Please try again.', the button stayed enabled after a reload so the cap cost a round trip to discover, and the other URL spelling granted a fresh set of spins. AFTER: 'You've already used your spins for this event.' and 'The spin window isn't open right now.' are distinct, the button is pre-disabled, and both URL spellings share one allowance. Delete the event afterwards.",
|
|
126
168
|
},
|
|
127
169
|
"checklist-admin-events-raffles-spin-event-entries-admin-view": {
|
|
128
170
|
roles: ["admin"],
|
|
@@ -284,12 +284,28 @@ export const authored = {
|
|
|
284
284
|
steps: [
|
|
285
285
|
"Open /.",
|
|
286
286
|
"Scroll down looking for a 'Raffles' or 'Spin Wheels' strip.",
|
|
287
|
+
"Read the TYPE of EVERY card in that strip — open each one if the card does not say. Write down any card that is not a raffle or a spin wheel.",
|
|
287
288
|
"Open /events in a new tab.",
|
|
288
289
|
"Note which events have a raffle or spin-wheel type and are currently active.",
|
|
289
290
|
],
|
|
290
|
-
expectedBehaviour: "If any raffle or spin-wheel event is active on /events, the homepage shows a 'Raffles & Spin Wheels' strip containing those events. The strip is absent only when genuinely no raffle event is active.",
|
|
291
|
-
expectedUiState: "The homepage shows a 'Raffles & Spin Wheels' heading with at least one event card beneath it (e.g. 'Daily Beyblade Pull! Spin for a Prize'). The same
|
|
292
|
-
|
|
291
|
+
expectedBehaviour: "If any raffle or spin-wheel event is active on /events, the homepage shows a 'Raffles & Spin Wheels' strip containing those events AND NOTHING ELSE. The strip is absent only when genuinely no raffle or spin-wheel event is active.",
|
|
292
|
+
expectedUiState: "The homepage shows a 'Raffles & Spin Wheels' heading with at least one event card beneath it (e.g. 'Daily Beyblade Pull! Spin for a Prize'). Every card in the strip is a raffle or a spin wheel. The same events must also appear on /events.",
|
|
293
|
+
/*
|
|
294
|
+
* 🛑 Step 3 — reading each card's TYPE — is the case.
|
|
295
|
+
*
|
|
296
|
+
* The strip filtered on `hasRaffle`, which was not in the events repository's
|
|
297
|
+
* SIEVE_FIELDS. The Sieve adapter runs with `throwExceptions: false`, so an
|
|
298
|
+
* unallowlisted field does not raise: the clause was SILENTLY DISCARDED and
|
|
299
|
+
* the query degraded to `status == active` sorted by start date. The strip
|
|
300
|
+
* rendered the six most recently-started active events of any type — sales,
|
|
301
|
+
* polls, feedback forms — under a heading promising raffles.
|
|
302
|
+
*
|
|
303
|
+
* Every card was a real, live, correctly-rendered event, which is exactly
|
|
304
|
+
* why this read as working. Only the heading was false, and the old case
|
|
305
|
+
* asked for "at least one event card", which that satisfied.
|
|
306
|
+
*/
|
|
307
|
+
expectedData: { nonPrizeEventsInStrip: 0 },
|
|
308
|
+
endResult: "BEFORE: the strip listed active events of ANY type — a clearance sale and a poll appeared under 'Raffles & Spin Wheels' — because the raffle filter was being dropped before the query ran. AFTER: every card in the strip is a raffle or a spin wheel. Nothing is persisted.",
|
|
293
309
|
},
|
|
294
310
|
"checklist-buying-browsing-search-homepage-prize-draws-section": {
|
|
295
311
|
roles: ["guest"],
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: Authored six-part procedures for content-discovery/authenticity-checker.
|
|
3
|
+
* WHAT: 13 cases, keyed by full checklist id.
|
|
4
|
+
*
|
|
5
|
+
* 🛑 EVERY CASE IS WRITTEN AS BEFORE → AFTER, because this feature's whole
|
|
6
|
+
* failure mode is a confident, plausible, wrong answer returned with HTTP 200. A
|
|
7
|
+
* case reading "check the authenticity tool works" passes against all of these:
|
|
8
|
+
*
|
|
9
|
+
* 1. A two-state sign-in gate. `SessionProvider` mounts `initialUser={null}`,
|
|
10
|
+
* so `loading` starts true for SIGNED-IN users too — the page flashed "sign
|
|
11
|
+
* in to run a lookup" at an authenticated collector on every hard load.
|
|
12
|
+
* 2. A verdict with no hedge. The engine can match a midfake batch code; the
|
|
13
|
+
* wrong version of that screen reads as an accusation against the seller.
|
|
14
|
+
* 3. One date printed as fact. Two decodings of a Metal Fight box code
|
|
15
|
+
* circulate and Takara Tomy has published neither.
|
|
16
|
+
* 4. Treating a real product code as evidence. A counterfeit catalogue
|
|
17
|
+
* measured for this site carried 900+ genuine Takara Tomy codes.
|
|
18
|
+
* 5. Picking one bey when the typed name matches a dozen.
|
|
19
|
+
* 6. Calling a legitimate spelling a fake tell — Takara Tomy romanises the
|
|
20
|
+
* line as "Pegasis" while Hasbro prints "Pegasus", and an earlier draft of
|
|
21
|
+
* the checker flagged the TT spelling as trademark evasion.
|
|
22
|
+
* 7. Calling Funskool a fake. It is a licensed Indian importer.
|
|
23
|
+
* 8. A sign-in wall with no public content, which is thin content to a crawler
|
|
24
|
+
* and useless to someone who arrived from search.
|
|
25
|
+
*
|
|
26
|
+
* So each case names what the wrong answer LOOKED like, and several assert the
|
|
27
|
+
* presence of a hedge rather than the presence of a result.
|
|
28
|
+
*
|
|
29
|
+
* @tag domain:tester
|
|
30
|
+
* @tag layer:seed
|
|
31
|
+
* @tag pattern:none
|
|
32
|
+
* @tag access:server-only
|
|
33
|
+
* @tag consumers:seed-data/authored/index.ts
|
|
34
|
+
* @tag sideEffects:none
|
|
35
|
+
*/
|
|
36
|
+
export const authored = {
|
|
37
|
+
"checklist-content-discovery-authenticity-checker-guest-sees-reference-not-a-wall": {
|
|
38
|
+
roles: ["guest"],
|
|
39
|
+
startPage: "/tools/authenticity",
|
|
40
|
+
steps: [
|
|
41
|
+
"Open /tools/authenticity signed out.",
|
|
42
|
+
"Confirm there is exactly ONE sign-in box, and it sits where the lookup form would be.",
|
|
43
|
+
"Scroll past it and confirm the generation table renders — six rows, each naming years, code prefixes and licensed marques.",
|
|
44
|
+
"Confirm the 'Checks that apply whatever the generation' list renders with real text in each row.",
|
|
45
|
+
"Answer no if the page is only a sign-in prompt, or if the reference content is missing.",
|
|
46
|
+
],
|
|
47
|
+
expectedBehaviour: "BEFORE: gating the page would have meant a signed-out visitor — and a crawler — getting nothing but 'please sign in', which is thin content and throws away the highest-intent query this site can answer. AFTER: only the LOOKUP is gated. The generation table, the box-code explanation, the universal checks and the licensed-marque list are server-rendered and public, identical for crawlers and humans.",
|
|
48
|
+
expectedUiState: "One sign-in card reading 'Sign in to run a lookup', followed by the heading 'Which generation is it, and what does the box code mean' and six generation rows, then the universal checks, then 'Who is actually licensed'.",
|
|
49
|
+
expectedData: { generationRows: 6 },
|
|
50
|
+
endResult: "Nothing is changed; this case only reads.",
|
|
51
|
+
},
|
|
52
|
+
"checklist-content-discovery-authenticity-checker-no-signin-flash-when-authenticated": {
|
|
53
|
+
roles: ["buyer"],
|
|
54
|
+
startPage: "/tools/authenticity",
|
|
55
|
+
steps: [
|
|
56
|
+
"Sign in as a buyer.",
|
|
57
|
+
"Hard-reload /tools/authenticity (Ctrl+F5, not a soft navigation).",
|
|
58
|
+
"Watch the lookup area through the first second of the load.",
|
|
59
|
+
"Answer no if the words 'Sign in to run a lookup' appear at any point.",
|
|
60
|
+
],
|
|
61
|
+
expectedBehaviour: "BEFORE: a two-state gate (`!user` → prompt) showed the sign-in card to signed-in users on every hard load, because the session provider mounts with a null user and resolves asynchronously. AFTER: three states — while `loading` is true the area shows 'Checking your session…', and the prompt is reachable only once the session has resolved to no user.",
|
|
62
|
+
expectedUiState: "Either 'Checking your session…' briefly, then the form. Never the sign-in card.",
|
|
63
|
+
endResult: "The form is present after reload. Nothing persists.",
|
|
64
|
+
},
|
|
65
|
+
"checklist-content-discovery-authenticity-checker-midfake-code-reads-as-batch-not-accusation": {
|
|
66
|
+
roles: ["buyer"],
|
|
67
|
+
startPage: "/tools/authenticity",
|
|
68
|
+
steps: [
|
|
69
|
+
"Sign in and open /tools/authenticity.",
|
|
70
|
+
"Enter A0813 in 'Code on the box' and leave everything else empty.",
|
|
71
|
+
"Submit.",
|
|
72
|
+
"Read the verdict block and the caveat directly beneath it.",
|
|
73
|
+
"Answer no if the screen anywhere calls the item counterfeit, or accuses the seller, or omits the caveat.",
|
|
74
|
+
],
|
|
75
|
+
inputs: { code: "A0813" },
|
|
76
|
+
expectedBehaviour: "BEFORE: the obvious implementation of a code match is a boolean 'FAKE'. A0813 is a real recorded midfake batch code, and a midfake is Takara-Tomy-blueprint product of reduced quality — a statement about a production run, not about whoever is selling it. AFTER: the verdict is 'Likely reproduction', and the caveat in the same block says it is a statement about a batch or brand and not an accusation against the seller.",
|
|
77
|
+
expectedUiState: "A 'Likely reproduction' badge, a one-sentence headline about matching a batch the community records as a midfake, and a caveat naming batch-or-brand rather than the seller. A Safety block about lead and cadmium is present.",
|
|
78
|
+
expectedData: { verdict: "likely-reproduction" },
|
|
79
|
+
endResult: "Nothing is written. Reloading clears the result.",
|
|
80
|
+
},
|
|
81
|
+
"checklist-content-discovery-authenticity-checker-date-code-shows-both-readings": {
|
|
82
|
+
roles: ["buyer"],
|
|
83
|
+
startPage: "/tools/authenticity",
|
|
84
|
+
steps: [
|
|
85
|
+
"Enter A0813 as the code and submit.",
|
|
86
|
+
"Find the 'Reading A0813' panel.",
|
|
87
|
+
"Confirm TWO readings are listed — one treating the letter as the month, one treating it as the factory with the digits as a week number.",
|
|
88
|
+
"Confirm the panel says both circulate and that Takara Tomy has published neither.",
|
|
89
|
+
"Answer no if only one date is shown, or if a date is stated without that hedge.",
|
|
90
|
+
],
|
|
91
|
+
inputs: { code: "A0813" },
|
|
92
|
+
expectedBehaviour: "BEFORE: the reference post states a month-day-year rule, so the obvious build prints '8 January 2013' as a fact. A factory/week/year reading also circulates and Takara Tomy has never published a decoding — the post itself says there is no official statement. AFTER: both readings render, labelled, with the disagreement stated.",
|
|
93
|
+
expectedUiState: "'Reading A0813' with two labelled lines — '…letter = month…' → 8 January 2013, and '…letter = factory, next two digits = week…' → factory A, week 8 of 2013 — plus a sentence saying to treat the date as indicative.",
|
|
94
|
+
endResult: "Nothing is changed.",
|
|
95
|
+
},
|
|
96
|
+
"checklist-content-discovery-authenticity-checker-real-product-code-is-not-evidence": {
|
|
97
|
+
roles: ["buyer"],
|
|
98
|
+
startPage: "/tools/authenticity",
|
|
99
|
+
steps: [
|
|
100
|
+
"Enter BB-105 in 'Code on the box', leave the name empty, and submit.",
|
|
101
|
+
"Read the signal rows under 'What I checked'.",
|
|
102
|
+
"Confirm one row says the code is a well-formed Metal Fight code AND that this says nothing about authenticity.",
|
|
103
|
+
"Answer no if a valid product code is presented as supporting authenticity.",
|
|
104
|
+
],
|
|
105
|
+
inputs: { code: "BB-105" },
|
|
106
|
+
expectedBehaviour: "BEFORE: matching a code against a real product list reads as verification, and that is the single most misleading thing this tool could do — a counterfeit catalogue measured for this site printed 516 real B-xx, 189 BB-xx and 147 BX-xx codes across 1,277 unlicensed products. AFTER: a well-formed code establishes the GENERATION and is marked neutral, with the 900-codes measurement stated.",
|
|
107
|
+
expectedUiState: "A neutral signal row (not a green/supporting one) saying BB-105 is a well-formed Metal Fight code and that unlicensed sellers print real codes routinely.",
|
|
108
|
+
expectedData: { generation: "mfb" },
|
|
109
|
+
endResult: "Nothing is changed.",
|
|
110
|
+
},
|
|
111
|
+
"checklist-content-discovery-authenticity-checker-name-code-mismatch-asks-rather-than-accuses": {
|
|
112
|
+
roles: ["buyer"],
|
|
113
|
+
startPage: "/tools/authenticity",
|
|
114
|
+
steps: [
|
|
115
|
+
"Enter 'Storm Pegasus' as the model name and BB-105 as the code. Submit.",
|
|
116
|
+
"Confirm the verdict is 'Discrepancy found' and a signal explains that Storm Pegasus is BB-28 while BB-105 is a different release.",
|
|
117
|
+
"Confirm the signal tells you a Random Booster or multi-pack is an innocent explanation.",
|
|
118
|
+
"Now tick 'This came in a Random Booster or a multi-pack' and submit again.",
|
|
119
|
+
"Confirm the verdict is no longer a discrepancy and a signal says a mismatch is EXPECTED for a Random Booster.",
|
|
120
|
+
"Answer no if the second submission still reports a discrepancy.",
|
|
121
|
+
],
|
|
122
|
+
inputs: { name: "Storm Pegasus", code: "BB-105", randomBooster: true },
|
|
123
|
+
expectedBehaviour: "BEFORE: flagging every name/code mismatch would flag a large class of genuine product, because a Random Booster box carries the BOOSTER's code and not the code of the bey inside — the reference table records several, e.g. Pisces DF145BS under F2810 marked 'Random Booster TT'. AFTER: the mismatch is reported as a question with the Random Booster explanation offered, and ticking the box re-reads it as expected rather than suspicious.",
|
|
124
|
+
expectedUiState: "First submission: 'Discrepancy found' plus a row naming both codes. Second: not a discrepancy, plus a row reading 'Code does not match the model — but you said this is a Random Booster'.",
|
|
125
|
+
expectedData: { firstVerdict: "discrepancy-found", secondVerdict: "inconclusive" },
|
|
126
|
+
endResult: "Nothing is written either time.",
|
|
127
|
+
},
|
|
128
|
+
"checklist-content-discovery-authenticity-checker-ambiguous-name-asks-which-model": {
|
|
129
|
+
roles: ["buyer"],
|
|
130
|
+
startPage: "/tools/authenticity",
|
|
131
|
+
steps: [
|
|
132
|
+
"Enter 'Valkyrie' as the model name, leave the code empty, and submit.",
|
|
133
|
+
"Confirm the tool does NOT announce one specific release.",
|
|
134
|
+
"Confirm a question appears asking which one, with several named options each showing its product code and generation.",
|
|
135
|
+
"Pick one option.",
|
|
136
|
+
"Confirm the result now names that release and the question is gone.",
|
|
137
|
+
"Answer no if the first submission confidently names a single Valkyrie.",
|
|
138
|
+
],
|
|
139
|
+
inputs: { name: "Valkyrie" },
|
|
140
|
+
expectedBehaviour: "BEFORE: a best-match lookup picks the top scoring row and announces it — 'Matched Ultimate Valkyrie Legacy Variable'-9' — which is a confident answer to a question nobody asked, and every per-model fact after it (the expected product code above all) is then about the wrong bey. AFTER: several candidates at the same quality become a question; picking one pins it and re-runs.",
|
|
141
|
+
expectedUiState: "A question headed 'Which one? \"Valkyrie\" matches N releases', with one button per candidate labelled by name and hinting its code and generation. After the pick, a signal naming the chosen release.",
|
|
142
|
+
endResult: "Nothing is written. The pick is not remembered after reload.",
|
|
143
|
+
},
|
|
144
|
+
"checklist-content-discovery-authenticity-checker-takara-spelling-is-not-flagged": {
|
|
145
|
+
roles: ["buyer"],
|
|
146
|
+
startPage: "/tools/authenticity",
|
|
147
|
+
steps: [
|
|
148
|
+
"Enter 'Storm Pegasis' (Takara Tomy's own spelling, with an i) and submit.",
|
|
149
|
+
"Confirm the bey resolves and NO signal suggests the spelling is a trademark-evasion tell.",
|
|
150
|
+
"Now enter 'Scythee Kronos' (a genuine evasion spelling, doubled e) and submit.",
|
|
151
|
+
"Confirm that one DOES carry the misspelling note.",
|
|
152
|
+
"Answer no if 'Storm Pegasis' is flagged, or if 'Scythee Kronos' is not.",
|
|
153
|
+
],
|
|
154
|
+
inputs: { name: "Storm Pegasis" },
|
|
155
|
+
expectedBehaviour: "BEFORE: this project's counterfeit crawl lists `Pegasis` among the trademark-evasion spellings — true of that one catalogue, and NOT a general rule. Takara Tomy romanises the line as Pegasis (the corpus's own Japanese for Storm Pegasus is ストームペガシス) while Hasbro prints Pegasus. An earlier draft told the user that the spelling printed on a genuine Takara Tomy box was a sign of a fake. AFTER: the pair is registered as a legitimate variant, so it resolves as an alias and the warning stays silent; a real evasion spelling still trips it.",
|
|
156
|
+
expectedUiState: "'Storm Pegasis' → a signal reading 'Matched Storm Pegasus 105RF' with no mention of evasion. 'Scythee Kronos' → a signal reading 'Read as \"Scythe Kronos T125EDS\"' that does mention trademark evasion.",
|
|
157
|
+
endResult: "Nothing is changed.",
|
|
158
|
+
},
|
|
159
|
+
"checklist-content-discovery-authenticity-checker-funskool-is-not-a-fake-signal": {
|
|
160
|
+
roles: ["buyer"],
|
|
161
|
+
startPage: "/tools/authenticity",
|
|
162
|
+
steps: [
|
|
163
|
+
"Enter 'Funskool' in 'Brand printed on the box' and submit.",
|
|
164
|
+
"Confirm the signal says Funskool is a LICENSED marque.",
|
|
165
|
+
"Confirm the verdict is not 'Likely reproduction'.",
|
|
166
|
+
"Now enter 'Rapidity' instead and submit.",
|
|
167
|
+
"Confirm that one IS reported as a known unlicensed producer, with a Safety block.",
|
|
168
|
+
"Answer no if Funskool is treated as a concern, or if Rapidity is not named.",
|
|
169
|
+
],
|
|
170
|
+
inputs: { marque: "Funskool" },
|
|
171
|
+
expectedBehaviour: "BEFORE: a tool that only knows 'Takara Tomy / Hasbro' tells an Indian buyer holding a legitimate Funskool box that the brand is unrecognised — a false positive against a licensed importer, in this marketplace's main market. AFTER: the licensed set includes Funskool (India), Mani (Hong Kong), Sheng Tai (Singapore), NewBoy (Middle East) and the rest, and only named unlicensed producers reach a reproduction verdict.",
|
|
172
|
+
expectedUiState: "Funskool → a supporting signal naming it licensed and mentioning the regional importers. Rapidity → 'Likely reproduction', a signal naming Hongyi, and a Safety block about lead and cadmium.",
|
|
173
|
+
expectedData: { funskoolVerdict: "inconclusive", rapidityVerdict: "likely-reproduction" },
|
|
174
|
+
endResult: "Nothing is written either time.",
|
|
175
|
+
},
|
|
176
|
+
"checklist-content-discovery-authenticity-checker-unknown-code-is-not-called-fake": {
|
|
177
|
+
roles: ["buyer"],
|
|
178
|
+
startPage: "/tools/authenticity",
|
|
179
|
+
steps: [
|
|
180
|
+
"Enter L9999 as the code and submit.",
|
|
181
|
+
"If asked which generation, choose Metal Fight.",
|
|
182
|
+
"Read the signal about the code.",
|
|
183
|
+
"Confirm it says the code is NOT RECORDED and explicitly that this does not mean fake.",
|
|
184
|
+
"Answer no if an unrecorded code is presented as evidence of a fake.",
|
|
185
|
+
],
|
|
186
|
+
inputs: { code: "L9999", generation: "mfb" },
|
|
187
|
+
expectedBehaviour: "BEFORE: an absent lookup result reads as a negative result. The reference table is 90 distinct codes compiled by collectors from boxes they personally held, so it was never complete and new genuine codes surface regularly. AFTER: an unmatched code produces a neutral signal saying 'not recorded', with that reasoning stated.",
|
|
188
|
+
expectedUiState: "A neutral signal row saying L9999 is not in either table and that an absent code means 'not recorded', not 'fake'. The verdict is 'Inconclusive', never 'Likely reproduction'.",
|
|
189
|
+
expectedData: { verdict: "inconclusive" },
|
|
190
|
+
endResult: "Nothing is changed.",
|
|
191
|
+
},
|
|
192
|
+
"checklist-content-discovery-authenticity-checker-asks-generation-when-nothing-inferable": {
|
|
193
|
+
roles: ["buyer"],
|
|
194
|
+
startPage: "/tools/authenticity",
|
|
195
|
+
steps: [
|
|
196
|
+
"Submit the form with every field empty.",
|
|
197
|
+
"Confirm the tool asks for something rather than returning a verdict.",
|
|
198
|
+
"Now enter a name that matches nothing — 'zzzznope' — and submit.",
|
|
199
|
+
"Confirm a question appears asking which generation it is, with six options each naming years and example code prefixes.",
|
|
200
|
+
"Answer no if the tool returns any verdict at all without being able to establish a generation.",
|
|
201
|
+
],
|
|
202
|
+
inputs: { name: "zzzznope" },
|
|
203
|
+
expectedBehaviour: "BEFORE: guessing a generation hands the user a confidently wrong checklist — tri-wing screws mean something on Burst and nothing on a 2001 plastic bey, and the box date-code table only covers the Metal series. AFTER: with no generation established the verdict is 'Need a bit more' and a question is asked, with the reason for asking stated.",
|
|
204
|
+
expectedUiState: "A 'Need a bit more' badge and a question headed 'Which generation is it?' with six buttons, each labelled with the generation, its years and an example code shape.",
|
|
205
|
+
expectedData: { verdict: "needs-input" },
|
|
206
|
+
endResult: "Nothing is changed.",
|
|
207
|
+
},
|
|
208
|
+
"checklist-content-discovery-authenticity-checker-api-refuses-signed-out": {
|
|
209
|
+
roles: ["guest"],
|
|
210
|
+
startPage: "/tools/authenticity",
|
|
211
|
+
steps: [
|
|
212
|
+
"Signed out, open the browser devtools console on /tools/authenticity.",
|
|
213
|
+
// A tester step is prose a human types into a devtools console: it cannot
|
|
214
|
+
// import a constant, and calling the real URL signed out IS the case.
|
|
215
|
+
// audit-hardcoded-api-routes-ok: literal URL inside a manual test procedure
|
|
216
|
+
"Run: await fetch('/api/tools/authenticity', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code: 'A0813' }) }).then(r => r.status)",
|
|
217
|
+
"Confirm the status is 401 (or 403) and not 200.",
|
|
218
|
+
"Confirm the response body contains no assessment — no verdict, no signals.",
|
|
219
|
+
"Answer no if a signed-out call returns an assessment.",
|
|
220
|
+
],
|
|
221
|
+
inputs: { code: "A0813" },
|
|
222
|
+
expectedBehaviour: "BEFORE: gating only the UI leaves the endpoint open, and the endpoint is where the output actually comes from — 'this matches a batch recorded as a midfake' read off an anonymous API is a quotable claim with no account behind it. AFTER: `auth: true` on the route refuses the call before the engine runs.",
|
|
223
|
+
expectedUiState: "The console shows 401 or 403. No verdict text appears anywhere in the response.",
|
|
224
|
+
expectedData: { status: 401 },
|
|
225
|
+
endResult: "Nothing is written; no assessment is produced.",
|
|
226
|
+
},
|
|
227
|
+
"checklist-content-discovery-authenticity-checker-json-ld-declares-the-gate": {
|
|
228
|
+
roles: ["guest"],
|
|
229
|
+
startPage: "/tools/authenticity",
|
|
230
|
+
steps: [
|
|
231
|
+
"Signed out, view source on /tools/authenticity (Ctrl+U, not devtools' rendered DOM).",
|
|
232
|
+
"Find the application/ld+json block with \"@type\": \"WebApplication\".",
|
|
233
|
+
"Confirm it carries isAccessibleForFree true at the top level and a hasPart entry with isAccessibleForFree FALSE and a cssSelector.",
|
|
234
|
+
"Copy that cssSelector value and run document.querySelectorAll(<selector>).length in the console.",
|
|
235
|
+
"Confirm the count is 1 — the selector matches a real element on the page.",
|
|
236
|
+
"Answer no if the selector matches nothing, or if the gate is undeclared.",
|
|
237
|
+
],
|
|
238
|
+
expectedBehaviour: "BEFORE: a page with gated content and no declaration is a structured-data mismatch — and the alternative mistake, serving the reference content only to crawler user-agents, is cloaking. AFTER: the page declares the gate exactly as the guest-price gate does: the free reference content is marked free, and the lookup region is carved out by selector as not free. 🛑 Root Cause #93 is the record of the failure this case exists to catch — a class name and a selector living as two literals in two files, where a rename points the crawler at a selector matching nothing and looks identical to making no declaration at all.",
|
|
239
|
+
expectedUiState: "A WebApplication JSON-LD block naming the tool, plus a breadcrumb block. The hasPart cssSelector resolves to exactly one element in the live DOM.",
|
|
240
|
+
expectedData: { gatedSelectorMatches: 1 },
|
|
241
|
+
endResult: "Nothing is changed; this case only reads.",
|
|
242
|
+
},
|
|
243
|
+
};
|