@mohasinac/appkit 4.41.0 → 4.41.2

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 (169) hide show
  1. package/dist/features/seller/schemas/coupon-form.d.ts +37 -37
  2. package/dist/features/seller/schemas/coupon-form.js +36 -8
  3. package/dist/features/shell/SectionForm.js +44 -5
  4. package/dist/schemas/registry.d.ts +10 -10
  5. package/package.json +2 -2
  6. package/dist/_internal/server/features/classified/actions.d.ts +0 -12
  7. package/dist/_internal/server/features/classified/actions.js +0 -33
  8. package/dist/_internal/server/features/digital-code/actions.d.ts +0 -12
  9. package/dist/_internal/server/features/digital-code/actions.js +0 -33
  10. package/dist/_internal/server/features/homepage/data.d.ts +0 -18
  11. package/dist/_internal/server/features/homepage/data.js +0 -70
  12. package/dist/_internal/server/features/live/actions.d.ts +0 -6
  13. package/dist/_internal/server/features/live/actions.js +0 -27
  14. package/dist/_internal/server/features/messages/actions.d.ts +0 -6
  15. package/dist/_internal/server/features/messages/actions.js +0 -6
  16. package/dist/_internal/server/features/messages/data.d.ts +0 -10
  17. package/dist/_internal/server/features/messages/data.js +0 -24
  18. package/dist/_internal/server/features/messages/index.d.ts +0 -2
  19. package/dist/_internal/server/features/messages/index.js +0 -2
  20. package/dist/_internal/server/jobs/core/testerSandboxRefresh.d.ts +0 -12
  21. package/dist/_internal/server/jobs/core/testerSandboxRefresh.js +0 -112
  22. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.d.ts +0 -2
  23. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.js +0 -2
  24. package/dist/_internal/shared/features/homepage/section-gate.d.ts +0 -14
  25. package/dist/_internal/shared/features/homepage/section-gate.js +0 -51
  26. package/dist/_internal/shared/features/lottery/config-form.d.ts +0 -58
  27. package/dist/_internal/shared/features/lottery/config-form.js +0 -71
  28. package/dist/client/api/useFeatureFlags.d.ts +0 -22
  29. package/dist/client/api/useFeatureFlags.js +0 -27
  30. package/dist/features/account/address-validation.d.ts +0 -20
  31. package/dist/features/account/address-validation.js +0 -35
  32. package/dist/features/account/components/ChatList.d.ts +0 -14
  33. package/dist/features/account/components/ChatList.js +0 -9
  34. package/dist/features/account/components/ChatWindow.d.ts +0 -17
  35. package/dist/features/account/components/ChatWindow.js +0 -6
  36. package/dist/features/account/components/MessagesView.d.ts +0 -16
  37. package/dist/features/account/components/MessagesView.js +0 -5
  38. package/dist/features/account/components/UserOffersView.d.ts +0 -10
  39. package/dist/features/account/components/UserOffersView.js +0 -5
  40. package/dist/features/account/constants/addresses.d.ts +0 -13
  41. package/dist/features/account/constants/addresses.js +0 -43
  42. package/dist/features/account/hooks/useAddressForm.d.ts +0 -24
  43. package/dist/features/account/hooks/useAddressForm.js +0 -58
  44. package/dist/features/admin/actions/chat-actions.d.ts +0 -29
  45. package/dist/features/admin/actions/chat-actions.js +0 -101
  46. package/dist/features/admin/components/AdminFeatureFlagsView.d.ts +0 -9
  47. package/dist/features/admin/components/AdminFeatureFlagsView.js +0 -109
  48. package/dist/features/admin/components/AdminSiteView.d.ts +0 -10
  49. package/dist/features/admin/components/AdminSiteView.js +0 -52
  50. package/dist/features/admin/hooks/useChat.d.ts +0 -24
  51. package/dist/features/admin/hooks/useChat.js +0 -100
  52. package/dist/features/admin/permission-map.d.ts +0 -3
  53. package/dist/features/admin/permission-map.js +0 -26
  54. package/dist/features/admin/repository/chat.repository.d.ts +0 -50
  55. package/dist/features/admin/repository/chat.repository.js +0 -223
  56. package/dist/features/auth/actions/password-change-otp-actions.d.ts +0 -18
  57. package/dist/features/auth/actions/password-change-otp-actions.js +0 -88
  58. package/dist/features/auth/actions/realtime-token-actions.d.ts +0 -11
  59. package/dist/features/auth/actions/realtime-token-actions.js +0 -38
  60. package/dist/features/auth/password-change-otp.d.ts +0 -34
  61. package/dist/features/auth/password-change-otp.js +0 -56
  62. package/dist/features/classified/components/ClassifiedContactSellerPanel.d.ts +0 -12
  63. package/dist/features/classified/components/ClassifiedContactSellerPanel.js +0 -49
  64. package/dist/features/events/api/route.d.ts +0 -12
  65. package/dist/features/events/api/route.js +0 -90
  66. package/dist/features/faq/api/route.d.ts +0 -11
  67. package/dist/features/faq/api/route.js +0 -52
  68. package/dist/features/faq/hooks/useFAQs.d.ts +0 -22
  69. package/dist/features/faq/hooks/useFAQs.js +0 -41
  70. package/dist/features/media/upload/video-poster.test.d.ts +0 -1
  71. package/dist/features/media/upload/video-poster.test.js +0 -42
  72. package/dist/features/messages/actions/index.d.ts +0 -2
  73. package/dist/features/messages/actions/index.js +0 -2
  74. package/dist/features/messages/actions/messages-actions.d.ts +0 -15
  75. package/dist/features/messages/actions/messages-actions.js +0 -33
  76. package/dist/features/messages/actions/ping-rtdb.d.ts +0 -2
  77. package/dist/features/messages/actions/ping-rtdb.js +0 -26
  78. package/dist/features/messages/hooks/useConversation.d.ts +0 -22
  79. package/dist/features/messages/hooks/useConversation.js +0 -155
  80. package/dist/features/messages/hooks/useConversations.d.ts +0 -15
  81. package/dist/features/messages/hooks/useConversations.js +0 -104
  82. package/dist/features/messages/index.d.ts +0 -6
  83. package/dist/features/messages/index.js +0 -4
  84. package/dist/features/messages/realtime.d.ts +0 -32
  85. package/dist/features/messages/realtime.js +0 -39
  86. package/dist/features/messages/repository/conversations.repository.d.ts +0 -71
  87. package/dist/features/messages/repository/conversations.repository.js +0 -284
  88. package/dist/features/messages/schemas/firestore.d.ts +0 -34
  89. package/dist/features/messages/schemas/firestore.js +0 -16
  90. package/dist/features/messages/schemas/index.d.ts +0 -177
  91. package/dist/features/messages/schemas/index.js +0 -35
  92. package/dist/features/messages/server.d.ts +0 -6
  93. package/dist/features/messages/server.js +0 -6
  94. package/dist/features/products/components/MakeOfferForm.d.ts +0 -20
  95. package/dist/features/products/components/MakeOfferForm.js +0 -9
  96. package/dist/features/reviews/api/route.d.ts +0 -21
  97. package/dist/features/reviews/api/route.js +0 -174
  98. package/dist/features/seller/api/offers/route.d.ts +0 -17
  99. package/dist/features/seller/api/offers/route.js +0 -63
  100. package/dist/features/seller/components/SellerAuctionsView.d.ts +0 -9
  101. package/dist/features/seller/components/SellerAuctionsView.js +0 -113
  102. package/dist/features/seller/components/SellerOffersPanel.d.ts +0 -21
  103. package/dist/features/seller/components/SellerOffersPanel.js +0 -160
  104. package/dist/features/seller/components/SellerPreOrdersView.d.ts +0 -6
  105. package/dist/features/seller/components/SellerPreOrdersView.js +0 -148
  106. package/dist/features/seller/components/SellerPrizeDrawsView.d.ts +0 -6
  107. package/dist/features/seller/components/SellerPrizeDrawsView.js +0 -152
  108. package/dist/features/seller/permission-map.d.ts +0 -5
  109. package/dist/features/seller/permission-map.js +0 -17
  110. package/dist/features/shell/StepForm.d.ts +0 -80
  111. package/dist/features/shell/StepForm.js +0 -136
  112. package/dist/features/stores/api/route.d.ts +0 -12
  113. package/dist/features/stores/api/route.js +0 -110
  114. package/dist/features/tester/seed-data/authored/account-auth__testing-program--admin.d.ts +0 -2
  115. package/dist/features/tester/seed-data/authored/account-auth__testing-program--admin.js +0 -33
  116. package/dist/features/tester/seed-data/authored/account-auth__testing-program.d.ts +0 -2
  117. package/dist/features/tester/seed-data/authored/account-auth__testing-program.js +0 -138
  118. package/dist/features/tester/seed-data/bids-tester-seed-data.d.ts +0 -2
  119. package/dist/features/tester/seed-data/bids-tester-seed-data.js +0 -37
  120. package/dist/features/tester/seed-data/blog-tester-seed-data.d.ts +0 -2
  121. package/dist/features/tester/seed-data/blog-tester-seed-data.js +0 -40
  122. package/dist/features/tester/seed-data/carts-tester-seed-data.d.ts +0 -3
  123. package/dist/features/tester/seed-data/carts-tester-seed-data.js +0 -70
  124. package/dist/features/tester/seed-data/categories-tester-seed-data.d.ts +0 -2
  125. package/dist/features/tester/seed-data/categories-tester-seed-data.js +0 -218
  126. package/dist/features/tester/seed-data/coupons-tester-seed-data.d.ts +0 -2
  127. package/dist/features/tester/seed-data/coupons-tester-seed-data.js +0 -76
  128. package/dist/features/tester/seed-data/events-tester-seed-data.d.ts +0 -2
  129. package/dist/features/tester/seed-data/events-tester-seed-data.js +0 -193
  130. package/dist/features/tester/seed-data/offers-tester-seed-data.d.ts +0 -2
  131. package/dist/features/tester/seed-data/offers-tester-seed-data.js +0 -191
  132. package/dist/features/tester/seed-data/orders-tester-seed-data.d.ts +0 -2
  133. package/dist/features/tester/seed-data/orders-tester-seed-data.js +0 -329
  134. package/dist/features/tester/seed-data/products-tester-seed-data.d.ts +0 -2
  135. package/dist/features/tester/seed-data/products-tester-seed-data.js +0 -815
  136. package/dist/features/tester/seed-data/stores-tester-seed-data.d.ts +0 -2
  137. package/dist/features/tester/seed-data/stores-tester-seed-data.js +0 -79
  138. package/dist/features/wishlist/components/WishlistToggleButton.d.ts +0 -12
  139. package/dist/features/wishlist/components/WishlistToggleButton.js +0 -36
  140. package/dist/monitoring/analytics.d.ts +0 -80
  141. package/dist/monitoring/analytics.js +0 -176
  142. package/dist/next/middleware/pii-redactor.d.ts +0 -32
  143. package/dist/next/middleware/pii-redactor.js +0 -84
  144. package/dist/providers/db-firebase/realtime.d.ts +0 -42
  145. package/dist/providers/db-firebase/realtime.js +0 -138
  146. package/dist/security/rbac/Can.d.ts +0 -31
  147. package/dist/security/rbac/Can.js +0 -22
  148. package/dist/security/rbac/default-roles.d.ts +0 -2
  149. package/dist/security/rbac/default-roles.js +0 -83
  150. package/dist/security/rbac/hook.d.ts +0 -20
  151. package/dist/security/rbac/hook.js +0 -30
  152. package/dist/security/rbac/index.d.ts +0 -8
  153. package/dist/security/rbac/index.js +0 -6
  154. package/dist/security/rbac/middleware.d.ts +0 -51
  155. package/dist/security/rbac/middleware.js +0 -51
  156. package/dist/security/rbac/resolver.d.ts +0 -5
  157. package/dist/security/rbac/resolver.js +0 -38
  158. package/dist/security/rbac/server.d.ts +0 -24
  159. package/dist/security/rbac/server.js +0 -42
  160. package/dist/security/rbac/types.d.ts +0 -22
  161. package/dist/security/rbac/types.js +0 -2
  162. package/dist/seed/conversations-seed-data.d.ts +0 -2
  163. package/dist/seed/conversations-seed-data.js +0 -292
  164. package/dist/utils/filter.helper.d.ts +0 -18
  165. package/dist/utils/filter.helper.js +0 -21
  166. package/dist/utils/pagination.helper.d.ts +0 -23
  167. package/dist/utils/pagination.helper.js +0 -24
  168. package/dist/utils/search-tokens.d.ts +0 -2
  169. package/dist/utils/search-tokens.js +0 -21
