@mohasinac/appkit 4.42.18 → 4.44.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 (263) hide show
  1. package/dist/_internal/client/features/lottery/LotteryDetailView.js +22 -1
  2. package/dist/_internal/server/features/categories/adapters.d.ts +47 -0
  3. package/dist/_internal/server/features/categories/adapters.js +235 -0
  4. package/dist/_internal/server/features/checkout/actions.js +161 -22
  5. package/dist/_internal/server/features/checkout/expand-order-items.js +34 -0
  6. package/dist/_internal/server/features/orders/adapters.js +21 -0
  7. package/dist/_internal/server/features/products/list-public.d.ts +24 -0
  8. package/dist/_internal/server/features/products/list-public.js +10 -2
  9. package/dist/_internal/server/features/refunds/actions.js +1 -1
  10. package/dist/_internal/server/features/seo/sitemap.js +54 -15
  11. package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
  12. package/dist/_internal/server/features/stores/adapters.js +6 -0
  13. package/dist/_internal/server/functions/scheduled.d.ts +4 -2
  14. package/dist/_internal/server/functions/scheduled.js +64 -6
  15. package/dist/_internal/server/jobs/core/adminAnalytics.d.ts +0 -1
  16. package/dist/_internal/server/jobs/core/adminAnalytics.js +9 -1
  17. package/dist/_internal/server/jobs/core/analyticsAlertEvaluate.d.ts +47 -0
  18. package/dist/_internal/server/jobs/core/analyticsAlertEvaluate.js +468 -0
  19. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +14 -1
  20. package/dist/_internal/server/jobs/core/countersReconcile.js +24 -8
  21. package/dist/_internal/server/jobs/core/onCategoryWrite.js +59 -0
  22. package/dist/_internal/server/jobs/core/onProductWrite.js +31 -0
  23. package/dist/_internal/server/jobs/core/onReviewWrite.js +8 -0
  24. package/dist/_internal/server/jobs/core/onStoreWrite.d.ts +1 -1
  25. package/dist/_internal/server/jobs/core/onStoreWrite.js +8 -2
  26. package/dist/_internal/server/jobs/core/priceIndexRollup.d.ts +47 -0
  27. package/dist/_internal/server/jobs/core/priceIndexRollup.js +234 -0
  28. package/dist/_internal/server/jobs/core/revalidateNotify.d.ts +28 -0
  29. package/dist/_internal/server/jobs/core/revalidateNotify.js +125 -0
  30. package/dist/_internal/server/jobs/core/{testerSandboxCleanup.d.ts → testDataCleanup.d.ts} +3 -3
  31. package/dist/_internal/server/jobs/core/{testerSandboxCleanup.js → testDataCleanup.js} +3 -3
  32. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +5 -1
  33. package/dist/_internal/server/jobs/handlers/analyticsAlertEvaluate.d.ts +2 -0
  34. package/dist/_internal/server/jobs/handlers/analyticsAlertEvaluate.js +2 -0
  35. package/dist/_internal/server/jobs/handlers/index.d.ts +3 -1
  36. package/dist/_internal/server/jobs/handlers/index.js +3 -1
  37. package/dist/_internal/server/jobs/handlers/messages.d.ts +5 -2
  38. package/dist/_internal/server/jobs/handlers/messages.js +5 -2
  39. package/dist/_internal/server/jobs/handlers/priceIndexRollup.d.ts +2 -0
  40. package/dist/_internal/server/jobs/handlers/priceIndexRollup.js +2 -0
  41. package/dist/_internal/server/jobs/handlers/testDataCleanup.d.ts +2 -0
  42. package/dist/_internal/server/jobs/handlers/testDataCleanup.js +2 -0
  43. package/dist/_internal/shared/actions/action-registry.js +34 -0
  44. package/dist/_internal/shared/cache/revalidate-targets.d.ts +49 -0
  45. package/dist/_internal/shared/cache/revalidate-targets.js +157 -0
  46. package/dist/_internal/shared/checkout/order-math.d.ts +113 -3
  47. package/dist/_internal/shared/checkout/order-math.js +194 -7
  48. package/dist/_internal/shared/features/categories/price-guidance.d.ts +110 -0
  49. package/dist/_internal/shared/features/categories/price-guidance.js +136 -0
  50. package/dist/_internal/shared/features/site-settings/gst-registration.d.ts +90 -0
  51. package/dist/_internal/shared/features/site-settings/gst-registration.js +126 -0
  52. package/dist/_internal/shared/fees/calculator.d.ts +105 -0
  53. package/dist/_internal/shared/fees/calculator.js +143 -0
  54. package/dist/_internal/shared/listing-types/art/config.d.ts +0 -6
  55. package/dist/_internal/shared/listing-types/classified/config.d.ts +0 -6
  56. package/dist/_internal/shared/listing-types/digital-code/config.d.ts +0 -6
  57. package/dist/_internal/shared/listing-types/live/config.d.ts +0 -6
  58. package/dist/_internal/shared/listing-types/standard/config.d.ts +0 -6
  59. package/dist/_internal/shared/listing-types/stickers/config.d.ts +0 -6
  60. package/dist/_internal/shared/templating/placeholders.d.ts +70 -0
  61. package/dist/_internal/shared/templating/placeholders.js +73 -0
  62. package/dist/client.d.ts +2 -0
  63. package/dist/client.js +1 -0
  64. package/dist/constants/api-endpoints.d.ts +42 -0
  65. package/dist/constants/api-endpoints.js +14 -0
  66. package/dist/features/admin/actions/admin-actions.d.ts +1 -1
  67. package/dist/features/admin/actions/admin-actions.js +1 -1
  68. package/dist/features/admin/components/AdminCategoryEditorView.js +269 -3
  69. package/dist/features/admin/components/AdminProductEditorView.js +8 -1
  70. package/dist/features/admin/components/AdminProductsView.js +78 -7
  71. package/dist/features/admin/components/AdminSiteSettingsView.js +20 -1
  72. package/dist/features/admin/components/AdminTaxCodeEditorView.d.ts +10 -0
  73. package/dist/features/admin/components/AdminTaxCodeEditorView.js +160 -0
  74. package/dist/features/admin/components/AdminTaxCodesView.d.ts +13 -0
  75. package/dist/features/admin/components/AdminTaxCodesView.js +86 -0
  76. package/dist/features/admin/components/AdminTesterChecklistView.js +1 -1
  77. package/dist/features/admin/components/analytics/AdminTopProductsTable.js +1 -1
  78. package/dist/features/auctions/components/AuctionBottomActions.js +10 -1
  79. package/dist/features/auctions/components/AuctionDetailPageView.js +2 -1
  80. package/dist/features/auctions/components/AuctionLiveStatusBadge.d.ts +20 -0
  81. package/dist/features/auctions/components/AuctionLiveStatusBadge.js +21 -0
  82. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +1 -1
  83. package/dist/features/auctions/components/PlaceBidFormClient.js +20 -2
  84. package/dist/features/auctions/hooks/useIsAuctionEnded.d.ts +41 -0
  85. package/dist/features/auctions/hooks/useIsAuctionEnded.js +76 -0
  86. package/dist/features/auth/schemas/firestore.d.ts +0 -1
  87. package/dist/features/blog/api/route.js +16 -1
  88. package/dist/features/blog/schemas/firestore.d.ts +2 -2
  89. package/dist/features/catalogue/components/CatalogueItemEditorView.js +14 -3
  90. package/dist/features/categories/actions/category-actions.d.ts +2 -2
  91. package/dist/features/categories/actions/category-actions.js +22 -10
  92. package/dist/features/categories/api/[id]/description-template/route.d.ts +45 -0
  93. package/dist/features/categories/api/[id]/description-template/route.js +252 -0
  94. package/dist/features/categories/api/route.js +156 -11
  95. package/dist/features/categories/components/CategoriesIndexListing.d.ts +1 -3
  96. package/dist/features/categories/components/CategoriesIndexListing.js +1 -1
  97. package/dist/features/categories/components/CategoriesIndexPageView.d.ts +27 -1
  98. package/dist/features/categories/components/CategoriesIndexPageView.js +24 -33
  99. package/dist/features/categories/components/CategoryDetailPageView.js +43 -10
  100. package/dist/features/categories/components/CategoryTemplatePicker.d.ts +65 -0
  101. package/dist/features/categories/components/CategoryTemplatePicker.js +170 -0
  102. package/dist/features/categories/constants/listing-categories.d.ts +17 -0
  103. package/dist/features/categories/constants/listing-categories.js +53 -0
  104. package/dist/features/categories/hooks/useCategoryTree.js +28 -2
  105. package/dist/features/categories/repository/categories.repository.d.ts +73 -12
  106. package/dist/features/categories/repository/categories.repository.js +203 -42
  107. package/dist/features/categories/repository/price-index.repository.d.ts +115 -0
  108. package/dist/features/categories/repository/price-index.repository.js +85 -0
  109. package/dist/features/categories/schemas/category-content.d.ts +206 -0
  110. package/dist/features/categories/schemas/category-content.js +28 -0
  111. package/dist/features/categories/schemas/firestore.d.ts +74 -5
  112. package/dist/features/categories/schemas/firestore.js +74 -3
  113. package/dist/features/categories/schemas/index.d.ts +1047 -0
  114. package/dist/features/categories/schemas/index.js +96 -0
  115. package/dist/features/categories/server.d.ts +1 -0
  116. package/dist/features/categories/server.js +7 -0
  117. package/dist/features/categories/types/index.d.ts +9 -0
  118. package/dist/features/events/schemas/firestore.d.ts +2 -2
  119. package/dist/features/faq/schemas/firestore.d.ts +2 -1
  120. package/dist/features/faq/schemas/firestore.js +20 -8
  121. package/dist/features/faq/utils/getHomepageFaqsCached.d.ts +20 -0
  122. package/dist/features/faq/utils/getHomepageFaqsCached.js +22 -0
  123. package/dist/features/homepage/components/MarketplaceHomepageView.js +13 -3
  124. package/dist/features/homepage/lib/live-stats.js +18 -8
  125. package/dist/features/media/MediaImage.js +19 -2
  126. package/dist/features/orders/schemas/firestore.d.ts +70 -0
  127. package/dist/features/orders/types/index.d.ts +55 -0
  128. package/dist/features/products/actions/product-actions.d.ts +0 -9
  129. package/dist/features/products/actions/product-actions.js +25 -0
  130. package/dist/features/products/columns/index.d.ts +0 -1
  131. package/dist/features/products/columns/index.js +5 -1
  132. package/dist/features/products/components/ArtStickersListView.js +7 -1
  133. package/dist/features/products/components/PrizeDrawsSection.js +5 -0
  134. package/dist/features/products/components/ProductFilters.d.ts +0 -9
  135. package/dist/features/products/components/ProductForm.d.ts +27 -1
  136. package/dist/features/products/components/ProductForm.js +26 -12
  137. package/dist/features/products/components/ProductsIndexPageView.js +1 -1
  138. package/dist/features/products/constants/sieve.d.ts +0 -6
  139. package/dist/features/products/constants/sieve.js +35 -2
  140. package/dist/features/products/repository/products.repository.d.ts +115 -1
  141. package/dist/features/products/repository/products.repository.js +491 -39
  142. package/dist/features/products/schemas/firestore.d.ts +65 -5
  143. package/dist/features/products/schemas/firestore.js +9 -0
  144. package/dist/features/products/schemas/product-features.d.ts +140 -2
  145. package/dist/features/products/schemas/product-features.js +62 -1
  146. package/dist/features/promotions/repository/coupons.repository.js +6 -1
  147. package/dist/features/promotions/schemas/firestore.d.ts +44 -1
  148. package/dist/features/promotions/schemas/firestore.js +76 -2
  149. package/dist/features/reviews/repository/reviews.repository.d.ts +22 -0
  150. package/dist/features/reviews/repository/reviews.repository.js +35 -0
  151. package/dist/features/seller/actions/seller-actions.js +17 -2
  152. package/dist/features/seller/components/PrintCenterView.d.ts +3 -2
  153. package/dist/features/seller/components/PrintCenterView.js +3 -2
  154. package/dist/features/seller/components/SellerAnalyticsAlertsView.js +21 -11
  155. package/dist/features/seller/components/SellerOrdersView.js +6 -1
  156. package/dist/features/seller/components/SellerPayoutSettingsView.js +19 -3
  157. package/dist/features/seller/components/SellerProductShell.d.ts +50 -2
  158. package/dist/features/seller/components/SellerProductShell.js +101 -9
  159. package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +13 -1
  160. package/dist/features/seller/components/SellerProductsFilterDrawer.js +2 -2
  161. package/dist/features/seller/components/SellerProductsView.js +108 -3
  162. package/dist/features/seller/types/index.d.ts +17 -0
  163. package/dist/features/seller/utils/product-draft-mapping.d.ts +13 -0
  164. package/dist/features/seller/utils/product-draft-mapping.js +17 -0
  165. package/dist/features/store-extensions/constants/alert-metrics.d.ts +86 -0
  166. package/dist/features/store-extensions/constants/alert-metrics.js +142 -0
  167. package/dist/features/store-extensions/schemas/firestore.d.ts +19 -1
  168. package/dist/features/stores/schemas/firestore.d.ts +31 -2
  169. package/dist/features/tax-codes/repository/tax-codes.repository.d.ts +55 -0
  170. package/dist/features/tax-codes/repository/tax-codes.repository.js +131 -0
  171. package/dist/features/tax-codes/schemas/firestore.d.ts +125 -0
  172. package/dist/features/tax-codes/schemas/firestore.js +82 -0
  173. package/dist/features/tax-codes/schemas/tax-code-form.d.ts +63 -0
  174. package/dist/features/tax-codes/schemas/tax-code-form.js +152 -0
  175. package/dist/features/tax-codes/schemas/tax-codes.validators.d.ts +107 -0
  176. package/dist/features/tax-codes/schemas/tax-codes.validators.js +85 -0
  177. package/dist/features/tester/actions/index.d.ts +0 -1
  178. package/dist/features/tester/actions/index.js +0 -1
  179. package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +1 -1
  180. package/dist/features/tester/components/AdminTesterFeedbackListView.js +1 -1
  181. package/dist/features/tester/components/index.d.ts +0 -4
  182. package/dist/features/tester/components/index.js +0 -2
  183. package/dist/features/tester/repository/tester-checklist-item.repository.d.ts +1 -20
  184. package/dist/features/tester/repository/tester-checklist-item.repository.js +0 -52
  185. package/dist/features/tester/schemas/firestore.d.ts +0 -6
  186. package/dist/features/tester/seed-data/authored/admin__admin-detail-round-trips.js +3 -3
  187. package/dist/features/tester/seed-data/authored/admin__site-system.js +4 -5
  188. package/dist/features/tester/seed-data/authored/content-discovery__category-counts-and-rollup.js +2 -2
  189. package/dist/features/tester/seed-data/authored/index.js +0 -2
  190. package/dist/features/tester/seed-data/authored/page-wiring__reachability.js +0 -19
  191. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +10 -29
  192. package/dist/index.d.ts +21 -7
  193. package/dist/index.js +53 -7
  194. package/dist/next/routing/route-map.d.ts +2 -4
  195. package/dist/next/routing/route-map.js +1 -2
  196. package/dist/repositories/index.d.ts +1 -0
  197. package/dist/repositories/index.js +1 -0
  198. package/dist/schemas/registry.d.ts +506 -0
  199. package/dist/seed/_helpers/brand-rows.d.ts +35 -0
  200. package/dist/seed/_helpers/brand-rows.js +576 -0
  201. package/dist/seed/_helpers/category-content.d.ts +39 -0
  202. package/dist/seed/_helpers/category-content.js +634 -0
  203. package/dist/seed/_helpers/category-forest.d.ts +2 -0
  204. package/dist/seed/_helpers/category-forest.js +585 -243
  205. package/dist/seed/_helpers/category-models.d.ts +5 -0
  206. package/dist/seed/_helpers/category-models.js +1364 -0
  207. package/dist/seed/_helpers/category-tree.js +17 -1
  208. package/dist/seed/_helpers/character-features.d.ts +10 -0
  209. package/dist/seed/_helpers/character-features.js +118 -0
  210. package/dist/seed/_helpers/media.d.ts +1 -1
  211. package/dist/seed/_helpers/media.js +44 -8
  212. package/dist/seed/_helpers/search-txt-wrappers.d.ts +10 -0
  213. package/dist/seed/_helpers/search-txt-wrappers.js +12 -1
  214. package/dist/seed/categories-seed-data.js +147 -169
  215. package/dist/seed/index.d.ts +1 -0
  216. package/dist/seed/index.js +1 -0
  217. package/dist/seed/manifest.d.ts +1 -1
  218. package/dist/seed/manifest.js +5 -0
  219. package/dist/seed/orders-seed-data.js +5 -3
  220. package/dist/seed/product-features-seed-data.d.ts +1 -8
  221. package/dist/seed/product-features-seed-data.js +358 -154
  222. package/dist/seed/products-art-seed-data.js +16 -4
  223. package/dist/seed/products-auctions-seed-data.js +6 -6
  224. package/dist/seed/products-classifieds-seed-data.js +17 -7
  225. package/dist/seed/products-digital-codes-seed-data.js +12 -12
  226. package/dist/seed/products-preorders-seed-data.js +8 -8
  227. package/dist/seed/products-prize-draws-seed-data.js +25 -8
  228. package/dist/seed/products-standard-seed-data.js +10 -60
  229. package/dist/seed/products-stickers-seed-data.js +6 -6
  230. package/dist/seed/site-settings-seed-data.js +24 -0
  231. package/dist/seed/store-extensions-seed-data.js +10 -4
  232. package/dist/seed/tax-codes-seed-data.d.ts +34 -0
  233. package/dist/seed/tax-codes-seed-data.js +100 -0
  234. package/dist/seed/test-data-ttl.d.ts +29 -0
  235. package/dist/seed/test-data-ttl.js +31 -0
  236. package/dist/server-entry.d.ts +1 -1
  237. package/dist/server-entry.js +6 -1
  238. package/dist/server.d.ts +4 -1
  239. package/dist/server.js +11 -2
  240. package/dist/styles.css +40 -0
  241. package/dist/ui/components/PaginatedSelect.d.ts +14 -0
  242. package/dist/ui/components/PaginatedSelect.js +0 -0
  243. package/dist/ui/components/PaginatedSelect.style.css +40 -0
  244. package/dist/utils/media-url.d.ts +26 -0
  245. package/dist/utils/media-url.js +144 -0
  246. package/dist/utils/physical-location.d.ts +23 -0
  247. package/dist/utils/physical-location.js +52 -0
  248. package/dist/utils/search-txt-builders.d.ts +20 -0
  249. package/dist/utils/search-txt-builders.js +36 -0
  250. package/package.json +1 -1
  251. package/scripts/seed-cli.mjs +4 -1
  252. package/dist/_internal/server/jobs/handlers/testerSandboxCleanup.d.ts +0 -2
  253. package/dist/_internal/server/jobs/handlers/testerSandboxCleanup.js +0 -2
  254. package/dist/features/tester/actions/leaderboard-actions.d.ts +0 -3
  255. package/dist/features/tester/actions/leaderboard-actions.js +0 -5
  256. package/dist/features/tester/components/BugHunterLeaderboardView.d.ts +0 -23
  257. package/dist/features/tester/components/BugHunterLeaderboardView.js +0 -18
  258. package/dist/features/tester/components/TesterHubView.d.ts +0 -5
  259. package/dist/features/tester/components/TesterHubView.js +0 -110
  260. package/dist/features/tester/seed-data/authored/public-pages__bug-hunters.d.ts +0 -2
  261. package/dist/features/tester/seed-data/authored/public-pages__bug-hunters.js +0 -78
  262. package/dist/features/tester/seed-data/tester-ttl.d.ts +0 -4
  263. package/dist/features/tester/seed-data/tester-ttl.js +0 -6
