@mohasinac/appkit 4.45.0 → 4.46.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 (152) hide show
  1. package/dist/.build-stamp.json +4 -4
  2. package/dist/_internal/server/features/categories/live-counts.d.ts +41 -0
  3. package/dist/_internal/server/features/categories/live-counts.js +72 -0
  4. package/dist/_internal/server/features/checkout/expand-order-items.js +17 -0
  5. package/dist/_internal/server/features/orders/adapters.js +29 -0
  6. package/dist/_internal/server/features/parts/assembly.d.ts +126 -0
  7. package/dist/_internal/server/features/parts/assembly.js +246 -0
  8. package/dist/_internal/server/features/parts/data.d.ts +65 -0
  9. package/dist/_internal/server/features/parts/data.js +170 -0
  10. package/dist/_internal/server/features/parts/og.d.ts +34 -0
  11. package/dist/_internal/server/features/parts/og.js +66 -0
  12. package/dist/_internal/server/features/search/search-everything.d.ts +75 -0
  13. package/dist/_internal/server/features/search/search-everything.js +120 -0
  14. package/dist/_internal/server/features/search/search-sources.d.ts +17 -0
  15. package/dist/_internal/server/features/search/search-sources.js +138 -0
  16. package/dist/_internal/server/jobs/core/categoryTaxonomyBackfill.d.ts +70 -0
  17. package/dist/_internal/server/jobs/core/categoryTaxonomyBackfill.js +302 -0
  18. package/dist/_internal/server/jobs/core/countersReconcile.js +128 -19
  19. package/dist/_internal/server/jobs/core/jobRunners.js +12 -0
  20. package/dist/_internal/server/jobs/core/onCategoryWrite.js +73 -3
  21. package/dist/_internal/server/jobs/core/onProductWrite.js +93 -43
  22. package/dist/_internal/server/jobs/core/productViewWindows.d.ts +2 -0
  23. package/dist/_internal/server/jobs/core/productViewWindows.js +173 -0
  24. package/dist/_internal/server/jobs/core/testDataCleanup.d.ts +1 -1
  25. package/dist/_internal/server/jobs/core/testDataCleanup.js +27 -1
  26. package/dist/_internal/server/jobs/handlers/pageViewRollup.d.ts +20 -0
  27. package/dist/_internal/server/jobs/handlers/pageViewRollup.js +25 -1
  28. package/dist/_internal/shared/actions/action-registry.js +36 -0
  29. package/dist/_internal/shared/features/promotions/schema.d.ts +2 -2
  30. package/dist/_internal/shared/features/shipping/protections.d.ts +93 -1
  31. package/dist/_internal/shared/features/shipping/protections.js +127 -0
  32. package/dist/_internal/shared/search/part-aliases.d.ts +44 -0
  33. package/dist/_internal/shared/search/part-aliases.js +104 -0
  34. package/dist/constants/api-endpoints.d.ts +18 -12
  35. package/dist/constants/api-endpoints.js +6 -8
  36. package/dist/constants/field-names.d.ts +0 -2
  37. package/dist/constants/field-names.js +0 -2
  38. package/dist/constants/index.d.ts +1 -1
  39. package/dist/constants/index.js +1 -1
  40. package/dist/core/index.d.ts +0 -3
  41. package/dist/core/index.js +0 -2
  42. package/dist/errors/messages.d.ts +0 -7
  43. package/dist/errors/messages.js +0 -8
  44. package/dist/features/admin/components/AdminSectionsView.js +83 -32
  45. package/dist/features/admin/components/AdminSiteSettingsView.js +6 -2
  46. package/dist/features/admin/components/sections/sectionTemplates.d.ts +117 -0
  47. package/dist/features/admin/components/sections/sectionTemplates.js +213 -0
  48. package/dist/features/admin/schemas/firestore.d.ts +15 -0
  49. package/dist/features/admin/types/index.d.ts +0 -1
  50. package/dist/features/auth/permissions/constants.d.ts +1 -1
  51. package/dist/features/auth/permissions/constants.js +0 -2
  52. package/dist/features/categories/api/[id]/description-template/route.js +40 -1
  53. package/dist/features/categories/components/BrandDetailPageView.js +14 -3
  54. package/dist/features/categories/components/CategoryDetailPageView.js +89 -14
  55. package/dist/features/categories/components/CategoryTemplatePicker.js +9 -1
  56. package/dist/features/categories/repository/categories.repository.d.ts +65 -0
  57. package/dist/features/categories/repository/categories.repository.js +168 -22
  58. package/dist/features/categories/repository/category-counts.d.ts +143 -0
  59. package/dist/features/categories/repository/category-counts.js +224 -0
  60. package/dist/features/categories/schemas/category-counts.d.ts +155 -0
  61. package/dist/features/categories/schemas/category-counts.js +116 -0
  62. package/dist/features/categories/schemas/firestore.d.ts +0 -2
  63. package/dist/features/categories/schemas/firestore.js +0 -1
  64. package/dist/features/categories/utils/breadcrumb-chain.d.ts +194 -0
  65. package/dist/features/categories/utils/breadcrumb-chain.js +202 -0
  66. package/dist/features/corporate/schemas/index.d.ts +4 -4
  67. package/dist/features/orders/schemas/firestore.d.ts +13 -0
  68. package/dist/features/orders/types/index.d.ts +30 -0
  69. package/dist/features/parts/components/AssemblyDrawer.d.ts +107 -0
  70. package/dist/features/parts/components/AssemblyDrawer.js +250 -0
  71. package/dist/features/parts/components/ModelCompositionPanel.d.ts +37 -0
  72. package/dist/features/parts/components/ModelCompositionPanel.js +34 -0
  73. package/dist/features/parts/components/PartDetailPageView.d.ts +48 -0
  74. package/dist/features/parts/components/PartDetailPageView.js +20 -0
  75. package/dist/features/parts/components/PartsIndexPageView.d.ts +33 -0
  76. package/dist/features/parts/components/PartsIndexPageView.js +17 -0
  77. package/dist/features/parts/schemas/firestore.d.ts +257 -0
  78. package/dist/features/parts/schemas/firestore.js +181 -0
  79. package/dist/features/parts/utils/colourway-match.d.ts +62 -0
  80. package/dist/features/parts/utils/colourway-match.js +98 -0
  81. package/dist/features/parts/utils/part-breakdown.d.ts +53 -0
  82. package/dist/features/parts/utils/part-breakdown.js +77 -0
  83. package/dist/features/products/components/ProductDetailPageView.d.ts +14 -1
  84. package/dist/features/products/components/ProductDetailPageView.js +22 -3
  85. package/dist/features/products/components/ProductTabsShell.d.ts +3 -1
  86. package/dist/features/products/components/ProductTabsShell.js +4 -1
  87. package/dist/features/products/repository/category-assignment.d.ts +132 -0
  88. package/dist/features/products/repository/category-assignment.js +304 -0
  89. package/dist/features/products/repository/products.repository.d.ts +58 -1
  90. package/dist/features/products/repository/products.repository.js +255 -28
  91. package/dist/features/products/schemas/firestore.d.ts +147 -6
  92. package/dist/features/products/schemas/firestore.js +49 -0
  93. package/dist/features/search/components/SearchResultsPageView.d.ts +33 -0
  94. package/dist/features/search/components/SearchResultsPageView.js +21 -0
  95. package/dist/features/seller/components/SellerProductsView.js +129 -3
  96. package/dist/features/tester/seed-data/authored/admin__site-system.js +0 -15
  97. package/dist/features/tester/seed-data/authored/content-discovery__parts-database.d.ts +2 -0
  98. package/dist/features/tester/seed-data/authored/content-discovery__parts-database.js +166 -0
  99. package/dist/features/tester/seed-data/authored/index.js +2 -0
  100. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +28 -1
  101. package/dist/index.d.ts +11 -16
  102. package/dist/index.js +26 -18
  103. package/dist/next/routing/route-map.d.ts +6 -2
  104. package/dist/next/routing/route-map.js +14 -1
  105. package/dist/providers/db-firebase/base.repository.d.ts +31 -0
  106. package/dist/providers/db-firebase/base.repository.js +31 -0
  107. package/dist/repositories/index.d.ts +1 -2
  108. package/dist/repositories/index.js +0 -1
  109. package/dist/seed/_helpers/category-content.d.ts +29 -0
  110. package/dist/seed/_helpers/category-content.js +98 -1
  111. package/dist/seed/_helpers/category-forest.js +104 -24
  112. package/dist/seed/_helpers/category-parts.d.ts +9 -0
  113. package/dist/seed/_helpers/category-parts.js +4594 -0
  114. package/dist/seed/carousel-slides-seed-data.js +81 -8
  115. package/dist/seed/categories-seed-data.js +44 -6
  116. package/dist/seed/homepage-sections-seed-data.js +170 -9
  117. package/dist/seed/index.d.ts +2 -0
  118. package/dist/seed/index.js +13 -0
  119. package/dist/seed/model-composition-seed-data.d.ts +15 -0
  120. package/dist/seed/model-composition-seed-data.js +8918 -0
  121. package/dist/seed/part-glossary-seed-data.d.ts +15 -0
  122. package/dist/seed/part-glossary-seed-data.js +10729 -0
  123. package/dist/seed/products-standard-seed-data.js +52 -0
  124. package/dist/seed/seed-purpose.d.ts +103 -4
  125. package/dist/seed/seed-purpose.js +162 -11
  126. package/dist/seo/json-ld.d.ts +10 -1
  127. package/dist/seo/json-ld.js +9 -1
  128. package/dist/server.d.ts +7 -0
  129. package/dist/server.js +11 -0
  130. package/dist/styles.css +1 -1
  131. package/dist/tailwind-utilities.css +1 -1
  132. package/dist/utils/id-generators.d.ts +30 -0
  133. package/dist/utils/id-generators.js +31 -1
  134. package/dist/utils/search-txt-builders.js +22 -0
  135. package/dist/utils/search-txt.d.ts +22 -0
  136. package/dist/utils/search-txt.js +17 -0
  137. package/dist/values/success-messages.d.ts +0 -4
  138. package/dist/values/success-messages.js +0 -5
  139. package/package.json +1 -1
  140. package/scripts/seed-cli.mjs +292 -16
  141. package/dist/core/copilot-log.repository.d.ts +0 -44
  142. package/dist/core/copilot-log.repository.js +0 -61
  143. package/dist/features/copilot/components/AdminCopilotView.d.ts +0 -21
  144. package/dist/features/copilot/components/AdminCopilotView.js +0 -51
  145. package/dist/features/copilot/components/index.d.ts +0 -2
  146. package/dist/features/copilot/components/index.js +0 -1
  147. package/dist/features/copilot/hooks/useCopilotChat.d.ts +0 -25
  148. package/dist/features/copilot/hooks/useCopilotChat.js +0 -65
  149. package/dist/features/copilot/hooks/useCopilotFeedback.d.ts +0 -15
  150. package/dist/features/copilot/hooks/useCopilotFeedback.js +0 -15
  151. package/dist/features/copilot/index.d.ts +0 -3
  152. package/dist/features/copilot/index.js +0 -3