@@ -1,70 +0,0 @@
1
- import { cache } from "react";
2
- import { homepageSectionsRepository, carouselRepository, productRepository, reviewRepository, blogRepository, eventRepository, categoriesRepository, siteSettingsRepository, } from "../../../../repositories";
3
- import { HOMEPAGE_FEATURED_REVIEWS_LIMIT, HOMEPAGE_RECENT_BLOG_POSTS_LIMIT, } from "../../../shared/features/homepage/config";
4
- /** Section types that require a feature flag to be enabled to show on the homepage. */
5
- const SECTION_FEATURE_GATE = {
6
- auctions: "auctions",
7
- "pre-orders": "preOrders",
8
- events: "events",
9
- "event-raffles": "events",
10
- "prize-draws": "events",
11
- "blog-articles": "blog",
12
- };
13
- export const getHomepageSections = cache(async () => {
14
- const [sections, settings] = await Promise.all([
15
- homepageSectionsRepository.getEnabledSections().catch(() => []),
16
- siteSettingsRepository.getSingleton().catch(() => null),
17
- ]);
18
- const flags = settings?.featureFlags;
19
- if (!flags)
20
- return sections;
21
- return sections.filter((s) => {
22
- const flagKey = SECTION_FEATURE_GATE[s.type];
23
- if (!flagKey)
24
- return true;
25
- return flags[flagKey] !== false;
26
- });
27
- });
28
- export const getHeroCarouselSlides = cache(async () => {
29
- return carouselRepository.getActiveSlides().catch(() => []);
30
- });
31
- /**
32
- * One parallel fetch returning every piece of data the homepage sections need.
33
- * Page components call this once; React.cache() deduplicates per request.
34
- */
35
- export const getHomepageInitial = cache(async () => {
36
- const [rawSections, settings, carouselSlides, featuredProducts, activeAuctions, activePreOrders, activeBrands, featuredReviews, recentBlogPosts, activeEvents, featuredCategories,] = await Promise.all([
37
- homepageSectionsRepository.getEnabledSections().catch(() => []),
38
- siteSettingsRepository.getSingleton().catch(() => null),
39
- carouselRepository.getActiveSlides().catch(() => []),
40
- productRepository.findFeatured().catch(() => []),
41
- productRepository.findActiveAuctions().catch(() => []),
42
- productRepository.findActivePreOrders().catch(() => []),
43
- categoriesRepository.findActiveBrands().catch(() => []),
44
- reviewRepository.findFeatured(HOMEPAGE_FEATURED_REVIEWS_LIMIT).catch(() => []),
45
- blogRepository.listPublished({}, { page: 1, pageSize: HOMEPAGE_RECENT_BLOG_POSTS_LIMIT, sorts: "-publishedAt" }).then((r) => r.items ?? []).catch(() => []),
46
- eventRepository.listActive().catch(() => []),
47
- categoriesRepository.getFeaturedCategories().catch(() => []),
48
- ]);
49
- const flags = settings?.featureFlags;
50
- const sections = flags
51
- ? rawSections.filter((s) => {
52
- const flagKey = SECTION_FEATURE_GATE[s.type];
53
- if (!flagKey)
54
- return true;
55
- return flags[flagKey] !== false;
56
- })
57
- : rawSections;
58
- return {
59
- sections,
60
- carouselSlides,
61
- featuredProducts,
62
- activeAuctions,
63
- activePreOrders,
64
- activeBrands,
65
- featuredReviews,
66
- recentBlogPosts,
67
- activeEvents,
68
- featuredCategories,
69
- };
70
- });
@@ -1,6 +0,0 @@
1
- import { type ActionResult } from "@mohasinac/appkit/server";
2
- import type { ConversationDocument } from "../../../../features/messages/schemas/firestore";
3
- export interface StartLiveConversationInput {
4
- productId: string;
5
- }
6
- export declare function startLiveConversationAction(input: StartLiveConversationInput): Promise<ActionResult<ConversationDocument>>;
@@ -1,27 +0,0 @@
1
- "use server";
2
- import { wrapAction } from "@mohasinac/appkit/server";
3
- import { conversationsRepository } from "../../../../features/messages/repository/conversations.repository";
4
- import { requireRoleUser } from "../../../../providers/auth-firebase/helpers";
5
- import { storeRepository, productRepository } from "../../../../repositories";
6
- export async function startLiveConversationAction(input) {
7
- return wrapAction(async () => {
8
- const user = await requireRoleUser(["user", "buyer", "seller", "admin"]);
9
- const product = await productRepository.findByIdOrSlug(input.productId);
10
- if (!product || product.listingType !== "live") {
11
- throw new Error("Product not found or not a live-item listing");
12
- }
13
- const store = await storeRepository.findById(product.storeId);
14
- if (!store) {
15
- throw new Error("Store not found");
16
- }
17
- return conversationsRepository.findOrCreateByContext({
18
- buyerId: user.uid,
19
- buyerDisplayName: user.name ?? user.email ?? user.uid,
20
- storeId: product.storeId,
21
- storeName: store.storeName,
22
- sellerDisplayName: store.storeName,
23
- productId: product.id,
24
- productTitle: product.title,
25
- });
26
- });
27
- }
@@ -1,6 +0,0 @@
1
- /**
2
- * Messages server actions — thin re-exports. Keeps RTDB ping-channel behaviour
3
- * (firestore canonical + RTDB notification) in the source action so consumers
4
- * cannot bypass it.
5
- */
6
- export { sendMessage as sendMessageAction, markConversationRead as markConversationReadAction, MESSAGE_MAX_LENGTH, } from "../../../../features/messages/actions/messages-actions";
@@ -1,6 +0,0 @@
1
- /**
2
- * Messages server actions — thin re-exports. Keeps RTDB ping-channel behaviour
3
- * (firestore canonical + RTDB notification) in the source action so consumers
4
- * cannot bypass it.
5
- */
6
- export { sendMessage as sendMessageAction, markConversationRead as markConversationReadAction, MESSAGE_MAX_LENGTH, } from "../../../../features/messages/actions/messages-actions";
@@ -1,10 +0,0 @@
1
- /**
2
- * Messages data layer — server-only fetchers with React.cache dedup.
3
- * RTDB ping channel is preserved (the actions module owns that side effect).
4
- */
5
- export interface MessagesDataOptions {
6
- _reserved?: never;
7
- }
8
- export declare const listConversationsForBuyer: (buyerId: string, _opts?: MessagesDataOptions) => Promise<import("../../../..").ConversationDocument[]>;
9
- export declare const listConversationsForStore: (storeId: string, _opts?: MessagesDataOptions) => Promise<import("../../../..").ConversationDocument[]>;
10
- export declare const getConversation: (convId: string, _opts?: MessagesDataOptions) => Promise<import("../../../..").ConversationDocument | null>;
@@ -1,24 +0,0 @@
1
- /**
2
- * Messages data layer — server-only fetchers with React.cache dedup.
3
- * RTDB ping channel is preserved (the actions module owns that side effect).
4
- */
5
- import { cache } from "react";
6
- import { listConversationsForBuyer as _listConversationsForBuyer, listConversationsForStore as _listConversationsForStore, getConversation as _getConversation, } from "../../../../features/messages/actions/messages-actions";
7
- export const listConversationsForBuyer = cache(async (buyerId, _opts) => {
8
- void _opts;
9
- if (!buyerId)
10
- return [];
11
- return _listConversationsForBuyer(buyerId);
12
- });
13
- export const listConversationsForStore = cache(async (storeId, _opts) => {
14
- void _opts;
15
- if (!storeId)
16
- return [];
17
- return _listConversationsForStore(storeId);
18
- });
19
- export const getConversation = cache(async (convId, _opts) => {
20
- void _opts;
21
- if (!convId)
22
- return null;
23
- return _getConversation(convId);
24
- });
@@ -1,2 +0,0 @@
1
- export * from "./data";
2
- export * from "./actions";
@@ -1,2 +0,0 @@
1
- export * from "./data";
2
- export * from "./actions";
@@ -1,12 +0,0 @@
1
- import type { JobContext } from "../runtime/types";
2
- /**
3
- * Reverts every live tester-sandbox fixture back to its canonical seeded
4
- * shape (undoing edits made through admin/seller forms) and prunes any
5
- * isTestData doc that isn't one of the known fixtures (something a tester
6
- * created during QA, e.g. a cloned product). Runs every 4 hours — distinct
7
- * from `runTesterSandboxCleanup`, which only deletes docs whose 7-day
8
- * `testDataExpiresAt` TTL has actually lapsed and never touches still-live
9
- * docs' fields. Scoped to the tester sandbox only (`isTestData: true`) —
10
- * the permanent Beyblade catalog real visitors browse is never touched.
11
- */
12
- export declare function runTesterSandboxRefresh(ctx: JobContext): Promise<void>;
@@ -1,112 +0,0 @@
1
- import { batchDelete, getTestDataRefs } from "../handlers/_helpers";
2
- import { BID_FIELDS } from "../../../../constants/field-names";
3
- import { encryptPiiFields } from "../../../../security/pii-encrypt";
4
- import { BID_PII_FIELDS, OFFER_PII_FIELDS, } from "../../../../security/pii-schemas";
5
- import { SANDBOX_COLLECTIONS } from "./testerSandboxCleanup";
6
- // Direct imports from each defining module (not the seed-data barrel) —
7
- // this is internal appkit code, not part of the package's public API surface.
8
- import { categoriesTesterSeedData } from "../../../../features/tester/seed-data/categories-tester-seed-data";
9
- import { storesTesterSeedData } from "../../../../features/tester/seed-data/stores-tester-seed-data";
10
- import { productsTesterSeedData } from "../../../../features/tester/seed-data/products-tester-seed-data";
11
- import { blogTesterSeedData } from "../../../../features/tester/seed-data/blog-tester-seed-data";
12
- import { eventsTesterSeedData } from "../../../../features/tester/seed-data/events-tester-seed-data";
13
- import { offersTesterSeedData } from "../../../../features/tester/seed-data/offers-tester-seed-data";
14
- import { bidsTesterSeedData } from "../../../../features/tester/seed-data/bids-tester-seed-data";
15
- /**
16
- * PII per sandbox collection — the same map `seed-cli.mjs` applies.
17
- *
18
- * This job wrote every document with a bare `batch.set(...)`, while its own
19
- * comment claimed to mirror seed-cli's loadGeneric. loadGeneric ENCRYPTS;
20
- * this did not. So the 4-hourly refresh re-seeded sandbox PII in plaintext and
21
- * quietly undid the backfill for those fixtures — caught by reading the raw
22
- * documents after a deploy, where `bids` had 37 encrypted rows and one
23
- * plaintext one that had not existed at backfill time.
24
- *
25
- * A scheduled job writing straight to Firestore bypasses `applyWriteHooks`
26
- * exactly like the hand-rolled repository overrides did. Same defect, one
27
- * level up.
28
- */
29
- const SANDBOX_PII = {
30
- offers: OFFER_PII_FIELDS,
31
- bids: BID_PII_FIELDS,
32
- products: ["sellerName", "sellerEmail"],
33
- };
34
- function encryptForSandbox(collection, data) {
35
- const fields = SANDBOX_PII[collection];
36
- return fields ? encryptPiiFields(data, [...fields]) : data;
37
- }
38
- const SEED_BY_COLLECTION = {
39
- categories: categoriesTesterSeedData,
40
- stores: storesTesterSeedData,
41
- products: productsTesterSeedData,
42
- blogPosts: blogTesterSeedData,
43
- events: eventsTesterSeedData,
44
- offers: offersTesterSeedData,
45
- };
46
- const BID_CHUNK_SIZE = 30; // Firestore `in` query cap
47
- /**
48
- * Reverts every live tester-sandbox fixture back to its canonical seeded
49
- * shape (undoing edits made through admin/seller forms) and prunes any
50
- * isTestData doc that isn't one of the known fixtures (something a tester
51
- * created during QA, e.g. a cloned product). Runs every 4 hours — distinct
52
- * from `runTesterSandboxCleanup`, which only deletes docs whose 7-day
53
- * `testDataExpiresAt` TTL has actually lapsed and never touches still-live
54
- * docs' fields. Scoped to the tester sandbox only (`isTestData: true`) —
55
- * the permanent Beyblade catalog real visitors browse is never touched.
56
- */
57
- export async function runTesterSandboxRefresh(ctx) {
58
- let reverted = 0;
59
- let deletedExtras = 0;
60
- for (const collection of SANDBOX_COLLECTIONS) {
61
- const seedDocs = SEED_BY_COLLECTION[collection] ?? [];
62
- const validIds = new Set(seedDocs.map((d) => d.id).filter((id) => Boolean(id)));
63
- if (seedDocs.length > 0) {
64
- const batch = ctx.db.batch();
65
- for (const doc of seedDocs) {
66
- const { id, ...data } = doc;
67
- if (!id)
68
- continue;
69
- // merge:true mirrors seed-cli.mjs's loadGeneric — the seed payload
70
- // carries every canonical field, so this fully overwrites anything
71
- // a tester edited through the UI.
72
- batch.set(ctx.db.collection(collection).doc(id), encryptForSandbox(collection, data), { merge: true });
73
- reverted++;
74
- }
75
- await batch.commit();
76
- }
77
- const testDataRefs = await getTestDataRefs(ctx.db, collection, null);
78
- const extraRefs = testDataRefs.filter((ref) => !validIds.has(ref.id));
79
- deletedExtras += await batchDelete(ctx, extraRefs);
80
- }
81
- // Bids aren't isTestData-flagged themselves — scoped by productId
82
- // referencing a sandbox product instead, mirroring runTesterSandboxCleanup's
83
- // cascade convention.
84
- const sandboxProductIds = (SEED_BY_COLLECTION.products ?? [])
85
- .map((d) => d.id)
86
- .filter((id) => Boolean(id));
87
- const validBidIds = new Set(bidsTesterSeedData.map((d) => d.id).filter((id) => Boolean(id)));
88
- if (bidsTesterSeedData.length > 0) {
89
- const bidBatch = ctx.db.batch();
90
- for (const bid of bidsTesterSeedData) {
91
- const { id, ...data } = bid;
92
- if (!id)
93
- continue;
94
- bidBatch.set(ctx.db.collection("bids").doc(id), encryptForSandbox("bids", data), { merge: true });
95
- reverted++;
96
- }
97
- await bidBatch.commit();
98
- }
99
- const extraBidRefs = [];
100
- for (let i = 0; i < sandboxProductIds.length; i += BID_CHUNK_SIZE) {
101
- const chunk = sandboxProductIds.slice(i, i + BID_CHUNK_SIZE);
102
- if (chunk.length === 0)
103
- continue;
104
- const snap = await ctx.db.collection("bids").where(BID_FIELDS.PRODUCT_ID, "in", chunk).get();
105
- for (const doc of snap.docs) {
106
- if (!validBidIds.has(doc.id))
107
- extraBidRefs.push(doc.ref);
108
- }
109
- }
110
- deletedExtras += await batchDelete(ctx, extraBidRefs);
111
- ctx.logger.info("Tester sandbox refresh complete", { reverted, deletedExtras });
112
- }
@@ -1,2 +0,0 @@
1
- import type { ScheduleHandler } from "../runtime/types";
2
- export declare const testerSandboxRefreshHandler: ScheduleHandler;
@@ -1,2 +0,0 @@
1
- import { runTesterSandboxRefresh } from "../core/testerSandboxRefresh";
2
- export const testerSandboxRefreshHandler = (ctx) => runTesterSandboxRefresh(ctx);
@@ -1,14 +0,0 @@
1
- import type { SectionType } from "../../../../features/homepage/schemas/firestore";
2
- import type { FeatureFlagKey } from "../../../../features/admin/schemas/firestore";
3
- /** Section types that require a feature flag to be enabled to show on the homepage. */
4
- export declare const SECTION_FEATURE_GATE: Partial<Record<SectionType, FeatureFlagKey>>;
5
- /**
6
- * Drop sections whose feature flag is off.
7
- *
8
- * A section type with no gate entry always passes, and a MISSING flag value
9
- * counts as enabled (`!== false`) — the flags map is partial, and a section
10
- * must not vanish just because an admin has never opened Site Settings.
11
- */
12
- export declare function filterSectionsByFeatureFlags<T extends {
13
- type: string;
14
- }>(sections: readonly T[], flags: Partial<Record<FeatureFlagKey, unknown>> | null | undefined): T[];
@@ -1,51 +0,0 @@
1
- /*
2
- * WHY: The only code in the repo that gated homepage sections on
3
- * `siteSettings.featureFlags` lived inside `getHomepageInitial`, a
4
- * function with ZERO call sites — the live homepage
5
- * (`MarketplaceHomepageView`) reads `homepageSectionsRepository`
6
- * directly. So turning off the `auctions` flag hid auctions everywhere
7
- * except the homepage, which kept advertising them.
8
- *
9
- * `getHomepageInitial` itself was deleted rather than wired up: its other
10
- * unique calls, `findActiveAuctions()` / `findActivePreOrders()`, are
11
- * unbounded `.get()` collection scans with no `.limit()`, which Rule #6
12
- * forbids. The gate is the only part worth keeping, so it moved here.
13
- *
14
- * EXPORTS: SECTION_FEATURE_GATE, filterSectionsByFeatureFlags
15
- *
16
- * @tag domain:homepage
17
- * @tag layer:shared
18
- * @tag pattern:none
19
- * @tag access:isomorphic
20
- * @tag consumers:MarketplaceHomepageView
21
- * @tag sideEffects:none
22
- */
23
- /** Section types that require a feature flag to be enabled to show on the homepage. */
24
- export const SECTION_FEATURE_GATE = {
25
- auctions: "auctions",
26
- "pre-orders": "preOrders",
27
- events: "events",
28
- "event-raffles": "events",
29
- "prize-draws": "events",
30
- "blog-articles": "blog",
31
- };
32
- /**
33
- * Drop sections whose feature flag is off.
34
- *
35
- * A section type with no gate entry always passes, and a MISSING flag value
36
- * counts as enabled (`!== false`) — the flags map is partial, and a section
37
- * must not vanish just because an admin has never opened Site Settings.
38
- */
39
- export function filterSectionsByFeatureFlags(sections,
40
- // Not `Partial<Record<FeatureFlagKey, boolean>>`: two of the flag keys
41
- // (`listingTypes`, `categoryTypes`) hold nested per-type objects rather than
42
- // booleans. Neither is a gate key, so the `=== false` test below simply
43
- // never fires for them.
44
- flags) {
45
- if (!flags)
46
- return [...sections];
47
- return sections.filter((section) => {
48
- const flagKey = SECTION_FEATURE_GATE[section.type];
49
- return !flagKey || flags[flagKey] !== false;
50
- });
51
- }
@@ -1,58 +0,0 @@
1
- import { z } from "zod";
2
- export declare const lotteryConfigFormSchema: z.ZodEffects<z.ZodObject<{
3
- pricingMode: z.ZodEnum<["uniform", "variable"]>;
4
- uniformPrice: z.ZodOptional<z.ZodNumber>;
5
- slots: z.ZodArray<z.ZodObject<{
6
- slotNumber: z.ZodNumber;
7
- name: z.ZodOptional<z.ZodString>;
8
- price: z.ZodOptional<z.ZodNumber>;
9
- image: z.ZodOptional<z.ZodString>;
10
- }, "strip", z.ZodTypeAny, {
11
- slotNumber: number;
12
- image?: string | undefined;
13
- name?: string | undefined;
14
- price?: number | undefined;
15
- }, {
16
- slotNumber: number;
17
- image?: string | undefined;
18
- name?: string | undefined;
19
- price?: number | undefined;
20
- }>, "many">;
21
- }, "strip", z.ZodTypeAny, {
22
- slots: {
23
- slotNumber: number;
24
- image?: string | undefined;
25
- name?: string | undefined;
26
- price?: number | undefined;
27
- }[];
28
- pricingMode: "uniform" | "variable";
29
- uniformPrice?: number | undefined;
30
- }, {
31
- slots: {
32
- slotNumber: number;
33
- image?: string | undefined;
34
- name?: string | undefined;
35
- price?: number | undefined;
36
- }[];
37
- pricingMode: "uniform" | "variable";
38
- uniformPrice?: number | undefined;
39
- }>, {
40
- slots: {
41
- slotNumber: number;
42
- image?: string | undefined;
43
- name?: string | undefined;
44
- price?: number | undefined;
45
- }[];
46
- pricingMode: "uniform" | "variable";
47
- uniformPrice?: number | undefined;
48
- }, {
49
- slots: {
50
- slotNumber: number;
51
- image?: string | undefined;
52
- name?: string | undefined;
53
- price?: number | undefined;
54
- }[];
55
- pricingMode: "uniform" | "variable";
56
- uniformPrice?: number | undefined;
57
- }>;
58
- export type LotteryConfigFormValues = z.infer<typeof lotteryConfigFormSchema>;
@@ -1,71 +0,0 @@
1
- /*
2
- * WHY: `LotteryAdminEditView` rendered `<Form>` with no schema. A lottery
3
- * config is priced money: `uniformPrice` was
4
- * `Math.round(parseFloat(x) * 100) / 100 || 0`, which turns any
5
- * unparseable input into **zero** rather than an error — so a typo in the
6
- * price box published a free lottery.
7
- *
8
- * `slots` is the other half: each row carries a `slotNumber`, a name and
9
- * a price, and a duplicate slot number makes two buyers think they own
10
- * the same slot.
11
- *
12
- * WHAT: The config contract.
13
- *
14
- * @tag domain:lottery
15
- * @tag layer:schema
16
- * @tag pattern:none
17
- * @tag access:isomorphic
18
- * @tag consumers:LotteryAdminEditView
19
- * @tag sideEffects:none
20
- */
21
- import { z } from "zod";
22
- import { annotate } from "../../../../features/shell/field-ui-meta";
23
- const slotSchema = z.object({
24
- slotNumber: z.coerce.number().int().min(1, "Slot numbers start at 1."),
25
- name: z.string().trim().max(120).optional(),
26
- price: z.coerce.number().min(0, "A slot price cannot be negative.").optional(),
27
- image: z.string().trim().optional(),
28
- });
29
- export const lotteryConfigFormSchema = z
30
- .object({
31
- pricingMode: annotate(z.enum(["uniform", "variable"]), {
32
- section: "pricing", sectionLabel: "Pricing", sectionRequired: true, quick: true, order: 1, row: "pair",
33
- }),
34
- /*
35
- * 🛑 Coerced with a real error, NOT `|| 0`. The previous
36
- * `parseFloat(x) || 0` silently turned "12,00" or "twelve" into a free
37
- * lottery — the failure was indistinguishable from deliberately pricing
38
- * it at zero.
39
- */
40
- uniformPrice: annotate(z.coerce
41
- .number({ invalid_type_error: "Enter the slot price as a number." })
42
- .min(0, "A price cannot be negative.")
43
- .max(10000000, "That looks like a typo.")
44
- .optional(), { section: "pricing", order: 2, row: "pair", kind: "number" }),
45
- slots: annotate(z.array(slotSchema).min(1, "A lottery needs at least one slot.").max(1000), {
46
- section: "slots", sectionLabel: "Slots", sectionRequired: true, order: 1, row: "full", kind: "list",
47
- }),
48
- })
49
- .superRefine((v, ctx) => {
50
- // Uniform pricing with no price is the free-lottery case the `|| 0` used
51
- // to create silently.
52
- if (v.pricingMode === "uniform" && (v.uniformPrice === undefined || v.uniformPrice <= 0)) {
53
- ctx.addIssue({
54
- code: z.ZodIssueCode.custom,
55
- path: ["uniformPrice"],
56
- message: "Set a slot price above zero, or switch to variable pricing.",
57
- });
58
- }
59
- // Two buyers cannot own the same slot number.
60
- const seen = new Set();
61
- for (const [i, s] of v.slots.entries()) {
62
- if (seen.has(s.slotNumber)) {
63
- ctx.addIssue({
64
- code: z.ZodIssueCode.custom,
65
- path: ["slots", i, "slotNumber"],
66
- message: `Slot ${s.slotNumber} appears more than once.`,
67
- });
68
- }
69
- seen.add(s.slotNumber);
70
- }
71
- });
@@ -1,22 +0,0 @@
1
- /**
2
- * Consumer app's flag names aren't known to appkit (they're project-specific,
3
- * e.g. "PAYOUTS" | "COUPONS" | ...) — this stays a loose string-keyed map so
4
- * appkit doesn't hardcode a consumer's roadmap. Cast the result where a
5
- * consumer wants a narrower, project-specific union.
6
- */
7
- export type FeatureFlagState = Record<string, boolean>;
8
- /**
9
- * Client-safe feature-flag check — `getFlag()`/`withFeatureGuard` are
10
- * server-only, so `"use client"` components have no way to know a flag is
11
- * off before firing a request against its (guarded) API. This hook fetches
12
- * the public flag snapshot once (cached for the query's lifetime) so a
13
- * component can skip the fetch and skip rendering entirely when disabled,
14
- * instead of eating a 404 from the guarded route.
15
- *
16
- * `flags` defaults to an empty object while loading, so callers should treat
17
- * a missing key as `false` (fail-closed) rather than assuming it's enabled.
18
- */
19
- export declare function useFeatureFlags(): {
20
- flags: FeatureFlagState;
21
- isLoading: boolean;
22
- };
@@ -1,27 +0,0 @@
1
- "use client";
2
- import { useApiQuery } from "./useApiQuery";
3
- import { apiClient } from "../../http";
4
- import { API_ENDPOINTS } from "../../constants/api-endpoints";
5
- /**
6
- * Client-safe feature-flag check — `getFlag()`/`withFeatureGuard` are
7
- * server-only, so `"use client"` components have no way to know a flag is
8
- * off before firing a request against its (guarded) API. This hook fetches
9
- * the public flag snapshot once (cached for the query's lifetime) so a
10
- * component can skip the fetch and skip rendering entirely when disabled,
11
- * instead of eating a 404 from the guarded route.
12
- *
13
- * `flags` defaults to an empty object while loading, so callers should treat
14
- * a missing key as `false` (fail-closed) rather than assuming it's enabled.
15
- */
16
- export function useFeatureFlags() {
17
- const query = useApiQuery({
18
- queryKey: ["feature-flags"],
19
- queryFn: () => apiClient.get(API_ENDPOINTS.CONFIG.FEATURE_FLAGS),
20
- staleTime: 60000,
21
- silentBackgroundFailures: true,
22
- });
23
- return {
24
- flags: query.data ?? {},
25
- isLoading: query.isLoading,
26
- };
27
- }
@@ -1,20 +0,0 @@
1
- export interface AddressValidationFormData {
2
- fullName: string;
3
- phoneNumber: string;
4
- pincode: string;
5
- addressLine1: string;
6
- addressLine2?: string;
7
- landmark?: string;
8
- city: string;
9
- state: string;
10
- addressType: string;
11
- isDefault: boolean;
12
- }
13
- export interface AddressValidationOptions {
14
- validatePhoneNumber?: (value: string) => boolean;
15
- validatePostalCode?: (value: string) => boolean;
16
- requiredFieldMessage?: string;
17
- invalidPhoneMessage?: string;
18
- invalidPostalCodeMessage?: string;
19
- }
20
- export declare function validateAddressForm(formData: AddressValidationFormData, options?: AddressValidationOptions): Record<string, string>;
@@ -1,35 +0,0 @@
1
- import { ERROR_MESSAGES } from "../../errors";
2
- export function validateAddressForm(formData, options) {
3
- const errors = {};
4
- const requiredFieldMessage = options?.requiredFieldMessage ?? ERROR_MESSAGES.VALIDATION.REQUIRED_FIELD;
5
- if (!formData.fullName?.trim()) {
6
- errors.fullName = requiredFieldMessage;
7
- }
8
- if (!formData.phoneNumber?.trim()) {
9
- errors.phoneNumber = requiredFieldMessage;
10
- }
11
- else if (options?.validatePhoneNumber &&
12
- !options.validatePhoneNumber(formData.phoneNumber)) {
13
- errors.phoneNumber =
14
- options.invalidPhoneMessage ?? ERROR_MESSAGES.VALIDATION.INVALID_PHONE;
15
- }
16
- if (!formData.pincode?.trim()) {
17
- errors.pincode = requiredFieldMessage;
18
- }
19
- else if (options?.validatePostalCode &&
20
- !options.validatePostalCode(formData.pincode)) {
21
- errors.pincode =
22
- options.invalidPostalCodeMessage ??
23
- ERROR_MESSAGES.VALIDATION.INVALID_INPUT;
24
- }
25
- if (!formData.addressLine1?.trim()) {
26
- errors.addressLine1 = requiredFieldMessage;
27
- }
28
- if (!formData.city?.trim()) {
29
- errors.city = requiredFieldMessage;
30
- }
31
- if (!formData.state?.trim()) {
32
- errors.state = requiredFieldMessage;
33
- }
34
- return errors;
35
- }
@@ -1,14 +0,0 @@
1
- import React from "react";
2
- export interface ChatListLabels {
3
- title?: string;
4
- }
5
- export interface ChatListProps {
6
- labels?: ChatListLabels;
7
- isLoading?: boolean;
8
- hasItems: boolean;
9
- renderLoading?: () => React.ReactNode;
10
- renderEmptyState?: () => React.ReactNode;
11
- renderList: () => React.ReactNode;
12
- className?: string;
13
- }
14
- export declare function ChatList({ labels, isLoading, hasItems, renderLoading, renderEmptyState, renderList, className, }: ChatListProps): React.JSX.Element;
@@ -1,9 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Div, Heading } from "../../../ui";
3
- export function ChatList({ labels = {}, isLoading = false, hasItems, renderLoading, renderEmptyState, renderList, className = "", }) {
4
- return (_jsxs(Div, { className: className, children: [labels.title && (_jsx(Heading, { level: 4, className: "mb-3", weight: "semibold", children: labels.title })), isLoading
5
- ? (renderLoading?.() ?? null)
6
- : hasItems
7
- ? renderList()
8
- : (renderEmptyState?.() ?? null)] }));
9
- }
@@ -1,17 +0,0 @@
1
- import React from "react";
2
- export interface ChatWindowLabels {
3
- title?: string;
4
- connected?: string;
5
- disconnected?: string;
6
- }
7
- export interface ChatWindowProps {
8
- labels?: ChatWindowLabels;
9
- isConnected?: boolean;
10
- isLoading?: boolean;
11
- error?: React.ReactNode;
12
- renderLoadingIndicator?: () => React.ReactNode;
13
- renderMessages: () => React.ReactNode;
14
- renderInput: () => React.ReactNode;
15
- className?: string;
16
- }
17
- export declare function ChatWindow({ labels, isConnected, isLoading, error, renderLoadingIndicator, renderMessages, renderInput, className, }: ChatWindowProps): React.JSX.Element;