@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.
Files changed (391) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +8 -29
  3. package/dist/_internal/client/features/layout/filterNavItems.d.ts +28 -0
  4. package/dist/_internal/client/features/layout/filterNavItems.js +35 -0
  5. package/dist/_internal/server/features/ads/booking.d.ts +118 -0
  6. package/dist/_internal/server/features/ads/booking.js +326 -0
  7. package/dist/_internal/server/features/checkout/actions.d.ts +129 -9
  8. package/dist/_internal/server/features/checkout/actions.js +850 -79
  9. package/dist/_internal/server/features/checkout/expand-order-items.d.ts +1 -2
  10. package/dist/_internal/server/features/checkout/expand-order-items.js +32 -0
  11. package/dist/_internal/server/features/checkout/freeze-parts.d.ts +50 -0
  12. package/dist/_internal/server/features/checkout/freeze-parts.js +94 -0
  13. package/dist/_internal/server/features/checkout/value-otp-gate.d.ts +55 -0
  14. package/dist/_internal/server/features/checkout/value-otp-gate.js +60 -0
  15. package/dist/_internal/server/features/dashboard/attention.d.ts +128 -0
  16. package/dist/_internal/server/features/dashboard/attention.js +183 -0
  17. package/dist/_internal/server/features/dashboard/data.d.ts +47 -0
  18. package/dist/_internal/server/features/dashboard/data.js +82 -0
  19. package/dist/_internal/server/features/inventory/reservations.d.ts +99 -0
  20. package/dist/_internal/server/features/inventory/reservations.js +273 -0
  21. package/dist/_internal/server/features/invoicing/allocate.d.ts +63 -0
  22. package/dist/_internal/server/features/invoicing/allocate.js +162 -0
  23. package/dist/_internal/server/features/invoicing/credit-note.d.ts +57 -0
  24. package/dist/_internal/server/features/invoicing/credit-note.js +126 -0
  25. package/dist/_internal/server/features/invoicing/gstr1.d.ts +74 -0
  26. package/dist/_internal/server/features/invoicing/gstr1.js +251 -0
  27. package/dist/_internal/server/features/invoicing/issue.d.ts +96 -0
  28. package/dist/_internal/server/features/invoicing/issue.js +202 -0
  29. package/dist/_internal/server/features/offers/destinations.d.ts +40 -0
  30. package/dist/_internal/server/features/offers/destinations.js +65 -0
  31. package/dist/_internal/server/features/orders/actions.d.ts +8 -1
  32. package/dist/_internal/server/features/orders/actions.js +81 -8
  33. package/dist/_internal/server/features/orders/adapters.js +54 -0
  34. package/dist/_internal/server/features/orders/amend-delivery.d.ts +56 -0
  35. package/dist/_internal/server/features/orders/amend-delivery.js +124 -0
  36. package/dist/_internal/server/features/orders/balance.d.ts +89 -0
  37. package/dist/_internal/server/features/orders/balance.js +256 -0
  38. package/dist/_internal/server/features/orders/cancellation-refund.d.ts +15 -9
  39. package/dist/_internal/server/features/orders/cancellation-refund.js +15 -9
  40. package/dist/_internal/server/features/points/award.js +46 -4
  41. package/dist/_internal/server/features/pricing/actions.d.ts +58 -0
  42. package/dist/_internal/server/features/pricing/actions.js +144 -0
  43. package/dist/_internal/server/features/refunds/actions.js +26 -0
  44. package/dist/_internal/server/features/shipping/book-split.d.ts +86 -0
  45. package/dist/_internal/server/features/shipping/book-split.js +130 -0
  46. package/dist/_internal/server/features/shipping/book.d.ts +61 -0
  47. package/dist/_internal/server/features/shipping/book.js +176 -0
  48. package/dist/_internal/server/features/shipping/courier-block.d.ts +69 -0
  49. package/dist/_internal/server/features/shipping/courier-block.js +264 -0
  50. package/dist/_internal/server/features/site-settings/adapters.d.ts +2 -2
  51. package/dist/_internal/server/features/site-settings/adapters.js +29 -0
  52. package/dist/_internal/server/features/stores/adapters.d.ts +2 -2
  53. package/dist/_internal/server/features/stores/adapters.js +65 -0
  54. package/dist/_internal/server/functions/scheduled.d.ts +2 -1
  55. package/dist/_internal/server/functions/scheduled.js +95 -2
  56. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +11 -2
  57. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +4 -0
  58. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +19 -2
  59. package/dist/_internal/server/jobs/core/index.d.ts +2 -1
  60. package/dist/_internal/server/jobs/core/index.js +2 -1
  61. package/dist/_internal/server/jobs/core/jobRunners.js +45 -0
  62. package/dist/_internal/server/jobs/core/onJobCreated.d.ts +30 -7
  63. package/dist/_internal/server/jobs/core/onJobCreated.js +78 -45
  64. package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +7 -0
  65. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +65 -0
  66. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +33 -0
  67. package/dist/_internal/server/jobs/core/reservationExpiry.d.ts +17 -0
  68. package/dist/_internal/server/jobs/core/reservationExpiry.js +40 -0
  69. package/dist/_internal/server/jobs/core/runJobOnce.d.ts +48 -0
  70. package/dist/_internal/server/jobs/core/runJobOnce.js +195 -0
  71. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +25 -2
  72. package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
  73. package/dist/_internal/server/jobs/handlers/index.js +1 -0
  74. package/dist/_internal/server/jobs/handlers/onOrderStatusChange.js +17 -0
  75. package/dist/_internal/server/jobs/handlers/reservationExpiry.d.ts +2 -0
  76. package/dist/_internal/server/jobs/handlers/reservationExpiry.js +2 -0
  77. package/dist/_internal/server/notifications/channel-health.d.ts +26 -3
  78. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  79. package/dist/_internal/shared/actions/action-registry.js +296 -0
  80. package/dist/_internal/shared/checkout/coupon-discount.d.ts +43 -1
  81. package/dist/_internal/shared/checkout/coupon-discount.js +26 -1
  82. package/dist/_internal/shared/checkout/order-math.d.ts +174 -0
  83. package/dist/_internal/shared/checkout/order-math.js +159 -0
  84. package/dist/_internal/shared/checkout/refundable-base.d.ts +25 -0
  85. package/dist/_internal/shared/checkout/refundable-base.js +13 -1
  86. package/dist/_internal/shared/checkout/rules/index.d.ts +1 -1
  87. package/dist/_internal/shared/checkout/rules/index.js +1 -1
  88. package/dist/_internal/shared/checkout/rules/payment-gate.d.ts +18 -0
  89. package/dist/_internal/shared/checkout/rules/payment-gate.js +25 -0
  90. package/dist/_internal/shared/checkout/split.d.ts +75 -0
  91. package/dist/_internal/shared/checkout/split.js +120 -0
  92. package/dist/_internal/shared/content/disclaimers.d.ts +88 -0
  93. package/dist/_internal/shared/content/disclaimers.js +217 -0
  94. package/dist/_internal/shared/content/published-values.d.ts +93 -0
  95. package/dist/_internal/shared/content/published-values.js +206 -0
  96. package/dist/_internal/shared/features/admin/integrations.d.ts +139 -0
  97. package/dist/_internal/shared/features/admin/integrations.js +263 -0
  98. package/dist/_internal/shared/features/dashboard/hub-sections.d.ts +72 -0
  99. package/dist/_internal/shared/features/dashboard/hub-sections.js +75 -0
  100. package/dist/_internal/shared/features/emi/choice.d.ts +98 -0
  101. package/dist/_internal/shared/features/emi/choice.js +69 -0
  102. package/dist/_internal/shared/features/emi/group.d.ts +84 -0
  103. package/dist/_internal/shared/features/emi/group.js +126 -0
  104. package/dist/_internal/shared/features/ledger/posting.d.ts +107 -0
  105. package/dist/_internal/shared/features/ledger/posting.js +165 -0
  106. package/dist/_internal/shared/features/ledger/reports.d.ts +76 -0
  107. package/dist/_internal/shared/features/ledger/reports.js +127 -0
  108. package/dist/_internal/shared/features/ledger/rules.d.ts +170 -0
  109. package/dist/_internal/shared/features/ledger/rules.js +221 -0
  110. package/dist/_internal/shared/features/offers/coordination.d.ts +122 -0
  111. package/dist/_internal/shared/features/offers/coordination.js +80 -0
  112. package/dist/_internal/shared/features/orders/delivery-snapshot.d.ts +114 -0
  113. package/dist/_internal/shared/features/orders/delivery-snapshot.js +116 -0
  114. package/dist/_internal/shared/features/orders/dispute-reasons.d.ts +113 -0
  115. package/dist/_internal/shared/features/orders/dispute-reasons.js +181 -0
  116. package/dist/_internal/shared/features/orders/refund-copy.d.ts +1 -1
  117. package/dist/_internal/shared/features/orders/refund-copy.js +18 -1
  118. package/dist/_internal/shared/features/payouts/pay-period.d.ts +91 -0
  119. package/dist/_internal/shared/features/payouts/pay-period.js +104 -0
  120. package/dist/_internal/shared/features/points/accepts.d.ts +29 -0
  121. package/dist/_internal/shared/features/points/accepts.js +49 -0
  122. package/dist/_internal/shared/features/pricing/rate-registry.d.ts +150 -0
  123. package/dist/_internal/shared/features/pricing/rate-registry.js +246 -0
  124. package/dist/_internal/shared/features/shipping/parcel.d.ts +123 -0
  125. package/dist/_internal/shared/features/shipping/parcel.js +139 -0
  126. package/dist/_internal/shared/features/shipping/shared-purchase.d.ts +91 -0
  127. package/dist/_internal/shared/features/shipping/shared-purchase.js +108 -0
  128. package/dist/_internal/shared/features/site-settings/fees.d.ts +16 -8
  129. package/dist/_internal/shared/features/site-settings/fees.js +12 -8
  130. package/dist/_internal/shared/features/site-settings/types.d.ts +11 -0
  131. package/dist/_internal/shared/fees/calculator.d.ts +33 -1
  132. package/dist/_internal/shared/fees/calculator.js +32 -2
  133. package/dist/_internal/shared/fees/explain.d.ts +81 -0
  134. package/dist/_internal/shared/fees/explain.js +101 -0
  135. package/dist/_internal/shared/history/index.d.ts +1 -1
  136. package/dist/_internal/shared/history/index.js +1 -1
  137. package/dist/_internal/shared/history/types.d.ts +7 -1
  138. package/dist/_internal/shared/history/types.js +25 -0
  139. package/dist/client.d.ts +21 -0
  140. package/dist/client.js +43 -0
  141. package/dist/constants/api-endpoints.d.ts +114 -0
  142. package/dist/constants/api-endpoints.js +55 -0
  143. package/dist/constants/field-names.d.ts +42 -0
  144. package/dist/constants/field-names.js +52 -0
  145. package/dist/contracts/index.d.ts +1 -1
  146. package/dist/contracts/shipping.d.ts +45 -0
  147. package/dist/contracts/shipping.js +15 -0
  148. package/dist/features/about/components/AboutView.js +1 -1
  149. package/dist/features/account/components/BuyerAuctionsGuideView.js +4 -1
  150. package/dist/features/account/components/UserOrdersView.js +47 -35
  151. package/dist/features/admin/components/AdminCouponEditorView.js +17 -0
  152. package/dist/features/admin/components/AdminIntegrationsView.d.ts +55 -0
  153. package/dist/features/admin/components/AdminIntegrationsView.js +74 -0
  154. package/dist/features/admin/components/AdminInvoicingView.d.ts +2 -0
  155. package/dist/features/admin/components/AdminInvoicingView.js +65 -0
  156. package/dist/features/admin/components/AdminOrderEditorView.d.ts +33 -1
  157. package/dist/features/admin/components/AdminOrderEditorView.js +81 -3
  158. package/dist/features/admin/components/AdminOrdersGuideView.js +23 -2
  159. package/dist/features/admin/components/AdminOrdersView.js +86 -5
  160. package/dist/features/admin/components/OwnerQuickActions.js +24 -2
  161. package/dist/features/admin/constants/filter-tabs.d.ts +6 -0
  162. package/dist/features/admin/constants/filter-tabs.js +6 -0
  163. package/dist/features/admin/constants/site-settings-tabs.d.ts +1 -1
  164. package/dist/features/admin/hooks/useAdminListingData.js +16 -0
  165. package/dist/features/admin/schemas/admin-user-form.d.ts +2 -2
  166. package/dist/features/admin/schemas/firestore.d.ts +62 -0
  167. package/dist/features/admin/schemas/firestore.js +5 -0
  168. package/dist/features/ads/schemas/booking-write.d.ts +120 -0
  169. package/dist/features/ads/schemas/booking-write.js +75 -0
  170. package/dist/features/ads/schemas/creative-form.d.ts +18 -0
  171. package/dist/features/ads/schemas/creative-form.js +51 -0
  172. package/dist/features/ads/schemas/firestore.d.ts +140 -0
  173. package/dist/features/ads/schemas/firestore.js +109 -0
  174. package/dist/features/auctions/repository/bid.repository.js +3 -1
  175. package/dist/features/audit-log/schemas/firestore.d.ts +140 -0
  176. package/dist/features/audit-log/schemas/firestore.js +89 -0
  177. package/dist/features/auth/actions/profile-actions.d.ts +69 -1
  178. package/dist/features/auth/actions/profile-actions.js +62 -1
  179. package/dist/features/auth/permissions/constants.js +13 -0
  180. package/dist/features/auth/schemas/firestore.d.ts +16 -2
  181. package/dist/features/auth/schemas/firestore.js +2 -1
  182. package/dist/features/blog/actions/blog-actions.d.ts +6 -0
  183. package/dist/features/blog/actions/blog-actions.js +17 -2
  184. package/dist/features/blog/api/route.js +4 -1
  185. package/dist/features/blog/columns/index.js +5 -0
  186. package/dist/features/blog/components/BlogIndexPageView.js +2 -1
  187. package/dist/features/blog/repository/blog.repository.d.ts +4 -0
  188. package/dist/features/blog/repository/blog.repository.js +5 -0
  189. package/dist/features/blog/schemas/blog-form.d.ts +3 -0
  190. package/dist/features/blog/schemas/blog-form.js +1 -0
  191. package/dist/features/blog/schemas/firestore.d.ts +38 -1
  192. package/dist/features/blog/schemas/firestore.js +23 -0
  193. package/dist/features/blog/schemas/index.d.ts +6 -0
  194. package/dist/features/blog/schemas/index.js +2 -0
  195. package/dist/features/blog/types/index.d.ts +2 -0
  196. package/dist/features/cart/components/CartPriceBreakdown.d.ts +25 -1
  197. package/dist/features/cart/components/CartPriceBreakdown.js +29 -6
  198. package/dist/features/cart/components/PointsTenderPicker.d.ts +61 -0
  199. package/dist/features/cart/components/PointsTenderPicker.js +15 -0
  200. package/dist/features/cart/components/StoreAddonsPicker.js +2 -1
  201. package/dist/features/cart/components/index.d.ts +2 -0
  202. package/dist/features/cart/components/index.js +1 -0
  203. package/dist/features/cart/repository/cart.repository.d.ts +33 -0
  204. package/dist/features/cart/repository/cart.repository.js +47 -0
  205. package/dist/features/cart/schemas/firestore.d.ts +116 -0
  206. package/dist/features/contact/email.d.ts +27 -10
  207. package/dist/features/contact/email.js +20 -3
  208. package/dist/features/dashboard/components/DashboardHubView.d.ts +83 -0
  209. package/dist/features/dashboard/components/DashboardHubView.js +56 -0
  210. package/dist/features/dashboard/components/DashboardSectionsClient.d.ts +23 -0
  211. package/dist/features/dashboard/components/DashboardSectionsClient.js +66 -0
  212. package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.d.ts +55 -0
  213. package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.js +175 -0
  214. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.d.ts +62 -0
  215. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +108 -0
  216. package/dist/features/dispute-reasons/schemas/firestore.d.ts +62 -0
  217. package/dist/features/dispute-reasons/schemas/firestore.js +40 -0
  218. package/dist/features/events/components/EventSaleBanner.js +1 -1
  219. package/dist/features/faq/components/FAQAccordion.d.ts +13 -1
  220. package/dist/features/faq/components/FAQAccordion.js +29 -4
  221. package/dist/features/faq/components/FAQPageContent.js +2 -2
  222. package/dist/features/gateway-events/schemas/index.d.ts +2 -2
  223. package/dist/features/homepage/schemas/index.d.ts +8 -8
  224. package/dist/features/inventory/schemas/stock-reservation.d.ts +114 -0
  225. package/dist/features/inventory/schemas/stock-reservation.js +103 -0
  226. package/dist/features/invoicing/schemas/firestore.d.ts +112 -0
  227. package/dist/features/invoicing/schemas/firestore.js +45 -0
  228. package/dist/features/invoicing/utils/financial-year.d.ts +43 -0
  229. package/dist/features/invoicing/utils/financial-year.js +53 -0
  230. package/dist/features/jobs/actions/enqueue-job.js +18 -1
  231. package/dist/features/jobs/repository/jobs.repository.d.ts +62 -1
  232. package/dist/features/jobs/repository/jobs.repository.js +84 -1
  233. package/dist/features/jobs/schemas/firestore.d.ts +80 -0
  234. package/dist/features/jobs/schemas/firestore.js +37 -0
  235. package/dist/features/layout/AppLayoutShell.d.ts +9 -1
  236. package/dist/features/layout/AppLayoutShell.js +12 -4
  237. package/dist/features/layout/BottomActions.js +2 -1
  238. package/dist/features/layout/BottomNavbar.d.ts +10 -2
  239. package/dist/features/layout/SideActionDock.js +52 -2
  240. package/dist/features/layout/TitleBarLayout.js +13 -4
  241. package/dist/features/layout/TitleBarLayout.style.css +59 -0
  242. package/dist/features/layout/useCtaReveal.d.ts +16 -1
  243. package/dist/features/layout/useCtaReveal.js +17 -26
  244. package/dist/features/ledger/repository/journal.repository.d.ts +110 -0
  245. package/dist/features/ledger/repository/journal.repository.js +312 -0
  246. package/dist/features/ledger/schemas/expenses.d.ts +87 -0
  247. package/dist/features/ledger/schemas/expenses.js +27 -0
  248. package/dist/features/ledger/schemas/firestore.d.ts +183 -0
  249. package/dist/features/ledger/schemas/firestore.js +69 -0
  250. package/dist/features/lottery/schemas/zod.d.ts +6 -6
  251. package/dist/features/media/MediaImage.js +22 -1
  252. package/dist/features/media/upload/ImageUpload.js +1 -1
  253. package/dist/features/obligations/constants/policies.js +24 -1
  254. package/dist/features/orders/components/BalancePaymentPanel.d.ts +12 -0
  255. package/dist/features/orders/components/BalancePaymentPanel.js +94 -0
  256. package/dist/features/orders/components/OrderPaymentSummary.js +10 -1
  257. package/dist/features/orders/components/OrderProvenanceCard.d.ts +8 -0
  258. package/dist/features/orders/components/OrderProvenanceCard.js +185 -0
  259. package/dist/features/orders/components/RaiseDisputeForm.d.ts +14 -0
  260. package/dist/features/orders/components/RaiseDisputeForm.js +119 -0
  261. package/dist/features/orders/components/RefundRequestView.js +19 -1
  262. package/dist/features/orders/components/index.d.ts +4 -0
  263. package/dist/features/orders/components/index.js +2 -0
  264. package/dist/features/orders/constants/order-lane-tabs.d.ts +69 -0
  265. package/dist/features/orders/constants/order-lane-tabs.js +88 -0
  266. package/dist/features/orders/repository/orders.repository.d.ts +85 -1
  267. package/dist/features/orders/repository/orders.repository.js +205 -6
  268. package/dist/features/orders/schemas/balance.d.ts +40 -0
  269. package/dist/features/orders/schemas/balance.js +47 -0
  270. package/dist/features/orders/schemas/firestore.d.ts +398 -1
  271. package/dist/features/orders/schemas/firestore.js +43 -0
  272. package/dist/features/orders/schemas/index.d.ts +56 -8
  273. package/dist/features/orders/schemas/index.js +12 -0
  274. package/dist/features/orders/types/index.d.ts +47 -0
  275. package/dist/features/orders/utils/courier-block.d.ts +25 -0
  276. package/dist/features/orders/utils/courier-block.js +23 -0
  277. package/dist/features/payment-cases/schemas/firestore.d.ts +26 -1
  278. package/dist/features/payment-cases/schemas/firestore.js +1 -0
  279. package/dist/features/payments/repository/payout.repository.js +3 -2
  280. package/dist/features/payments/schemas/firestore.d.ts +92 -1
  281. package/dist/features/payments/schemas/firestore.js +23 -0
  282. package/dist/features/pricing/components/AdminPricingView.d.ts +13 -0
  283. package/dist/features/pricing/components/AdminPricingView.js +157 -0
  284. package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -2
  285. package/dist/features/products/components/ProductDetailPageView.d.ts +17 -1
  286. package/dist/features/products/components/ProductDetailPageView.js +2 -2
  287. package/dist/features/products/schemas/firestore.d.ts +26 -1
  288. package/dist/features/products/schemas/firestore.js +2 -0
  289. package/dist/features/products/schemas/index.d.ts +2 -2
  290. package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
  291. package/dist/features/promotions/actions/coupon-stacking.d.ts +30 -3
  292. package/dist/features/promotions/actions/coupon-stacking.js +21 -2
  293. package/dist/features/promotions/components/CouponCard.js +13 -0
  294. package/dist/features/promotions/constants/coupon-help.d.ts +2 -2
  295. package/dist/features/promotions/constants/coupon-help.js +9 -1
  296. package/dist/features/promotions/schemas/firestore.d.ts +24 -0
  297. package/dist/features/promotions/schemas/firestore.js +4 -0
  298. package/dist/features/promotions/types/index.d.ts +19 -1
  299. package/dist/features/reviews/repository/reviews.repository.d.ts +14 -0
  300. package/dist/features/reviews/repository/reviews.repository.js +16 -0
  301. package/dist/features/scams/components/ScamProfileView.js +2 -2
  302. package/dist/features/scams/components/ScamRegistryView.js +2 -2
  303. package/dist/features/scams/repository/scammer.repository.js +2 -1
  304. package/dist/features/scams/schemas/firestore.d.ts +2 -1
  305. package/dist/features/scams/schemas/firestore.js +2 -1
  306. package/dist/features/search/action-index/tab-entries.js +1 -1
  307. package/dist/features/seller/actions/offer-actions.js +56 -0
  308. package/dist/features/seller/actions/seller-actions.d.ts +1 -1
  309. package/dist/features/seller/actions/seller-actions.js +37 -3
  310. package/dist/features/seller/components/DispatchForm.d.ts +11 -0
  311. package/dist/features/seller/components/DispatchForm.js +190 -0
  312. package/dist/features/seller/components/SellerOrdersView.js +221 -27
  313. package/dist/features/seller/components/index.d.ts +1 -0
  314. package/dist/features/seller/components/index.js +1 -0
  315. package/dist/features/seller/hooks/useSellerListingData.js +25 -0
  316. package/dist/features/seller/repository/offer.repository.js +4 -3
  317. package/dist/features/seller/schemas/firestore.d.ts +23 -2
  318. package/dist/features/seller/schemas/firestore.js +3 -2
  319. package/dist/features/seller/schemas/index.d.ts +4 -4
  320. package/dist/features/seller/schemas/order-forms.d.ts +61 -4
  321. package/dist/features/seller/schemas/order-forms.js +41 -8
  322. package/dist/features/shipments/schemas/firestore.d.ts +25 -0
  323. package/dist/features/shipments/schemas/validation.d.ts +4 -4
  324. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +82 -0
  325. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +145 -0
  326. package/dist/features/shipping-boxes/schemas/firestore.d.ts +80 -0
  327. package/dist/features/shipping-boxes/schemas/firestore.js +53 -0
  328. package/dist/features/store-extensions/schemas/report-create-form.d.ts +2 -2
  329. package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +2 -2
  330. package/dist/features/stores/components/StoreOrdersGuideView.js +6 -1
  331. package/dist/features/stores/repository/store.repository.d.ts +18 -0
  332. package/dist/features/stores/repository/store.repository.js +27 -1
  333. package/dist/features/stores/schemas/firestore.d.ts +112 -0
  334. package/dist/features/stores/schemas/firestore.js +20 -0
  335. package/dist/features/stores/schemas/index.d.ts +6 -6
  336. package/dist/features/stores/types/index.d.ts +19 -0
  337. package/dist/features/support/repository/support.repository.js +72 -6
  338. package/dist/features/support/schemas/firestore.d.ts +17 -0
  339. package/dist/features/support/schemas/index.d.ts +8 -8
  340. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
  341. package/dist/index.d.ts +11 -1
  342. package/dist/index.js +33 -1
  343. package/dist/next/routing/route-map.d.ts +112 -0
  344. package/dist/next/routing/route-map.js +63 -0
  345. package/dist/providers/shipping-shiprocket/index.d.ts +27 -1
  346. package/dist/providers/shipping-shiprocket/index.js +84 -1
  347. package/dist/react/contexts/SessionContext.d.ts +17 -1
  348. package/dist/schemas/registry.d.ts +69 -34
  349. package/dist/schemas/sieve.d.ts +2 -2
  350. package/dist/security/pii-schemas.d.ts +1 -1
  351. package/dist/security/pii-schemas.js +45 -0
  352. package/dist/server-entry.d.ts +2 -2
  353. package/dist/server-entry.js +8 -1
  354. package/dist/server.d.ts +36 -1
  355. package/dist/server.js +183 -1
  356. package/dist/styles.css +412 -80
  357. package/dist/tailwind-utilities.css +1 -1
  358. package/dist/tokens/themes/default-dark.js +1 -0
  359. package/dist/tokens/themes/default-light.js +1 -0
  360. package/dist/tokens/themes/required.d.ts +1 -1
  361. package/dist/tokens/themes/required.js +1 -0
  362. package/dist/tokens/tokens.css +15 -0
  363. package/dist/ui/components/Button.d.ts +10 -1
  364. package/dist/ui/components/Button.js +25 -3
  365. package/dist/ui/components/Chart.d.ts +63 -0
  366. package/dist/ui/components/Chart.js +169 -0
  367. package/dist/ui/components/Chart.style.css +214 -0
  368. package/dist/ui/components/Disclaimer.d.ts +19 -0
  369. package/dist/ui/components/Disclaimer.js +83 -0
  370. package/dist/ui/components/OverflowRow.js +54 -2
  371. package/dist/ui/components/OverflowRow.style.css +38 -0
  372. package/dist/ui/components/RichTextEditor.js +99 -8
  373. package/dist/ui/components/chart-spec.d.ts +68 -0
  374. package/dist/ui/components/chart-spec.js +140 -0
  375. package/dist/ui/components/index.style.css +1 -0
  376. package/dist/ui/components/surface-tokens.d.ts +1 -1
  377. package/dist/ui/components/surface-tokens.js +9 -0
  378. package/dist/ui/components/tabs-overflow.js +64 -11
  379. package/dist/ui/components/toolbar-collapse.d.ts +13 -0
  380. package/dist/ui/components/toolbar-collapse.js +10 -1
  381. package/dist/ui/index.d.ts +8 -0
  382. package/dist/ui/index.js +9 -0
  383. package/dist/ui/rich-text/RichText.d.ts +10 -1
  384. package/dist/ui/rich-text/RichText.js +33 -2
  385. package/dist/ui/rich-text/RichText.style.css +83 -79
  386. package/dist/ui/rich-text/RichTextRenderer.d.ts +30 -1
  387. package/dist/ui/rich-text/RichTextRenderer.js +34 -2
  388. package/dist/ui/rich-text/chart-blocks.d.ts +62 -0
  389. package/dist/ui/rich-text/chart-blocks.js +87 -0
  390. package/package.json +1 -1
  391. 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 })] }), _jsx(EmailFooter, { copyright: `© ${new Date().getFullYear()}${siteName ? ` ${siteName}` : ""}. All rights reserved.` })] }) }))}`;
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
- * Flow:
9
- * 1. Mark the job "processing".
10
- * 2. Look up a runner in `JOB_RUNNERS` by `job.jobType`.
11
- * 3. Run it, mark the job "done"/"failed", and best-effort ping the
12
- * `bulk_events/{jobId}` RTDB channel the client is subscribed to via
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
- * Flow:
9
- * 1. Mark the job "processing".
10
- * 2. Look up a runner in `JOB_RUNNERS` by `job.jobType`.
11
- * 3. Run it, mark the job "done"/"failed", and best-effort ping the
12
- * `bulk_events/{jobId}` RTDB channel the client is subscribed to via
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 { getAdminRealtimeDb } from "../../../../providers/db-firebase/index.js";
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
- await jobsRepository.markProcessing(jobId);
38
- const runner = JOB_RUNNERS[job.jobType];
39
- if (!runner) {
40
- const error = `No runner registered for jobType: ${job.jobType}`;
41
- await jobsRepository.markFailed(jobId, error);
42
- await pingBulkEvent(jobId, { status: "failed", action: job.jobType, error }, ctx);
43
- ctx.logger.error("onJobCreated: unregistered jobType", null, { jobId, jobType: job.jobType });
44
- return;
45
- }
46
- try {
47
- const result = await runner(job.payload, ctx);
48
- await jobsRepository.markDone(jobId, result);
49
- await pingBulkEvent(jobId, { status: "success", action: job.jobType, ...result }, ctx);
50
- ctx.logger.info("onJobCreated: job complete", { jobId, jobType: job.jobType, summary: result.summary });
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
- catch (err) {
53
- void normalizeError(err);
54
- const message = err instanceof Error ? err.message : String(err);
55
- await jobsRepository.markFailed(jobId, message);
56
- await pingBulkEvent(jobId, { status: "failed", action: job.jobType, error: message }, ctx);
57
- ctx.logger.error("onJobCreated: job failed", err, { jobId, jobType: job.jobType });
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;