@mohasinac/appkit 4.9.1 → 4.9.3

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 (48) hide show
  1. package/dist/features/admin/repository/notification.repository.js +2 -2
  2. package/dist/features/auctions/repository/bid.repository.d.ts +3 -1
  3. package/dist/features/auctions/repository/bid.repository.js +3 -3
  4. package/dist/features/auth/repository/session.repository.js +4 -4
  5. package/dist/features/auth/repository/user.repository.js +3 -3
  6. package/dist/features/blog/repository/blog.repository.d.ts +4 -1
  7. package/dist/features/blog/repository/blog.repository.js +4 -4
  8. package/dist/features/cart/repository/cart.repository.js +3 -3
  9. package/dist/features/catalogue/repository/catalogue.repository.d.ts +2 -1
  10. package/dist/features/catalogue/repository/catalogue.repository.js +2 -2
  11. package/dist/features/categories/repository/categories.repository.js +2 -2
  12. package/dist/features/events/repository/event-entry.repository.js +2 -2
  13. package/dist/features/events/repository/events.repository.js +4 -4
  14. package/dist/features/faq/repository/faqs.repository.js +2 -2
  15. package/dist/features/homepage/repository/carousel.repository.js +3 -3
  16. package/dist/features/homepage/repository/homepage-sections.repository.js +3 -3
  17. package/dist/features/lottery/repository/lottery-entry.repository.js +2 -2
  18. package/dist/features/orders/repository/orders.repository.d.ts +5 -1
  19. package/dist/features/orders/repository/orders.repository.js +5 -5
  20. package/dist/features/payments/repository/payout.repository.d.ts +4 -1
  21. package/dist/features/payments/repository/payout.repository.js +4 -4
  22. package/dist/features/products/repository/products.repository.d.ts +7 -1
  23. package/dist/features/products/repository/products.repository.js +7 -7
  24. package/dist/features/promotions/repository/coupons.repository.d.ts +4 -1
  25. package/dist/features/promotions/repository/coupons.repository.js +4 -2
  26. package/dist/features/reviews/repository/reviews.repository.d.ts +3 -1
  27. package/dist/features/reviews/repository/reviews.repository.js +3 -3
  28. package/dist/features/scams/repository/scammer.repository.d.ts +2 -1
  29. package/dist/features/scams/repository/scammer.repository.js +2 -2
  30. package/dist/features/server-errors/repository/server-errors.repository.js +2 -2
  31. package/dist/features/shipments/repository/shipment-items.repository.d.ts +2 -1
  32. package/dist/features/shipments/repository/shipment-items.repository.js +2 -2
  33. package/dist/features/shipments/repository/shipment-lots.repository.d.ts +2 -1
  34. package/dist/features/shipments/repository/shipment-lots.repository.js +2 -2
  35. package/dist/features/shipments/repository/shipments.repository.d.ts +3 -1
  36. package/dist/features/shipments/repository/shipments.repository.js +3 -3
  37. package/dist/features/support/repository/support.repository.js +3 -3
  38. package/dist/features/tester/repository/tester-checklist-item.repository.js +2 -2
  39. package/dist/features/tester/repository/tester-checklist-response.repository.js +2 -2
  40. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +61 -1
  41. package/dist/index.d.ts +1 -0
  42. package/dist/providers/db-firebase/index.d.ts +1 -1
  43. package/dist/providers/db-firebase/index.js +1 -1
  44. package/dist/providers/db-firebase/sieve.d.ts +19 -0
  45. package/dist/providers/db-firebase/sieve.js +11 -0
  46. package/dist/server.d.ts +2 -0
  47. package/dist/server.js +3 -0
  48. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { normalizeError } from "../../../errors/normalize";
