@mohasinac/appkit 4.52.5 → 4.53.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 (630) hide show
  1. package/dist/.build-stamp.json +4 -4
  2. package/dist/_internal/client/features/sticker-trace/canvas-codec.d.ts +2 -0
  3. package/dist/_internal/client/features/sticker-trace/canvas-codec.js +148 -0
  4. package/dist/_internal/server/features/auctions/actions.js +1 -1
  5. package/dist/_internal/server/features/categories/data.js +23 -7
  6. package/dist/_internal/server/features/checkout/actions.js +10 -3
  7. package/dist/_internal/server/features/checkout/data.d.ts +15 -0
  8. package/dist/_internal/server/features/checkout/data.js +20 -3
  9. package/dist/_internal/server/features/lottery/actions.js +9 -1
  10. package/dist/_internal/server/features/lottery/og.d.ts +40 -0
  11. package/dist/_internal/server/features/lottery/og.js +79 -0
  12. package/dist/_internal/server/features/messages/actions.js +4 -0
  13. package/dist/_internal/server/features/offers/adapters.js +17 -3
  14. package/dist/_internal/server/features/orders/actions.js +7 -2
  15. package/dist/_internal/server/features/raffle/actions.d.ts +19 -0
  16. package/dist/_internal/server/features/raffle/actions.js +25 -0
  17. package/dist/_internal/server/features/reviews/actions.js +1 -1
  18. package/dist/_internal/server/features/reviews/data.d.ts +1 -1
  19. package/dist/_internal/server/features/search/index-build/artifact-store.d.ts +89 -0
  20. package/dist/_internal/server/features/search/index-build/artifact-store.js +208 -0
  21. package/dist/_internal/server/features/search/index-build/build.d.ts +57 -0
  22. package/dist/_internal/server/features/search/index-build/build.js +285 -0
  23. package/dist/_internal/server/features/search/index-build/sources.d.ts +53 -0
  24. package/dist/_internal/server/features/search/index-build/sources.js +385 -0
  25. package/dist/_internal/server/features/search/search-sources.d.ts +0 -15
  26. package/dist/_internal/server/features/search/search-sources.js +142 -7
  27. package/dist/_internal/server/features/seo/robots.js +21 -1
  28. package/dist/_internal/server/features/seo/sitemap.js +1 -1
  29. package/dist/_internal/server/features/shared/listing-data-factory.js +16 -4
  30. package/dist/_internal/server/features/site-settings/adapters.d.ts +1 -1
  31. package/dist/_internal/server/features/site-settings/adapters.js +26 -0
  32. package/dist/_internal/server/features/sticker-trace/actions.d.ts +109 -0
  33. package/dist/_internal/server/features/sticker-trace/actions.js +172 -0
  34. package/dist/_internal/server/features/sticker-trace/adapters.d.ts +80 -0
  35. package/dist/_internal/server/features/sticker-trace/adapters.js +174 -0
  36. package/dist/_internal/server/features/sticker-trace/build-cut-file.d.ts +26 -0
  37. package/dist/_internal/server/features/sticker-trace/build-cut-file.js +127 -0
  38. package/dist/_internal/server/features/sticker-trace/coordinator.d.ts +27 -0
  39. package/dist/_internal/server/features/sticker-trace/coordinator.js +169 -0
  40. package/dist/_internal/server/features/sticker-trace/fanout.d.ts +43 -0
  41. package/dist/_internal/server/features/sticker-trace/fanout.js +180 -0
  42. package/dist/_internal/server/features/sticker-trace/finalizer.d.ts +25 -0
  43. package/dist/_internal/server/features/sticker-trace/finalizer.js +123 -0
  44. package/dist/_internal/server/features/sticker-trace/mosaic-inputs.d.ts +30 -0
  45. package/dist/_internal/server/features/sticker-trace/mosaic-inputs.js +112 -0
  46. package/dist/_internal/server/features/sticker-trace/progress.d.ts +81 -0
  47. package/dist/_internal/server/features/sticker-trace/progress.js +198 -0
  48. package/dist/_internal/server/features/sticker-trace/promote.d.ts +23 -0
  49. package/dist/_internal/server/features/sticker-trace/promote.js +193 -0
  50. package/dist/_internal/server/features/sticker-trace/shard.d.ts +50 -0
  51. package/dist/_internal/server/features/sticker-trace/shard.js +174 -0
  52. package/dist/_internal/server/features/sticker-trace/sheet-actions.d.ts +141 -0
  53. package/dist/_internal/server/features/sticker-trace/sheet-actions.js +279 -0
  54. package/dist/_internal/server/features/sticker-trace/storage.d.ts +135 -0
  55. package/dist/_internal/server/features/sticker-trace/storage.js +282 -0
  56. package/dist/_internal/server/functions/scheduled.d.ts +2 -1
  57. package/dist/_internal/server/functions/scheduled.js +25 -1
  58. package/dist/_internal/server/jobs/core/_event-ref.d.ts +51 -0
  59. package/dist/_internal/server/jobs/core/_event-ref.js +63 -0
  60. package/dist/_internal/server/jobs/core/assignSpinPrize.d.ts +18 -0
  61. package/dist/_internal/server/jobs/core/assignSpinPrize.js +77 -16
  62. package/dist/_internal/server/jobs/core/auctionSettlement.js +13 -4
  63. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +44 -15
  64. package/dist/_internal/server/jobs/core/bundleStockSync.js +3 -4
  65. package/dist/_internal/server/jobs/core/cleanupRtdbEvents.js +44 -0
  66. package/dist/_internal/server/jobs/core/event-draw-reasons.d.ts +78 -0
  67. package/dist/_internal/server/jobs/core/event-draw-reasons.js +133 -0
  68. package/dist/_internal/server/jobs/core/listingProcessor.js +28 -8
  69. package/dist/_internal/server/jobs/core/onOrderCreate.js +12 -1
  70. package/dist/_internal/server/jobs/core/onScamReportCreate.js +13 -2
  71. package/dist/_internal/server/jobs/core/onScamReportRejected.js +8 -2
  72. package/dist/_internal/server/jobs/core/onScamReportVerified.js +8 -2
  73. package/dist/_internal/server/jobs/core/onStoreWrite.js +11 -4
  74. package/dist/_internal/server/jobs/core/prizeDrawAssignWinner.js +5 -4
  75. package/dist/_internal/server/jobs/core/prizeDrawExpiryReveal.js +1 -4
  76. package/dist/_internal/server/jobs/core/prizeDrawSoldOutReveal.js +1 -2
  77. package/dist/_internal/server/jobs/core/promotions.js +7 -4
  78. package/dist/_internal/server/jobs/core/searchIndexBuild.d.ts +7 -0
  79. package/dist/_internal/server/jobs/core/searchIndexBuild.js +211 -0
  80. package/dist/_internal/server/jobs/core/triggerEventRaffle.d.ts +35 -0
  81. package/dist/_internal/server/jobs/core/triggerEventRaffle.js +213 -31
  82. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +20 -14
  83. package/dist/_internal/server/jobs/handlers/assignSpinPrize.d.ts +8 -0
  84. package/dist/_internal/server/jobs/handlers/assignSpinPrize.js +15 -15
  85. package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
  86. package/dist/_internal/server/jobs/handlers/index.js +1 -0
  87. package/dist/_internal/server/jobs/handlers/onCategoryWrite.js +18 -0
  88. package/dist/_internal/server/jobs/handlers/onProductWrite.js +18 -0
  89. package/dist/_internal/server/jobs/handlers/onReviewWrite.js +18 -0
  90. package/dist/_internal/server/jobs/handlers/onScamReportUpdate.js +18 -0
  91. package/dist/_internal/server/jobs/handlers/onStoreWrite.js +18 -0
  92. package/dist/_internal/server/jobs/handlers/searchIndexBuild.d.ts +2 -0
  93. package/dist/_internal/server/jobs/handlers/searchIndexBuild.js +2 -0
  94. package/dist/_internal/server/jobs/handlers/triggerEventRaffle.d.ts +8 -0
  95. package/dist/_internal/server/jobs/handlers/triggerEventRaffle.js +15 -6
  96. package/dist/_internal/shared/actions/action-registry.js +29 -0
  97. package/dist/_internal/shared/cache/revalidate-targets.d.ts +0 -1
  98. package/dist/_internal/shared/cache/revalidate-targets.js +10 -8
  99. package/dist/_internal/shared/features/events/config.d.ts +14 -0
  100. package/dist/_internal/shared/features/events/config.js +14 -0
  101. package/dist/_internal/shared/features/notifications/action-url.js +8 -0
  102. package/dist/_internal/shared/features/site-settings/types.d.ts +11 -0
  103. package/dist/_internal/shared/features/sticker-trace/codec.d.ts +81 -0
  104. package/dist/_internal/shared/features/sticker-trace/codec.js +30 -0
  105. package/dist/_internal/shared/features/sticker-trace/config.d.ts +81 -0
  106. package/dist/_internal/shared/features/sticker-trace/config.js +177 -0
  107. package/dist/_internal/shared/features/sticker-trace/cut-contour.d.ts +20 -0
  108. package/dist/_internal/shared/features/sticker-trace/cut-contour.js +60 -0
  109. package/dist/_internal/shared/features/sticker-trace/export/build-export-set.d.ts +65 -0
  110. package/dist/_internal/shared/features/sticker-trace/export/build-export-set.js +176 -0
  111. package/dist/_internal/shared/features/sticker-trace/export/cut-svg.d.ts +56 -0
  112. package/dist/_internal/shared/features/sticker-trace/export/cut-svg.js +120 -0
  113. package/dist/_internal/shared/features/sticker-trace/export/machine-profiles.d.ts +92 -0
  114. package/dist/_internal/shared/features/sticker-trace/export/machine-profiles.js +153 -0
  115. package/dist/_internal/shared/features/sticker-trace/export/nesting.d.ts +51 -0
  116. package/dist/_internal/shared/features/sticker-trace/export/nesting.js +110 -0
  117. package/dist/_internal/shared/features/sticker-trace/export/page-sizes.d.ts +68 -0
  118. package/dist/_internal/shared/features/sticker-trace/export/page-sizes.js +223 -0
  119. package/dist/_internal/shared/features/sticker-trace/export/part-tiling.d.ts +51 -0
  120. package/dist/_internal/shared/features/sticker-trace/export/part-tiling.js +178 -0
  121. package/dist/_internal/shared/features/sticker-trace/export/simplify-budget.d.ts +26 -0
  122. package/dist/_internal/shared/features/sticker-trace/export/simplify-budget.js +134 -0
  123. package/dist/_internal/shared/features/sticker-trace/export/vectorise-art.d.ts +40 -0
  124. package/dist/_internal/shared/features/sticker-trace/export/vectorise-art.js +195 -0
  125. package/dist/_internal/shared/features/sticker-trace/index.d.ts +2 -0
  126. package/dist/_internal/shared/features/sticker-trace/index.js +59 -0
  127. package/dist/_internal/shared/features/sticker-trace/math/catmull-rom.d.ts +11 -0
  128. package/dist/_internal/shared/features/sticker-trace/math/catmull-rom.js +66 -0
  129. package/dist/_internal/shared/features/sticker-trace/math/edt.d.ts +27 -0
  130. package/dist/_internal/shared/features/sticker-trace/math/edt.js +165 -0
  131. package/dist/_internal/shared/features/sticker-trace/math/label.d.ts +15 -0
  132. package/dist/_internal/shared/features/sticker-trace/math/label.js +156 -0
  133. package/dist/_internal/shared/features/sticker-trace/math/marching-squares.d.ts +16 -0
  134. package/dist/_internal/shared/features/sticker-trace/math/marching-squares.js +242 -0
  135. package/dist/_internal/shared/features/sticker-trace/math/morphology.d.ts +46 -0
  136. package/dist/_internal/shared/features/sticker-trace/math/morphology.js +168 -0
  137. package/dist/_internal/shared/features/sticker-trace/math/rasterize-path.d.ts +88 -0
  138. package/dist/_internal/shared/features/sticker-trace/math/rasterize-path.js +286 -0
  139. package/dist/_internal/shared/features/sticker-trace/math/rdp.d.ts +10 -0
  140. package/dist/_internal/shared/features/sticker-trace/math/rdp.js +74 -0
  141. package/dist/_internal/shared/features/sticker-trace/resume.d.ts +50 -0
  142. package/dist/_internal/shared/features/sticker-trace/resume.js +160 -0
  143. package/dist/_internal/shared/features/sticker-trace/trace/cluster.d.ts +20 -0
  144. package/dist/_internal/shared/features/sticker-trace/trace/cluster.js +132 -0
  145. package/dist/_internal/shared/features/sticker-trace/trace/cross-profile.d.ts +28 -0
  146. package/dist/_internal/shared/features/sticker-trace/trace/cross-profile.js +120 -0
  147. package/dist/_internal/shared/features/sticker-trace/trace/flood.d.ts +21 -0
  148. package/dist/_internal/shared/features/sticker-trace/trace/flood.js +104 -0
  149. package/dist/_internal/shared/features/sticker-trace/trace/geometry.d.ts +3 -0
  150. package/dist/_internal/shared/features/sticker-trace/trace/geometry.js +140 -0
  151. package/dist/_internal/shared/features/sticker-trace/trace/line-filter.d.ts +15 -0
  152. package/dist/_internal/shared/features/sticker-trace/trace/line-filter.js +138 -0
  153. package/dist/_internal/shared/features/sticker-trace/trace/merge.d.ts +27 -0
  154. package/dist/_internal/shared/features/sticker-trace/trace/merge.js +187 -0
  155. package/dist/_internal/shared/features/sticker-trace/trace/paper.d.ts +32 -0
  156. package/dist/_internal/shared/features/sticker-trace/trace/paper.js +147 -0
  157. package/dist/_internal/shared/features/sticker-trace/trace/prefilter.d.ts +30 -0
  158. package/dist/_internal/shared/features/sticker-trace/trace/prefilter.js +125 -0
  159. package/dist/_internal/shared/features/sticker-trace/trace/quantise.d.ts +10 -0
  160. package/dist/_internal/shared/features/sticker-trace/trace/quantise.js +153 -0
  161. package/dist/_internal/shared/features/sticker-trace/trace/ridge.d.ts +88 -0
  162. package/dist/_internal/shared/features/sticker-trace/trace/ridge.js +344 -0
  163. package/dist/_internal/shared/features/sticker-trace/trace/segment.d.ts +10 -0
  164. package/dist/_internal/shared/features/sticker-trace/trace/segment.js +251 -0
  165. package/dist/_internal/shared/features/sticker-trace/types.d.ts +197 -0
  166. package/dist/_internal/shared/features/sticker-trace/types.js +49 -0
  167. package/dist/_internal/shared/listing-types/_registry.d.ts +11 -0
  168. package/dist/_internal/shared/listing-types/art/config.d.ts +1 -0
  169. package/dist/_internal/shared/listing-types/art/config.js +1 -0
  170. package/dist/_internal/shared/listing-types/auction/config.d.ts +1 -0
  171. package/dist/_internal/shared/listing-types/auction/config.js +1 -0
  172. package/dist/_internal/shared/listing-types/classified/config.d.ts +1 -0
  173. package/dist/_internal/shared/listing-types/classified/config.js +1 -0
  174. package/dist/_internal/shared/listing-types/digital-code/config.d.ts +1 -0
  175. package/dist/_internal/shared/listing-types/digital-code/config.js +1 -0
  176. package/dist/_internal/shared/listing-types/feature-flags.d.ts +7 -0
  177. package/dist/_internal/shared/listing-types/feature-flags.js +9 -0
  178. package/dist/_internal/shared/listing-types/live/config.d.ts +1 -0
  179. package/dist/_internal/shared/listing-types/live/config.js +1 -0
  180. package/dist/_internal/shared/listing-types/pre-order/config.d.ts +1 -0
  181. package/dist/_internal/shared/listing-types/pre-order/config.js +1 -0
  182. package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +1 -0
  183. package/dist/_internal/shared/listing-types/prize-draw/config.js +1 -0
  184. package/dist/_internal/shared/listing-types/standard/config.d.ts +1 -0
  185. package/dist/_internal/shared/listing-types/standard/config.js +1 -0
  186. package/dist/_internal/shared/listing-types/stickers/config.d.ts +1 -0
  187. package/dist/_internal/shared/listing-types/stickers/config.js +1 -0
  188. package/dist/_internal/shared/media/limits.d.ts +12 -2
  189. package/dist/_internal/shared/media/limits.js +12 -2
  190. package/dist/_internal/shared/search/artifact-url.d.ts +35 -0
  191. package/dist/_internal/shared/search/artifact-url.js +119 -0
  192. package/dist/_internal/shared/search/index-record.d.ts +68 -0
  193. package/dist/_internal/shared/search/index-record.js +96 -0
  194. package/dist/_internal/shared/search/match.d.ts +65 -0
  195. package/dist/_internal/shared/search/match.js +263 -0
  196. package/dist/client.d.ts +8 -7
  197. package/dist/client.js +12 -5
  198. package/dist/configs/eslint.js +22 -2
  199. package/dist/constants/api-endpoints.d.ts +68 -0
  200. package/dist/constants/api-endpoints.js +32 -0
  201. package/dist/constants/field-names.d.ts +7 -1
  202. package/dist/constants/field-names.js +36 -1
  203. package/dist/contracts/repository.d.ts +8 -7
  204. package/dist/contracts/search.d.ts +2 -2
  205. package/dist/contracts/table.d.ts +3 -3
  206. package/dist/contracts/table.js +3 -3
  207. package/dist/core/contact-submissions.repository.js +2 -2
  208. package/dist/core/integration-keys.js +7 -1
  209. package/dist/core/newsletter.repository.js +2 -2
  210. package/dist/core/site-config.js +1 -1
  211. package/dist/errors/error-codes.d.ts +30 -0
  212. package/dist/errors/error-codes.js +30 -0
  213. package/dist/errors/error-display-map.js +40 -1
  214. package/dist/features/account/api/[userId]/addresses/route.js +1 -1
  215. package/dist/features/account/components/UserBidsView.js +1 -1
  216. package/dist/features/account/components/index.d.ts +0 -8
  217. package/dist/features/account/components/index.js +0 -4
  218. package/dist/features/addresses/repository/addresses.repository.d.ts +17 -6
  219. package/dist/features/addresses/repository/addresses.repository.js +65 -9
  220. package/dist/features/addresses/schemas/firestore.d.ts +17 -6
  221. package/dist/features/admin/actions/notification-actions.js +15 -6
  222. package/dist/features/admin/api/bids/route.js +1 -1
  223. package/dist/features/admin/api/coupons/route.js +1 -1
  224. package/dist/features/admin/api/products/route.js +1 -1
  225. package/dist/features/admin/api/reviews/route.js +1 -1
  226. package/dist/features/admin/components/AdminAdEditorView.js +19 -5
  227. package/dist/features/admin/components/AdminAuditLogView.js +1 -1
  228. package/dist/features/admin/components/AdminBlogEditorView.js +0 -1
  229. package/dist/features/admin/components/AdminBlogView.js +1 -1
  230. package/dist/features/admin/components/AdminBrandsView.js +17 -1
  231. package/dist/features/admin/components/AdminCategoryEditorView.js +0 -1
  232. package/dist/features/admin/components/AdminContactView.js +1 -1
  233. package/dist/features/admin/components/AdminEmployeeEditorView.js +1 -3
  234. package/dist/features/admin/components/AdminNewsletterView.js +1 -1
  235. package/dist/features/admin/components/AdminOrdersView.js +9 -2
  236. package/dist/features/admin/components/AdminPayoutsView.js +1 -1
  237. package/dist/features/admin/components/AdminPrizeDrawsView.js +11 -0
  238. package/dist/features/admin/components/AdminProductEditorView.d.ts +10 -1
  239. package/dist/features/admin/components/AdminProductEditorView.js +24 -37
  240. package/dist/features/admin/components/AdminProductsView.js +10 -3
  241. package/dist/features/admin/components/AdminScammersView.js +1 -1
  242. package/dist/features/admin/components/AdminSectionsView.js +21 -5
  243. package/dist/features/admin/components/AdminSessionsView.js +1 -1
  244. package/dist/features/admin/components/AdminSublistingCategoriesView.js +22 -1
  245. package/dist/features/admin/components/AdminTeamView.js +1 -1
  246. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +0 -1
  247. package/dist/features/admin/components/AdminUsersView.js +1 -1
  248. package/dist/features/admin/components/DataListingView.d.ts +2 -1
  249. package/dist/features/admin/components/DataListingView.js +9 -4
  250. package/dist/features/admin/components/analytics/AdminPageViewsReportView.js +0 -2
  251. package/dist/features/admin/hooks/useAdminListing.d.ts +1 -1
  252. package/dist/features/admin/repository/site-settings.repository.js +8 -3
  253. package/dist/features/admin/schemas/firestore.d.ts +62 -1
  254. package/dist/features/admin/schemas/firestore.js +32 -0
  255. package/dist/features/admin/types/index.d.ts +1 -1
  256. package/dist/features/auctions/actions/bid-actions.d.ts +2 -2
  257. package/dist/features/auctions/actions/bid-actions.js +16 -4
  258. package/dist/features/auctions/api/route.js +1 -1
  259. package/dist/features/auctions/components/AuctionBottomActions.js +4 -0
  260. package/dist/features/auctions/repository/bid.repository.d.ts +46 -4
  261. package/dist/features/auctions/repository/bid.repository.js +114 -13
  262. package/dist/features/auctions/schemas/firestore.d.ts +24 -4
  263. package/dist/features/auctions/schemas/firestore.js +1 -0
  264. package/dist/features/auth/actions/profile-actions.js +30 -4
  265. package/dist/features/auth/components/index.d.ts +0 -1
  266. package/dist/features/auth/components/index.js +0 -1
  267. package/dist/features/auth/repository/session.repository.d.ts +17 -3
  268. package/dist/features/auth/repository/session.repository.js +46 -5
  269. package/dist/features/auth/repository/token.repository.d.ts +27 -4
  270. package/dist/features/auth/repository/token.repository.js +65 -11
  271. package/dist/features/auth/repository/user.repository.d.ts +33 -7
  272. package/dist/features/auth/repository/user.repository.js +100 -15
  273. package/dist/features/auth/schemas/firestore.d.ts +63 -12
  274. package/dist/features/auth/schemas/firestore.js +11 -0
  275. package/dist/features/before-after/api/route.js +1 -1
  276. package/dist/features/blog/components/BlogListView.js +16 -3
  277. package/dist/features/blog/components/BlogPostView.js +1 -1
  278. package/dist/features/blog/schemas/index.d.ts +2 -2
  279. package/dist/features/cart/api/route.js +1 -1
  280. package/dist/features/catalogue/repository/catalogue.repository.d.ts +7 -7
  281. package/dist/features/catalogue/repository/catalogue.repository.js +28 -10
  282. package/dist/features/catalogue/schemas/firestore.d.ts +11 -0
  283. package/dist/features/categories/api/route.js +10 -10
  284. package/dist/features/categories/components/CategoryDetailPageView.js +35 -5
  285. package/dist/features/categories/components/CategorySelectorCreate.js +0 -1
  286. package/dist/features/categories/components/index.d.ts +0 -2
  287. package/dist/features/categories/components/index.js +0 -1
  288. package/dist/features/categories/repository/categories.repository.d.ts +1 -0
  289. package/dist/features/categories/repository/categories.repository.js +31 -1
  290. package/dist/features/categories/schemas/index.d.ts +2 -2
  291. package/dist/features/categories/utils/taxonomy-visual.d.ts +21 -0
  292. package/dist/features/categories/utils/taxonomy-visual.js +58 -0
  293. package/dist/features/checkout/actions/checkout-value-otp-actions.js +8 -1
  294. package/dist/features/collections/api/[slug]/route.js +3 -3
  295. package/dist/features/collections/api/route.js +1 -1
  296. package/dist/features/consultation/api/route.js +1 -1
  297. package/dist/features/consultation/repository/consultations.repository.js +1 -1
  298. package/dist/features/consultation/types/index.d.ts +1 -1
  299. package/dist/features/contact/components/ContactForm.js +1 -3
  300. package/dist/features/corporate/api/route.js +1 -1
  301. package/dist/features/corporate/repository/corporate.repository.js +1 -1
  302. package/dist/features/corporate/types/index.d.ts +1 -1
  303. package/dist/features/cron/jobs/auction-expiry.job.js +1 -1
  304. package/dist/features/cron/jobs/preorder-reminder.job.js +1 -1
  305. package/dist/features/events/actions/event-actions.d.ts +14 -0
  306. package/dist/features/events/actions/event-actions.js +82 -3
  307. package/dist/features/events/api/[id]/route.js +2 -2
  308. package/dist/features/events/components/AdminEventEditorView.js +22 -3
  309. package/dist/features/events/components/AdminEventsView.js +32 -8
  310. package/dist/features/events/components/EventRafflesSection.d.ts +34 -1
  311. package/dist/features/events/components/EventRafflesSection.js +52 -3
  312. package/dist/features/events/components/SpinWheelView.d.ts +2 -2
  313. package/dist/features/events/components/SpinWheelView.js +41 -4
  314. package/dist/features/events/repository/event-entry.repository.d.ts +14 -2
  315. package/dist/features/events/repository/event-entry.repository.js +41 -7
  316. package/dist/features/events/schemas/firestore.d.ts +17 -4
  317. package/dist/features/events/types/index.d.ts +37 -0
  318. package/dist/features/faq/repository/faqs.repository.js +1 -1
  319. package/dist/features/faq/schemas/index.d.ts +5 -2
  320. package/dist/features/faq/schemas/index.js +6 -0
  321. package/dist/features/faq/types/index.d.ts +0 -1
  322. package/dist/features/grouped/components/GroupedListingEditorView.js +17 -2
  323. package/dist/features/homepage/api/carousel/[id]/route.js +1 -1
  324. package/dist/features/homepage/api/carousel/route.js +4 -4
  325. package/dist/features/homepage/api/route.js +2 -2
  326. package/dist/features/homepage/api/sections/route.js +1 -1
  327. package/dist/features/homepage/components/AnnouncementBar.d.ts +1 -9
  328. package/dist/features/homepage/components/AnnouncementBar.js +13 -19
  329. package/dist/features/homepage/components/MarketplaceHomepageView.js +6 -1
  330. package/dist/features/homepage/components/index.d.ts +0 -2
  331. package/dist/features/homepage/components/index.js +0 -1
  332. package/dist/features/homepage/repository/homepage.repository.js +1 -1
  333. package/dist/features/homepage/schemas/firestore.d.ts +6 -0
  334. package/dist/features/homepage/schemas/index.d.ts +10 -10
  335. package/dist/features/layout/AppLayoutShell.d.ts +11 -0
  336. package/dist/features/layout/AppLayoutShell.js +64 -12
  337. package/dist/features/layout/BottomActionsContext.d.ts +16 -0
  338. package/dist/features/layout/BottomActionsContext.js +5 -0
  339. package/dist/features/layout/BottomChrome.js +48 -9
  340. package/dist/features/layout/BottomNavbar.d.ts +14 -0
  341. package/dist/features/layout/BottomNavbar.js +43 -3
  342. package/dist/features/layout/MainNavbar.d.ts +11 -0
  343. package/dist/features/layout/NavbarLayout.d.ts +3 -0
  344. package/dist/features/layout/NavbarLayout.js +88 -11
  345. package/dist/features/layout/SideActionDock.d.ts +1 -0
  346. package/dist/features/layout/SideActionDock.js +219 -0
  347. package/dist/features/layout/TitleBarLayout.js +1 -1
  348. package/dist/features/layout/cta-anchor.d.ts +31 -0
  349. package/dist/features/layout/cta-anchor.js +31 -0
  350. package/dist/features/layout/hooks/useBottomActions.d.ts +7 -0
  351. package/dist/features/layout/hooks/useBottomActions.js +6 -1
  352. package/dist/features/layout/index.d.ts +9 -1
  353. package/dist/features/layout/index.js +9 -1
  354. package/dist/features/layout/useCtaReveal.d.ts +24 -0
  355. package/dist/features/layout/useCtaReveal.js +82 -0
  356. package/dist/features/layout/useHoldToOpen.d.ts +41 -0
  357. package/dist/features/layout/useHoldToOpen.js +84 -0
  358. package/dist/features/lottery/repository/lottery-entry.repository.d.ts +18 -3
  359. package/dist/features/lottery/repository/lottery-entry.repository.js +48 -6
  360. package/dist/features/lottery/schemas/firestore.d.ts +33 -5
  361. package/dist/features/loyalty/api/balance/route.js +1 -1
  362. package/dist/features/loyalty/api/history/route.js +1 -1
  363. package/dist/features/media/MediaImage.js +0 -1
  364. package/dist/features/media/MediaPickerModal.js +7 -1
  365. package/dist/features/media/modals/ImageEditor.js +5 -1
  366. package/dist/features/media/modals/VideoThumbnailSelector.js +17 -1
  367. package/dist/features/media/repository/media-assets.repository.d.ts +19 -0
  368. package/dist/features/media/upload/CameraCapture.js +16 -1
  369. package/dist/features/media/upload/ImageUpload.js +42 -4
  370. package/dist/features/media/upload/MediaUploadField.js +69 -13
  371. package/dist/features/media/upload/downscale-image.d.ts +56 -0
  372. package/dist/features/media/upload/downscale-image.js +150 -0
  373. package/dist/features/messages/components/ConversationThreadView.js +45 -2
  374. package/dist/features/messages/components/MessageSellerButton.d.ts +15 -1
  375. package/dist/features/messages/components/MessageSellerButton.js +10 -2
  376. package/dist/features/messages/schemas/firestore.d.ts +15 -0
  377. package/dist/features/orders/hooks/useOrders.js +4 -2
  378. package/dist/features/orders/repository/orders.repository.d.ts +15 -4
  379. package/dist/features/orders/repository/orders.repository.js +52 -20
  380. package/dist/features/orders/schemas/firestore.d.ts +15 -4
  381. package/dist/features/orders/schemas/index.d.ts +8 -8
  382. package/dist/features/orders/types/index.d.ts +2 -2
  383. package/dist/features/payments/repository/payments.repository.js +2 -2
  384. package/dist/features/payments/repository/payout.repository.d.ts +23 -6
  385. package/dist/features/payments/repository/payout.repository.js +63 -23
  386. package/dist/features/payments/repository/saved-payment-methods.repository.d.ts +17 -6
  387. package/dist/features/payments/repository/saved-payment-methods.repository.js +59 -11
  388. package/dist/features/payments/schemas/firestore.d.ts +14 -3
  389. package/dist/features/payments/schemas/saved-methods-firestore.d.ts +12 -1
  390. package/dist/features/pre-orders/api/[slug]/route.js +1 -1
  391. package/dist/features/pre-orders/api/admin/route.js +1 -1
  392. package/dist/features/pre-orders/api/route.js +1 -1
  393. package/dist/features/pre-orders/repository/pre-orders.repository.d.ts +3 -3
  394. package/dist/features/pre-orders/repository/pre-orders.repository.js +6 -6
  395. package/dist/features/products/api/[id]/route.js +1 -1
  396. package/dist/features/products/components/ListingBottomActions.js +4 -0
  397. package/dist/features/products/components/ProductDetailPageView.js +2 -1
  398. package/dist/features/products/components/ProductGalleryClient.js +2 -2
  399. package/dist/features/products/components/ProductGrid.js +12 -1
  400. package/dist/features/products/components/index.d.ts +0 -2
  401. package/dist/features/products/components/index.js +0 -1
  402. package/dist/features/products/config/listing-type-listing-config.d.ts +0 -3
  403. package/dist/features/products/config/listing-type-listing-config.js +27 -2
  404. package/dist/features/products/constants/listing-tabs.d.ts +9 -0
  405. package/dist/features/products/constants/listing-tabs.js +3 -2
  406. package/dist/features/products/hooks/useProducts.js +2 -2
  407. package/dist/features/products/repository/products.repository.js +13 -1
  408. package/dist/features/products/schemas/index.d.ts +2 -2
  409. package/dist/features/products/types/index.d.ts +1 -1
  410. package/dist/features/promotions/api/route.js +1 -1
  411. package/dist/features/reviews/actions/review-actions.d.ts +6 -6
  412. package/dist/features/reviews/actions/review-actions.js +22 -8
  413. package/dist/features/reviews/components/ReviewDetailPageView.js +13 -1
  414. package/dist/features/reviews/components/ReviewsIndexPageView.js +21 -4
  415. package/dist/features/reviews/repository/reviews.repository.d.ts +19 -7
  416. package/dist/features/reviews/repository/reviews.repository.js +63 -12
  417. package/dist/features/reviews/schemas/firestore.d.ts +13 -2
  418. package/dist/features/reviews/schemas/index.d.ts +2 -2
  419. package/dist/features/scams/actions/scam-actions.js +16 -4
  420. package/dist/features/search/components/SearchResultsPageView.js +2 -1
  421. package/dist/features/search/hooks/useSearchIndex.d.ts +34 -0
  422. package/dist/features/search/hooks/useSearchIndex.js +172 -0
  423. package/dist/features/search/repository/search.repository.js +2 -2
  424. package/dist/features/seller/actions/offer-actions.d.ts +5 -5
  425. package/dist/features/seller/actions/offer-actions.js +38 -8
  426. package/dist/features/seller/actions/seller-actions.js +34 -7
  427. package/dist/features/seller/api/coupons/route.js +1 -1
  428. package/dist/features/seller/api/products/route.js +1 -1
  429. package/dist/features/seller/api/store/route.js +1 -1
  430. package/dist/features/seller/components/SellerBidsView.js +1 -1
  431. package/dist/features/seller/components/SellerCouponsView.js +1 -1
  432. package/dist/features/seller/components/SellerListingTypeView.js +9 -1
  433. package/dist/features/seller/components/SellerOrdersView.js +1 -1
  434. package/dist/features/seller/components/SellerPayoutSettingsView.js +0 -1
  435. package/dist/features/seller/components/SellerPayoutsView.js +1 -1
  436. package/dist/features/seller/components/SellerProductShell.js +0 -1
  437. package/dist/features/seller/components/SellerProductsView.js +1 -1
  438. package/dist/features/seller/components/SellerShippingView.js +0 -1
  439. package/dist/features/seller/components/SellerStorefrontView.js +0 -1
  440. package/dist/features/seller/components/index.d.ts +0 -4
  441. package/dist/features/seller/components/index.js +0 -2
  442. package/dist/features/seller/repository/offer.repository.d.ts +15 -3
  443. package/dist/features/seller/repository/offer.repository.js +43 -7
  444. package/dist/features/seller/schemas/firestore.d.ts +14 -3
  445. package/dist/features/seller/schemas/index.d.ts +4 -4
  446. package/dist/features/seller/schemas/order-forms.d.ts +39 -0
  447. package/dist/features/seller/schemas/order-forms.js +37 -0
  448. package/dist/features/shell/FormShell.js +6 -2
  449. package/dist/features/shell/QuickFormDrawer.js +15 -1
  450. package/dist/features/shipments/components/AdminShipmentEditorView.js +0 -1
  451. package/dist/features/shipments/components/AdminShipmentsView.js +1 -1
  452. package/dist/features/sticker-trace/components/StickerCommitPanel.d.ts +30 -0
  453. package/dist/features/sticker-trace/components/StickerCommitPanel.js +93 -0
  454. package/dist/features/sticker-trace/components/StickerPrintPanel.d.ts +33 -0
  455. package/dist/features/sticker-trace/components/StickerPrintPanel.js +104 -0
  456. package/dist/features/sticker-trace/components/StickerRetryPanel.d.ts +20 -0
  457. package/dist/features/sticker-trace/components/StickerRetryPanel.js +128 -0
  458. package/dist/features/sticker-trace/components/StickerRunHistoryPanel.d.ts +45 -0
  459. package/dist/features/sticker-trace/components/StickerRunHistoryPanel.js +84 -0
  460. package/dist/features/sticker-trace/components/StickerTraceStudio.d.ts +27 -0
  461. package/dist/features/sticker-trace/components/StickerTraceStudio.js +171 -0
  462. package/dist/features/sticker-trace/constants/help-content.d.ts +23 -0
  463. package/dist/features/sticker-trace/constants/help-content.js +172 -0
  464. package/dist/features/sticker-trace/hooks/useLiveTrace.d.ts +27 -0
  465. package/dist/features/sticker-trace/hooks/useLiveTrace.js +160 -0
  466. package/dist/features/sticker-trace/hooks/useStickerTraceProgress.d.ts +22 -0
  467. package/dist/features/sticker-trace/hooks/useStickerTraceProgress.js +106 -0
  468. package/dist/features/sticker-trace/repository/sticker-sheets.repository.d.ts +113 -0
  469. package/dist/features/sticker-trace/repository/sticker-sheets.repository.js +146 -0
  470. package/dist/features/sticker-trace/repository/sticker-trace-runs.repository.d.ts +132 -0
  471. package/dist/features/sticker-trace/repository/sticker-trace-runs.repository.js +174 -0
  472. package/dist/features/sticker-trace/schemas/firestore.d.ts +248 -0
  473. package/dist/features/sticker-trace/schemas/firestore.js +79 -0
  474. package/dist/features/sticker-trace/server.d.ts +8 -0
  475. package/dist/features/sticker-trace/server.js +51 -0
  476. package/dist/features/stores/actions/store-query-actions.d.ts +2 -2
  477. package/dist/features/stores/actions/store-query-actions.js +7 -2
  478. package/dist/features/stores/api/[storeSlug]/auctions/route.js +3 -3
  479. package/dist/features/stores/api/[storeSlug]/products/route.js +3 -3
  480. package/dist/features/stores/api/[storeSlug]/reviews/route.js +1 -1
  481. package/dist/features/stores/api/[storeSlug]/route.js +1 -1
  482. package/dist/features/stores/components/StoreNavTabs.js +17 -2
  483. package/dist/features/stores/manifest.js +8 -4
  484. package/dist/features/support/repository/support.repository.d.ts +14 -3
  485. package/dist/features/support/repository/support.repository.js +93 -13
  486. package/dist/features/support/schemas/firestore.d.ts +16 -5
  487. package/dist/features/tester/seed-data/authored/admin__events-raffles-spin.js +61 -19
  488. package/dist/features/tester/seed-data/authored/buying__browsing-search.js +19 -3
  489. package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.d.ts +2 -0
  490. package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.js +243 -0
  491. package/dist/features/tester/seed-data/authored/content-discovery__events.js +36 -10
  492. package/dist/features/tester/seed-data/authored/content-discovery__overflowing-pill-rows.d.ts +2 -0
  493. package/dist/features/tester/seed-data/authored/content-discovery__overflowing-pill-rows.js +209 -0
  494. package/dist/features/tester/seed-data/authored/design-ux__header-brand-mark-centring.d.ts +2 -0
  495. package/dist/features/tester/seed-data/authored/design-ux__header-brand-mark-centring.js +90 -0
  496. package/dist/features/tester/seed-data/authored/index.js +8 -0
  497. package/dist/features/tester/seed-data/authored/selling__media-limits.js +19 -17
  498. package/dist/features/tester/seed-data/authored/selling__product-upload-details.js +10 -5
  499. package/dist/features/tester/seed-data/authored/tools__sticker-tracer.d.ts +2 -0
  500. package/dist/features/tester/seed-data/authored/tools__sticker-tracer.js +283 -0
  501. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +192 -22
  502. package/dist/index.d.ts +10 -23
  503. package/dist/index.js +27 -19
  504. package/dist/next/api/routeHandler.js +9 -0
  505. package/dist/next/routing/require-record.d.ts +13 -0
  506. package/dist/next/routing/require-record.js +62 -0
  507. package/dist/next/routing/route-map.d.ts +4 -4
  508. package/dist/next/routing/route-map.js +25 -3
  509. package/dist/providers/db-firebase/base.d.ts +4 -2
  510. package/dist/providers/db-firebase/base.js +21 -5
  511. package/dist/providers/db-firebase/base.repository.d.ts +11 -9
  512. package/dist/providers/db-firebase/base.repository.js +19 -0
  513. package/dist/providers/db-firebase/rtdb-paths.d.ts +12 -0
  514. package/dist/providers/db-firebase/rtdb-paths.js +12 -0
  515. package/dist/react/ErrorBoundary.js +6 -1
  516. package/dist/react/hooks/realtime-event-constants.d.ts +1 -0
  517. package/dist/react/hooks/realtime-event-constants.js +8 -0
  518. package/dist/react/hooks/useEntityDelete.js +5 -1
  519. package/dist/react/hooks/useMessage.js +5 -1
  520. package/dist/repositories/index.d.ts +2 -2
  521. package/dist/repositories/index.js +6 -1
  522. package/dist/schemas/registry.d.ts +13 -20
  523. package/dist/schemas/registry.js +31 -14
  524. package/dist/schemas/webhooks/google-oauth.d.ts +2 -2
  525. package/dist/security/encrypted.d.ts +117 -0
  526. package/dist/security/encrypted.js +100 -0
  527. package/dist/security/index.d.ts +5 -2
  528. package/dist/security/index.js +16 -2
  529. package/dist/security/pii-accessors.d.ts +71 -0
  530. package/dist/security/pii-accessors.js +150 -0
  531. package/dist/security/pii-encrypt.d.ts +0 -8
  532. package/dist/security/pii-encrypt.js +22 -15
  533. package/dist/security/pii-mask.d.ts +18 -0
  534. package/dist/security/pii-mask.js +27 -0
  535. package/dist/seed/_helpers/search-txt-wrappers.d.ts +6 -6
  536. package/dist/seed/_helpers/search-txt-wrappers.js +22 -0
  537. package/dist/seed/addresses-seed-data.d.ts +2 -2
  538. package/dist/seed/authenticity-knowledge-seed-data.d.ts +34 -0
  539. package/dist/seed/authenticity-knowledge-seed-data.js +1277 -0
  540. package/dist/seed/bids-seed-data.d.ts +2 -2
  541. package/dist/seed/bids-seed-data.js +23 -2
  542. package/dist/seed/events-seed-data.d.ts +2 -2
  543. package/dist/seed/events-seed-data.js +5 -0
  544. package/dist/seed/faq-seed-data.js +175 -5
  545. package/dist/seed/homepage-sections-seed-data.js +39 -23
  546. package/dist/seed/index.d.ts +1 -0
  547. package/dist/seed/index.js +1 -0
  548. package/dist/seed/lottery-entries-seed-data.d.ts +2 -2
  549. package/dist/seed/offers-seed-data.d.ts +2 -2
  550. package/dist/seed/offers-seed-data.js +1 -1
  551. package/dist/seed/orders-seed-data.d.ts +2 -2
  552. package/dist/seed/payouts-seed-data.d.ts +2 -2
  553. package/dist/seed/payouts-seed-data.js +1 -1
  554. package/dist/seed/reviews-seed-data.d.ts +1 -2
  555. package/dist/seed/seed-purpose.js +7 -0
  556. package/dist/seed/sessions-seed-data.d.ts +2 -2
  557. package/dist/seed/sessions-seed-data.js +5 -0
  558. package/dist/seed/site-settings-seed-data.js +1 -1
  559. package/dist/seed/support-tickets-seed-data.d.ts +2 -2
  560. package/dist/seed/support-tickets-seed-data.js +1 -1
  561. package/dist/seed/users-seed-data.d.ts +2 -2
  562. package/dist/seed/users-seed-data.js +1 -1
  563. package/dist/seo/json-ld.js +5 -1
  564. package/dist/server.d.ts +18 -3
  565. package/dist/server.js +59 -3
  566. package/dist/styles.css +237 -1
  567. package/dist/tailwind-utilities.css +1 -1
  568. package/dist/ui/components/Button.js +7 -1
  569. package/dist/ui/components/FilterChipGroup.d.ts +11 -0
  570. package/dist/ui/components/FilterChipGroup.js +1 -1
  571. package/dist/ui/components/ImageGallery.js +6 -1
  572. package/dist/ui/components/ListingLayout.js +14 -8
  573. package/dist/ui/components/OverflowRow.d.ts +92 -0
  574. package/dist/ui/components/OverflowRow.js +203 -0
  575. package/dist/ui/components/OverflowRow.style.css +235 -0
  576. package/dist/ui/components/RecordDetailModal.js +14 -1
  577. package/dist/ui/components/RowActionMenu.js +5 -1
  578. package/dist/ui/components/SideDrawer.js +5 -1
  579. package/dist/ui/components/SideModal.js +9 -1
  580. package/dist/ui/components/TabBarShell.d.ts +12 -1
  581. package/dist/ui/components/TabBarShell.js +3 -2
  582. package/dist/ui/components/Tabs.js +36 -24
  583. package/dist/ui/components/Toast.d.ts +3 -24
  584. package/dist/ui/components/Toast.js +2 -19
  585. package/dist/ui/components/TopBannerPortal.d.ts +15 -0
  586. package/dist/ui/components/TopBannerPortal.js +19 -0
  587. package/dist/ui/components/UnsavedChangesModal.js +5 -1
  588. package/dist/ui/components/index.style.css +1 -0
  589. package/dist/ui/components/row-collapse.d.ts +15 -0
  590. package/dist/ui/components/row-collapse.js +127 -0
  591. package/dist/ui/components/toast-context.d.ts +63 -0
  592. package/dist/ui/components/toast-context.js +48 -0
  593. package/dist/ui/components/top-banner-slot.d.ts +37 -0
  594. package/dist/ui/components/top-banner-slot.js +45 -0
  595. package/dist/ui/icons/icon-registry.d.ts +6 -0
  596. package/dist/ui/icons/icon-registry.js +17 -1
  597. package/dist/ui/index.d.ts +2 -0
  598. package/dist/ui/index.js +1 -0
  599. package/dist/utils/array.helper.d.ts +2 -2
  600. package/dist/utils/array.helper.js +5 -5
  601. package/dist/utils/id-generators.d.ts +6 -0
  602. package/dist/utils/id-generators.js +10 -0
  603. package/dist/utils/media-url.js +0 -2
  604. package/dist/utils/search-txt-builders.d.ts +28 -4
  605. package/dist/utils/search-txt-builders.js +41 -1
  606. package/dist/validation/schemas.d.ts +8 -8
  607. package/package.json +6 -2
  608. package/scripts/seed-cli.mjs +3 -0
  609. package/dist/features/account/components/UserAddressesView.d.ts +0 -13
  610. package/dist/features/account/components/UserAddressesView.js +0 -5
  611. package/dist/features/account/components/UserNotificationsView.d.ts +0 -15
  612. package/dist/features/account/components/UserNotificationsView.js +0 -12
  613. package/dist/features/account/components/UserSettingsView.d.ts +0 -16
  614. package/dist/features/account/components/UserSettingsView.js +0 -13
  615. package/dist/features/account/components/UserSupportView.d.ts +0 -3
  616. package/dist/features/account/components/UserSupportView.js +0 -166
  617. package/dist/features/admin/components/DemoSeedView.d.ts +0 -11
  618. package/dist/features/admin/components/DemoSeedView.js +0 -5
  619. package/dist/features/auth/components/AuthStatusPanel.d.ts +0 -10
  620. package/dist/features/auth/components/AuthStatusPanel.js +0 -22
  621. package/dist/features/categories/components/CategoryProductsView.d.ts +0 -45
  622. package/dist/features/categories/components/CategoryProductsView.js +0 -21
  623. package/dist/features/homepage/components/HowItWorksInfoView.d.ts +0 -36
  624. package/dist/features/homepage/components/HowItWorksInfoView.js +0 -10
  625. package/dist/features/layout/BottomActions.d.ts +0 -1
  626. package/dist/features/layout/BottomActions.js +0 -311
  627. package/dist/features/products/components/ProductsView.d.ts +0 -6
  628. package/dist/features/products/components/ProductsView.js +0 -5
  629. package/dist/features/seller/components/SellerStoreView.d.ts +0 -8
  630. package/dist/features/seller/components/SellerStoreView.js +0 -8
