@mohasinac/appkit 4.30.18 → 4.31.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 (557) hide show
  1. package/dist/_internal/client/features/digital-code/CodeRevealPanel.js +5 -2
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +3 -3
  3. package/dist/_internal/client/features/layout/useSidebarSearch.d.ts +21 -5
  4. package/dist/_internal/client/features/layout/useSidebarSearch.js +27 -6
  5. package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +23 -3
  6. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +106 -86
  7. package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +16 -5
  8. package/dist/_internal/client/features/lottery/LotteryPullForm.js +65 -40
  9. package/dist/_internal/client/features/maintenance/views/AnalysisRunnerView.js +5 -2
  10. package/dist/_internal/client/features/maintenance/views/CloudLogsListView.js +11 -3
  11. package/dist/_internal/client/hand-mode/HandModeProvider.js +2 -0
  12. package/dist/_internal/server/features/auctions/actions.js +14 -3
  13. package/dist/_internal/server/features/auctions/service.js +1 -1
  14. package/dist/_internal/server/features/auth/permissions.d.ts +2 -0
  15. package/dist/_internal/server/features/auth/permissions.js +3 -2
  16. package/dist/_internal/server/features/blog/data.js +2 -2
  17. package/dist/_internal/server/features/blog/service.js +3 -3
  18. package/dist/_internal/server/features/brands/data.js +1 -1
  19. package/dist/_internal/server/features/brands/service.js +3 -3
  20. package/dist/_internal/server/features/bundles/actions.js +6 -1
  21. package/dist/_internal/server/features/bundles/data.js +21 -4
  22. package/dist/_internal/server/features/cart/data.js +3 -1
  23. package/dist/_internal/server/features/cart/service.js +13 -2
  24. package/dist/_internal/server/features/categories/data.js +26 -6
  25. package/dist/_internal/server/features/classified/index.d.ts +0 -1
  26. package/dist/_internal/server/features/classified/index.js +0 -1
  27. package/dist/_internal/server/features/digital-code/index.d.ts +0 -1
  28. package/dist/_internal/server/features/digital-code/index.js +0 -1
  29. package/dist/_internal/server/features/events/actions.js +17 -4
  30. package/dist/_internal/server/features/events/data.js +1 -1
  31. package/dist/_internal/server/features/events/service.js +1 -1
  32. package/dist/_internal/server/features/grouped/data.js +7 -2
  33. package/dist/_internal/server/features/item-requests/data.js +1 -1
  34. package/dist/_internal/server/features/live/index.d.ts +0 -1
  35. package/dist/_internal/server/features/live/index.js +0 -1
  36. package/dist/_internal/server/features/lottery/actions.js +2 -2
  37. package/dist/_internal/server/features/lottery/data.js +1 -1
  38. package/dist/_internal/server/features/orders/actions.js +7 -7
  39. package/dist/_internal/server/features/orders/data.d.ts +1 -1
  40. package/dist/_internal/server/features/orders/data.js +14 -2
  41. package/dist/_internal/server/features/orders/service.js +1 -1
  42. package/dist/_internal/server/features/pre-orders/service.js +1 -1
  43. package/dist/_internal/server/features/prize-draws/actions.js +1 -1
  44. package/dist/_internal/server/features/products/data.js +21 -6
  45. package/dist/_internal/server/features/products/list-public.d.ts +9 -0
  46. package/dist/_internal/server/features/products/list-public.js +95 -7
  47. package/dist/_internal/server/features/products/service.js +1 -1
  48. package/dist/_internal/server/features/promotions/actions.js +1 -1
  49. package/dist/_internal/server/features/promotions/data.js +1 -1
  50. package/dist/_internal/server/features/promotions/service.js +1 -1
  51. package/dist/_internal/server/features/reviews/data.d.ts +1 -1
  52. package/dist/_internal/server/features/reviews/data.js +6 -1
  53. package/dist/_internal/server/features/reviews/og-data.js +1 -1
  54. package/dist/_internal/server/features/shared/listing-data-factory.js +7 -2
  55. package/dist/_internal/server/features/site-settings/adapters.d.ts +2 -2
  56. package/dist/_internal/server/features/site-settings/adapters.js +8 -5
  57. package/dist/_internal/server/features/stores/data.js +1 -1
  58. package/dist/_internal/server/features/sublisting-categories/data.js +1 -1
  59. package/dist/_internal/server/features/tester/visibility.d.ts +2 -1
  60. package/dist/_internal/server/features/tester/visibility.js +2 -2
  61. package/dist/_internal/server/functions/scheduled.d.ts +3 -1
  62. package/dist/_internal/server/functions/scheduled.js +27 -1
  63. package/dist/_internal/server/jobs/core/auctionSettlement.js +6 -1
  64. package/dist/_internal/server/jobs/core/hardBanCascade.js +4 -0
  65. package/dist/_internal/server/jobs/core/index.d.ts +2 -0
  66. package/dist/_internal/server/jobs/core/index.js +2 -0
  67. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +7 -15
  68. package/dist/_internal/server/jobs/core/pageViewPrune.d.ts +10 -0
  69. package/dist/_internal/server/jobs/core/pageViewPrune.js +44 -0
  70. package/dist/_internal/server/jobs/core/pageViewRollup.d.ts +2 -0
  71. package/dist/_internal/server/jobs/core/pageViewRollup.js +87 -0
  72. package/dist/_internal/server/jobs/core/revenueRollup.js +7 -1
  73. package/dist/_internal/server/jobs/handlers/index.d.ts +2 -0
  74. package/dist/_internal/server/jobs/handlers/index.js +2 -0
  75. package/dist/_internal/server/jobs/handlers/pageViewPrune.d.ts +2 -0
  76. package/dist/_internal/server/jobs/handlers/pageViewPrune.js +2 -0
  77. package/dist/_internal/server/jobs/handlers/pageViewRollup.d.ts +2 -0
  78. package/dist/_internal/server/jobs/handlers/pageViewRollup.js +2 -0
  79. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  80. package/dist/_internal/shared/actions/action-registry.js +69 -22
  81. package/dist/_internal/shared/checkout/rules/classified.rule.d.ts +9 -4
  82. package/dist/_internal/shared/checkout/rules/classified.rule.js +7 -2
  83. package/dist/_internal/shared/features/layout/matchesNavQuery.d.ts +14 -0
  84. package/dist/_internal/shared/features/layout/matchesNavQuery.js +72 -0
  85. package/dist/_internal/shared/features/layout/types.d.ts +37 -1
  86. package/dist/_internal/shared/features/layout/types.js +19 -1
  87. package/dist/_internal/shared/features/notifications/action-url.d.ts +9 -1
  88. package/dist/_internal/shared/features/notifications/action-url.js +47 -0
  89. package/dist/_internal/shared/features/offers/config.d.ts +65 -0
  90. package/dist/_internal/shared/features/offers/config.js +73 -2
  91. package/dist/_internal/shared/features/orders/schema.d.ts +19 -1
  92. package/dist/_internal/shared/features/orders/schema.js +22 -2
  93. package/dist/_internal/shared/features/site-settings/types.d.ts +12 -3
  94. package/dist/_internal/shared/listing-types/capabilities.d.ts +8 -2
  95. package/dist/_internal/shared/listing-types/capabilities.js +6 -1
  96. package/dist/_internal/shared/listing-types/classified/ctas.js +6 -1
  97. package/dist/_internal/shared/listing-types/feature-flags.d.ts +10 -5
  98. package/dist/_internal/shared/listing-types/feature-flags.js +11 -6
  99. package/dist/client/api/useApiQuery.js +17 -6
  100. package/dist/client.d.ts +38 -20
  101. package/dist/client.js +39 -14
  102. package/dist/configs/next.js +10 -2
  103. package/dist/constants/api-endpoints.d.ts +18 -54
  104. package/dist/constants/api-endpoints.js +6 -26
  105. package/dist/constants/field-names.d.ts +6 -24
  106. package/dist/constants/field-names.js +10 -27
  107. package/dist/constants/geo/countries.d.ts +50 -0
  108. package/dist/constants/geo/countries.js +129 -0
  109. package/dist/constants/geo/subdivisions.d.ts +13 -0
  110. package/dist/constants/geo/subdivisions.js +104 -0
  111. package/dist/constants/index.d.ts +2 -2
  112. package/dist/constants/index.js +2 -2
  113. package/dist/constants/table-keys.d.ts +24 -0
  114. package/dist/constants/table-keys.js +28 -0
  115. package/dist/contracts/client-realtime.d.ts +6 -0
  116. package/dist/contracts/extend.d.ts +0 -9
  117. package/dist/contracts/index.d.ts +1 -1
  118. package/dist/contracts/realtime-session.d.ts +12 -12
  119. package/dist/contracts/realtime-session.js +79 -77
  120. package/dist/core/browser.d.ts +1 -1
  121. package/dist/core/browser.js +1 -1
  122. package/dist/core/index.d.ts +1 -1
  123. package/dist/core/index.js +1 -1
  124. package/dist/core/site-config.d.ts +0 -3
  125. package/dist/core/site-config.js +0 -3
  126. package/dist/errors/index.d.ts +1 -1
  127. package/dist/errors/index.js +1 -1
  128. package/dist/errors/messages.d.ts +0 -8
  129. package/dist/errors/messages.js +0 -9
  130. package/dist/features/about/components/DeveloperView.js +7 -2
  131. package/dist/features/about/components/PolicyPageView.js +9 -1
  132. package/dist/features/about/components/PublicProfileView.js +23 -5
  133. package/dist/features/about/schemas/index.d.ts +8 -8
  134. package/dist/features/account/components/AddressFilters.js +32 -11
  135. package/dist/features/account/components/AddressForm.d.ts +26 -25
  136. package/dist/features/account/components/AddressForm.js +81 -103
  137. package/dist/features/account/components/AddressesIndexListing.js +8 -4
  138. package/dist/features/account/components/NotificationBell.js +4 -3
  139. package/dist/features/account/components/NotificationPreferencesPanel.js +1 -1
  140. package/dist/features/account/components/UserOffersPanel.js +1 -1
  141. package/dist/features/account/components/UserSidebar.d.ts +3 -4
  142. package/dist/features/account/components/UserSupportView.js +89 -58
  143. package/dist/features/account/components/index.d.ts +1 -7
  144. package/dist/features/account/components/index.js +0 -3
  145. package/dist/features/account/hooks/useAddresses.d.ts +4 -3
  146. package/dist/features/account/hooks/useAddresses.js +4 -6
  147. package/dist/features/account/hooks/useCollapsedSections.d.ts +51 -10
  148. package/dist/features/account/hooks/useCollapsedSections.js +211 -38
  149. package/dist/features/account/hooks/useProfile.d.ts +8 -0
  150. package/dist/features/account/index.d.ts +0 -3
  151. package/dist/features/account/index.js +0 -3
  152. package/dist/features/account/schemas/index.d.ts +0 -92
  153. package/dist/features/account/schemas/index.js +42 -20
  154. package/dist/features/addresses/components/AddressLocationFields.d.ts +13 -0
  155. package/dist/features/addresses/components/AddressLocationFields.js +41 -0
  156. package/dist/features/addresses/hooks/usePostalLookup.d.ts +17 -0
  157. package/dist/features/addresses/hooks/usePostalLookup.js +133 -0
  158. package/dist/features/addresses/schemas/address-form.d.ts +423 -0
  159. package/dist/features/addresses/schemas/address-form.js +212 -0
  160. package/dist/features/addresses/schemas/index.d.ts +1 -0
  161. package/dist/features/addresses/schemas/index.js +8 -0
  162. package/dist/features/admin/actions/admin-read-actions.d.ts +1 -1
  163. package/dist/features/admin/actions/index.d.ts +0 -1
  164. package/dist/features/admin/actions/index.js +0 -1
  165. package/dist/features/admin/actions/notification-actions.js +1 -41
  166. package/dist/features/admin/components/AdminActionIndexView.d.ts +32 -0
  167. package/dist/features/admin/components/AdminActionIndexView.js +128 -0
  168. package/dist/features/admin/components/AdminAdEditorView.js +159 -163
  169. package/dist/features/admin/components/AdminAddressEditorView.js +97 -55
  170. package/dist/features/admin/components/AdminBrandEditorView.d.ts +15 -0
  171. package/dist/features/admin/components/AdminBrandEditorView.js +113 -77
  172. package/dist/features/admin/components/AdminBundleEditorView.d.ts +19 -9
  173. package/dist/features/admin/components/AdminBundleEditorView.js +136 -168
  174. package/dist/features/admin/components/AdminCategoryEditorView.js +120 -43
  175. package/dist/features/admin/components/AdminCouponEditorView.js +145 -109
  176. package/dist/features/admin/components/AdminEmployeeEditorView.js +125 -56
  177. package/dist/features/admin/components/AdminFaqEditorView.js +112 -58
  178. package/dist/features/admin/components/AdminFeatureEditorView.js +108 -68
  179. package/dist/features/admin/components/AdminFulfillmentView.js +5 -2
  180. package/dist/features/admin/components/AdminMediaView.js +3 -3
  181. package/dist/features/admin/components/AdminNavEditorView.js +69 -44
  182. package/dist/features/admin/components/AdminOrderEditorView.js +47 -10
  183. package/dist/features/admin/components/AdminPrizeDrawsView.js +16 -2
  184. package/dist/features/admin/components/AdminSidebar.d.ts +11 -7
  185. package/dist/features/admin/components/AdminSiteSettingsView.js +37 -37
  186. package/dist/features/admin/components/AdminStoreEditorView.js +75 -52
  187. package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +71 -30
  188. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +95 -65
  189. package/dist/features/admin/components/BrandQuickCreateForm.d.ts +10 -0
  190. package/dist/features/admin/components/BrandQuickCreateForm.js +49 -26
  191. package/dist/features/admin/components/CategoryQuickCreateForm.d.ts +11 -0
  192. package/dist/features/admin/components/CategoryQuickCreateForm.js +48 -26
  193. package/dist/features/admin/components/index.d.ts +2 -2
  194. package/dist/features/admin/components/index.js +1 -1
  195. package/dist/features/admin/constants/site-settings-tabs.d.ts +120 -0
  196. package/dist/features/admin/constants/site-settings-tabs.js +152 -0
  197. package/dist/features/admin/index.d.ts +0 -3
  198. package/dist/features/admin/index.js +0 -2
  199. package/dist/features/admin/repository/analytics-rollup.repository.d.ts +35 -0
  200. package/dist/features/admin/repository/analytics-rollup.repository.js +30 -0
  201. package/dist/features/admin/schemas/admin-editor-forms.d.ts +56 -57
  202. package/dist/features/admin/schemas/admin-editor-forms.js +97 -54
  203. package/dist/features/admin/schemas/admin-ops-forms.d.ts +57 -5
  204. package/dist/features/admin/schemas/admin-ops-forms.js +104 -20
  205. package/dist/features/admin/schemas/admin-user-form.js +7 -0
  206. package/dist/features/admin/schemas/firestore.d.ts +48 -95
  207. package/dist/features/admin/schemas/firestore.js +15 -151
  208. package/dist/features/admin/schemas/nav-item-form.js +9 -2
  209. package/dist/features/admin/server.d.ts +0 -4
  210. package/dist/features/admin/server.js +0 -2
  211. package/dist/features/admin/types/index.d.ts +0 -7
  212. package/dist/features/analytics/repository/page-views.repository.d.ts +14 -0
  213. package/dist/features/analytics/repository/page-views.repository.js +24 -0
  214. package/dist/features/analytics/types.d.ts +1 -1
  215. package/dist/features/analytics/types.js +13 -0
  216. package/dist/features/auctions/components/AuctionDetailPageView.js +6 -2
  217. package/dist/features/auctions/components/MarketplaceAuctionCard.js +3 -1
  218. package/dist/features/auth/actions/index.d.ts +0 -1
  219. package/dist/features/auth/actions/index.js +0 -1
  220. package/dist/features/auth/actions/profile-actions.js +4 -3
  221. package/dist/features/auth/actions/realtime-token-actions.js +1 -12
  222. package/dist/features/auth/auth-helpers.js +5 -7
  223. package/dist/features/auth/components/ForgotPasswordView.js +1 -1
  224. package/dist/features/auth/components/Guards.js +2 -1
  225. package/dist/features/auth/components/LoginForm.d.ts +0 -2
  226. package/dist/features/auth/components/LoginForm.js +2 -4
  227. package/dist/features/auth/components/RegisterForm.js +62 -19
  228. package/dist/features/auth/components/ResetPasswordView.js +1 -1
  229. package/dist/features/auth/hooks/useAuth.js +3 -0
  230. package/dist/features/auth/permissions/constants.d.ts +27 -1
  231. package/dist/features/auth/role-predicates.d.ts +75 -7
  232. package/dist/features/auth/role-predicates.js +28 -2
  233. package/dist/features/auth/schemas/firestore.d.ts +22 -0
  234. package/dist/features/auth/schemas/index.d.ts +1 -1
  235. package/dist/features/auth/schemas/index.js +44 -8
  236. package/dist/features/auth/types/index.d.ts +14 -1
  237. package/dist/features/blog/api/route.js +10 -2
  238. package/dist/features/blog/components/BlogIndexListing.js +1 -1
  239. package/dist/features/blog/hooks/useBlog.js +2 -2
  240. package/dist/features/blog/repository/blog.repository.js +3 -1
  241. package/dist/features/blog/schemas/index.d.ts +4 -0
  242. package/dist/features/blog/schemas/index.js +2 -0
  243. package/dist/features/blog/types/index.d.ts +1 -1
  244. package/dist/features/cart/actions/cart-actions.js +13 -4
  245. package/dist/features/cart/utils/pending-ops.js +1 -1
  246. package/dist/features/catalogue/components/CatalogueItemEditorView.d.ts +22 -0
  247. package/dist/features/catalogue/components/CatalogueItemEditorView.js +112 -52
  248. package/dist/features/catalogue/schemas/validation.d.ts +4 -4
  249. package/dist/features/catalogue/schemas/validation.js +47 -10
  250. package/dist/features/categories/components/BrandDetailPageView.js +28 -12
  251. package/dist/features/categories/components/CategoryDetailPageView.js +37 -20
  252. package/dist/features/categories/repository/categories.repository.js +3 -1
  253. package/dist/features/categories/schemas/bundle-form.d.ts +142 -0
  254. package/dist/features/categories/schemas/bundle-form.js +149 -0
  255. package/dist/features/checkout/actions/checkout-value-otp-actions.js +10 -1
  256. package/dist/features/classified/components/ClassifiedContactSellerPanel.js +1 -1
  257. package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +5 -4
  258. package/dist/features/classified/components/ClassifiedDetailPageView.js +47 -18
  259. package/dist/features/consultation/components/ConsultationForm.js +1 -1
  260. package/dist/features/consultation/schemas/index.js +34 -10
  261. package/dist/features/corporate/schemas/index.js +36 -15
  262. package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +4 -1
  263. package/dist/features/events/components/EventBanner.js +5 -2
  264. package/dist/features/events/components/EventOfferCard.js +3 -1
  265. package/dist/features/events/components/EventPollWidget.js +1 -1
  266. package/dist/features/events/components/SpinWheelView.js +3 -2
  267. package/dist/features/filters/FilterPanel.js +3 -3
  268. package/dist/features/filters/RangeFilter.d.ts +10 -0
  269. package/dist/features/filters/RangeFilter.js +41 -4
  270. package/dist/features/grouped/components/GroupedListingEditorView.js +2 -1
  271. package/dist/features/history/hooks/useHistory.js +29 -27
  272. package/dist/features/homepage/components/AdSlot.js +3 -1
  273. package/dist/features/homepage/components/GoogleReviewsSection.js +8 -2
  274. package/dist/features/homepage/components/MarketplaceHomepageView.js +58 -15
  275. package/dist/features/homepage/components/SocialFeedSection.js +4 -1
  276. package/dist/features/homepage/hooks/useBlogArticles.js +1 -1
  277. package/dist/features/homepage/lib/live-stats.js +8 -1
  278. package/dist/features/homepage/repository/carousel.repository.js +2 -1
  279. package/dist/features/layout/BottomActions.js +84 -11
  280. package/dist/features/layout/BottomActionsContext.d.ts +1 -6
  281. package/dist/features/layout/BottomActionsContext.js +1 -1
  282. package/dist/features/layout/hooks/useBottomActions.js +2 -2
  283. package/dist/features/layout/hooks/useFormBottomActions.d.ts +12 -1
  284. package/dist/features/layout/hooks/useFormBottomActions.js +12 -6
  285. package/dist/features/live/components/LiveItemDetailPageView.js +4 -1
  286. package/dist/features/lottery/schemas/config-write.d.ts +9 -5
  287. package/dist/features/lottery/schemas/config-write.js +69 -9
  288. package/dist/features/media/finalize.js +3 -1
  289. package/dist/features/media/modals/ImageEditor.js +1 -1
  290. package/dist/features/media/modals/VideoThumbnailSelector.js +5 -2
  291. package/dist/features/media/modals/VideoTrimModal.js +5 -2
  292. package/dist/features/media/upload/MediaUploadField.js +2 -2
  293. package/dist/features/messages/hooks/useConversation.js +18 -4
  294. package/dist/features/messages/hooks/useConversations.js +25 -8
  295. package/dist/features/orders/components/RefundRequestView.js +5 -2
  296. package/dist/features/orders/schemas/buyer-forms.d.ts +6 -3
  297. package/dist/features/orders/schemas/buyer-forms.js +31 -3
  298. package/dist/features/orders/schemas/index.d.ts +2 -2
  299. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -1
  300. package/dist/features/pre-orders/components/PreOrderActionsClient.js +1 -1
  301. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +4 -1
  302. package/dist/features/products/components/GroupSettingsPanel.js +3 -1
  303. package/dist/features/products/components/MakeOfferButton.d.ts +13 -2
  304. package/dist/features/products/components/MakeOfferButton.js +33 -11
  305. package/dist/features/products/components/PrizeDrawDetailPageView.js +4 -1
  306. package/dist/features/products/components/ProductDetailActions.js +0 -1
  307. package/dist/features/products/components/ProductDetailPageView.d.ts +7 -3
  308. package/dist/features/products/components/ProductDetailPageView.js +16 -7
  309. package/dist/features/products/components/ShareButton.js +3 -1
  310. package/dist/features/products/constants/action-defs.d.ts +1 -2
  311. package/dist/features/products/constants/action-defs.js +40 -43
  312. package/dist/features/products/repository/loadProductFeatures.js +10 -2
  313. package/dist/features/products/repository/products.repository.d.ts +5 -1
  314. package/dist/features/products/repository/products.repository.js +25 -2
  315. package/dist/features/products/schemas/firestore.d.ts +7 -3
  316. package/dist/features/products/schemas/product-features.validators.js +3 -0
  317. package/dist/features/promotions/components/CouponCard.js +11 -8
  318. package/dist/features/reviews/actions/review-actions.js +8 -1
  319. package/dist/features/reviews/components/ReviewDetailPageView.js +13 -3
  320. package/dist/features/reviews/components/ReviewDetailShell.js +3 -1
  321. package/dist/features/scams/actions/scam-actions.d.ts +9 -1
  322. package/dist/features/scams/actions/scam-actions.js +52 -12
  323. package/dist/features/scams/components/SellerTrustBadge.d.ts +1 -1
  324. package/dist/features/scams/components/SellerTrustBadge.js +13 -0
  325. package/dist/features/scams/repository/scammer.repository.js +2 -1
  326. package/dist/features/scams/schemas/firestore.d.ts +15 -1
  327. package/dist/features/scams/schemas/report-form.d.ts +5 -5
  328. package/dist/features/scams/schemas/report-form.js +18 -3
  329. package/dist/features/search/action-index/derive.d.ts +49 -0
  330. package/dist/features/search/action-index/derive.js +121 -0
  331. package/dist/features/search/action-index/repository.d.ts +42 -0
  332. package/dist/features/search/action-index/repository.js +100 -0
  333. package/dist/features/search/action-index/settings-entries.d.ts +41 -0
  334. package/dist/features/search/action-index/settings-entries.js +268 -0
  335. package/dist/features/search/action-index/types.d.ts +81 -0
  336. package/dist/features/search/action-index/types.js +102 -0
  337. package/dist/features/search/action-index/validate-entry.d.ts +34 -0
  338. package/dist/features/search/action-index/validate-entry.js +114 -0
  339. package/dist/features/search/hooks/useNavSuggestions.d.ts +5 -1
  340. package/dist/features/search/hooks/useNavSuggestions.js +23 -3
  341. package/dist/features/search/repository/search.repository.js +3 -1
  342. package/dist/features/seller/actions/offer-actions.js +43 -19
  343. package/dist/features/seller/actions/seller-actions.js +29 -5
  344. package/dist/features/seller/components/CategoryChipPicker.d.ts +9 -0
  345. package/dist/features/seller/components/CategoryChipPicker.js +12 -0
  346. package/dist/features/seller/components/FulfillmentView.js +5 -2
  347. package/dist/features/seller/components/PhysicalLocationModal.js +1 -1
  348. package/dist/features/seller/components/PrintCenterView.js +3 -1
  349. package/dist/features/seller/components/SellerAddressesView.js +5 -1
  350. package/dist/features/seller/components/SellerAnalyticsAlertsView.js +83 -43
  351. package/dist/features/seller/components/SellerArtView.d.ts +14 -7
  352. package/dist/features/seller/components/SellerArtView.js +4 -119
  353. package/dist/features/seller/components/SellerBidsView.js +1 -1
  354. package/dist/features/seller/components/SellerClassifiedView.d.ts +14 -7
  355. package/dist/features/seller/components/SellerClassifiedView.js +4 -124
  356. package/dist/features/seller/components/SellerCouponEditorView.d.ts +22 -1
  357. package/dist/features/seller/components/SellerCouponEditorView.js +91 -45
  358. package/dist/features/seller/components/SellerCouponsView.js +1 -1
  359. package/dist/features/seller/components/SellerDigitalCodesView.d.ts +14 -7
  360. package/dist/features/seller/components/SellerDigitalCodesView.js +4 -126
  361. package/dist/features/seller/components/SellerGoogleReviewsView.js +2 -2
  362. package/dist/features/seller/components/SellerListingTypeView.d.ts +66 -0
  363. package/dist/features/seller/components/SellerListingTypeView.js +277 -0
  364. package/dist/features/seller/components/SellerLiveView.d.ts +14 -7
  365. package/dist/features/seller/components/SellerLiveView.js +4 -126
  366. package/dist/features/seller/components/SellerOrdersView.js +3 -3
  367. package/dist/features/seller/components/SellerPayoutMethodsView.js +2 -2
  368. package/dist/features/seller/components/SellerProductShell.d.ts +5 -0
  369. package/dist/features/seller/components/SellerProductShell.js +15 -5
  370. package/dist/features/seller/components/SellerProductsView.js +24 -10
  371. package/dist/features/seller/components/SellerReviewsView.js +47 -16
  372. package/dist/features/seller/components/SellerShippingConfigsView.js +5 -4
  373. package/dist/features/seller/components/SellerSidebar.d.ts +3 -6
  374. package/dist/features/seller/components/SellerStickersView.d.ts +14 -7
  375. package/dist/features/seller/components/SellerStickersView.js +4 -119
  376. package/dist/features/seller/components/SellerStoreCategoriesView.js +3 -2
  377. package/dist/features/seller/components/SellerSupportView.d.ts +31 -0
  378. package/dist/features/seller/components/SellerSupportView.js +100 -0
  379. package/dist/features/seller/components/index.d.ts +4 -8
  380. package/dist/features/seller/components/index.js +2 -4
  381. package/dist/features/seller/index.d.ts +0 -1
  382. package/dist/features/seller/index.js +0 -1
  383. package/dist/features/seller/schemas/coupon-form.d.ts +214 -0
  384. package/dist/features/seller/schemas/coupon-form.js +155 -23
  385. package/dist/features/seller/schemas/offer-forms.d.ts +16 -7
  386. package/dist/features/seller/schemas/offer-forms.js +28 -12
  387. package/dist/features/shell/FormShell.js +2 -2
  388. package/dist/features/shell/QuickFormDrawer.js +2 -2
  389. package/dist/features/shell/SectionForm.d.ts +19 -1
  390. package/dist/features/shell/SectionForm.js +104 -17
  391. package/dist/features/shell/build-sections.d.ts +23 -0
  392. package/dist/features/shell/build-sections.js +106 -8
  393. package/dist/features/shell/field-ui-meta.d.ts +44 -0
  394. package/dist/features/shell/field-ui-meta.js +14 -7
  395. package/dist/features/shell/form-sections.d.ts +46 -0
  396. package/dist/features/shell/form-sections.js +114 -0
  397. package/dist/features/shipments/components/AdminShipmentEditorView.js +87 -64
  398. package/dist/features/shipments/schemas/validation.d.ts +2 -2
  399. package/dist/features/shipments/schemas/validation.js +44 -13
  400. package/dist/features/store-extensions/schemas/custom-role-form.js +6 -2
  401. package/dist/features/store-extensions/schemas/listing-template-form.d.ts +7 -7
  402. package/dist/features/store-extensions/schemas/payout-method-form.js +14 -0
  403. package/dist/features/stores/actions/store-query-actions.js +3 -2
  404. package/dist/features/stores/api/[storeSlug]/auctions/route.js +3 -1
  405. package/dist/features/stores/api/[storeSlug]/products/route.js +3 -1
  406. package/dist/features/stores/components/StoreDetailLayoutView.d.ts +1 -1
  407. package/dist/features/stores/components/StoreDetailLayoutView.js +12 -3
  408. package/dist/features/support/repository/support.repository.d.ts +21 -0
  409. package/dist/features/support/repository/support.repository.js +63 -0
  410. package/dist/features/support/schemas/firestore.d.ts +27 -2
  411. package/dist/features/support/schemas/firestore.js +2 -0
  412. package/dist/features/support/schemas/ticket-create-form.js +7 -0
  413. package/dist/features/tester/schemas/checklist-item-form.d.ts +43 -0
  414. package/dist/features/tester/schemas/checklist-item-form.js +109 -0
  415. package/dist/features/tester/seed-data/blog-tester-seed-data.js +2 -2
  416. package/dist/features/tester/seed-data/categories-tester-seed-data.js +2 -2
  417. package/dist/features/tester/seed-data/events-tester-seed-data.js +5 -5
  418. package/dist/features/tester/seed-data/orders-tester-seed-data.js +2 -2
  419. package/dist/features/tester/seed-data/products-tester-seed-data.js +48 -49
  420. package/dist/features/tester/seed-data/stores-tester-seed-data.js +3 -3
  421. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +291 -30
  422. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +51 -14
  423. package/dist/features/wishlist/hooks/useWishlistToggle.js +8 -1
  424. package/dist/http/api-handler.js +2 -1
  425. package/dist/index.d.ts +25 -104
  426. package/dist/index.js +38 -143
  427. package/dist/jobs.d.ts +1 -1
  428. package/dist/jobs.js +1 -1
  429. package/dist/monitoring/browser.d.ts +0 -2
  430. package/dist/monitoring/browser.js +12 -1
  431. package/dist/monitoring/index.d.ts +0 -2
  432. package/dist/monitoring/index.js +0 -1
  433. package/dist/next/api/routeHandler.js +9 -4
  434. package/dist/next/index.d.ts +1 -2
  435. package/dist/next/index.js +0 -1
  436. package/dist/next/middleware/index.d.ts +0 -2
  437. package/dist/next/middleware/index.js +0 -1
  438. package/dist/next/routing/route-map.d.ts +8 -7
  439. package/dist/next/routing/route-map.js +10 -3
  440. package/dist/providers/auth-firebase/session.js +3 -1
  441. package/dist/providers/db-firebase/admin-app-lite.js +8 -8
  442. package/dist/providers/db-firebase/admin.js +27 -13
  443. package/dist/providers/db-firebase/browser-stub.d.ts +1 -11
  444. package/dist/providers/db-firebase/browser-stub.js +6 -11
  445. package/dist/providers/db-firebase/index.d.ts +0 -1
  446. package/dist/providers/db-firebase/index.js +0 -1
  447. package/dist/providers/db-firebase/rtdb-paths.d.ts +15 -6
  448. package/dist/providers/db-firebase/rtdb-paths.js +15 -6
  449. package/dist/providers/db-firebase/rtdb-url.d.ts +27 -0
  450. package/dist/providers/db-firebase/rtdb-url.js +50 -0
  451. package/dist/providers/db-firebase/sieve.d.ts +22 -0
  452. package/dist/providers/firebase-client/realtime.d.ts +20 -0
  453. package/dist/providers/firebase-client/realtime.js +34 -1
  454. package/dist/providers.d.ts +1 -1
  455. package/dist/providers.js +1 -1
  456. package/dist/react/hooks/use-action-dispatch.js +8 -2
  457. package/dist/react/hooks/useFormStatePreservation.js +3 -1
  458. package/dist/react/hooks/useListingTypeFlags.d.ts +1 -1
  459. package/dist/react/hooks/useListingTypeFlags.js +1 -1
  460. package/dist/react/hooks/usePendingFilters.js +5 -3
  461. package/dist/react/hooks/usePendingTable.js +15 -3
  462. package/dist/react/hooks/useRealtimeEvent.js +20 -9
  463. package/dist/repositories/index.d.ts +0 -1
  464. package/dist/repositories/index.js +0 -1
  465. package/dist/schemas/registry.d.ts +197 -232
  466. package/dist/schemas/registry.js +5 -6
  467. package/dist/schemas/webhooks/razorpay.d.ts +50 -50
  468. package/dist/security/authorization.d.ts +9 -0
  469. package/dist/security/authorization.js +16 -2
  470. package/dist/security/browser.d.ts +1 -3
  471. package/dist/security/browser.js +5 -2
  472. package/dist/security/index.d.ts +1 -3
  473. package/dist/security/index.js +19 -2
  474. package/dist/security/pii-encrypt.js +16 -0
  475. package/dist/security/pii-schemas.d.ts +0 -14
  476. package/dist/security/pii-schemas.js +7 -15
  477. package/dist/seed/_helpers/category-forest.js +2 -2
  478. package/dist/seed/_helpers/media.d.ts +19 -0
  479. package/dist/seed/_helpers/media.js +75 -0
  480. package/dist/seed/blog-posts-seed-data.js +17 -17
  481. package/dist/seed/carousel-slides-seed-data.js +7 -7
  482. package/dist/seed/cart-seed-data.js +13 -13
  483. package/dist/seed/categories-seed-data.js +10 -10
  484. package/dist/seed/events-seed-data.js +24 -24
  485. package/dist/seed/factories/product.factory.js +4 -4
  486. package/dist/seed/factories/store.factory.js +3 -3
  487. package/dist/seed/factories/user.factory.js +2 -2
  488. package/dist/seed/grouped-listings-seed-data.js +11 -11
  489. package/dist/seed/homepage-sections-seed-data.js +2 -2
  490. package/dist/seed/index.d.ts +0 -1
  491. package/dist/seed/index.js +0 -1
  492. package/dist/seed/manifest.d.ts +1 -1
  493. package/dist/seed/manifest.js +0 -5
  494. package/dist/seed/notifications-seed-data.js +20 -1
  495. package/dist/seed/orders-seed-data.js +2 -2
  496. package/dist/seed/products-art-seed-data.js +16 -16
  497. package/dist/seed/products-auctions-seed-data.js +34 -34
  498. package/dist/seed/products-classifieds-seed-data.js +35 -37
  499. package/dist/seed/products-digital-codes-seed-data.js +20 -20
  500. package/dist/seed/products-live-items-seed-data.js +11 -11
  501. package/dist/seed/products-preorders-seed-data.js +13 -13
  502. package/dist/seed/products-prize-draws-seed-data.js +43 -43
  503. package/dist/seed/products-standard-seed-data.js +54 -54
  504. package/dist/seed/products-stickers-seed-data.js +15 -15
  505. package/dist/seed/reviews-seed-data.js +4 -4
  506. package/dist/seed/scammers-seed-data.js +3 -3
  507. package/dist/seed/site-settings-seed-data.js +11 -24
  508. package/dist/seed/store-extensions-seed-data.js +10 -10
  509. package/dist/seed/stores-seed-data.js +11 -11
  510. package/dist/seed/support-tickets-seed-data.d.ts +14 -0
  511. package/dist/seed/support-tickets-seed-data.js +20 -1
  512. package/dist/seed/users-seed-data.js +20 -20
  513. package/dist/server-entry.d.ts +0 -1
  514. package/dist/server-entry.js +0 -1
  515. package/dist/server.d.ts +9 -6
  516. package/dist/server.js +7 -13
  517. package/dist/styles.css +248 -68
  518. package/dist/tailwind-utilities.css +1 -1
  519. package/dist/tokens/themes/default-dark.js +2 -2
  520. package/dist/tokens/themes/default-light.js +2 -2
  521. package/dist/tokens/tokens.css +24 -4
  522. package/dist/ui/components/ActionRow.d.ts +50 -0
  523. package/dist/ui/components/ActionRow.js +53 -0
  524. package/dist/ui/components/ActionRow.style.css +145 -0
  525. package/dist/ui/components/Drawer.d.ts +10 -0
  526. package/dist/ui/components/Drawer.js +2 -1
  527. package/dist/ui/components/FilterDrawer.js +4 -1
  528. package/dist/ui/components/FilterDrawer.style.css +6 -12
  529. package/dist/ui/components/FormActionBar.js +14 -1
  530. package/dist/ui/components/FormActionBar.style.css +12 -11
  531. package/dist/ui/components/ListingLayout.js +8 -1
  532. package/dist/ui/components/ListingLayout.style.css +6 -13
  533. package/dist/ui/components/Modal.d.ts +15 -1
  534. package/dist/ui/components/Modal.js +17 -3
  535. package/dist/ui/components/QuickCreateModal.js +2 -2
  536. package/dist/ui/components/QuickCreateModal.style.css +78 -84
  537. package/dist/ui/components/RecordDetailModal.js +2 -2
  538. package/dist/ui/components/SideDrawer.d.ts +8 -0
  539. package/dist/ui/components/SideDrawer.js +2 -1
  540. package/dist/ui/components/SideDrawer.style.css +6 -3
  541. package/dist/ui/components/SideModal.d.ts +11 -1
  542. package/dist/ui/components/SideModal.js +3 -2
  543. package/dist/ui/components/SideModal.style.css +14 -0
  544. package/dist/ui/components/Typography.style.css +33 -24
  545. package/dist/ui/components/index.style.css +1 -0
  546. package/dist/ui/components/toolbar-collapse.js +1 -2
  547. package/dist/ui/forms/FormShell.style.css +260 -311
  548. package/dist/ui/index.d.ts +2 -0
  549. package/dist/ui/index.js +1 -0
  550. package/dist/utils/index.d.ts +0 -1
  551. package/dist/utils/index.js +0 -1
  552. package/dist/validation/index.d.ts +1 -1
  553. package/dist/validation/index.js +1 -1
  554. package/dist/validation/schemas.d.ts +0 -19
  555. package/dist/validation/schemas.js +8 -19
  556. package/package.json +1 -1
  557. package/scripts/seed-cli.mjs +3 -6