@@ -67,7 +67,7 @@ export { getSearchResults, searchAction, type SearchQuery, } from "./_internal/s
67
67
  export { getCategoryForDetail, listRootCategories, listFeaturedCategories, listMenuCategories, getCategoryTree, listSitemapCategories, CATEGORIES_PAGE_SIZE, CATEGORIES_ROOT_TIER, CATEGORIES_MAX_DEPTH, CATEGORIES_SITEMAP_LIMIT, CATEGORIES_FEATURED_LIMIT, CATEGORIES_MENU_LIMIT, } from "./_internal/server/features/categories/index";
68
68
  export { getGroupedListingForDetail, getGroupedListingWithItems, listGroupedListings, listFeaturedGroupedListings, listSitemapGroupedListings, getGroupsForProduct, getGroupsWithItemsForProduct, GROUPED_LISTINGS_PAGE_SIZE, GROUPED_LISTINGS_FEATURED_LIMIT, GROUPED_LISTINGS_SITEMAP_LIMIT, buildGroupedListingMetadata, renderGroupedListingOg, type GroupedListingWithItems, type ListGroupedListingsParams, type SitemapGroupedListing, type GroupedListingMetadataOptions, } from "./_internal/server/features/grouped/index";
69
69
  export { createCheckoutOrderAction, attachPaymentAction, verifyAndPlacePhonePeOrderAction, previewCheckoutPricing, resolveShippingCost, formatShippingAddress, CHECKOUT_DEFAULT_COMMISSIONS, CHECKOUT_PAYMENT_METHODS, type CreateCheckoutOrderInput, type VerifyAndPlacePhonePeOrderInput, type CheckoutOrderResult, type CheckoutPaymentMethod, type CheckoutPricingPreviewInput, type CheckoutPricingPreview, } from "./_internal/server/features/checkout/index";