@@ -0,0 +1,89 @@
1
+ import type { SearchIndexArtifact } from "../../../../shared/search/index-record";
2
+ import type { Firestore } from "firebase-admin/firestore";
3
+ import type { Bucket } from "@google-cloud/storage";
4
+ export declare const SEARCH_INDEX_COLLECTION = "searchIndex";
5
+ export declare const SEARCH_INDEX_MANIFEST_ID = "manifest";
6
+ /**
7
+ * 🛑 A declared ceiling, and the build FAILS rather than truncating.
8
+ *
9
+ * Deliberately the opposite of `buildSearchTxt`'s `MAX_TOKENS`, which this
10
+ * codebase complains about in four separate places for silently slicing. A
11
+ * short artifact is a search index missing rows, and nothing downstream can
12
+ * tell that from a small catalogue. 1.5 MB against a measured 384 KB leaves
13
+ * roughly 4x headroom; if it is ever hit, that is a real signal.
14
+ */
15
+ export declare const SEARCH_INDEX_MAX_BYTES = 1500000;
16
+ export interface SearchIndexManifest {
17
+ schema: number;
18
+ version: number;
19
+ builtAt: string;
20
+ lastFullAt: string | null;
21
+ storagePath: string;
22
+ recordCount: number;
23
+ bytes: number;
24
+ perType: Record<string, number>;
25
+ /** Set by the content triggers; cleared when a build consumes it. */
26
+ dirty: boolean;
27
+ }
28
+ type ManifestDb = Firestore;
29
+ type StorageBucket = Bucket;
30
+ export declare function manifestRef(db: ManifestDb): FirebaseFirestore.DocumentReference<FirebaseFirestore.DocumentData, FirebaseFirestore.DocumentData>;
31
+ export declare function readManifest(db: ManifestDb): Promise<SearchIndexManifest | null>;
32
+ /**
33
+ * Mark the index stale. Called from the content triggers.
34
+ *
35
+ * 🛑 READ-THEN-WRITE, guarding on a BOOLEAN — never a deep compare.
36
+ * Root Cause #92's trigger cost 12 million invocations a day because its
37
+ * no-op guard used `JSON.stringify`, which is key-order sensitive and so
38
+ * never matched. A boolean cannot have that failure mode, and the read means
39
+ * a burst of 50 product writes costs 50 reads and exactly ONE write.
40
+ *
41
+ * Never throws: a failed dirty-mark delays the index by one cadence, and
42
+ * failing the product write that triggered it would be far worse.
43
+ */
44
+ export declare function markSearchIndexDirty(db: ManifestDb, job: string): Promise<void>;
45
+ export declare function storagePathFor(version: number): string;
46
+ export interface PublishResult {
47
+ storagePath: string;
48
+ bytes: number;
49
+ pruned: number;
50
+ }
51
+ /**
52
+ * Write the artifact and prune older versions.
53
+ *
54
+ * The object carries `public, max-age=31536000, immutable` because the URL
55
+ * contains the version — a new build is a new URL, so nothing ever needs
56
+ * revalidating.
57
+ */
58
+ export declare function publishArtifact(bucket: StorageBucket, artifact: SearchIndexArtifact): Promise<PublishResult>;
59
+ /**
60
+ * Mirror the manifest to Storage so the client needs no Firebase SDK.
61
+ *
62
+ * Writes only the four fields a browser has any business reading — `dirty`,
63
+ * `lastFullAt` and `perType` are build bookkeeping, and publishing "the index
64
+ * is currently stale" to every visitor tells them nothing they can act on.
65
+ * The shape is `SearchIndexManifestPublic`, declared beside the URL builders.
66
+ *
67
+ * ## 🛑 60 seconds, and the number is load-bearing in both directions
68
+ *
69
+ * This is the ONLY uncacheable-for-long hop in the whole design, so it sets
70
+ * the floor on how fast a new build reaches a visitor — and the ceiling on
71
+ * how often a returning visitor pays for a request. The artifact behind it is
72
+ * `immutable`, so a client that re-reads the manifest and finds the same
73
+ * version does one small conditional fetch and no download at all.
74
+ *
75
+ * Too long and the 15-minute build cadence is a lie; too short and every
76
+ * visitor re-requests it per page view for a document that changes a few
77
+ * times an hour.
78
+ *
79
+ * ## Written LAST, and failing it does not fail the build
80
+ *
81
+ * A published artifact with a stale manifest serves the previous index for up
82
+ * to a minute — correct, just behind. A manifest pointing at an artifact that
83
+ * was never written is a 404 on the client's critical path. So the order is
84
+ * artifact, then Firestore, then this; and a throw here is logged, not
85
+ * propagated, because the Firestore manifest (which is what the NEXT build
86
+ * reads) is already correct by this point.
87
+ */
88
+ export declare function publishManifestMirror(bucket: StorageBucket, manifest: SearchIndexManifest): Promise<void>;
89
+ export {};
@@ -0,0 +1,208 @@
1
+ /*
2
+ * WHY: Where the search artifact lives, and how it is served without a
3
+ * Next.js route in front of it.
4
+ * WHAT: The Firestore manifest singleton, the Storage upload, and the prune.
5
+ *
6
+ * ## Why Storage directly rather than an /api route
7
+ *
8
+ * A version-addressed object written with `immutable` cache headers is served
9
+ * by the browser and any CDN in front of the bucket with no function in the
10
+ * path at all. `storage.rules` is already `allow read: if true` for every
11
+ * object, so no rules change is needed.
12
+ *
13
+ * It is also the only shape that can be deployed AND verified under this
14
+ * round's constraint of no Vercel deploy: the Function writes the artifact and
15
+ * it is immediately fetchable. A same-origin `/api/search-index/*` proxy stays
16
+ * a documented fallback if CORS or egress accounting later argues for it — it
17
+ * would change the two URLs and nothing else.
18
+ *
19
+ * ## 🛑 Old versions MUST be pruned
20
+ *
21
+ * Immutable version-addressed objects never overwrite, so without a prune this
22
+ * accumulates ~384 KB per content change, for ever, against a ~5 GB allowance
23
+ * the media library already shares. Two versions are kept — the current one
24
+ * and the one it replaced — so a client that fetched mid-publish is never
25
+ * orphaned, matching the service worker's `maxEntries: 2`.
26
+ *
27
+ * @tag domain:search
28
+ * @tag layer:server
29
+ * @tag pattern:none
30
+ * @tag access:server
31
+ * @tag consumers:searchIndexBuild
32
+ * @tag sideEffects:firestore,storage
33
+ */
34
+ import { normalizeError } from "../../../../../errors/normalize.js";
35
+ import { serverLogger } from "../../../../../monitoring/server-logger.js";
36
+ import { SEARCH_INDEX_PREFIX, SEARCH_MANIFEST_OBJECT, searchIndexObjectPath, } from "../../../../shared/search/artifact-url.js";
37
+ export const SEARCH_INDEX_COLLECTION = "searchIndex";
38
+ export const SEARCH_INDEX_MANIFEST_ID = "manifest";
39
+ /*
40
+ * 🛑 `SEARCH_INDEX_PREFIX` and the object paths are IMPORTED, not declared
41
+ * here. They were declared here first, and the client then needed the same
42
+ * two strings — which is a mirror, and a mirror of a URL fails in the worst
43
+ * available way: the builder writes `search-index/v4.json`, the client asks
44
+ * for `searchIndex/v4.json`, and a 404 is indistinguishable from "the index
45
+ * has not been built yet" (Root Cause #42). One declaration, in the shared
46
+ * module both sides already import.
47
+ */
48
+ /** Current + previous. See the prune note in the header. */
49
+ const KEEP_VERSIONS = 2;
50
+ /**
51
+ * 🛑 A declared ceiling, and the build FAILS rather than truncating.
52
+ *
53
+ * Deliberately the opposite of `buildSearchTxt`'s `MAX_TOKENS`, which this
54
+ * codebase complains about in four separate places for silently slicing. A
55
+ * short artifact is a search index missing rows, and nothing downstream can
56
+ * tell that from a small catalogue. 1.5 MB against a measured 384 KB leaves
57
+ * roughly 4x headroom; if it is ever hit, that is a real signal.
58
+ */
59
+ export const SEARCH_INDEX_MAX_BYTES = 1500000;
60
+ export function manifestRef(db) {
61
+ return db.collection(SEARCH_INDEX_COLLECTION).doc(SEARCH_INDEX_MANIFEST_ID);
62
+ }
63
+ export async function readManifest(db) {
64
+ const snap = await manifestRef(db).get();
65
+ if (!snap.exists)
66
+ return null;
67
+ /*
68
+ * Firestore hands back `DocumentData`, which is `Record<string, any>` — the
69
+ * one place a cast is unavoidable, because the SDK cannot know the shape.
70
+ * Narrowed to the declared interface at the single read site rather than
71
+ * left as `DocumentData` for callers to re-cast: the latter is how a
72
+ * boundary type spreads (Root Cause #98).
73
+ */
74
+ return snap.data() ?? null;
75
+ }
76
+ /**
77
+ * Mark the index stale. Called from the content triggers.
78
+ *
79
+ * 🛑 READ-THEN-WRITE, guarding on a BOOLEAN — never a deep compare.
80
+ * Root Cause #92's trigger cost 12 million invocations a day because its
81
+ * no-op guard used `JSON.stringify`, which is key-order sensitive and so
82
+ * never matched. A boolean cannot have that failure mode, and the read means
83
+ * a burst of 50 product writes costs 50 reads and exactly ONE write.
84
+ *
85
+ * Never throws: a failed dirty-mark delays the index by one cadence, and
86
+ * failing the product write that triggered it would be far worse.
87
+ */
88
+ export async function markSearchIndexDirty(db, job) {
89
+ try {
90
+ const ref = manifestRef(db);
91
+ const snap = await ref.get();
92
+ if (snap.exists && snap.data()?.dirty === true)
93
+ return;
94
+ await ref.set({ dirty: true }, { merge: true });
95
+ }
96
+ catch (err) {
97
+ const e = normalizeError(err);
98
+ serverLogger.warn("markSearchIndexDirty failed (non-fatal)", { job, message: e.message });
99
+ }
100
+ }
101
+ export function storagePathFor(version) {
102
+ return searchIndexObjectPath(version);
103
+ }
104
+ /**
105
+ * Write the artifact and prune older versions.
106
+ *
107
+ * The object carries `public, max-age=31536000, immutable` because the URL
108
+ * contains the version — a new build is a new URL, so nothing ever needs
109
+ * revalidating.
110
+ */
111
+ export async function publishArtifact(bucket, artifact) {
112
+ const body = Buffer.from(JSON.stringify(artifact), "utf8");
113
+ if (body.byteLength > SEARCH_INDEX_MAX_BYTES) {
114
+ // Loud, not truncated. See SEARCH_INDEX_MAX_BYTES.
115
+ throw new Error(`search index artifact is ${body.byteLength} bytes, over the declared ` +
116
+ `${SEARCH_INDEX_MAX_BYTES}. Refusing to publish a silently short index — ` +
117
+ `raise the ceiling deliberately or find out why it grew.`);
118
+ }
119
+ const path = storagePathFor(artifact.version);
120
+ await bucket.file(path).save(body, {
121
+ contentType: "application/json; charset=utf-8",
122
+ metadata: {
123
+ cacheControl: "public, max-age=31536000, immutable",
124
+ contentEncoding: undefined,
125
+ },
126
+ });
127
+ let pruned = 0;
128
+ try {
129
+ const [files] = await bucket.getFiles({ prefix: `${SEARCH_INDEX_PREFIX}/v` });
130
+ const versions = files
131
+ .map((f) => ({ name: f.name, v: Number(/v(\d+)\.json$/.exec(f.name)?.[1] ?? NaN) }))
132
+ .filter((f) => Number.isFinite(f.v))
133
+ .sort((a, b) => b.v - a.v);
134
+ for (const stale of versions.slice(KEEP_VERSIONS)) {
135
+ await bucket.file(stale.name).delete();
136
+ pruned++;
137
+ }
138
+ }
139
+ catch (err) {
140
+ /*
141
+ * A failed prune is NOT a failed publish. The new artifact is already
142
+ * live and correct; the cost of leaving an extra 384 KB object behind is
143
+ * trivial next to failing a build that produced a good index.
144
+ */
145
+ const e = normalizeError(err);
146
+ serverLogger.warn("search index prune failed (artifact is published)", {
147
+ message: e.message,
148
+ });
149
+ }
150
+ return { storagePath: path, bytes: body.byteLength, pruned };
151
+ }
152
+ /**
153
+ * Mirror the manifest to Storage so the client needs no Firebase SDK.
154
+ *
155
+ * Writes only the four fields a browser has any business reading — `dirty`,
156
+ * `lastFullAt` and `perType` are build bookkeeping, and publishing "the index
157
+ * is currently stale" to every visitor tells them nothing they can act on.
158
+ * The shape is `SearchIndexManifestPublic`, declared beside the URL builders.
159
+ *
160
+ * ## 🛑 60 seconds, and the number is load-bearing in both directions
161
+ *
162
+ * This is the ONLY uncacheable-for-long hop in the whole design, so it sets
163
+ * the floor on how fast a new build reaches a visitor — and the ceiling on
164
+ * how often a returning visitor pays for a request. The artifact behind it is
165
+ * `immutable`, so a client that re-reads the manifest and finds the same
166
+ * version does one small conditional fetch and no download at all.
167
+ *
168
+ * Too long and the 15-minute build cadence is a lie; too short and every
169
+ * visitor re-requests it per page view for a document that changes a few
170
+ * times an hour.
171
+ *
172
+ * ## Written LAST, and failing it does not fail the build
173
+ *
174
+ * A published artifact with a stale manifest serves the previous index for up
175
+ * to a minute — correct, just behind. A manifest pointing at an artifact that
176
+ * was never written is a 404 on the client's critical path. So the order is
177
+ * artifact, then Firestore, then this; and a throw here is logged, not
178
+ * propagated, because the Firestore manifest (which is what the NEXT build
179
+ * reads) is already correct by this point.
180
+ */
181
+ export async function publishManifestMirror(bucket, manifest) {
182
+ try {
183
+ const body = Buffer.from(JSON.stringify({
184
+ schema: manifest.schema,
185
+ version: manifest.version,
186
+ builtAt: manifest.builtAt,
187
+ recordCount: manifest.recordCount,
188
+ }), "utf8");
189
+ await bucket.file(SEARCH_MANIFEST_OBJECT).save(body, {
190
+ contentType: "application/json; charset=utf-8",
191
+ metadata: { cacheControl: "public, max-age=60" },
192
+ });
193
+ }
194
+ catch (err) {
195
+ const e = normalizeError(err);
196
+ /*
197
+ * WARN rather than throw. But it is a real symptom: the artifact is live
198
+ * and no client will ever learn its version, so the index silently stops
199
+ * advancing while every other signal says the build succeeded. That is
200
+ * precisely the "confident wrong answer" shape, which is why it is logged
201
+ * at WARN and not swallowed.
202
+ */
203
+ serverLogger.warn("search index manifest mirror failed (artifact is published)", {
204
+ version: manifest.version,
205
+ message: e.message,
206
+ });
207
+ }
208
+ }
@@ -0,0 +1,57 @@
1
+ import { type IndexDocData } from "./sources";
2
+ import { type SearchIndexArtifact, type SearchIndexRecord } from "../../../../shared/search/index-record";
3
+ /** The minimal Firestore surface this module needs, so it stays testable. */
4
+ interface BuildQuery {
5
+ where(field: string, op: string, value: unknown): BuildQuery;
6
+ select(...fields: string[]): BuildQuery;
7
+ get(): Promise<{
8
+ docs: Array<{
9
+ id: string;
10
+ data(): IndexDocData;
11
+ }>;
12
+ }>;
13
+ }
14
+ interface BuildDb {
15
+ collection(name: string): BuildQuery;
16
+ }
17
+ export interface BuildStats {
18
+ readonly mode: "full" | "incremental";
19
+ /** Documents READ. This is the number that has to stay inside the budget. */
20
+ readonly reads: number;
21
+ readonly perType: Readonly<Record<string, number>>;
22
+ /** Documents read but rejected by `toRecord` — unpublished, unjoinable, … */
23
+ readonly dropped: number;
24
+ /**
25
+ * Sources that threw. Non-zero means the artifact is INCOMPLETE for those
26
+ * types, so the caller must not treat the build as clean — see the
27
+ * `dirty` handling in `searchIndexBuild`.
28
+ */
29
+ readonly failed: number;
30
+ readonly durationMs: number;
31
+ }
32
+ export interface BuildResult {
33
+ readonly artifact: SearchIndexArtifact;
34
+ readonly stats: BuildStats;
35
+ }
36
+ /**
37
+ * Build from scratch. Reconciles hard deletes by construction, because the
38
+ * result contains exactly what the sources currently return.
39
+ */
40
+ export declare function buildFull(db: BuildDb, version: number, now?: Date): Promise<BuildResult>;
41
+ /**
42
+ * Patch the previous artifact with everything that changed since it was built.
43
+ *
44
+ * 🛑 A source that fails here is logged and SKIPPED, not fatal — unlike the
45
+ * full build. The difference is what the failure costs: a skipped incremental
46
+ * source leaves that type's records at their previous (correct, slightly
47
+ * stale) values and the next pass picks them up, whereas a skipped full source
48
+ * would delete the type from the artifact entirely.
49
+ */
50
+ export declare function buildIncremental(db: BuildDb, previous: SearchIndexArtifact, since: Date, version: number, now?: Date): Promise<BuildResult>;
51
+ /**
52
+ * Re-key a record from a previous artifact. Mirrors `keyOf` — both sides key
53
+ * on `${typeKey}:${slug}`, which is the invariant that makes an incremental
54
+ * patch REPLACE a changed row rather than add a second copy of it.
55
+ */
56
+ export declare function keyFromRecord(rec: SearchIndexRecord): string | null;
57
+ export {};
@@ -0,0 +1,285 @@
1
+ /*
2
+ * WHY: Turn the public collections into one small artifact the browser can
3
+ * search locally, WITHOUT the read cost that makes the obvious version
4
+ * impossible.
5
+ * WHAT: Full and incremental builds, plus the stats a caller must log.
6
+ *
7
+ * ## 🛑 THE READ BUDGET IS THE DESIGN. Read this before changing the cadence.
8
+ *
9
+ * Firestore bills **per document read regardless of `select()`** — projection
10
+ * cuts egress, not read count. So the naive version, a full rebuild every 15
11
+ * minutes, is `96 × 3,730 ≈ 358,000 reads/day` against a **50,000/day** free
12
+ * tier. Seven times over, for a feature nobody asked to be expensive. Hourly
13
+ * is still ~89,000.
14
+ *
15
+ * Hence two modes:
16
+ *
17
+ * idle manifest says not dirty 1 read ~90/day
18
+ * incremental `updatedAt > lastBuiltAt` per source 13 + changed ~200/day
19
+ * full everything; reconciles hard deletes 3,730 3,730/day
20
+ *
21
+ * ≈ 4,000 reads/day, about 8% of the tier.
22
+ *
23
+ * 🛑 THE INCREMENTAL CLAUSE NEEDS A COMPOSITE INDEX PER SOURCE, and an earlier
24
+ * version of this header claimed the opposite.
25
+ *
26
+ * A LONE range clause on `updatedAt` needs nothing — Firestore auto-creates
27
+ * single-field ascending indexes. But every source that also carries a
28
+ * `where` equality (`status == published`, `categoryType == brand`,
29
+ * `isActive == true`) combines equality + range, and that DOES require a
30
+ * composite. Only `category`, `part` and `model` — the three with no `where`
31
+ * — are index-free.
32
+ *
33
+ * Found by running the job against production, not by reading it: the FULL
34
+ * build has no range clause and passed cleanly, so nothing surfaced until the
35
+ * first INCREMENTAL pass returned `9 FAILED_PRECONDITION` for brand, store,
36
+ * blog and every other filtered source. Exactly the Root Cause #59 shape the
37
+ * old comment claimed immunity from.
38
+ *
39
+ * The indexes are declared in `appkit/firebase/base/firestore.indexes.json`.
40
+ * Adding a source with a `where` means adding its `(field, updatedAt)` pair
41
+ * there too, or that source silently stops updating incrementally.
42
+ *
43
+ * ## Unpublish vs hard delete — different mechanisms, say which is which
44
+ *
45
+ * **Unpublish is handled incrementally.** A product flipped `published ->
46
+ * draft` still has a fresh `updatedAt`, so the builder re-reads it, finds it
47
+ * fails the source's filter, and REMOVES it from the artifact within the
48
+ * cadence. That is the case that matters, because it is the one that happens.
49
+ *
50
+ * **A hard delete is invisible to an `updatedAt` query** and is reconciled by
51
+ * the daily full pass. Worst case is a dead link for up to 24 h, which is link
52
+ * rot rather than disclosure. Tombstone machinery would be real complexity for
53
+ * the rarer half of a split that can be described in two sentences.
54
+ *
55
+ * @tag domain:search
56
+ * @tag layer:server
57
+ * @tag pattern:none
58
+ * @tag access:server
59
+ * @tag consumers:searchIndexBuild
60
+ * @tag sideEffects:firestore-read
61
+ */
62
+ import { normalizeError } from "../../../../../errors/normalize.js";
63
+ import { serverLogger } from "../../../../../monitoring/server-logger.js";
64
+ import { hidePublicTestData } from "../../tester/visibility.js";
65
+ import { INDEX_TYPES, PUBLIC_INDEX_SOURCES, typeIndexFor, } from "./sources.js";
66
+ import { INDEX_UPDATED_AT_FIELD, SEARCH_INDEX_SCHEMA_VERSION, } from "../../../../shared/search/index-record.js";
67
+ /**
68
+ * The patch key, derived from the source's own `slugOf` so the REMOVE path and
69
+ * the ADD path cannot disagree — and so re-keying a previous artifact (which
70
+ * has only the slug) lands on the same string.
71
+ */
72
+ function typeKeyFor(source, d) {
73
+ return source.typeKeyOf ? source.typeKeyOf(d) : source.typeKey;
74
+ }
75
+ function keyOf(source, id, d) {
76
+ const slug = source.slugOf(id, d);
77
+ return slug ? `${typeKeyFor(source, d)}:${slug}` : null;
78
+ }
79
+ async function readSource(db, source, since) {
80
+ let q = db.collection(source.collection);
81
+ for (const [field, value] of source.where ?? [])
82
+ q = q.where(field, "==", value);
83
+ /*
84
+ * 🛑 The incremental clause is applied INSTEAD OF nothing, not in addition
85
+ * to a page limit. An unbounded `updatedAt >` on a quiet collection returns
86
+ * a handful of rows; on a reseed it returns everything, which is correct —
87
+ * a reseed genuinely did change everything.
88
+ */
89
+ if (since)
90
+ q = q.where(INDEX_UPDATED_AT_FIELD, ">", since);
91
+ const snap = await q.select(...source.select, "isTestData", INDEX_UPDATED_AT_FIELD).get();
92
+ /*
93
+ * Firestore bills a minimum of one read for a query that matches nothing,
94
+ * so an idle incremental pass still costs one per source. Counting it is
95
+ * the difference between a budget and a guess.
96
+ */
97
+ return { docs: snap.docs, reads: Math.max(1, snap.docs.length) };
98
+ }
99
+ function toRecords(source, docs) {
100
+ const kept = [];
101
+ const removed = [];
102
+ let dropped = 0;
103
+ /*
104
+ * 🛑 `hidePublicTestData` is NOT optional here, and this call site differs
105
+ * from every other one. Elsewhere a sandbox row that slips through is shown
106
+ * to one viewer on one render. Here it is baked into a shared, immutable,
107
+ * CDN-cached body and published to everyone for the life of that version.
108
+ */
109
+ /*
110
+ * 🛑 An INTERSECTION, not a cast. `IndexDocData` is `Record<string,
111
+ * unknown>`, whose index signature makes `isTestData` an `unknown` and so
112
+ * fails `TestDataFlagged`. The lazy fix is `as any`, which would also
113
+ * silence the fact that `hidePublicTestData` is generic and preserves the
114
+ * row type — including `__id`, which the loop below needs. Naming the shape
115
+ * keeps both ends type-checked.
116
+ */
117
+ const rows = docs.map((d) => ({ ...d.data(), __id: d.id }));
118
+ const visible = hidePublicTestData(rows);
119
+ for (const row of visible) {
120
+ const id = row.__id;
121
+ const key = keyOf(source, id, row);
122
+ // No derivable slug means no href and no stable identity — such a row
123
+ // cannot be a search result at all, so it is dropped rather than keyed
124
+ // under something invented.
125
+ if (!key) {
126
+ dropped++;
127
+ continue;
128
+ }
129
+ const rec = source.toRecord(id, row);
130
+ if (!rec) {
131
+ dropped++;
132
+ // A document that fails `toRecord` on an INCREMENTAL pass is usually a
133
+ // row that just became non-public — so it must be removed, not ignored.
134
+ removed.push(key);
135
+ continue;
136
+ }
137
+ /*
138
+ * 🛑 Resolved per DOCUMENT, not per source. `products` holds all nine
139
+ * listing types and each has its own detail route, so a single type index
140
+ * for the collection would send every auction to /products/{slug} —
141
+ * Root Cause #48, rebuilt.
142
+ */
143
+ const typeKey = typeKeyFor(source, row);
144
+ const typeIdx = typeIndexFor(typeKey);
145
+ if (typeIdx < 0) {
146
+ dropped++;
147
+ continue;
148
+ }
149
+ const slug = key.slice(typeKey.length + 1);
150
+ const tuple = rec.facets === undefined
151
+ ? [typeIdx, rec.title, rec.detail, slug]
152
+ : [typeIdx, rec.title, rec.detail, slug, rec.facets];
153
+ kept.push([key, tuple]);
154
+ }
155
+ return { kept, removed, dropped };
156
+ }
157
+ function envelope(version, keyed, builtAt) {
158
+ return {
159
+ schema: SEARCH_INDEX_SCHEMA_VERSION,
160
+ version,
161
+ builtAt: builtAt.toISOString(),
162
+ types: INDEX_TYPES,
163
+ // Sorted by key so two builds over identical data produce identical bytes,
164
+ // which is what makes "did anything actually change" answerable.
165
+ r: Object.keys(keyed)
166
+ .sort()
167
+ .map((k) => keyed[k]),
168
+ };
169
+ }
170
+ /**
171
+ * Build from scratch. Reconciles hard deletes by construction, because the
172
+ * result contains exactly what the sources currently return.
173
+ */
174
+ export async function buildFull(db, version, now = new Date()) {
175
+ const started = Date.now();
176
+ const keyed = {};
177
+ const perType = {};
178
+ let reads = 0;
179
+ let dropped = 0;
180
+ for (const source of PUBLIC_INDEX_SOURCES) {
181
+ try {
182
+ const { docs, reads: r } = await readSource(db, source, null);
183
+ reads += r;
184
+ const out = toRecords(source, docs);
185
+ for (const [k, rec] of out.kept) {
186
+ keyed[k] = rec;
187
+ const tk = INDEX_TYPES[rec[0]]?.key ?? source.typeKey;
188
+ perType[tk] = (perType[tk] ?? 0) + 1;
189
+ }
190
+ dropped += out.dropped;
191
+ }
192
+ catch (err) {
193
+ const e = normalizeError(err);
194
+ /*
195
+ * 🛑 A failing source does NOT silently shrink the artifact. A full
196
+ * build that quietly omits `products` would publish a search index
197
+ * missing a third of the site and report success — the exact shape of
198
+ * every silent-degradation bug this codebase keeps recording.
199
+ */
200
+ serverLogger.error("searchIndexBuild: source failed during FULL build — aborting", {
201
+ source: source.typeKey,
202
+ collection: source.collection,
203
+ error: e.message,
204
+ });
205
+ throw err;
206
+ }
207
+ }
208
+ return {
209
+ artifact: envelope(version, keyed, now),
210
+ stats: {
211
+ mode: "full",
212
+ reads,
213
+ perType,
214
+ dropped,
215
+ failed: 0,
216
+ durationMs: Date.now() - started,
217
+ },
218
+ };
219
+ }
220
+ /**
221
+ * Patch the previous artifact with everything that changed since it was built.
222
+ *
223
+ * 🛑 A source that fails here is logged and SKIPPED, not fatal — unlike the
224
+ * full build. The difference is what the failure costs: a skipped incremental
225
+ * source leaves that type's records at their previous (correct, slightly
226
+ * stale) values and the next pass picks them up, whereas a skipped full source
227
+ * would delete the type from the artifact entirely.
228
+ */
229
+ export async function buildIncremental(db, previous, since, version, now = new Date()) {
230
+ const started = Date.now();
231
+ // Re-key the previous artifact. The tuple carries no id, so the key is
232
+ // rebuilt from type + slug — which is why `slug` must be stable per record.
233
+ const keyed = {};
234
+ for (const rec of previous.r) {
235
+ const key = keyFromRecord(rec);
236
+ if (key)
237
+ keyed[key] = rec;
238
+ }
239
+ const perType = {};
240
+ let reads = 0;
241
+ let dropped = 0;
242
+ let failed = 0;
243
+ for (const source of PUBLIC_INDEX_SOURCES) {
244
+ try {
245
+ const { docs, reads: r } = await readSource(db, source, since);
246
+ reads += r;
247
+ const out = toRecords(source, docs);
248
+ for (const k of out.removed)
249
+ delete keyed[k];
250
+ for (const [k, rec] of out.kept)
251
+ keyed[k] = rec;
252
+ perType[source.typeKey] = out.kept.length;
253
+ dropped += out.dropped;
254
+ }
255
+ catch (err) {
256
+ failed++;
257
+ const e = normalizeError(err);
258
+ serverLogger.warn("searchIndexBuild: source failed during INCREMENTAL build — skipped", {
259
+ source: source.typeKey,
260
+ collection: source.collection,
261
+ error: e.message,
262
+ });
263
+ }
264
+ }
265
+ return {
266
+ artifact: envelope(version, keyed, now),
267
+ stats: {
268
+ mode: "incremental",
269
+ reads,
270
+ perType,
271
+ dropped,
272
+ failed,
273
+ durationMs: Date.now() - started,
274
+ },
275
+ };
276
+ }
277
+ /**
278
+ * Re-key a record from a previous artifact. Mirrors `keyOf` — both sides key
279
+ * on `${typeKey}:${slug}`, which is the invariant that makes an incremental
280
+ * patch REPLACE a changed row rather than add a second copy of it.
281
+ */
282
+ export function keyFromRecord(rec) {
283
+ const type = INDEX_TYPES[rec[0]];
284
+ return type ? `${type.key}:${rec[3]}` : null;
285
+ }