@@ -1,15 +1,16 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { normalizeError } from "../../../errors/normalize";
4
- import { useState } from "react";
4
+ import React, { useState } from "react";
5
5
  import { z } from "zod";
6
- import { Alert, Button, Div, Heading, Row, SiteMark, Stack, Text, useToast } from "../../../ui";
7
- import { Form } from "../../../ui/components/Form";
6
+ import { Alert, Div, Heading, Row, SiteMark, Stack, Text } from "../../../ui";
8
7
  import { FieldInput } from "../../../ui/forms/FieldInput";
9
8
  import { FieldCheckbox } from "../../../ui/forms/FieldCheckbox";
10
9
  import { applyZodIssues } from "../../../ui/forms/apply-zod-issues";
10
+ import { FormShellContext, useFormShellState } from "../../../ui/forms/FormShell";
11
11
  import { registerSchema, registerPasswordSchema } from "../schemas";
12
12
  import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
13
+ import { SectionForm, useSectionFormNav } from "../../shell/SectionForm";
13
14
  const registerClientSchema = registerSchema.extend({
14
15
  displayName: z.string().min(1, "Enter your name"),
15
16
  confirmPassword: registerPasswordSchema,
@@ -20,8 +21,35 @@ const registerClientSchema = registerSchema.extend({
20
21
  message: "Passwords do not match",
21
22
  path: ["confirmPassword"],
22
23
  });
24
+ /*
25
+ * Hand-authored rather than derived from the schema, which is the exception
26
+ * here and not the rule.
27
+ *
28
+ * Two reasons the generator is the wrong tool for this one form. `labels` is a
29
+ * PUBLIC prop of a published component — every string below is consumer
30
+ * overridable, and moving them into schema annotations would silently drop that
31
+ * override for anyone outside this repo. And `renderPasswordStrength` /
32
+ * `renderTermsLink` are POSITIONAL slots: a meter that must sit under the
33
+ * password box, a terms line that must sit under the checkbox. The generator
34
+ * places fields, not arbitrary nodes between them.
35
+ *
36
+ * One section, `required: true` — so it renders open, without a collapse
37
+ * control, i.e. exactly the flat list it has always been. A five-field signup
38
+ * form has nothing to group; what it gains here is the error→section
39
+ * attribution that makes `<FormErrorSummary>` able to jump to a field.
40
+ */
41
+ function buildSections(labels, renderPasswordStrength, renderTermsLink) {
42
+ return [
43
+ {
44
+ id: "account",
45
+ label: labels.title ?? "Create Account",
46
+ required: true,
47
+ fields: ["displayName", "email", "password", "confirmPassword", "acceptTerms"],
48
+ render: ({ values, onChange }) => (_jsxs(_Fragment, { children: [_jsx(FieldInput, { name: "displayName", label: labels.displayNameLabel ?? "Full name", type: "text", autoComplete: "name", required: true, placeholder: labels.displayNamePlaceholder ?? "Your name", value: values.displayName, onChange: (v) => onChange({ displayName: v }) }), _jsx(FieldInput, { name: "email", label: labels.emailLabel ?? "Email address", type: "email", autoComplete: "username", required: true, placeholder: labels.emailPlaceholder ?? "you@example.com", value: values.email, onChange: (v) => onChange({ email: v }) }), _jsxs(Div, { children: [_jsx(FieldInput, { name: "password", label: labels.passwordLabel ?? "Password", type: "password", autoComplete: "new-password", required: true, placeholder: labels.passwordPlaceholder ?? "••••••••", value: values.password, onChange: (v) => onChange({ password: v }) }), renderPasswordStrength?.(values.password)] }), _jsx(FieldInput, { name: "confirmPassword", label: labels.confirmPasswordLabel ?? "Confirm password", type: "password", autoComplete: "new-password", required: true, placeholder: labels.confirmPasswordPlaceholder ?? "••••••••", value: values.confirmPassword, onChange: (v) => onChange({ confirmPassword: v }) }), _jsx(FieldCheckbox, { name: "acceptTerms", label: `${labels.acceptTermsLabel ?? "I accept the terms"}`, checked: values.acceptTerms, onChange: (c) => onChange({ acceptTerms: c }) }), renderTermsLink && (_jsx(Text, { size: "xs", variant: "secondary", children: renderTermsLink() }))] })),
49
+ },
50
+ ];
51
+ }
23
52
  export function RegisterForm({ onSubmit, isLoading = false, error, success, labels = {}, renderSocialButtons, renderLoginLink, renderTermsLink, renderPasswordStrength, className = "", }) {
24
- const { showToast } = useToast();
25
53
  const [values, setValues] = useState({
26
54
  email: "",
27
55
  password: "",
@@ -29,19 +57,34 @@ export function RegisterForm({ onSubmit, isLoading = false, error, success, labe
29
57
  displayName: "",
30
58
  acceptTerms: false,
31
59
  });
32
- return (_jsx(Row, { className: `min-h-[60vh] ${className}`, align: "center", justify: "center", padding: "x-md", children: _jsxs(Stack, { className: "max-w-md w-full", gap: "lg", children: [_jsxs(Div, { className: "text-center", children: [_jsx(Row, { justify: "center", className: "mb-3", children: _jsx(SiteMark, { size: "lg" }) }), _jsx(Heading, { level: 1, className: "font-extrabold", size: "3xl", children: labels.title ?? "Create Account" }), (labels.subtitle || renderLoginLink) && (_jsxs(Text, { className: "mt-2 text-[var(--appkit-color-text-muted)]", size: "sm", children: [labels.subtitle ?? "Already have an account?", " ", renderLoginLink?.()] }))] }), success && (_jsx(Alert, { variant: "success", compact: true, children: success })), error && (_jsx(Alert, { variant: "error", compact: true, children: error })), _jsx(Form, { schema: registerClientSchema, noValidate: true, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsx(FormErrorSummary, {}), _jsx(FieldInput, { name: "displayName", label: labels.displayNameLabel ?? "Full name", type: "text", autoComplete: "name", required: true, placeholder: labels.displayNamePlaceholder ?? "Your name", value: values.displayName, onChange: (v) => setValues({ ...values, displayName: v }) }), _jsx(FieldInput, { name: "email", label: labels.emailLabel ?? "Email address", type: "email", autoComplete: "username", required: true, placeholder: labels.emailPlaceholder ?? "you@example.com", value: values.email, onChange: (v) => setValues({ ...values, email: v }) }), _jsxs(Div, { children: [_jsx(FieldInput, { name: "password", label: labels.passwordLabel ?? "Password", type: "password", autoComplete: "new-password", required: true, placeholder: labels.passwordPlaceholder ?? "••••••••", value: values.password, onChange: (v) => setValues({ ...values, password: v }) }), renderPasswordStrength?.(values.password)] }), _jsx(FieldInput, { name: "confirmPassword", label: labels.confirmPasswordLabel ?? "Confirm password", type: "password", autoComplete: "new-password", required: true, placeholder: labels.confirmPasswordPlaceholder ?? "••••••••", value: values.confirmPassword, onChange: (v) => setValues({ ...values, confirmPassword: v }) }), _jsx(FieldCheckbox, { name: "acceptTerms", label: `${labels.acceptTermsLabel ?? "I accept the terms"}`, checked: values.acceptTerms, onChange: (c) => setValues({ ...values, acceptTerms: c }) }), renderTermsLink && (_jsx(Text, { size: "xs", variant: "secondary", children: renderTermsLink() })), _jsx(Button, { type: "submit", isLoading: isLoading, disabled: isLoading, className: "w-full", onClick: async () => {
33
- clearErrors();
34
- const parsed = registerClientSchema.safeParse(values);
35
- if (!parsed.success)
36
- return applyZodIssues(parsed.error.issues, setFieldError);
37
- try {
38
- await onSubmit(values);
39
- }
40
- catch (err) {
41
- void normalizeError(err);
42
- showToast(err instanceof Error ? err.message : "Registration failed", "error");
43
- }
44
- }, children: isLoading
45
- ? (labels.submittingLabel ?? "Creating account…")
46
- : (labels.submitLabel ?? "Create account") })] })) }), renderSocialButtons?.()] }) }));
60
+ const sections = React.useMemo(() => buildSections(labels, renderPasswordStrength, renderTermsLink), [labels, renderPasswordStrength, renderTermsLink]);
61
+ const nav = useSectionFormNav(sections, values, { scope: "auth:register" });
62
+ const { shellCtx, setFieldError, clearErrors } = useFormShellState(registerClientSchema, {
63
+ sections: nav.sectionMeta,
64
+ onGoToSection: nav.goToSection,
65
+ fieldToSectionIndex: nav.fieldToSectionIndex,
66
+ });
67
+ const handleSubmit = async () => {
68
+ clearErrors();
69
+ const parsed = registerClientSchema.safeParse(values);
70
+ if (!parsed.success) {
71
+ applyZodIssues(parsed.error.issues, setFieldError);
72
+ return;
73
+ }
74
+ try {
75
+ await onSubmit(values);
76
+ }
77
+ catch (err) {
78
+ /*
79
+ * On a field, not a toast. "Registration failed" told the user nothing
80
+ * and discarded whatever the caller actually objected to — a taken
81
+ * email, a rejected password — which is the one thing they can act on.
82
+ */
83
+ const normalized = normalizeError(err);
84
+ setFieldError("email", normalized.message || "Registration failed");
85
+ }
86
+ };
87
+ return (_jsx(Row, { className: `min-h-[60vh] ${className}`, align: "center", justify: "center", padding: "x-md", children: _jsxs(Stack, { className: "max-w-md w-full", gap: "lg", children: [_jsxs(Div, { className: "text-center", children: [_jsx(Row, { justify: "center", className: "mb-3", children: _jsx(SiteMark, { size: "lg" }) }), _jsx(Heading, { level: 1, className: "font-extrabold", size: "3xl", children: labels.title ?? "Create Account" }), (labels.subtitle || renderLoginLink) && (_jsxs(Text, { className: "mt-2 text-[var(--appkit-color-text-muted)]", size: "sm", children: [labels.subtitle ?? "Already have an account?", " ", renderLoginLink?.()] }))] }), success && (_jsx(Alert, { variant: "success", compact: true, children: success })), error && (_jsx(Alert, { variant: "error", compact: true, children: error })), _jsxs(FormShellContext.Provider, { value: shellCtx, children: [_jsx(FormErrorSummary, {}), _jsx(SectionForm, { sections: sections, values: values, onChange: (partial) => setValues((prev) => Object.assign({}, prev, partial)), onSubmit: handleSubmit, schema: registerClientSchema, openIds: nav.openIds, onOpenChange: nav.setOpenIds, isLoading: isLoading, submitLabel: isLoading
88
+ ? (labels.submittingLabel ?? "Creating account…")
89
+ : (labels.submitLabel ?? "Create account") })] }), renderSocialButtons?.()] }) }));
47
90
  }