70
- export { unitPriceFor, lineTotalFor, computePreOrderDepositAmount, } from "./_internal/shared/checkout/order-math";
70
+ export { unitPriceFor, lineTotalFor, sumGroupGst, computePreOrderDepositAmount, type GroupGstSummary, type GstRateSlice, } from "./_internal/shared/checkout/order-math";
71
71
  export { processRefundAction, type ProcessRefundInput, } from "./_internal/server/features/refunds/actions";
72
72
  export { getSublistingCategoryForDetail } from "./_internal/server/features/sublisting-categories/index";
73
73
  export { getStoreForDetail, listStoreProductsInitial, listStoreAuctionsInitial, listStorePreOrdersInitial, listSitemapStores, STORES_PAGE_SIZE, STORES_PRODUCTS_PAGE_SIZE, STORES_SITEMAP_LIMIT, STORES_FEATURED_LIMIT, } from "./_internal/server/features/stores/index";
@@ -101,7 +101,12 @@ export { createCheckoutOrderAction, attachPaymentAction, verifyAndPlacePhonePeOr
101
101
  // consumer's Razorpay create-order route needs it: that route used to hand-roll
102
102
  // the rule and reproduced only the bundle branch, so it captured the LIST price
103
103
  // for an accepted offer. Pure math — no Firestore, no server-only imports.
104
- export { unitPriceFor, lineTotalFor, computePreOrderDepositAmount, } from "./_internal/shared/checkout/order-math";
104
+ // `sumGroupGst` joins them for exactly the same reason, one defect later: that
105
+ // same create-order route decides what the gateway CAPTURES and omitted product
106
+ // GST entirely, so the buyer was shown the tax, charged without it, and the
107
+ // order recorded none — with the verify-side mismatch guard omitting it too, so
108
+ // the three were consistently wrong and the undercharge was undetectable.
109
+ export { unitPriceFor, lineTotalFor, sumGroupGst, computePreOrderDepositAmount, } from "./_internal/shared/checkout/order-math";
105
110
  // S-SBUNI-RULES: refund action
106
111
  export { processRefundAction, } from "./_internal/server/features/refunds/actions";
107
112
  // S3: sublisting categories data layer
package/dist/server.d.ts CHANGED
@@ -231,6 +231,7 @@ export { POST } from "./features/categories/server";
231
231
  export { categoriesGET } from "./features/categories/server";
232
232
  export { categoryItemDELETE } from "./features/categories/server";
233
233
  export { categoryItemGET } from "./features/categories/server";
234
+ export { categoryDescriptionTemplateGET } from "./features/categories/server";
234
235
  export { categoryItemPATCH } from "./features/categories/server";
235
236
  export { createCategory } from "./features/categories/server";
236
237
  export { deleteCategory } from "./features/categories/server";
@@ -267,7 +268,6 @@ export { getEventPollResults } from "./features/events/server";
267
268
  export type { PollResultEntry, SpinResultEntry } from "./features/events/index";
268
269
  export { getEventSpinResults } from "./features/events/server";
269
270
  export { getUserSpinResultsForEvent } from "./features/events/server";
270
- export { getBugHunterLeaderboard } from "./features/tester/server";
271
271
  export { getPublicEventById } from "./features/events/server";
272
272
  export { listPublicEvents } from "./features/events/server";
273
273
  export { getRelatedEvents } from "./features/events/server";
@@ -427,6 +427,7 @@ export { removeFromWishlist } from "./features/wishlist/server";
427
427
  export { siteSettingsRepository } from "./repositories/index";
428
428
  export { storeRepository } from "./repositories/index";
429
429
  export { orderRepository } from "./repositories/index";
430
+ export { taxCodesRepository } from "./repositories/index";
430
431
  export type { OrderDocument } from "./features/orders/index";
431
432
  export type { OrderDocumentItem } from "./features/orders/index";
432
433
  export { serverLogger } from "./monitoring/index";
@@ -536,6 +537,8 @@ export type { SitemapOptions, RobotsOptions, ManifestOptions, DefaultOgOptions }
536
537
  export { checkActionAllowed } from "./features/admin/utils/checkActionAllowed";
537
538
  export { getDisabledRoutes } from "./features/admin/utils/getDisabledRoutes";
538
539
  export { getSiteSettingsGlobal } from "./features/admin/utils/getSiteSettingsGlobal";
540
+ export { getHomepageFaqsCached } from "./features/faq/utils/getHomepageFaqsCached";
541
+ export { revalidateTargetsFor, type RevalidateHints, } from "./_internal/shared/cache/revalidate-targets";
539
542
  export { getSiteSettingsSafe } from "./_internal/server/features/site-settings/safe-settings";
540
543
  export { toPublicSiteSettings, PUBLIC_SITE_SETTINGS_FIELDS, PRIVATE_SITE_SETTINGS_FIELDS, type ToPublicSiteSettingsOptions, } from "./_internal/server/features/site-settings/adapters";
541
544
  export { callFirebaseIdentityToolkit } from "./_internal/server/features/auth/firebase-identity";
package/dist/server.js CHANGED
@@ -665,6 +665,8 @@ export { categoryItemDELETE } from "./features/categories/server";
665
665
  // [SERVER-ONLY]-Server-only — uses Node.js, Next.js server internals, or third-party server SDKs (auth, email, payment, shipping).
666
666
  // categoryItemGET - Shared export for category item get.
667
667
  export { categoryItemGET } from "./features/categories/server";
668
+ // categoryDescriptionTemplateGET - the seller form's inherited-template read.
669
+ export { categoryDescriptionTemplateGET } from "./features/categories/server";
668
670
  // [SERVER-ONLY]-Server-only — uses Node.js, Next.js server internals, or third-party server SDKs (auth, email, payment, shipping).
669
671
  // categoryItemPATCH - Shared export for category item patch.
670
672
  export { categoryItemPATCH } from "./features/categories/server";
@@ -780,8 +782,6 @@ export { getEventSpinResults } from "./features/events/server";
780
782
  // getUserSpinResultsForEvent - The CALLER'S OWN spins for an event, unmasked. Companion to the masked public feed above.
781
783
  export { getUserSpinResultsForEvent } from "./features/events/server";
782
784
  // [SERVER-ONLY]-Server-only — uses Node.js, Next.js server internals, or third-party server SDKs (auth, email, payment, shipping).
783
- // getBugHunterLeaderboard - Ranks testers by confirmed-bug count.
784
- export { getBugHunterLeaderboard } from "./features/tester/server";
785
785
  // [SERVER-ONLY]-Server-only — uses Node.js, Next.js server internals, or third-party server SDKs (auth, email, payment, shipping).
786
786
  // getPublicEventById - Helper for get public event by id.
787
787
  export { getPublicEventById } from "./features/events/server";
@@ -1248,6 +1248,9 @@ export { siteSettingsRepository } from "./repositories/index";
1248
1248
  export { storeRepository } from "./repositories/index";
1249
1249
  // orderRepository - Shared export for order repository (needed by consumer Firebase Functions, e.g. invoice PDF generation).
1250
1250
  export { orderRepository } from "./repositories/index";
1251
+ // taxCodesRepository (B3) — resolved by deriveTaxonomy on every product write
1252
+ // that names a taxonomy field, so it is a server-only read on a hot path.
1253
+ export { taxCodesRepository } from "./repositories/index";
1251
1254
  // SB-UNI-V — bundlesRepository deleted; use categoriesRepository with categoryType:"bundle".
1252
1255
  // -- Monitoring --------------------------------------------------------------
1253
1256
  // serverLogger - Structured server-side logger.
@@ -1404,6 +1407,12 @@ export { buildSitemap, buildRobots, buildManifest, buildDefaultOgImage, DEFAULT_
1404
1407
  export { checkActionAllowed } from "./features/admin/utils/checkActionAllowed";
1405
1408
  export { getDisabledRoutes } from "./features/admin/utils/getDisabledRoutes";
1406
1409
  export { getSiteSettingsGlobal } from "./features/admin/utils/getSiteSettingsGlobal";
1410
+ // Same React.cache dedup, for the FAQ list the homepage reads twice per render
1411
+ // (once for the section, once for its JSON-LD).
1412
+ export { getHomepageFaqsCached } from "./features/faq/utils/getHomepageFaqsCached";
1413
+ // Which PAGE paths a write makes stale — the input to revalidatePath(). Long
1414
+ // detail-route TTLs are only safe because this exists.
1415
+ export { revalidateTargetsFor, } from "./_internal/shared/cache/revalidate-targets";
1407
1416
  // Null-safe wrapper — returns null on Firestore failure instead of throwing.
1408
1417
  export { getSiteSettingsSafe } from "./_internal/server/features/site-settings/safe-settings";
1409
1418
  // Public site-settings allow-list projection — the ONLY sanctioned shape for
package/dist/styles.css CHANGED
@@ -7369,6 +7369,46 @@
7369
7369
  background-color: var(--appkit-color-slate-800);
7370
7370
  }
7371
7371
 
7372
+ /* ── edit the selected option (C2c) ──
7373
+ Absolutely positioned inside `.appkit-ps` (which is `position: relative`)
7374
+ rather than being a flex sibling of the trigger: the trigger is a
7375
+ full-width button and the pencil has to sit ON it visually while remaining
7376
+ OUTSIDE it in the DOM. A button nested inside a button is invalid HTML and
7377
+ the browser's recovery hoists it out, after which which element receives
7378
+ the click is an engine decision — so the pencil would open the dropdown on
7379
+ some browsers and the editor on others.
7380
+
7381
+ `right` clears the ▾ caret. The hover fill uses the theme-inverting
7382
+ surface-hover token, never a named palette tint: `hover:bg-zinc-50` and
7383
+ friends are the SAME near-white in every theme, which is white-on-white
7384
+ under dark ink (Root Cause #79). */
7385
+ .appkit-ps__edit-btn {
7386
+ position: absolute;
7387
+ top: 50%;
7388
+ right: 2rem;
7389
+ transform: translateY(-50%);
7390
+ display: inline-flex;
7391
+ align-items: center;
7392
+ justify-content: center;
7393
+ width: 1.75rem;
7394
+ height: 1.75rem;
7395
+ border-radius: var(--appkit-radius-md, 0.375rem);
7396
+ font-size: 0.875rem;
7397
+ line-height: 1;
7398
+ color: var(--appkit-color-text-muted);
7399
+ transition: background-color 0.1s, color 0.1s;
7400
+ }
7401
+
7402
+ .appkit-ps__edit-btn:hover {
7403
+ background-color: var(--appkit-color-surface-hover);
7404
+ color: var(--appkit-color-primary);
7405
+ }
7406
+
7407
+ .appkit-ps__edit-btn:focus-visible {
7408
+ outline: 2px solid var(--appkit-color-focus-ring);
7409
+ outline-offset: 1px;
7410
+ }
7411
+
7372
7412
  /* SideDrawer — accessible slide-in panel with focus trap + unsaved-changes */
7373
7413
 
7374
7414
  .appkit-side-drawer__backdrop {
@@ -32,6 +32,20 @@ interface BaseProps<V> {
32
32
  createSchema?: import("zod").ZodType<FormValues> | import("zod").ZodType<unknown>;
33
33
  onCreateSubmit?: (values: FormValues) => Promise<PaginatedSelectOption<V>>;
34
34
  createSubmitLabel?: string;
35
+ editLabel?: string;
36
+ editDrawerTitle?: string;
37
+ renderEditForm?: (props: {
38
+ value: V;
39
+ option: PaginatedSelectOption<V> | null;
40
+ onEdited: (option: PaginatedSelectOption<V>) => void;
41
+ onCancel: () => void;
42
+ }) => ReactNode;
43
+ editFields?: QuickFieldDef[];
44
+ editSchema?: import("zod").ZodType<FormValues> | import("zod").ZodType<unknown>;
45
+ /** Seed the quick form from the row being edited. */
46
+ onEditLoad?: (value: V) => Promise<FormValues>;
47
+ onEditSubmit?: (value: V, values: FormValues) => Promise<PaginatedSelectOption<V>>;
48
+ editSubmitLabel?: string;
35
49
  }
36
50
  interface SingleProps<V> extends BaseProps<V> {
37
51
  multiple?: false;
@@ -260,3 +260,43 @@
260
260
  :is(.dark) .appkit-ps__create-btn:hover {
261
261
  background-color: var(--appkit-color-slate-800);
262
262
  }
263
+
264
+ /* ── edit the selected option (C2c) ──
265
+ Absolutely positioned inside `.appkit-ps` (which is `position: relative`)
266
+ rather than being a flex sibling of the trigger: the trigger is a
267
+ full-width button and the pencil has to sit ON it visually while remaining
268
+ OUTSIDE it in the DOM. A button nested inside a button is invalid HTML and
269
+ the browser's recovery hoists it out, after which which element receives
270
+ the click is an engine decision — so the pencil would open the dropdown on
271
+ some browsers and the editor on others.
272
+
273
+ `right` clears the ▾ caret. The hover fill uses the theme-inverting
274
+ surface-hover token, never a named palette tint: `hover:bg-zinc-50` and
275
+ friends are the SAME near-white in every theme, which is white-on-white
276
+ under dark ink (Root Cause #79). */
277
+ .appkit-ps__edit-btn {
278
+ position: absolute;
279
+ top: 50%;
280
+ right: 2rem;
281
+ transform: translateY(-50%);
282
+ display: inline-flex;
283
+ align-items: center;
284
+ justify-content: center;
285
+ width: 1.75rem;
286
+ height: 1.75rem;
287
+ border-radius: var(--appkit-radius-md, 0.375rem);
288
+ font-size: 0.875rem;
289
+ line-height: 1;
290
+ color: var(--appkit-color-text-muted);
291
+ transition: background-color 0.1s, color 0.1s;
292
+ }
293
+
294
+ .appkit-ps__edit-btn:hover {
295
+ background-color: var(--appkit-color-surface-hover);
296
+ color: var(--appkit-color-primary);
297
+ }
298
+
299
+ .appkit-ps__edit-btn:focus-visible {
300
+ outline: 2px solid var(--appkit-color-focus-ring);
301
+ outline-offset: 1px;
302
+ }
@@ -30,6 +30,32 @@ export declare const MEDIA_URL_MAX_LENGTH = 2048;
30
30
  */
31
31
  export declare const APPROVED_MEDIA_DOMAINS: readonly string[];
32
32
  export declare const MEDIA_URL_MESSAGE = "Must be a stored media reference (/media/<slug>) or a URL on an approved CDN domain";
33
+ /**
34
+ * Local, static stand-ins for seed photography. Served straight out of
35
+ * `public/` — no function, no upstream fetch, no sharp pipeline.
36
+ *
37
+ * 🛑 This is the single most load-bearing cost control in the app. Every seed
38
+ * image used to resolve to `/api/media/ext?url=https://placehold.co/…`, i.e. a
39
+ * Node lambda that fetched a third party (up to 2 × 4 s) and ran a full sharp
40
+ * decode/watermark/encode — ONE INVOCATION PER IMAGE. Measured 2026-10-09: the
41
+ * homepage referenced **160** such images at ~49.5 KB each, and
42
+ * `52.52 GB of Fast Origin Transfer ÷ 49.5 KB ≈ 1.06 M` proxy responses in a
43
+ * week. That is what suspended the project (HTTP 402).
44
+ *
45
+ * Six tiles, because the stored URL already encodes which of the six
46
+ * `SEED_PHOTO_COLOURS` it was generated with — so the mapping needs no new
47
+ * data and no migration. The homepage goes 160 images → 6.
48
+ */
49
+ export declare const SEED_TILE_PREFIX = "/images/seed-tiles/";
50
+ /**
51
+ * A local tile for `absUrl` when it points at a placeholder host, else undefined.
52
+ *
53
+ * placehold.co bakes its colours into the path (`/{w}x{h}/{bg}/{fg}/png`), so the
54
+ * first 6-hex segment IS the background the fixture was generated with — the
55
+ * size segment contains an `x` and cannot match, and the foreground comes after
56
+ * the background, so `.find()` picks the right one.
57
+ */
58
+ export declare function placeholderTileFor(absUrl: string): string | undefined;
33
59
  /**
34
60
  * Is `value` something we are willing to PERSIST as a media reference?
35
61
  *
@@ -38,6 +38,122 @@ export const APPROVED_MEDIA_DOMAINS = [
38
38
  "images.unsplash.com",
39
39
  ];
40
40
  export const MEDIA_URL_MESSAGE = "Must be a stored media reference (/media/<slug>) or a URL on an approved CDN domain";
41
+ /* ── Placeholder imagery ─────────────────────────────────────────────────── */
42
+ /**
43
+ * Local, static stand-ins for seed photography. Served straight out of
44
+ * `public/` — no function, no upstream fetch, no sharp pipeline.
45
+ *
46
+ * 🛑 This is the single most load-bearing cost control in the app. Every seed
47
+ * image used to resolve to `/api/media/ext?url=https://placehold.co/…`, i.e. a
48
+ * Node lambda that fetched a third party (up to 2 × 4 s) and ran a full sharp
49
+ * decode/watermark/encode — ONE INVOCATION PER IMAGE. Measured 2026-10-09: the
50
+ * homepage referenced **160** such images at ~49.5 KB each, and
51
+ * `52.52 GB of Fast Origin Transfer ÷ 49.5 KB ≈ 1.06 M` proxy responses in a
52
+ * week. That is what suspended the project (HTTP 402).
53
+ *
54
+ * Six tiles, because the stored URL already encodes which of the six
55
+ * `SEED_PHOTO_COLOURS` it was generated with — so the mapping needs no new
56
+ * data and no migration. The homepage goes 160 images → 6.
57
+ */
58
+ export const SEED_TILE_PREFIX = "/images/seed-tiles/";
59
+ /**
60
+ * Must stay in lockstep with `SEED_PHOTO_COLOURS` in
61
+ * `appkit/src/seed/_helpers/media.ts`, and with the filenames in
62
+ * `public/images/seed-tiles/`. A colour missing here is not an error — it falls
63
+ * back to a deterministic pick — so the failure mode is "less variety", never a
64
+ * broken image.
65
+ */
66
+ const SEED_TILE_COLOURS = [
67
+ "1e293b",
68
+ "334155",
69
+ "3f3f46",
70
+ "44403c",
71
+ "312e81",
72
+ "164e63",
73
+ ];
74
+ /**
75
+ * Hosts that only ever serve synthetic placeholder imagery.
76
+ *
77
+ * These are the four the seed helper documents having cycled through as each
78
+ * one fell over (picsum 503 on 2026-08-31, placekitten 521, loremflickr too
79
+ * slow for the proxy's 4 s timeout). Watermarking a generated placeholder is
80
+ * pointless, and proxying one costs a function invocation plus full origin
81
+ * transfer for an image we can synthesise locally for free.
82
+ */
83
+ const PLACEHOLDER_MEDIA_HOSTS = new Set([
84
+ "placehold.co",
85
+ "placeholder.com",
86
+ "via.placeholder.com",
87
+ "picsum.photos",
88
+ "fastly.picsum.photos",
89
+ "placekitten.com",
90
+ "loremflickr.com",
91
+ "dummyimage.com",
92
+ ]);
93
+ /** Deterministic 32-bit hash — same input, same tile, every render. */
94
+ function tileHash(input) {
95
+ let h = 2166136261;
96
+ for (let i = 0; i < input.length; i += 1) {
97
+ h ^= input.charCodeAt(i);
98
+ h = Math.imul(h, 16777619);
99
+ }
100
+ return h >>> 0;
101
+ }
102
+ /**
103
+ * The inner `url=` of a stored `/api/media/ext?url=…` value, or null.
104
+ *
105
+ * This is what lets the fix reach data that is ALREADY in Firestore. `seedPhoto`
106
+ * persisted the wrapped form for ~400 assets, and `resolveMediaUrl` used to hand
107
+ * such a value straight back untouched — `new URL()` throws on the relative path
108
+ * and the `catch` returns the input — so the proxy was entered on every render.
109
+ * Unwrapping here fixes every stored row with no migration.
110
+ */
111
+ function innerExtUrl(value) {
112
+ if (!value.startsWith(MEDIA_ENDPOINTS.EXT))
113
+ return null;
114
+ const q = value.indexOf("?");
115
+ if (q === -1)
116
+ return null;
117
+ try {
118
+ return new URLSearchParams(value.slice(q + 1)).get("url");
119
+ }
120
+ catch (_err) {
121
+ void normalizeError(_err);
122
+ return null;
123
+ }
124
+ }
125
+ /**
126
+ * A local tile for `absUrl` when it points at a placeholder host, else undefined.
127
+ *
128
+ * placehold.co bakes its colours into the path (`/{w}x{h}/{bg}/{fg}/png`), so the
129
+ * first 6-hex segment IS the background the fixture was generated with — the
130
+ * size segment contains an `x` and cannot match, and the foreground comes after
131
+ * the background, so `.find()` picks the right one.
132
+ */
133
+ export function placeholderTileFor(absUrl) {
134
+ let parsed;
135
+ try {
136
+ parsed = new URL(absUrl);
137
+ }
138
+ catch (_err) {
139
+ void normalizeError(_err);
140
+ return undefined;
141
+ }
142
+ const host = parsed.hostname.replace(/^www\./, "");
143
+ if (!PLACEHOLDER_MEDIA_HOSTS.has(host))
144
+ return undefined;
145
+ const bg = parsed.pathname
146
+ .split("/")
147
+ .find((seg) => /^[0-9a-fA-F]{6}$/.test(seg))
148
+ ?.toLowerCase();
149
+ if (bg && SEED_TILE_COLOURS.includes(bg))
150
+ return `${SEED_TILE_PREFIX}${bg}.svg`;
151
+ // Unknown or absent colour (picsum/placekitten carry none) — pick
152
+ // deterministically from the path so the same fixture always gets the same
153
+ // tile and the grid keeps its variety.
154
+ const fallback = SEED_TILE_COLOURS[tileHash(parsed.pathname) % SEED_TILE_COLOURS.length];
155
+ return `${SEED_TILE_PREFIX}${fallback}.svg`;
156
+ }
41
157
  /**
42
158
  * Is `value` something we are willing to PERSIST as a media reference?
43
159
  *
@@ -95,6 +211,11 @@ export function isStoredMediaRef(value) {
95
211
  return false;
96
212
  if (value.startsWith(MEDIA_PROXY_PREFIX))
97
213
  return true;
214
+ // A local seed tile (`/images/seed-tiles/<hex>.svg`). Persistable: it is a
215
+ // static asset we ship, so unlike the `/api/media/ext?url=` form rejected
216
+ // below it is not a render-time transform and does not double-wrap.
217
+ if (value.startsWith(SEED_TILE_PREFIX))
218
+ return true;
98
219
  if (value.startsWith("blob:") || value.startsWith("data:"))
99
220
  return false;
100
221
  if (value.startsWith(MEDIA_ENDPOINTS.EXT))
@@ -148,6 +269,8 @@ export function resolveMediaUrl(url, opts) {
148
269
  }
149
270
  if (url.startsWith(PROXY_PREFIX))
150
271
  return url;
272
+ if (url.startsWith(SEED_TILE_PREFIX))
273
+ return url;
151
274
  // A blob:/data: URI is only ever valid in the tab that created it (e.g. a
152
275
  // freshly-selected file preview via URL.createObjectURL, or a FileReader
153
276
  // data URL fed to a crop modal). Routing it through the external-URL
@@ -155,8 +278,29 @@ export function resolveMediaUrl(url, opts) {
155
278
  // rendered directly instead.
156
279
  if (url.startsWith("blob:") || url.startsWith("data:"))
157
280
  return url;
281
+ /*
282
+ * An ALREADY-WRAPPED `/api/media/ext?url=…` value, which is what the seed
283
+ * catalogue persisted ~400 times. Unwrap and look at what it actually points
284
+ * at: a placeholder host becomes a local tile (no function, no fetch), and
285
+ * anything else falls through to the old behaviour of returning the wrapped
286
+ * value untouched. Without this the branch below is unreachable for stored
287
+ * data, because `new URL()` throws on the relative path.
288
+ */
289
+ const inner = innerExtUrl(url);
290
+ if (inner) {
291
+ const innerTile = placeholderTileFor(inner);
292
+ if (innerTile)
293
+ return innerTile;
294
+ return url;
295
+ }
158
296
  try {
159
297
  const parsed = new URL(url);
298
+ // Synthetic placeholder imagery: never worth a function invocation. The
299
+ // placeholder hosts and our own Storage hosts are disjoint sets, so this
300
+ // can sit before the own-bucket branches without shadowing a real asset.
301
+ const tile = placeholderTileFor(url);
302
+ if (tile)
303
+ return tile;
160
304
  if (parsed.hostname.endsWith(FIREBASE_STORAGE_HOST)) {
161
305
  const m = parsed.pathname.match(/\/o\/([^?]+)/);
162
306
  if (m)
@@ -0,0 +1,23 @@
1
+ /** The parts a location can have. All optional — see the blank-parts note. */
2
+ export interface PhysicalLocationParts {
3
+ zone?: string | null;
4
+ shelf?: string | null;
5
+ bin?: string | null;
6
+ }
7
+ /**
8
+ * Render a location as `zone/shelf/bin`, or `undefined` when there is nothing
9
+ * to show.
10
+ *
11
+ * 🛑 Returns `undefined` rather than `"//"` for an all-blank location, and
12
+ * that case is REACHABLE, not defensive: `PhysicalLocationModal` tells the
13
+ * user "All fields are optional" while both bulk-location routes only check
14
+ * `typeof === "string"`, so `{zone:"",shelf:"",bin:""}` saves successfully.
15
+ * Every caller already branches on falsy before rendering, so returning
16
+ * `undefined` makes those rows fall through to their em-dash instead of
17
+ * printing punctuation with no content.
18
+ *
19
+ * Partial locations keep their separators (`A//3`) on purpose — the gap is
20
+ * information, and collapsing it would make "zone A, bin 3" indistinguishable
21
+ * from "zone A, shelf 3".
22
+ */
23
+ export declare function formatPhysicalLocation(loc: PhysicalLocationParts | null | undefined): string | undefined;
@@ -0,0 +1,52 @@
1
+ /*
2
+ * WHY: `physicalLocation` is a `{zone, shelf, bin}` OBJECT on both
3
+ * `ProductDocument` and `OrderDocument`, and three surfaces render it.
4
+ * Two had hand-written the same `a/b/c` interpolation
5
+ * (`SellerProductsView`, `SellerOrdersView`) and the third —
6
+ * `PrintCenterView` — declared the field as a `string` and rendered the
7
+ * object as a bare React child.
8
+ *
9
+ * 🛑 THAT THIRD ONE IS A CRASH. Rendering an object as a React child throws
10
+ * "Objects are not valid as a React child", so the Print Centre would die
11
+ * the first time any seller used the Set Location bulk action. It is
12
+ * LATENT, not live: measured on production, 0 of 72 products and 0 of 67
13
+ * orders carry the field, so nobody has used that feature yet. It was
14
+ * invisible because BOTH mappers in `PrintCenterPanel` are typed `(p: any)`
15
+ * and the products array is passed `as any` — so neither the wrong
16
+ * declaration nor the wrong value had anything to trip over.
17
+ *
18
+ * WHAT: one formatter. Pure, no imports, structural parameter so a caller can
19
+ * pass a `ProductDocument`'s field, an `OrderDocument`'s, or the
20
+ * `PhysicalLocation` client type without a cast.
21
+ *
22
+ * @tag domain:inventory
23
+ * @tag layer:util
24
+ * @tag access:isomorphic
25
+ * @tag sideEffects:none
26
+ */
27
+ /**
28
+ * Render a location as `zone/shelf/bin`, or `undefined` when there is nothing
29
+ * to show.
30
+ *
31
+ * 🛑 Returns `undefined` rather than `"//"` for an all-blank location, and
32
+ * that case is REACHABLE, not defensive: `PhysicalLocationModal` tells the
33
+ * user "All fields are optional" while both bulk-location routes only check
34
+ * `typeof === "string"`, so `{zone:"",shelf:"",bin:""}` saves successfully.
35
+ * Every caller already branches on falsy before rendering, so returning
36
+ * `undefined` makes those rows fall through to their em-dash instead of
37
+ * printing punctuation with no content.
38
+ *
39
+ * Partial locations keep their separators (`A//3`) on purpose — the gap is
40
+ * information, and collapsing it would make "zone A, bin 3" indistinguishable
41
+ * from "zone A, shelf 3".
42
+ */
43
+ export function formatPhysicalLocation(loc) {
44
+ if (!loc)
45
+ return undefined;
46
+ const zone = (loc.zone ?? "").trim();
47
+ const shelf = (loc.shelf ?? "").trim();
48
+ const bin = (loc.bin ?? "").trim();
49
+ if (!zone && !shelf && !bin)
50
+ return undefined;
51
+ return `${zone}/${shelf}/${bin}`;
52
+ }
@@ -1,4 +1,5 @@
1
1
  import type { ProductDocument } from "../features/products/schemas/firestore";
2
+ import type { CategoryDocument } from "../features/categories/schemas/firestore";
2
3
  import type { ScammerDocument } from "../features/scams/schemas/firestore";
3
4
  import type { StoreDocument } from "../features/stores/schemas/firestore";
4
5
  import type { EventDocument } from "../features/events/schemas/firestore";
@@ -9,6 +10,25 @@ import type { CouponDocument } from "../features/promotions/schemas/firestore";
9
10
  import type { OfferDocument } from "../features/seller/schemas/firestore";
10
11
  /** products — title, description, brand, taxonomy, condition, card/grading, specs. */
11
12
  export declare function buildProductSearchTxt(p: Partial<ProductDocument>): string[];
13
+ /**
14
+ * categories — name, slug, path, ancestor names, item code, editorial copy.
15
+ *
16
+ * Covers brand and bundle rows too, since they share the collection: hence
17
+ * `brandCountry` and `itemCode`, which only one kind ever carries.
18
+ *
19
+ * 🛑 `ancestors[].name` is in here deliberately, and it is what makes a leaf
20
+ * findable by its lineage — typing "burst" reaches `category-lost-longinus`
21
+ * because Beyblade Burst is one of its ancestors. Without it, the only way to
22
+ * find a tier-4 model is to already know its exact name.
23
+ *
24
+ * `description` is stripped of HTML because `contentBody`-style copy is
25
+ * keyword-rich markup and the tags are not search terms.
26
+ *
27
+ * NOT indexed: `createdBy` (a raw uid), `createdByStoreName` (operator
28
+ * identity), `position`/`subtreeSize` (internal coordinates), and the
29
+ * `metrics` counters (numbers nobody searches by).
30
+ */
31
+ export declare function buildCategorySearchTxt(c: Partial<CategoryDocument>): string[];
12
32
  /** stores — name, description, category. No payout details, no contact. */
13
33
  export declare function buildStoreSearchTxt(s: Partial<StoreDocument>): string[];
14
34
  /** events — title, description, type, tags. */
@@ -46,6 +46,42 @@ export function buildProductSearchTxt(p) {
46
46
  p.specifications?.map((s) => `${s.name} ${s.value}`),
47
47
  ]);
48
48
  }
49
+ /**
50
+ * categories — name, slug, path, ancestor names, item code, editorial copy.
51
+ *
52
+ * Covers brand and bundle rows too, since they share the collection: hence
53
+ * `brandCountry` and `itemCode`, which only one kind ever carries.
54
+ *
55
+ * 🛑 `ancestors[].name` is in here deliberately, and it is what makes a leaf
56
+ * findable by its lineage — typing "burst" reaches `category-lost-longinus`
57
+ * because Beyblade Burst is one of its ancestors. Without it, the only way to
58
+ * find a tier-4 model is to already know its exact name.
59
+ *
60
+ * `description` is stripped of HTML because `contentBody`-style copy is
61
+ * keyword-rich markup and the tags are not search terms.
62
+ *
63
+ * NOT indexed: `createdBy` (a raw uid), `createdByStoreName` (operator
64
+ * identity), `position`/`subtreeSize` (internal coordinates), and the
65
+ * `metrics` counters (numbers nobody searches by).
66
+ */
67
+ export function buildCategorySearchTxt(c) {
68
+ return buildSearchTxt([
69
+ c.name,
70
+ c.slug,
71
+ stripHtml(c.description),
72
+ c.path,
73
+ c.itemCode,
74
+ c.brandCountry,
75
+ ...(c.ancestors ?? []).map((a) => a.name),
76
+ /*
77
+ * B5 — LAST on purpose. `buildSearchTxt` truncates at 600 tokens and does
78
+ * it silently, so whatever sits at the end of this array is what gets
79
+ * dropped first. A model leaf's own name and its lineage must survive
80
+ * ahead of "also known as" spellings.
81
+ */
82
+ ...(c.aliases ?? []),
83
+ ]);
84
+ }
49
85
  /** stores — name, description, category. No payout details, no contact. */
50
86
  export function buildStoreSearchTxt(s) {
51
87
  return buildSearchTxt([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "4.42.18",
3
+ "version": "4.44.0",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -195,7 +195,7 @@ const {
195
195
  siteSettingsSeedData, faqSeedData,
196
196
  wishlistsSeedData, historySeedData,
197
197
  groupedListingsSeedData,
198
- scammersSeedData, productFeaturesSeedData,
198
+ scammersSeedData, productFeaturesSeedData, taxCodesSeedData,
199
199
  testerChecklistSeedData, testerResponsesSeedData,
200
200
  supportTicketsSeedData, catalogueSeedData,
201
201
  shipmentsSeedData, shipmentLotsSeedData, shipmentItemsSeedData,
@@ -210,6 +210,7 @@ const {
210
210
  CART_COLLECTION,
211
211
  GROUPED_LISTINGS_COLLECTION, SCAMMER_COLLECTION,
212
212
  WISHLIST_COLLECTION, HISTORY_COLLECTION, PRODUCT_FEATURES_COLLECTION,
213
+ TAX_CODES_COLLECTION,
213
214
  TESTER_CHECKLIST_ITEM_COLLECTION,
214
215
  TESTER_CHECKLIST_RESPONSE_COLLECTION,
215
216
  // PII field/index constants (plain data, still in the main barrel — the
@@ -285,6 +286,7 @@ const COLLECTION_MAP = {
285
286
  groupedListings: GROUPED_LISTINGS_COLLECTION,
286
287
  scammerProfiles: SCAMMER_COLLECTION,
287
288
  productFeatures: PRODUCT_FEATURES_COLLECTION,
289
+ taxCodes: TAX_CODES_COLLECTION,
288
290
  testerChecklistItems: TESTER_CHECKLIST_ITEM_COLLECTION,
289
291
  /*
290
292
  * Tester submissions. DERIVED tier in the tester's own map (wiped at setup so
@@ -370,6 +372,7 @@ const SEED_DATA_MAP = {
370
372
  groupedListings: groupedListingsSeedData,
371
373
  scammerProfiles: scammersSeedData,
372
374
  productFeatures: productFeaturesSeedData,
375
+ taxCodes: taxCodesSeedData,
373
376
  testerChecklistItems: testerChecklistSeedData,
374
377
  testerChecklistResponses: testerResponsesSeedData,
375
378
  };
@@ -1,2 +0,0 @@
1
- import type { ScheduleHandler } from "../runtime/types";
2
- export declare const testerSandboxCleanupHandler: ScheduleHandler;
@@ -1,2 +0,0 @@
1
- import { runTesterSandboxCleanup } from "../core/testerSandboxCleanup";
2
- export const testerSandboxCleanupHandler = (ctx) => runTesterSandboxCleanup(ctx);
@@ -1,3 +0,0 @@
1
- import type { BugHunterLeaderboardEntry } from "../schemas/firestore";
2
- /** Ranks testers by confirmed-bug count. Mirrors getEventLeaderboard()'s shape. */
3
- export declare function getBugHunterLeaderboard(limit?: number): Promise<BugHunterLeaderboardEntry[]>;
@@ -1,5 +0,0 @@
1
- import { testerChecklistItemRepository } from "../repository/tester-checklist-item.repository";
2
- /** Ranks testers by confirmed-bug count. Mirrors getEventLeaderboard()'s shape. */
3
- export async function getBugHunterLeaderboard(limit = 50) {
4
- return testerChecklistItemRepository.getBugHunterLeaderboard(limit);
5
- }