@mohasinac/appkit 4.57.0 → 4.59.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.
- package/dist/.build-stamp.json +4 -4
- package/dist/_internal/server/features/beys/data.d.ts +44 -0
- package/dist/_internal/server/features/beys/data.js +107 -0
- package/dist/_internal/server/features/beys/og.d.ts +42 -0
- package/dist/_internal/server/features/beys/og.js +89 -0
- package/dist/_internal/server/features/parts/data.js +3 -2
- package/dist/_internal/server/jobs/handlers/messages.d.ts +1 -1
- package/dist/_internal/server/jobs/handlers/messages.js +1 -1
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +86 -0
- package/dist/_internal/shared/types/base-document.d.ts +1 -1
- package/dist/client.d.ts +2 -0
- package/dist/client.js +1 -0
- package/dist/constants/api-endpoints.d.ts +12 -0
- package/dist/constants/api-endpoints.js +10 -0
- package/dist/constants/field-names.d.ts +46 -0
- package/dist/constants/field-names.js +46 -0
- package/dist/constants/seed-meta.d.ts +113 -0
- package/dist/constants/seed-meta.js +91 -0
- package/dist/features/admin/components/AdminBeysView.d.ts +27 -0
- package/dist/features/admin/components/AdminBeysView.js +178 -0
- package/dist/features/admin/components/AdminPartsView.d.ts +27 -0
- package/dist/features/admin/components/AdminPartsView.js +160 -0
- package/dist/features/admin/components/OwnerQuickActions.d.ts +19 -2
- package/dist/features/admin/components/OwnerQuickActions.js +31 -3
- package/dist/features/admin/components/RecordOwnerQuickActions.d.ts +31 -0
- package/dist/features/admin/components/RecordOwnerQuickActions.js +128 -0
- package/dist/features/admin/components/ReferenceRecordEditor.d.ts +45 -0
- package/dist/features/admin/components/ReferenceRecordEditor.js +153 -0
- package/dist/features/admin/schemas/firestore.d.ts +1 -1
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +15 -2
- package/dist/features/auth/permissions/constants.d.ts +1 -1
- package/dist/features/auth/permissions/constants.js +12 -1
- package/dist/features/beys/components/BeyDetailPageView.d.ts +50 -0
- package/dist/features/beys/components/BeyDetailPageView.js +98 -0
- package/dist/features/beys/schemas/firestore.d.ts +417 -0
- package/dist/features/beys/schemas/firestore.js +65 -0
- package/dist/features/blog/components/BlogFeaturedCard.js +15 -2
- package/dist/features/blog/components/BlogPostView.js +2 -2
- package/dist/features/categories/components/BrandDetailPageView.js +4 -2
- package/dist/features/categories/components/BundleDetailView.js +2 -2
- package/dist/features/categories/components/CategoryDetailPageView.js +36 -3
- package/dist/features/categories/components/CategoryGrid.js +24 -2
- package/dist/features/categories/components/FranchiseDetailPageView.js +2 -3
- package/dist/features/events/components/EventCard.js +15 -2
- package/dist/features/grouped/components/GroupedListingDetailView.js +2 -3
- package/dist/features/homepage/components/CustomCardsSection.js +67 -50
- package/dist/features/homepage/components/ShopByCategorySection.js +23 -10
- package/dist/features/homepage/schemas/index.d.ts +10 -10
- package/dist/features/parts/components/PartDetailPageView.js +5 -3
- package/dist/features/parts/components/PartsSearchBox.js +79 -4
- package/dist/features/parts/schemas/admin-edit.d.ts +81 -0
- package/dist/features/parts/schemas/admin-edit.js +89 -0
- package/dist/features/parts/schemas/firestore.d.ts +2 -1
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +15 -2
- package/dist/features/products/components/MarketplaceBundleCard.js +15 -2
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +15 -2
- package/dist/features/products/components/ProductGrid.js +18 -2
- package/dist/features/products/schemas/firestore.d.ts +18 -0
- package/dist/features/seller/components/CategoryInlineSelect.d.ts +10 -1
- package/dist/features/seller/components/CategoryInlineSelect.js +53 -5
- package/dist/features/seller/components/SellerProductShell.d.ts +39 -0
- package/dist/features/seller/components/SellerProductShell.js +31 -3
- package/dist/index.d.ts +6 -129
- package/dist/index.js +61 -292
- package/dist/next/routing/route-map.d.ts +13 -1
- package/dist/next/routing/route-map.js +37 -0
- package/dist/schemas/registry.d.ts +6 -6
- package/dist/server.d.ts +2 -69
- package/dist/server.js +8 -69
- package/dist/styles.css +468 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/PosterCard.d.ts +99 -0
- package/dist/ui/components/PosterCard.js +109 -0
- package/dist/ui/components/PosterCard.style.css +466 -0
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +2 -0
- package/package.json +4 -2
- package/scripts/seed-cli.mjs +177 -2
- package/dist/features/tester/seed-data/_happy-path.d.ts +0 -23
- package/dist/features/tester/seed-data/_happy-path.js +0 -710
- package/dist/features/tester/seed-data/_money-flows.d.ts +0 -23
- package/dist/features/tester/seed-data/_money-flows.js +0 -610
- package/dist/features/tester/seed-data/authored/_types.d.ts +0 -57
- package/dist/features/tester/seed-data/authored/_types.js +0 -35
- package/dist/features/tester/seed-data/authored/account-auth__profile-settings.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/account-auth__profile-settings.js +0 -226
- package/dist/features/tester/seed-data/authored/account-auth__signup-login.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/account-auth__signup-login.js +0 -329
- package/dist/features/tester/seed-data/authored/addresses__address-filters.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/addresses__address-filters.js +0 -41
- package/dist/features/tester/seed-data/authored/addresses__postal-lookup.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/addresses__postal-lookup.js +0 -76
- package/dist/features/tester/seed-data/authored/addresses__postal-validation.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/addresses__postal-validation.js +0 -78
- package/dist/features/tester/seed-data/authored/addresses__state-picker.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/addresses__state-picker.js +0 -59
- package/dist/features/tester/seed-data/authored/addresses__unban-request.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/addresses__unban-request.js +0 -77
- package/dist/features/tester/seed-data/authored/admin__admin-detail-round-trips.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__admin-detail-round-trips.js +0 -151
- package/dist/features/tester/seed-data/authored/admin__bans-and-trust.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__bans-and-trust.js +0 -265
- package/dist/features/tester/seed-data/authored/admin__blog-faqs.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__blog-faqs.js +0 -131
- package/dist/features/tester/seed-data/authored/admin__bug-hunter-rewards.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__bug-hunter-rewards.js +0 -143
- package/dist/features/tester/seed-data/authored/admin__bulk-actions.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__bulk-actions.js +0 -207
- package/dist/features/tester/seed-data/authored/admin__bundles.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__bundles.js +0 -144
- package/dist/features/tester/seed-data/authored/admin__buyer-data-admin.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__buyer-data-admin.js +0 -265
- package/dist/features/tester/seed-data/authored/admin__catalog-listings.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__catalog-listings.js +0 -318
- package/dist/features/tester/seed-data/authored/admin__category-brand-authoring.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__category-brand-authoring.js +0 -177
- package/dist/features/tester/seed-data/authored/admin__classifieds-digitalcodes-live.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__classifieds-digitalcodes-live.js +0 -165
- package/dist/features/tester/seed-data/authored/admin__content-deletes.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__content-deletes.js +0 -267
- package/dist/features/tester/seed-data/authored/admin__content-marketing.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__content-marketing.js +0 -188
- package/dist/features/tester/seed-data/authored/admin__coupons.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__coupons.js +0 -215
- package/dist/features/tester/seed-data/authored/admin__events-raffles-spin.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__events-raffles-spin.js +0 -202
- package/dist/features/tester/seed-data/authored/admin__firebase-function-effects.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__firebase-function-effects.js +0 -249
- package/dist/features/tester/seed-data/authored/admin__media-watermark.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__media-watermark.js +0 -148
- package/dist/features/tester/seed-data/authored/admin__orders-fulfillment.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__orders-fulfillment.js +0 -350
- package/dist/features/tester/seed-data/authored/admin__prize-draws-lotteries.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__prize-draws-lotteries.js +0 -129
- package/dist/features/tester/seed-data/authored/admin__site-system.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__site-system.js +0 -586
- package/dist/features/tester/seed-data/authored/admin__uncovered-admin-pages.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__uncovered-admin-pages.js +0 -260
- package/dist/features/tester/seed-data/authored/admin__users-trust.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/admin__users-trust.js +0 -569
- package/dist/features/tester/seed-data/authored/buying__bidding.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/buying__bidding.js +0 -449
- package/dist/features/tester/seed-data/authored/buying__browsing-search.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/buying__browsing-search.js +0 -684
- package/dist/features/tester/seed-data/authored/buying__buying-checkout.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/buying__buying-checkout.js +0 -546
- package/dist/features/tester/seed-data/authored/buying__buying-coupons.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/buying__buying-coupons.js +0 -379
- package/dist/features/tester/seed-data/authored/buying__cart.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/buying__cart.js +0 -1264
- package/dist/features/tester/seed-data/authored/buying__image-tile-layout.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/buying__image-tile-layout.js +0 -122
- package/dist/features/tester/seed-data/authored/buying__my-orders.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/buying__my-orders.js +0 -279
- package/dist/features/tester/seed-data/authored/buying__offers.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/buying__offers.js +0 -1127
- package/dist/features/tester/seed-data/authored/buying__order-detail-actions.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/buying__order-detail-actions.js +0 -153
- package/dist/features/tester/seed-data/authored/buying__order-status-lifecycle.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/buying__order-status-lifecycle.js +0 -252
- package/dist/features/tester/seed-data/authored/buying__product-detail.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/buying__product-detail.js +0 -474
- package/dist/features/tester/seed-data/authored/buying__return-request.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/buying__return-request.js +0 -165
- package/dist/features/tester/seed-data/authored/buying__reviews-pagination.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/buying__reviews-pagination.js +0 -260
- package/dist/features/tester/seed-data/authored/buying__reviews.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/buying__reviews.js +0 -128
- package/dist/features/tester/seed-data/authored/buying__spares-and-groups.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/buying__spares-and-groups.js +0 -96
- package/dist/features/tester/seed-data/authored/buying__user-dashboard-extras.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/buying__user-dashboard-extras.js +0 -283
- package/dist/features/tester/seed-data/authored/buying__user-dashboard-navigation.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/buying__user-dashboard-navigation.js +0 -169
- package/dist/features/tester/seed-data/authored/buying__user-uncovered-pages.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/buying__user-uncovered-pages.js +0 -105
- package/dist/features/tester/seed-data/authored/buying__wishlist-history.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/buying__wishlist-history.js +0 -374
- package/dist/features/tester/seed-data/authored/community-support__messages.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/community-support__messages.js +0 -200
- package/dist/features/tester/seed-data/authored/community-support__public-profile.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/community-support__public-profile.js +0 -54
- package/dist/features/tester/seed-data/authored/community-support__support-tickets.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/community-support__support-tickets.js +0 -76
- package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.d.ts +0 -4
- package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.js +0 -590
- package/dist/features/tester/seed-data/authored/content-discovery__blog.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/content-discovery__blog.js +0 -110
- package/dist/features/tester/seed-data/authored/content-discovery__category-brand-relations.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/content-discovery__category-brand-relations.js +0 -149
- package/dist/features/tester/seed-data/authored/content-discovery__category-counts-and-rollup.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/content-discovery__category-counts-and-rollup.js +0 -145
- package/dist/features/tester/seed-data/authored/content-discovery__category-tabs-franchise-bundles.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/content-discovery__category-tabs-franchise-bundles.js +0 -174
- package/dist/features/tester/seed-data/authored/content-discovery__coupons.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/content-discovery__coupons.js +0 -169
- package/dist/features/tester/seed-data/authored/content-discovery__event-detail-subroutes.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/content-discovery__event-detail-subroutes.js +0 -209
- package/dist/features/tester/seed-data/authored/content-discovery__event-participation.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/content-discovery__event-participation.js +0 -207
- package/dist/features/tester/seed-data/authored/content-discovery__events.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/content-discovery__events.js +0 -377
- package/dist/features/tester/seed-data/authored/content-discovery__faq-help.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/content-discovery__faq-help.js +0 -87
- package/dist/features/tester/seed-data/authored/content-discovery__notifications.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/content-discovery__notifications.js +0 -147
- package/dist/features/tester/seed-data/authored/content-discovery__overflowing-pill-rows.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/content-discovery__overflowing-pill-rows.js +0 -209
- package/dist/features/tester/seed-data/authored/content-discovery__parts-database.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/content-discovery__parts-database.js +0 -167
- package/dist/features/tester/seed-data/authored/content-discovery__price-index-and-compare.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/content-discovery__price-index-and-compare.js +0 -73
- package/dist/features/tester/seed-data/authored/content-discovery__search.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/content-discovery__search.js +0 -363
- package/dist/features/tester/seed-data/authored/content-discovery__site-wide-index.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/content-discovery__site-wide-index.js +0 -340
- package/dist/features/tester/seed-data/authored/content-discovery__store-reviews-aggregate.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/content-discovery__store-reviews-aggregate.js +0 -88
- package/dist/features/tester/seed-data/authored/cta-layout__checkout-bottom-bar.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/cta-layout__checkout-bottom-bar.js +0 -78
- package/dist/features/tester/seed-data/authored/cta-layout__dialog-footers.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/cta-layout__dialog-footers.js +0 -76
- package/dist/features/tester/seed-data/authored/cta-layout__editor-action-bar.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/cta-layout__editor-action-bar.js +0 -62
- package/dist/features/tester/seed-data/authored/cta-layout__navbar-ctas.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/cta-layout__navbar-ctas.js +0 -163
- package/dist/features/tester/seed-data/authored/cta-layout__product-bottom-bar.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/cta-layout__product-bottom-bar.js +0 -61
- package/dist/features/tester/seed-data/authored/design-ux__action-bar-placement.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/design-ux__action-bar-placement.js +0 -89
- package/dist/features/tester/seed-data/authored/design-ux__back-to-top-button.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/design-ux__back-to-top-button.js +0 -95
- package/dist/features/tester/seed-data/authored/design-ux__carousel-arrow-bounds.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/design-ux__carousel-arrow-bounds.js +0 -191
- package/dist/features/tester/seed-data/authored/design-ux__dashboard-layout.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/design-ux__dashboard-layout.js +0 -158
- package/dist/features/tester/seed-data/authored/design-ux__feature-badge-picker.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/design-ux__feature-badge-picker.js +0 -82
- package/dist/features/tester/seed-data/authored/design-ux__footer-theme.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/design-ux__footer-theme.js +0 -106
- package/dist/features/tester/seed-data/authored/design-ux__form-validation-errors.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/design-ux__form-validation-errors.js +0 -88
- package/dist/features/tester/seed-data/authored/design-ux__general-design.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/design-ux__general-design.js +0 -204
- package/dist/features/tester/seed-data/authored/design-ux__hand-mode-layout.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/design-ux__hand-mode-layout.js +0 -301
- package/dist/features/tester/seed-data/authored/design-ux__header-brand-mark-centring.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/design-ux__header-brand-mark-centring.js +0 -90
- package/dist/features/tester/seed-data/authored/design-ux__header-controls-and-messages.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/design-ux__header-controls-and-messages.js +0 -85
- package/dist/features/tester/seed-data/authored/design-ux__homepage-carousels.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/design-ux__homepage-carousels.js +0 -407
- package/dist/features/tester/seed-data/authored/design-ux__nav-reachability.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/design-ux__nav-reachability.js +0 -88
- package/dist/features/tester/seed-data/authored/design-ux__rich-text-readability.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/design-ux__rich-text-readability.js +0 -84
- package/dist/features/tester/seed-data/authored/design-ux__status-badge-legibility.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/design-ux__status-badge-legibility.js +0 -214
- package/dist/features/tester/seed-data/authored/design-ux__sticky-cta-bar.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/design-ux__sticky-cta-bar.js +0 -275
- package/dist/features/tester/seed-data/authored/index.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/index.js +0 -301
- package/dist/features/tester/seed-data/authored/page-wiring__data-loss.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/page-wiring__data-loss.js +0 -125
- package/dist/features/tester/seed-data/authored/page-wiring__detail-pages.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/page-wiring__detail-pages.js +0 -85
- package/dist/features/tester/seed-data/authored/page-wiring__drawer-pages.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/page-wiring__drawer-pages.js +0 -92
- package/dist/features/tester/seed-data/authored/page-wiring__reachability.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/page-wiring__reachability.js +0 -115
- package/dist/features/tester/seed-data/authored/public-pages__auth-error-pages.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/public-pages__auth-error-pages.js +0 -137
- package/dist/features/tester/seed-data/authored/public-pages__core-listing-pages.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/public-pages__core-listing-pages.js +0 -263
- package/dist/features/tester/seed-data/authored/public-pages__help-how-it-works.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/public-pages__help-how-it-works.js +0 -229
- package/dist/features/tester/seed-data/authored/public-pages__help-scams-guides-subpages.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/public-pages__help-scams-guides-subpages.js +0 -325
- package/dist/features/tester/seed-data/authored/public-pages__legal-policy-pages.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/public-pages__legal-policy-pages.js +0 -126
- package/dist/features/tester/seed-data/authored/public-pages__newly-wired-browse-indexes.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/public-pages__newly-wired-browse-indexes.js +0 -141
- package/dist/features/tester/seed-data/authored/public-pages__store-tabs.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/public-pages__store-tabs.js +0 -115
- package/dist/features/tester/seed-data/authored/public-pages__stores-sellers-directories.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/public-pages__stores-sellers-directories.js +0 -253
- package/dist/features/tester/seed-data/authored/search-and-nav__employee-permissions.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/search-and-nav__employee-permissions.js +0 -93
- package/dist/features/tester/seed-data/authored/search-and-nav__header-search.d.ts +0 -3
- package/dist/features/tester/seed-data/authored/search-and-nav__header-search.js +0 -185
- package/dist/features/tester/seed-data/authored/search-and-nav__settings-deep-links.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/search-and-nav__settings-deep-links.js +0 -82
- package/dist/features/tester/seed-data/authored/search-and-nav__sidebar-search.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/search-and-nav__sidebar-search.js +0 -60
- package/dist/features/tester/seed-data/authored/selling__become-seller.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__become-seller.js +0 -117
- package/dist/features/tester/seed-data/authored/selling__digital-content-delivery.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__digital-content-delivery.js +0 -148
- package/dist/features/tester/seed-data/authored/selling__final-sale-authoring.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__final-sale-authoring.js +0 -108
- package/dist/features/tester/seed-data/authored/selling__listing-a-product.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__listing-a-product.js +0 -274
- package/dist/features/tester/seed-data/authored/selling__listing-edit-roundtrip.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__listing-edit-roundtrip.js +0 -145
- package/dist/features/tester/seed-data/authored/selling__listing-lifecycle.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__listing-lifecycle.js +0 -336
- package/dist/features/tester/seed-data/authored/selling__listing-type-fields-roundtrip.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__listing-type-fields-roundtrip.js +0 -136
- package/dist/features/tester/seed-data/authored/selling__media-limits.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__media-limits.js +0 -135
- package/dist/features/tester/seed-data/authored/selling__product-upload-details.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__product-upload-details.js +0 -155
- package/dist/features/tester/seed-data/authored/selling__quick-add-minimum-details.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__quick-add-minimum-details.js +0 -138
- package/dist/features/tester/seed-data/authored/selling__sectionised-forms.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__sectionised-forms.js +0 -191
- package/dist/features/tester/seed-data/authored/selling__seller-analytics-payouts.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__seller-analytics-payouts.js +0 -138
- package/dist/features/tester/seed-data/authored/selling__seller-bids-bundles-filters.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__seller-bids-bundles-filters.js +0 -106
- package/dist/features/tester/seed-data/authored/selling__seller-catalog-org.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__seller-catalog-org.js +0 -192
- package/dist/features/tester/seed-data/authored/selling__seller-custom-brands.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__seller-custom-brands.js +0 -109
- package/dist/features/tester/seed-data/authored/selling__seller-guide.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__seller-guide.js +0 -34
- package/dist/features/tester/seed-data/authored/selling__seller-listing-types.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__seller-listing-types.js +0 -348
- package/dist/features/tester/seed-data/authored/selling__seller-marketing-extras.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__seller-marketing-extras.js +0 -87
- package/dist/features/tester/seed-data/authored/selling__seller-ops-comms.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__seller-ops-comms.js +0 -90
- package/dist/features/tester/seed-data/authored/selling__seller-orders-at-scale.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__seller-orders-at-scale.js +0 -85
- package/dist/features/tester/seed-data/authored/selling__seller-orders.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__seller-orders.js +0 -213
- package/dist/features/tester/seed-data/authored/selling__seller-shipping-payouts-setup.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__seller-shipping-payouts-setup.js +0 -192
- package/dist/features/tester/seed-data/authored/selling__spares-and-group-editing.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__spares-and-group-editing.js +0 -77
- package/dist/features/tester/seed-data/authored/selling__store-dashboard-navigation.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__store-dashboard-navigation.js +0 -165
- package/dist/features/tester/seed-data/authored/selling__store-uncovered-pages.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/selling__store-uncovered-pages.js +0 -116
- package/dist/features/tester/seed-data/authored/seo__canonical-and-host.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/seo__canonical-and-host.js +0 -127
- package/dist/features/tester/seed-data/authored/seo__og-images.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/seo__og-images.js +0 -107
- package/dist/features/tester/seed-data/authored/seo__page-metadata.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/seo__page-metadata.js +0 -120
- package/dist/features/tester/seed-data/authored/seo__sitemap-and-robots.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/seo__sitemap-and-robots.js +0 -105
- package/dist/features/tester/seed-data/authored/tools__sticker-promote-to-listings.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/tools__sticker-promote-to-listings.js +0 -58
- package/dist/features/tester/seed-data/authored/tools__sticker-tracer.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/tools__sticker-tracer.js +0 -284
- package/dist/features/tester/seed-data/index.d.ts +0 -2
- package/dist/features/tester/seed-data/index.js +0 -2
- package/dist/features/tester/seed-data/tester-checklist-seed-data.d.ts +0 -2
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +0 -7601
- package/dist/features/tester/seed-data/tester-responses-seed-data.d.ts +0 -2
- package/dist/features/tester/seed-data/tester-responses-seed-data.js +0 -95
- package/dist/features/tester/seed-data/tester-window.d.ts +0 -25
- package/dist/features/tester/seed-data/tester-window.js +0 -66
- package/dist/seed/_helpers/brand-rows.d.ts +0 -35
- package/dist/seed/_helpers/brand-rows.js +0 -600
- package/dist/seed/_helpers/category-content-generated.d.ts +0 -24
- package/dist/seed/_helpers/category-content-generated.js +0 -1853
- package/dist/seed/_helpers/category-content.d.ts +0 -68
- package/dist/seed/_helpers/category-content.js +0 -731
- package/dist/seed/_helpers/category-forest.d.ts +0 -4
- package/dist/seed/_helpers/category-forest.js +0 -824
- package/dist/seed/_helpers/category-images-generated.d.ts +0 -57
- package/dist/seed/_helpers/category-images-generated.js +0 -618
- package/dist/seed/_helpers/category-models/chunk0.d.ts +0 -2
- package/dist/seed/_helpers/category-models/chunk0.js +0 -2648
- package/dist/seed/_helpers/category-models/chunk1.d.ts +0 -2
- package/dist/seed/_helpers/category-models/chunk1.js +0 -2318
- package/dist/seed/_helpers/category-models/chunk2.d.ts +0 -2
- package/dist/seed/_helpers/category-models/chunk2.js +0 -2560
- package/dist/seed/_helpers/category-models/chunk3.d.ts +0 -2
- package/dist/seed/_helpers/category-models/chunk3.js +0 -2196
- package/dist/seed/_helpers/category-models/chunk4.d.ts +0 -2
- package/dist/seed/_helpers/category-models/chunk4.js +0 -1732
- package/dist/seed/_helpers/category-models.d.ts +0 -12
- package/dist/seed/_helpers/category-models.js +0 -45
- package/dist/seed/_helpers/category-names.d.ts +0 -54
- package/dist/seed/_helpers/category-names.js +0 -118
- package/dist/seed/_helpers/category-parts-glossary-map.d.ts +0 -1
- package/dist/seed/_helpers/category-parts-glossary-map.js +0 -2499
- package/dist/seed/_helpers/category-parts.d.ts +0 -9
- package/dist/seed/_helpers/category-parts.js +0 -15022
- package/dist/seed/_helpers/category-tree.d.ts +0 -27
- package/dist/seed/_helpers/category-tree.js +0 -119
- package/dist/seed/_helpers/character-features.d.ts +0 -10
- package/dist/seed/_helpers/character-features.js +0 -120
- package/dist/seed/_helpers/final-sale-fixtures.d.ts +0 -35
- package/dist/seed/_helpers/final-sale-fixtures.js +0 -61
- package/dist/seed/_helpers/franchise-rows.d.ts +0 -43
- package/dist/seed/_helpers/franchise-rows.js +0 -189
- package/dist/seed/_helpers/media.d.ts +0 -67
- package/dist/seed/_helpers/media.js +0 -172
- package/dist/seed/_helpers/part-colours-generated.d.ts +0 -18
- package/dist/seed/_helpers/part-colours-generated.js +0 -273
- package/dist/seed/_helpers/product-search-txt.d.ts +0 -18
- package/dist/seed/_helpers/product-search-txt.js +0 -23
- package/dist/seed/_helpers/search-txt-wrappers.d.ts +0 -26
- package/dist/seed/_helpers/search-txt-wrappers.js +0 -58
- package/dist/seed/addresses-seed-data.d.ts +0 -2
- package/dist/seed/addresses-seed-data.js +0 -78
- package/dist/seed/authenticity-knowledge-seed-data.d.ts +0 -78
- package/dist/seed/authenticity-knowledge-seed-data.js +0 -10729
- package/dist/seed/bids-seed-data.d.ts +0 -2
- package/dist/seed/bids-seed-data.js +0 -206
- package/dist/seed/blog-posts-seed-data.d.ts +0 -6
- package/dist/seed/blog-posts-seed-data.js +0 -442
- package/dist/seed/carousel-slides-seed-data.d.ts +0 -2
- package/dist/seed/carousel-slides-seed-data.js +0 -411
- package/dist/seed/carousels-seed-data.d.ts +0 -2
- package/dist/seed/carousels-seed-data.js +0 -36
- package/dist/seed/cart-seed-data.d.ts +0 -2
- package/dist/seed/cart-seed-data.js +0 -241
- package/dist/seed/catalogue-seed-data.d.ts +0 -2
- package/dist/seed/catalogue-seed-data.js +0 -112
- package/dist/seed/categories-seed-data.d.ts +0 -3
- package/dist/seed/categories-seed-data.js +0 -816
- package/dist/seed/claimed-coupons-seed-data.d.ts +0 -2
- package/dist/seed/claimed-coupons-seed-data.js +0 -85
- package/dist/seed/coupon-usage-seed-data.d.ts +0 -9
- package/dist/seed/coupon-usage-seed-data.js +0 -55
- package/dist/seed/coupons-seed-data.d.ts +0 -2
- package/dist/seed/coupons-seed-data.js +0 -226
- package/dist/seed/defaults/categories.d.ts +0 -2
- package/dist/seed/defaults/categories.js +0 -12
- package/dist/seed/defaults/faqs.d.ts +0 -2
- package/dist/seed/defaults/faqs.js +0 -40
- package/dist/seed/events-seed-data.d.ts +0 -7
- package/dist/seed/events-seed-data.js +0 -482
- package/dist/seed/factories/address.factory.d.ts +0 -21
- package/dist/seed/factories/address.factory.js +0 -44
- package/dist/seed/factories/bid.factory.d.ts +0 -15
- package/dist/seed/factories/bid.factory.js +0 -33
- package/dist/seed/factories/blog-post.factory.d.ts +0 -15
- package/dist/seed/factories/blog-post.factory.js +0 -22
- package/dist/seed/factories/carousel.factory.d.ts +0 -11
- package/dist/seed/factories/carousel.factory.js +0 -17
- package/dist/seed/factories/cart.factory.d.ts +0 -25
- package/dist/seed/factories/cart.factory.js +0 -35
- package/dist/seed/factories/category.factory.d.ts +0 -11
- package/dist/seed/factories/category.factory.js +0 -16
- package/dist/seed/factories/coupon.factory.d.ts +0 -21
- package/dist/seed/factories/coupon.factory.js +0 -43
- package/dist/seed/factories/faq.factory.d.ts +0 -10
- package/dist/seed/factories/faq.factory.js +0 -15
- package/dist/seed/factories/notification.factory.d.ts +0 -18
- package/dist/seed/factories/notification.factory.js +0 -39
- package/dist/seed/factories/order.factory.d.ts +0 -20
- package/dist/seed/factories/order.factory.js +0 -20
- package/dist/seed/factories/payout.factory.d.ts +0 -21
- package/dist/seed/factories/payout.factory.js +0 -38
- package/dist/seed/factories/product.factory.d.ts +0 -27
- package/dist/seed/factories/product.factory.js +0 -80
- package/dist/seed/factories/review.factory.d.ts +0 -14
- package/dist/seed/factories/review.factory.js +0 -52
- package/dist/seed/factories/session.factory.d.ts +0 -15
- package/dist/seed/factories/session.factory.js +0 -31
- package/dist/seed/factories/store.factory.d.ts +0 -23
- package/dist/seed/factories/store.factory.js +0 -47
- package/dist/seed/factories/user.factory.d.ts +0 -19
- package/dist/seed/factories/user.factory.js +0 -56
- package/dist/seed/faq-seed-data.d.ts +0 -18
- package/dist/seed/faq-seed-data.js +0 -1956
- package/dist/seed/firestore-indexes.d.ts +0 -12
- package/dist/seed/firestore-indexes.js +0 -31
- package/dist/seed/grouped-listings-seed-data.d.ts +0 -2
- package/dist/seed/grouped-listings-seed-data.js +0 -283
- package/dist/seed/history-seed-data.d.ts +0 -12
- package/dist/seed/history-seed-data.js +0 -70
- package/dist/seed/homepage-sections-seed-data.d.ts +0 -2
- package/dist/seed/homepage-sections-seed-data.js +0 -1018
- package/dist/seed/index.d.ts +0 -98
- package/dist/seed/index.js +0 -112
- package/dist/seed/lottery-entries-seed-data.d.ts +0 -2
- package/dist/seed/lottery-entries-seed-data.js +0 -108
- package/dist/seed/manifest.d.ts +0 -16
- package/dist/seed/manifest.js +0 -235
- package/dist/seed/media-assets-seed-data.d.ts +0 -22
- package/dist/seed/media-assets-seed-data.js +0 -5593
- package/dist/seed/model-composition-seed-data.d.ts +0 -15
- package/dist/seed/model-composition-seed-data.js +0 -9032
- package/dist/seed/notifications-seed-data.d.ts +0 -2
- package/dist/seed/notifications-seed-data.js +0 -606
- package/dist/seed/offers-seed-data.d.ts +0 -2
- package/dist/seed/offers-seed-data.js +0 -398
- package/dist/seed/orders-seed-data.d.ts +0 -2
- package/dist/seed/orders-seed-data.js +0 -845
- package/dist/seed/part-glossary/chunk0.d.ts +0 -9
- package/dist/seed/part-glossary/chunk0.js +0 -35448
- package/dist/seed/part-glossary/chunk1.d.ts +0 -9
- package/dist/seed/part-glossary/chunk1.js +0 -40494
- package/dist/seed/part-glossary/chunk2.d.ts +0 -9
- package/dist/seed/part-glossary/chunk2.js +0 -51242
- package/dist/seed/part-glossary/chunk3.d.ts +0 -9
- package/dist/seed/part-glossary/chunk3.js +0 -53337
- package/dist/seed/part-glossary/chunk4.d.ts +0 -9
- package/dist/seed/part-glossary/chunk4.js +0 -51454
- package/dist/seed/part-glossary-seed-data.d.ts +0 -15
- package/dist/seed/part-glossary-seed-data.js +0 -25
- package/dist/seed/payouts-seed-data.d.ts +0 -2
- package/dist/seed/payouts-seed-data.js +0 -302
- package/dist/seed/product-features-seed-data.d.ts +0 -3
- package/dist/seed/product-features-seed-data.js +0 -366
- package/dist/seed/products-art-seed-data.d.ts +0 -2
- package/dist/seed/products-art-seed-data.js +0 -289
- package/dist/seed/products-auctions-seed-data.d.ts +0 -2
- package/dist/seed/products-auctions-seed-data.js +0 -475
- package/dist/seed/products-beyblade-x-seed-data.d.ts +0 -2
- package/dist/seed/products-beyblade-x-seed-data.js +0 -476
- package/dist/seed/products-catalogue-seed-data.d.ts +0 -22
- package/dist/seed/products-catalogue-seed-data.js +0 -86384
- package/dist/seed/products-classifieds-seed-data.d.ts +0 -7
- package/dist/seed/products-classifieds-seed-data.js +0 -435
- package/dist/seed/products-digital-codes-seed-data.d.ts +0 -7
- package/dist/seed/products-digital-codes-seed-data.js +0 -395
- package/dist/seed/products-live-items-seed-data.d.ts +0 -7
- package/dist/seed/products-live-items-seed-data.js +0 -269
- package/dist/seed/products-preorders-seed-data.d.ts +0 -2
- package/dist/seed/products-preorders-seed-data.js +0 -360
- package/dist/seed/products-prize-draws-seed-data.d.ts +0 -7
- package/dist/seed/products-prize-draws-seed-data.js +0 -427
- package/dist/seed/products-spares-seed-data.d.ts +0 -2
- package/dist/seed/products-spares-seed-data.js +0 -368
- package/dist/seed/products-standard-seed-data.d.ts +0 -2
- package/dist/seed/products-standard-seed-data.js +0 -632
- package/dist/seed/products-stickers-seed-data.d.ts +0 -2
- package/dist/seed/products-stickers-seed-data.js +0 -319
- package/dist/seed/reviews-seed-data.d.ts +0 -1
- package/dist/seed/reviews-seed-data.js +0 -185
- package/dist/seed/runner.d.ts +0 -2
- package/dist/seed/runner.js +0 -125
- package/dist/seed/scammers-seed-data.d.ts +0 -2
- package/dist/seed/scammers-seed-data.js +0 -235
- package/dist/seed/seed-market-config.d.ts +0 -48
- package/dist/seed/seed-market-config.js +0 -199
- package/dist/seed/seed-purpose.d.ts +0 -299
- package/dist/seed/seed-purpose.js +0 -431
- package/dist/seed/sessions-seed-data.d.ts +0 -3
- package/dist/seed/sessions-seed-data.js +0 -116
- package/dist/seed/shipments-seed-data.d.ts +0 -5
- package/dist/seed/shipments-seed-data.js +0 -185
- package/dist/seed/site-settings-seed-data.d.ts +0 -2
- package/dist/seed/site-settings-seed-data.js +0 -1069
- package/dist/seed/store-addresses-seed-data.d.ts +0 -18
- package/dist/seed/store-addresses-seed-data.js +0 -71
- package/dist/seed/store-extensions-seed-data.d.ts +0 -11
- package/dist/seed/store-extensions-seed-data.js +0 -292
- package/dist/seed/stores-seed-data.d.ts +0 -6
- package/dist/seed/stores-seed-data.js +0 -242
- package/dist/seed/support-tickets-seed-data.d.ts +0 -16
- package/dist/seed/support-tickets-seed-data.js +0 -239
- package/dist/seed/tax-codes-seed-data.d.ts +0 -34
- package/dist/seed/tax-codes-seed-data.js +0 -100
- package/dist/seed/test-data-ttl.d.ts +0 -29
- package/dist/seed/test-data-ttl.js +0 -31
- package/dist/seed/test-utils.d.ts +0 -49
- package/dist/seed/test-utils.js +0 -110
- package/dist/seed/types.d.ts +0 -89
- package/dist/seed/types.js +0 -15
- package/dist/seed/users-seed-data.d.ts +0 -2
- package/dist/seed/users-seed-data.js +0 -597
- package/dist/seed/wishlists-seed-data.d.ts +0 -12
- package/dist/seed/wishlists-seed-data.js +0 -54
|
@@ -1,816 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* WHY: The real catalogue taxonomy. 🛑 This file is NO LONGER Beyblade-only —
|
|
3
|
-
* its header said "narrowed to Beyblade so the demo has a coherent
|
|
4
|
-
* dataset instead of a sprawling multi-franchise catalog" and B5 reversed
|
|
5
|
-
* that deliberately, on evidence: a 15-site crawl of ~25,900 live Indian
|
|
6
|
-
* listings showed trading cards, collectible figures and Hot Wheels are
|
|
7
|
-
* each a real vertical with its own measured price bands, and three of
|
|
8
|
-
* the five largest competitors sell more than one of them.
|
|
9
|
-
*
|
|
10
|
-
* The old framing is not contradicted here, it is superseded — coherence
|
|
11
|
-
* now comes from the TREE being one axis deep per vertical rather than
|
|
12
|
-
* from the catalogue being one franchise wide.
|
|
13
|
-
*
|
|
14
|
-
* WHAT: Exports categoriesSeedData — a 5-level, SIX-root forest (Spinning Tops,
|
|
15
|
-
* Trading Cards, Collectible Figures, Hot Wheels, Model Kits, Living
|
|
16
|
-
* Collectibles — see _helpers/category-forest.ts), 28 brand rows
|
|
17
|
-
* (categoryType:"brand", _helpers/brand-rows.ts), two sublisting rows and
|
|
18
|
-
* five pricing bundles (categoryType:"bundle").
|
|
19
|
-
*
|
|
20
|
-
* The tree was 2 levels under a single root until 2026-08-24 and 47 nodes
|
|
21
|
-
* across 2 roots until B5. The first state made
|
|
22
|
-
* `categoryType:"sublisting"` — documented as "tier-4 leaf groups under a
|
|
23
|
-
* parent category" — structurally unreachable, and left the live-item
|
|
24
|
-
* products with no category at all.
|
|
25
|
-
*
|
|
26
|
-
* The tier-4 layer is 216 NAMED MODEL leaves (Lost Longinus, Storm
|
|
27
|
-
* Pegasus, Dran Sword …), GENERATED from the canonical corpus in
|
|
28
|
-
* _helpers/category-models.ts rather than typed by hand — a slug is the
|
|
29
|
-
* document id, the public URL and a searchTxt source, so it is the one
|
|
30
|
-
* field that cannot be quietly corrected later.
|
|
31
|
-
*
|
|
32
|
-
* Products tag their FULL ancestor chain (leaf -> … -> root) so a single
|
|
33
|
-
* array-contains at any level returns the whole subtree. That is what lets
|
|
34
|
-
* a category page match on its own id alone; expanding descendants into an
|
|
35
|
-
* `array-contains-any` would break past Firestore's 30-value cap.
|
|
36
|
-
*
|
|
37
|
-
* Structural fields (parentIds/ancestors/tier/path/position/subtreeSize/
|
|
38
|
-
* childrenIds/isLeaf) are DERIVED by buildCategoryTree — never hand-written.
|
|
39
|
-
* The old hand-written values were already internally inconsistent: the root
|
|
40
|
-
* claimed subtreeSize 4 at position 0 while its four children also occupied
|
|
41
|
-
* positions 0-3, which is not a valid DFS pre-order numbering.
|
|
42
|
-
*
|
|
43
|
-
* EXPORTS:
|
|
44
|
-
* categoriesSeedData — array of Partial<CategoryDocument> for the seed runner
|
|
45
|
-
*
|
|
46
|
-
* @tag domain:categories,brands
|
|
47
|
-
* @tag layer:seed
|
|
48
|
-
* @tag pattern:none
|
|
49
|
-
* @tag access:server-only
|
|
50
|
-
* @tag consumers:seed/runner.ts
|
|
51
|
-
* @tag sideEffects:none
|
|
52
|
-
*/
|
|
53
|
-
import { CATEGORY_FIELDS } from "../constants/field-names.js";
|
|
54
|
-
import { seedPhoto, isSeedPlaceholderImage } from "./_helpers/media.js";
|
|
55
|
-
import { buildCategoryTree } from "./_helpers/category-tree.js";
|
|
56
|
-
import { withCategorySearchTxt } from "./_helpers/search-txt-wrappers.js";
|
|
57
|
-
import { CATEGORY_FOREST } from "./_helpers/category-forest.js";
|
|
58
|
-
import { BRAND_ROW_COUNT, buildBrandRows } from "./_helpers/brand-rows.js";
|
|
59
|
-
import { FRANCHISE_ROW_COUNT, buildFranchiseRows } from "./_helpers/franchise-rows.js";
|
|
60
|
-
import { MODEL_LEAF_COUNT, MODEL_LEAVES } from "./_helpers/category-models.js";
|
|
61
|
-
import { PART_LEAF_COUNT, PART_LEAVES } from "./_helpers/category-parts.js";
|
|
62
|
-
import { MARKET_BAND_LEAF_IDS } from "./_helpers/category-content.js";
|
|
63
|
-
import { GENERATED_CATEGORY_CONTENT, GENERATED_CATEGORY_CONTENT_COUNT, } from "./_helpers/category-content-generated.js";
|
|
64
|
-
import { GENERATED_CATEGORY_IMAGES, GENERATED_CATEGORY_IMAGE_COUNT, GENERATED_CATEGORY_HASBRO_IMAGES, } from "./_helpers/category-images-generated.js";
|
|
65
|
-
import { productsStandardSeedData } from "./products-standard-seed-data.js";
|
|
66
|
-
import { productsBeybladeXSeedData } from "./products-beyblade-x-seed-data.js";
|
|
67
|
-
const NOW = new Date();
|
|
68
|
-
const daysAgo = (n) => new Date(NOW.getTime() - n * 86400000);
|
|
69
|
-
const emptyMetrics = {
|
|
70
|
-
productCount: 0,
|
|
71
|
-
productIds: [],
|
|
72
|
-
auctionCount: 0,
|
|
73
|
-
auctionIds: [],
|
|
74
|
-
totalProductCount: 0,
|
|
75
|
-
totalAuctionCount: 0,
|
|
76
|
-
totalItemCount: 0,
|
|
77
|
-
lastUpdated: daysAgo(1),
|
|
78
|
-
};
|
|
79
|
-
// ──────────────────────────────────────────────────────────────────────────────
|
|
80
|
-
// Categories — 381 rows across 5 tiers and 6 roots, every structural field
|
|
81
|
-
// derived. The shape lives in _helpers/category-forest.ts, the named-model
|
|
82
|
-
// tier-4 leaves in the GENERATED _helpers/category-models.ts, and the
|
|
83
|
-
// derivation in _helpers/category-tree.ts.
|
|
84
|
-
//
|
|
85
|
-
// 🛑 Never quote this count without running `buildCategoryTree` against the
|
|
86
|
-
// forest — it was 47 before B5 and the comment here said so for months after
|
|
87
|
-
// the tree changed underneath it.
|
|
88
|
-
// ──────────────────────────────────────────────────────────────────────────────
|
|
89
|
-
const rawCategories = buildCategoryTree(CATEGORY_FOREST, {
|
|
90
|
-
createdBy: "user-admin-letitrip",
|
|
91
|
-
createdAt: daysAgo(300),
|
|
92
|
-
updatedAt: daysAgo(1),
|
|
93
|
-
defaults: {
|
|
94
|
-
metrics: emptyMetrics,
|
|
95
|
-
isFeatured: false,
|
|
96
|
-
isActive: true,
|
|
97
|
-
isSearchable: true,
|
|
98
|
-
showOnHomepage: false,
|
|
99
|
-
},
|
|
100
|
-
});
|
|
101
|
-
/**
|
|
102
|
-
* Sublistings — `categoryType:"sublisting"`, documented on `CategoryDocument`
|
|
103
|
-
* as "tier-4 leaf groups under a parent category". This array was EMPTY for the
|
|
104
|
-
* life of the seed, because the tree only went two levels deep and there was no
|
|
105
|
-
* tier-3 leaf to hang a tier-4 group off. Now that there is, these give
|
|
106
|
-
* `itemCode` and `ProductDocument.sublistingCategoryId` / `sublistingIcon`
|
|
107
|
-
* their first real data.
|
|
108
|
-
*
|
|
109
|
-
* `itemCode` is the grading/variant code a collector actually searches by.
|
|
110
|
-
*/
|
|
111
|
-
const sublistingRows = [
|
|
112
|
-
{
|
|
113
|
-
id: "sublisting-dranzer-s-a-5",
|
|
114
|
-
slug: "sublisting-dranzer-s-a-5",
|
|
115
|
-
name: "Dranzer S (A-5)",
|
|
116
|
-
description: "The A-5 Dranzer S release — the original Plastic Generation Dranzer.",
|
|
117
|
-
categoryType: CATEGORY_FIELDS.CATEGORY_TYPE_VALUES.SUBLISTING,
|
|
118
|
-
itemCode: "A-5",
|
|
119
|
-
rootId: "category-spinning-tops",
|
|
120
|
-
parentIds: [
|
|
121
|
-
"category-spinning-tops",
|
|
122
|
-
"category-beyblade-original",
|
|
123
|
-
"category-original-tops",
|
|
124
|
-
"category-original-plastic-gen",
|
|
125
|
-
],
|
|
126
|
-
childrenIds: [],
|
|
127
|
-
ancestors: [
|
|
128
|
-
{ id: "category-spinning-tops", name: "Spinning Tops", tier: 0 },
|
|
129
|
-
{ id: "category-beyblade-original", name: "Beyblade Original", tier: 1 },
|
|
130
|
-
{ id: "category-original-tops", name: "Original Tops", tier: 2 },
|
|
131
|
-
{ id: "category-original-plastic-gen", name: "Plastic Generation", tier: 3 },
|
|
132
|
-
],
|
|
133
|
-
tier: 4,
|
|
134
|
-
path: "spinning-tops/beyblade-original/original-tops/original-plastic-gen/dranzer-s-a-5",
|
|
135
|
-
isLeaf: true,
|
|
136
|
-
order: 1,
|
|
137
|
-
position: 900,
|
|
138
|
-
subtreeSize: 1,
|
|
139
|
-
metrics: emptyMetrics,
|
|
140
|
-
isFeatured: false,
|
|
141
|
-
isActive: true,
|
|
142
|
-
isSearchable: true,
|
|
143
|
-
display: { icon: "🔥", showInMenu: false, showInFooter: false },
|
|
144
|
-
seo: { title: "Dranzer S (A-5) | LetItRip", description: "The A-5 Dranzer S release.", keywords: ["dranzer s", "a-5", "plastic generation"] },
|
|
145
|
-
createdBy: "user-admin-letitrip",
|
|
146
|
-
createdAt: daysAgo(300),
|
|
147
|
-
updatedAt: daysAgo(30),
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
id: "sublisting-storm-pegasus-105rf",
|
|
151
|
-
slug: "sublisting-storm-pegasus-105rf",
|
|
152
|
-
name: "Storm Pegasus 105RF",
|
|
153
|
-
description: "Storm Pegasus on a 105 track with a Rubber Flat bottom — the canonical attack build.",
|
|
154
|
-
categoryType: CATEGORY_FIELDS.CATEGORY_TYPE_VALUES.SUBLISTING,
|
|
155
|
-
itemCode: "105RF",
|
|
156
|
-
rootId: "category-spinning-tops",
|
|
157
|
-
parentIds: [
|
|
158
|
-
"category-spinning-tops",
|
|
159
|
-
"category-beyblade-metal",
|
|
160
|
-
"category-metal-tops",
|
|
161
|
-
"category-metal-fusion",
|
|
162
|
-
],
|
|
163
|
-
childrenIds: [],
|
|
164
|
-
ancestors: [
|
|
165
|
-
{ id: "category-spinning-tops", name: "Spinning Tops", tier: 0 },
|
|
166
|
-
{ id: "category-beyblade-metal", name: "Beyblade Metal Fight", tier: 1 },
|
|
167
|
-
{ id: "category-metal-tops", name: "Metal Fight Tops", tier: 2 },
|
|
168
|
-
{ id: "category-metal-fusion", name: "Metal Fusion", tier: 3 },
|
|
169
|
-
],
|
|
170
|
-
tier: 4,
|
|
171
|
-
path: "spinning-tops/beyblade-metal/metal-tops/metal-fusion/storm-pegasus-105rf",
|
|
172
|
-
isLeaf: true,
|
|
173
|
-
order: 2,
|
|
174
|
-
position: 901,
|
|
175
|
-
subtreeSize: 1,
|
|
176
|
-
metrics: emptyMetrics,
|
|
177
|
-
isFeatured: false,
|
|
178
|
-
isActive: true,
|
|
179
|
-
isSearchable: true,
|
|
180
|
-
display: { icon: "🐴", showInMenu: false, showInFooter: false },
|
|
181
|
-
seo: { title: "Storm Pegasus 105RF | LetItRip", description: "Storm Pegasus 105RF builds.", keywords: ["storm pegasus", "105rf", "metal fusion"] },
|
|
182
|
-
createdBy: "user-admin-letitrip",
|
|
183
|
-
createdAt: daysAgo(300),
|
|
184
|
-
updatedAt: daysAgo(30),
|
|
185
|
-
},
|
|
186
|
-
];
|
|
187
|
-
// ──────────────────────────────────────────────────────────────────────────────
|
|
188
|
-
// Brands (categoryType:"brand") — 4 rows -> 28, built by a factory.
|
|
189
|
-
//
|
|
190
|
-
// The shape, the three live traps (display-name matching, icon-beats-cover,
|
|
191
|
-
// and why a brand with no products is worse than no brand) and the logo
|
|
192
|
-
// decision all live in _helpers/brand-rows.ts. Four inline rows at ~35 lines
|
|
193
|
-
// each was already the wrong shape for 28.
|
|
194
|
-
// ──────────────────────────────────────────────────────────────────────────────
|
|
195
|
-
const brandRows = buildBrandRows({
|
|
196
|
-
createdBy: "user-admin-letitrip",
|
|
197
|
-
createdAt: daysAgo(300),
|
|
198
|
-
updatedAt: daysAgo(30),
|
|
199
|
-
});
|
|
200
|
-
// FRANCHISE rows — what a product is FROM, where a brand is who MADE it.
|
|
201
|
-
// Same tier-0, outside-the-forest shape as brands. Seven of the eight are
|
|
202
|
-
// seeded INACTIVE on purpose; see the dead-facet rule in _helpers/franchise-rows.ts.
|
|
203
|
-
const franchiseRows = buildFranchiseRows({
|
|
204
|
-
createdBy: "user-admin-letitrip",
|
|
205
|
-
createdAt: daysAgo(300),
|
|
206
|
-
updatedAt: daysAgo(30),
|
|
207
|
-
});
|
|
208
|
-
// P-17 — 5 bundle rows (categoryType:"bundle") grouping the Beyblade-minimal
|
|
209
|
-
// standard products by generation, plus one cross-generation starter pack.
|
|
210
|
-
// Every bundle references real seeded product ids (products-standard-seed-data.ts),
|
|
211
|
-
// all currently under store-beyblade-arena.
|
|
212
|
-
/*
|
|
213
|
-
* 🛑 `bundleCategorySlugs` is DERIVED, because it is a MIRROR and mirrors
|
|
214
|
-
* drift (Root Cause #42).
|
|
215
|
-
*
|
|
216
|
-
* The field is the union of the members' `categorySlugs`, and it is what lets
|
|
217
|
-
* a category page scope its Bundles tab at all. It was hand-written across
|
|
218
|
-
* all five bundles — and it drifted in B5 the moment the Beyblade X tree
|
|
219
|
-
* changed: two bundles still named `category-x-starters` and
|
|
220
|
-
* `category-x-boosters`, leaves that no longer exist, so those bundles had
|
|
221
|
-
* silently stopped appearing under any Beyblade X category.
|
|
222
|
-
*
|
|
223
|
-
* Worse, the stale strings were still being READ as valid seed ids by
|
|
224
|
-
* `audit-tester-plugin-wiring` R7 once its id scan was widened, which briefly
|
|
225
|
-
* made two genuinely-broken authored tester cases stop being reported. A
|
|
226
|
-
* hand-maintained mirror does not only go wrong — it can keep the check that
|
|
227
|
-
* would have caught it quiet.
|
|
228
|
-
*
|
|
229
|
-
* Deriving from the members' own chains means the union cannot disagree with
|
|
230
|
-
* them, and a member whose chain moves updates every bundle holding it.
|
|
231
|
-
*/
|
|
232
|
-
/*
|
|
233
|
-
* Both real-product arrays, not just the standard one.
|
|
234
|
-
*
|
|
235
|
-
* `productsBeybladeXSeedData` was added 2026-10-19 as a separate file (real,
|
|
236
|
-
* wiki-verified Beyblade X listings under store-letitrip-official). A bundle
|
|
237
|
-
* may legitimately hold one of those, and resolving only the standard array
|
|
238
|
-
* would make `bundleCategorySlugsFor` throw "not in productsStandardSeedData"
|
|
239
|
-
* for a product that genuinely exists — which is exactly what happened when
|
|
240
|
-
* the two fictional X fixtures this file's bundles used to reference were
|
|
241
|
-
* removed in favour of real ones.
|
|
242
|
-
*/
|
|
243
|
-
const PRODUCT_CHAINS = new Map([...productsStandardSeedData, ...productsBeybladeXSeedData].map((p) => [
|
|
244
|
-
String(p.id),
|
|
245
|
-
(p.categorySlugs ?? []),
|
|
246
|
-
]));
|
|
247
|
-
function bundleCategorySlugsFor(productIds) {
|
|
248
|
-
const out = [];
|
|
249
|
-
for (const id of productIds) {
|
|
250
|
-
const chain = PRODUCT_CHAINS.get(id);
|
|
251
|
-
/*
|
|
252
|
-
* A member missing from the real-product seeds is a BUG, not something to
|
|
253
|
-
* shrug past: the bundle would silently lose that member's categories and
|
|
254
|
-
* the Bundles tab would under-report. The map was widened (rather than
|
|
255
|
-
* this check softened) on 2026-10-19 to cover
|
|
256
|
-
* `productsBeybladeXSeedData` as well — if another real-product file is
|
|
257
|
-
* added, widen it again.
|
|
258
|
-
*/
|
|
259
|
-
if (!chain) {
|
|
260
|
-
throw new Error(`bundle member ${id} is in neither productsStandardSeedData nor productsBeybladeXSeedData`);
|
|
261
|
-
}
|
|
262
|
-
for (const c of chain)
|
|
263
|
-
if (!out.includes(c))
|
|
264
|
-
out.push(c);
|
|
265
|
-
}
|
|
266
|
-
return out;
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* Attach a bundle's member images so its card can render a collage.
|
|
270
|
-
*
|
|
271
|
-
* 🛑 A bundle IS its contents, so a single placeholder tile is the least
|
|
272
|
-
* informative thing a bundle card can show. Resolved here, at seed time, from
|
|
273
|
-
* the members' own `mainImage` — a card cannot do it at render time without one
|
|
274
|
-
* query per bundle, and a grid of five would be five reads for decoration.
|
|
275
|
-
*
|
|
276
|
-
* `<ImageMosaic>` renders nothing below `MOSAIC_MIN` (3), so a bundle with one
|
|
277
|
-
* or two resolvable members quietly falls back to its cover image rather than
|
|
278
|
-
* showing a broken collage. A member id that resolves to no product is dropped
|
|
279
|
-
* rather than defaulted — a wrong photo is worse than one fewer tile.
|
|
280
|
-
*/
|
|
281
|
-
function withBundleMosaic(row) {
|
|
282
|
-
const ids = row.bundleProductIds ?? [];
|
|
283
|
-
// `row.display` must already exist — `CategoryDocumentDisplay` requires
|
|
284
|
-
// `showInFooter`, so spreading onto `{}` would drop a required field. Every
|
|
285
|
-
// bundle row declares one; a row without is left untouched rather than
|
|
286
|
-
// half-built.
|
|
287
|
-
if (ids.length === 0 || !row.display)
|
|
288
|
-
return row;
|
|
289
|
-
// Both real-product arrays, for the same reason as PRODUCT_CHAINS above —
|
|
290
|
-
// otherwise a bundle holding a Beyblade X member silently loses that tile.
|
|
291
|
-
const images = ids
|
|
292
|
-
.map((id) => [...productsStandardSeedData, ...productsBeybladeXSeedData].find((p) => p.id === id)
|
|
293
|
-
?.mainImage)
|
|
294
|
-
.filter((src) => typeof src === "string" && src.length > 0);
|
|
295
|
-
if (images.length === 0)
|
|
296
|
-
return row;
|
|
297
|
-
return { ...row, display: { ...row.display, mosaicImages: images } };
|
|
298
|
-
}
|
|
299
|
-
const bundleRows = [
|
|
300
|
-
{
|
|
301
|
-
id: "bundle-original-collectors-set",
|
|
302
|
-
name: "Original Collector's Set",
|
|
303
|
-
slug: "bundle-original-collectors-set",
|
|
304
|
-
description: "Both original-generation Beyblades — Dranzer S and Driger V — bundled at a discount.",
|
|
305
|
-
categoryType: "bundle",
|
|
306
|
-
bundleKind: "special",
|
|
307
|
-
brandSlug: "brand-beyblade",
|
|
308
|
-
bundlePrice: 2999,
|
|
309
|
-
bundleQueryRule: {
|
|
310
|
-
type: "static",
|
|
311
|
-
productIds: ["product-beyblade-original-dranzer-s", "product-beyblade-original-driger-v", "product-beyblade-metal-storm-pegasus"],
|
|
312
|
-
},
|
|
313
|
-
bundleProductIds: ["product-beyblade-original-dranzer-s", "product-beyblade-original-driger-v", "product-beyblade-metal-storm-pegasus"],
|
|
314
|
-
bundleCategorySlugs: bundleCategorySlugsFor(["product-beyblade-original-dranzer-s", "product-beyblade-original-driger-v", "product-beyblade-metal-storm-pegasus"]),
|
|
315
|
-
bundleOriginalTotal: 4597, // 1499 + 1799 + 1299
|
|
316
|
-
bundleStockStatus: "in_stock",
|
|
317
|
-
display: { coverImage: seedPhoto("bundle-original-collectors-set-20260101", 1200, 900), showInFooter: false },
|
|
318
|
-
isActive: true,
|
|
319
|
-
isSearchable: true,
|
|
320
|
-
isFeatured: true,
|
|
321
|
-
order: 0,
|
|
322
|
-
rootId: "bundle-original-collectors-set",
|
|
323
|
-
parentIds: [],
|
|
324
|
-
childrenIds: [],
|
|
325
|
-
tier: 0,
|
|
326
|
-
path: "bundle-original-collectors-set",
|
|
327
|
-
position: 0,
|
|
328
|
-
subtreeSize: 1,
|
|
329
|
-
metrics: { productCount: 0, productIds: [], auctionCount: 0, auctionIds: [], totalProductCount: 0, totalAuctionCount: 0, totalItemCount: 0, lastUpdated: NOW },
|
|
330
|
-
createdBy: "user-admin-letitrip",
|
|
331
|
-
createdByType: "admin",
|
|
332
|
-
createdAt: daysAgo(20),
|
|
333
|
-
updatedAt: daysAgo(5),
|
|
334
|
-
},
|
|
335
|
-
{
|
|
336
|
-
id: "bundle-metal-fusion-duo",
|
|
337
|
-
name: "Metal Fusion Duo",
|
|
338
|
-
slug: "bundle-metal-fusion-duo",
|
|
339
|
-
description: "Storm Pegasus and Flame Sagittario — the classic Metal Fight rivalry, together.",
|
|
340
|
-
categoryType: "bundle",
|
|
341
|
-
bundleKind: "special",
|
|
342
|
-
brandSlug: "brand-takara-tomy",
|
|
343
|
-
bundlePrice: 3499,
|
|
344
|
-
bundleQueryRule: {
|
|
345
|
-
type: "static",
|
|
346
|
-
productIds: ["product-beyblade-metal-storm-pegasus", "product-beyblade-metal-flame-sagittario", "product-beyblade-original-dranzer-s"],
|
|
347
|
-
},
|
|
348
|
-
bundleProductIds: ["product-beyblade-metal-storm-pegasus", "product-beyblade-metal-flame-sagittario", "product-beyblade-original-dranzer-s"],
|
|
349
|
-
bundleCategorySlugs: bundleCategorySlugsFor(["product-beyblade-metal-storm-pegasus", "product-beyblade-metal-flame-sagittario", "product-beyblade-original-dranzer-s"]),
|
|
350
|
-
bundleOriginalTotal: 3997, // 1299 + 1199 + 1499
|
|
351
|
-
bundleStockStatus: "in_stock",
|
|
352
|
-
display: { coverImage: seedPhoto("bundle-metal-fusion-duo-20260101", 1200, 900), showInFooter: false },
|
|
353
|
-
isActive: true,
|
|
354
|
-
isSearchable: true,
|
|
355
|
-
isFeatured: false,
|
|
356
|
-
order: 1,
|
|
357
|
-
rootId: "bundle-metal-fusion-duo",
|
|
358
|
-
parentIds: [],
|
|
359
|
-
childrenIds: [],
|
|
360
|
-
tier: 0,
|
|
361
|
-
path: "bundle-metal-fusion-duo",
|
|
362
|
-
position: 0,
|
|
363
|
-
subtreeSize: 1,
|
|
364
|
-
metrics: { productCount: 0, productIds: [], auctionCount: 0, auctionIds: [], totalProductCount: 0, totalAuctionCount: 0, totalItemCount: 0, lastUpdated: NOW },
|
|
365
|
-
createdBy: "user-admin-letitrip",
|
|
366
|
-
createdByType: "admin",
|
|
367
|
-
createdAt: daysAgo(18),
|
|
368
|
-
updatedAt: daysAgo(4),
|
|
369
|
-
},
|
|
370
|
-
{
|
|
371
|
-
id: "bundle-burst-battlers-pack",
|
|
372
|
-
name: "Burst Battlers Pack",
|
|
373
|
-
slug: "bundle-burst-battlers-pack",
|
|
374
|
-
description: "Valkyrie and Regalia Genesis — top-tier Burst-era attackers in one set.",
|
|
375
|
-
categoryType: "bundle",
|
|
376
|
-
bundleKind: "special",
|
|
377
|
-
brandSlug: "brand-beyblade",
|
|
378
|
-
// Was 3799 — priced ABOVE the 999+1399+1199=3597 member total (no real
|
|
379
|
-
// discount, contradicted the bundle's whole purpose). Corrected 2026-08-19.
|
|
380
|
-
bundlePrice: 2899,
|
|
381
|
-
bundleQueryRule: {
|
|
382
|
-
type: "static",
|
|
383
|
-
productIds: ["product-beyblade-burst-valkyrie", "product-beyblade-burst-regalia-genesis", "product-beyblade-metal-flame-sagittario"],
|
|
384
|
-
},
|
|
385
|
-
bundleProductIds: ["product-beyblade-burst-valkyrie", "product-beyblade-burst-regalia-genesis", "product-beyblade-metal-flame-sagittario"],
|
|
386
|
-
bundleCategorySlugs: bundleCategorySlugsFor(["product-beyblade-burst-valkyrie", "product-beyblade-burst-regalia-genesis", "product-beyblade-metal-flame-sagittario"]),
|
|
387
|
-
bundleOriginalTotal: 3597, // 999 + 1399 + 1199
|
|
388
|
-
bundleStockStatus: "in_stock",
|
|
389
|
-
display: { coverImage: seedPhoto("bundle-burst-battlers-pack-20260101", 1200, 900), showInFooter: false },
|
|
390
|
-
isActive: true,
|
|
391
|
-
isSearchable: true,
|
|
392
|
-
isFeatured: true,
|
|
393
|
-
order: 2,
|
|
394
|
-
rootId: "bundle-burst-battlers-pack",
|
|
395
|
-
parentIds: [],
|
|
396
|
-
childrenIds: [],
|
|
397
|
-
tier: 0,
|
|
398
|
-
path: "bundle-burst-battlers-pack",
|
|
399
|
-
position: 0,
|
|
400
|
-
subtreeSize: 1,
|
|
401
|
-
metrics: { productCount: 0, productIds: [], auctionCount: 0, auctionIds: [], totalProductCount: 0, totalAuctionCount: 0, totalItemCount: 0, lastUpdated: NOW },
|
|
402
|
-
createdBy: "user-admin-letitrip",
|
|
403
|
-
createdByType: "admin",
|
|
404
|
-
createdAt: daysAgo(15),
|
|
405
|
-
updatedAt: daysAgo(3),
|
|
406
|
-
},
|
|
407
|
-
{
|
|
408
|
-
id: "bundle-x-series-starter",
|
|
409
|
-
name: "X-Series Starter Set",
|
|
410
|
-
slug: "bundle-x-series-starter",
|
|
411
|
-
description: "WizardArrow and KnightShield Ver.2.0 — the re-tuned Stamina and Defense tops from the BX-53 set, paired with a Burst-era Valkyrie.",
|
|
412
|
-
categoryType: "bundle",
|
|
413
|
-
bundleKind: "special",
|
|
414
|
-
brandSlug: "brand-takara-tomy",
|
|
415
|
-
bundlePrice: 2499,
|
|
416
|
-
bundleQueryRule: {
|
|
417
|
-
type: "static",
|
|
418
|
-
productIds: ["product-beyblade-x-bx-53-wizardarrow-v2", "product-beyblade-x-bx-53-knightshield-v2", "product-beyblade-burst-valkyrie"],
|
|
419
|
-
},
|
|
420
|
-
bundleProductIds: ["product-beyblade-x-bx-53-wizardarrow-v2", "product-beyblade-x-bx-53-knightshield-v2", "product-beyblade-burst-valkyrie"],
|
|
421
|
-
bundleCategorySlugs: bundleCategorySlugsFor(["product-beyblade-x-bx-53-wizardarrow-v2", "product-beyblade-x-bx-53-knightshield-v2", "product-beyblade-burst-valkyrie"]),
|
|
422
|
-
// 1500 + 1500 + 999. Recomputed 2026-10-19 when the two fictional X
|
|
423
|
-
// fixtures (899 + 949) were replaced by the real BX-53 component
|
|
424
|
-
// listings — a stale total here drives a wrong discount-% badge.
|
|
425
|
-
bundleOriginalTotal: 3999,
|
|
426
|
-
bundleStockStatus: "in_stock",
|
|
427
|
-
display: { coverImage: seedPhoto("bundle-x-series-starter-20260101", 1200, 900), showInFooter: false },
|
|
428
|
-
isActive: true,
|
|
429
|
-
isSearchable: true,
|
|
430
|
-
isFeatured: false,
|
|
431
|
-
order: 3,
|
|
432
|
-
rootId: "bundle-x-series-starter",
|
|
433
|
-
parentIds: [],
|
|
434
|
-
childrenIds: [],
|
|
435
|
-
tier: 0,
|
|
436
|
-
path: "bundle-x-series-starter",
|
|
437
|
-
position: 0,
|
|
438
|
-
subtreeSize: 1,
|
|
439
|
-
metrics: { productCount: 0, productIds: [], auctionCount: 0, auctionIds: [], totalProductCount: 0, totalAuctionCount: 0, totalItemCount: 0, lastUpdated: NOW },
|
|
440
|
-
createdBy: "user-admin-letitrip",
|
|
441
|
-
createdByType: "admin",
|
|
442
|
-
createdAt: daysAgo(12),
|
|
443
|
-
updatedAt: daysAgo(2),
|
|
444
|
-
},
|
|
445
|
-
{
|
|
446
|
-
id: "bundle-every-generation-starter-pack",
|
|
447
|
-
name: "Every Generation Starter Pack",
|
|
448
|
-
slug: "bundle-every-generation-starter-pack",
|
|
449
|
-
description: "One top from every Beyblade generation — Original, Metal Fight, Burst, and X — the ultimate collector's starter pack.",
|
|
450
|
-
categoryType: "bundle",
|
|
451
|
-
bundleKind: "special",
|
|
452
|
-
// Deliberately no brandSlug — genuinely cross-brand (2 Beyblade + 2
|
|
453
|
-
// Takara-Tomy member products), a real "no specific brand" test case.
|
|
454
|
-
// Was 5999 — priced ABOVE the 1799+1299+999+899=4996 member total (no
|
|
455
|
-
// real discount, contradicted the bundle's whole purpose). Corrected 2026-08-19.
|
|
456
|
-
bundlePrice: 3999,
|
|
457
|
-
bundleQueryRule: {
|
|
458
|
-
type: "static",
|
|
459
|
-
productIds: [
|
|
460
|
-
"product-beyblade-original-driger-v",
|
|
461
|
-
"product-beyblade-metal-storm-pegasus",
|
|
462
|
-
"product-beyblade-burst-valkyrie",
|
|
463
|
-
"product-beyblade-x-bx-53-wizardarrow-v2",
|
|
464
|
-
],
|
|
465
|
-
},
|
|
466
|
-
bundleProductIds: [
|
|
467
|
-
"product-beyblade-original-driger-v",
|
|
468
|
-
"product-beyblade-metal-storm-pegasus",
|
|
469
|
-
"product-beyblade-burst-valkyrie",
|
|
470
|
-
"product-beyblade-x-bx-53-wizardarrow-v2",
|
|
471
|
-
],
|
|
472
|
-
bundleCategorySlugs: bundleCategorySlugsFor(["product-beyblade-original-driger-v", "product-beyblade-metal-storm-pegasus", "product-beyblade-burst-valkyrie", "product-beyblade-x-bx-53-wizardarrow-v2"]),
|
|
473
|
-
// 1799 + 1299 + 999 + 1500. Recomputed 2026-10-19 — the X member changed
|
|
474
|
-
// from the fictional "wizard-arrow" (899) to the real BX-53 WizardArrow
|
|
475
|
-
// 4-80B Ver.2.0 (1500).
|
|
476
|
-
bundleOriginalTotal: 5597,
|
|
477
|
-
bundleStockStatus: "in_stock",
|
|
478
|
-
display: { coverImage: seedPhoto("bundle-every-generation-starter-pack-20260101", 1200, 900), showInFooter: false },
|
|
479
|
-
isActive: true,
|
|
480
|
-
isSearchable: true,
|
|
481
|
-
isFeatured: true,
|
|
482
|
-
order: 4,
|
|
483
|
-
rootId: "bundle-every-generation-starter-pack",
|
|
484
|
-
parentIds: [],
|
|
485
|
-
childrenIds: [],
|
|
486
|
-
tier: 0,
|
|
487
|
-
path: "bundle-every-generation-starter-pack",
|
|
488
|
-
position: 0,
|
|
489
|
-
subtreeSize: 1,
|
|
490
|
-
metrics: { productCount: 0, productIds: [], auctionCount: 0, auctionIds: [], totalProductCount: 0, totalAuctionCount: 0, totalItemCount: 0, lastUpdated: NOW },
|
|
491
|
-
createdBy: "user-admin-letitrip",
|
|
492
|
-
createdByType: "admin",
|
|
493
|
-
createdAt: daysAgo(10),
|
|
494
|
-
updatedAt: daysAgo(1),
|
|
495
|
-
},
|
|
496
|
-
];
|
|
497
|
-
/*
|
|
498
|
-
* One array, wrapped once.
|
|
499
|
-
*
|
|
500
|
-
* `categoriesSeedData` and `categoriesP1SeedData` were two byte-identical
|
|
501
|
-
* literals, so every change had to be made twice — and `searchTxt` would have
|
|
502
|
-
* been two derivations to keep in step, which is the exact drift
|
|
503
|
-
* `search-txt-wrappers.ts` exists to prevent.
|
|
504
|
-
*
|
|
505
|
-
* 🛑 `withCategorySearchTxt` is applied AFTER the spread, deliberately:
|
|
506
|
-
* `buildCategorySearchTxt` indexes `path` and `ancestors[].name`, both DERIVED
|
|
507
|
-
* by `buildCategoryTree`. Wrapping a row before derivation would index
|
|
508
|
-
* name/slug/description only and silently drop the lineage search — typing
|
|
509
|
-
* "burst" would stop reaching a tier-4 model under Beyblade Burst.
|
|
510
|
-
*/
|
|
511
|
-
/**
|
|
512
|
-
* I7 — attach the GENERATED body to any row that has one and does not already
|
|
513
|
-
* carry a hand-authored one.
|
|
514
|
-
*
|
|
515
|
-
* 🛑 The generator ran and its 745 bodies were imported by NOBODY. The corpus
|
|
516
|
-
* sat in the repo as 128 KB of dead weight: not seeded, so not in Firestore,
|
|
517
|
-
* so unreadable by the page that renders it. The generator being written is
|
|
518
|
-
* not the same as its output being wired, and the second half is invisible —
|
|
519
|
-
* a category page with no body renders perfectly well, it just ranks on
|
|
520
|
-
* nothing.
|
|
521
|
-
*
|
|
522
|
-
* 🛑 A HAND-AUTHORED body always wins. The four generation roots and several
|
|
523
|
-
* tier-1 nodes carry prose written against the crawl research; the generator
|
|
524
|
-
* composes from facts and is deliberately the weaker writer at those tiers
|
|
525
|
-
* (its own header says the part leaves are its strongest tier). Overwriting
|
|
526
|
-
* authored copy with composed copy would be a silent downgrade of the exact
|
|
527
|
-
* pages this is for.
|
|
528
|
-
*
|
|
529
|
-
* 🛑 And nothing is INVENTED here: a row absent from the map keeps no body at
|
|
530
|
-
* all, because `GENERATED_CATEGORY_CONTENT` omits a node it has nothing
|
|
531
|
-
* factual to say about rather than emitting an empty one.
|
|
532
|
-
*/
|
|
533
|
-
function withGeneratedContent(row) {
|
|
534
|
-
if (typeof row.contentBody === "string" && row.contentBody.trim().length > 0)
|
|
535
|
-
return row;
|
|
536
|
-
const generated = row.id ? GENERATED_CATEGORY_CONTENT[row.id] : undefined;
|
|
537
|
-
return generated ? { ...row, contentBody: generated } : row;
|
|
538
|
-
}
|
|
539
|
-
/**
|
|
540
|
-
* Attach a fetched cover image to a bey-model leaf that has no hand-authored one.
|
|
541
|
-
*
|
|
542
|
-
* Same three rules as `withGeneratedContent`, for the same reasons: a
|
|
543
|
-
* hand-authored `display.coverImage` wins, a row absent from the map keeps
|
|
544
|
-
* nothing rather than being given a placeholder, and the map is keyed on
|
|
545
|
-
* category id so it needs no transformation here.
|
|
546
|
-
*
|
|
547
|
-
* 🛑 These are LOCAL paths under `public/images/beys/`, never `seedExtMedia()`.
|
|
548
|
-
* Wrapping a third-party image URL in `/api/media/ext` makes every render a
|
|
549
|
-
* lambda plus an upstream fetch plus a sharp watermark pass — that is Root Cause
|
|
550
|
-
* #104, and it is part of why this project was suspended on Fast Origin
|
|
551
|
-
* Transfer. 195 proxied covers would have been a direct repeat.
|
|
552
|
-
*
|
|
553
|
-
* 🛑 Measured before this existed: **1,131 of 1,177** categories had no cover at
|
|
554
|
-
* all, and the 46 that did shared six generic SVG colour tiles. So the
|
|
555
|
-
* alternative to this map is not "a different image" — it is a grid of blank
|
|
556
|
-
* cards.
|
|
557
|
-
*/
|
|
558
|
-
function withGeneratedCoverImage(row) {
|
|
559
|
-
const existing = row.display?.coverImage;
|
|
560
|
-
/*
|
|
561
|
-
* 🛑 A seed PLACEHOLDER tile does not count as hand-authored.
|
|
562
|
-
*
|
|
563
|
-
* The rule is still "a hand-authored cover wins", but a `seedPhoto(...)`
|
|
564
|
-
* tile was never authored — it is what the seed emits when it has nothing
|
|
565
|
-
* better. Treating it as a decision is what made the placeholder BLOCK every
|
|
566
|
-
* fetched wordmark: a logo could be downloaded, uploaded to Storage, mapped,
|
|
567
|
-
* and the card would still show a grey tile, with nothing anywhere
|
|
568
|
-
* reporting why.
|
|
569
|
-
*
|
|
570
|
-
* So the precedence is: real authored path > generated image > placeholder
|
|
571
|
-
* tile. That lets every branch row keep an honest tile in source — which is
|
|
572
|
-
* what it falls back to when no image exists, and is much better than the
|
|
573
|
-
* empty grey box a brand row would otherwise render, since brand rows carry
|
|
574
|
-
* no `color` and no `icon` either.
|
|
575
|
-
*/
|
|
576
|
-
if (typeof existing === "string" && existing.trim().length > 0 && !isSeedPlaceholderImage(existing)) {
|
|
577
|
-
return row;
|
|
578
|
-
}
|
|
579
|
-
const generated = row.id ? GENERATED_CATEGORY_IMAGES[row.id] : undefined;
|
|
580
|
-
if (!generated)
|
|
581
|
-
return row;
|
|
582
|
-
return { ...row, display: { ...(row.display ?? {}), coverImage: generated } };
|
|
583
|
-
}
|
|
584
|
-
/**
|
|
585
|
-
* Attach the REGIONAL-VARIANT logo to a row that names a Hasbro equivalent.
|
|
586
|
-
*
|
|
587
|
-
* 🛑 Gated on `hasbroName`, not merely on the map having an entry. The image is
|
|
588
|
-
* rendered beside the "Also known as" line, so without the name there is
|
|
589
|
-
* nothing to label it and a bare second logo reads as a duplicate of the
|
|
590
|
-
* cover. The name is the fact; the image only illustrates it.
|
|
591
|
-
*
|
|
592
|
-
* Same hand-authored-wins rule as the cover, for the same reason: the admin
|
|
593
|
-
* editor can set `hasbroCoverImage` on a row the generator knows nothing
|
|
594
|
-
* about, and a reseed must not take that away.
|
|
595
|
-
*/
|
|
596
|
-
function withGeneratedHasbroCover(row) {
|
|
597
|
-
if (!row.hasbroName)
|
|
598
|
-
return row;
|
|
599
|
-
const existing = row.hasbroCoverImage;
|
|
600
|
-
if (typeof existing === "string" && existing.trim().length > 0)
|
|
601
|
-
return row;
|
|
602
|
-
const generated = row.id ? GENERATED_CATEGORY_HASBRO_IMAGES[row.id] : undefined;
|
|
603
|
-
return generated ? { ...row, hasbroCoverImage: generated } : row;
|
|
604
|
-
}
|
|
605
|
-
const allCategoryRows = [
|
|
606
|
-
...rawCategories.map((c) => ({ ancestors: [], ...c, createdByType: "admin" })),
|
|
607
|
-
...sublistingRows.map((s) => ({ ancestors: [], ...s, createdByType: "admin" })),
|
|
608
|
-
...brandRows.map((b) => ({ ancestors: [], ...b, createdByType: "admin" })),
|
|
609
|
-
...franchiseRows.map((f) => ({ ancestors: [], ...f, createdByType: "admin" })),
|
|
610
|
-
...bundleRows.map((b) => ({ ancestors: [], ...withBundleMosaic(b) })),
|
|
611
|
-
]
|
|
612
|
-
.map(withGeneratedContent)
|
|
613
|
-
.map(withGeneratedCoverImage)
|
|
614
|
-
.map(withGeneratedHasbroCover)
|
|
615
|
-
.map(withCategorySearchTxt);
|
|
616
|
-
/*
|
|
617
|
-
* ── Assertions, at MODULE LOAD, so a silent drop is loud ─────────────────────
|
|
618
|
-
*
|
|
619
|
-
* 🛑 These exist because of a specific failure shape this file is exposed to:
|
|
620
|
-
* `appkit-seed status` counts documents BY ID, so it cannot see content drift
|
|
621
|
-
* at all, and `load` is a `set(…, {merge:true})` that never removes a field.
|
|
622
|
-
* A generated map that silently emptied — `MODEL_LEAVES` keyed on a line id
|
|
623
|
-
* the forest renamed, say — would produce a tree that builds, typechecks,
|
|
624
|
-
* seeds without error, reports perfectly in sync, and is missing 216 pages.
|
|
625
|
-
*
|
|
626
|
-
* Throwing at import is the right severity: every consumer of this module
|
|
627
|
-
* (the seed CLI, the tester fixtures, the audits) wants to fail before writing
|
|
628
|
-
* rather than after.
|
|
629
|
-
*/
|
|
630
|
-
/*
|
|
631
|
-
* 🛑 TIER 4 IS NOW TWO GENERATED FAMILIES, not one.
|
|
632
|
-
*
|
|
633
|
-
* This assertion compared the tier-4 count against `MODEL_LEAF_COUNT` alone,
|
|
634
|
-
* and it CAUGHT the part leaves the moment they landed — 968 against an
|
|
635
|
-
* expected 216 — which is precisely the job it was written for. Widening it to
|
|
636
|
-
* the sum is the fix; deleting it or loosening it to `>=` would retire the one
|
|
637
|
-
* check that can see a generated map silently empty.
|
|
638
|
-
*
|
|
639
|
-
* Kept as an exact equality for the same reason: a `>=` would pass with
|
|
640
|
-
* `PART_LEAVES` entirely gone.
|
|
641
|
-
*/
|
|
642
|
-
/*
|
|
643
|
-
* 🛑 DISTINCT IDS. This assertion did not exist, and its absence is what let 20
|
|
644
|
-
* part leaves overwrite 20 model leaves in production.
|
|
645
|
-
*
|
|
646
|
-
* The whole failure was silent at every layer: the forest walked 1,138 nodes
|
|
647
|
-
* with only 1,118 distinct ids, `appkit-seed load` is a `set(…, {merge:true})`
|
|
648
|
-
* so the second row with an id quietly replaced the first, and the CLI reported
|
|
649
|
-
* `created 1173, errors 0` while Firestore held 1,153 documents. A buyer opening
|
|
650
|
-
* `/categories/category-galeon` got the attack ring instead of the bey.
|
|
651
|
-
*
|
|
652
|
-
* Counting rows is not counting documents, and this is the one check that can
|
|
653
|
-
* tell the difference BEFORE anything is written.
|
|
654
|
-
*/
|
|
655
|
-
/*
|
|
656
|
-
* 🛑 THE GENERATED BODIES ACTUALLY LANDED ON ROWS.
|
|
657
|
-
*
|
|
658
|
-
* I7's corpus is a `Record<id, html>` keyed on category ids, and a key that no
|
|
659
|
-
* longer matches a row is invisible everywhere: the map still exports 745
|
|
660
|
-
* entries, the seed still loads 1,173 rows, `appkit-seed status` still reports
|
|
661
|
-
* in sync, and every one of those category pages renders without a body. That
|
|
662
|
-
* is the same failure shape as the 20 ids that overwrote each other below —
|
|
663
|
-
* counting the SOURCE is not counting what reached a row.
|
|
664
|
-
*
|
|
665
|
-
* Tolerance rather than exact equality, for one real reason: a hand-authored
|
|
666
|
-
* body legitimately wins over a generated one, so the number of rows carrying
|
|
667
|
-
* a GENERATED body is `COUNT` minus however many nodes have been written by
|
|
668
|
-
* hand since. A floor of 90% catches a key-space break (which drops it to ~0)
|
|
669
|
-
* without failing on authored prose being added.
|
|
670
|
-
*/
|
|
671
|
-
const generatedApplied = allCategoryRows.filter((c) => typeof c.id === "string" && GENERATED_CATEGORY_CONTENT[c.id] === c.contentBody).length;
|
|
672
|
-
if (generatedApplied < Math.floor(GENERATED_CATEGORY_CONTENT_COUNT * 0.9)) {
|
|
673
|
-
throw new Error(`category seed: only ${generatedApplied} of ${GENERATED_CATEGORY_CONTENT_COUNT} generated ` +
|
|
674
|
-
`content bodies matched a row id. The corpus is keyed on category ids, so this means ` +
|
|
675
|
-
`the key space has drifted — re-run scripts/gen-category-content.mjs against the ` +
|
|
676
|
-
`current forest. Nothing would error at load time: the pages would simply have no body.`);
|
|
677
|
-
}
|
|
678
|
-
/*
|
|
679
|
-
* The same assertion for the cover images, and the same reasoning: the map is
|
|
680
|
-
* keyed on category ids, so a key that stops matching a row is invisible —
|
|
681
|
-
* the map still exports its count, the seed still loads every row, and the
|
|
682
|
-
* pages simply go back to having no image.
|
|
683
|
-
*
|
|
684
|
-
* The floor is 90% for the one legitimate reason a row can be missing: a
|
|
685
|
-
* hand-authored `display.coverImage` wins, so the number of rows carrying a
|
|
686
|
-
* GENERATED path is `COUNT` minus however many have been art-directed by hand.
|
|
687
|
-
*/
|
|
688
|
-
const generatedImagesApplied = allCategoryRows.filter((c) => typeof c.id === "string" && GENERATED_CATEGORY_IMAGES[c.id] === c.display?.coverImage).length;
|
|
689
|
-
if (generatedImagesApplied < Math.floor(GENERATED_CATEGORY_IMAGE_COUNT * 0.9)) {
|
|
690
|
-
throw new Error(`category seed: only ${generatedImagesApplied} of ${GENERATED_CATEGORY_IMAGE_COUNT} generated ` +
|
|
691
|
-
`cover images matched a row id — the key space has drifted. Re-run ` +
|
|
692
|
-
`scripts/fetch-taxonomy-images.mjs against the current forest. Nothing would error ` +
|
|
693
|
-
`at load time: the category grids would just go back to blank cards.`);
|
|
694
|
-
}
|
|
695
|
-
/*
|
|
696
|
-
* The regional-variant logos. This catches ORPHANED logos, and deliberately
|
|
697
|
-
* only those.
|
|
698
|
-
*
|
|
699
|
-
* 🛑 The two directions are NOT symmetric, which a first draft of this comment
|
|
700
|
-
* got wrong until a negative control was actually run against it:
|
|
701
|
-
*
|
|
702
|
-
* - **A map entry whose row does not name a Hasbro wave is an ERROR.**
|
|
703
|
-
* `withGeneratedHasbroCover` gates on `hasbroName`, so that image can
|
|
704
|
-
* never render — it was fetched, uploaded to Storage and mapped, and is
|
|
705
|
-
* now a stored object nothing will ever serve. Silent waste. Throw.
|
|
706
|
-
*
|
|
707
|
-
* - **A named wave with no logo is FINE.** The "Also known as" block renders
|
|
708
|
-
* the name and omits the image (`CategoryDetailPageView` makes the picture
|
|
709
|
-
* conditional), which is a correct, intentional-looking page. Setting a
|
|
710
|
-
* name we can evidence while having no logo we can source is exactly the
|
|
711
|
-
* honest-gap rule the rest of this work follows, so failing the seed for
|
|
712
|
-
* it would punish the right behaviour.
|
|
713
|
-
*
|
|
714
|
-
* Hence `covered !== keys` and NOT `covered !== named`. Exact equality rather
|
|
715
|
-
* than a 90% floor because there are five, hand-set against five confirmed
|
|
716
|
-
* pairings: "most of them" is not a meaningful tolerance at that size.
|
|
717
|
-
*/
|
|
718
|
-
const hasbroNamed = allCategoryRows.filter((c) => typeof c.hasbroName === "string" && c.hasbroName);
|
|
719
|
-
const hasbroCovered = hasbroNamed.filter((c) => typeof c.id === "string" && GENERATED_CATEGORY_HASBRO_IMAGES[c.id] === c.hasbroCoverImage);
|
|
720
|
-
const hasbroMapKeys = Object.keys(GENERATED_CATEGORY_HASBRO_IMAGES);
|
|
721
|
-
if (hasbroCovered.length !== hasbroMapKeys.length) {
|
|
722
|
-
const namedIds = new Set(hasbroNamed.map((c) => c.id));
|
|
723
|
-
const orphanLogos = hasbroMapKeys.filter((k) => !namedIds.has(k));
|
|
724
|
-
throw new Error(`category seed: ${hasbroCovered.length} of ${hasbroMapKeys.length} regional-variant logos ` +
|
|
725
|
-
`reached a row that names one. ` +
|
|
726
|
-
(orphanLogos.length > 0
|
|
727
|
-
? `These have a logo but no hasbroName: ${orphanLogos.join(", ")}. `
|
|
728
|
-
: "") +
|
|
729
|
-
`Either set hasbroName in category-forest.ts, or drop the entry from the manifest and ` +
|
|
730
|
-
`re-run scripts/fetch-category-images.mjs. Nothing would error at load time — the ` +
|
|
731
|
-
`"Also known as" block would just render without its logo.`);
|
|
732
|
-
}
|
|
733
|
-
const allIds = allCategoryRows.map((c) => c.id).filter((x) => typeof x === "string");
|
|
734
|
-
const duplicateIds = [...new Set(allIds.filter((id, i) => allIds.indexOf(id) !== i))];
|
|
735
|
-
if (duplicateIds.length > 0) {
|
|
736
|
-
throw new Error(`category seed: ${duplicateIds.length} duplicate id(s) — a merge write would ` +
|
|
737
|
-
`silently overwrite, so the load would report success and lose rows. ` +
|
|
738
|
-
`First few: ${duplicateIds.slice(0, 8).join(", ")}`);
|
|
739
|
-
}
|
|
740
|
-
if (allIds.length !== allCategoryRows.length) {
|
|
741
|
-
throw new Error(`category seed: ${allCategoryRows.length - allIds.length} row(s) have no string id`);
|
|
742
|
-
}
|
|
743
|
-
const tier4Rows = rawCategories.filter((c) => c.tier === 4).length;
|
|
744
|
-
const expectedTier4 = MODEL_LEAF_COUNT + PART_LEAF_COUNT;
|
|
745
|
-
if (tier4Rows !== expectedTier4) {
|
|
746
|
-
/*
|
|
747
|
-
* 🛑 NAME THE DROPPED LEAVES. A count told us 2,626 of 2,711 arrived and
|
|
748
|
-
* nothing more, which is unactionable: the ids are all distinct and every
|
|
749
|
-
* parent key exists in the forest, so the difference is in WHICH parent each
|
|
750
|
-
* leaf hangs off and at what depth — and that is only visible per leaf.
|
|
751
|
-
*
|
|
752
|
-
* The expensive version of this was a session spent inferring the cause from
|
|
753
|
-
* indentation in the forest literal. The cheap version is the set
|
|
754
|
-
* difference, grouped by the parent that owns each missing leaf, which also
|
|
755
|
-
* says the tier it actually landed at. One run names the fix.
|
|
756
|
-
*/
|
|
757
|
-
const reached = new Set(rawCategories.filter((c) => c.tier === 4).map((c) => String(c.id)));
|
|
758
|
-
const tierOf = new Map(rawCategories.map((c) => [String(c.id), c.tier]));
|
|
759
|
-
const declared = [];
|
|
760
|
-
for (const [parent, leaves] of Object.entries(MODEL_LEAVES)) {
|
|
761
|
-
for (const leaf of leaves)
|
|
762
|
-
declared.push({ id: leaf.id, parent });
|
|
763
|
-
}
|
|
764
|
-
for (const [parent, leaves] of Object.entries(PART_LEAVES)) {
|
|
765
|
-
for (const leaf of leaves)
|
|
766
|
-
declared.push({ id: leaf.id, parent });
|
|
767
|
-
}
|
|
768
|
-
const missing = declared.filter((d) => !reached.has(d.id));
|
|
769
|
-
const byParent = new Map();
|
|
770
|
-
for (const m of missing) {
|
|
771
|
-
if (!byParent.has(m.parent)) {
|
|
772
|
-
byParent.set(m.parent, { n: 0, landedAt: new Set(), sample: [] });
|
|
773
|
-
}
|
|
774
|
-
const g = byParent.get(m.parent);
|
|
775
|
-
g.n += 1;
|
|
776
|
-
g.landedAt.add(tierOf.has(m.id) ? `tier ${tierOf.get(m.id)}` : "absent");
|
|
777
|
-
if (g.sample.length < 3)
|
|
778
|
-
g.sample.push(m.id);
|
|
779
|
-
}
|
|
780
|
-
const detail = [...byParent.entries()]
|
|
781
|
-
.sort((a, b) => b[1].n - a[1].n)
|
|
782
|
-
.map(([parent, g]) => ` ${parent}: ${g.n} leaf/leaves landed at ${[...g.landedAt].join("+")} ` +
|
|
783
|
-
`(e.g. ${g.sample.join(", ")})`)
|
|
784
|
-
.join("\n");
|
|
785
|
-
throw new Error(`category seed: ${tier4Rows} tier-4 leaves reached the tree but the generated ` +
|
|
786
|
-
`maps declare ${expectedTier4} (${MODEL_LEAF_COUNT} models + ${PART_LEAF_COUNT} parts).\n` +
|
|
787
|
-
` ${missing.length} declared leaf/leaves did not arrive as tier 4, by parent:\n` +
|
|
788
|
-
`${detail}\n` +
|
|
789
|
-
` A parent at the wrong DEPTH puts its leaves at the wrong tier (a leaf is ` +
|
|
790
|
-
`always parentDepth+1); a parent id the forest renamed makes them absent ` +
|
|
791
|
-
`entirely. Fix the forest, then re-run node scripts/gen-category-models.mjs ` +
|
|
792
|
-
`and node scripts/gen-category-parts.mjs.`);
|
|
793
|
-
}
|
|
794
|
-
if (brandRows.length !== BRAND_ROW_COUNT) {
|
|
795
|
-
throw new Error(`category seed: ${brandRows.length} brand rows built, expected ${BRAND_ROW_COUNT}`);
|
|
796
|
-
}
|
|
797
|
-
if (franchiseRows.length !== FRANCHISE_ROW_COUNT) {
|
|
798
|
-
throw new Error(`category seed: ${franchiseRows.length} franchise rows built, expected ${FRANCHISE_ROW_COUNT}`);
|
|
799
|
-
}
|
|
800
|
-
{
|
|
801
|
-
/*
|
|
802
|
-
* A price band keyed on a leaf that does not exist is dead weight that READS
|
|
803
|
-
* AS COVERAGE — the hint renders nowhere and nothing says so. Cheap to
|
|
804
|
-
* check, and it is the most likely thing to rot as the tree is edited.
|
|
805
|
-
*/
|
|
806
|
-
const ids = new Set(rawCategories.map((c) => c.id));
|
|
807
|
-
const orphans = MARKET_BAND_LEAF_IDS.filter((id) => !ids.has(id));
|
|
808
|
-
if (orphans.length) {
|
|
809
|
-
throw new Error(`category seed: ${orphans.length} market band(s) key a non-existent ` +
|
|
810
|
-
`leaf: ${orphans.join(", ")}`);
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
export const categoriesSeedData = allCategoryRows;
|
|
814
|
-
// P-1 default seed: identical to categoriesSeedData (bundle rows included — P-17
|
|
815
|
-
// re-added them 2026-08-15 after the earlier removal referenced below).
|
|
816
|
-
export const categoriesP1SeedData = allCategoryRows;
|