@@ -30,7 +30,7 @@ export function ResetPasswordView({ oobCode, onSubmit, isLoading = false, error,
30
30
  }
31
31
  catch (err) {
32
32
  void normalizeError(err);
33
- showToast(err instanceof Error ? err.message : "Password reset failed", "error");
33
+ showToast("Password reset failed", "error");
34
34
  }
35
35
  };
36
36
  }
@@ -315,6 +315,9 @@ export function useRegister(options) {
315
315
  // fail registration — the user can re-request verification later.
316
316
  try {
317
317
  await authProvider.sendEmailVerification();
318
+ // Non-fatal by design: the account already exists, and the user can
319
+ // re-request verification from Settings at any time. Firebase surfaces
320
+ // its own send failures; failing registration here would be worse.
318
321
  }
319
322
  catch (err) {
320
323
  void normalizeError(err);
@@ -11,7 +11,33 @@
11
11
  * Permission naming: admin:resource:action
12
12
  * Ban action naming: plain verb phrase — write_reviews, place_bids, etc.
13
13
  */
14
- export type Permission = "admin:dashboard:view" | "admin:users:read" | "admin:users:write" | "admin:users:delete" | "admin:user-bans:read" | "admin:user-bans:write" | "admin:products:read" | "admin:products:write" | "admin:products:delete" | "admin:orders:read" | "admin:orders:write" | "admin:returns:read" | "admin:returns:write" | "admin:stores:read" | "admin:stores:write" | "admin:store-addresses:read" | "admin:addresses:read" | "admin:addresses:write" | "admin:analytics:view" | "admin:payouts:read" | "admin:payouts:write" | "admin:audit-log:read" | "admin:categories:read" | "admin:categories:write" | "admin:categories:delete" | "admin:brands:read" | "admin:brands:write" | "admin:brands:delete" | "admin:coupons:read" | "admin:coupons:write" | "admin:coupons:delete" | "admin:deals:read" | "admin:deals:write" | "admin:featured:read" | "admin:featured:write" | "admin:reviews:read" | "admin:reviews:write" | "admin:reviews:delete" | "admin:blog:read" | "admin:blog:write" | "admin:blog:delete" | "admin:blog:publish" | "admin:bids:read" | "admin:bids:write" | "admin:offers:read" | "admin:offers:write" | "admin:media:read" | "admin:media:write" | "admin:media:delete" | "admin:site:read" | "admin:site:write" | "admin:settings:write" | "admin:navigation:read" | "admin:navigation:write" | "admin:sections:read" | "admin:sections:write" | "admin:carousel:read" | "admin:carousel:write" | "admin:carousel:delete" | "admin:ads:read" | "admin:ads:write" | "admin:ads:delete" | "admin:faqs:read" | "admin:faqs:write" | "admin:faqs:delete" | "admin:tester-checklist:read" | "admin:tester-checklist:write" | "admin:tester-checklist:delete" | "admin:tester-feedback:read" | "admin:tester-feedback:write" | "admin:newsletter:read" | "admin:newsletter:write" | "admin:contact:read" | "admin:events:read" | "admin:events:write" | "admin:events:delete" | "admin:event-entries:read" | "admin:event-entries:write" | "admin:support-tickets:read" | "admin:support-tickets:write" | "admin:shipments:read" | "admin:shipments:write" | "admin:catalogue:read" | "admin:catalogue:write" | "admin:grouped-listings:read" | "admin:grouped-listings:write" | "admin:support-tickets:assign" | "admin:support-tickets:close" | "admin:scammers:read" | "admin:scammers:write" | "admin:scammers:verify" | "admin:scammers:delete" | "admin:sessions:read" | "admin:sessions:delete" | "admin:notifications:read" | "admin:notifications:write" | "admin:carts:read" | "admin:wishlists:read" | "admin:feature-flags:read" | "admin:feature-flags:write" | "admin:copilot:view" | "admin:team:read" | "admin:team:write" | "admin:maintenance:view-server-errors" | "admin:maintenance:view-client-errors" | "admin:maintenance:view-function-errors" | "admin:maintenance:view-cloud-logs" | "admin:maintenance:view-payment-rollbacks" | "admin:maintenance:run-analysis" | "admin:maintenance:purge-errors";
14
+ export type Permission = "admin:dashboard:view" | "admin:users:read" | "admin:users:write" | "admin:users:delete" | "admin:user-bans:read" | "admin:user-bans:write" | "admin:products:read" | "admin:products:write" | "admin:products:delete" | "admin:orders:read" | "admin:orders:write" | "admin:returns:read" | "admin:returns:write" | "admin:stores:read" | "admin:stores:write" | "admin:store-addresses:read" | "admin:addresses:read" | "admin:addresses:write" | "admin:analytics:view" | "admin:payouts:read" | "admin:payouts:write" | "admin:audit-log:read" | "admin:categories:read" | "admin:categories:write" | "admin:categories:delete" | "admin:brands:read" | "admin:brands:write" | "admin:brands:delete" | "admin:coupons:read" | "admin:coupons:write" | "admin:coupons:delete" | "admin:deals:read" | "admin:deals:write" | "admin:featured:read" | "admin:featured:write" | "admin:reviews:read" | "admin:reviews:write" | "admin:reviews:delete" | "admin:blog:read" | "admin:blog:write" | "admin:blog:delete" | "admin:blog:publish" | "admin:bids:read" | "admin:bids:write" | "admin:offers:read" | "admin:offers:write" | "admin:media:read" | "admin:media:write" | "admin:media:delete" | "admin:site:read" | "admin:site:write" | "admin:settings:write" | "admin:navigation:read" | "admin:navigation:write" | "admin:sections:read" | "admin:sections:write" | "admin:carousel:read" | "admin:carousel:write" | "admin:carousel:delete" | "admin:ads:read" | "admin:ads:write" | "admin:ads:delete" | "admin:faqs:read" | "admin:faqs:write" | "admin:faqs:delete" | "admin:tester-checklist:read" | "admin:tester-checklist:write" | "admin:tester-checklist:delete" | "admin:tester-feedback:read" | "admin:tester-feedback:write" | "admin:newsletter:read" | "admin:newsletter:write" | "admin:contact:read"
15
+ /** Destructive/mutating contact-submission actions. Added 2026-08-29 —
16
+ * `DELETE /api/admin/contact-submissions/[id]` declared `admin:contact:delete`,
17
+ * which is not a member of this union and therefore could never be granted. */
18
+ | "admin:contact:write" | "admin:moderation:read" | "admin:roles:read"
19
+ /**
20
+ * Skip OTP + payment when placing an order. Replaces
21
+ * `siteSettings.featureFlags.adminCheckoutBypass`: a capability granted to a
22
+ * person belongs in the permission system, not in a site-wide toggle that is
23
+ * either on for every admin or off for all of them.
24
+ *
25
+ * Named `admin:checkout:bypass` rather than the bare `checkout:bypass` so it
26
+ * matches the `admin:resource:action` convention every other member follows
27
+ * and is picked up by PERMISSION_DOMAINS prefix matching.
28
+ */
29
+ | "admin:checkout:bypass"
30
+ /**
31
+ * Lets a `tester` reach `/admin/**` with real read/write access.
32
+ *
33
+ * Replaces the `canTestAdmin` boolean. Not a second role: the hierarchy is
34
+ * linear, so there is no level meaning "seller-level, plus read admin" that
35
+ * would not also outrank moderator and employee. Granted through the same
36
+ * `permissions[]` array employees already use, so there is one grant
37
+ * mechanism — and revoking it is a permission change, which the admin audit
38
+ * log already records.
39
+ */
40
+ | "tester:admin-surfaces" | "admin:events:read" | "admin:events:write" | "admin:events:delete" | "admin:event-entries:read" | "admin:event-entries:write" | "admin:support-tickets:read" | "admin:support-tickets:write" | "admin:shipments:read" | "admin:shipments:write" | "admin:catalogue:read" | "admin:catalogue:write" | "admin:grouped-listings:read" | "admin:grouped-listings:write" | "admin:support-tickets:assign" | "admin:support-tickets:close" | "admin:scammers:read" | "admin:scammers:write" | "admin:scammers:verify" | "admin:scammers:delete" | "admin:sessions:read" | "admin:sessions:delete" | "admin:notifications:read" | "admin:notifications:write" | "admin:carts:read" | "admin:wishlists:read" | "admin:feature-flags:read" | "admin:feature-flags:write" | "admin:copilot:view" | "admin:team:read" | "admin:team:write" | "admin:maintenance:view-server-errors" | "admin:maintenance:view-client-errors" | "admin:maintenance:view-function-errors" | "admin:maintenance:view-cloud-logs" | "admin:maintenance:view-payment-rollbacks" | "admin:maintenance:run-analysis" | "admin:maintenance:purge-errors";
15
41
  /**
16
42
  * Granular user actions that can be soft-banned individually.
17
43
  * A user may have multiple simultaneous soft bans of different types.
@@ -13,22 +13,90 @@ type RoleCarrier = {
13
13
  role?: string | null;
14
14
  } | null | undefined;
15
15
  export declare const isAdminUser: (input: RoleCarrier) => boolean;
16
+ /**
17
+ * True for sellers AND testers.
18
+ *
19
+ * A QA tester owns a real store — `becomeSeller`/`createStore` auto-approve it —
20
+ * so every seller-facing surface must admit them. Making `tester` a peer role
21
+ * of `seller` rather than a boolean is what removed the `isTester` flag; this
22
+ * predicate is where that peerage is expressed.
23
+ */
16
24
  export declare const isSellerUser: (input: RoleCarrier) => boolean;
25
+ /** Strictly the `seller` role — for the rare case that must EXCLUDE testers. */
26
+ export declare const isStrictSellerUser: (input: RoleCarrier) => boolean;
17
27
  export declare const isModeratorUser: (input: RoleCarrier) => boolean;
18
28
  export declare const isEmployeeUser: (input: RoleCarrier) => boolean;
19
29
  export declare const isBuyerUser: (input: RoleCarrier) => boolean;
20
30
  /**
21
- * True for real admins OR a tester explicitly flagged to test admin areas
22
- * (`isTester && canTestAdmin`). Used at the two RBAC chokepoints — the API route
23
- * guard (`createRouteHandler`) and the admin layout guard (`makeAdminSectionLayout`)
24
- * — so a flagged tester gets real read/write access to `/admin/**` without changing
25
- * their `role`. Callers must supply `isTester`/`canTestAdmin` from a live Firestore
26
- * read; these are never present in the session-cookie JWT claims (only `role` is).
31
+ * ── Tester identity: role first, legacy booleans second ──────────────────────
32
+ *
33
+ * `isTester` / `canTestAdmin` are being replaced by the `tester` role and the
34
+ * `tester:admin-surfaces` permission. Both predicates below read the NEW shape
35
+ * first and fall back to the OLD one, which is what makes the code safe to ship
36
+ * before the data migration: a tester whose document still says
37
+ * `{ role: "seller", isTester: true }` keeps working unchanged, and one already
38
+ * migrated to `{ role: "tester" }` works too.
39
+ *
40
+ * ⛔ Do NOT delete the fallbacks until the backfill has run and been verified.
41
+ * Removing them early locks every existing tester out of the Tester Hub and out
42
+ * of the auto-approved store the QA program depends on.
27
43
  */
28
- type EffectiveAdminCarrier = {
44
+ type TesterCarrier = {
29
45
  role?: string | null;
30
46
  isTester?: boolean;
31
47
  canTestAdmin?: boolean;
48
+ permissions?: readonly string[] | null;
32
49
  } | null | undefined;
50
+ /** The `tester` role, or the legacy `isTester` flag. */
51
+ export declare const isTesterUser: (input: TesterCarrier) => boolean;
52
+ /**
53
+ * May this tester reach `/admin/**`?
54
+ *
55
+ * The `tester:admin-surfaces` permission, or the legacy `canTestAdmin` flag.
56
+ * Granted through the same `permissions[]` array employees already use, so it
57
+ * is one grant mechanism rather than a bespoke boolean — and revoking it is a
58
+ * permission change, which the admin audit log already records.
59
+ */
60
+ export declare const canTestAdminSurfaces: (input: TesterCarrier) => boolean;
61
+ /**
62
+ * True for real admins OR a tester cleared for admin surfaces. Used at the two
63
+ * RBAC chokepoints — the API route guard (`createRouteHandler`) and the admin
64
+ * layout guard (`makeAdminSectionLayout`) — so a cleared tester gets real
65
+ * read/write access to `/admin/**` without holding the admin role.
66
+ *
67
+ * Callers must supply the role/permissions from a live Firestore read; none of
68
+ * these are present in the session-cookie JWT claims (only `role` is).
69
+ */
70
+ type EffectiveAdminCarrier = TesterCarrier;
33
71
  export declare const isEffectiveAdminUser: (input: EffectiveAdminCarrier) => boolean;
72
+ /**
73
+ * ── Is this account blocked? ─────────────────────────────────────────────────
74
+ *
75
+ * 🛑 There are TWO ban fields on `UserDocument` and, until 2026-08-29, no ban
76
+ * path wrote the one every guard reads.
77
+ *
78
+ * `disabled` — required, and read by EVERY server-side guard:
79
+ * auth-server, api-handler, authorization.requireActiveUser,
80
+ * Guards.requireActiveAccount, SessionContext.
81
+ * Its only writers, `userRepository.disable()/enable()`, had
82
+ * ZERO callers.
83
+ * `isDisabled` — optional, and what `hardBanCascade` and the bulk-suspend
84
+ * route actually set, alongside the Firebase Auth record.
85
+ *
86
+ * The consequence was that **no ban terminated an existing session.** A hard
87
+ * ban set Firebase Auth `disabled: true`, which blocks a fresh login — and
88
+ * nothing else. Every authenticated request from a session cookie the user
89
+ * already held kept passing, because each guard consulted a Firestore field
90
+ * the ban never touched. Silent, and invisible to anyone testing a ban by
91
+ * logging out first.
92
+ *
93
+ * This predicate reads BOTH so already-banned accounts are enforced from the
94
+ * moment it ships, without waiting on a backfill. Every write path now sets
95
+ * both fields too; `isDisabled` is removed once the data is reconciled.
96
+ */
97
+ type BanCarrier = {
98
+ disabled?: boolean | null;
99
+ isDisabled?: boolean | null;
100
+ } | null | undefined;
101
+ export declare const isAccountDisabled: (input: BanCarrier) => boolean;
34
102
  export {};
@@ -2,8 +2,34 @@ function normalizeRole(input) {
2
2
  return (input?.role ?? "user");
3
3
  }
4
4
  export const isAdminUser = (input) => normalizeRole(input) === "admin";
5
- export const isSellerUser = (input) => normalizeRole(input) === "seller";
5
+ /**
6
+ * True for sellers AND testers.
7
+ *
8
+ * A QA tester owns a real store — `becomeSeller`/`createStore` auto-approve it —
9
+ * so every seller-facing surface must admit them. Making `tester` a peer role
10
+ * of `seller` rather than a boolean is what removed the `isTester` flag; this
11
+ * predicate is where that peerage is expressed.
12
+ */
13
+ export const isSellerUser = (input) => {
14
+ const role = normalizeRole(input);
15
+ return role === "seller" || role === "tester";
16
+ };
17
+ /** Strictly the `seller` role — for the rare case that must EXCLUDE testers. */
18
+ export const isStrictSellerUser = (input) => normalizeRole(input) === "seller";
6
19
  export const isModeratorUser = (input) => normalizeRole(input) === "moderator";
7
20
  export const isEmployeeUser = (input) => normalizeRole(input) === "employee";
8
21
  export const isBuyerUser = (input) => normalizeRole(input) === "user";
9
- export const isEffectiveAdminUser = (input) => isAdminUser(input) || Boolean(input?.isTester && input?.canTestAdmin);
22
+ /** The `tester` role, or the legacy `isTester` flag. */
23
+ export const isTesterUser = (input) => normalizeRole(input) === "tester" || input?.isTester === true;
24
+ /**
25
+ * May this tester reach `/admin/**`?
26
+ *
27
+ * The `tester:admin-surfaces` permission, or the legacy `canTestAdmin` flag.
28
+ * Granted through the same `permissions[]` array employees already use, so it
29
+ * is one grant mechanism rather than a bespoke boolean — and revoking it is a
30
+ * permission change, which the admin audit log already records.
31
+ */
32
+ export const canTestAdminSurfaces = (input) => Boolean(input?.permissions?.includes("tester:admin-surfaces")) ||
33
+ input?.canTestAdmin === true;
34
+ export const isEffectiveAdminUser = (input) => isAdminUser(input) || (isTesterUser(input) && canTestAdminSurfaces(input));
35
+ export const isAccountDisabled = (input) => input?.disabled === true || input?.isDisabled === true;
@@ -140,6 +140,28 @@ export interface UserDocument extends BaseDocument {
140
140
  * stay unique). Absent/missing IDs default to collapsed for a section the
141
141
  * user has never interacted with. See useCollapsedSections. */
142
142
  collapsedSections?: string[];
143
+ /**
144
+ * Which sections are OPEN, namespaced per surface: `scope -> openIds[]`.
145
+ *
146
+ * Supersedes `collapsedSections`, and the polarity flip is the point.
147
+ * Storing what is COLLAPSED cannot express "section 1 open by default"
148
+ * without a sentinel, and an unseen scope has to be guessed at; storing
149
+ * what is OPEN makes a never-seen scope default correctly *by absence*.
150
+ *
151
+ * Namespaced because the flat list was global: every page's ids shared one
152
+ * array and were kept apart only by a naming convention. The scope key is
153
+ * `"{portal}:{surface}"` — deliberately NOT a hash of the section ids,
154
+ * which would change the moment a section is added and silently reset
155
+ * every user's layout.
156
+ *
157
+ * `collapsedSections` is still written during the migration window; see
158
+ * `useSectionState`'s one-way dual-read.
159
+ */
160
+ sectionState?: Record<string, string[]>;
161
+ /** Preferred density for list surfaces that offer a view switch.
162
+ * Was written by the route, the API type and useDataViewMode while being
163
+ * absent from this interface — declared 2026-08-29. */
164
+ dataViewMode?: "table" | "grid" | "list";
143
165
  /** Which side CTA panels/drawers/sidebars dock to and where close buttons
144
166
  * sit. Absent/"right" = default (right-docked). "left" = left-hand mode. */
145
167
  handMode?: "left" | "right";
@@ -26,9 +26,9 @@ export declare const loginSchema: z.ZodObject<{
26
26
  */
27
27
  export declare const registerPasswordSchema: z.ZodString;
28
28
  export declare const registerSchema: z.ZodObject<{
29
+ displayName: z.ZodOptional<z.ZodString>;
29
30
  email: z.ZodString;
30
31
  password: z.ZodString;
31
- displayName: z.ZodOptional<z.ZodString>;
32
32
  }, "strip", z.ZodTypeAny, {
33
33
  email: string;
34
34
  password: string;
@@ -1,7 +1,20 @@
1
1
  export * from "./firestore";
2
2
  import { z } from "zod";
3
3
  import { ERROR_MESSAGES } from "../../../errors/messages";
4
+ import { annotate } from "../../shell/field-ui-meta";
4
5
  // --- Form schemas -------------------------------------------------------------
6
+ /*
7
+ * The `annotate()` calls below add UI metadata to a WeakMap keyed on the schema
8
+ * object; they return the same schema and change nothing about parsing. That is
9
+ * what makes them safe on `registerSchema`, which the register ROUTE also
10
+ * parses — the server never reads the registry.
11
+ *
12
+ * Every auth form is a single required section. That is not a section in any
13
+ * interesting sense, and it is not meant to be: `sectionRequired` renders the
14
+ * panel open and non-collapsible, so the page looks exactly as it does today
15
+ * and gains the pinned mobile action bar, which is the whole reason to put a
16
+ * short form through <SectionForm> at all.
17
+ */
5
18
  /**
6
19
  * Login form schema — use with react-hook-form + zodResolver.
7
20
  *
@@ -10,8 +23,13 @@ import { ERROR_MESSAGES } from "../../../errors/messages";
10
23
  * const form = useForm({ resolver: zodResolver(loginSchema) });
11
24
  */
12
25
  export const loginSchema = z.object({
13
- email: z.string().email(),
14
- password: z.string().min(6),
26
+ email: annotate(z.string().email(), {
27
+ section: "basics", sectionLabel: "Sign in", sectionRequired: true,
28
+ order: 1, row: "full", inputType: "email", label: "Email",
29
+ }),
30
+ password: annotate(z.string().min(6), {
31
+ section: "basics", order: 2, row: "full", inputType: "password", label: "Password",
32
+ }),
15
33
  });
16
34
  /**
17
35
  * Matches the server-side complexity rule enforced in
@@ -27,16 +45,34 @@ export const registerPasswordSchema = z
27
45
  .regex(/[a-z]/, ERROR_MESSAGES.PASSWORD.NO_LOWERCASE)
28
46
  .regex(/[0-9]/, ERROR_MESSAGES.PASSWORD.NO_NUMBER);
29
47
  export const registerSchema = z.object({
30
- email: z.string().email(),
31
- password: registerPasswordSchema,
32
- displayName: z.string().min(1).optional(),
48
+ displayName: annotate(z.string().min(1).optional(), {
49
+ section: "basics", sectionLabel: "Create your account", sectionRequired: true,
50
+ order: 1, row: "full", label: "Display name",
51
+ }),
52
+ email: annotate(z.string().email(), {
53
+ section: "basics", order: 2, row: "full", inputType: "email", label: "Email",
54
+ }),
55
+ password: annotate(registerPasswordSchema, {
56
+ section: "basics", order: 3, row: "full", inputType: "password", label: "Password",
57
+ help: "At least 8 characters, with an uppercase letter, a lowercase letter and a number.",
58
+ }),
33
59
  });
34
60
  export const forgotPasswordSchema = z.object({
35
- email: z.string().email(),
61
+ email: annotate(z.string().email(), {
62
+ section: "basics", sectionLabel: "Reset your password", sectionRequired: true,
63
+ order: 1, row: "full", inputType: "email", label: "Email",
64
+ }),
36
65
  });
37
66
  export const resetPasswordSchema = z.object({
38
- token: z.string().min(1),
39
- password: z.string().min(6),
67
+ // Carried in the reset link, never typed. `derived` keeps it out of the
68
+ // rendered form; the schema still requires it, because the SUBMIT needs it.
69
+ token: annotate(z.string().min(1), {
70
+ section: "basics", order: 1, derived: true, tier: "t2-server",
71
+ }),
72
+ password: annotate(z.string().min(6), {
73
+ section: "basics", sectionLabel: "Choose a new password", sectionRequired: true,
74
+ order: 2, row: "full", inputType: "password", label: "New password",
75
+ }),
40
76
  });
41
77
  // --- Auth user schema ---------------------------------------------------------
42
78
  export const userRoleSchema = z.enum([
@@ -1,4 +1,17 @@
1
- export type UserRole = "user" | "seller" | "moderator" | "employee" | "admin";
1
+ /**
2
+ * `tester` sits at the SAME level as `seller` (see ROLE_HIERARCHY in
3
+ * security/authorization.ts) because a QA tester owns a real store and must
4
+ * pass every seller gate — the Tester QA Program depends on it.
5
+ *
6
+ * It replaces the `isTester` boolean. The companion `canTestAdmin` boolean
7
+ * became the `tester:admin-surfaces` permission rather than a second role,
8
+ * because this hierarchy is LINEAR: there is no level that means
9
+ * "seller-level, plus read admin" without also meaning admin.
10
+ *
11
+ * Both booleans are still read during the migration window — see
12
+ * `isTesterUser` / `canTestAdminSurfaces` in ./role-predicates.
13
+ */
14
+ export type UserRole = "user" | "seller" | "tester" | "moderator" | "employee" | "admin";
2
15
  export interface AuthUser {
3
16
  id: string;
4
17
  email?: string;
@@ -25,7 +25,15 @@ export async function GET(request) {
25
25
  try {
26
26
  const url = new URL(request.url);
27
27
  const page = numParam(url, "page", 1);
28
- const perPage = numParam(url, "perPage", 20);
28
+ /*
29
+ * `pageSize` is canonical; `perPage` is still read because this is a public
30
+ * GET and a link built before 2026-08-31 carries the old spelling. Ignoring
31
+ * it would silently serve the default page size with a 200 — the shape of
32
+ * failure this codebase keeps paying for.
33
+ */
34
+ const pageSize = url.searchParams.has("perPage")
35
+ ? numParam(url, "perPage", 20)
36
+ : numParam(url, "pageSize", 20);
29
37
  const sort = param(url, "sort") ?? "-publishedAt";
30
38
  const parts = [];
31
39
  const category = param(url, "category");
@@ -52,7 +60,7 @@ export async function GET(request) {
52
60
  // `blogRepository.listAll` rather than the generic `db.getRepository(...)`:
53
61
  // only the feature repository knows how to push a searchTxt clause down and
54
62
  // AND-refine the remaining terms.
55
- const result = await blogRepository.listAll({ filters, sorts: sort, page, pageSize: perPage }, q ? { search: q } : undefined);
63
+ const result = await blogRepository.listAll({ filters, sorts: sort, page, pageSize }, q ? { search: q } : undefined);
56
64
  const meta = {
57
65
  total: result.total,
58
66
  page: result.page,
@@ -106,7 +106,7 @@ export function BlogIndexListing({ initialData }) {
106
106
  tags: table.get(TABLE_KEYS.TAGS) || undefined,
107
107
  sort: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT,
108
108
  page: table.getNumber(TABLE_KEYS.PAGE, 1),
109
- perPage: table.getNumber(TABLE_KEYS.PAGE_SIZE, PAGE_SIZE),
109
+ pageSize: table.getNumber(TABLE_KEYS.PAGE_SIZE, PAGE_SIZE),
110
110
  };
111
111
  const { posts, totalPages, isLoading } = useBlogPosts(params, { initialData });
112
112
  const currentPage = table.getNumber("page", 1);
@@ -11,8 +11,8 @@ export function useBlogPosts(params = {}, opts) {
11
11
  searchParams.set("tags", params.tags);
12
12
  if (params.page)
13
13
  searchParams.set("page", String(params.page));
14
- if (params.perPage)
15
- searchParams.set("perPage", String(params.perPage));
14
+ if (params.pageSize)
15
+ searchParams.set("pageSize", String(params.pageSize));
16
16
  if (params.sort)
17
17
  searchParams.set("sort", params.sort);
18
18
  if (params.featured !== undefined)
@@ -72,10 +72,12 @@ class BlogRepository extends BaseRepository {
72
72
  .collection(this.collection)
73
73
  .doc(id)
74
74
  .update({ [BLOG_POST_FIELDS.VIEWS]: increment(1) });
75
+ // Fire-and-forget analytics: a lost view increment changes nothing that
76
+ // renders and cannot usefully be retried, so it must never fail the blog
77
+ // post read that triggered it — the count is approximate by design.
75
78
  }
76
79
  catch (_err) {
77
80
  void normalizeError(_err);
78
- // Fire-and-forget by design.
79
81
  }
80
82
  }
81
83
  async findRelated(category, excludeId, limit = 3) {
@@ -458,6 +458,8 @@ export declare const blogListParamsSchema: z.ZodObject<{
458
458
  tags: z.ZodOptional<z.ZodString>;
459
459
  q: z.ZodOptional<z.ZodString>;
460
460
  page: z.ZodOptional<z.ZodNumber>;
461
+ pageSize: z.ZodOptional<z.ZodNumber>;
462
+ /** Accepted, never emitted — a link built before the 2026-08-31 rename. */
461
463
  perPage: z.ZodOptional<z.ZodNumber>;
462
464
  sort: z.ZodOptional<z.ZodString>;
463
465
  featured: z.ZodOptional<z.ZodBoolean>;
@@ -466,6 +468,7 @@ export declare const blogListParamsSchema: z.ZodObject<{
466
468
  page?: number | undefined;
467
469
  q?: string | undefined;
468
470
  perPage?: number | undefined;
471
+ pageSize?: number | undefined;
469
472
  category?: "news" | "tips" | "guides" | "updates" | "community" | undefined;
470
473
  featured?: boolean | undefined;
471
474
  tags?: string | undefined;
@@ -474,6 +477,7 @@ export declare const blogListParamsSchema: z.ZodObject<{
474
477
  page?: number | undefined;
475
478
  q?: string | undefined;
476
479
  perPage?: number | undefined;
480
+ pageSize?: number | undefined;
477
481
  category?: "news" | "tips" | "guides" | "updates" | "community" | undefined;
478
482
  featured?: boolean | undefined;
479
483
  tags?: string | undefined;
@@ -90,6 +90,8 @@ export const blogListParamsSchema = z.object({
90
90
  tags: z.string().optional(),
91
91
  q: z.string().optional(),
92
92
  page: z.coerce.number().optional(),
93
+ pageSize: z.coerce.number().optional(),
94
+ /** Accepted, never emitted — a link built before the 2026-08-31 rename. */
93
95
  perPage: z.coerce.number().optional(),
94
96
  sort: z.string().optional(),
95
97
  featured: z.coerce.boolean().optional(),
@@ -43,7 +43,7 @@ export interface BlogListParams {
43
43
  tags?: string;
44
44
  q?: string;
45
45
  page?: number;
46
- perPage?: number;
46
+ pageSize?: number;
47
47
  sort?: string;
48
48
  featured?: boolean;
49
49
  }
@@ -14,6 +14,7 @@ import { getDefaultCurrency } from "../../../core/baseline-resolver";
14
14
  import { CART_LANE, activeLane, canAddNewItems, } from "../../../_internal/shared/checkout/lanes";
15
15
  import { CART_MAX_ITEMS } from "../../../constants/limits";
16
16
  import { getExpandedDecrements, validateCartItemStock, } from "../../../_internal/server/features/checkout/bundle-expansion";
17
+ import { safeRead } from "../../../errors/safe-read";
17
18
  /**
18
19
  * Product documents don't always carry a denormalized `storeName` — the
19
20
  * create routes only set it when the caller explicitly passes one, so
@@ -26,7 +27,9 @@ import { getExpandedDecrements, validateCartItemStock, } from "../../../_interna
26
27
  async function resolveStoreName(storeId, candidate) {
27
28
  if (candidate)
28
29
  return candidate;
29
- const store = await storeRepository.findById(storeId).catch(() => null);
30
+ const store = await safeRead(() => storeRepository.findById(storeId), {
31
+ route: "/cart", key: "stores.findById", fallback: null,
32
+ });
30
33
  return store?.storeName ?? storeId;
31
34
  }
32
35
  /**
@@ -102,7 +105,9 @@ export async function addGroupLineToCart(userId, input) {
102
105
  const cart = await assertCanAddNewItems(userId);
103
106
  // One batched read for the whole selection — capped at 20 above, so well
104
107
  // inside the per-request Firestore budget.
105
- const docs = await Promise.all(members.map((m) => productRepository.findById(m.productId).catch(() => null)));
108
+ const docs = await Promise.all(members.map((m) => safeRead(() => productRepository.findById(m.productId), {
109
+ route: "/cart", key: "products.findById", fallback: null,
110
+ })));
106
111
  const productById = new Map(members.map((m, i) => [m.productId, docs[i]]));
107
112
  const { getListingRule } = await import("../../../_internal/shared/checkout/rules");
108
113
  const { ProductStatusValues } = await import("../../products/schemas/firestore");
@@ -209,14 +214,18 @@ export async function addGroupLineToCart(userId, input) {
209
214
  /** Is this product listed on that `groupedListings` document? */
210
215
  async function isGroupedListingMember(groupId, productId) {
211
216
  const { groupedListingsRepository } = await import("../../grouped/repository/grouped-listings.repository");
212
- const group = await groupedListingsRepository.findById(groupId).catch(() => null);
217
+ const group = await safeRead(() => groupedListingsRepository.findById(groupId), {
218
+ route: "/cart", key: "groupedListings.findById", fallback: null,
219
+ });
213
220
  return Boolean(group?.productIds?.includes(productId));
214
221
  }
215
222
  /** Display name for the line. Falls back to the parent product's group title. */
216
223
  async function resolveGroupTitle(groupId, groupSource, sample) {
217
224
  if (groupSource === "grouped-listing") {
218
225
  const { groupedListingsRepository } = await import("../../grouped/repository/grouped-listings.repository");
219
- const group = await groupedListingsRepository.findById(groupId).catch(() => null);
226
+ const group = await safeRead(() => groupedListingsRepository.findById(groupId), {
227
+ route: "/cart", key: "groupedListings.title", fallback: null,
228
+ });
220
229
  if (group?.title)
221
230
  return group.title;
222
231
  }