@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
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
import { normalizeError } from "../../../../errors/normalize.js";
|
|
1
2
|
import { decryptPii } from "../../../../security/index.js";
|
|
2
3
|
import { sendNotification } from "../../../../features/admin/actions/notification-actions.js";
|
|
4
|
+
import { orderRepository } from "../../../../features/orders/repository/orders.repository.js";
|
|
5
|
+
/* Phase 7 — numbering only; the PDF renderer lives in `functions/` because
|
|
6
|
+
* `pdfkit` must not enter appkit's module graph. */
|
|
7
|
+
import { allocateSeriesNumber } from "../../features/invoicing/allocate.js";
|
|
3
8
|
import { ORDER_MESSAGES } from "../handlers/messages.js";
|
|
4
9
|
const STATUS_CONFIG = {
|
|
5
10
|
confirmed: {
|
|
@@ -31,6 +36,66 @@ export async function handleOrderStatusChange(input, ctx) {
|
|
|
31
36
|
const { orderId, before, after } = input;
|
|
32
37
|
if (!before || !after)
|
|
33
38
|
return;
|
|
39
|
+
/*
|
|
40
|
+
* ── PHASE 7: number the invoice on the PAYMENT transition ─────────────────
|
|
41
|
+
*
|
|
42
|
+
* 🛑 PLACED FIRST, before the `status`-unchanged early return. That return
|
|
43
|
+
* is correct for the notification work below it, and it is also why this
|
|
44
|
+
* cannot go after it: `paymentStatus: pending → paid` frequently happens
|
|
45
|
+
* with NO change to `status` at all (a COD order verified by an admin, a
|
|
46
|
+
* manual proof approved), so appending this below would have numbered only
|
|
47
|
+
* the minority of paid orders whose status happened to move in the same
|
|
48
|
+
* write. That is Root Cause #27's sweep-coverage trap, and R1 in
|
|
49
|
+
* `paymentWindowTimeout` had to be placed the same way for the same reason.
|
|
50
|
+
*
|
|
51
|
+
* 🛑 Extending THIS trigger rather than adding a second one on `orders`.
|
|
52
|
+
* A second Firestore trigger on the same collection wakes for every write
|
|
53
|
+
* and returns — which is how `onShipmentDeleted` burned a million
|
|
54
|
+
* invocations a day sitting correctly-guarded beside a neighbour's loop
|
|
55
|
+
* (Root Cause #92). The sticker-trace definitions carry the same 🛑.
|
|
56
|
+
*
|
|
57
|
+
* 🛑 And it is numbering ONLY — no PDF. `pdfkit` is a Functions dependency
|
|
58
|
+
* and must not enter appkit's module graph; the bytes are rendered once,
|
|
59
|
+
* lazily, by the serve route and stored from there. Numbering is the half
|
|
60
|
+
* that must be immediate and transactional, because it is the half that
|
|
61
|
+
* cannot be corrected: a number cannot be withdrawn without leaving a gap.
|
|
62
|
+
*/
|
|
63
|
+
const becamePaid = before.paymentStatus !== "paid" && after.paymentStatus === "paid";
|
|
64
|
+
if (becamePaid && !after.invoiceNumber) {
|
|
65
|
+
try {
|
|
66
|
+
const allocation = await allocateSeriesNumber({
|
|
67
|
+
kind: "invoice",
|
|
68
|
+
subjectId: orderId,
|
|
69
|
+
orderId,
|
|
70
|
+
});
|
|
71
|
+
/*
|
|
72
|
+
* Only writes when THIS call allocated. A re-entry that found an
|
|
73
|
+
* existing claim skips the write entirely, which keeps a re-fired
|
|
74
|
+
* trigger from touching the order at all — the write that would
|
|
75
|
+
* otherwise re-fire this very trigger.
|
|
76
|
+
*/
|
|
77
|
+
if (allocation.allocated) {
|
|
78
|
+
await orderRepository.update(orderId, {
|
|
79
|
+
invoiceNumber: allocation.number,
|
|
80
|
+
invoiceIssuedAt: new Date(),
|
|
81
|
+
});
|
|
82
|
+
ctx.logger.info(`Invoice ${allocation.number} allocated`, { orderId });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
/*
|
|
87
|
+
* Logged and swallowed, so a numbering failure cannot suppress the
|
|
88
|
+
* buyer's order notification below. The order stays un-invoiced, which
|
|
89
|
+
* is a visible, retryable state — the next write that touches
|
|
90
|
+
* `paymentStatus` re-attempts, and the serve route can allocate on
|
|
91
|
+
* demand. Throwing here would also make the Firestore trigger retry the
|
|
92
|
+
* whole handler, re-sending the notification.
|
|
93
|
+
*/
|
|
94
|
+
ctx.logger.error("Invoice allocation failed", normalizeError(error), {
|
|
95
|
+
orderId,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
34
99
|
if (before.status === after.status)
|
|
35
100
|
return;
|
|
36
101
|
const newStatus = after.status;
|
|
@@ -6,6 +6,9 @@ import { OrderStatusValues } from "../../../../features/orders/schemas/firestore
|
|
|
6
6
|
import { PAYMENT_WINDOW_EXPIRED_REASON } from "../../../../features/orders/constants/payment-window.js";
|
|
7
7
|
import { ORDER_MESSAGES } from "../handlers/messages.js";
|
|
8
8
|
import { reconcileStuckGatewayEvents } from "./paymentReconcile.js";
|
|
9
|
+
/* Phase 8 — the job retry sweep. Folded in here rather than given its own
|
|
10
|
+
* Scheduler job; see its own docstring and the 🛑 at the call site. */
|
|
11
|
+
import { runDueJobRetries } from "./onJobCreated.js";
|
|
9
12
|
/**
|
|
10
13
|
* 15-minute payment window sweep — cancels + restocks any `upi_manual`/
|
|
11
14
|
* `cash`/`emi` order still `pending`/`pending` with no proof uploaded past
|
|
@@ -53,6 +56,36 @@ export async function runPaymentWindowTimeout(ctx) {
|
|
|
53
56
|
err,
|
|
54
57
|
});
|
|
55
58
|
}
|
|
59
|
+
/*
|
|
60
|
+
* ── Phase 8 rides along too: job retries whose backoff has elapsed ────────
|
|
61
|
+
*
|
|
62
|
+
* Same two reasons as R1 above, and the same placement rule: **FIRST, before
|
|
63
|
+
* the early return**. A retry is a `pending` job document with a future
|
|
64
|
+
* `nextAttemptAt`, and nothing else in the system will ever run it — the
|
|
65
|
+
* create trigger deliberately defers to this sweep, because creating the
|
|
66
|
+
* retry document fires that trigger instantly and would collapse the backoff
|
|
67
|
+
* to zero.
|
|
68
|
+
*
|
|
69
|
+
* So if this call sat after the early return, a retry would run only on a
|
|
70
|
+
* tick that happened to have an expired payment window — which is the
|
|
71
|
+
* minority — and a job scheduled on a quiet afternoon could wait hours.
|
|
72
|
+
* Root Cause #27 again, now with a second mechanism depending on it.
|
|
73
|
+
*
|
|
74
|
+
* Never throws, for the same reason R1 does not: a stuck job must not stop
|
|
75
|
+
* the payment-window cancellations this function exists for.
|
|
76
|
+
*/
|
|
77
|
+
try {
|
|
78
|
+
const retries = await runDueJobRetries(ctx);
|
|
79
|
+
if (retries.ran > 0) {
|
|
80
|
+
ctx.logger.info("Phase 8: ran due job retries", { ...retries });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
void normalizeError(err);
|
|
85
|
+
ctx.logger.error("job retry sweep failed inside paymentWindowTimeout — continuing", {
|
|
86
|
+
err,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
56
89
|
ctx.logger.info("Scanning orders past their 15-minute payment deadline");
|
|
57
90
|
const expired = await orderRepository.getExpiredPaymentDeadlines();
|
|
58
91
|
if (expired.length === 0) {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { JobContext } from "../runtime/types";
|
|
2
|
+
/**
|
|
3
|
+
* Return expired stock holds to the shelf.
|
|
4
|
+
*
|
|
5
|
+
* 🛑 This sweep is the ONLY thing that bounds a leaked hold. Everything else
|
|
6
|
+
* that ends a reservation is an action somebody takes — a release, a
|
|
7
|
+
* consume — and the entire reason `expiresAt` exists is that nobody takes
|
|
8
|
+
* those when they close the tab. Without this job a hold is permanent, and a
|
|
9
|
+
* permanent hold is indistinguishable from stock the seller no longer has.
|
|
10
|
+
*
|
|
11
|
+
* 🛑 Runs every 15 minutes, which is the real resolution of a TTL. The
|
|
12
|
+
* default hold is 72 hours, so the cadence does not need to be tight — but a
|
|
13
|
+
* daily sweep would make the worst case "expires at 09:01, returns tomorrow",
|
|
14
|
+
* and a buyer told an item is sold out for 24 hours after it stopped being
|
|
15
|
+
* held is the failure this exists to prevent.
|
|
16
|
+
*/
|
|
17
|
+
export declare function runReservationExpiry(ctx: JobContext): Promise<void>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { expireDueReservations } from "../../features/inventory/reservations.js";
|
|
2
|
+
import { normalizeError } from "../../../../errors/normalize.js";
|
|
3
|
+
/**
|
|
4
|
+
* Return expired stock holds to the shelf.
|
|
5
|
+
*
|
|
6
|
+
* 🛑 This sweep is the ONLY thing that bounds a leaked hold. Everything else
|
|
7
|
+
* that ends a reservation is an action somebody takes — a release, a
|
|
8
|
+
* consume — and the entire reason `expiresAt` exists is that nobody takes
|
|
9
|
+
* those when they close the tab. Without this job a hold is permanent, and a
|
|
10
|
+
* permanent hold is indistinguishable from stock the seller no longer has.
|
|
11
|
+
*
|
|
12
|
+
* 🛑 Runs every 15 minutes, which is the real resolution of a TTL. The
|
|
13
|
+
* default hold is 72 hours, so the cadence does not need to be tight — but a
|
|
14
|
+
* daily sweep would make the worst case "expires at 09:01, returns tomorrow",
|
|
15
|
+
* and a buyer told an item is sold out for 24 hours after it stopped being
|
|
16
|
+
* held is the failure this exists to prevent.
|
|
17
|
+
*/
|
|
18
|
+
export async function runReservationExpiry(ctx) {
|
|
19
|
+
ctx.logger.info("Starting stock-reservation expiry sweep");
|
|
20
|
+
let result;
|
|
21
|
+
try {
|
|
22
|
+
result = await expireDueReservations();
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
const normalized = normalizeError(err);
|
|
26
|
+
ctx.logger.error("Reservation expiry sweep failed", { message: normalized.message });
|
|
27
|
+
throw err;
|
|
28
|
+
}
|
|
29
|
+
/*
|
|
30
|
+
* `truncated` is logged rather than swallowed, and is the signal that the
|
|
31
|
+
* bound was reached: a run that hits its cap left real holds in place, and
|
|
32
|
+
* reading "expired 200" as "all done" is the same mistake a truncated
|
|
33
|
+
* recount invites (Root Cause #102).
|
|
34
|
+
*/
|
|
35
|
+
ctx.logger.info("Reservation expiry sweep complete", {
|
|
36
|
+
scanned: result.scanned,
|
|
37
|
+
expired: result.expired,
|
|
38
|
+
truncated: result.truncated,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run ONE attempt of a job, and decide what happens next (Phase 8).
|
|
3
|
+
*
|
|
4
|
+
* ## 🛑 ONE dispatch path, shared by the trigger and the retry sweep
|
|
5
|
+
*
|
|
6
|
+
* `handleJobCreated` is the `jobs/{jobId}` create trigger; `runDueJobRetries`
|
|
7
|
+
* is the sweep that picks up scheduled retries. Both call THIS, so there is
|
|
8
|
+
* exactly one place that knows how to run a job, classify its failure and
|
|
9
|
+
* schedule the next attempt. Two dispatch paths would drift the first time one
|
|
10
|
+
* of them learned something — which is the shape of every defect in this
|
|
11
|
+
* plan's § "Recurrent Root Cause Patterns" that begins "a fix applied to the
|
|
12
|
+
* route but never back-ported".
|
|
13
|
+
*
|
|
14
|
+
* ## 🛑 A retry is a NEW DOCUMENT
|
|
15
|
+
*
|
|
16
|
+
* `onJobCreated` fires on CREATE. Re-running a job by flipping its status back
|
|
17
|
+
* to `pending` would never wake the trigger, so the job would sit `pending`
|
|
18
|
+
* forever looking scheduled — a stall that reads as "queued". So a retry is a
|
|
19
|
+
* fresh `jobs` document carrying `attempt + 1`, `parentJobId` and a
|
|
20
|
+
* `nextAttemptAt`, and the sweep is what runs it when due.
|
|
21
|
+
*
|
|
22
|
+
* ## What is retryable
|
|
23
|
+
*
|
|
24
|
+
* `mapToHttpError(err).status >= 500`. Reused rather than invented: that
|
|
25
|
+
* function already encodes "is this our fault or the caller's" for every error
|
|
26
|
+
* class in the codebase, and a second classification would answer differently
|
|
27
|
+
* the first time either changed.
|
|
28
|
+
*
|
|
29
|
+
* A 4xx is terminal IMMEDIATELY — no backoff, no attempts burned. A validation
|
|
30
|
+
* failure does not become valid by waiting, and retrying it three times over
|
|
31
|
+
* 35 minutes just delays the case by 35 minutes.
|
|
32
|
+
*/
|
|
33
|
+
import { type JobDocument } from "../../../../features/jobs/schemas/firestore";
|
|
34
|
+
import type { JobContext } from "../runtime/types";
|
|
35
|
+
export interface RunJobOnceInput {
|
|
36
|
+
jobId: string;
|
|
37
|
+
job: JobDocument;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Run one attempt. Marks the job done, schedules a retry, or declares it dead.
|
|
41
|
+
*
|
|
42
|
+
* Never throws: a job's failure is recorded on the job, and letting it escape
|
|
43
|
+
* would make the Firebase trigger retry the whole handler — a second,
|
|
44
|
+
* uncontrolled retry mechanism layered under this one, which is precisely the
|
|
45
|
+
* doubling that cost 55 sessions in Root Cause #91 (`run.mjs`'s internal retry
|
|
46
|
+
* multiplying the pool's).
|
|
47
|
+
*/
|
|
48
|
+
export declare function runJobOnce(input: RunJobOnceInput, ctx: JobContext): Promise<void>;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run ONE attempt of a job, and decide what happens next (Phase 8).
|
|
3
|
+
*
|
|
4
|
+
* ## 🛑 ONE dispatch path, shared by the trigger and the retry sweep
|
|
5
|
+
*
|
|
6
|
+
* `handleJobCreated` is the `jobs/{jobId}` create trigger; `runDueJobRetries`
|
|
7
|
+
* is the sweep that picks up scheduled retries. Both call THIS, so there is
|
|
8
|
+
* exactly one place that knows how to run a job, classify its failure and
|
|
9
|
+
* schedule the next attempt. Two dispatch paths would drift the first time one
|
|
10
|
+
* of them learned something — which is the shape of every defect in this
|
|
11
|
+
* plan's § "Recurrent Root Cause Patterns" that begins "a fix applied to the
|
|
12
|
+
* route but never back-ported".
|
|
13
|
+
*
|
|
14
|
+
* ## 🛑 A retry is a NEW DOCUMENT
|
|
15
|
+
*
|
|
16
|
+
* `onJobCreated` fires on CREATE. Re-running a job by flipping its status back
|
|
17
|
+
* to `pending` would never wake the trigger, so the job would sit `pending`
|
|
18
|
+
* forever looking scheduled — a stall that reads as "queued". So a retry is a
|
|
19
|
+
* fresh `jobs` document carrying `attempt + 1`, `parentJobId` and a
|
|
20
|
+
* `nextAttemptAt`, and the sweep is what runs it when due.
|
|
21
|
+
*
|
|
22
|
+
* ## What is retryable
|
|
23
|
+
*
|
|
24
|
+
* `mapToHttpError(err).status >= 500`. Reused rather than invented: that
|
|
25
|
+
* function already encodes "is this our fault or the caller's" for every error
|
|
26
|
+
* class in the codebase, and a second classification would answer differently
|
|
27
|
+
* the first time either changed.
|
|
28
|
+
*
|
|
29
|
+
* A 4xx is terminal IMMEDIATELY — no backoff, no attempts burned. A validation
|
|
30
|
+
* failure does not become valid by waiting, and retrying it three times over
|
|
31
|
+
* 35 minutes just delays the case by 35 minutes.
|
|
32
|
+
*/
|
|
33
|
+
import { normalizeError } from "../../../../errors/normalize.js";
|
|
34
|
+
import { mapToHttpError } from "../../../../errors/error-mapping.js";
|
|
35
|
+
import { jobsRepository } from "../../../../repositories/index.js";
|
|
36
|
+
import { paymentCasesRepository } from "../../../../features/payment-cases/repository/payment-cases.repository.js";
|
|
37
|
+
import { getAdminRealtimeDb } from "../../../../providers/db-firebase/index.js";
|
|
38
|
+
import { RTDB_PATHS } from "../../../../providers/db-firebase/rtdb-paths.js";
|
|
39
|
+
import { JOB_BACKOFF_MS, JOB_MAX_ATTEMPTS, } from "../../../../features/jobs/schemas/firestore.js";
|
|
40
|
+
import { JOB_RUNNERS } from "./jobRunners.js";
|
|
41
|
+
async function pingBulkEvent(jobId, payload, ctx) {
|
|
42
|
+
try {
|
|
43
|
+
await getAdminRealtimeDb()
|
|
44
|
+
.ref(`${RTDB_PATHS.BULK_EVENTS}/${jobId}`)
|
|
45
|
+
.set({ ...payload, updatedAt: Date.now() });
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
void normalizeError(err);
|
|
49
|
+
ctx.logger.warn("runJobOnce: RTDB ping failed (non-fatal)", {
|
|
50
|
+
jobId,
|
|
51
|
+
error: err instanceof Error ? err.message : String(err),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The delay before the next attempt.
|
|
57
|
+
*
|
|
58
|
+
* 🛑 Clamped to the LAST element rather than indexing past the end. An
|
|
59
|
+
* out-of-range index yields `undefined`, `new Date(Date.now() + undefined)` is
|
|
60
|
+
* `Invalid Date`, and the sweep's `nextAttemptAt <= now` comparison is false
|
|
61
|
+
* for an invalid date forever — so the retry would be scheduled and never run.
|
|
62
|
+
* A stall that looks like a queue is the exact failure this phase removes.
|
|
63
|
+
*/
|
|
64
|
+
function backoffFor(attempt) {
|
|
65
|
+
const idx = Math.max(0, Math.min(attempt - 1, JOB_BACKOFF_MS.length - 1));
|
|
66
|
+
return JOB_BACKOFF_MS[idx];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Run one attempt. Marks the job done, schedules a retry, or declares it dead.
|
|
70
|
+
*
|
|
71
|
+
* Never throws: a job's failure is recorded on the job, and letting it escape
|
|
72
|
+
* would make the Firebase trigger retry the whole handler — a second,
|
|
73
|
+
* uncontrolled retry mechanism layered under this one, which is precisely the
|
|
74
|
+
* doubling that cost 55 sessions in Root Cause #91 (`run.mjs`'s internal retry
|
|
75
|
+
* multiplying the pool's).
|
|
76
|
+
*/
|
|
77
|
+
export async function runJobOnce(input, ctx) {
|
|
78
|
+
const { jobId, job } = input;
|
|
79
|
+
const attempt = job.attempt ?? 1;
|
|
80
|
+
const maxAttempts = job.maxAttempts ?? JOB_MAX_ATTEMPTS;
|
|
81
|
+
await jobsRepository.markProcessing(jobId);
|
|
82
|
+
const runner = JOB_RUNNERS[job.jobType];
|
|
83
|
+
if (!runner) {
|
|
84
|
+
/*
|
|
85
|
+
* 🛑 Terminal immediately, and DEAD rather than failed. An unregistered
|
|
86
|
+
* jobType cannot become registered by waiting, and the thing that would
|
|
87
|
+
* fix it is a deploy — so this is the clearest possible case for a human,
|
|
88
|
+
* not a retry. It is also the failure mode of the seed-loader registry
|
|
89
|
+
* (Root Cause #90): a missing map entry whose symptom is silence.
|
|
90
|
+
*/
|
|
91
|
+
const error = `No runner registered for jobType: ${job.jobType}`;
|
|
92
|
+
await jobsRepository.markDead(jobId, error);
|
|
93
|
+
await pingBulkEvent(jobId, { status: "failed", action: job.jobType, error }, ctx);
|
|
94
|
+
await openDeadJobCase(jobId, job, error, ctx);
|
|
95
|
+
ctx.logger.error("runJobOnce: unregistered jobType", null, { jobId, jobType: job.jobType });
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const result = await runner(job.payload, ctx);
|
|
100
|
+
await jobsRepository.markDone(jobId, result);
|
|
101
|
+
await pingBulkEvent(jobId, { status: "success", action: job.jobType, ...result }, ctx);
|
|
102
|
+
ctx.logger.info("runJobOnce: job complete", {
|
|
103
|
+
jobId,
|
|
104
|
+
jobType: job.jobType,
|
|
105
|
+
attempt,
|
|
106
|
+
summary: result.summary,
|
|
107
|
+
});
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
void normalizeError(err);
|
|
112
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
113
|
+
/*
|
|
114
|
+
* 🛑 `mapToHttpError` rather than a bespoke test. It already decides "ours
|
|
115
|
+
* or theirs" for every error class here, so a 5xx is a transient fault
|
|
116
|
+
* worth retrying and a 4xx is a permanent one that will fail identically
|
|
117
|
+
* in five minutes.
|
|
118
|
+
*/
|
|
119
|
+
const status = mapToHttpError(err).status;
|
|
120
|
+
const retryable = status >= 500;
|
|
121
|
+
const attemptsLeft = attempt < maxAttempts;
|
|
122
|
+
if (retryable && attemptsLeft) {
|
|
123
|
+
const nextAttemptAt = new Date(Date.now() + backoffFor(attempt));
|
|
124
|
+
/*
|
|
125
|
+
* 🛑 The NEW document is created BEFORE this one is marked failed. The
|
|
126
|
+
* reverse ordering leaves a window where the job reads as failed with no
|
|
127
|
+
* retry scheduled — and if the process dies in that window, nothing
|
|
128
|
+
* retries and nothing says so. This way the worst case is a scheduled
|
|
129
|
+
* retry whose parent still reads `processing`, which the sweep handles
|
|
130
|
+
* and a human can see.
|
|
131
|
+
*/
|
|
132
|
+
const retryId = await jobsRepository.createRetry(jobId, job, {
|
|
133
|
+
attempt: attempt + 1,
|
|
134
|
+
maxAttempts,
|
|
135
|
+
nextAttemptAt,
|
|
136
|
+
});
|
|
137
|
+
await jobsRepository.markFailed(jobId, message);
|
|
138
|
+
/*
|
|
139
|
+
* The client is told "retrying", not "failed". `useBulkEvent` watches
|
|
140
|
+
* for a terminal state, and reporting failure here would stop a progress
|
|
141
|
+
* UI on a job that is still going.
|
|
142
|
+
*/
|
|
143
|
+
await pingBulkEvent(jobId, { status: "retrying", action: job.jobType, error: message, retryJobId: retryId }, ctx);
|
|
144
|
+
ctx.logger.warn("runJobOnce: attempt failed, retry scheduled", {
|
|
145
|
+
jobId,
|
|
146
|
+
retryId,
|
|
147
|
+
jobType: job.jobType,
|
|
148
|
+
attempt,
|
|
149
|
+
status,
|
|
150
|
+
nextAttemptAt: nextAttemptAt.toISOString(),
|
|
151
|
+
});
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
await jobsRepository.markDead(jobId, message);
|
|
155
|
+
await pingBulkEvent(jobId, { status: "failed", action: job.jobType, error: message }, ctx);
|
|
156
|
+
await openDeadJobCase(jobId, job, message, ctx);
|
|
157
|
+
ctx.logger.error("runJobOnce: job DEAD", err, {
|
|
158
|
+
jobId,
|
|
159
|
+
jobType: job.jobType,
|
|
160
|
+
attempt,
|
|
161
|
+
status,
|
|
162
|
+
reason: retryable ? "attempts exhausted" : "permanent failure",
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* A dead job is a case, not a log line.
|
|
168
|
+
*
|
|
169
|
+
* 🛑 Keyed on the ORIGINAL job id (`parentJobId ?? jobId`), so three exhausted
|
|
170
|
+
* attempts of one job produce ONE case rather than three. `openOrObserve`
|
|
171
|
+
* increments its own counter — the property that makes it safe to open a case
|
|
172
|
+
* from a hot path at all.
|
|
173
|
+
*
|
|
174
|
+
* Best-effort: a job is already dead by this point, and failing to file the
|
|
175
|
+
* case must not turn that into a thrown error from a trigger.
|
|
176
|
+
*/
|
|
177
|
+
async function openDeadJobCase(jobId, job, error, ctx) {
|
|
178
|
+
const subjectId = job.parentJobId ?? jobId;
|
|
179
|
+
try {
|
|
180
|
+
await paymentCasesRepository().openOrObserve({
|
|
181
|
+
kind: "dead_job",
|
|
182
|
+
subjectId,
|
|
183
|
+
severity: "error",
|
|
184
|
+
entityType: "job",
|
|
185
|
+
entityId: subjectId,
|
|
186
|
+
title: `Job ${job.jobType} gave up after ${job.attempt ?? 1} attempt(s)`,
|
|
187
|
+
detail: `The last error was: ${error}. ` +
|
|
188
|
+
`Requested by ${job.requestedBy}. The job document holds the payload.`,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
void normalizeError(err);
|
|
193
|
+
ctx.logger.error("runJobOnce: could not open dead_job case", err, { jobId });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -8,7 +8,7 @@ import { getDefaultCurrency } from "../../../../core/index.js";
|
|
|
8
8
|
import { computePayoutDeduction } from "../../../shared/fees/calculator.js";
|
|
9
9
|
import { payoutCutoff } from "../../../shared/fees/payout-hold.js";
|
|
10
10
|
import { PROTECTION_CLAIM_WINDOW_HOURS } from "../../../shared/features/shipping/protections.js";
|
|
11
|
-
import { ordersTaxableValue, ordersShippingReimbursement, } from "../../../shared/checkout/order-math.js";
|
|
11
|
+
import { ordersTaxableValue, ordersShippingReimbursement, ordersPointsBorneByStore, } from "../../../shared/checkout/order-math.js";
|
|
12
12
|
export async function runWeeklyPayoutEligibility(ctx) {
|
|
13
13
|
// `FEATURE_PAYOUTS` gate deleted 2026-10-19 — see `autoPayoutEligibility.ts`.
|
|
14
14
|
const siteSettings = await siteSettingsRepository.getSingleton();
|
|
@@ -33,8 +33,21 @@ export async function runWeeklyPayoutEligibility(ctx) {
|
|
|
33
33
|
holdSource: hold.source,
|
|
34
34
|
cutoff: cutoff.toISOString(),
|
|
35
35
|
});
|
|
36
|
+
/*
|
|
37
|
+
* 🛑 Resolved BEFORE the sweep, not per group.
|
|
38
|
+
*
|
|
39
|
+
* The widened query needs the whole set up front — the grouping by store
|
|
40
|
+
* happens after the rows come back, so a per-group lookup would arrive too
|
|
41
|
+
* late to have admitted the shipped rows in the first place. One bounded
|
|
42
|
+
* read against a hand-granted flag.
|
|
43
|
+
*/
|
|
44
|
+
const trustedStoreIds = await storeRepository.findTrustedStoreIds();
|
|
45
|
+
if (trustedStoreIds.size > 0) {
|
|
46
|
+
ctx.logger.info(`${trustedStoreIds.size} trusted store(s) — their SHIPPED orders are eligible without waiting for delivery`, { trustedStoreCount: trustedStoreIds.size });
|
|
47
|
+
}
|
|
36
48
|
const eligible = await orderRepository.getEligibleAutomatic(cutoff, {
|
|
37
49
|
onWarn: (m, meta) => ctx.logger.warn(m, meta),
|
|
50
|
+
trustedStoreIds,
|
|
38
51
|
});
|
|
39
52
|
if (eligible.length === 0) {
|
|
40
53
|
ctx.logger.info("No eligible delivered orders found");
|
|
@@ -75,7 +88,15 @@ export async function runWeeklyPayoutEligibility(ctx) {
|
|
|
75
88
|
* `{ id, data }` wrappers, and passing the wrapper would read every field
|
|
76
89
|
* as undefined and silently reimburse zero.
|
|
77
90
|
*/
|
|
78
|
-
ordersShippingReimbursement(orders.map((o) => o.data))
|
|
91
|
+
ordersShippingReimbursement(orders.map((o) => o.data)),
|
|
92
|
+
/*
|
|
93
|
+
* D46/D47 — the store-funded points, both ends. Must match
|
|
94
|
+
* `autoPayoutEligibility` exactly, for the reason stated above
|
|
95
|
+
* `grossAmount`: the weekly run and the automatic one pay the same orders,
|
|
96
|
+
* and a term present in one and absent in the other is a seller paid two
|
|
97
|
+
* different amounts depending on which job got there first.
|
|
98
|
+
*/
|
|
99
|
+
ordersPointsBorneByStore(orders.map((o) => o.data)));
|
|
79
100
|
/*
|
|
80
101
|
* 🛑 REVEALED before building a payout — see the identical note in
|
|
81
102
|
* `autoPayoutEligibility.ts`. `findById` is ciphertext-by-default and
|
|
@@ -85,6 +106,8 @@ export async function runWeeklyPayoutEligibility(ctx) {
|
|
|
85
106
|
*/
|
|
86
107
|
const sellerPlain = userRepository.revealUser(seller);
|
|
87
108
|
const payoutInput = {
|
|
109
|
+
/* Every row this job writes settles a SELLER liability. */
|
|
110
|
+
payeeType: "store",
|
|
88
111
|
storeId,
|
|
89
112
|
// `displayName` is plaintext by D1; the email fallback must be revealed.
|
|
90
113
|
sellerName: (seller.displayName ?? sellerPlain.email ?? store.ownerId),
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export { couponExpiryHandler } from "./couponExpiry";
|
|
11
11
|
export { offerExpiryHandler } from "./offerExpiry";
|
|
12
|
+
export { reservationExpiryHandler } from "./reservationExpiry";
|
|
12
13
|
export { cartPruneHandler } from "./cartPrune";
|
|
13
14
|
export { notificationPruneHandler } from "./notificationPrune";
|
|
14
15
|
export { dailyDataCleanupHandler } from "./dailyDataCleanup";
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export { couponExpiryHandler } from "./couponExpiry.js";
|
|
11
11
|
export { offerExpiryHandler } from "./offerExpiry.js";
|
|
12
|
+
export { reservationExpiryHandler } from "./reservationExpiry.js";
|
|
12
13
|
export { cartPruneHandler } from "./cartPrune.js";
|
|
13
14
|
export { notificationPruneHandler } from "./notificationPrune.js";
|
|
14
15
|
export { dailyDataCleanupHandler } from "./dailyDataCleanup.js";
|
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
import { handleOrderStatusChange, } from "../core/onOrderStatusChange.js";
|
|
2
|
+
/*
|
|
3
|
+
* 🛑 Bound as **`documentUpdated`**, which is what Phase 7's numbering needs
|
|
4
|
+
* and is already what this function uses — verified rather than assumed.
|
|
5
|
+
*
|
|
6
|
+
* `paymentStatus: pending → paid` is frequently the ONLY field that moves on a
|
|
7
|
+
* COD or manual order, and `documentUpdated` fires on any update, so the
|
|
8
|
+
* handler sees it. `documentCreated` would miss every one of them, and
|
|
9
|
+
* `documentWritten` would add nothing except a wake-up on create, where an
|
|
10
|
+
* order is `pending` and there is nothing to number.
|
|
11
|
+
*
|
|
12
|
+
* 🛑 What makes a write-back from this handler safe is the allocation claim:
|
|
13
|
+
* it updates `orders` only when IT allocated, so the re-fire caused by its own
|
|
14
|
+
* write finds the claim taken and writes nothing. That is the guard Root Cause
|
|
15
|
+
* #92's 12M-invocation loop lacked — there the no-op check compared Firestore
|
|
16
|
+
* maps with `JSON.stringify`, which is key-order sensitive and could never
|
|
17
|
+
* match, so every invocation wrote and every write re-triggered.
|
|
18
|
+
*/
|
|
2
19
|
export const onOrderStatusChangeHandler = async (event, ctx) => {
|
|
3
20
|
await handleOrderStatusChange({ orderId: event.params.orderId, before: event.before, after: event.after }, ctx);
|
|
4
21
|
};
|
|
@@ -10,10 +10,33 @@
|
|
|
10
10
|
* its own outcome decides whether the circuit re-closes or re-opens.
|
|
11
11
|
*/
|
|
12
12
|
/**
|
|
13
|
-
* Email is the only outbound channel with a transport.
|
|
14
|
-
* other arm until 2026-10-20, when the Cloud API
|
|
13
|
+
* Email is the only outbound *notification* channel with a transport.
|
|
14
|
+
* "whatsapp" was the other arm until 2026-10-20, when the Cloud API
|
|
15
|
+
* integration was removed.
|
|
16
|
+
*
|
|
17
|
+
* ## 🛑 Phase 8 widened this to the RECONCILERS' reads — and only those
|
|
18
|
+
*
|
|
19
|
+
* `razorpay_read` and `shiprocket_read` name the one placement where "stop
|
|
20
|
+
* hammering a dead API" is the right behaviour. Two placements are **actively
|
|
21
|
+
* harmful** and must never be added:
|
|
22
|
+
*
|
|
23
|
+
* 1. **NOT the webhook ingest path.** An open breaker converts a transient
|
|
24
|
+
* blip into a DROPPED money event — and dropping is precisely what a
|
|
25
|
+
* breaker does. The ingest path's whole job is to persist first and ack
|
|
26
|
+
* honestly so the gateway retries; a breaker there would ack a 5xx we never
|
|
27
|
+
* recorded.
|
|
28
|
+
*
|
|
29
|
+
* 2. **NOT around `payment.refund()`.** A refund POST that timed out may have
|
|
30
|
+
* SUCCEEDED, so retrying it issues two refunds — and the idempotency claim
|
|
31
|
+
* does not help, because it is the same call with the same key making a
|
|
32
|
+
* second network attempt. The two-phase reserve/settle exists so a human
|
|
33
|
+
* can resolve that by reading the gateway, not so a breaker can guess.
|
|
34
|
+
*
|
|
35
|
+
* The names carry `_read` for exactly that reason: a reader is idempotent, so
|
|
36
|
+
* backing off costs only freshness. Anything that moves money is not a reader
|
|
37
|
+
* and does not belong in this union.
|
|
15
38
|
*/
|
|
16
|
-
export type NotificationChannel = "email";
|
|
39
|
+
export type NotificationChannel = "email" | "razorpay_read" | "shiprocket_read";
|
|
17
40
|
/**
|
|
18
41
|
* Returns false only when the circuit is open and still within its cooldown
|
|
19
42
|
* window. Any other state (closed, or open-but-cooldown-elapsed) is treated
|
|
@@ -29,7 +29,7 @@ import type { IconKey } from "../../../ui/icons/icon-registry";
|
|
|
29
29
|
/** What KIND of action — drives default UI affordance (link vs button vs danger). */
|
|
30
30
|
export type ActionKind = "primary" | "secondary" | "danger" | "ghost" | "link" | "chip";
|
|
31
31
|
/** Resource bucket — keyed top-level in the ACTIONS tree. */
|
|
32
|
-
export type ActionResource = "PRODUCT" | "AUCTION" | "PRE_ORDER" | "PRIZE_DRAW" | "DIGITAL_CODE" | "BUNDLE" | "GROUP" | "CATEGORY" | "BRAND" | "PART" | "BEY" | "FAQ" | "SUBLISTING" | "STORE" | "BLOG" | "EVENT" | "REVIEW" | "SHIPMENT" | "CATALOGUE" | "USER" | "SELLER" | "ADMIN" | "CART" | "CHECKOUT" | "NAV" | "MEDIA" | "SUPPORT" | "LOTTERY" | "TESTER";
|
|
32
|
+
export type ActionResource = "PRODUCT" | "AUCTION" | "PRE_ORDER" | "PRIZE_DRAW" | "DIGITAL_CODE" | "BUNDLE" | "GROUP" | "CATEGORY" | "BRAND" | "ADS" | "PART" | "BEY" | "FAQ" | "SUBLISTING" | "STORE" | "BLOG" | "EVENT" | "REVIEW" | "SHIPMENT" | "CATALOGUE" | "USER" | "SELLER" | "ADMIN" | "CART" | "CHECKOUT" | "NAV" | "MEDIA" | "SUPPORT" | "LOTTERY" | "TESTER";
|
|
33
33
|
/** Confirmation modal config — when set, <Button action={...}> opens
|
|
34
34
|
* a confirm dialog before firing the actual handler. */
|
|
35
35
|
export interface ActionConfirmation {
|