package/dist/index.js CHANGED
@@ -649,17 +649,11 @@ export { mergeFeatureMessages } from "./cli/index.js";
649
649
  export { withFeatures } from "./cli/index.js";
650
650
  // ./core/index
651
651
  // [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
652
- // COPILOT_LOGS_COLLECTION - Constant used across modules.
653
- export { COPILOT_LOGS_COLLECTION } from "./core/index.js";
654
652
  // [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
655
- // COPILOT_LOGS_INDEXED_FIELDS - Constant used across modules.
656
- export { COPILOT_LOGS_INDEXED_FIELDS } from "./core/index.js";
657
653
  // [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
658
654
  // CacheManager - Shared export for cache manager.
659
655
  export { CacheManager } from "./core/index.js";
660
656
  // [DB]-Database layer â€" uses firebase-admin or another server-side DB SDK; can only run in a trusted server environment.
661
- // CopilotLogRepository - Shared export for copilot log repository.
662
- export { CopilotLogRepository } from "./core/index.js";
663
657
  // [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
664
658
  // EventBus - Shared export for event bus.
665
659
  export { EventBus } from "./core/index.js";
@@ -698,11 +692,7 @@ export { cacheManager } from "./core/index.js";
698
692
  // configureMarketDefaults - Shared export for configure market defaults.
