@mohasinac/appkit 4.62.0 → 4.63.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 +3 -3
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +8 -29
- package/dist/_internal/client/features/layout/filterNavItems.d.ts +28 -0
- package/dist/_internal/client/features/layout/filterNavItems.js +35 -0
- package/dist/_internal/server/features/ads/booking.d.ts +118 -0
- package/dist/_internal/server/features/ads/booking.js +326 -0
- package/dist/_internal/server/features/checkout/actions.d.ts +129 -9
- package/dist/_internal/server/features/checkout/actions.js +850 -79
- package/dist/_internal/server/features/checkout/expand-order-items.d.ts +1 -2
- package/dist/_internal/server/features/checkout/expand-order-items.js +32 -0
- package/dist/_internal/server/features/checkout/freeze-parts.d.ts +50 -0
- package/dist/_internal/server/features/checkout/freeze-parts.js +94 -0
- package/dist/_internal/server/features/checkout/value-otp-gate.d.ts +55 -0
- package/dist/_internal/server/features/checkout/value-otp-gate.js +60 -0
- package/dist/_internal/server/features/dashboard/attention.d.ts +128 -0
- package/dist/_internal/server/features/dashboard/attention.js +183 -0
- package/dist/_internal/server/features/dashboard/data.d.ts +47 -0
- package/dist/_internal/server/features/dashboard/data.js +82 -0
- package/dist/_internal/server/features/inventory/reservations.d.ts +99 -0
- package/dist/_internal/server/features/inventory/reservations.js +273 -0
- package/dist/_internal/server/features/invoicing/allocate.d.ts +63 -0
- package/dist/_internal/server/features/invoicing/allocate.js +162 -0
- package/dist/_internal/server/features/invoicing/credit-note.d.ts +57 -0
- package/dist/_internal/server/features/invoicing/credit-note.js +126 -0
- package/dist/_internal/server/features/invoicing/gstr1.d.ts +74 -0
- package/dist/_internal/server/features/invoicing/gstr1.js +251 -0
- package/dist/_internal/server/features/invoicing/issue.d.ts +96 -0
- package/dist/_internal/server/features/invoicing/issue.js +202 -0
- package/dist/_internal/server/features/offers/destinations.d.ts +40 -0
- package/dist/_internal/server/features/offers/destinations.js +65 -0
- package/dist/_internal/server/features/orders/actions.d.ts +8 -1
- package/dist/_internal/server/features/orders/actions.js +81 -8
- package/dist/_internal/server/features/orders/adapters.js +54 -0
- package/dist/_internal/server/features/orders/amend-delivery.d.ts +56 -0
- package/dist/_internal/server/features/orders/amend-delivery.js +124 -0
- package/dist/_internal/server/features/orders/balance.d.ts +89 -0
- package/dist/_internal/server/features/orders/balance.js +256 -0
- package/dist/_internal/server/features/orders/cancellation-refund.d.ts +15 -9
- package/dist/_internal/server/features/orders/cancellation-refund.js +15 -9
- package/dist/_internal/server/features/points/award.js +46 -4
- package/dist/_internal/server/features/pricing/actions.d.ts +58 -0
- package/dist/_internal/server/features/pricing/actions.js +144 -0
- package/dist/_internal/server/features/refunds/actions.js +26 -0
- package/dist/_internal/server/features/shipping/book-split.d.ts +86 -0
- package/dist/_internal/server/features/shipping/book-split.js +130 -0
- package/dist/_internal/server/features/shipping/book.d.ts +61 -0
- package/dist/_internal/server/features/shipping/book.js +176 -0
- package/dist/_internal/server/features/shipping/courier-block.d.ts +69 -0
- package/dist/_internal/server/features/shipping/courier-block.js +264 -0
- package/dist/_internal/server/features/site-settings/adapters.d.ts +2 -2
- package/dist/_internal/server/features/site-settings/adapters.js +29 -0
- package/dist/_internal/server/features/stores/adapters.d.ts +2 -2
- package/dist/_internal/server/features/stores/adapters.js +65 -0
- package/dist/_internal/server/functions/scheduled.d.ts +2 -1
- package/dist/_internal/server/functions/scheduled.js +95 -2
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +11 -2
- package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +4 -0
- package/dist/_internal/server/jobs/core/dailyStatusDigest.js +19 -2
- package/dist/_internal/server/jobs/core/index.d.ts +2 -1
- package/dist/_internal/server/jobs/core/index.js +2 -1
- package/dist/_internal/server/jobs/core/jobRunners.js +45 -0
- package/dist/_internal/server/jobs/core/onJobCreated.d.ts +30 -7
- package/dist/_internal/server/jobs/core/onJobCreated.js +78 -45
- package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +7 -0
- package/dist/_internal/server/jobs/core/onOrderStatusChange.js +65 -0
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +33 -0
- package/dist/_internal/server/jobs/core/reservationExpiry.d.ts +17 -0
- package/dist/_internal/server/jobs/core/reservationExpiry.js +40 -0
- package/dist/_internal/server/jobs/core/runJobOnce.d.ts +48 -0
- package/dist/_internal/server/jobs/core/runJobOnce.js +195 -0
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +25 -2
- 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/onOrderStatusChange.js +17 -0
- package/dist/_internal/server/jobs/handlers/reservationExpiry.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/reservationExpiry.js +2 -0
- package/dist/_internal/server/notifications/channel-health.d.ts +26 -3
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +296 -0
- package/dist/_internal/shared/checkout/coupon-discount.d.ts +43 -1
- package/dist/_internal/shared/checkout/coupon-discount.js +26 -1
- package/dist/_internal/shared/checkout/order-math.d.ts +174 -0
- package/dist/_internal/shared/checkout/order-math.js +159 -0
- package/dist/_internal/shared/checkout/refundable-base.d.ts +25 -0
- package/dist/_internal/shared/checkout/refundable-base.js +13 -1
- package/dist/_internal/shared/checkout/rules/index.d.ts +1 -1
- package/dist/_internal/shared/checkout/rules/index.js +1 -1
- package/dist/_internal/shared/checkout/rules/payment-gate.d.ts +18 -0
- package/dist/_internal/shared/checkout/rules/payment-gate.js +25 -0
- package/dist/_internal/shared/checkout/split.d.ts +75 -0
- package/dist/_internal/shared/checkout/split.js +120 -0
- package/dist/_internal/shared/content/disclaimers.d.ts +88 -0
- package/dist/_internal/shared/content/disclaimers.js +217 -0
- package/dist/_internal/shared/content/published-values.d.ts +93 -0
- package/dist/_internal/shared/content/published-values.js +206 -0
- package/dist/_internal/shared/features/admin/integrations.d.ts +139 -0
- package/dist/_internal/shared/features/admin/integrations.js +263 -0
- package/dist/_internal/shared/features/dashboard/hub-sections.d.ts +72 -0
- package/dist/_internal/shared/features/dashboard/hub-sections.js +75 -0
- package/dist/_internal/shared/features/emi/choice.d.ts +98 -0
- package/dist/_internal/shared/features/emi/choice.js +69 -0
- package/dist/_internal/shared/features/emi/group.d.ts +84 -0
- package/dist/_internal/shared/features/emi/group.js +126 -0
- package/dist/_internal/shared/features/ledger/posting.d.ts +107 -0
- package/dist/_internal/shared/features/ledger/posting.js +165 -0
- package/dist/_internal/shared/features/ledger/reports.d.ts +76 -0
- package/dist/_internal/shared/features/ledger/reports.js +127 -0
- package/dist/_internal/shared/features/ledger/rules.d.ts +170 -0
- package/dist/_internal/shared/features/ledger/rules.js +221 -0
- package/dist/_internal/shared/features/offers/coordination.d.ts +122 -0
- package/dist/_internal/shared/features/offers/coordination.js +80 -0
- package/dist/_internal/shared/features/orders/delivery-snapshot.d.ts +114 -0
- package/dist/_internal/shared/features/orders/delivery-snapshot.js +116 -0
- package/dist/_internal/shared/features/orders/dispute-reasons.d.ts +113 -0
- package/dist/_internal/shared/features/orders/dispute-reasons.js +181 -0
- package/dist/_internal/shared/features/orders/refund-copy.d.ts +1 -1
- package/dist/_internal/shared/features/orders/refund-copy.js +18 -1
- package/dist/_internal/shared/features/payouts/pay-period.d.ts +91 -0
- package/dist/_internal/shared/features/payouts/pay-period.js +104 -0
- package/dist/_internal/shared/features/points/accepts.d.ts +29 -0
- package/dist/_internal/shared/features/points/accepts.js +49 -0
- package/dist/_internal/shared/features/pricing/rate-registry.d.ts +150 -0
- package/dist/_internal/shared/features/pricing/rate-registry.js +246 -0
- package/dist/_internal/shared/features/shipping/parcel.d.ts +123 -0
- package/dist/_internal/shared/features/shipping/parcel.js +139 -0
- package/dist/_internal/shared/features/shipping/shared-purchase.d.ts +91 -0
- package/dist/_internal/shared/features/shipping/shared-purchase.js +108 -0
- package/dist/_internal/shared/features/site-settings/fees.d.ts +16 -8
- package/dist/_internal/shared/features/site-settings/fees.js +12 -8
- package/dist/_internal/shared/features/site-settings/types.d.ts +11 -0
- package/dist/_internal/shared/fees/calculator.d.ts +33 -1
- package/dist/_internal/shared/fees/calculator.js +32 -2
- package/dist/_internal/shared/fees/explain.d.ts +81 -0
- package/dist/_internal/shared/fees/explain.js +101 -0
- package/dist/_internal/shared/history/index.d.ts +1 -1
- package/dist/_internal/shared/history/index.js +1 -1
- package/dist/_internal/shared/history/types.d.ts +7 -1
- package/dist/_internal/shared/history/types.js +25 -0
- package/dist/client.d.ts +21 -0
- package/dist/client.js +43 -0
- package/dist/constants/api-endpoints.d.ts +114 -0
- package/dist/constants/api-endpoints.js +55 -0
- package/dist/constants/field-names.d.ts +42 -0
- package/dist/constants/field-names.js +52 -0
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/shipping.d.ts +45 -0
- package/dist/contracts/shipping.js +15 -0
- package/dist/features/about/components/AboutView.js +1 -1
- package/dist/features/account/components/BuyerAuctionsGuideView.js +4 -1
- package/dist/features/account/components/UserOrdersView.js +47 -35
- package/dist/features/admin/components/AdminCouponEditorView.js +17 -0
- package/dist/features/admin/components/AdminIntegrationsView.d.ts +55 -0
- package/dist/features/admin/components/AdminIntegrationsView.js +74 -0
- package/dist/features/admin/components/AdminInvoicingView.d.ts +2 -0
- package/dist/features/admin/components/AdminInvoicingView.js +65 -0
- package/dist/features/admin/components/AdminOrderEditorView.d.ts +33 -1
- package/dist/features/admin/components/AdminOrderEditorView.js +81 -3
- package/dist/features/admin/components/AdminOrdersGuideView.js +23 -2
- package/dist/features/admin/components/AdminOrdersView.js +86 -5
- package/dist/features/admin/components/OwnerQuickActions.js +24 -2
- package/dist/features/admin/constants/filter-tabs.d.ts +6 -0
- package/dist/features/admin/constants/filter-tabs.js +6 -0
- package/dist/features/admin/constants/site-settings-tabs.d.ts +1 -1
- package/dist/features/admin/hooks/useAdminListingData.js +16 -0
- package/dist/features/admin/schemas/admin-user-form.d.ts +2 -2
- package/dist/features/admin/schemas/firestore.d.ts +62 -0
- package/dist/features/admin/schemas/firestore.js +5 -0
- package/dist/features/ads/schemas/booking-write.d.ts +120 -0
- package/dist/features/ads/schemas/booking-write.js +75 -0
- package/dist/features/ads/schemas/creative-form.d.ts +18 -0
- package/dist/features/ads/schemas/creative-form.js +51 -0
- package/dist/features/ads/schemas/firestore.d.ts +140 -0
- package/dist/features/ads/schemas/firestore.js +109 -0
- package/dist/features/auctions/repository/bid.repository.js +3 -1
- package/dist/features/audit-log/schemas/firestore.d.ts +140 -0
- package/dist/features/audit-log/schemas/firestore.js +89 -0
- package/dist/features/auth/actions/profile-actions.d.ts +69 -1
- package/dist/features/auth/actions/profile-actions.js +62 -1
- package/dist/features/auth/permissions/constants.js +13 -0
- package/dist/features/auth/schemas/firestore.d.ts +16 -2
- package/dist/features/auth/schemas/firestore.js +2 -1
- package/dist/features/blog/actions/blog-actions.d.ts +6 -0
- package/dist/features/blog/actions/blog-actions.js +17 -2
- package/dist/features/blog/api/route.js +4 -1
- package/dist/features/blog/columns/index.js +5 -0
- package/dist/features/blog/components/BlogIndexPageView.js +2 -1
- package/dist/features/blog/repository/blog.repository.d.ts +4 -0
- package/dist/features/blog/repository/blog.repository.js +5 -0
- package/dist/features/blog/schemas/blog-form.d.ts +3 -0
- package/dist/features/blog/schemas/blog-form.js +1 -0
- package/dist/features/blog/schemas/firestore.d.ts +38 -1
- package/dist/features/blog/schemas/firestore.js +23 -0
- package/dist/features/blog/schemas/index.d.ts +6 -0
- package/dist/features/blog/schemas/index.js +2 -0
- package/dist/features/blog/types/index.d.ts +2 -0
- package/dist/features/cart/components/CartPriceBreakdown.d.ts +25 -1
- package/dist/features/cart/components/CartPriceBreakdown.js +29 -6
- package/dist/features/cart/components/PointsTenderPicker.d.ts +61 -0
- package/dist/features/cart/components/PointsTenderPicker.js +15 -0
- package/dist/features/cart/components/StoreAddonsPicker.js +2 -1
- package/dist/features/cart/components/index.d.ts +2 -0
- package/dist/features/cart/components/index.js +1 -0
- package/dist/features/cart/repository/cart.repository.d.ts +33 -0
- package/dist/features/cart/repository/cart.repository.js +47 -0
- package/dist/features/cart/schemas/firestore.d.ts +116 -0
- package/dist/features/contact/email.d.ts +27 -10
- package/dist/features/contact/email.js +20 -3
- package/dist/features/dashboard/components/DashboardHubView.d.ts +83 -0
- package/dist/features/dashboard/components/DashboardHubView.js +56 -0
- package/dist/features/dashboard/components/DashboardSectionsClient.d.ts +23 -0
- package/dist/features/dashboard/components/DashboardSectionsClient.js +66 -0
- package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.d.ts +55 -0
- package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.js +175 -0
- package/dist/features/dispute-reasons/repository/dispute-reasons.repository.d.ts +62 -0
- package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +108 -0
- package/dist/features/dispute-reasons/schemas/firestore.d.ts +62 -0
- package/dist/features/dispute-reasons/schemas/firestore.js +40 -0
- package/dist/features/events/components/EventSaleBanner.js +1 -1
- package/dist/features/faq/components/FAQAccordion.d.ts +13 -1
- package/dist/features/faq/components/FAQAccordion.js +29 -4
- package/dist/features/faq/components/FAQPageContent.js +2 -2
- package/dist/features/gateway-events/schemas/index.d.ts +2 -2
- package/dist/features/homepage/schemas/index.d.ts +8 -8
- package/dist/features/inventory/schemas/stock-reservation.d.ts +114 -0
- package/dist/features/inventory/schemas/stock-reservation.js +103 -0
- package/dist/features/invoicing/schemas/firestore.d.ts +112 -0
- package/dist/features/invoicing/schemas/firestore.js +45 -0
- package/dist/features/invoicing/utils/financial-year.d.ts +43 -0
- package/dist/features/invoicing/utils/financial-year.js +53 -0
- package/dist/features/jobs/actions/enqueue-job.js +18 -1
- package/dist/features/jobs/repository/jobs.repository.d.ts +62 -1
- package/dist/features/jobs/repository/jobs.repository.js +84 -1
- package/dist/features/jobs/schemas/firestore.d.ts +80 -0
- package/dist/features/jobs/schemas/firestore.js +37 -0
- package/dist/features/layout/AppLayoutShell.d.ts +9 -1
- package/dist/features/layout/AppLayoutShell.js +12 -4
- package/dist/features/layout/BottomActions.js +2 -1
- package/dist/features/layout/BottomNavbar.d.ts +10 -2
- package/dist/features/layout/SideActionDock.js +52 -2
- package/dist/features/layout/TitleBarLayout.js +13 -4
- package/dist/features/layout/TitleBarLayout.style.css +59 -0
- package/dist/features/layout/useCtaReveal.d.ts +16 -1
- package/dist/features/layout/useCtaReveal.js +17 -26
- package/dist/features/ledger/repository/journal.repository.d.ts +110 -0
- package/dist/features/ledger/repository/journal.repository.js +312 -0
- package/dist/features/ledger/schemas/expenses.d.ts +87 -0
- package/dist/features/ledger/schemas/expenses.js +27 -0
- package/dist/features/ledger/schemas/firestore.d.ts +183 -0
- package/dist/features/ledger/schemas/firestore.js +69 -0
- package/dist/features/lottery/schemas/zod.d.ts +6 -6
- package/dist/features/media/MediaImage.js +22 -1
- package/dist/features/media/upload/ImageUpload.js +1 -1
- package/dist/features/obligations/constants/policies.js +24 -1
- package/dist/features/orders/components/BalancePaymentPanel.d.ts +12 -0
- package/dist/features/orders/components/BalancePaymentPanel.js +94 -0
- package/dist/features/orders/components/OrderPaymentSummary.js +10 -1
- package/dist/features/orders/components/OrderProvenanceCard.d.ts +8 -0
- package/dist/features/orders/components/OrderProvenanceCard.js +185 -0
- package/dist/features/orders/components/RaiseDisputeForm.d.ts +14 -0
- package/dist/features/orders/components/RaiseDisputeForm.js +119 -0
- package/dist/features/orders/components/RefundRequestView.js +19 -1
- package/dist/features/orders/components/index.d.ts +4 -0
- package/dist/features/orders/components/index.js +2 -0
- package/dist/features/orders/constants/order-lane-tabs.d.ts +69 -0
- package/dist/features/orders/constants/order-lane-tabs.js +88 -0
- package/dist/features/orders/repository/orders.repository.d.ts +85 -1
- package/dist/features/orders/repository/orders.repository.js +205 -6
- package/dist/features/orders/schemas/balance.d.ts +40 -0
- package/dist/features/orders/schemas/balance.js +47 -0
- package/dist/features/orders/schemas/firestore.d.ts +398 -1
- package/dist/features/orders/schemas/firestore.js +43 -0
- package/dist/features/orders/schemas/index.d.ts +56 -8
- package/dist/features/orders/schemas/index.js +12 -0
- package/dist/features/orders/types/index.d.ts +47 -0
- package/dist/features/orders/utils/courier-block.d.ts +25 -0
- package/dist/features/orders/utils/courier-block.js +23 -0
- package/dist/features/payment-cases/schemas/firestore.d.ts +26 -1
- package/dist/features/payment-cases/schemas/firestore.js +1 -0
- package/dist/features/payments/repository/payout.repository.js +3 -2
- package/dist/features/payments/schemas/firestore.d.ts +92 -1
- package/dist/features/payments/schemas/firestore.js +23 -0
- package/dist/features/pricing/components/AdminPricingView.d.ts +13 -0
- package/dist/features/pricing/components/AdminPricingView.js +157 -0
- package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -2
- package/dist/features/products/components/ProductDetailPageView.d.ts +17 -1
- package/dist/features/products/components/ProductDetailPageView.js +2 -2
- package/dist/features/products/schemas/firestore.d.ts +26 -1
- package/dist/features/products/schemas/firestore.js +2 -0
- package/dist/features/products/schemas/index.d.ts +2 -2
- package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
- package/dist/features/promotions/actions/coupon-stacking.d.ts +30 -3
- package/dist/features/promotions/actions/coupon-stacking.js +21 -2
- package/dist/features/promotions/components/CouponCard.js +13 -0
- package/dist/features/promotions/constants/coupon-help.d.ts +2 -2
- package/dist/features/promotions/constants/coupon-help.js +9 -1
- package/dist/features/promotions/schemas/firestore.d.ts +24 -0
- package/dist/features/promotions/schemas/firestore.js +4 -0
- package/dist/features/promotions/types/index.d.ts +19 -1
- package/dist/features/reviews/repository/reviews.repository.d.ts +14 -0
- package/dist/features/reviews/repository/reviews.repository.js +16 -0
- package/dist/features/scams/components/ScamProfileView.js +2 -2
- package/dist/features/scams/components/ScamRegistryView.js +2 -2
- package/dist/features/scams/repository/scammer.repository.js +2 -1
- package/dist/features/scams/schemas/firestore.d.ts +2 -1
- package/dist/features/scams/schemas/firestore.js +2 -1
- package/dist/features/search/action-index/tab-entries.js +1 -1
- package/dist/features/seller/actions/offer-actions.js +56 -0
- package/dist/features/seller/actions/seller-actions.d.ts +1 -1
- package/dist/features/seller/actions/seller-actions.js +37 -3
- package/dist/features/seller/components/DispatchForm.d.ts +11 -0
- package/dist/features/seller/components/DispatchForm.js +190 -0
- package/dist/features/seller/components/SellerOrdersView.js +221 -27
- package/dist/features/seller/components/index.d.ts +1 -0
- package/dist/features/seller/components/index.js +1 -0
- package/dist/features/seller/hooks/useSellerListingData.js +25 -0
- package/dist/features/seller/repository/offer.repository.js +4 -3
- package/dist/features/seller/schemas/firestore.d.ts +23 -2
- package/dist/features/seller/schemas/firestore.js +3 -2
- package/dist/features/seller/schemas/index.d.ts +4 -4
- package/dist/features/seller/schemas/order-forms.d.ts +61 -4
- package/dist/features/seller/schemas/order-forms.js +41 -8
- package/dist/features/shipments/schemas/firestore.d.ts +25 -0
- package/dist/features/shipments/schemas/validation.d.ts +4 -4
- package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +82 -0
- package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +145 -0
- package/dist/features/shipping-boxes/schemas/firestore.d.ts +80 -0
- package/dist/features/shipping-boxes/schemas/firestore.js +53 -0
- package/dist/features/store-extensions/schemas/report-create-form.d.ts +2 -2
- package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +2 -2
- package/dist/features/stores/components/StoreOrdersGuideView.js +6 -1
- package/dist/features/stores/repository/store.repository.d.ts +18 -0
- package/dist/features/stores/repository/store.repository.js +27 -1
- package/dist/features/stores/schemas/firestore.d.ts +112 -0
- package/dist/features/stores/schemas/firestore.js +20 -0
- package/dist/features/stores/schemas/index.d.ts +6 -6
- package/dist/features/stores/types/index.d.ts +19 -0
- package/dist/features/support/repository/support.repository.js +72 -6
- package/dist/features/support/schemas/firestore.d.ts +17 -0
- package/dist/features/support/schemas/index.d.ts +8 -8
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +33 -1
- package/dist/next/routing/route-map.d.ts +112 -0
- package/dist/next/routing/route-map.js +63 -0
- package/dist/providers/shipping-shiprocket/index.d.ts +27 -1
- package/dist/providers/shipping-shiprocket/index.js +84 -1
- package/dist/react/contexts/SessionContext.d.ts +17 -1
- package/dist/schemas/registry.d.ts +69 -34
- package/dist/schemas/sieve.d.ts +2 -2
- package/dist/security/pii-schemas.d.ts +1 -1
- package/dist/security/pii-schemas.js +45 -0
- package/dist/server-entry.d.ts +2 -2
- package/dist/server-entry.js +8 -1
- package/dist/server.d.ts +36 -1
- package/dist/server.js +183 -1
- package/dist/styles.css +412 -80
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/themes/default-dark.js +1 -0
- package/dist/tokens/themes/default-light.js +1 -0
- package/dist/tokens/themes/required.d.ts +1 -1
- package/dist/tokens/themes/required.js +1 -0
- package/dist/tokens/tokens.css +15 -0
- package/dist/ui/components/Button.d.ts +10 -1
- package/dist/ui/components/Button.js +25 -3
- package/dist/ui/components/Chart.d.ts +63 -0
- package/dist/ui/components/Chart.js +169 -0
- package/dist/ui/components/Chart.style.css +214 -0
- package/dist/ui/components/Disclaimer.d.ts +19 -0
- package/dist/ui/components/Disclaimer.js +83 -0
- package/dist/ui/components/OverflowRow.js +54 -2
- package/dist/ui/components/OverflowRow.style.css +38 -0
- package/dist/ui/components/RichTextEditor.js +99 -8
- package/dist/ui/components/chart-spec.d.ts +68 -0
- package/dist/ui/components/chart-spec.js +140 -0
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/components/surface-tokens.d.ts +1 -1
- package/dist/ui/components/surface-tokens.js +9 -0
- package/dist/ui/components/tabs-overflow.js +64 -11
- package/dist/ui/components/toolbar-collapse.d.ts +13 -0
- package/dist/ui/components/toolbar-collapse.js +10 -1
- package/dist/ui/index.d.ts +8 -0
- package/dist/ui/index.js +9 -0
- package/dist/ui/rich-text/RichText.d.ts +10 -1
- package/dist/ui/rich-text/RichText.js +33 -2
- package/dist/ui/rich-text/RichText.style.css +83 -79
- package/dist/ui/rich-text/RichTextRenderer.d.ts +30 -1
- package/dist/ui/rich-text/RichTextRenderer.js +34 -2
- package/dist/ui/rich-text/chart-blocks.d.ts +62 -0
- package/dist/ui/rich-text/chart-blocks.js +87 -0
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +1834 -1822
|
@@ -33,10 +33,62 @@ export const PUBLIC_STORE_FIELDS = [
|
|
|
33
33
|
// which is the one outcome the tri-state rule forbids.
|
|
34
34
|
"supportsShippingProtection",
|
|
35
35
|
"supportsSaleProtection",
|
|
36
|
+
/*
|
|
37
|
+
* ── D46/D47 + S5a — three more public store flags, same argument ──────────
|
|
38
|
+
*
|
|
39
|
+
* PUBLIC for the tri-state reason the two protection flags above are: the
|
|
40
|
+
* buyer has to know whether this seller will split an order across
|
|
41
|
+
* addresses BEFORE they commit, and withholding it makes "has not opted in"
|
|
42
|
+
* indistinguishable from "we did not tell you".
|
|
43
|
+
*
|
|
44
|
+
* The two shared-purchase FEES are public for a stronger reason still: a
|
|
45
|
+
* coordinator commits to a quoted total, and a handling fee plus N−1
|
|
46
|
+
* shipping legs discovered after they committed is exactly the surprise the
|
|
47
|
+
* arrangement makes expensive.
|
|
48
|
+
*/
|
|
49
|
+
"allowSharedPurchase",
|
|
50
|
+
"sharedPurchaseHandlingFee",
|
|
51
|
+
"sharedPurchaseExtraShippingPerDestination",
|
|
36
52
|
"createdAt",
|
|
37
53
|
];
|
|
38
54
|
/** Everything else, with the reason it stays server-side. */
|
|
39
55
|
export const PRIVATE_STORE_FIELDS = [
|
|
56
|
+
/*
|
|
57
|
+
* 🛑 `trustedSeller` — OUR RISK POSTURE, not a badge.
|
|
58
|
+
*
|
|
59
|
+
* It means we pay this store before the parcel is delivered, so the platform
|
|
60
|
+
* carries a lost parcel and a non-delivery dispute. That is a statement
|
|
61
|
+
* about what we are willing to lose, and a buyer has no use for it: it says
|
|
62
|
+
* nothing about the goods, the seller's rating or their dispatch record.
|
|
63
|
+
*
|
|
64
|
+
* Published, it would read as an endorsement — "LetItRip trusts this store"
|
|
65
|
+
* — which is both a claim we never made and an incentive for sellers to
|
|
66
|
+
* compete for the flag. It is admin-granted on risk grounds and revocable;
|
|
67
|
+
* neither survives being visible.
|
|
68
|
+
*/
|
|
69
|
+
"trustedSeller",
|
|
70
|
+
/*
|
|
71
|
+
* 🛑 PRIVATE, and it was briefly classified PUBLIC on a mistaken argument
|
|
72
|
+
* worth recording so nobody re-makes it.
|
|
73
|
+
*
|
|
74
|
+
* The argument was "the cart has to show a buyer whether their points can be
|
|
75
|
+
* used HERE before they commit" — which is true, and is NOT a reason for
|
|
76
|
+
* this projection to carry the field. Measured: the cart reads
|
|
77
|
+
* `pricingPreview.stores[].acceptsPoints`, produced by the checkout pricing
|
|
78
|
+
* preview (checkout/actions.ts:2980), and the only client readers are
|
|
79
|
+
* `CartPriceBreakdown.tsx:317` and `CartRouteClient.tsx:1282`, both off that
|
|
80
|
+
* preview. Neither touches `toStoreListItem` / `toStoreDetail`.
|
|
81
|
+
*
|
|
82
|
+
* So the store projection has no reader, and the rule for this file is
|
|
83
|
+
* explicit: a field moves to PUBLIC only once you can NAME the client that
|
|
84
|
+
* reads it from this payload. "A different payload needs it" is not that.
|
|
85
|
+
*
|
|
86
|
+
* A store page could legitimately advertise "accepts points" one day. When a
|
|
87
|
+
* reader exists, move it and add it to both builders — the omission is what
|
|
88
|
+
* `audit-public-projection-parity` caught here (declared PUBLIC, emitted by
|
|
89
|
+
* nothing), which is the half-migrated state that reads as done.
|
|
90
|
+
*/
|
|
91
|
+
"acceptsPoints",
|
|
40
92
|
// Internal search index, not content. It is a normalized re-encoding of
|
|
41
93
|
// storeName/description/category — publishing it would leak nothing new, but
|
|
42
94
|
// it has no client reader and would bloat every store payload with a few
|
|
@@ -147,6 +199,19 @@ export function toStoreListItem(doc, _opts) {
|
|
|
147
199
|
*/
|
|
148
200
|
supportsShippingProtection: doc.supportsShippingProtection ?? false,
|
|
149
201
|
supportsSaleProtection: doc.supportsSaleProtection ?? false,
|
|
202
|
+
/*
|
|
203
|
+
* Same `?? false` rule for the same reason — a definite "not offered"
|
|
204
|
+
* rather than an `undefined` a truthiness test reads as neither.
|
|
205
|
+
*
|
|
206
|
+
* 🛑 The two FEES are passed through as `undefined` when unset, and that
|
|
207
|
+
* asymmetry is deliberate: a flag's absence means "no", but a fee's
|
|
208
|
+
* absence means "not configured", which is NOT the same as ₹0. Defaulting
|
|
209
|
+
* them to zero would quote a coordinator a free arrangement from a store
|
|
210
|
+
* that simply has not set a price.
|
|
211
|
+
*/
|
|
212
|
+
allowSharedPurchase: doc.allowSharedPurchase ?? false,
|
|
213
|
+
sharedPurchaseHandlingFee: doc.sharedPurchaseHandlingFee,
|
|
214
|
+
sharedPurchaseExtraShippingPerDestination: doc.sharedPurchaseExtraShippingPerDestination,
|
|
150
215
|
createdAt: toIsoString(doc.createdAt),
|
|
151
216
|
};
|
|
152
217
|
}
|
|
@@ -5,6 +5,7 @@ export declare const hardBanReinstatement: import("./types").ScheduledFunctionDe
|
|
|
5
5
|
export declare const paymentReviewAutoApprove: import("./types").ScheduledFunctionDefinition;
|
|
6
6
|
export declare const couponExpiry: import("./types").ScheduledFunctionDefinition;
|
|
7
7
|
export declare const offerExpiry: import("./types").ScheduledFunctionDefinition;
|
|
8
|
+
export declare const reservationExpiry: import("./types").ScheduledFunctionDefinition;
|
|
8
9
|
export declare const productStatsSync: import("./types").ScheduledFunctionDefinition;
|
|
9
10
|
export declare const revenueRollup: import("./types").ScheduledFunctionDefinition;
|
|
10
11
|
export declare const searchIndexBuild: import("./types").ScheduledFunctionDefinition;
|
|
@@ -99,4 +100,4 @@ export declare const prizeDrawExpiryReveal: import("./types").ScheduledFunctionD
|
|
|
99
100
|
export declare const bundleStockSync: import("./types").ScheduledFunctionDefinition;
|
|
100
101
|
export declare const emiInstallmentReminder: import("./types").ScheduledFunctionDefinition;
|
|
101
102
|
export declare const catalogueImageStalenessReminder: import("./types").ScheduledFunctionDefinition;
|
|
102
|
-
export declare const SCHEDULED_FUNCTIONS: readonly [import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition];
|
|
103
|
+
export declare const SCHEDULED_FUNCTIONS: readonly [import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Scheduled function definitions (Cloud Scheduler triggers).
|
|
2
2
|
// Cron syntax accepts both Firebase shorthand and standard cron expressions.
|
|
3
|
-
import { auctionSettlementHandler, autoPayoutEligibilityHandler, bundleStockSyncHandler, cartPruneHandler, cleanupRtdbEventsHandler, countersReconcileHandler, boxRollupReconcileHandler, couponExpiryHandler, dailyDataCleanupHandler, draftPruneHandler, emiInstallmentReminderHandler, catalogueImageStalenessReminderHandler, mediaTmpCleanupHandler, notificationPruneHandler, offerExpiryHandler, obligationSweepHandler, paymentReconcileHandler, paymentWindowTimeoutHandler, hardBanReinstatementHandler, paymentReviewAutoApproveHandler, pendingOrderTimeoutHandler, positionsReconcileHandler, prizeDrawExpiryRevealHandler, productStatsSyncHandler, revenueRollupHandler, priceIndexRollupHandler, marketPriceRollupHandler, searchIndexBuildHandler, analyticsAlertEvaluateHandler, pageViewRollupHandler, pageViewPruneHandler, dailyStatusDigestHandler, weeklyPayoutEligibilityHandler, testDataCleanupHandler, } from "../jobs/handlers/index.js";
|
|
3
|
+
import { auctionSettlementHandler, autoPayoutEligibilityHandler, bundleStockSyncHandler, cartPruneHandler, cleanupRtdbEventsHandler, countersReconcileHandler, boxRollupReconcileHandler, couponExpiryHandler, dailyDataCleanupHandler, draftPruneHandler, emiInstallmentReminderHandler, catalogueImageStalenessReminderHandler, mediaTmpCleanupHandler, notificationPruneHandler, offerExpiryHandler, reservationExpiryHandler, obligationSweepHandler, paymentReconcileHandler, paymentWindowTimeoutHandler, hardBanReinstatementHandler, paymentReviewAutoApproveHandler, pendingOrderTimeoutHandler, positionsReconcileHandler, prizeDrawExpiryRevealHandler, productStatsSyncHandler, revenueRollupHandler, priceIndexRollupHandler, marketPriceRollupHandler, searchIndexBuildHandler, analyticsAlertEvaluateHandler, pageViewRollupHandler, pageViewPruneHandler, dailyStatusDigestHandler, weeklyPayoutEligibilityHandler, testDataCleanupHandler, } from "../jobs/handlers/index.js";
|
|
4
4
|
import { defineFunction } from "./define.js";
|
|
5
5
|
const REGION = "asia-south1";
|
|
6
6
|
const EVERY_5_MIN = "every 5 minutes";
|
|
@@ -18,9 +18,80 @@ export const pendingOrderTimeout = defineFunction({
|
|
|
18
18
|
handler: pendingOrderTimeoutHandler,
|
|
19
19
|
options: { region: REGION, timeoutSeconds: 120, memory: "256MiB", maxInstances: 1 },
|
|
20
20
|
});
|
|
21
|
+
/*
|
|
22
|
+
* ── 🛑 THE "RETIRE THREE SWEEPS" PLAN STEP IS SELF-CONTRADICTORY ────────────
|
|
23
|
+
*
|
|
24
|
+
* Phase 8's plan text reads: *"a retry is a new document … picked up by a
|
|
25
|
+
* sweep folded into `paymentWindowTimeout` … **Then** retire the three
|
|
26
|
+
* superseded sweeps"* — and names those three as `pendingOrderTimeout`,
|
|
27
|
+
* **`paymentWindowTimeout`** and `paymentReviewAutoApprove`.
|
|
28
|
+
*
|
|
29
|
+
* It cannot be both. `paymentWindowTimeout` is now the HOST of the job-retry
|
|
30
|
+
* sweep (see `runDueJobRetries`, called first in its handler, before the early
|
|
31
|
+
* return), so retiring it would delete the only mechanism that runs a
|
|
32
|
+
* scheduled retry — and the create trigger deliberately defers to that sweep,
|
|
33
|
+
* because creating the retry document fires the trigger instantly and would
|
|
34
|
+
* collapse the backoff to zero. Deleting the host leaves every retry
|
|
35
|
+
* permanently `pending`: a stall that reads as a queue, which is the exact
|
|
36
|
+
* failure Phase 8 exists to remove.
|
|
37
|
+
*
|
|
38
|
+
* So the retry/DLQ half is built and the retirement is NOT done. That is a
|
|
39
|
+
* deliberate stop, not an omission:
|
|
40
|
+
*
|
|
41
|
+
* - These three cancel unpaid orders and restock real inventory. Deleting
|
|
42
|
+
* them on the strength of a plan line, in a session that cannot run a
|
|
43
|
+
* single check, is the autonomous destructive decision Rule #1 forbids.
|
|
44
|
+
* - The claim that obligations supersede them needs reading all three
|
|
45
|
+
* against the obligation policies AND confirming `obligationSweep`'s `act`
|
|
46
|
+
* tier covers each — and the sweep's own comment records that its acting
|
|
47
|
+
* branch was narrowed precisely because monetary outcomes were not safe to
|
|
48
|
+
* automate. That narrowing was widened for six kinds; whether it covers
|
|
49
|
+
* these three is a measurement, not an inference.
|
|
50
|
+
* - The Scheduler arithmetic in the plan (34 → 31) is therefore unchanged by
|
|
51
|
+
* this commit. Nothing was added either, so the count stands wherever it
|
|
52
|
+
* actually is — and CLAUDE.md says to recount rather than quote it, this
|
|
53
|
+
* number having drifted six times.
|
|
54
|
+
*
|
|
55
|
+
* Whoever takes the retirement: pick a different host for the retry sweep
|
|
56
|
+
* first, or keep `paymentWindowTimeout` and retire only the other two.
|
|
57
|
+
*
|
|
58
|
+
* ── 🛑 MEASURED 2026-10-23: RETIRE NEITHER OF THE OTHER TWO EITHER ─────────
|
|
59
|
+
*
|
|
60
|
+
* The paragraph above asks for a measurement rather than an inference —
|
|
61
|
+
* *"whether [`obligationSweep`'s act tier] covers these three is a
|
|
62
|
+
* measurement"* — so here it is. **It does not cover either of them, and the
|
|
63
|
+
* answer is a single predicate.**
|
|
64
|
+
*
|
|
65
|
+
* `OBLIGATION_POLICIES["seller-accept"]` is the only candidate for
|
|
66
|
+
* `pendingOrderTimeout`, and its precondition opens with:
|
|
67
|
+
*
|
|
68
|
+
* precondition: (o) => isPaid(o) && notTerminal(o) && !o.acceptedAt && …
|
|
69
|
+
* duration: { value: 3, unit: "days" }
|
|
70
|
+
*
|
|
71
|
+
* with `const isPaid = (o) => o.paymentStatus === "paid"`. A pending COD order
|
|
72
|
+
* is **not paid** — that is the entire point of COD — so the policy EXCLUDES
|
|
73
|
+
* exactly the orders this sweep exists for, and waits three days where the
|
|
74
|
+
* sweep acts at 24 hours. Pairwise disjoint, which is the same failure shape
|
|
75
|
+
* the obligations collection was built to end (Root Cause #27: three sweeps,
|
|
76
|
+
* pairwise disjoint, an order holding stock forever).
|
|
77
|
+
*
|
|
78
|
+
* `paymentReviewAutoApprove` has no candidate at all. The ONLY policy
|
|
79
|
+
* mentioning `paymentReviewOutcome` is `proof-reupload`, keyed on
|
|
80
|
+
* `=== "reupload_requested"` — the BUYER re-uploading, not staff failing to
|
|
81
|
+
* review. Nothing in the twelve kinds means "a proof nobody looked at".
|
|
82
|
+
*
|
|
83
|
+
* **Retiring them would therefore**: leave unpaid COD orders holding real
|
|
84
|
+
* stock forever, and leave an uploaded proof nobody reviewed in limbo forever.
|
|
85
|
+
* Both silent — an order that is never cancelled raises nothing.
|
|
86
|
+
*
|
|
87
|
+
* So the correct state is THREE sweeps kept, not one. The plan's "retire the
|
|
88
|
+
* other two" was an inference from a coverage claim that measurement does not
|
|
89
|
+
* support, and this note exists so the next reader does not re-derive it from
|
|
90
|
+
* the same plan line.
|
|
91
|
+
*/
|
|
21
92
|
export const paymentWindowTimeout = defineFunction({
|
|
22
93
|
name: "paymentWindowTimeout",
|
|
23
|
-
description: "Cancel + restock orders whose 15-minute manual-payment window expired without proof.",
|
|
94
|
+
description: "Cancel + restock orders whose 15-minute manual-payment window expired without proof. Also hosts R1 (stuck gateway events) and the Phase 8 job-retry sweep, both before its early return.",
|
|
24
95
|
trigger: { kind: "schedule", cron: EVERY_5_MIN },
|
|
25
96
|
handler: paymentWindowTimeoutHandler,
|
|
26
97
|
options: { region: REGION, timeoutSeconds: 120, memory: "256MiB", maxInstances: 1 },
|
|
@@ -53,6 +124,27 @@ export const offerExpiry = defineFunction({
|
|
|
53
124
|
handler: offerExpiryHandler,
|
|
54
125
|
options: { region: REGION, timeoutSeconds: 120, memory: "256MiB", maxInstances: 1 },
|
|
55
126
|
});
|
|
127
|
+
/*
|
|
128
|
+
* 🛑 The ONLY bound on a leaked stock hold.
|
|
129
|
+
*
|
|
130
|
+
* Every other way a reservation ends is an action somebody takes; this is
|
|
131
|
+
* what happens when nobody does. Without it a hold is permanent, and a
|
|
132
|
+
* permanent hold is indistinguishable from stock the seller no longer has —
|
|
133
|
+
* with no error anywhere, because a lower `availableQuantity` is just a
|
|
134
|
+
* number.
|
|
135
|
+
*
|
|
136
|
+
* Every 15 minutes rather than daily: the default hold is 72 hours, so the
|
|
137
|
+
* cadence is not about precision, it is about the WORST case. A daily sweep
|
|
138
|
+
* means "expired at 09:01, buyable again tomorrow", and a buyer told an item
|
|
139
|
+
* is sold out for a day after the hold lapsed is the failure this prevents.
|
|
140
|
+
*/
|
|
141
|
+
export const reservationExpiry = defineFunction({
|
|
142
|
+
name: "reservationExpiry",
|
|
143
|
+
description: "Expire stock reservations past their TTL and restore availableQuantity (every 15 minutes).",
|
|
144
|
+
trigger: { kind: "schedule", cron: "every 15 minutes" },
|
|
145
|
+
handler: reservationExpiryHandler,
|
|
146
|
+
options: { region: REGION, timeoutSeconds: 300, memory: "256MiB", maxInstances: 1 },
|
|
147
|
+
});
|
|
56
148
|
export const productStatsSync = defineFunction({
|
|
57
149
|
name: "productStatsSync",
|
|
58
150
|
description: "Recompute aggregated product statistics (daily 01:00 UTC).",
|
|
@@ -407,6 +499,7 @@ export const SCHEDULED_FUNCTIONS = [
|
|
|
407
499
|
paymentReviewAutoApprove,
|
|
408
500
|
couponExpiry,
|
|
409
501
|
offerExpiry,
|
|
502
|
+
reservationExpiry,
|
|
410
503
|
productStatsSync,
|
|
411
504
|
revenueRollup,
|
|
412
505
|
priceIndexRollup,
|
|
@@ -3,7 +3,7 @@ import { getDefaultCurrency } from "../../../../core/index.js";
|
|
|
3
3
|
import { computePayoutDeduction } from "../../../shared/fees/calculator.js";
|
|
4
4
|
import { payoutCutoff } from "../../../shared/fees/payout-hold.js";
|
|
5
5
|
import { PROTECTION_CLAIM_WINDOW_HOURS } from "../../../shared/features/shipping/protections.js";
|
|
6
|
-
import { ordersTaxableValue, ordersShippingReimbursement, } from "../../../shared/checkout/order-math.js";
|
|
6
|
+
import { ordersTaxableValue, ordersShippingReimbursement, ordersPointsBorneByStore, } from "../../../shared/checkout/order-math.js";
|
|
7
7
|
import { BATCH_LIMIT } from "../handlers/messages.js";
|
|
8
8
|
/*
|
|
9
9
|
* 🛑 `AUTO_PAYOUT_WINDOW_DAYS = 7` and `getBusinessDayCutoff` were REMOVED on
|
|
@@ -120,8 +120,17 @@ export async function runAutoPayoutEligibility(ctx) {
|
|
|
120
120
|
* `{ id, data }` wrappers, and passing the wrapper would read every field
|
|
121
121
|
* as undefined and silently reimburse zero.
|
|
122
122
|
*/
|
|
123
|
-
ordersShippingReimbursement(orders.map((o) => o.data))
|
|
123
|
+
ordersShippingReimbursement(orders.map((o) => o.data)),
|
|
124
|
+
/*
|
|
125
|
+
* D46/D47 — the store-funded points, both ends (decisions 3 and 17).
|
|
126
|
+
* Same `.data` unwrap for the same reason, and the same position outside
|
|
127
|
+
* the fee bases: a commission on a discount the seller funded is not a
|
|
128
|
+
* commission on a sale.
|
|
129
|
+
*/
|
|
130
|
+
ordersPointsBorneByStore(orders.map((o) => o.data)));
|
|
124
131
|
const payoutInput = {
|
|
132
|
+
/* Every row this job writes settles a SELLER liability. */
|
|
133
|
+
payeeType: "store",
|
|
125
134
|
storeId,
|
|
126
135
|
// `displayName` is plaintext by D1; the email fallback must be revealed.
|
|
127
136
|
sellerName: (seller.displayName ?? sellerPlain.email ?? sellerId),
|
|
@@ -58,6 +58,10 @@ export interface DailyStatusDigestSummary {
|
|
|
58
58
|
nearestCaseDueAt: Date | null;
|
|
59
59
|
/** Gateway events that never reached a terminal status — the inbox backlog. */
|
|
60
60
|
unprocessedGatewayEventCount: number;
|
|
61
|
+
courierWalletBalance: number | null;
|
|
62
|
+
courierWalletLow: boolean;
|
|
63
|
+
/** Why the balance is null, when it is. Printed instead of a figure. */
|
|
64
|
+
courierWalletNote?: string;
|
|
61
65
|
}
|
|
62
66
|
/** Gathers the last-24h numbers. Exported so the admin manual-trigger route can reuse it. */
|
|
63
67
|
export declare function collectDailyStatusSummary(): Promise<DailyStatusDigestSummary>;
|
|
@@ -16,6 +16,8 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
16
16
|
import { Fragment } from "react";
|
|
17
17
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
18
18
|
import { getAdminDb } from "../../../../providers/db-firebase/index.js";
|
|
19
|
+
import { getProviders } from "../../../../contracts/registry.js";
|
|
20
|
+
import { checkCourierWalletAndAlert } from "../../features/shipping/courier-block.js";
|
|
19
21
|
import { sendEmail } from "../../../../features/contact/email.js";
|
|
20
22
|
import { siteSettingsRepository } from "../../../../features/admin/repository/site-settings.repository.js";
|
|
21
23
|
import { ORDER_COLLECTION, OrderStatusValues } from "../../../../features/orders/schemas/firestore.js";
|
|
@@ -163,6 +165,14 @@ export async function collectDailyStatusSummary() {
|
|
|
163
165
|
* nearest-deadline query below swallows and returns null rather than
|
|
164
166
|
* throwing.
|
|
165
167
|
*/
|
|
168
|
+
/*
|
|
169
|
+
* The courier wallet. Same spirit as `safeCount` above — it can never fail
|
|
170
|
+
* the digest, and it ALERTS at most once per unresolved episode rather than
|
|
171
|
+
* once per day (the case's `openOrObserve` is the dedupe). `getProviders()`
|
|
172
|
+
* always populates `shipping`, and the base class's `getWalletBalance`
|
|
173
|
+
* default answers "no wallet" for manual, so this needs no capability check.
|
|
174
|
+
*/
|
|
175
|
+
const courierWallet = await checkCourierWalletAndAlert(getProviders().shipping);
|
|
166
176
|
const [paymentCasesError, paymentCasesWarning, paymentCasesInfo, unprocessedGatewayEventCount] = await Promise.all([
|
|
167
177
|
safeCount(db
|
|
168
178
|
.collection(PAYMENT_CASES_COLLECTION)
|
|
@@ -245,6 +255,9 @@ export async function collectDailyStatusSummary() {
|
|
|
245
255
|
paymentCasesInfo,
|
|
246
256
|
nearestCaseDueAt,
|
|
247
257
|
unprocessedGatewayEventCount,
|
|
258
|
+
courierWalletBalance: courierWallet.balance,
|
|
259
|
+
courierWalletLow: courierWallet.low,
|
|
260
|
+
...(courierWallet.reason ? { courierWalletNote: courierWallet.reason } : {}),
|
|
248
261
|
};
|
|
249
262
|
}
|
|
250
263
|
function renderDigestHtml(summary, siteName, version) {
|
|
@@ -254,7 +267,9 @@ function renderDigestHtml(summary, siteName, version) {
|
|
|
254
267
|
? `Deployment ${version} is live. Activity for the last 24 hours.`
|
|
255
268
|
: "Activity for the last 24 hours.", _jsx("br", {}), _jsx("br", {}), _jsx(StatLine, { label: "Orders placed", value: summary.orderCount }), _jsx(StatLine, { label: "Revenue (excl. cancelled/refunded)", value: currency(summary.revenue) }), _jsx(StatLine, { label: "Active listings", value: summary.activeListingCount }), _jsx(StatLine, { label: "Pending > 24h (needs attention)", value: summary.stuckPendingCount })] }), _jsx(EmailDivider, {}), _jsxs(EmailRow, { children: [_jsx(EmailBold, { children: "Orders by status" }), _jsx("br", {}), _jsx("br", {}), statusRows.length === 0 ? ("No orders in this window.") : (statusRows.map(([status, count]) => (_jsx(StatLine, { label: status, value: count }, status))))] }), _jsx(EmailDivider, {}), _jsxs(EmailRow, { children: [_jsx(EmailBold, { children: "Inbox" }), _jsx("br", {}), _jsx("br", {}), _jsx(StatLine, { label: "New contact messages", value: summary.contactTotal }), _jsx(StatLine, { label: "New support tickets", value: summary.newTicketCount }), _jsx(StatLine, { label: "Tickets open / in progress / waiting on user", value: `${summary.ticketsOpen} / ${summary.ticketsInProgress} / ${summary.ticketsWaitingOnUser}` }), _jsx(StatLine, { label: "Unread admin notifications", value: summary.unreadAdminNotificationCount })] }), summary.contactMessages.length > 0 ? (_jsxs(EmailRow, { children: [_jsx(EmailBold, { children: "Contact messages" }), _jsx("br", {}), _jsx("br", {}), summary.contactMessages.map((m) => (_jsxs(Fragment, { children: [_jsx(EmailBold, { children: m.subject }), _jsx("br", {}), m.name, " \u2014", " ", _jsxs(EmailLink, { href: `mailto:${m.email}?subject=${encodeURIComponent(`Re: ${m.subject}`)}`, children: ["Reply to ", m.email] }), _jsx("br", {}), m.excerpt, _jsx("br", {}), _jsx("br", {})] }, m.id))), summary.contactTotal > summary.contactMessages.length
|
|
256
269
|
? `…and ${summary.contactTotal - summary.contactMessages.length} more in /admin/contact.`
|
|
257
|
-
: null] })) : null, _jsx(EmailDivider, {}), _jsxs(EmailRow, { children: [_jsx(EmailBold, { children: "Needs attention" }), _jsx("br", {}), _jsx("br", {}), _jsx(StatLine, { label: "Manual payments pending", value: summary.manualPaymentPendingCount }), _jsx(StatLine, { label: "New scam reports", value: summary.newScamReportCount }), _jsx(StatLine, { label: "New newsletter subscribers", value: summary.newSubscriberCount }), _jsx(StatLine, { label: "Admin actions logged", value: summary.adminActionCount })] }), _jsx(EmailDivider, {}), _jsxs(EmailRow, { children: [_jsx(EmailBold, { children: "Money at risk" }), _jsx("br", {}), _jsx("br", {}), _jsx(StatLine, { label: "Open cases \u2014 urgent", value: summary.paymentCasesError }), _jsx(StatLine, { label: "Open cases \u2014 warning", value: summary.paymentCasesWarning }), _jsx(StatLine, { label: "Open cases \u2014 info", value: summary.paymentCasesInfo }), _jsx(StatLine, { label: "Nearest external deadline", value: summary.nearestCaseDueAt ? summary.nearestCaseDueAt.toISOString() : "none" }), _jsx(StatLine, { label: "Unfinished gateway events", value: summary.unprocessedGatewayEventCount })
|
|
270
|
+
: null] })) : null, _jsx(EmailDivider, {}), _jsxs(EmailRow, { children: [_jsx(EmailBold, { children: "Needs attention" }), _jsx("br", {}), _jsx("br", {}), _jsx(StatLine, { label: "Manual payments pending", value: summary.manualPaymentPendingCount }), _jsx(StatLine, { label: "New scam reports", value: summary.newScamReportCount }), _jsx(StatLine, { label: "New newsletter subscribers", value: summary.newSubscriberCount }), _jsx(StatLine, { label: "Admin actions logged", value: summary.adminActionCount })] }), _jsx(EmailDivider, {}), _jsxs(EmailRow, { children: [_jsx(EmailBold, { children: "Money at risk" }), _jsx("br", {}), _jsx("br", {}), _jsx(StatLine, { label: "Open cases \u2014 urgent", value: summary.paymentCasesError }), _jsx(StatLine, { label: "Open cases \u2014 warning", value: summary.paymentCasesWarning }), _jsx(StatLine, { label: "Open cases \u2014 info", value: summary.paymentCasesInfo }), _jsx(StatLine, { label: "Nearest external deadline", value: summary.nearestCaseDueAt ? summary.nearestCaseDueAt.toISOString() : "none" }), _jsx(StatLine, { label: "Unfinished gateway events", value: summary.unprocessedGatewayEventCount }), _jsx(StatLine, { label: "Courier wallet", value: summary.courierWalletBalance === null
|
|
271
|
+
? `unknown (${summary.courierWalletNote ?? "not read"})`
|
|
272
|
+
: `${currency(summary.courierWalletBalance)}${summary.courierWalletLow ? " — LOW, top up" : ""}` })] }), _jsx(EmailFooter, { copyright: `© ${new Date().getFullYear()}${siteName ? ` ${siteName}` : ""}. All rights reserved.` })] }) }))}`;
|
|
258
273
|
}
|
|
259
274
|
/**
|
|
260
275
|
* The plain-text twin of `renderDigestHtml`.
|
|
@@ -288,7 +303,9 @@ function renderDigestText(summary, version) {
|
|
|
288
303
|
* the same unconditional rendering, because a mail client showing the text
|
|
289
304
|
* part must not get a digest missing the one block with a deadline in it.
|
|
290
305
|
*/
|
|
291
|
-
lines.push("", "MONEY AT RISK", `Open cases — urgent: ${summary.paymentCasesError}`, `Open cases — warning: ${summary.paymentCasesWarning}`, `Open cases — info: ${summary.paymentCasesInfo}`, `Nearest external deadline: ${summary.nearestCaseDueAt ? summary.nearestCaseDueAt.toISOString() : "none"}`, `Unfinished gateway events: ${summary.unprocessedGatewayEventCount}`
|
|
306
|
+
lines.push("", "MONEY AT RISK", `Open cases — urgent: ${summary.paymentCasesError}`, `Open cases — warning: ${summary.paymentCasesWarning}`, `Open cases — info: ${summary.paymentCasesInfo}`, `Nearest external deadline: ${summary.nearestCaseDueAt ? summary.nearestCaseDueAt.toISOString() : "none"}`, `Unfinished gateway events: ${summary.unprocessedGatewayEventCount}`, `Courier wallet: ${summary.courierWalletBalance === null
|
|
307
|
+
? `unknown (${summary.courierWalletNote ?? "not read"})`
|
|
308
|
+
: `${currency(summary.courierWalletBalance)}${summary.courierWalletLow ? " — LOW, top up" : ""}`}`);
|
|
292
309
|
return lines.join("\n");
|
|
293
310
|
}
|
|
294
311
|
const DEPLOY_MARKER_COLLECTION = "system";
|
|
@@ -46,4 +46,5 @@ export { handleProductStockChange, type HandleProductStockChangeInput, syncBundl
|
|
|
46
46
|
export { handleReviewWrite, type HandleReviewWriteInput } from "./onReviewWrite";
|
|
47
47
|
export { handleCategoryWrite, type HandleCategoryWriteInput, type CategoryDoc, } from "./onCategoryWrite";
|
|
48
48
|
export { handleStoreWrite, type HandleStoreWriteInput, type StoreDoc, } from "./onStoreWrite";
|
|
49
|
-
export { handleJobCreated, type HandleJobCreatedInput } from "./onJobCreated";
|
|
49
|
+
export { handleJobCreated, runDueJobRetries, type HandleJobCreatedInput, } from "./onJobCreated";
|
|
50
|
+
export { runJobOnce, type RunJobOnceInput } from "./runJobOnce";
|
|
@@ -49,4 +49,5 @@ export { handleProductStockChange, syncBundlesForProduct, syncGroupedListingsFor
|
|
|
49
49
|
export { handleReviewWrite } from "./onReviewWrite.js";
|
|
50
50
|
export { handleCategoryWrite, } from "./onCategoryWrite.js";
|
|
51
51
|
export { handleStoreWrite, } from "./onStoreWrite.js";
|
|
52
|
-
export { handleJobCreated } from "./onJobCreated.js";
|
|
52
|
+
export { handleJobCreated, runDueJobRetries, } from "./onJobCreated.js";
|
|
53
|
+
export { runJobOnce } from "./runJobOnce.js";
|
|
@@ -14,6 +14,7 @@ import { runResetOtpVerification } from "./resetOtpVerification.js";
|
|
|
14
14
|
import { runNewsletterExport } from "./newsletterExport.js";
|
|
15
15
|
import { runStoreNameBackfill } from "./storeNameBackfill.js";
|
|
16
16
|
import { runCategoryTaxonomyBackfill } from "./categoryTaxonomyBackfill.js";
|
|
17
|
+
import { bookShipment } from "../../features/shipping/book.js";
|
|
17
18
|
async function runPayoutsWeeklyJob(_payload, ctx) {
|
|
18
19
|
const { payoutsCreated, ordersProcessed, payoutIds } = await runWeeklyPayoutEligibility(ctx);
|
|
19
20
|
return {
|
|
@@ -56,7 +57,51 @@ async function runCategoryTaxonomyBackfillJob(payload, ctx) {
|
|
|
56
57
|
const kind = payload.kind === "rename" ? "rename" : "reparent";
|
|
57
58
|
return runCategoryTaxonomyBackfill({ categoryId: String(payload.categoryId ?? ""), kind }, ctx);
|
|
58
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Book a parcel through the platform's carrier account.
|
|
62
|
+
*
|
|
63
|
+
* 🛑 A JOB rather than a synchronous route, and the reason is Rule #6. A
|
|
64
|
+
* booking is an outbound carrier call on a path the seller is waiting on,
|
|
65
|
+
* against a 10s sync-function ceiling — and a timeout there is the worst
|
|
66
|
+
* outcome available: the carrier may have accepted, so retrying double-books
|
|
67
|
+
* and not retrying leaves a parcel nobody collects.
|
|
68
|
+
*
|
|
69
|
+
* The runner's own failure is recoverable: `bookShipment` refuses an order
|
|
70
|
+
* that already has a tracking number, so a retried job is a no-op rather
|
|
71
|
+
* than a second parcel.
|
|
72
|
+
*/
|
|
73
|
+
async function runBookShipmentJob(payload, _ctx) {
|
|
74
|
+
const dims = (payload.dimensions ?? {});
|
|
75
|
+
const result = await bookShipment({
|
|
76
|
+
orderId: String(payload.orderId ?? ""),
|
|
77
|
+
pickupAddressId: String(payload.pickupAddressId ?? ""),
|
|
78
|
+
dimensions: {
|
|
79
|
+
weight: Number(dims.weight ?? 0),
|
|
80
|
+
length: Number(dims.length ?? 0),
|
|
81
|
+
width: Number(dims.width ?? 0),
|
|
82
|
+
height: Number(dims.height ?? 0),
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
const orderId = String(payload.orderId ?? "");
|
|
86
|
+
return {
|
|
87
|
+
summary: { total: 1, succeeded: 1, skipped: 0, failed: 0 },
|
|
88
|
+
succeeded: [orderId],
|
|
89
|
+
skipped: [],
|
|
90
|
+
failed: [],
|
|
91
|
+
/*
|
|
92
|
+
* The tracking id goes in `data`, which `useBulkEvent` surfaces — the
|
|
93
|
+
* seller's next question after "did it book" is "what is the number",
|
|
94
|
+
* and making them reload the order to find it is a wasted round trip.
|
|
95
|
+
*/
|
|
96
|
+
data: {
|
|
97
|
+
trackingId: result.trackingId,
|
|
98
|
+
...(result.courier ? { courier: result.courier } : {}),
|
|
99
|
+
...(result.estimatedDelivery ? { estimatedDelivery: result.estimatedDelivery } : {}),
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
59
103
|
export const JOB_RUNNERS = {
|
|
104
|
+
bookShipment: runBookShipmentJob,
|
|
60
105
|
payoutsWeekly: runPayoutsWeeklyJob,
|
|
61
106
|
hardBanCascade: runHardBanCascadeJob,
|
|
62
107
|
resetOtpVerification: runResetOtpVerificationJob,
|
|
@@ -5,13 +5,11 @@
|
|
|
5
5
|
* (CLAUDE.md Rule #6 — Vercel routes never do heavy work; they write a
|
|
6
6
|
* `jobs` doc and return, this Function does the actual processing).
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* `useBulkEvent` — every RTDB write is its own try/catch so a ping
|
|
14
|
-
* failure never masks the real Firestore result.
|
|
8
|
+
* ## 🛑 It no longer runs the job itself — `runJobOnce` does (Phase 8)
|
|
9
|
+
*
|
|
10
|
+
* The attempt, the failure classification and the retry scheduling all live in
|
|
11
|
+
* `runJobOnce`, which the retry sweep also calls. One dispatch path for both
|
|
12
|
+
* entry points, or the two drift the first time either learns something.
|
|
15
13
|
*/
|
|
16
14
|
import type { JobDocument } from "../../../../features/jobs/schemas/firestore";
|
|
17
15
|
import type { JobContext } from "../runtime/types";
|
|
@@ -20,3 +18,28 @@ export interface HandleJobCreatedInput {
|
|
|
20
18
|
job: JobDocument;
|
|
21
19
|
}
|
|
22
20
|
export declare function handleJobCreated(input: HandleJobCreatedInput, ctx: JobContext): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* The retry sweep — runs every scheduled attempt whose time has come.
|
|
23
|
+
*
|
|
24
|
+
* ## 🛑 Folded into an EXISTING scheduled function, not a new one
|
|
25
|
+
*
|
|
26
|
+
* It is called from `paymentWindowTimeout` (every 5 minutes). Cloud Scheduler
|
|
27
|
+
* bills per registered job and this project already carries ~34 of them
|
|
28
|
+
* against 3 free; adding a 35th to run a usually-empty query is the cost this
|
|
29
|
+
* plan explicitly refuses. The same reasoning put the ledger reconciler inside
|
|
30
|
+
* `dailyDataCleanup` and R1 inside this same function.
|
|
31
|
+
*
|
|
32
|
+
* 🛑 And it is placed FIRST at its call site, before that function's early
|
|
33
|
+
* return — `paymentWindowTimeout` returns as soon as there are no expired
|
|
34
|
+
* payment windows, which is most ticks, so appending would run the sweep only
|
|
35
|
+
* on the minority of ticks with unrelated work (Root Cause #27).
|
|
36
|
+
*
|
|
37
|
+
* Sequential, not `Promise.all`. A job runner is heavy by definition — that is
|
|
38
|
+
* why it is a job — and twenty of them concurrently inside one 540s function
|
|
39
|
+
* is how a sweep exhausts its own budget. Twenty sequential attempts that run
|
|
40
|
+
* out of time simply leave the rest `pending` for the next tick, which is the
|
|
41
|
+
* safe failure.
|
|
42
|
+
*/
|
|
43
|
+
export declare function runDueJobRetries(ctx: JobContext): Promise<{
|
|
44
|
+
ran: number;
|
|
45
|
+
}>;
|
|
@@ -5,55 +5,88 @@
|
|
|
5
5
|
* (CLAUDE.md Rule #6 — Vercel routes never do heavy work; they write a
|
|
6
6
|
* `jobs` doc and return, this Function does the actual processing).
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* `useBulkEvent` — every RTDB write is its own try/catch so a ping
|
|
14
|
-
* failure never masks the real Firestore result.
|
|
8
|
+
* ## 🛑 It no longer runs the job itself — `runJobOnce` does (Phase 8)
|
|
9
|
+
*
|
|
10
|
+
* The attempt, the failure classification and the retry scheduling all live in
|
|
11
|
+
* `runJobOnce`, which the retry sweep also calls. One dispatch path for both
|
|
12
|
+
* entry points, or the two drift the first time either learns something.
|
|
15
13
|
*/
|
|
16
|
-
import { normalizeError } from "../../../../errors/normalize.js";
|
|
17
14
|
import { jobsRepository } from "../../../../repositories/index.js";
|
|
18
|
-
import {
|
|
19
|
-
import { RTDB_PATHS } from "../../../../providers/db-firebase/rtdb-paths.js";
|
|
20
|
-
import { JOB_RUNNERS } from "./jobRunners.js";
|
|
21
|
-
async function pingBulkEvent(jobId, payload, ctx) {
|
|
22
|
-
try {
|
|
23
|
-
await getAdminRealtimeDb()
|
|
24
|
-
.ref(`${RTDB_PATHS.BULK_EVENTS}/${jobId}`)
|
|
25
|
-
.set({ ...payload, updatedAt: Date.now() });
|
|
26
|
-
}
|
|
27
|
-
catch (err) {
|
|
28
|
-
void normalizeError(err);
|
|
29
|
-
ctx.logger.warn("onJobCreated: RTDB ping failed (non-fatal)", {
|
|
30
|
-
jobId,
|
|
31
|
-
error: err instanceof Error ? err.message : String(err),
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
}
|
|
15
|
+
import { runJobOnce } from "./runJobOnce.js";
|
|
35
16
|
export async function handleJobCreated(input, ctx) {
|
|
36
17
|
const { jobId, job } = input;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
18
|
+
/*
|
|
19
|
+
* ── 🛑 A SCHEDULED RETRY MUST NOT RUN HERE ────────────────────────────────
|
|
20
|
+
*
|
|
21
|
+
* This guard is the whole reason Phase 8's backoff works, and its absence
|
|
22
|
+
* was a defect in this feature's own first draft.
|
|
23
|
+
*
|
|
24
|
+
* A retry is a NEW `jobs` document (it has to be — this trigger fires on
|
|
25
|
+
* CREATE, so flipping a job back to `pending` would never wake anything).
|
|
26
|
+
* But creating that document fires THIS trigger immediately — so without
|
|
27
|
+
* this check, every retry would run the instant it was scheduled, the
|
|
28
|
+
* 5-minute and 30-minute backoffs would be zero, and three attempts would
|
|
29
|
+
* burn in under a second against a fault that had not had time to clear.
|
|
30
|
+
* The backoff fields would still be written, so the database would read as
|
|
31
|
+
* though it had waited.
|
|
32
|
+
*
|
|
33
|
+
* `nextAttemptAt` in the future means "the sweep owns this one". Returning
|
|
34
|
+
* leaves the document `pending`, which is exactly what `getDueRetries`
|
|
35
|
+
* queries for.
|
|
36
|
+
*/
|
|
37
|
+
if (job.nextAttemptAt) {
|
|
38
|
+
const due = new Date(job.nextAttemptAt).getTime();
|
|
39
|
+
if (Number.isFinite(due) && due > Date.now()) {
|
|
40
|
+
ctx.logger.info("onJobCreated: retry scheduled, deferring to the sweep", {
|
|
41
|
+
jobId,
|
|
42
|
+
jobType: job.jobType,
|
|
43
|
+
attempt: job.attempt ?? 1,
|
|
44
|
+
nextAttemptAt: new Date(due).toISOString(),
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
51
48
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
49
|
+
await runJobOnce({ jobId, job }, ctx);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The retry sweep — runs every scheduled attempt whose time has come.
|
|
53
|
+
*
|
|
54
|
+
* ## 🛑 Folded into an EXISTING scheduled function, not a new one
|
|
55
|
+
*
|
|
56
|
+
* It is called from `paymentWindowTimeout` (every 5 minutes). Cloud Scheduler
|
|
57
|
+
* bills per registered job and this project already carries ~34 of them
|
|
58
|
+
* against 3 free; adding a 35th to run a usually-empty query is the cost this
|
|
59
|
+
* plan explicitly refuses. The same reasoning put the ledger reconciler inside
|
|
60
|
+
* `dailyDataCleanup` and R1 inside this same function.
|
|
61
|
+
*
|
|
62
|
+
* 🛑 And it is placed FIRST at its call site, before that function's early
|
|
63
|
+
* return — `paymentWindowTimeout` returns as soon as there are no expired
|
|
64
|
+
* payment windows, which is most ticks, so appending would run the sweep only
|
|
65
|
+
* on the minority of ticks with unrelated work (Root Cause #27).
|
|
66
|
+
*
|
|
67
|
+
* Sequential, not `Promise.all`. A job runner is heavy by definition — that is
|
|
68
|
+
* why it is a job — and twenty of them concurrently inside one 540s function
|
|
69
|
+
* is how a sweep exhausts its own budget. Twenty sequential attempts that run
|
|
70
|
+
* out of time simply leave the rest `pending` for the next tick, which is the
|
|
71
|
+
* safe failure.
|
|
72
|
+
*/
|
|
73
|
+
export async function runDueJobRetries(ctx) {
|
|
74
|
+
const due = await jobsRepository.getDueRetries();
|
|
75
|
+
if (due.length === 0)
|
|
76
|
+
return { ran: 0 };
|
|
77
|
+
ctx.logger.info("job retries: running due attempts", { count: due.length });
|
|
78
|
+
let ran = 0;
|
|
79
|
+
for (const { id, data } of due) {
|
|
80
|
+
/*
|
|
81
|
+
* `runJobOnce` never throws — it records every outcome on the job itself.
|
|
82
|
+
* The `.catch` is belt-and-braces so one pathological job cannot abort the
|
|
83
|
+
* rest of the sweep, which would leave the remainder un-retried with no
|
|
84
|
+
* record of why.
|
|
85
|
+
*/
|
|
86
|
+
await runJobOnce({ jobId: id, job: data }, ctx).catch((err) => {
|
|
87
|
+
ctx.logger.error("job retries: attempt threw", err, { jobId: id });
|
|
88
|
+
});
|
|
89
|
+
ran += 1;
|
|
58
90
|
}
|
|
91
|
+
return { ran };
|
|
59
92
|
}
|
|
@@ -6,9 +6,16 @@ export type OrderAfter = {
|
|
|
6
6
|
userEmail: string;
|
|
7
7
|
productTitle: string;
|
|
8
8
|
trackingNumber?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Phase 7 — the invoice is numbered on the PAYMENT transition, which is a
|
|
11
|
+
* different axis from `status` and is why these two fields are here.
|
|
12
|
+
*/
|
|
13
|
+
paymentStatus?: string;
|
|
14
|
+
invoiceNumber?: string;
|
|
9
15
|
};
|
|
10
16
|
export type OrderBefore = {
|
|
11
17
|
status: OrderStatus;
|
|
18
|
+
paymentStatus?: string;
|
|
12
19
|
};
|
|
13
20
|
export interface HandleOrderStatusChangeInput {
|
|
14
21
|
orderId: string;
|