2
- import { BaseRepository, getFirestoreCount, prepareForFirestore, } from "../../../providers/db-firebase";
2
+ import { BaseRepository, getFirestoreCount, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
3
3
  import { NOTIFICATIONS_COLLECTION, NOTIFICATION_FIELDS, } from "../schemas";
4
4
  import { serverLogger } from "../../../monitoring";
5
5
  import { serverTimestamp } from "../../../contracts/field-ops";
@@ -218,6 +218,6 @@ NotificationRepository.SIEVE_FIELDS = {
218
218
  priority: { canFilter: true, canSort: true },
219
219
  isRead: { canFilter: true, canSort: false },
220
220
  relatedType: { canFilter: true, canSort: false },
221
- createdAt: { canFilter: true, canSort: true },
221
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
222
222
  };
223
223
  export const notificationRepository = new NotificationRepository();
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Data access layer for bid documents in Firestore
5
5
  */
6
- import { BaseRepository } from "../../../providers/db-firebase";
6
+ import { BaseRepository, parseSieveDateValue } from "../../../providers/db-firebase";
7
7
  import type { DocumentReference, WriteBatch } from "firebase-admin/firestore";
8
8
  import type { SieveModel, FirebaseSieveResult } from "../../../providers/db-firebase";
9
9
  import type { BidDocument, BidCreateInput, BidUpdateInput, BidAdminUpdateInput, BidStatus } from "../schemas";
@@ -138,10 +138,12 @@ export declare class BidRepository extends BaseRepository<BidDocument> {
138
138
  bidDate: {
139
139
  canFilter: boolean;
140
140
  canSort: boolean;
141
+ parseValue: typeof parseSieveDateValue;
141
142
  };
142
143
  createdAt: {
143
144
  canFilter: boolean;
144
145
  canSort: boolean;
146
+ parseValue: typeof parseSieveDateValue;
145
147
  };
146
148
  };
147
149
  /**
@@ -4,7 +4,7 @@ import { normalizeError } from "../../../errors/normalize";
4
4
  *
5
5
  * Data access layer for bid documents in Firestore
6
6
  */
7
- import { BaseRepository, getFirestoreCount, prepareForFirestore, } from "../../../providers/db-firebase";
7
+ import { BaseRepository, getFirestoreCount, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
8
8
  import { DatabaseError } from "../../../errors";
9
9
  import { BID_COLLECTION, createBidId } from "../schemas";
10
10
  import { encryptPiiFields, decryptPiiFields, BID_PII_FIELDS, } from "../../../security";
@@ -375,7 +375,7 @@ BidRepository.SIEVE_FIELDS = {
375
375
  bidAmount: { canFilter: true, canSort: true },
376
376
  status: { canFilter: true, canSort: true },
377
377
  isWinning: { canFilter: true, canSort: false },
378
- bidDate: { canFilter: true, canSort: true },
379
- createdAt: { canFilter: true, canSort: true },
378
+ bidDate: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
379
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
380
380
  };
381
381
  export const bidRepository = new BidRepository();
@@ -1,7 +1,7 @@
1
1
  import { normalizeError } from "../../../errors/normalize";
2
2
  import { DatabaseError } from "../../../errors";
3
3
  import { serverLogger } from "../../../monitoring";
4
- import { BaseRepository, } from "../../../providers/db-firebase";
4
+ import { BaseRepository, parseSieveDateValue, } from "../../../providers/db-firebase";
5
5
  import { generateSessionId } from "../token-helpers";
6
6
  import { SESSION_COLLECTION, SESSION_EXPIRATION_MS, SESSION_FIELDS, } from "../schemas";
7
7
  export class SessionRepository extends BaseRepository {
@@ -273,8 +273,8 @@ SessionRepository.SIEVE_FIELDS = {
273
273
  canFilter: true,
274
274
  canSort: false,
275
275
  },
276
- lastActivity: { canFilter: true, canSort: true },
277
- expiresAt: { canFilter: true, canSort: true },
278
- createdAt: { canFilter: true, canSort: true },
276
+ lastActivity: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
277
+ expiresAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
278
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
279
279
  };
280
280
  export const sessionRepository = new SessionRepository();
@@ -1,6 +1,6 @@
1
1
  import { normalizeError } from "../../../errors/normalize";
2
2
  import { DatabaseError } from "../../../errors";
3
- import { BaseRepository, getFirestoreCount, prepareForFirestore, } from "../../../providers/db-firebase";
3
+ import { BaseRepository, getFirestoreCount, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
4
4
  import { USER_PII_FIELDS, decryptPayoutDetails, decryptPiiFields, decryptShippingConfig, encryptPayoutDetails, encryptPiiFields, encryptShippingConfig, piiBlindIndex, } from "../../../security";
5
5
  import { USER_COLLECTION, USER_FIELDS, createUserId, } from "../schemas";
6
6
  export class UserRepository extends BaseRepository {
@@ -257,7 +257,7 @@ UserRepository.SIEVE_FIELDS = {
257
257
  emailVerified: { canFilter: true, canSort: false },
258
258
  disabled: { canFilter: true, canSort: true },
259
259
  storeStatus: { canFilter: true, canSort: false },
260
- createdAt: { canFilter: true, canSort: true },
261
- updatedAt: { canFilter: true, canSort: true },
260
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
261
+ updatedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
262
262
  };
263
263
  export const userRepository = new UserRepository();
@@ -1,5 +1,5 @@
1
1
  import type { FirebaseSieveResult, SieveModel } from "../../../providers/db-firebase";
2
- import { BaseRepository } from "../../../providers/db-firebase";
2
+ import { BaseRepository, parseSieveDateValue } from "../../../providers/db-firebase";
3
3
  import { type BlogPostCreateInput, type BlogPostDocument, type BlogPostUpdateInput } from "../schemas";
4
4
  import type { BlogPostCategory } from "../types";
5
5
  declare class BlogRepository extends BaseRepository<BlogPostDocument> {
@@ -61,10 +61,12 @@ declare class BlogRepository extends BaseRepository<BlogPostDocument> {
61
61
  publishedAt: {
62
62
  canFilter: boolean;
63
63
  canSort: boolean;
64
+ parseValue: typeof parseSieveDateValue;
64
65
  };
65
66
  updatedAt: {
66
67
  canFilter: boolean;
67
68
  canSort: boolean;
69
+ parseValue: typeof parseSieveDateValue;
68
70
  };
69
71
  tags: {
70
72
  canFilter: boolean;
@@ -73,6 +75,7 @@ declare class BlogRepository extends BaseRepository<BlogPostDocument> {
73
75
  createdAt: {
74
76
  canFilter: boolean;
75
77
  canSort: boolean;
78
+ parseValue: typeof parseSieveDateValue;
76
79
  };
77
80
  };
78
81
  listPublished(opts: {
@@ -1,6 +1,6 @@
1
1
  import { DatabaseError } from "../../../errors";
2
2
  import { normalizeError } from "../../../errors/normalize";
3
- import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
3
+ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
4
4
  import { BLOG_POST_FIELDS, BLOG_POSTS_COLLECTION, } from "../schemas";
5
5
  import { createBlogPostId } from "../schemas";
6
6
  import { increment } from "../../../contracts/field-ops";
@@ -171,10 +171,10 @@ BlogRepository.SIEVE_FIELDS = {
171
171
  isFeatured: { canFilter: true, canSort: false },
172
172
  readTimeMinutes: { canFilter: true, canSort: true },
173
173
  views: { canFilter: true, canSort: true },
174
- publishedAt: { canFilter: true, canSort: true },
175
- updatedAt: { canFilter: true, canSort: true },
174
+ publishedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
175
+ updatedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
176
176
  tags: { canFilter: true, canSort: false },
177
- createdAt: { canFilter: true, canSort: true },
177
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
178
178
  };
179
179
  const blogRepository = new BlogRepository();
180
180
  export { BlogRepository, blogRepository };
@@ -4,7 +4,7 @@ import { CART_FIELDS } from "../../../constants/field-names";
4
4
  import { normalizeError } from "../../../errors/normalize";
5
5
  const ERR_CART_ITEM_NOT_FOUND = "Cart item not found";
6
6
  const ERR_CART_ITEM_LOCKED = "This item requires payment and cannot be removed or modified.";
7
- import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
7
+ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
8
8
  import { decryptPii } from "../../../security";
9
9
  import { CART_COLLECTION, } from "../schemas";
10
10
  export class CartRepository extends BaseRepository {
@@ -291,7 +291,7 @@ export class CartRepository extends BaseRepository {
291
291
  CartRepository.SIEVE_FIELDS = {
292
292
  userId: { canFilter: true, canSort: false },
293
293
  sessionId: { canFilter: true, canSort: false },
294
- updatedAt: { canFilter: true, canSort: true },
295
- createdAt: { canFilter: true, canSort: true },
294
+ updatedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
295
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
296
296
  };
297
297
  export const cartRepository = new CartRepository();
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Personal Catalogue Repository (Feature B).
3
3
  */
4
- import { BaseRepository, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
4
+ import { BaseRepository, parseSieveDateValue, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
5
5
  import { type CatalogueItemDocument, type CatalogueItemCreateInput, type CatalogueItemUpdateInput } from "../schemas/firestore";
6
6
  export declare class CatalogueRepository extends BaseRepository<CatalogueItemDocument> {
7
7
  constructor();
@@ -31,6 +31,7 @@ export declare class CatalogueRepository extends BaseRepository<CatalogueItemDoc
31
31
  createdAt: {
32
32
  canFilter: boolean;
33
33
  canSort: boolean;
34
+ parseValue: typeof parseSieveDateValue;
34
35
  };
35
36
  };
36
37
  /** Bounded scan for the staleness-reminder Function — images older than N days, not yet reminded for this update. */
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Personal Catalogue Repository (Feature B).
3
3
  */
4
- import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
4
+ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
5
5
  import { CATALOGUE_COLLECTION, CATALOGUE_FIELDS, createCatalogueItemId, } from "../schemas/firestore";
6
6
  export class CatalogueRepository extends BaseRepository {
7
7
  constructor() {
@@ -63,6 +63,6 @@ CatalogueRepository.SIEVE_FIELDS = {
63
63
  ownerId: { canFilter: true, canSort: false },
64
64
  visibility: { canFilter: true, canSort: false },
65
65
  listingStatus: { canFilter: true, canSort: false },
66
- createdAt: { canFilter: true, canSort: true },
66
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
67
67
  };
68
68
  export const catalogueRepository = new CatalogueRepository();
@@ -1,7 +1,7 @@
1
1
  import { normalizeError } from "../../../errors/normalize";
2
2
  import { increment, arrayUnion, arrayRemove, } from "../../../contracts/field-ops";
3
3
  import { DatabaseError } from "../../../errors";
4
- import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
4
+ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
5
5
  import { PRODUCT_FIELDS } from "../../../constants/field-names";
6
6
  import { CATEGORY_FIELDS, CATEGORIES_COLLECTION, MIN_ITEMS_FOR_FEATURED, buildCategoryTree, calculateCategoryFields, canBeFeatured, createCategoryId, isValidCategoryMove, } from "../schemas";
7
7
  export class CategoriesRepository extends BaseRepository {
@@ -523,6 +523,6 @@ CategoriesRepository.SIEVE_FIELDS = {
523
523
  },
524
524
  id: { canFilter: true, canSort: false },
525
525
  isLeaf: { canFilter: true, canSort: false },
526
- createdAt: { canFilter: true, canSort: true },
526
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
527
527
  };
528
528
  export const categoriesRepository = new CategoriesRepository();
@@ -1,7 +1,7 @@
1
1
  import { normalizeError } from "../../../errors/normalize";
2
2
  import { DatabaseError } from "../../../errors";
3
3
  import { serverLogger } from "../../../monitoring";
4
- import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
4
+ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
5
5
  import { decryptPiiFields, encryptPiiFields, EVENT_ENTRY_PII_FIELDS, } from "../../../security";
6
6
  import { EVENT_ENTRIES_COLLECTION, EVENT_ENTRY_FIELDS, } from "../schemas";
7
7
  /** Extracted from getExportReport's poll branch to stay under the max nesting depth. */
@@ -304,7 +304,7 @@ EventEntryRepository.SIEVE_FIELDS = {
304
304
  userDisplayName: { canFilter: false, canSort: false },
305
305
  reviewStatus: { canFilter: true, canSort: false },
306
306
  status: { canFilter: true, canSort: false },
307
- submittedAt: { canFilter: true, canSort: true },
307
+ submittedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
308
308
  points: { canFilter: true, canSort: true },
309
309
  };
310
310
  const eventEntryRepository = new EventEntryRepository();
@@ -2,7 +2,7 @@ import { DatabaseError } from "../../../errors";
2
2
  import { serverLogger } from "../../../monitoring";
3
3
  import { slugify } from "../../../utils";
4
4
  import { normalizeError } from "../../../errors/normalize";
5
- import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
5
+ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
6
6
  import { EVENT_FIELDS, EVENTS_COLLECTION, } from "../schemas";
7
7
  import { increment } from "../../../contracts/field-ops";
8
8
  class EventRepository extends BaseRepository {
@@ -143,8 +143,8 @@ EventRepository.SIEVE_FIELDS = {
143
143
  status: { canFilter: true, canSort: true },
144
144
  title: { canFilter: true, canSort: true },
145
145
  createdBy: { canFilter: true, canSort: false },
146
- startsAt: { canFilter: true, canSort: true },
147
- endsAt: { canFilter: true, canSort: true },
146
+ startsAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
147
+ endsAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
148
148
  "stats.totalEntries": {
149
149
  path: "stats.totalEntries",
150
150
  canFilter: true,
@@ -161,7 +161,7 @@ EventRepository.SIEVE_FIELDS = {
161
161
  canSort: true,
162
162
  },
163
163
  id: { canFilter: true, canSort: false },
164
- createdAt: { canFilter: true, canSort: true },
164
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
165
165
  };
166
166
  const eventRepository = new EventRepository();
167
167
  export { EventRepository, eventRepository };
@@ -1,4 +1,4 @@
1
- import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
1
+ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
2
2
  import { DatabaseError } from "../../../errors";
3
3
  import { increment } from "../../../contracts/field-ops";
4
4
  import { FAQ_FIELDS } from "../../../constants/field-names";
@@ -295,6 +295,6 @@ FirebaseFAQsRepository.SIEVE_FIELDS = {
295
295
  tags: { canFilter: true, canSort: false },
296
296
  searchTokens: { canFilter: true, canSort: false },
297
297
  "stats.helpful": { canFilter: false, canSort: true },
298
- createdAt: { canFilter: true, canSort: true },
298
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
299
299
  };
300
300
  export const faqsRepository = new FirebaseFAQsRepository();
@@ -4,7 +4,7 @@ import { normalizeError } from "../../../errors/normalize";
4
4
  *
5
5
  * Manages homepage carousel slides with grid card system
6
6
  */
7
- import { BaseRepository, getFirestoreCount, prepareForFirestore, } from "../../../providers/db-firebase";
7
+ import { BaseRepository, getFirestoreCount, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
8
8
  import { increment } from "../../../contracts/field-ops";
9
9
  import { CAROUSEL_SLIDES_COLLECTION, MAX_ACTIVE_SLIDES, DEFAULT_CAROUSEL_SLIDE_DATA, canActivateSlide, createCarouselId, } from "../schemas";
10
10
  import { DatabaseError } from "../../../errors";
@@ -250,8 +250,8 @@ CarouselRepository.SIEVE_FIELDS = {
250
250
  active: { canFilter: true, canSort: false },
251
251
  order: { canFilter: true, canSort: true },
252
252
  createdBy: { canFilter: true, canSort: false },
253
- createdAt: { canFilter: true, canSort: true },
254
- updatedAt: { canFilter: true, canSort: true },
253
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
254
+ updatedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
255
255
  };
256
256
  // Export singleton instance
257
257
  export const carouselRepository = new CarouselRepository();
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { DatabaseError } from "../../../errors";
7
7
  import { HOMEPAGE_SECTION_FIELDS } from "../../../constants/field-names";
8
- import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
8
+ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
9
9
  import { normalizeError } from "../../../errors/normalize";
10
10
  import { HOMEPAGE_SECTIONS_COLLECTION, createHomepageSectionId, } from "../schemas";
11
11
  export class HomepageSectionsRepository extends BaseRepository {
@@ -180,7 +180,7 @@ HomepageSectionsRepository.SIEVE_FIELDS = {
180
180
  type: { canFilter: true, canSort: false },
181
181
  enabled: { canFilter: true, canSort: false },
182
182
  order: { canFilter: true, canSort: true },
183
- createdAt: { canFilter: true, canSort: true },
184
- updatedAt: { canFilter: true, canSort: true },
183
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
184
+ updatedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
185
185
  };
186
186
  export const homepageSectionsRepository = new HomepageSectionsRepository();
@@ -1,7 +1,7 @@
1
1
  import { normalizeError } from "../../../errors/normalize";
2
2
  import { DatabaseError } from "../../../errors";
3
3
  import { serverLogger } from "../../../monitoring";
4
- import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
4
+ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
5
5
  import { decryptPiiFields, encryptPiiFields, LOTTERY_ENTRY_PII_FIELDS, } from "../../../security";
6
6
  import { LOTTERY_ENTRIES_COLLECTION, LOTTERY_ENTRY_FIELDS, } from "../schemas/firestore";
7
7
  class LotteryEntryRepository extends BaseRepository {
@@ -123,7 +123,7 @@ LotteryEntryRepository.SIEVE_FIELDS = {
123
123
  productId: { canFilter: true, canSort: false },
124
124
  userId: { canFilter: true, canSort: false },
125
125
  status: { canFilter: true, canSort: false },
126
- submittedAt: { canFilter: true, canSort: true },
126
+ submittedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
127
127
  userLotteryNumber: { canFilter: false, canSort: true },
128
128
  };
129
129
  export { LotteryEntryRepository };
@@ -1,6 +1,6 @@
1
1
  import type { DocumentReference, WriteBatch } from "firebase-admin/firestore";
2
2
  import type { FirebaseSieveResult, SieveModel } from "../../../providers/db-firebase";
3
- import { BaseRepository } from "../../../providers/db-firebase";
3
+ import { BaseRepository, parseSieveDateValue } from "../../../providers/db-firebase";
4
4
  import { type OrderCreateInput, type OrderDocument, type OrderRefundEvent } from "../schemas";
5
5
  import type { OrderStatus, PaymentStatus } from "../types";
6
6
  declare class OrderRepository extends BaseRepository<OrderDocument> {
@@ -89,10 +89,12 @@ declare class OrderRepository extends BaseRepository<OrderDocument> {
89
89
  orderDate: {
90
90
  canFilter: boolean;
91
91
  canSort: boolean;
92
+ parseValue: typeof parseSieveDateValue;
92
93
  };
93
94
  createdAt: {
94
95
  canFilter: boolean;
95
96
  canSort: boolean;
97
+ parseValue: typeof parseSieveDateValue;
96
98
  };
97
99
  contestable: {
98
100
  canFilter: boolean;
@@ -157,10 +159,12 @@ declare class OrderRepository extends BaseRepository<OrderDocument> {
157
159
  orderDate: {
158
160
  canFilter: boolean;
159
161
  canSort: boolean;
162
+ parseValue: typeof parseSieveDateValue;
160
163
  };
161
164
  createdAt: {
162
165
  canFilter: boolean;
163
166
  canSort: boolean;
167
+ parseValue: typeof parseSieveDateValue;
164
168
  };
165
169
  paymentBatchId: {
166
170
  canFilter: boolean;
@@ -1,6 +1,6 @@
1
1
  import { normalizeError } from "../../../errors/normalize";
2
2
  import { DatabaseError, NotFoundError } from "../../../errors";
3
- import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
3
+ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
4
4
  import { decryptPiiFields, encryptPiiFields, ORDER_PII_FIELDS, } from "../../../security";
5
5
  import { serverTimestamp } from "../../../contracts/field-ops";
6
6
  import { createOrderId, ORDER_COLLECTION, OrderStatusValues, } from "../schemas";
@@ -464,8 +464,8 @@ OrderRepository.SELLER_SIEVE_FIELDS = {
464
464
  paymentStatus: { canFilter: true, canSort: true },
465
465
  paymentMethod: { canFilter: true, canSort: true },
466
466
  totalPrice: { canFilter: true, canSort: true },
467
- orderDate: { canFilter: true, canSort: true },
468
- createdAt: { canFilter: true, canSort: true },
467
+ orderDate: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
468
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
469
469
  contestable: { canFilter: true, canSort: false },
470
470
  };
471
471
  OrderRepository.ADMIN_SIEVE_FIELDS = {
@@ -482,8 +482,8 @@ OrderRepository.ADMIN_SIEVE_FIELDS = {
482
482
  shippingMethod: { canFilter: true, canSort: true },
483
483
  payoutStatus: { canFilter: true, canSort: false },
484
484
  totalPrice: { canFilter: true, canSort: true },
485
- orderDate: { canFilter: true, canSort: true },
486
- createdAt: { canFilter: true, canSort: true },
485
+ orderDate: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
486
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
487
487
  // S-SBUNI-RULES 2026-05-13 — refund + batch fields
488
488
  paymentBatchId: { canFilter: true, canSort: false },
489
489
  contestable: { canFilter: true, canSort: false },
@@ -4,7 +4,7 @@
4
4
  * Data access layer for payout documents in Firestore.
5
5
  * Handles seller payout requests and admin payout processing.
6
6
  */
7
- import { BaseRepository, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
7
+ import { BaseRepository, parseSieveDateValue, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
8
8
  import type { DocumentReference, WriteResult } from "firebase-admin/firestore";
9
9
  import type { PayoutDocument, PayoutCreateInput, PayoutRefundDeduction, PayoutStatus, PayoutUpdateInput } from "../schemas";
10
10
  export declare class PayoutRepository extends BaseRepository<PayoutDocument> {
@@ -109,14 +109,17 @@ export declare class PayoutRepository extends BaseRepository<PayoutDocument> {
109
109
  requestedAt: {
110
110
  canFilter: boolean;
111
111
  canSort: boolean;
112
+ parseValue: typeof parseSieveDateValue;
112
113
  };
113
114
  createdAt: {
114
115
  canFilter: boolean;
115
116
  canSort: boolean;
117
+ parseValue: typeof parseSieveDateValue;
116
118
  };
117
119
  processedAt: {
118
120
  canFilter: boolean;
119
121
  canSort: boolean;
122
+ parseValue: typeof parseSieveDateValue;
120
123
  };
121
124
  };
122
125
  /**
@@ -4,7 +4,7 @@
4
4
  * Data access layer for payout documents in Firestore.
5
5
  * Handles seller payout requests and admin payout processing.
6
6
  */
7
- import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
7
+ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
8
8
  import { PAYOUT_COLLECTION, PAYOUT_FIELDS, createPayoutId } from "../schemas";
9
9
  import { encryptPiiFields, decryptPiiFields, addPiiIndices, PAYOUT_PII_FIELDS, PAYOUT_PII_INDEX_MAP, encryptPayoutBankAccount, decryptPayoutBankAccount, } from "../../../security";
10
10
  export class PayoutRepository extends BaseRepository {
@@ -241,9 +241,9 @@ PayoutRepository.SIEVE_FIELDS = {
241
241
  paymentMethod: { canFilter: true, canSort: false },
242
242
  amount: { canFilter: true, canSort: true },
243
243
  netAmount: { canFilter: true, canSort: true },
244
- requestedAt: { canFilter: true, canSort: true },
245
- createdAt: { canFilter: true, canSort: true },
246
- processedAt: { canFilter: true, canSort: true },
244
+ requestedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
245
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
246
+ processedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
247
247
  };
248
248
  // Export singleton instance
249
249
  export const payoutRepository = new PayoutRepository();
@@ -1,6 +1,6 @@
1
1
  import type { DocumentReference, WriteBatch } from "firebase-admin/firestore";
2
2
  import type { DocumentSnapshot } from "../../../providers/db-firebase";
3
- import { BaseRepository, type FirebaseSieveResult, type SieveFilterAliases, type SieveModel } from "../../../providers/db-firebase";
3
+ import { BaseRepository, parseSieveDateValue, type FirebaseSieveResult, type SieveFilterAliases, type SieveModel } from "../../../providers/db-firebase";
4
4
  import { type ProductCreateInput, type ProductDocument, type ProductUpdateInput } from "../schemas";
5
5
  import type { ProductStatus } from "../types";
6
6
  export declare class ProductRepository extends BaseRepository<ProductDocument> {
@@ -140,22 +140,27 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
140
140
  createdAt: {
141
141
  canFilter: boolean;
142
142
  canSort: boolean;
143
+ parseValue: typeof parseSieveDateValue;
143
144
  };
144
145
  updatedAt: {
145
146
  canFilter: boolean;
146
147
  canSort: boolean;
148
+ parseValue: typeof parseSieveDateValue;
147
149
  };
148
150
  auctionEndDate: {
149
151
  canFilter: boolean;
150
152
  canSort: boolean;
153
+ parseValue: typeof parseSieveDateValue;
151
154
  };
152
155
  prizeRevealWindowStart: {
153
156
  canFilter: boolean;
154
157
  canSort: boolean;
158
+ parseValue: typeof parseSieveDateValue;
155
159
  };
156
160
  prizeRevealWindowEnd: {
157
161
  canFilter: boolean;
158
162
  canSort: boolean;
163
+ parseValue: typeof parseSieveDateValue;
159
164
  };
160
165
  prizeRevealStatus: {
161
166
  canFilter: boolean;
@@ -184,6 +189,7 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
184
189
  preOrderDeliveryDate: {
185
190
  canFilter: boolean;
186
191
  canSort: boolean;
192
+ parseValue: typeof parseSieveDateValue;
187
193
  };
188
194
  preOrderDepositPercent: {
189
195
  canFilter: boolean;
@@ -1,7 +1,7 @@
1
1
  import { normalizeError } from "../../../errors/normalize";
2
2
  import { increment, serverTimestamp } from "../../../contracts/field-ops";
3
3
  import { DatabaseError } from "../../../errors";
4
- import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
4
+ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
5
5
  import { cacheManager } from "../../../core";
6
6
  import { generateUniqueId, slugify, buildSearchTokens, tokenizeQuery, generateBarcodeId } from "../../../utils";
7
7
  import { PRODUCT_COLLECTION, ProductStatusValues } from "../schemas";
@@ -500,18 +500,18 @@ ProductRepository.SIEVE_FIELDS = {
500
500
  bidCount: { canFilter: true, canSort: true },
501
501
  isSold: { canFilter: true, canSort: true },
502
502
  isPartOfBundle: { canFilter: true, canSort: false },
503
- createdAt: { canFilter: true, canSort: true },
504
- updatedAt: { canFilter: true, canSort: true },
505
- auctionEndDate: { canFilter: true, canSort: true },
506
- prizeRevealWindowStart: { canFilter: true, canSort: true },
507
- prizeRevealWindowEnd: { canFilter: true, canSort: true },
503
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
504
+ updatedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
505
+ auctionEndDate: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
506
+ prizeRevealWindowStart: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
507
+ prizeRevealWindowEnd: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
508
508
  prizeRevealStatus: { canFilter: true, canSort: false },
509
509
  startingBid: { canFilter: true, canSort: true },
510
510
  buyNowPrice: { canFilter: true, canSort: true },
511
511
  minBidIncrement: { canFilter: true, canSort: false },
512
512
  autoExtendable: { canFilter: true, canSort: false },
513
513
  reservePrice: { canFilter: true, canSort: true },
514
- preOrderDeliveryDate: { canFilter: true, canSort: true },
514
+ preOrderDeliveryDate: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
515
515
  preOrderDepositPercent: { canFilter: true, canSort: false },
516
516
  preOrderDepositAmount: { canFilter: true, canSort: true },
517
517
  preOrderMaxQuantity: { canFilter: true, canSort: true },
@@ -4,7 +4,7 @@ import type { ListingType } from "../../products/types";
4
4
  *
5
5
  * Manages coupon creation, validation, and usage tracking
6
6
  */
7
- import { BaseRepository } from "../../../providers/db-firebase";
7
+ import { BaseRepository, parseSieveDateValue } from "../../../providers/db-firebase";
8
8
  import type { SieveModel, FirebaseSieveResult } from "../../../providers/db-firebase";
9
9
  import { CouponDocument, CouponUsageDocument, CouponCreateInput } from "../schemas";
10
10
  import type { CouponType } from "../types";
@@ -179,11 +179,13 @@ export declare class CouponsRepository extends BaseRepository<CouponDocument> {
179
179
  path: string;
180
180
  canFilter: boolean;
181
181
  canSort: boolean;
182
+ parseValue: typeof parseSieveDateValue;
182
183
  };
183
184
  "validity.startDate": {
184
185
  path: string;
185
186
  canFilter: boolean;
186
187
  canSort: boolean;
188
+ parseValue: typeof parseSieveDateValue;
187
189
  };
188
190
  "discount.value": {
189
191
  path: string;
@@ -203,6 +205,7 @@ export declare class CouponsRepository extends BaseRepository<CouponDocument> {
203
205
  createdAt: {
204
206
  canFilter: boolean;
205
207
  canSort: boolean;
208
+ parseValue: typeof parseSieveDateValue;
206
209
  };
207
210
  storeId: {
208
211
  canFilter: boolean;
@@ -5,7 +5,7 @@ import { serverLogger } from "../../../monitoring/server-logger";
5
5
  *
6
6
  * Manages coupon creation, validation, and usage tracking
7
7
  */
8
- import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
8
+ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
9
9
  import { COUPONS_COLLECTION, COUPON_FIELDS, isValidCouponCode, isCouponValid, canUserUseCoupon, calculateDiscount, createCouponId, } from "../schemas";
10
10
  import { USER_COLLECTION } from "../../auth/schemas";
11
11
  import { DatabaseError } from "../../../errors";
@@ -500,11 +500,13 @@ CouponsRepository.SIEVE_FIELDS = {
500
500
  path: "validity.endDate",
501
501
  canFilter: true,
502
502
  canSort: true,
503
+ parseValue: parseSieveDateValue,
503
504
  },
504
505
  "validity.startDate": {
505
506
  path: "validity.startDate",
506
507
  canFilter: true,
507
508
  canSort: true,
509
+ parseValue: parseSieveDateValue,
508
510
  },
509
511
  "discount.value": {
510
512
  path: "discount.value",
@@ -521,7 +523,7 @@ CouponsRepository.SIEVE_FIELDS = {
521
523
  canFilter: true,
522
524
  canSort: true,
523
525
  },
524
- createdAt: { canFilter: true, canSort: true },
526
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
525
527
  storeId: { canFilter: true, canSort: false },
526
528
  scope: { canFilter: true, canSort: false },
527
529
  };
@@ -1,5 +1,5 @@
1
1
  import type { FirebaseSieveResult, SieveModel } from "../../../providers/db-firebase";
2
- import { BaseRepository } from "../../../providers/db-firebase";
2
+ import { BaseRepository, parseSieveDateValue } from "../../../providers/db-firebase";
3
3
  import { type ReviewCreateInput, type ReviewDocument } from "../schemas";
4
4
  import type { ReviewStatus } from "../types";
5
5
  export interface ReviewRatingAggregate {
@@ -107,10 +107,12 @@ declare class ReviewRepository extends BaseRepository<ReviewDocument> {
107
107
  updatedAt: {
108
108
  canFilter: boolean;
109
109
  canSort: boolean;
110
+ parseValue: typeof parseSieveDateValue;
110
111
  };
111
112
  createdAt: {
112
113
  canFilter: boolean;
113
114
  canSort: boolean;
115
+ parseValue: typeof parseSieveDateValue;
114
116
  };
115
117
  };
116
118
  listForProduct(productId: string, model: SieveModel): Promise<FirebaseSieveResult<ReviewDocument>>;
@@ -1,4 +1,4 @@
1
- import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
1
+ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
2
2
  import { addPiiIndices, decryptPiiFields, encryptPiiFields, REVIEW_PII_FIELDS, REVIEW_PII_INDEX_MAP, } from "../../../security";
3
3
  import { createReviewId, REVIEW_COLLECTION, } from "../schemas";
4
4
  const REVIEW_FIELDS = {
@@ -262,8 +262,8 @@ ReviewRepository.SIEVE_FIELDS = {
262
262
  helpfulCount: { canFilter: true, canSort: true },
263
263
  featured: { canFilter: true, canSort: false },
264
264
  reportCount: { canFilter: true, canSort: true },
265
- updatedAt: { canFilter: true, canSort: true },
266
- createdAt: { canFilter: true, canSort: true },
265
+ updatedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
266
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
267
267
  };
268
268
  const reviewRepository = new ReviewRepository();
269
269
  export { ReviewRepository, reviewRepository };
@@ -1,4 +1,4 @@
1
- import { BaseRepository, type FirebaseSieveResult, type SieveModel } from "../../../providers/db-firebase";
1
+ import { BaseRepository, parseSieveDateValue, type FirebaseSieveResult, type SieveModel } from "../../../providers/db-firebase";
2
2
  import { type ScammerDocument, type ScammerIncidentDocument, type ScammerCommentDocument, type ScammerCreateInput, type ScammerAdminUpdateInput } from "../schemas/firestore";
3
3
  declare class ScammerRepository extends BaseRepository<ScammerDocument> {
4
4
  constructor();
@@ -42,6 +42,7 @@ declare class ScammerRepository extends BaseRepository<ScammerDocument> {
42
42
  createdAt: {
43
43
  canFilter: boolean;
44
44
  canSort: boolean;
45
+ parseValue: typeof parseSieveDateValue;
45
46
  };
46
47
  updatedAt: {
47
48
  canFilter: boolean;
@@ -1,5 +1,5 @@
1
1
  import { DatabaseError } from "../../../errors";
2
- import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
2
+ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
3
3
  import { normalizeError } from "../../../errors/normalize";
4
4
  import { serverLogger } from "../../../monitoring/server-logger";
5
5
  import { increment } from "../../../contracts/field-ops";
@@ -190,7 +190,7 @@ ScammerRepository.SIEVE_FIELDS = {
190
190
  isContested: { canFilter: true, canSort: false },
191
191
  views: { canFilter: false, canSort: true },
192
192
  incidentCount: { canFilter: false, canSort: true },
193
- createdAt: { canFilter: true, canSort: true },
193
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
194
194
  updatedAt: { canFilter: false, canSort: true },
195
195
  };
196
196
  const scammerRepository = new ScammerRepository();
@@ -1,5 +1,5 @@
1
1
  import { BaseRepository } from "../../../providers/db-firebase";
2
- import { applySieveToFirestore } from "../../../providers/db-firebase";
2
+ import { applySieveToFirestore, parseSieveDateValue } from "../../../providers/db-firebase";
3
3
  import { SERVER_ERRORS_COLLECTION, SERVER_ERROR_STACK_MAX_BYTES, } from "../schemas/firestore";
4
4
  import { normalizeError } from "../../../errors/normalize";
5
5
  /** Truncate a stack to the documented byte budget — never persist > 4 KB. */
@@ -63,7 +63,7 @@ export class ServerErrorsRepository extends BaseRepository {
63
63
  }
64
64
  }
65
65
  ServerErrorsRepository.SIEVE_FIELDS = {
66
- occurredAt: { canFilter: true, canSort: true },
66
+ occurredAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
67
67
  source: { canFilter: true, canSort: false },
68
68
  route: { canFilter: true, canSort: false },
69
69
  method: { canFilter: true, canSort: false },
@@ -4,7 +4,7 @@
4
4
  * exactly Firestore's native batched-write limit, so bulkCreate() writes an
5
5
  * entire import in a single WriteBatch with no chunking logic.
6
6
  */
7
- import { BaseRepository, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
7
+ import { BaseRepository, parseSieveDateValue, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
8
8
  import { type ShipmentItem, type ShipmentItemCreateInput, type ShipmentItemUpdateInput } from "../schemas/firestore";
9
9
  export declare class ShipmentItemsRepository extends BaseRepository<ShipmentItem> {
10
10
  constructor();
@@ -50,6 +50,7 @@ export declare class ShipmentItemsRepository extends BaseRepository<ShipmentItem
50
50
  createdAt: {
51
51
  canFilter: boolean;
52
52
  canSort: boolean;
53
+ parseValue: typeof parseSieveDateValue;
53
54
  };
54
55
  };
55
56
  }
@@ -4,7 +4,7 @@
4
4
  * exactly Firestore's native batched-write limit, so bulkCreate() writes an
5
5
  * entire import in a single WriteBatch with no chunking logic.
6
6
  */
7
- import { BaseRepository, } from "../../../providers/db-firebase";
7
+ import { BaseRepository, parseSieveDateValue, } from "../../../providers/db-firebase";
8
8
  import { SHIPMENT_ITEM_COLLECTION, SHIPMENT_ITEM_FIELDS, MAX_ITEMS_PER_LOT, } from "../schemas/firestore";
9
9
  import { ValidationError } from "../../../errors";
10
10
  import { sieveAnd, sieveFilter, SIEVE_OP } from "../../../utils/sieve-builder";
@@ -73,6 +73,6 @@ ShipmentItemsRepository.SIEVE_FIELDS = {
73
73
  title: { canFilter: true, canSort: true },
74
74
  price: { canFilter: true, canSort: true },
75
75
  linkedProductId: { canFilter: true, canSort: false },
76
- createdAt: { canFilter: true, canSort: true },
76
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
77
77
  };
78
78
  export const shipmentItemsRepository = new ShipmentItemsRepository();
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Shipment Lots Repository — one doc per lot, FK'd to a shipment.
3
3
  */
4
- import { BaseRepository, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
4
+ import { BaseRepository, parseSieveDateValue, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
5
5
  import { type ShipmentLot, type ShipmentLotCreateInput, type ShipmentLotUpdateInput } from "../schemas/firestore";
6
6
  export declare class ShipmentLotsRepository extends BaseRepository<ShipmentLot> {
7
7
  constructor();
@@ -42,6 +42,7 @@ export declare class ShipmentLotsRepository extends BaseRepository<ShipmentLot>
42
42
  createdAt: {
43
43
  canFilter: boolean;
44
44
  canSort: boolean;
45
+ parseValue: typeof parseSieveDateValue;
45
46
  };
46
47
  };
47
48
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Shipment Lots Repository — one doc per lot, FK'd to a shipment.
3
3
  */
4
- import { BaseRepository, } from "../../../providers/db-firebase";
4
+ import { BaseRepository, parseSieveDateValue, } from "../../../providers/db-firebase";
5
5
  import { SHIPMENT_LOT_COLLECTION, SHIPMENT_LOT_FIELDS, } from "../schemas/firestore";
6
6
  export class ShipmentLotsRepository extends BaseRepository {
7
7
  constructor() {
@@ -50,6 +50,6 @@ ShipmentLotsRepository.SIEVE_FIELDS = {
50
50
  lotName: { canFilter: true, canSort: true },
51
51
  projectedProfit: { canFilter: true, canSort: true },
52
52
  projectedRevenue: { canFilter: true, canSort: true },
53
- createdAt: { canFilter: true, canSort: true },
53
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
54
54
  };
55
55
  export const shipmentLotsRepository = new ShipmentLotsRepository();
@@ -2,7 +2,7 @@
2
2
  * Procurement Shipments Repository — header document only.
3
3
  * Lots/items live in ShipmentLotsRepository / ShipmentItemsRepository.
4
4
  */
5
- import { BaseRepository, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
5
+ import { BaseRepository, parseSieveDateValue, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
6
6
  import { type ShipmentDocument, type ShipmentCreateInput, type ShipmentUpdateInput } from "../schemas/firestore";
7
7
  export declare class ShipmentsRepository extends BaseRepository<ShipmentDocument> {
8
8
  constructor();
@@ -39,10 +39,12 @@ export declare class ShipmentsRepository extends BaseRepository<ShipmentDocument
39
39
  etaDate: {
40
40
  canFilter: boolean;
41
41
  canSort: boolean;
42
+ parseValue: typeof parseSieveDateValue;
42
43
  };
43
44
  createdAt: {
44
45
  canFilter: boolean;
45
46
  canSort: boolean;
47
+ parseValue: typeof parseSieveDateValue;
46
48
  };
47
49
  };
48
50
  list(model: SieveModel): Promise<FirebaseSieveResult<ShipmentDocument>>;
@@ -2,7 +2,7 @@
2
2
  * Procurement Shipments Repository — header document only.
3
3
  * Lots/items live in ShipmentLotsRepository / ShipmentItemsRepository.
4
4
  */
5
- import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
5
+ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
6
6
  import { ConflictError } from "../../../errors";
7
7
  import { SHIPMENT_COLLECTION, SHIPMENT_FIELDS, SHIPMENT_ITEM_COLLECTION, DEFAULT_SHIPMENT_TOTALS, createShipmentId, } from "../schemas/firestore";
8
8
  export class ShipmentsRepository extends BaseRepository {
@@ -71,7 +71,7 @@ ShipmentsRepository.SIEVE_FIELDS = {
71
71
  shipmentNumber: { canFilter: true, canSort: true },
72
72
  supplierName: { canFilter: true, canSort: true },
73
73
  status: { canFilter: true, canSort: true },
74
- etaDate: { canFilter: true, canSort: true },
75
- createdAt: { canFilter: true, canSort: true },
74
+ etaDate: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
75
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
76
76
  };
77
77
  export const shipmentsRepository = new ShipmentsRepository();
@@ -1,5 +1,5 @@
1
1
  import { firebaseFieldOps } from "../../../providers/db-firebase";
2
- import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
2
+ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
3
3
  import { normalizeError } from "../../../errors/normalize";
4
4
  import { DatabaseError } from "../../../errors";
5
5
  import { SUPPORT_TICKET_FIELDS } from "../../../constants/field-names";
@@ -115,7 +115,7 @@ SupportRepository.SIEVE_FIELDS = {
115
115
  priority: { canFilter: true, canSort: false },
116
116
  assignedTo: { canFilter: true, canSort: false },
117
117
  orderId: { canFilter: true, canSort: false },
118
- createdAt: { canFilter: true, canSort: true },
119
- updatedAt: { canFilter: true, canSort: true },
118
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
119
+ updatedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
120
120
  };
121
121
  export const supportRepository = new SupportRepository();
@@ -1,4 +1,4 @@
1
- import { BaseRepository } from "../../../providers/db-firebase";
1
+ import { BaseRepository, parseSieveDateValue } from "../../../providers/db-firebase";
2
2
  import { DatabaseError } from "../../../errors";
3
3
  import { TESTER_CHECKLIST_ITEM_COLLECTION, TESTER_CHECKLIST_ITEM_FIELDS, createChecklistItemId, } from "../schemas/firestore";
4
4
  export class TesterChecklistItemRepository extends BaseRepository {
@@ -130,7 +130,7 @@ TesterChecklistItemRepository.SIEVE_FIELDS = {
130
130
  phase: { canFilter: true, canSort: true },
131
131
  isActive: { canFilter: true, canSort: false },
132
132
  searchTokens: { canFilter: true, canSort: false },
133
- createdAt: { canFilter: true, canSort: true },
133
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
134
134
  bugConfirmed: { canFilter: true, canSort: false },
135
135
  };
136
136
  export const testerChecklistItemRepository = new TesterChecklistItemRepository();
@@ -1,4 +1,4 @@
1
- import { BaseRepository } from "../../../providers/db-firebase";
1
+ import { BaseRepository, parseSieveDateValue } from "../../../providers/db-firebase";
2
2
  import { TESTER_CHECKLIST_RESPONSE_COLLECTION, TESTER_CHECKLIST_RESPONSE_FIELDS, createChecklistResponseId, } from "../schemas/firestore";
3
3
  import { testerChecklistItemRepository } from "./tester-checklist-item.repository";
4
4
  function escapeMd(text) {
@@ -230,6 +230,6 @@ TesterChecklistResponseRepository.SIEVE_FIELDS = {
230
230
  phase: { canFilter: true, canSort: true },
231
231
  answer: { canFilter: true, canSort: false },
232
232
  status: { canFilter: true, canSort: false },
233
- createdAt: { canFilter: true, canSort: true },
233
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
234
234
  };
235
235
  export const testerChecklistResponseRepository = new TesterChecklistResponseRepository();
@@ -207,6 +207,12 @@ const rawTesterChecklistItems = [
207
207
  { key: "classified-contact-flow", label: "A classified listing shows a contact-seller flow with deliberately no checkout/buy button" },
208
208
  { key: "digitalcode-delivery", label: "Purchasing a digital-code listing delivers the code to the buyer post-purchase" },
209
209
  { key: "live-item-detail", label: "A live-item listing's detail page shows the livestream link correctly" },
210
+ {
211
+ key: "live-item-video-mandatory",
212
+ label: "Creating a live-item listing (species: animals/plants) without a video is rejected with a clear error; a live listing WITH a video plays correctly in the gallery's video slide and its poster thumbnail is watermarked",
213
+ description: "Verify the block on both the admin product editor and the seller \"List a live item\" form. Then verify playback against the tester-sandbox fixture \"Test Live Item — Golden Retriever Puppy\" (live-tester-sandbox-1) — its video is a real dog clip (previously a broken YouTube link that never played); confirm it actually plays in theater mode and the thumbnail carries the site watermark.",
214
+ href: "/store/live/new",
215
+ },
210
216
  ],
211
217
  },
212
218
  {
@@ -224,7 +230,11 @@ const rawTesterChecklistItems = [
224
230
  { key: "shipping-address-edit", label: "Editing an existing shipping address from checkout works" },
225
231
  { key: "shipping-method-selection", label: "Selecting a shipping method/provider at checkout recalculates the shipping fee correctly" },
226
232
  { key: "gst-breakdown-display", label: "When GST is enabled in Site Settings, checkout shows the correct CGST/SGST or IGST breakdown based on buyer vs seller state" },
227
- { key: "checkout-otp-highvalue", label: "Carts ≥ the admin-configured high-value OTP threshold (Site Settings → Shipping → \"High-value checkout OTP threshold\") prompt a \"Verify this order\" email OTP right before payment (except COD)" },
233
+ {
234
+ key: "checkout-otp-highvalue",
235
+ label: "Carts ≥ the admin-configured high-value OTP threshold (Site Settings → Shipping → \"High-value checkout OTP threshold\", default ₹5,000) prompt a \"Verify this order\" email OTP right before payment (except COD); carts below the threshold check out directly with no OTP step at all",
236
+ description: "Root-caused 2026-08-20 — the OTP email send used to be fire-and-forget, so a Resend/API failure silently left the buyer stuck with no code and no error. Sending now surfaces a real error if the email genuinely fails to send, and the \"already sent, retry in N minutes\" rate-limit message is a clear sentence instead of a raw error code. Place a real order ≥ the threshold and confirm the code actually arrives by email (check spam too) — report the exact error text if it still doesn't.",
237
+ },
228
238
  { key: "payment-method-selection", label: "Choosing between COD, UPI/manual, and Razorpay (when enabled) at checkout works" },
229
239
  { key: "payment-window-countdown", label: "Manual-payment orders show a 15-minute countdown timer on the payment page" },
230
240
  { key: "payment-proof-upload", label: "Uploading manual payment proof (screenshot, UTR, mark-as-paid + agreement checkboxes) works" },
@@ -334,6 +344,17 @@ const rawTesterChecklistItems = [
334
344
  label: "The mobile bottom navigation bar shows a Cart tab with a live item-count badge; Wishlist is no longer a bottom-tab slot but is still reachable from the header",
335
345
  description: "Wishlist was intentionally removed only from the bottom tab bar, not from the header/app-bar or main navigation — confirm the header wishlist icon is unaffected.",
336
346
  },
347
+ {
348
+ key: "cart-badge-matches-cart-page",
349
+ label: "After adding an item to the cart, the item actually appears when you open the cart page — not just an updated badge count with an empty cart",
350
+ description: "Root-caused 2026-08-20 — a background sync manager that replays queued cart/wishlist writes to the server was built but never mounted anywhere in the app, so the cart badge (read from a local queue) updated instantly while the server-backed cart page (read from Firestore) never received the write and showed empty. Add an item, then navigate to /cart (not just glance at the badge) and confirm it's really there — check on both a fresh session and after a page reload.",
351
+ href: "/cart",
352
+ },
353
+ {
354
+ key: "cart-mobile-no-overflow",
355
+ label: "On mobile, cart item cards stay fully within the screen width per seller group — no horizontal overflow/clipping — and a floating checkout bar (styled like the bulk-action bar, sitting above the bottom nav) shows the total and a Checkout button, with enough bottom margin that the last cart item is never hidden behind it",
356
+ href: "/cart",
357
+ },
337
358
  ],
338
359
  },
339
360
  {
@@ -398,6 +419,12 @@ const rawTesterChecklistItems = [
398
419
  { key: "browser-back-forward", label: "Browser back/forward buttons move correctly between dashboard sub-pages without breaking the layout" },
399
420
  { key: "logged-out-redirect", label: "Opening a user dashboard URL while logged out redirects to login, then returns to the originally requested page after signing in" },
400
421
  { key: "breadcrumbs-accurate", label: "Breadcrumbs (where present) on nested user dashboard pages accurately reflect the current location" },
422
+ {
423
+ key: "sidebar-logout-button",
424
+ label: "The user dashboard sidebar itself (not just the header profile dropdown) has a visible \"Log out\" action at the bottom, and clicking it signs out and redirects to login",
425
+ description: "Previously the only logout affordance while on a dashboard page was the header profile dropdown — the sidebar had none. Check on both mobile (drawer) and desktop (persistent rail).",
426
+ href: "/user",
427
+ },
401
428
  ],
402
429
  },
403
430
  ]),
@@ -536,6 +563,11 @@ const rawTesterChecklistItems = [
536
563
  { key: "store-browser-back-forward", label: "Browser back/forward buttons move correctly between store dashboard sub-pages without breaking the layout" },
537
564
  { key: "store-logged-out-redirect", label: "Opening a store dashboard URL while logged out (or as a non-seller) redirects appropriately instead of erroring" },
538
565
  { key: "store-nav-listing-type-groups", label: "The seller nav correctly groups/labels each listing-type management area (standard, auctions, pre-orders, prize draws, bundles, classifieds, digital codes, live, art, stickers)" },
566
+ {
567
+ key: "store-sidebar-logout-button",
568
+ label: "The store dashboard sidebar has a visible \"Log out\" action at the bottom (not just the header profile dropdown), and clicking it signs out and redirects to login",
569
+ href: "/store",
570
+ },
539
571
  ],
540
572
  },
541
573
  ]),
@@ -669,6 +701,11 @@ const rawTesterChecklistItems = [
669
701
  { key: "card-row-heights-aligned", label: "Product/auction/pre-order/event cards in the same row are the same height, with their action button (Reserve now, Place bid, View details, etc.) aligned along a common bottom edge even when one card's description is longer than another's" },
670
702
  { key: "image-watermark-subtle", label: "Product and listing images show a small, subtle watermark (not oversized or fully opaque, doesn't obscure the image)" },
671
703
  { key: "section-cta-buttons-visible", label: "Homepage section \"View all →\" / \"Go to…\" buttons use a solid primary-colored fill so they're clearly identifiable as clickable CTAs, not a plain white/outline box that blends into the page" },
704
+ {
705
+ key: "mobile-search-bar-proportions",
706
+ label: "On mobile, the global header search bar's resource-type dropdown (Products/Auctions/etc.) is a compact, capped width and the text input takes up most of the remaining space — not an oversized dropdown squeezing the input down to a sliver",
707
+ href: "/",
708
+ },
672
709
  ],
673
710
  },
674
711
  {
@@ -752,6 +789,12 @@ const rawTesterChecklistItems = [
752
789
  description: "Verify \"Beyblade Burst\" shows 2 items (matching the 2 real products inside it when browsed), not \"0 items\". If any category shows 0 despite having published products, run `npm run categories:backfill-metrics` to recompute the counters.",
753
790
  href: "/categories",
754
791
  },
792
+ {
793
+ key: "parent-category-includes-children",
794
+ label: "Visiting a PARENT category's page shows products filed under its child categories too (not just products tagged with the parent's own id), and its \"N products\" count is the sum of its own products plus every child category's",
795
+ description: "Verify against \"Spinning Tops\" (category-spinning-tops, the root over Original/Metal Fight/Burst/X) — its product listing and count should include every Beyblade product across all 4 generations, not show empty or only the root's own (normally zero) direct products. Note: this rollup applies to page 1 / default sort only — changing sort or paging within the tab may still narrow back to the parent's own products (a known follow-up, not yet fixed).",
796
+ href: "/categories",
797
+ },
755
798
  {
756
799
  key: "category-brand-related-sections",
757
800
  label: "A category detail page shows a \"Related Categories\" section (other categories sharing the same root) and a brand detail page shows a \"Related Brands\" section, each with real items",
@@ -937,6 +980,12 @@ const rawTesterChecklistItems = [
937
980
  pageLabel: "Users & Trust",
938
981
  cases: [
939
982
  { key: "users-role-change", label: "Admin can change a user's role and toggle isTester/canTestAdmin", href: "/admin/users" },
983
+ {
984
+ key: "admin-delete-user-complete",
985
+ label: "Deleting a user from the admin Users editor removes their Firestore profile, their active sessions, AND their Firebase Auth record — not just the Firestore doc",
986
+ description: "Verify the deleted account can no longer log in at all (Auth record gone), not just that it's missing from the admin list.",
987
+ href: "/admin/users",
988
+ },
940
989
  { key: "roles-crud", label: "Admin can create and edit custom roles", href: "/admin/roles" },
941
990
  { key: "sessions-revoke", label: "Admin can revoke a user's active sessions", href: "/admin/sessions" },
942
991
  { key: "scammers-registry-admin", label: "Admin can manage the scammer registry", href: "/admin/scammers" },
@@ -976,6 +1025,17 @@ const rawTesterChecklistItems = [
976
1025
  { key: "guide-pages-admin", label: "The 8 admin guide pages all load correctly", href: "/admin/guide" },
977
1026
  { key: "tester-checklist-crud-admin", label: "Admin can create, edit, and toggle adminOnly on tester checklist items", href: "/admin/tester-checklist" },
978
1027
  { key: "tester-feedback-report-export", label: "Admin tester-feedback report shows Yes/No analytics grouped correctly and the Download Report export works", href: "/admin/tester-feedback" },
1028
+ {
1029
+ key: "admin-sidebar-logout-button",
1030
+ label: "The admin dashboard sidebar has a visible \"Log out\" action at the bottom (not just the header profile dropdown), and clicking it signs out and redirects to login",
1031
+ href: "/admin/dashboard",
1032
+ },
1033
+ {
1034
+ key: "dashboard-tables-colors-avatars-icons",
1035
+ label: "Admin/store/user dashboard tables and list/grid cards show color-coded status badges (green/amber/red/blue by meaning, not one flat color for every status) and a circular avatar/icon per row (a real photo when the item has one — user avatar, store logo, product image — otherwise a resource-type icon), instead of plain black-on-white text rows",
1036
+ description: "Spot-check a few different dashboards — Users (avatar photos), Stores (logos), Products (thumbnails), and a resource with no natural photo like Orders/Payouts/Coupons (should still show a resource icon, not a blank row).",
1037
+ href: "/admin/users",
1038
+ },
979
1039
  ],
980
1040
  },
981
1041
  {
package/dist/index.d.ts CHANGED
@@ -1838,6 +1838,7 @@ export type { FeedbackConfig } from "./features/events/index";
1838
1838
  export type { FormFieldType } from "./features/events/index";
1839
1839
  export type { LeaderboardEntry } from "./features/events/index";
1840
1840
  export type { PollResultEntry } from "./features/events/index";
1841
+ export type { SpinResultEntry } from "./features/events/index";
1841
1842
  export type { OfferConfig } from "./features/events/index";
1842
1843
  export type { PollConfig } from "./features/events/index";
1843
1844
  export type { PollResultsVisibility } from "./features/events/index";
@@ -27,4 +27,4 @@ export declare const firebaseDbProvider: IDbProvider;
27
27
  export { buildFirebaseClientConfig, normalizeFirebaseConfigValue, } from "./client-config";
28
28
  export type { FirebaseClientConfig } from "./client-config";
29
29
  export type { SieveModel, SieveFields, SieveFieldConfig, SieveOptions, SieveResult, SieveFilterAlias, SieveFilterAliases, FirebaseSieveFieldConfig, FirebaseSieveFields, FirebaseSieveOptions, FirebaseSieveResult, } from "./sieve";
30
- export { applySieveToFirestore, expandFilterAliases } from "./sieve";
30
+ export { applySieveToFirestore, expandFilterAliases, parseSieveDateValue } from "./sieve";
@@ -38,4 +38,4 @@ export const firebaseDbProvider = {
38
38
  };
39
39
  // Firebase client config builder (for consumer app bundles)
40
40
  export { buildFirebaseClientConfig, normalizeFirebaseConfigValue, } from "./client-config";
41
- export { applySieveToFirestore, expandFilterAliases } from "./sieve";
41
+ export { applySieveToFirestore, expandFilterAliases, parseSieveDateValue } from "./sieve";
@@ -75,8 +75,27 @@ export type SieveFieldConfig = {
75
75
  path?: string;
76
76
  canFilter?: boolean;
77
77
  canSort?: boolean;
78
+ /**
79
+ * Convert the raw string filter value before it reaches the Firestore
80
+ * adapter. Required for any field stored as a Firestore Timestamp — without
81
+ * it, sievejs's default `convertValue` (`processor.js`) leaves date-like
82
+ * filter values as plain strings, and a Firestore inequality (`>`, `<`,
83
+ * `>=`, `<=`) comparing a Timestamp field against a string value matches
84
+ * ZERO documents (Firestore requires the query value's type to match the
85
+ * field's stored type for range comparisons) — silently, with no error.
86
+ * Use `parseSieveDateValue` below for Timestamp fields.
87
+ */
88
+ parseValue?: (raw: string) => unknown;
78
89
  };
79
90
  export type SieveFields = Record<string, SieveFieldConfig>;
91
+ /**
92
+ * `SieveFieldConfig.parseValue` for any field stored as a Firestore
93
+ * Timestamp. Converts an ISO date string (or any `Date`-parseable value) into
94
+ * a real `Date`, which the Admin SDK compares correctly against a Timestamp
95
+ * field. Falls back to the raw string on an unparseable value rather than
96
+ * throwing, so a malformed filter degrades to "no match" instead of a 500.
97
+ */
98
+ export declare function parseSieveDateValue(raw: string): Date | string;
80
99
  export interface SieveModel {
81
100
  /** Comma-delimited filter expressions, e.g. `status==published,price>=100` */
82
101
  filters?: string;
@@ -101,6 +101,17 @@ import { expandFilterAliases as _expandFilterAliases, } from "./filter-aliases";
101
101
  // should prefer importing from `./filter-aliases` directly — that path has
102
102
  // zero runtime deps and is client-safe.
103
103
  export { _expandFilterAliases as expandFilterAliases, };
104
+ /**
105
+ * `SieveFieldConfig.parseValue` for any field stored as a Firestore
106
+ * Timestamp. Converts an ISO date string (or any `Date`-parseable value) into
107
+ * a real `Date`, which the Admin SDK compares correctly against a Timestamp
108
+ * field. Falls back to the raw string on an unparseable value rather than
109
+ * throwing, so a malformed filter degrades to "no match" instead of a 500.
110
+ */
111
+ export function parseSieveDateValue(raw) {
112
+ const parsed = new Date(raw);
113
+ return Number.isNaN(parsed.getTime()) ? raw : parsed;
114
+ }
104
115
  const SIEVE_DEFAULTS = {
105
116
  caseSensitive: false,
106
117
  defaultPageSize: 20,
package/dist/server.d.ts CHANGED
@@ -272,6 +272,8 @@ export { eventIdGET } from "./features/events/server";
272
272
  export { eventsGET } from "./features/events/server";
273
273
  export { getEventLeaderboard } from "./features/events/server";
274
274
  export { getEventPollResults } from "./features/events/server";
275
+ export type { PollResultEntry, SpinResultEntry } from "./features/events/index";
276
+ export { getEventSpinResults } from "./features/events/server";
275
277
  export { getBugHunterLeaderboard } from "./features/tester/server";
276
278
  export { getPublicEventById } from "./features/events/server";
277
279
  export { listPublicEvents } from "./features/events/server";
package/dist/server.js CHANGED
@@ -779,6 +779,9 @@ export { getEventLeaderboard } from "./features/events/server";
779
779
  // getEventPollResults - Helper for get event poll results (per-option vote tally).
780
780
  export { getEventPollResults } from "./features/events/server";
781
781
  // [SERVER-ONLY]-Server-only — uses Node.js, Next.js server internals, or third-party server SDKs (auth, email, payment, shipping).
782
+ // getEventSpinResults - Last N winning spins for a spin_wheel event, most recent first.
783
+ export { getEventSpinResults } from "./features/events/server";
784
+ // [SERVER-ONLY]-Server-only — uses Node.js, Next.js server internals, or third-party server SDKs (auth, email, payment, shipping).
782
785
  // getBugHunterLeaderboard - Ranks testers by confirmed-bug count.
783
786
  export { getBugHunterLeaderboard } from "./features/tester/server";
784
787
  // [SERVER-ONLY]-Server-only — uses Node.js, Next.js server internals, or third-party server SDKs (auth, email, payment, shipping).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "4.9.1",
3
+ "version": "4.9.3",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"