699
693
  export { configureMarketDefaults } from "./core/index.js";
700
694
  // [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
701
- // copilotLogQueryHelpers - Shared export for copilot log query helpers.
702
- export { copilotLogQueryHelpers } from "./core/index.js";
703
695
  // [DB]-Database layer â€" uses firebase-admin or another server-side DB SDK; can only run in a trusted server environment.
704
- // copilotLogRepository - Shared export for copilot log repository.
705
- export { copilotLogRepository } from "./core/index.js";
706
696
  // [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
707
697
  // createServerAction - Helper for create server action.
708
698
  export { createServerAction } from "./core/index.js";
@@ -1321,6 +1311,7 @@ export { catalogueSeedData } from "./seed/index.js";
1321
1311
  // [SERVER-ONLY]-Server-only â€" uses Node.js, Next.js server internals, or third-party server SDKs (auth, email, payment, shipping).
1322
1312
  // productsStandardSeedData - Seed data for standard (buy-now) product listings.
1323
1313
  export { productsStandardSeedData } from "./seed/index.js";
1314
+ // 1,173-node taxonomy has a catalogue in it (967 of 968 leaves were empty).
1324
1315
  // [SERVER-ONLY]-Server-only â€" uses Node.js, Next.js server internals, or third-party server SDKs (auth, email, payment, shipping).
1325
1316
  // productsAuctionsSeedData - Seed data for auction product listings.
1326
1317
  export { productsAuctionsSeedData } from "./seed/index.js";
@@ -3093,16 +3084,13 @@ export { ContactPageView } from "./features/contact/index.js";
3093
3084
  // [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
3094
3085
  // useContactSubmit - React hook for use contact submit.
3095
3086
  export { useContactSubmit } from "./features/contact/index.js";
3096
- // ./features/copilot/index
3097
3087
  // [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
3098
- // AdminCopilotView - Component for admin copilot view.
3099
- export { AdminCopilotView } from "./features/copilot/index.js";
3100
3088
  // [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
3101
- // useCopilotChat - React hook for use copilot chat.
3102
- export { useCopilotChat } from "./features/copilot/index.js";
3103
3089
  // [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
3104
- // useCopilotFeedback - React hook for use copilot feedback.
3105
- export { useCopilotFeedback } from "./features/copilot/index.js";
3090
+ // [TYPE]-TypeScript type-only export â€" erased at compile time, zero runtime cost.
3091
+ // [TYPE]-TypeScript type-only export â€" erased at compile time, zero runtime cost.
3092
+ // [TYPE]-TypeScript type-only export â€" erased at compile time, zero runtime cost.
3093
+ // [TYPE]-TypeScript type-only export â€" erased at compile time, zero runtime cost.
3106
3094
  // ./features/corporate/index
3107
3095
  // [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
3108
3096
  // CorporateInquiryForm - Component for corporate inquiry form.
@@ -5355,7 +5343,7 @@ export { Ol } from "./ui/index.js";
5355
5343
  export { LoginRequiredModal } from "./ui/components/LoginRequiredModal.js";
5356
5344
  export { isAuthError } from "./utils/auth-error.js";
5357
5345
  // -- Missing constants ----------------------------------------------------------
