@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
@@ -36,7 +36,23 @@
36
36
  */
37
37
  export function buildCategoryTree(roots, opts) {
38
38
  const rows = [];
39
- let position = 0;
39
+ /*
40
+ * 🛑 1-BASED, and that is not cosmetic — it must match `positionsReconcile`,
41
+ * which starts its own DFS at `let counter = 1`.
42
+ *
43
+ * This counter started at 0 until 2026-10-10. The nightly reconcile then
44
+ * found every row off by one (`current.position !== position`) and rewrote
45
+ * the entire collection on the first run after any reseed — 58 writes then,
46
+ * ~330 once the catalogue grows, each re-entering `onCategoryWrite` as a
47
+ * no-op invocation. Measured in production before the change: seed emits
48
+ * 0..57, live data reads 1..58, i.e. the reconcile had already corrected
49
+ * all of it.
50
+ *
51
+ * It also makes position 0 UNAMBIGUOUS, which `handleCategoryWrite` now
52
+ * relies on: a document arriving with `position >= 1` was numbered by this
53
+ * builder, and one with 0/absent was not.
54
+ */
55
+ let position = 1;
40
56
  function visit(node, rootId,
41
57
  /** Ancestors nearest-last, matching how `parentIds` is read elsewhere. */
42
58
  ancestors, tier, parentPath, order) {
@@ -0,0 +1,10 @@
1
+ /** A blader, and how many canonical beys the corpus attributes to them. */
2
+ export interface CharacterSeed {
3
+ /** Without the `feature-` prefix — the factory prepends it. */
4
+ slug: string;
5
+ name: string;
6
+ /** Canonical beys owned. Drives display order; not stored on the row. */
7
+ beyCount: number;
8
+ }
9
+ export declare const CHARACTER_SEEDS: CharacterSeed[];
10
+ export declare const CHARACTER_SEED_COUNT = 93;
@@ -0,0 +1,118 @@
1
+ /*
2
+ * GENERATED by scripts/gen-category-models.mjs — do not edit by hand.
3
+ *
4
+ * WHY: the clickable owner linkage. `Owner (Anime)` is 100% populated in the
5
+ * corpus but it is a SENTENCE — "Tyson Granger (Tyson's 9th and final
6
+ * bey)" — so it renders as prose and filters nothing. These are the
7
+ * normalised names as `character`-group features, which makes
8
+ * "show me every Tyson Granger bey" one click from any listing page.
9
+ *
10
+ * 🛑 A FEATURE and never a category node. A blader crosses every generation —
11
+ * Tyson owns nine beys spanning Spin Gear, Magnacore, Engine Gear and HMS —
12
+ * so a tree node would duplicate all nine under a second parent and break
13
+ * the one-chain-per-product invariant. As a feature it COMPOSES with the
14
+ * generation tree instead of competing with it: browse Beyblade Original,
15
+ * then narrow to Tyson's.
16
+ *
17
+ * @tag domain:products
18
+ * @tag layer:seed
19
+ * @tag pattern:generated
20
+ * @tag access:server-only
21
+ * @tag sideEffects:none
22
+ */
23
+ export const CHARACTER_SEEDS = [
24
+ { slug: "blader-tyson-granger", name: "Tyson Granger", beyCount: 9 },
25
+ { slug: "blader-kai-hiwatari", name: "Kai Hiwatari", beyCount: 7 },
26
+ { slug: "blader-daichi-sumeragi", name: "Daichi Sumeragi", beyCount: 6 },
27
+ { slug: "blader-max-tate", name: "Max Tate", beyCount: 6 },
28
+ { slug: "blader-ray-kon", name: "Ray Kon", beyCount: 6 },
29
+ { slug: "blader-shu-kurenai", name: "Shu Kurenai", beyCount: 6 },
30
+ { slug: "blader-gingka-hagane", name: "Gingka Hagane", beyCount: 5 },
31
+ { slug: "blader-lui-shirosagi", name: "Lui Shirosagi", beyCount: 5 },
32
+ { slug: "blader-tala", name: "Tala", beyCount: 5 },
33
+ { slug: "blader-valt-aoi", name: "Valt Aoi", beyCount: 5 },
34
+ { slug: "blader-free-de-la-hoya", name: "Free De La Hoya", beyCount: 4 },
35
+ { slug: "blader-aiger-akabane", name: "Aiger Akabane", beyCount: 3 },
36
+ { slug: "blader-dante-koryu", name: "Dante Koryu", beyCount: 3 },
37
+ { slug: "blader-jaxon-cross", name: "Jaxon Cross", beyCount: 3 },
38
+ { slug: "blader-kenta-yumiya", name: "Kenta Yumiya", beyCount: 3 },
39
+ { slug: "blader-kyoya-tategami", name: "Kyoya Tategami", beyCount: 3 },
40
+ { slug: "blader-multi-nana-iro", name: "Multi Nana-iro", beyCount: 3 },
41
+ { slug: "blader-ryuga", name: "Ryuga", beyCount: 3 },
42
+ { slug: "blader-benkei-hanawa", name: "Benkei Hanawa", beyCount: 2 },
43
+ { slug: "blader-daigo-kurogami", name: "Daigo Kurogami", beyCount: 2 },
44
+ { slug: "blader-damian-hart", name: "Damian Hart", beyCount: 2 },
45
+ { slug: "blader-dashan-wang", name: "Dashan Wang", beyCount: 2 },
46
+ { slug: "blader-delta-zakuro", name: "Delta Zakuro", beyCount: 2 },
47
+ { slug: "blader-dunga", name: "Dunga", beyCount: 2 },
48
+ { slug: "blader-eddy", name: "Eddy", beyCount: 2 },
49
+ { slug: "blader-fumiya-kindo", name: "Fumiya Kindo", beyCount: 2 },
50
+ { slug: "blader-hikaru-hizashi", name: "Hikaru Hizashi", beyCount: 2 },
51
+ { slug: "blader-hyde", name: "Hyde", beyCount: 2 },
52
+ { slug: "blader-jin-of-the-gale", name: "Jin of the Gale", beyCount: 2 },
53
+ { slug: "blader-kira-hayama", name: "Kira Hayama", beyCount: 2 },
54
+ { slug: "blader-lee", name: "Lee", beyCount: 2 },
55
+ { slug: "blader-masamune-kadoya", name: "Masamune Kadoya", beyCount: 2 },
56
+ { slug: "blader-michael-summers", name: "Michael Summers", beyCount: 2 },
57
+ { slug: "blader-miguel", name: "Miguel", beyCount: 2 },
58
+ { slug: "blader-ozuma", name: "Ozuma", beyCount: 2 },
59
+ { slug: "blader-robin-kazami", name: "Robin Kazami", beyCount: 2 },
60
+ { slug: "blader-tsubasa-otori", name: "Tsubasa Otori", beyCount: 2 },
61
+ { slug: "blader-wakiya-murasaki", name: "Wakiya Murasaki", beyCount: 2 },
62
+ { slug: "blader-xander-shakadera", name: "Xander Shakadera", beyCount: 2 },
63
+ { slug: "blader-yu-tendo", name: "Yu Tendo", beyCount: 2 },
64
+ { slug: "blader-aguma", name: "Aguma", beyCount: 1 },
65
+ { slug: "blader-akuya-onizaki", name: "Akuya Onizaki", beyCount: 1 },
66
+ { slug: "blader-anime", name: "Anime", beyCount: 1 },
67
+ { slug: "blader-anime-secondary", name: "Anime-secondary", beyCount: 1 },
68
+ { slug: "blader-blader-dj", name: "Blader DJ", beyCount: 1 },
69
+ { slug: "blader-brooklyn", name: "Brooklyn", beyCount: 1 },
70
+ { slug: "blader-chris", name: "Chris", beyCount: 1 },
71
+ { slug: "blader-claude", name: "Claude", beyCount: 1 },
72
+ { slug: "blader-clio-delon", name: "Clio Delon", beyCount: 1 },
73
+ { slug: "blader-crusher", name: "Crusher", beyCount: 1 },
74
+ { slug: "blader-cycnus", name: "Cycnus", beyCount: 1 },
75
+ { slug: "blader-dan-sodo-and-reiki-sodo", name: "Dan Sodo and Reiki Sodo", beyCount: 1 },
76
+ { slug: "blader-doji", name: "Doji", beyCount: 1 },
77
+ { slug: "blader-faust", name: "Faust", beyCount: 1 },
78
+ { slug: "blader-garland-siebald", name: "Garland Siebald", beyCount: 1 },
79
+ { slug: "blader-gary", name: "Gary", beyCount: 1 },
80
+ { slug: "blader-ginro", name: "Ginro", beyCount: 1 },
81
+ { slug: "blader-hikaru-hasama", name: "Hikaru Hasama", beyCount: 1 },
82
+ { slug: "blader-hokuto", name: "Hokuto", beyCount: 1 },
83
+ { slug: "blader-hyoma", name: "Hyoma", beyCount: 1 },
84
+ { slug: "blader-hyuga-hizashi", name: "Hyuga Hizashi", beyCount: 1 },
85
+ { slug: "blader-ian-papov", name: "Ian Papov", beyCount: 1 },
86
+ { slug: "blader-johnny-mcgregor", name: "Johnny McGregor", beyCount: 1 },
87
+ { slug: "blader-joseph-burton", name: "Joseph Burton", beyCount: 1 },
88
+ { slug: "blader-julia-fernandez", name: "Julia Fernandez", beyCount: 1 },
89
+ { slug: "blader-julian-konzern", name: "Julian Konzern", beyCount: 1 },
90
+ { slug: "blader-karla-konjiki", name: "Karla Konjiki", beyCount: 1 },
91
+ { slug: "blader-ken-midori", name: "Ken Midori", beyCount: 1 },
92
+ { slug: "blader-kenny", name: "Kenny", beyCount: 1 },
93
+ { slug: "blader-kevin", name: "Kevin", beyCount: 1 },
94
+ { slug: "blader-kite-unabara", name: "Kite Unabara", beyCount: 1 },
95
+ { slug: "blader-lain-valhalla", name: "Lain Valhalla", beyCount: 1 },
96
+ { slug: "blader-meiko-myoden", name: "Meiko Myoden", beyCount: 1 },
97
+ { slug: "blader-nile", name: "Nile", beyCount: 1 },
98
+ { slug: "blader-nine-kurosu", name: "Nine Kurosu", beyCount: 1 },
99
+ { slug: "blader-omega-shiroboshi", name: "Omega Shiroboshi", beyCount: 1 },
100
+ { slug: "blader-phi", name: "Phi", beyCount: 1 },
101
+ { slug: "blader-rago", name: "Rago", beyCount: 1 },
102
+ { slug: "blader-reiji-mizuchi", name: "Reiji Mizuchi", beyCount: 1 },
103
+ { slug: "blader-reiyu-kuwabara", name: "Reiyu Kuwabara", beyCount: 1 },
104
+ { slug: "blader-rex-jura", name: "Rex Jura", beyCount: 1 },
105
+ { slug: "blader-rick-anderson", name: "Rick Anderson", beyCount: 1 },
106
+ { slug: "blader-robert-jurgens", name: "Robert Jurgens", beyCount: 1 },
107
+ { slug: "blader-sakyo-kurayami", name: "Sakyo Kurayami", beyCount: 1 },
108
+ { slug: "blader-silas-karlisle", name: "Silas Karlisle", beyCount: 1 },
109
+ { slug: "blader-spencer", name: "Spencer", beyCount: 1 },
110
+ { slug: "blader-teru-saotome", name: "Teru Saotome", beyCount: 1 },
111
+ { slug: "blader-tobio-oike", name: "Tobio Oike", beyCount: 1 },
112
+ { slug: "blader-toby", name: "Toby", beyCount: 1 },
113
+ { slug: "blader-toguro-okunaga", name: "Toguro Okunaga", beyCount: 1 },
114
+ { slug: "blader-yuki-mizusawa", name: "Yuki Mizusawa", beyCount: 1 },
115
+ { slug: "blader-yuni-naniwa", name: "Yuni Naniwa", beyCount: 1 },
116
+ { slug: "blader-zyro-kurogane", name: "Zyro Kurogane", beyCount: 1 },
117
+ ];
118
+ export const CHARACTER_SEED_COUNT = 93;
@@ -17,4 +17,4 @@ export declare function seedPhotoLabel(seed: string): string;
17
17
  * `appkit-seed` invocation, so anything non-stable here would give the same
18
18
  * fixture a different URL on every run.
19
19
  */
20
- export declare function seedPhoto(seed: string, width: number, height: number): string;
20
+ export declare function seedPhoto(seed: string, _width?: number, _height?: number): string;
@@ -47,16 +47,49 @@ export function seedExtMedia(url) {
47
47
  * photography for a demo catalogue: every card now says what it is, and nobody
48
48
  * can mistake seeded data for real inventory.
49
49
  */
50
- /** The one place a seed image host is named. Changing it is a one-line edit. */
51
- const SEED_PHOTO_HOST = "https://placehold.co";
50
+ /*
51
+ * 🛑 SUPERSEDED 2026-10-09 — seed photography is now LOCAL and costs nothing.
52
+ *
53
+ * The reasoning above is still correct about third-party fragility, and the
54
+ * one-line-to-move property is still the goal. What it missed is the cost: every
55
+ * one of these URLs was persisted as `/api/media/ext?url=…`, so every render of
56
+ * every card was a Node lambda doing a third-party fetch (up to 2 × 4 s) plus a
57
+ * full sharp decode/watermark/encode — ONE INVOCATION PER IMAGE. The homepage
58
+ * alone referenced 160 of them at ~49.5 KB; `52.52 GB of origin transfer ÷
59
+ * 49.5 KB ≈ 1.06 M` proxy responses in a week, which suspended the project
60
+ * (HTTP 402).
61
+ *
62
+ * A placeholder is synthetic by definition, so there was never anything to
63
+ * fetch. Six static tiles ship in `public/images/seed-tiles/`, chosen by the
64
+ * same deterministic hash, and the homepage goes 160 images → 6.
65
+ *
66
+ * The trade accepted: the tile no longer renders the item's NAME. That text was
67
+ * a genuine gain and it is lost here — but it cost a function invocation per
68
+ * card to render text the card already displays beside the image, and `alt` still
69
+ * carries it for assistive tech. `seedPhotoLabel` is kept and still exported for
70
+ * exactly that use.
71
+ *
72
+ * 🛑 Do NOT reintroduce a third-party host here, and do NOT encode width/height
73
+ * into the path — a per-size URL is a per-size CDN cache key, which is how 6
74
+ * files become 400 again. `audit-media-proxy-hosts` blocks the first; this
75
+ * comment is the only thing guarding the second.
76
+ */
77
+ const SEED_TILE_PREFIX = "/images/seed-tiles/";
52
78
  /**
53
79
  * Muted backgrounds, all dark enough for the same light foreground.
54
80
  *
55
- * Raw hex rather than theme tokens because these are baked into a URL that a
56
- * third party renders — no stylesheet and no CSS custom property is in scope.
81
+ * Raw hex rather than theme tokens because an SVG served as the body of an
82
+ * `<img>` has no stylesheet and no `var(--appkit-color-*)` in scope — the same
83
+ * reason `_placeholder.ts` keeps its artwork in an asset file.
84
+ *
85
+ * 🛑 These six values ARE the filenames in `public/images/seed-tiles/`, and they
86
+ * are mirrored a third time in `PLACEHOLDER_MEDIA_HOSTS`' sibling
87
+ * `SEED_TILE_COLOURS` (`appkit/src/utils/media-url.ts`), which maps a legacy
88
+ * stored `placehold.co` URL back to the same tile. Add a colour and all three
89
+ * must move together; a missing one degrades to a deterministic fallback tile
90
+ * rather than a broken image, so the failure mode is "less variety", never a 404.
57
91
  */
58
92
  const SEED_PHOTO_COLOURS = ["1e293b", "334155", "3f3f46", "44403c", "312e81", "164e63"];
59
- const SEED_PHOTO_FG = "f8fafc";
60
93
  /** Deterministic 32-bit hash. Same seed, same colour, every reseed. */
61
94
  function seedHash(input) {
62
95
  let h = 2166136261;
@@ -95,8 +128,11 @@ export function seedPhotoLabel(seed) {
95
128
  * `appkit-seed` invocation, so anything non-stable here would give the same
96
129
  * fixture a different URL on every run.
97
130
  */
98
- export function seedPhoto(seed, width, height) {
131
+ export function seedPhoto(seed, _width, _height) {
99
132
  const bg = SEED_PHOTO_COLOURS[seedHash(seed) % SEED_PHOTO_COLOURS.length];
100
- const text = encodeURIComponent(seedPhotoLabel(seed));
101
- return seedExtMedia(`${SEED_PHOTO_HOST}/${width}x${height}/${bg}/${SEED_PHOTO_FG}/png?text=${text}`);
133
+ // Width/height are accepted and ignored. All 366 call sites pass them, and the
134
+ // tiles carry `preserveAspectRatio="xMidYMid slice"` so one file fits every
135
+ // aspect — baking the dimensions into the path would mint a separate cache
136
+ // entry per size, which is the thing this change removes.
137
+ return `${SEED_TILE_PREFIX}${bg}.svg`;
102
138
  }
@@ -1,3 +1,4 @@
1
+ import type { CategoryDocument } from "../../features/categories/schemas/firestore";
1
2
  import type { StoreDocument } from "../../features/stores/schemas/firestore";
2
3
  import type { ScammerDocument } from "../../features/scams/schemas/firestore";
3
4
  import type { EventDocument } from "../../features/events/schemas/firestore";
@@ -6,6 +7,15 @@ import type { ReviewDocument } from "../../features/reviews/schemas/firestore";
6
7
  import type { OrderDocument } from "../../features/orders/schemas/firestore";
7
8
  import type { CouponDocument } from "../../features/promotions/schemas/firestore";
8
9
  import type { OfferDocument } from "../../features/seller/schemas/firestore";
10
+ /**
11
+ * 🛑 Apply AFTER `buildCategoryTree`, never before.
12
+ *
13
+ * `buildCategorySearchTxt` indexes `path` and `ancestors[].name`, and both are
14
+ * DERIVED by the tree builder — a row wrapped before derivation gets tokens
15
+ * from `name`/`slug`/`description` only, and silently loses the lineage search
16
+ * that is the whole reason ancestors are indexed.
17
+ */
18
+ export declare function withCategorySearchTxt<T extends Partial<CategoryDocument>>(p: T): T;
9
19
  export declare function withStoreSearchTxt<T extends Partial<StoreDocument>>(p: T): T;
10
20
  export declare function withEventSearchTxt<T extends Partial<EventDocument>>(p: T): T;
11
21
  export declare function withBlogSearchTxt<T extends Partial<BlogPostDocument>>(p: T): T;
@@ -1,4 +1,15 @@
1
- import { buildStoreSearchTxt, buildEventSearchTxt, buildBlogSearchTxt, buildReviewSearchTxt, buildOrderSearchTxt, buildCouponSearchTxt, buildOfferSearchTxt, buildScammerSearchTxt, } from "../../utils/search-txt-builders";
1
+ import { buildCategorySearchTxt, buildStoreSearchTxt, buildEventSearchTxt, buildBlogSearchTxt, buildReviewSearchTxt, buildOrderSearchTxt, buildCouponSearchTxt, buildOfferSearchTxt, buildScammerSearchTxt, } from "../../utils/search-txt-builders";
2
+ /**
3
+ * 🛑 Apply AFTER `buildCategoryTree`, never before.
4
+ *
5
+ * `buildCategorySearchTxt` indexes `path` and `ancestors[].name`, and both are
6
+ * DERIVED by the tree builder — a row wrapped before derivation gets tokens
7
+ * from `name`/`slug`/`description` only, and silently loses the lineage search
8
+ * that is the whole reason ancestors are indexed.
9
+ */
10
+ export function withCategorySearchTxt(p) {
11
+ return { ...p, searchTxt: buildCategorySearchTxt(p) };
12
+ }
2
13
  export function withStoreSearchTxt(p) {
3
14
  return { ...p, searchTxt: buildStoreSearchTxt(p) };
4
15
  }