5358
- export { API_ENDPOINTS, API_ROUTES, LOGS_ENDPOINTS, AUTH_ENDPOINTS, ACCOUNT_ENDPOINTS, NOTIFICATIONS_ENDPOINTS, ADMIN_ENDPOINTS, AUCTION_ENDPOINTS, BID_ENDPOINTS, CART_ENDPOINTS, CATEGORY_ENDPOINTS, CHECKOUT_ENDPOINTS, PAYMENT_ENDPOINTS, COPILOT_ENDPOINTS, CORPORATE_ENDPOINTS, EVENT_ENDPOINTS, FAQ_ENDPOINTS, HOMEPAGE_ENDPOINTS, LOYALTY_ENDPOINTS, ANALYTICS_ENDPOINTS, MEDIA_ENDPOINTS, ORDER_ENDPOINTS, PREORDER_ENDPOINTS, PRODUCT_ENDPOINTS, REVIEW_ENDPOINTS, SEARCH_ENDPOINTS, SELLER_ENDPOINTS, BLOG_ENDPOINTS, WISHLIST_ENDPOINTS, COLLECTION_CACHE_PATHS, resolveEndpoint, resolveEndpointFn, ROUTES, PUBLIC_ROUTES, PROTECTED_ROUTES, AUTH_ROUTES, WISHLIST_MAX, HISTORY_MAX, CART_MAX_ITEMS, WISHLIST_DOC_ID, HISTORY_DOC_ID, WISHLIST_COLLECTION, HISTORY_COLLECTION, } from "./constants/index.js";
5346
+ export { API_ENDPOINTS, API_ROUTES, LOGS_ENDPOINTS, AUTH_ENDPOINTS, ACCOUNT_ENDPOINTS, NOTIFICATIONS_ENDPOINTS, ADMIN_ENDPOINTS, AUCTION_ENDPOINTS, BID_ENDPOINTS, CART_ENDPOINTS, CATEGORY_ENDPOINTS, CHECKOUT_ENDPOINTS, FAQ_ENDPOINTS, HOMEPAGE_ENDPOINTS, LOYALTY_ENDPOINTS, ANALYTICS_ENDPOINTS, MEDIA_ENDPOINTS, ORDER_ENDPOINTS, PREORDER_ENDPOINTS, PRODUCT_ENDPOINTS, REVIEW_ENDPOINTS, SEARCH_ENDPOINTS, SELLER_ENDPOINTS, BLOG_ENDPOINTS, WISHLIST_ENDPOINTS, COLLECTION_CACHE_PATHS, resolveEndpoint, resolveEndpointFn, ROUTES, PUBLIC_ROUTES, PROTECTED_ROUTES, AUTH_ROUTES, WISHLIST_MAX, HISTORY_MAX, CART_MAX_ITEMS, WISHLIST_DOC_ID, HISTORY_DOC_ID, WISHLIST_COLLECTION, HISTORY_COLLECTION, } from "./constants/index.js";
5359
5347
  // Wishlist server errors (server-only via tree-shake â€" class only, no firebase-admin at module scope)
5360
5348
  export { WishlistFullError } from "./features/wishlist/server.js";
5361
5349
  // History â€" client hook + guest util are safe in main barrel; repository is server-only
@@ -5460,6 +5448,26 @@ export { productFeaturesSeedData } from "./seed/index.js";
5460
5448
  * had no data, which is indistinguishable from an empty fixture set.
5461
5449
  */
5462
5450
  export { taxCodesSeedData } from "./seed/index.js";
5451
+ /*
5452
+ * E4/D12 — generated from the extracted corpus research.
5453
+ *
5454
+ * 🛑 Exported from the MAIN barrel, because `seed-cli.mjs` destructures all of
5455
+ * its seed data from `appkit` (not from `/server`, which is what I assumed
5456
+ * first). Adding a collection to `seed/index.ts` and `COLLECTION_MAP` alone
5457
+ * leaves the loader reading `undefined` — and `appkit-seed status` then reports
5458
+ * "0 seed rows", i.e. a collection that looks registered and loads nothing.
5459
+ * That is Root Cause #90 exactly: the PII half was wired and the LOADING half
5460
+ * was not, so every grep found real hits while the collection stayed empty.
5461
+ */
5462
+ // E4/D12 — the collection constants and types.
5463
+ export { PART_GLOSSARY_COLLECTION, MODEL_COMPOSITION_COLLECTION, PART_SLOT_VALUES, PART_SLOT_LABEL, ALL_PART_SLOTS, LISTING_TYPE_HAS_COMPOSITION, partSlotFromSegment, partGlossaryId, } from "./features/parts/schemas/firestore.js";
5464
+ export { partGlossarySeedData } from "./seed/index.js";
5465
+ export { modelCompositionSeedData } from "./seed/index.js";
5466
+ // D12 — the public part surfaces.
5467
+ export { ModelCompositionPanel } from "./features/parts/components/ModelCompositionPanel.js";
5468
+ export { PartsIndexPageView, PartClassificationPageView, } from "./features/parts/components/PartsIndexPageView.js";
5469
+ export { PartDetailPageView } from "./features/parts/components/PartDetailPageView.js";
5470
+ export { AssemblyDrawer } from "./features/parts/components/AssemblyDrawer.js";
5463
5471
  // Scam registry â€" schema types and constants
5464
5472
  export { SCAMMER_COLLECTION, SCAMMER_ID_PREFIX, SCAMMER_MATCH_FIELDS, SCAMMER_INDEXED_FIELDS, SCAMMER_FIELDS, SCAMMER_INCIDENTS_SUBCOLLECTION, SCAMMER_COMMENTS_SUBCOLLECTION, SCAMMER_CONTESTS_SUBCOLLECTION, MAX_COMMENT_BODY_LENGTH, MAX_PENDING_SCAMMER_REPORTS_PER_USER, ScammerStatusValues, ScamPlatformValues, SocialPlatformValues, ContestTypeValues, ContestStatusValues, SCAM_PLATFORM_LABELS, SOCIAL_PLATFORM_LABELS, SCAMMER_STATUS_LABELS, CONTEST_TYPE_LABELS, CONTEST_STATUS_LABELS, DEFAULT_SCAMMER_DATA, DEFAULT_SCAMMER_INCIDENT_DATA, DEFAULT_SCAMMER_COMMENT_DATA, DEFAULT_SCAMMER_CONTEST_DATA, } from "./features/scams/schemas/firestore.js";
5465
5473
  // Scam registry â€" scam type definitions
@@ -106,6 +106,9 @@ export declare const DEFAULT_ROUTE_MAP: {
106
106
  readonly ITEM_REQUESTS_NEW: "/item-requests/new";
107
107
  readonly TOOLS: "/tools";
108
108
  readonly TOOL_PRICE_INDEX: "/tools/price-index";
109
+ readonly PARTS: "/parts";
110
+ readonly PART_CLASSIFICATION: (classification: string) => string;
111
+ readonly PART_DETAIL: (classification: string, code: string) => string;
109
112
  readonly SCAM_TYPES: "/scams/types";
110
113
  readonly SCAM_FAQS: "/scams/faqs";
111
114
  readonly CLASSIFIED: "/classified";
@@ -335,7 +338,6 @@ export declare const DEFAULT_ROUTE_MAP: {
335
338
  readonly EVENT_ENTRIES: (id: string) => string;
336
339
  readonly STORES: "/admin/stores";
337
340
  readonly STORE_DETAIL: (id: string) => string;
338
- readonly COPILOT: "/admin/copilot";
339
341
  readonly ADS: "/admin/ads";
340
342
  readonly ADS_NEW: "/admin/ads/new";
341
343
  readonly ADS_EDIT: (id: string) => string;
@@ -511,6 +513,9 @@ export declare const ROUTES: {
511
513
  readonly ITEM_REQUESTS_NEW: "/item-requests/new";
512
514
  readonly TOOLS: "/tools";
513
515
  readonly TOOL_PRICE_INDEX: "/tools/price-index";
516
+ readonly PARTS: "/parts";
517
+ readonly PART_CLASSIFICATION: (classification: string) => string;
518
+ readonly PART_DETAIL: (classification: string, code: string) => string;
514
519
  readonly SCAM_TYPES: "/scams/types";
515
520
  readonly SCAM_FAQS: "/scams/faqs";
516
521
  readonly CLASSIFIED: "/classified";
@@ -740,7 +745,6 @@ export declare const ROUTES: {
740
745
  readonly EVENT_ENTRIES: (id: string) => string;
741
746
  readonly STORES: "/admin/stores";
742
747
  readonly STORE_DETAIL: (id: string) => string;
743
- readonly COPILOT: "/admin/copilot";
744
748
  readonly ADS: "/admin/ads";
745
749
  readonly ADS_NEW: "/admin/ads/new";
746
750
  readonly ADS_EDIT: (id: string) => string;
@@ -106,6 +106,20 @@ export const DEFAULT_ROUTE_MAP = {
106
106
  */
107
107
  TOOLS: "/tools",
108
108
  TOOL_PRICE_INDEX: "/tools/price-index",
109
+ /*
110
+ * The PARTS database. A reference surface, so it lives beside the other
111
+ * two things that answer a question rather than sell you something.
112
+ *
113
+ * 🛑 `/parts/{classification}/{code}` — the classification is REQUIRED in
114
+ * the path, because a code alone is ambiguous: `105` is a Metal Fight
115
+ * spin-track height, a bare number in Burst is a disc, and `1-60` is a
116
+ * Beyblade X ratchet. The two segments together are the document id
117
+ * (`${slot}--${tail}`), so the page is a direct doc read with no query
118
+ * and no index.
119
+ */
120
+ PARTS: "/parts",
121
+ PART_CLASSIFICATION: (classification) => `/parts/${classification}`,
122
+ PART_DETAIL: (classification, code) => `/parts/${classification}/${code}`,
109
123
  SCAM_TYPES: "/scams/types",
110
124
  SCAM_FAQS: "/scams/faqs",
111
125
  CLASSIFIED: "/classified",
@@ -364,7 +378,6 @@ export const DEFAULT_ROUTE_MAP = {
364
378
  EVENT_ENTRIES: (id) => `/admin/events/${id}/entries`,
365
379
  STORES: "/admin/stores",
366
380
  STORE_DETAIL: (id) => `/admin/stores/${id}/view`,
367
- COPILOT: "/admin/copilot",
368
381
  ADS: "/admin/ads",
369
382
  ADS_NEW: "/admin/ads/new",
370
383
  ADS_EDIT: (id) => `/admin/ads/${id}/edit`,
@@ -112,6 +112,37 @@ export declare abstract class BaseRepository<T extends DocumentData> {
112
112
  patch: Partial<T>;
113
113
  }>): Promise<number>;
114
114
  create(data: Partial<T> | FirestoreDocument): Promise<T>;
115
+ /**
116
+ * Write a document at a CALLER-CHOSEN id.
117
+ *
118
+ * 🛑 **This OVERWRITES. It is an upsert, not a create**, despite the name —
119
+ * `.set()` with no merge option replaces the whole document, and there is no
120
+ * existence check. Writing an id that already exists silently discards
121
+ * everything that was there and returns a success.
122
+ *
123
+ * That is **deliberate and load-bearing for most of its 28 callers**, which
124
+ * are genuine upserts keyed on a deterministic id:
125
+ *
126
+ * - `testerChecklistResponses` — one doc per (tester, case), re-answered
127
+ * every time a verdict changes. CLAUDE.md records that this upsert is
128
+ * *"what makes Yes/No answers + comments + screenshots survive a page
129
+ * reload"*, so a create-only variant here would break verdict recording.
130
+ * - `sessions`, analytics rollups, `priceIndex` — all rewritten by design.
131
+ *
132
+ * 🛑 **So do NOT "fix" this to `.create()`.** I was about to: the plan carried
133
+ * it as a severity-1 silent-overwrite risk for products, on the reasoning that
134
+ * two physical units of one model would derive the same slug and the second
135
+ * would destroy the first. **Measured, that is false** —
136
+ * `ProductRepository.create` OVERRIDES this method and runs `generateUniqueId`
137
+ * with a real `findById` probe, so units get `-1`, `-2`, … and never collide.
138
+ * A blanket `.create()` would have broken the tester harness to fix a defect
139
+ * products do not have.
140
+ *
141
+ * **A caller that needs create-only semantics must probe first**, the way
142
+ * `ProductRepository.create` does. The hazard this docstring removes is the
143
+ * NAME: `createWithId` reads as "create" and behaves as "replace", and that
144
+ * gap is what made the wrong conclusion so easy to reach.
145
+ */
115
146
  createWithId(id: string, data: Partial<T>): Promise<T>;
116
147
  update(id: string, data: Partial<T>): Promise<T>;
117
148
  delete(id: string): Promise<void>;
@@ -284,6 +284,37 @@ export class BaseRepository {
284
284
  throw new DatabaseError(`Failed to create document: ${detailOf(error)}`, error);
285
285
  }
286
286
  }
287
+ /**
288
+ * Write a document at a CALLER-CHOSEN id.
289
+ *
290
+ * 🛑 **This OVERWRITES. It is an upsert, not a create**, despite the name —
291
+ * `.set()` with no merge option replaces the whole document, and there is no
292
+ * existence check. Writing an id that already exists silently discards
293
+ * everything that was there and returns a success.
294
+ *
295
+ * That is **deliberate and load-bearing for most of its 28 callers**, which
296
+ * are genuine upserts keyed on a deterministic id:
297
+ *
298
+ * - `testerChecklistResponses` — one doc per (tester, case), re-answered
299
+ * every time a verdict changes. CLAUDE.md records that this upsert is
300
+ * *"what makes Yes/No answers + comments + screenshots survive a page
301
+ * reload"*, so a create-only variant here would break verdict recording.
302
+ * - `sessions`, analytics rollups, `priceIndex` — all rewritten by design.
303
+ *
304
+ * 🛑 **So do NOT "fix" this to `.create()`.** I was about to: the plan carried
305
+ * it as a severity-1 silent-overwrite risk for products, on the reasoning that
306
+ * two physical units of one model would derive the same slug and the second
307
+ * would destroy the first. **Measured, that is false** —
308
+ * `ProductRepository.create` OVERRIDES this method and runs `generateUniqueId`
309
+ * with a real `findById` probe, so units get `-1`, `-2`, … and never collide.
310
+ * A blanket `.create()` would have broken the tester harness to fix a defect
311
+ * products do not have.
312
+ *
313
+ * **A caller that needs create-only semantics must probe first**, the way
314
+ * `ProductRepository.create` does. The hazard this docstring removes is the
315
+ * NAME: `createWithId` reads as "create" and behaves as "replace", and that
316
+ * gap is what made the wrong conclusion so easy to reach.
317
+ */
287
318
  async createWithId(id, data) {
288
319
  try {
289
320
  const now = new Date();
@@ -49,8 +49,7 @@ export { EventRepository, EventsRepository, eventRepository, } from "../features
49
49
  export { EventEntryRepository, EventEntriesRepository, eventEntryRepository, } from "../features/events/repository/event-entry.repository";
50
50
  export { NewsletterRepository, newsletterRepository, } from "../core/newsletter.repository";
51
51
  export type { NewsletterSubscriberDocument, NewsletterSubscriberCreateInput, NewsletterSubscriberUpdateInput, NewsletterListModel, } from "../core/newsletter.repository";
52
- export { CopilotLogRepository, copilotLogRepository } from "../core";
53
- export type { CopilotFeedback, CopilotLogDocument, CopilotLogCreateInput, } from "../core";
52
+ export type {} from "../core";
54
53
  export { ScammerRepository, scammerRepository, } from "../features/scams/repository/scammer.repository";
55
54
  export { SupportRepository, supportRepository, } from "../features/support/repository/support.repository";
56
55
  export type { SupportTicketDocument, SupportTicketCreateInput, SupportTicketUpdateInput, TicketMessage, TicketCategory, TicketStatus, TicketPriority, } from "../features/support/schemas/firestore";
@@ -61,7 +61,6 @@ export { historyRepository, } from "../features/history/repository/user-history.
61
61
  export { EventRepository, EventsRepository, eventRepository, } from "../features/events/repository/events.repository.js";
62
62
  export { EventEntryRepository, EventEntriesRepository, eventEntryRepository, } from "../features/events/repository/event-entry.repository.js";
63
63
  export { NewsletterRepository, newsletterRepository, } from "../core/newsletter.repository.js";
64
- export { CopilotLogRepository, copilotLogRepository } from "../core/index.js";
65
64
  export { ScammerRepository, scammerRepository, } from "../features/scams/repository/scammer.repository.js";
66
65
  export { SupportRepository, supportRepository, } from "../features/support/repository/support.repository.js";
67
66
  // SB-UNI-B — SublistingCategoriesRepository deleted; sublistings now live on
@@ -15,6 +15,7 @@ export declare const CRAWL_AS_OF: Date;
15
15
  * description containing a literal `{{series}}`.
16
16
  */
17
17
  export declare const TEMPLATES: Record<CategoryTemplateVariant, CategoryDescriptionTemplate>;
18
+ export declare const BEY_TEMPLATES: CategoryDescriptionTemplate[];
18
19
  /** The set a normal collectible category offers. */
19
20
  export declare const COLLECTIBLE_TEMPLATES: CategoryDescriptionTemplate[];
20
21
  /** Parts and small accessories arrive carded or loose, not boxed. */
@@ -25,6 +26,34 @@ export declare function templateSet(...variants: CategoryTemplateVariant[]): Cat
25
26
  export declare function marketBand(leafId: string): CategoryPriceGuidance | undefined;
26
27
  /** Every leaf id this map claims a band for — the seed asserts they all exist. */
27
28
  export declare const MARKET_BAND_LEAF_IDS: string[];
29
+ /**
30
+ * B6 — the ROOT-LEVEL tax fallback, one per root.
31
+ *
32
+ * 🛑 Measured 2026-10-12: the tax code sat on tiers 1–3 only (t1=4, t2=47,
33
+ * t3=71) and on NO root, so **3 of 70 live products resolved no tax code at
34
+ * all** and would have fallen to `DEFAULT_GST_RATE` — a listing filed at
35
+ * `category-spinning-tops`, `category-burst-tops` or `category-x-tops`.
36
+ *
37
+ * C0c's own rule is that reaching that constant is a TRIAGE SIGNAL, not a
38
+ * feature: "a listing reaching step 3 at all means its category has no tax
39
+ * code — and that, not the constant, is the defect to fix. The measure of
40
+ * success is that step 3 fires for nothing."
41
+ *
42
+ * Putting it at the root is what makes that true for every branch, including
43
+ * branches nobody has filed under yet — `resolveCategoryDefaults` walks the
44
+ * chain nearest-first, so a tier-2 or tier-3 entry still WINS as a
45
+ * more-specific override. This is a floor, not a replacement.
46
+ *
47
+ * 🛑 Deliberately NOT a single shared constant. Each root is a different HSN
48
+ * question and a wrong HSN on a real invoice is worse than an absent one —
49
+ * printed cards are chapter 4911, not 9503, and a live plant is exempt. Only
50
+ * 9503 was verified (section 3.2); the three non-9503 roots therefore take the
51
+ * closest VERIFIED code rather than an unverified guess, and say so.
52
+ */
53
+ export declare const ROOT_AUTHENTICITY_DEFAULT: "unverified";
54
+ export declare const ROOT_TAX_DEFAULTS: Record<string, {
55
+ taxCodeId: string;
56
+ }>;
28
57
  /** A complete top. */
29
58
  export declare function beyDefaults(leafId: string, series: string, system: string): CategoryProductDefaults;
30
59
  /** A loose component. */
@@ -144,6 +144,36 @@ export const TEMPLATES = {
144
144
  version: 1,
145
145
  },
146
146
  };
147
+ /**
148
+ * ── D12: the BEYBLADE bodies, which are the only ones that state PARTS ─────
149
+ *
150
+ * 🛑 A separate set rather than adding the slot to the shared bodies, and the
151
+ * reason is mechanical: `COLLECTIBLE_TEMPLATES` is also what every figure,
152
+ * Hot Wheels and TCG category uses, and a `{{partBreakdown}}` on a Marvel
153
+ * Legends listing resolves to nothing. The endpoint strips the line when
154
+ * there is no composition, so the artifact would be invisible rather than
155
+ * wrong — but it would also be a read attempted on four roots that can never
156
+ * satisfy it, on an endpoint the seller form hits on every category change.
157
+ *
158
+ * Assigned to the 21 `line(...)` nodes, which are Beyblade generation lines
159
+ * and nothing else — their children are `MODEL_LEAVES`. A model leaf carries
160
+ * no body of its own and INHERITS this one, while the slot resolves against
161
+ * the leaf's OWN composition. That is the whole design: one body per
162
+ * generation, 216 models' worth of parts.
163
+ */
164
+ const BEY_PARTS_LINE = ` <li><strong>Parts:</strong> {{partBreakdown}}</li>`;
165
+ /** The slot sits on its own source line, because the strip is line-based. */
166
+ function withPartsLine(body) {
167
+ return body.replace("</ul>", `${BEY_PARTS_LINE}
168
+ </ul>`);
169
+ }
170
+ const BEY_TEMPLATE_VARIANTS = [
171
+ "new_in_box",
172
+ "pre_owned",
173
+ "replica",
174
+ "default",
175
+ ];
176
+ export const BEY_TEMPLATES = BEY_TEMPLATE_VARIANTS.map((v) => ({ ...TEMPLATES[v], body: withPartsLine(TEMPLATES[v].body) }));
147
177
  /** The set a normal collectible category offers. */
148
178
  export const COLLECTIBLE_TEMPLATES = [
149
179
  TEMPLATES.new_in_box,
@@ -357,6 +387,54 @@ export const MARKET_BAND_LEAF_IDS = Object.keys(MARKET_BANDS);
357
387
  const TAX_TOY = "tax-hsn-95030020"; // non-electronic toys incl. spinning tops — 5%
358
388
  const TAX_TOY_OTHER = "tax-hsn-95030099"; // other toys — 5%
359
389
  const TAX_EXEMPT = "tax-exempt-0";
390
+ /**
391
+ * B6 — the ROOT-LEVEL tax fallback, one per root.
392
+ *
393
+ * 🛑 Measured 2026-10-12: the tax code sat on tiers 1–3 only (t1=4, t2=47,
394
+ * t3=71) and on NO root, so **3 of 70 live products resolved no tax code at
395
+ * all** and would have fallen to `DEFAULT_GST_RATE` — a listing filed at
396
+ * `category-spinning-tops`, `category-burst-tops` or `category-x-tops`.
397
+ *
398
+ * C0c's own rule is that reaching that constant is a TRIAGE SIGNAL, not a
399
+ * feature: "a listing reaching step 3 at all means its category has no tax
400
+ * code — and that, not the constant, is the defect to fix. The measure of
401
+ * success is that step 3 fires for nothing."
402
+ *
403
+ * Putting it at the root is what makes that true for every branch, including
404
+ * branches nobody has filed under yet — `resolveCategoryDefaults` walks the
405
+ * chain nearest-first, so a tier-2 or tier-3 entry still WINS as a
406
+ * more-specific override. This is a floor, not a replacement.
407
+ *
408
+ * 🛑 Deliberately NOT a single shared constant. Each root is a different HSN
409
+ * question and a wrong HSN on a real invoice is worse than an absent one —
410
+ * printed cards are chapter 4911, not 9503, and a live plant is exempt. Only
411
+ * 9503 was verified (section 3.2); the three non-9503 roots therefore take the
412
+ * closest VERIFIED code rather than an unverified guess, and say so.
413
+ */
414
+ export const ROOT_AUTHENTICITY_DEFAULT = "unverified";
415
+ export const ROOT_TAX_DEFAULTS = {
416
+ // Verified: HSN 9503 0020 — non-electronic toys including spinning tops, 5%.
417
+ "category-spinning-tops": { taxCodeId: TAX_TOY },
418
+ // Verified: HSN 9503 0099 — "other toys", 5%. A built model kit and a
419
+ // die-cast car are both toys under 9503; neither is a spinning top.
420
+ "category-hot-wheels": { taxCodeId: TAX_TOY_OTHER },
421
+ "category-model-kits": { taxCodeId: TAX_TOY_OTHER },
422
+ "category-collectible-figures": { taxCodeId: TAX_TOY_OTHER },
423
+ /*
424
+ * 🛑 Trading cards are chapter 4911 (printed matter) or 9504 (playing cards)
425
+ * and NEITHER was verified — section 3.2 confirmed 9503 only, and § "Still
426
+ * open" records "verify each before seeding; 9503 is the only one I
427
+ * confirmed". `9503 0099` is used as the floor because a TCG single sold as
428
+ * a collectible is a toy-chapter good at the same 5% rate, so the RATE is
429
+ * right even where the chapter may not be. It must be re-chaptered before
430
+ * the first real TCG invoice, and that is a data task with a source, not a
431
+ * guess to make here.
432
+ */
433
+ "category-trading-cards": { taxCodeId: TAX_TOY_OTHER },
434
+ // A live plant or animal is nil-rated — a deliberate exemption, which is
435
+ // exactly why `gstRate: 0` must survive derivation rather than be "corrected".
436
+ "category-living-collectibles": { taxCodeId: TAX_EXEMPT },
437
+ };
360
438
  /** Shared across every Beyblade-family node. */
361
439
  const BEY_FAQ_TEMPLATES = [
362
440
  {
@@ -591,9 +669,28 @@ export function diecastDefaults(leafId, line) {
591
669
  ],
592
670
  };
593
671
  }
672
+ const TAX_TOY_ELECTRONIC = "tax-hsn-95030010"; // electronic toys — 18%
673
+ /**
674
+ * 🛑 THE ONLY 18% BRANCH IN THE CATALOGUE, and it is derived rather than
675
+ * hand-written on a product.
676
+ *
677
+ * `tax-hsn-95030010` (electronic toys, 18%) has existed as a `taxCodes` row
678
+ * since B3 with **nothing referencing it** — so the 18% arm of `calculateGst`
679
+ * and every mixed-rate path (C0c defect 4's `gstByRate`, the per-rate invoice
680
+ * table, the blended-rate payout) had no data that could exercise it. A code
681
+ * with no referrer is the vocabulary-with-no-rows shape of Root Cause #90.
682
+ *
683
+ * Counters and BeyPointers are battery-powered scoring devices, which is what
684
+ * 9503 0010 covers — so this is the honest home for it, not a flag planted on
685
+ * an arbitrary row. Putting it on the CATEGORY rather than on a product means
686
+ * the 18% is proven through `deriveTaxonomy`, i.e. through the mechanism that
687
+ * will actually carry it in production, instead of through a literal that
688
+ * would pass while the derivation stayed untested.
689
+ */
690
+ const ELECTRONIC_ACCESSORY_LEAVES = new Set(["category-accessory-counters"]);
594
691
  export function accessoryDefaults(leafId, kind) {
595
692
  return {
596
- taxCodeId: TAX_TOY_OTHER,
693
+ taxCodeId: ELECTRONIC_ACCESSORY_LEAVES.has(leafId) ? TAX_TOY_ELECTRONIC : TAX_TOY_OTHER,
597
694
  specifications: [SPEC("Accessory type", kind)],
598
695
  defaultAuthenticity: "unverified",
599
696
  priceGuidance: marketBand(leafId),