@mohasinac/appkit 3.0.6 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +42 -0
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +86 -0
- package/dist/_internal/client/features/lottery/LotteryAdminSlotView.d.ts +14 -0
- package/dist/_internal/client/features/lottery/LotteryAdminSlotView.js +46 -0
- package/dist/_internal/client/features/lottery/LotteryDetailView.d.ts +28 -0
- package/dist/_internal/client/features/lottery/LotteryDetailView.js +17 -0
- package/dist/_internal/client/features/lottery/LotteryEntriesView.d.ts +23 -0
- package/dist/_internal/client/features/lottery/LotteryEntriesView.js +58 -0
- package/dist/_internal/client/features/lottery/LotteryListView.d.ts +21 -0
- package/dist/_internal/client/features/lottery/LotteryListView.js +25 -0
- package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +19 -0
- package/dist/_internal/client/features/lottery/LotteryPullForm.js +65 -0
- package/dist/_internal/client/features/lottery/LotterySlotGrid.d.ts +13 -0
- package/dist/_internal/client/features/lottery/LotterySlotGrid.js +21 -0
- package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.d.ts +28 -0
- package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.js +18 -0
- package/dist/_internal/server/features/auctions/__tests__/service.test.d.ts +1 -0
- package/dist/_internal/server/features/auctions/__tests__/service.test.js +140 -0
- package/dist/_internal/server/features/checkout/__tests__/actions.test.d.ts +1 -0
- package/dist/_internal/server/features/checkout/__tests__/actions.test.js +600 -0
- package/dist/_internal/server/features/lottery/actions.d.ts +43 -0
- package/dist/_internal/server/features/lottery/actions.js +243 -0
- package/dist/_internal/server/features/lottery/adapters.d.ts +9 -0
- package/dist/_internal/server/features/lottery/adapters.js +21 -0
- package/dist/_internal/server/features/lottery/data.d.ts +20 -0
- package/dist/_internal/server/features/lottery/data.js +41 -0
- package/dist/_internal/server/features/lottery/metadata.d.ts +6 -0
- package/dist/_internal/server/features/lottery/metadata.js +31 -0
- package/dist/_internal/server/features/refunds/__tests__/actions.test.d.ts +1 -0
- package/dist/_internal/server/features/refunds/__tests__/actions.test.js +263 -0
- package/dist/_internal/server/jobs/core/__tests__/auctionSettlement.test.d.ts +1 -0
- package/dist/_internal/server/jobs/core/__tests__/auctionSettlement.test.js +249 -0
- package/dist/_internal/server/jobs/core/__tests__/onBidPlaced.test.d.ts +1 -0
- package/dist/_internal/server/jobs/core/__tests__/onBidPlaced.test.js +201 -0
- package/dist/_internal/server/jobs/core/__tests__/onOrderCreate.test.d.ts +1 -0
- package/dist/_internal/server/jobs/core/__tests__/onOrderCreate.test.js +169 -0
- package/dist/_internal/server/jobs/core/__tests__/onOrderStatusChange.test.d.ts +1 -0
- package/dist/_internal/server/jobs/core/__tests__/onOrderStatusChange.test.js +148 -0
- package/dist/_internal/server/jobs/core/__tests__/onProductStockChange.test.d.ts +1 -0
- package/dist/_internal/server/jobs/core/__tests__/onProductStockChange.test.js +210 -0
- package/dist/_internal/server/jobs/core/__tests__/onReviewWrite.test.d.ts +1 -0
- package/dist/_internal/server/jobs/core/__tests__/onReviewWrite.test.js +190 -0
- package/dist/_internal/server/jobs/core/__tests__/onUserBanChange.test.d.ts +1 -0
- package/dist/_internal/server/jobs/core/__tests__/onUserBanChange.test.js +161 -0
- package/dist/_internal/server/jobs/core/__tests__/payoutBatch.test.d.ts +1 -0
- package/dist/_internal/server/jobs/core/__tests__/payoutBatch.test.js +302 -0
- package/dist/_internal/server/jobs/core/__tests__/triggerEventRaffle.test.d.ts +1 -0
- package/dist/_internal/server/jobs/core/__tests__/triggerEventRaffle.test.js +252 -0
- package/dist/_internal/server/jobs/core/__tests__/wrapJobHandler.test.d.ts +1 -0
- package/dist/_internal/server/jobs/core/__tests__/wrapJobHandler.test.js +129 -0
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +46 -0
- package/dist/_internal/shared/features/products/schema.d.ts +8 -8
- package/dist/client.d.ts +9 -0
- package/dist/client.js +9 -0
- package/dist/constants/field-names.d.ts +5 -1
- package/dist/constants/field-names.js +5 -1
- package/dist/features/addresses/repository/__tests__/addresses.repository.test.d.ts +1 -0
- package/dist/features/addresses/repository/__tests__/addresses.repository.test.js +386 -0
- package/dist/features/admin/api/bids/route.d.ts +2 -2
- package/dist/features/admin/api/coupons/route.d.ts +2 -2
- package/dist/features/admin/api/products/route.d.ts +2 -2
- package/dist/features/admin/api/reviews/route.d.ts +2 -2
- package/dist/features/admin/repository/__tests__/notification.repository.test.d.ts +1 -0
- package/dist/features/admin/repository/__tests__/notification.repository.test.js +356 -0
- package/dist/features/admin/repository/__tests__/site-settings.repository.test.d.ts +1 -0
- package/dist/features/admin/repository/__tests__/site-settings.repository.test.js +298 -0
- package/dist/features/auctions/repository/__tests__/bid.repository.test.d.ts +1 -0
- package/dist/features/auctions/repository/__tests__/bid.repository.test.js +290 -0
- package/dist/features/auth/api/route.d.ts +2 -2
- package/dist/features/auth/repository/__tests__/session.repository.test.d.ts +1 -0
- package/dist/features/auth/repository/__tests__/session.repository.test.js +310 -0
- package/dist/features/auth/repository/__tests__/session.test.d.ts +1 -0
- package/dist/features/auth/repository/__tests__/session.test.js +146 -0
- package/dist/features/auth/repository/__tests__/sms-counter.repository.test.d.ts +1 -0
- package/dist/features/auth/repository/__tests__/sms-counter.repository.test.js +171 -0
- package/dist/features/auth/repository/__tests__/sms-counter.test.d.ts +1 -0
- package/dist/features/auth/repository/__tests__/sms-counter.test.js +88 -0
- package/dist/features/auth/repository/__tests__/token.repository.test.d.ts +1 -0
- package/dist/features/auth/repository/__tests__/token.repository.test.js +260 -0
- package/dist/features/auth/repository/__tests__/token.test.d.ts +1 -0
- package/dist/features/auth/repository/__tests__/token.test.js +140 -0
- package/dist/features/auth/repository/__tests__/user.repository.test.d.ts +1 -0
- package/dist/features/auth/repository/__tests__/user.repository.test.js +352 -0
- package/dist/features/auth/repository/__tests__/user.test.d.ts +1 -0
- package/dist/features/auth/repository/__tests__/user.test.js +226 -0
- package/dist/features/auth/repository/session.repository.js +10 -1
- package/dist/features/cart/repository/__tests__/cart.repository.test.d.ts +1 -0
- package/dist/features/cart/repository/__tests__/cart.repository.test.js +371 -0
- package/dist/features/cart/repository/cart.repository.js +2 -2
- package/dist/features/categories/api/[id]/route.d.ts +4 -4
- package/dist/features/categories/api/route.d.ts +2 -2
- package/dist/features/categories/repository/__tests__/categories.repository.test.d.ts +1 -0
- package/dist/features/categories/repository/__tests__/categories.repository.test.js +813 -0
- package/dist/features/events/components/EventCard.js +1 -0
- package/dist/features/events/repository/__tests__/event.repository.test.d.ts +1 -0
- package/dist/features/events/repository/__tests__/event.repository.test.js +299 -0
- package/dist/features/events/schemas/firestore.d.ts +3 -0
- package/dist/features/events/schemas/index.d.ts +834 -14
- package/dist/features/events/schemas/index.js +80 -1
- package/dist/features/events/types/index.d.ts +4 -1
- package/dist/features/faq/repository/__tests__/faqs.repository.test.d.ts +1 -0
- package/dist/features/faq/repository/__tests__/faqs.repository.test.js +311 -0
- package/dist/features/history/repository/__tests__/user-history.repository.test.d.ts +1 -0
- package/dist/features/history/repository/__tests__/user-history.repository.test.js +180 -0
- package/dist/features/homepage/api/[id]/route.d.ts +4 -4
- package/dist/features/homepage/api/carousel/[id]/route.d.ts +4 -4
- package/dist/features/homepage/api/carousel/route.d.ts +2 -2
- package/dist/features/homepage/api/route.d.ts +2 -2
- package/dist/features/homepage/repository/__tests__/carousel.repository.test.d.ts +1 -0
- package/dist/features/homepage/repository/__tests__/carousel.repository.test.js +182 -0
- package/dist/features/homepage/repository/__tests__/homepage-sections.repository.test.d.ts +1 -0
- package/dist/features/homepage/repository/__tests__/homepage-sections.repository.test.js +185 -0
- package/dist/features/lottery/repository/lottery-entry.repository.d.ts +33 -0
- package/dist/features/lottery/repository/lottery-entry.repository.js +130 -0
- package/dist/features/lottery/schemas/firestore.d.ts +49 -0
- package/dist/features/lottery/schemas/firestore.js +25 -0
- package/dist/features/lottery/schemas/zod.d.ts +113 -0
- package/dist/features/lottery/schemas/zod.js +39 -0
- package/dist/features/lottery/types/index.d.ts +67 -0
- package/dist/features/lottery/types/index.js +55 -0
- package/dist/features/messages/repository/__tests__/conversations.repository.test.d.ts +1 -0
- package/dist/features/messages/repository/__tests__/conversations.repository.test.js +257 -0
- package/dist/features/orders/repository/__tests__/orders.repository.test.d.ts +1 -0
- package/dist/features/orders/repository/__tests__/orders.repository.test.js +211 -0
- package/dist/features/payments/repository/__tests__/payout.repository.test.d.ts +1 -0
- package/dist/features/payments/repository/__tests__/payout.repository.test.js +292 -0
- package/dist/features/products/api/[id]/route.d.ts +4 -4
- package/dist/features/products/api/route.d.ts +2 -2
- package/dist/features/products/repository/__tests__/product-features.repository.test.d.ts +1 -0
- package/dist/features/products/repository/__tests__/product-features.repository.test.js +329 -0
- package/dist/features/products/repository/__tests__/products.repository.test.d.ts +1 -0
- package/dist/features/products/repository/__tests__/products.repository.test.js +607 -0
- package/dist/features/products/schemas/firestore.d.ts +15 -1
- package/dist/features/products/schemas/index.d.ts +12 -12
- package/dist/features/products/schemas/index.js +4 -2
- package/dist/features/products/types/index.d.ts +1 -1
- package/dist/features/promotions/repository/__tests__/claimed-coupons.repository.test.d.ts +1 -0
- package/dist/features/promotions/repository/__tests__/claimed-coupons.repository.test.js +219 -0
- package/dist/features/promotions/repository/__tests__/coupons.repository.test.d.ts +1 -0
- package/dist/features/promotions/repository/__tests__/coupons.repository.test.js +480 -0
- package/dist/features/reviews/api/[id]/route.d.ts +4 -4
- package/dist/features/reviews/api/route.d.ts +2 -2
- package/dist/features/reviews/repository/__tests__/reviews.repository.test.d.ts +1 -0
- package/dist/features/reviews/repository/__tests__/reviews.repository.test.js +327 -0
- package/dist/features/scams/repository/__tests__/scammer.repository.test.d.ts +1 -0
- package/dist/features/scams/repository/__tests__/scammer.repository.test.js +385 -0
- package/dist/features/search/repository/__tests__/search.repository.test.d.ts +1 -0
- package/dist/features/search/repository/__tests__/search.repository.test.js +369 -0
- package/dist/features/seller/api/coupons/route.d.ts +2 -2
- package/dist/features/seller/api/offers/route.d.ts +2 -2
- package/dist/features/seller/api/products/route.d.ts +2 -2
- package/dist/features/seller/api/store/route.d.ts +2 -2
- package/dist/features/seller/repository/__tests__/offer.repository.test.d.ts +1 -0
- package/dist/features/seller/repository/__tests__/offer.repository.test.js +458 -0
- package/dist/features/stores/repository/__tests__/store.repository.test.d.ts +1 -0
- package/dist/features/stores/repository/__tests__/store.repository.test.js +261 -0
- package/dist/features/support/repository/__tests__/support.repository.test.d.ts +1 -0
- package/dist/features/support/repository/__tests__/support.repository.test.js +312 -0
- package/dist/features/wishlist/repository/__tests__/user-wishlist.repository.test.d.ts +1 -0
- package/dist/features/wishlist/repository/__tests__/user-wishlist.repository.test.js +139 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +17 -0
- package/dist/next/api/routeHandler.d.ts +1 -1
- package/dist/next/routing/route-map.d.ts +13 -0
- package/dist/next/routing/route-map.js +6 -0
- package/dist/repositories/index.d.ts +1 -0
- package/dist/repositories/index.js +2 -0
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/pii-schemas.d.ts +2 -0
- package/dist/security/pii-schemas.js +6 -0
- package/dist/seed/actions/demo-seed-actions.d.ts +1 -1
- package/dist/seed/events-seed-data.js +85 -2
- package/dist/seed/grouped-listings-seed-data.d.ts +3 -1
- package/dist/seed/index.d.ts +1 -0
- package/dist/seed/index.js +1 -0
- package/dist/seed/lottery-entries-seed-data.d.ts +2 -0
- package/dist/seed/lottery-entries-seed-data.js +108 -0
- package/dist/seed/manifest.js +1 -0
- package/dist/seed/products-preorders-seed-data.d.ts +3 -1
- package/dist/server.d.ts +6 -0
- package/dist/server.js +6 -0
- package/dist/styles.css +1 -1
- package/dist/tailwind-utilities.css +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { makeMockDb, makeSnap, makeQuerySnap } from "../../../../../tests/helpers/mock-firestore";
|
|
3
|
+
const { db, mockDocRef, mockCollection, mockQuery, mockBatch } = makeMockDb();
|
|
4
|
+
// Hoist schema mocks so vi.mock factories can reference them before module init
|
|
5
|
+
const { mockIsCouponValid, mockCanUserUseCoupon, mockCalculateDiscount, mockIsValidCouponCode, } = vi.hoisted(() => ({
|
|
6
|
+
mockIsCouponValid: vi.fn().mockReturnValue(true),
|
|
7
|
+
mockCanUserUseCoupon: vi.fn().mockReturnValue(true),
|
|
8
|
+
mockCalculateDiscount: vi.fn().mockReturnValue(100),
|
|
9
|
+
mockIsValidCouponCode: vi.fn().mockReturnValue(true),
|
|
10
|
+
}));
|
|
11
|
+
vi.mock("../../../../providers/db-firebase/admin", () => ({
|
|
12
|
+
getAdminDb: () => db,
|
|
13
|
+
}));
|
|
14
|
+
vi.mock("../../../../providers/db-firebase", async (importOriginal) => {
|
|
15
|
+
const actual = await importOriginal();
|
|
16
|
+
return {
|
|
17
|
+
...actual,
|
|
18
|
+
getFirestoreCount: vi.fn().mockResolvedValue(0),
|
|
19
|
+
prepareForFirestore: (d) => d,
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
vi.mock("../../../../contracts/field-ops", async (importOriginal) => {
|
|
23
|
+
const actual = await importOriginal();
|
|
24
|
+
return {
|
|
25
|
+
...actual,
|
|
26
|
+
increment: (n) => n,
|
|
27
|
+
serverTimestamp: () => new Date(),
|
|
28
|
+
arrayUnion: (...a) => a,
|
|
29
|
+
arrayRemove: (...a) => a,
|
|
30
|
+
deleteField: () => null,
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
vi.mock("../../../../monitoring", () => ({
|
|
34
|
+
serverLogger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
|
35
|
+
}));
|
|
36
|
+
vi.mock("../../../../errors/normalize", () => ({
|
|
37
|
+
normalizeError: vi.fn(),
|
|
38
|
+
}));
|
|
39
|
+
// Override schema validation functions so tests control branching without date arithmetic.
|
|
40
|
+
// Path is ../../schemas (2 levels up from __tests__) to reach promotions/schemas.
|
|
41
|
+
vi.mock("../../schemas", async (importOriginal) => {
|
|
42
|
+
const actual = await importOriginal();
|
|
43
|
+
return {
|
|
44
|
+
...actual,
|
|
45
|
+
isCouponValid: mockIsCouponValid,
|
|
46
|
+
canUserUseCoupon: mockCanUserUseCoupon,
|
|
47
|
+
calculateDiscount: mockCalculateDiscount,
|
|
48
|
+
isValidCouponCode: mockIsValidCouponCode,
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
import { CouponsRepository } from "../coupons.repository";
|
|
52
|
+
const repo = new CouponsRepository();
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Helpers
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
function makeCoupon(overrides = {}) {
|
|
57
|
+
return {
|
|
58
|
+
id: "coupon-welcome10",
|
|
59
|
+
code: "WELCOME10",
|
|
60
|
+
name: "Welcome 10",
|
|
61
|
+
type: "percentage",
|
|
62
|
+
scope: "admin",
|
|
63
|
+
discount: { value: 10, maxDiscount: 500, minPurchase: 200 },
|
|
64
|
+
usage: { totalLimit: 100, perUserLimit: 1, currentUsage: 0 },
|
|
65
|
+
validity: {
|
|
66
|
+
isActive: true,
|
|
67
|
+
startDate: new Date(Date.now() - 86400000),
|
|
68
|
+
endDate: new Date(Date.now() + 86400000 * 30),
|
|
69
|
+
},
|
|
70
|
+
restrictions: {
|
|
71
|
+
combineWithSellerCoupons: true,
|
|
72
|
+
applicableProducts: [],
|
|
73
|
+
excludeProducts: [],
|
|
74
|
+
},
|
|
75
|
+
stats: { totalUses: 0, totalRevenue: 0, totalDiscount: 0 },
|
|
76
|
+
createdAt: new Date(),
|
|
77
|
+
updatedAt: new Date(),
|
|
78
|
+
...overrides,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const CART_ITEMS = [
|
|
82
|
+
{
|
|
83
|
+
productId: "product-a",
|
|
84
|
+
storeId: "store-A",
|
|
85
|
+
price: 1000,
|
|
86
|
+
quantity: 1,
|
|
87
|
+
listingType: "standard",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
productId: "product-b",
|
|
91
|
+
storeId: "store-B",
|
|
92
|
+
price: 500,
|
|
93
|
+
quantity: 2,
|
|
94
|
+
listingType: "auction",
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
beforeEach(() => {
|
|
98
|
+
vi.clearAllMocks();
|
|
99
|
+
// Re-establish chainability every test (clearAllMocks removes call history only, but be explicit)
|
|
100
|
+
db.collection.mockReturnValue(mockCollection);
|
|
101
|
+
mockCollection.doc.mockReturnValue(mockDocRef);
|
|
102
|
+
mockDocRef.collection.mockReturnValue(mockCollection);
|
|
103
|
+
mockQuery.where.mockReturnValue(mockQuery);
|
|
104
|
+
mockQuery.orderBy.mockReturnValue(mockQuery);
|
|
105
|
+
mockQuery.limit.mockReturnValue(mockQuery);
|
|
106
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([]));
|
|
107
|
+
mockDocRef.get.mockResolvedValue(makeSnap(null));
|
|
108
|
+
mockBatch.commit.mockResolvedValue(undefined);
|
|
109
|
+
// Schema function defaults — all pass by default
|
|
110
|
+
mockIsCouponValid.mockReturnValue(true);
|
|
111
|
+
mockCanUserUseCoupon.mockReturnValue(true);
|
|
112
|
+
mockCalculateDiscount.mockReturnValue(100);
|
|
113
|
+
mockIsValidCouponCode.mockReturnValue(true);
|
|
114
|
+
});
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
// getCouponByCode
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
describe("CouponsRepository.getCouponByCode", () => {
|
|
119
|
+
it("returns null when code does not exist (empty snapshot)", async () => {
|
|
120
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([]));
|
|
121
|
+
const result = await repo.getCouponByCode("NOTEXIST");
|
|
122
|
+
expect(result).toBeNull();
|
|
123
|
+
});
|
|
124
|
+
it("returns coupon document when code is found", async () => {
|
|
125
|
+
const coupon = makeCoupon();
|
|
126
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([{ id: coupon.id, data: coupon }]));
|
|
127
|
+
const result = await repo.getCouponByCode("WELCOME10");
|
|
128
|
+
expect(result).not.toBeNull();
|
|
129
|
+
expect(result?.code).toBe("WELCOME10");
|
|
130
|
+
});
|
|
131
|
+
it("normalizes lowercase input code to uppercase before querying", async () => {
|
|
132
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([]));
|
|
133
|
+
await repo.getCouponByCode("welcome10");
|
|
134
|
+
// COUPON_FIELDS.CODE maps to "code"; the value must be uppercased
|
|
135
|
+
expect(mockQuery.where).toHaveBeenCalledWith("code", "==", "WELCOME10");
|
|
136
|
+
});
|
|
137
|
+
it("uses limit(1) to avoid scanning the full collection", async () => {
|
|
138
|
+
await repo.getCouponByCode("ANYCODE");
|
|
139
|
+
expect(mockQuery.limit).toHaveBeenCalledWith(1);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// validateCoupon
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
describe("CouponsRepository.validateCoupon", () => {
|
|
146
|
+
it("invalid code format (isValidCouponCode=false) → { valid: false }", async () => {
|
|
147
|
+
mockIsValidCouponCode.mockReturnValue(false);
|
|
148
|
+
const result = await repo.validateCoupon("!BAD!", "user-1", 1000);
|
|
149
|
+
expect(result.valid).toBe(false);
|
|
150
|
+
expect(result.message).toMatch(/invalid/i);
|
|
151
|
+
});
|
|
152
|
+
it("code not found in Firestore → { valid: false, message contains 'not found' }", async () => {
|
|
153
|
+
mockIsValidCouponCode.mockReturnValue(true);
|
|
154
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([]));
|
|
155
|
+
const result = await repo.validateCoupon("FAKE99", "user-1", 1000);
|
|
156
|
+
expect(result.valid).toBe(false);
|
|
157
|
+
expect(result.message).toMatch(/not found/i);
|
|
158
|
+
});
|
|
159
|
+
it("isCouponValid=false (expired/inactive) → { valid: false, message contains 'not currently valid' }", async () => {
|
|
160
|
+
const coupon = makeCoupon();
|
|
161
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([{ id: coupon.id, data: coupon }]));
|
|
162
|
+
mockIsCouponValid.mockReturnValue(false);
|
|
163
|
+
mockDocRef.get.mockResolvedValue(makeSnap(null));
|
|
164
|
+
const result = await repo.validateCoupon("WELCOME10", "user-1", 1000);
|
|
165
|
+
expect(result.valid).toBe(false);
|
|
166
|
+
expect(result.message).toMatch(/not currently valid/i);
|
|
167
|
+
});
|
|
168
|
+
it("perUserLimit reached (canUserUseCoupon=false) → { valid: false, message contains 'usage limit' }", async () => {
|
|
169
|
+
const coupon = makeCoupon();
|
|
170
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([{ id: coupon.id, data: coupon }]));
|
|
171
|
+
mockDocRef.get.mockResolvedValue(makeSnap({ usageCount: 1 }));
|
|
172
|
+
mockCanUserUseCoupon.mockReturnValue(false);
|
|
173
|
+
const result = await repo.validateCoupon("WELCOME10", "user-1", 1000);
|
|
174
|
+
expect(result.valid).toBe(false);
|
|
175
|
+
expect(result.message).toMatch(/usage limit/i);
|
|
176
|
+
});
|
|
177
|
+
it("orderTotal < minPurchase → { valid: false, message contains 'Minimum purchase' }", async () => {
|
|
178
|
+
const coupon = makeCoupon({ discount: { value: 10, minPurchase: 5000 } });
|
|
179
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([{ id: coupon.id, data: coupon }]));
|
|
180
|
+
mockDocRef.get.mockResolvedValue(makeSnap(null));
|
|
181
|
+
const result = await repo.validateCoupon("WELCOME10", "user-1", 400); // 400 < 5000
|
|
182
|
+
expect(result.valid).toBe(false);
|
|
183
|
+
expect(result.message).toMatch(/minimum purchase/i);
|
|
184
|
+
});
|
|
185
|
+
it("orderTotal === minPurchase → passes the check", async () => {
|
|
186
|
+
const coupon = makeCoupon({ discount: { value: 10, minPurchase: 1000 } });
|
|
187
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([{ id: coupon.id, data: coupon }]));
|
|
188
|
+
mockDocRef.get.mockResolvedValue(makeSnap(null));
|
|
189
|
+
const result = await repo.validateCoupon("WELCOME10", "user-1", 1000);
|
|
190
|
+
expect(result.valid).toBe(true);
|
|
191
|
+
});
|
|
192
|
+
it("valid coupon → { valid: true, discountAmount from calculateDiscount }", async () => {
|
|
193
|
+
const coupon = makeCoupon();
|
|
194
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([{ id: coupon.id, data: coupon }]));
|
|
195
|
+
mockDocRef.get.mockResolvedValue(makeSnap(null));
|
|
196
|
+
mockCalculateDiscount.mockReturnValue(150);
|
|
197
|
+
const result = await repo.validateCoupon("WELCOME10", "user-1", 1500);
|
|
198
|
+
expect(result.valid).toBe(true);
|
|
199
|
+
expect(result.discountAmount).toBe(150);
|
|
200
|
+
});
|
|
201
|
+
it("valid coupon → includes coupon document in response", async () => {
|
|
202
|
+
const coupon = makeCoupon();
|
|
203
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([{ id: coupon.id, data: coupon }]));
|
|
204
|
+
mockDocRef.get.mockResolvedValue(makeSnap(null));
|
|
205
|
+
const result = await repo.validateCoupon("WELCOME10", "user-1", 1000);
|
|
206
|
+
expect(result.valid).toBe(true);
|
|
207
|
+
expect(result.coupon).toBeDefined();
|
|
208
|
+
});
|
|
209
|
+
it("coupon with no minPurchase → no minimum purchase check performed", async () => {
|
|
210
|
+
const coupon = makeCoupon({ discount: { value: 10 } }); // no minPurchase
|
|
211
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([{ id: coupon.id, data: coupon }]));
|
|
212
|
+
mockDocRef.get.mockResolvedValue(makeSnap(null));
|
|
213
|
+
const result = await repo.validateCoupon("WELCOME10", "user-1", 1); // tiny order
|
|
214
|
+
expect(result.valid).toBe(true);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
// validateCouponForCart
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
describe("CouponsRepository.validateCouponForCart", () => {
|
|
221
|
+
function setupCoupon(overrides = {}) {
|
|
222
|
+
const coupon = makeCoupon(overrides);
|
|
223
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([{ id: coupon.id, data: coupon }]));
|
|
224
|
+
mockDocRef.get.mockResolvedValue(makeSnap(null)); // no user usage
|
|
225
|
+
return coupon;
|
|
226
|
+
}
|
|
227
|
+
it("seller coupon + no items from that store → { valid: false }", async () => {
|
|
228
|
+
setupCoupon({
|
|
229
|
+
scope: "seller",
|
|
230
|
+
storeId: "store-X", // no cart items from store-X
|
|
231
|
+
restrictions: { applicableProducts: [], excludeProducts: [] },
|
|
232
|
+
});
|
|
233
|
+
const result = await repo.validateCouponForCart("STORESEL", "user-1", CART_ITEMS);
|
|
234
|
+
expect(result.valid).toBe(false);
|
|
235
|
+
});
|
|
236
|
+
it("seller coupon + items from that store → eligibleProductIds contains only store items", async () => {
|
|
237
|
+
setupCoupon({
|
|
238
|
+
scope: "seller",
|
|
239
|
+
storeId: "store-A",
|
|
240
|
+
restrictions: { applicableProducts: [], excludeProducts: [] },
|
|
241
|
+
});
|
|
242
|
+
const result = await repo.validateCouponForCart("STORESEL", "user-1", CART_ITEMS);
|
|
243
|
+
expect(result.valid).toBe(true);
|
|
244
|
+
expect(result.eligibleProductIds).toEqual(["product-a"]);
|
|
245
|
+
});
|
|
246
|
+
it("applicableToAuctions=true → filters to auction items only", async () => {
|
|
247
|
+
setupCoupon({
|
|
248
|
+
applicableToAuctions: true,
|
|
249
|
+
restrictions: { applicableProducts: [], excludeProducts: [] },
|
|
250
|
+
});
|
|
251
|
+
const result = await repo.validateCouponForCart("AUC10", "user-1", CART_ITEMS);
|
|
252
|
+
expect(result.valid).toBe(true);
|
|
253
|
+
expect(result.eligibleProductIds).toEqual(["product-b"]);
|
|
254
|
+
});
|
|
255
|
+
it("applicableToAuctions=false → filters out auction items", async () => {
|
|
256
|
+
setupCoupon({
|
|
257
|
+
applicableToAuctions: false,
|
|
258
|
+
restrictions: { applicableProducts: [], excludeProducts: [] },
|
|
259
|
+
});
|
|
260
|
+
const result = await repo.validateCouponForCart("NOAUC", "user-1", CART_ITEMS);
|
|
261
|
+
expect(result.valid).toBe(true);
|
|
262
|
+
expect(result.eligibleProductIds).toEqual(["product-a"]);
|
|
263
|
+
});
|
|
264
|
+
it("applicableProducts restriction → only those products are eligible", async () => {
|
|
265
|
+
setupCoupon({
|
|
266
|
+
restrictions: { applicableProducts: ["product-a"], excludeProducts: [] },
|
|
267
|
+
});
|
|
268
|
+
const result = await repo.validateCouponForCart("RESTR", "user-1", CART_ITEMS);
|
|
269
|
+
expect(result.valid).toBe(true);
|
|
270
|
+
expect(result.eligibleProductIds).toEqual(["product-a"]);
|
|
271
|
+
});
|
|
272
|
+
it("excludeProducts restriction → listed products removed from eligibility", async () => {
|
|
273
|
+
setupCoupon({
|
|
274
|
+
restrictions: { applicableProducts: [], excludeProducts: ["product-a"] },
|
|
275
|
+
});
|
|
276
|
+
const result = await repo.validateCouponForCart("EX10", "user-1", CART_ITEMS);
|
|
277
|
+
expect(result.valid).toBe(true);
|
|
278
|
+
expect(result.eligibleProductIds).not.toContain("product-a");
|
|
279
|
+
expect(result.eligibleProductIds).toContain("product-b");
|
|
280
|
+
});
|
|
281
|
+
it("all products excluded → { valid: false }", async () => {
|
|
282
|
+
setupCoupon({
|
|
283
|
+
restrictions: {
|
|
284
|
+
applicableProducts: [],
|
|
285
|
+
excludeProducts: ["product-a", "product-b"],
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
const result = await repo.validateCouponForCart("EX10", "user-1", CART_ITEMS);
|
|
289
|
+
expect(result.valid).toBe(false);
|
|
290
|
+
});
|
|
291
|
+
it("eligibleSubtotal < minPurchase → { valid: false, message: Minimum purchase }", async () => {
|
|
292
|
+
setupCoupon({
|
|
293
|
+
discount: { value: 10, minPurchase: 50000 }, // 50,000 paise >> eligible subtotal
|
|
294
|
+
restrictions: { applicableProducts: ["product-a"], excludeProducts: [] },
|
|
295
|
+
});
|
|
296
|
+
const result = await repo.validateCouponForCart("MINP", "user-1", CART_ITEMS);
|
|
297
|
+
expect(result.valid).toBe(false);
|
|
298
|
+
expect(result.message).toMatch(/minimum purchase/i);
|
|
299
|
+
});
|
|
300
|
+
it("calculateDiscount called with eligibleSubtotal not full cart total", async () => {
|
|
301
|
+
// product-a only eligible (price=1000, qty=1) — product-b excluded
|
|
302
|
+
setupCoupon({
|
|
303
|
+
restrictions: { applicableProducts: ["product-a"], excludeProducts: [] },
|
|
304
|
+
});
|
|
305
|
+
await repo.validateCouponForCart("SEL", "user-1", CART_ITEMS);
|
|
306
|
+
// eligibleSubtotal = 1000 (product-a price * qty)
|
|
307
|
+
expect(mockCalculateDiscount).toHaveBeenCalledWith(expect.anything(), 1000);
|
|
308
|
+
});
|
|
309
|
+
it("returns eligibleSubtotal and eligibleProductIds on success", async () => {
|
|
310
|
+
setupCoupon({
|
|
311
|
+
restrictions: { applicableProducts: [], excludeProducts: [] },
|
|
312
|
+
});
|
|
313
|
+
mockCalculateDiscount.mockReturnValue(200);
|
|
314
|
+
const result = await repo.validateCouponForCart("ALL", "user-1", CART_ITEMS);
|
|
315
|
+
expect(result.valid).toBe(true);
|
|
316
|
+
expect(result.eligibleSubtotal).toBeDefined();
|
|
317
|
+
expect(Array.isArray(result.eligibleProductIds)).toBe(true);
|
|
318
|
+
expect(result.eligibleProductIds?.length).toBeGreaterThan(0);
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
// ---------------------------------------------------------------------------
|
|
322
|
+
// applyCoupon
|
|
323
|
+
// ---------------------------------------------------------------------------
|
|
324
|
+
describe("CouponsRepository.applyCoupon", () => {
|
|
325
|
+
it("batch.update increments usage.currentUsage on coupon doc", async () => {
|
|
326
|
+
await repo.applyCoupon("coupon-1", "WELCOME10", "user-1", ["order-1"], 100);
|
|
327
|
+
expect(mockBatch.update).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({ "usage.currentUsage": 1 }));
|
|
328
|
+
});
|
|
329
|
+
it("batch.update increments stats.totalUses on coupon doc", async () => {
|
|
330
|
+
await repo.applyCoupon("coupon-1", "WELCOME10", "user-1", ["order-1"], 100);
|
|
331
|
+
expect(mockBatch.update).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({ "stats.totalUses": 1 }));
|
|
332
|
+
});
|
|
333
|
+
it("batch.update increments stats.totalDiscount by the discountAmount", async () => {
|
|
334
|
+
await repo.applyCoupon("coupon-1", "WELCOME10", "user-1", ["order-1"], 250);
|
|
335
|
+
expect(mockBatch.update).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({ "stats.totalDiscount": 250 }));
|
|
336
|
+
});
|
|
337
|
+
it("batch.set on couponUsage subcollection with merge: true", async () => {
|
|
338
|
+
await repo.applyCoupon("coupon-1", "WELCOME10", "user-1", ["order-1"], 100);
|
|
339
|
+
expect(mockBatch.set).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({
|
|
340
|
+
usageCount: 1,
|
|
341
|
+
userId: "user-1",
|
|
342
|
+
couponCode: "WELCOME10",
|
|
343
|
+
}), { merge: true });
|
|
344
|
+
});
|
|
345
|
+
it("batch.commit() called exactly once", async () => {
|
|
346
|
+
await repo.applyCoupon("coupon-1", "WELCOME10", "user-1", ["order-1"], 100);
|
|
347
|
+
expect(mockBatch.commit).toHaveBeenCalledOnce();
|
|
348
|
+
});
|
|
349
|
+
it("multiple orderIds are all passed to arrayUnion spread", async () => {
|
|
350
|
+
await repo.applyCoupon("coupon-1", "WELCOME10", "user-1", ["order-1", "order-2", "order-3"], 150);
|
|
351
|
+
const setCall = mockBatch.set.mock.calls[0];
|
|
352
|
+
const data = setCall[1];
|
|
353
|
+
// arrayUnion mock returns args as array; verify all orderIds are in there
|
|
354
|
+
const orders = data.orders;
|
|
355
|
+
expect(orders).toContain("order-1");
|
|
356
|
+
expect(orders).toContain("order-2");
|
|
357
|
+
expect(orders).toContain("order-3");
|
|
358
|
+
});
|
|
359
|
+
it("couponCode stored in uppercase on the usage doc", async () => {
|
|
360
|
+
await repo.applyCoupon("coupon-1", "welcome10", "user-1", ["order-1"], 100);
|
|
361
|
+
expect(mockBatch.set).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({ couponCode: "WELCOME10" }), { merge: true });
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
// ---------------------------------------------------------------------------
|
|
365
|
+
// getUserCouponUsageCount
|
|
366
|
+
// ---------------------------------------------------------------------------
|
|
367
|
+
describe("CouponsRepository.getUserCouponUsageCount", () => {
|
|
368
|
+
it("no usage doc → returns 0", async () => {
|
|
369
|
+
mockDocRef.get.mockResolvedValue(makeSnap(null)); // exists=false
|
|
370
|
+
const count = await repo.getUserCouponUsageCount("user-1", "coupon-1");
|
|
371
|
+
expect(count).toBe(0);
|
|
372
|
+
});
|
|
373
|
+
it("existing doc with usageCount=3 → returns 3", async () => {
|
|
374
|
+
mockDocRef.get.mockResolvedValue(makeSnap({ usageCount: 3 }));
|
|
375
|
+
const count = await repo.getUserCouponUsageCount("user-1", "coupon-1");
|
|
376
|
+
expect(count).toBe(3);
|
|
377
|
+
});
|
|
378
|
+
it("existing doc with no usageCount field → defaults to 1 (legacy record)", async () => {
|
|
379
|
+
mockDocRef.get.mockResolvedValue(makeSnap({})); // exists=true, usageCount absent
|
|
380
|
+
const count = await repo.getUserCouponUsageCount("user-1", "coupon-1");
|
|
381
|
+
expect(count).toBe(1);
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
// ---------------------------------------------------------------------------
|
|
385
|
+
// getActiveCoupons
|
|
386
|
+
// ---------------------------------------------------------------------------
|
|
387
|
+
describe("CouponsRepository.getActiveCoupons", () => {
|
|
388
|
+
it("queries where validity.isActive == true", async () => {
|
|
389
|
+
await repo.getActiveCoupons();
|
|
390
|
+
expect(mockQuery.where).toHaveBeenCalledWith("validity.isActive", "==", true);
|
|
391
|
+
});
|
|
392
|
+
it("coupon returned by Firestore but isCouponValid=false → filtered out in-memory", async () => {
|
|
393
|
+
const coupon = makeCoupon();
|
|
394
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([{ id: coupon.id, data: coupon }]));
|
|
395
|
+
mockIsCouponValid.mockReturnValue(false);
|
|
396
|
+
const result = await repo.getActiveCoupons();
|
|
397
|
+
expect(result).toHaveLength(0);
|
|
398
|
+
});
|
|
399
|
+
it("coupon where isCouponValid=true → included in result", async () => {
|
|
400
|
+
const coupon = makeCoupon();
|
|
401
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([{ id: coupon.id, data: coupon }]));
|
|
402
|
+
mockIsCouponValid.mockReturnValue(true);
|
|
403
|
+
const result = await repo.getActiveCoupons();
|
|
404
|
+
expect(result).toHaveLength(1);
|
|
405
|
+
});
|
|
406
|
+
it("empty snapshot → returns []", async () => {
|
|
407
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([]));
|
|
408
|
+
const result = await repo.getActiveCoupons();
|
|
409
|
+
expect(result).toHaveLength(0);
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
// ---------------------------------------------------------------------------
|
|
413
|
+
// getStoreCoupons
|
|
414
|
+
// ---------------------------------------------------------------------------
|
|
415
|
+
describe("CouponsRepository.getStoreCoupons", () => {
|
|
416
|
+
it("queries where storeId matches provided store", async () => {
|
|
417
|
+
await repo.getStoreCoupons("store-X");
|
|
418
|
+
expect(mockQuery.where).toHaveBeenCalledWith("storeId", "==", "store-X");
|
|
419
|
+
});
|
|
420
|
+
it("returns empty array when no coupons exist for store", async () => {
|
|
421
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([]));
|
|
422
|
+
const result = await repo.getStoreCoupons("store-unknown");
|
|
423
|
+
expect(result).toHaveLength(0);
|
|
424
|
+
});
|
|
425
|
+
it("returns all coupons for the store", async () => {
|
|
426
|
+
const c1 = makeCoupon({ id: "coupon-1", code: "STORE10" });
|
|
427
|
+
const c2 = makeCoupon({ id: "coupon-2", code: "STORE20" });
|
|
428
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([
|
|
429
|
+
{ id: "coupon-1", data: c1 },
|
|
430
|
+
{ id: "coupon-2", data: c2 },
|
|
431
|
+
]));
|
|
432
|
+
const result = await repo.getStoreCoupons("store-A");
|
|
433
|
+
expect(result).toHaveLength(2);
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
// ---------------------------------------------------------------------------
|
|
437
|
+
// getExpiredActiveRefs
|
|
438
|
+
// ---------------------------------------------------------------------------
|
|
439
|
+
describe("CouponsRepository.getExpiredActiveRefs", () => {
|
|
440
|
+
it("queries where validity.isActive==true and validity.endDate<=now", async () => {
|
|
441
|
+
const now = new Date();
|
|
442
|
+
await repo.getExpiredActiveRefs(now);
|
|
443
|
+
expect(mockQuery.where).toHaveBeenCalledWith("validity.isActive", "==", true);
|
|
444
|
+
expect(mockQuery.where).toHaveBeenCalledWith("validity.endDate", "<=", now);
|
|
445
|
+
});
|
|
446
|
+
it("returns doc refs for expired active coupons", async () => {
|
|
447
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([
|
|
448
|
+
{ id: "coupon-expired-1", data: {} },
|
|
449
|
+
{ id: "coupon-expired-2", data: {} },
|
|
450
|
+
]));
|
|
451
|
+
const refs = await repo.getExpiredActiveRefs(new Date());
|
|
452
|
+
expect(refs).toHaveLength(2);
|
|
453
|
+
});
|
|
454
|
+
it("empty snapshot → returns empty array", async () => {
|
|
455
|
+
mockQuery.get.mockResolvedValue(makeQuerySnap([]));
|
|
456
|
+
const refs = await repo.getExpiredActiveRefs(new Date());
|
|
457
|
+
expect(refs).toHaveLength(0);
|
|
458
|
+
});
|
|
459
|
+
it("limits query to 500 results", async () => {
|
|
460
|
+
await repo.getExpiredActiveRefs(new Date());
|
|
461
|
+
expect(mockQuery.limit).toHaveBeenCalledWith(500);
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
// ---------------------------------------------------------------------------
|
|
465
|
+
// deactivateInBatch
|
|
466
|
+
// ---------------------------------------------------------------------------
|
|
467
|
+
describe("CouponsRepository.deactivateInBatch", () => {
|
|
468
|
+
it("stages update { validity.isActive: false } on the ref in caller's batch", () => {
|
|
469
|
+
const ref = { id: "coupon-old" };
|
|
470
|
+
const batch = mockBatch;
|
|
471
|
+
repo.deactivateInBatch(batch, ref);
|
|
472
|
+
expect(mockBatch.update).toHaveBeenCalledWith(ref, expect.objectContaining({ "validity.isActive": false }));
|
|
473
|
+
});
|
|
474
|
+
it("does NOT call batch.commit() — caller owns the commit", () => {
|
|
475
|
+
const ref = { id: "coupon-old" };
|
|
476
|
+
const batch = mockBatch;
|
|
477
|
+
repo.deactivateInBatch(batch, ref);
|
|
478
|
+
expect(mockBatch.commit).not.toHaveBeenCalled();
|
|
479
|
+
});
|
|
480
|
+
});
|
|
@@ -15,9 +15,9 @@ export declare function GET(_request: Request, context: {
|
|
|
15
15
|
}>;
|
|
16
16
|
}): Promise<NextResponse>;
|
|
17
17
|
export { GET as reviewItemGET };
|
|
18
|
-
export declare const reviewItemPATCH: (request: Request, context
|
|
18
|
+
export declare const reviewItemPATCH: (request: Request, context?: {
|
|
19
19
|
params: Promise<Record<string, string>>;
|
|
20
|
-
}) => Promise<Response>;
|
|
21
|
-
export declare const reviewItemDELETE: (request: Request, context
|
|
20
|
+
} | undefined) => Promise<Response>;
|
|
21
|
+
export declare const reviewItemDELETE: (request: Request, context?: {
|
|
22
22
|
params: Promise<Record<string, string>>;
|
|
23
|
-
}) => Promise<Response>;
|
|
23
|
+
} | undefined) => Promise<Response>;
|
|
@@ -16,6 +16,6 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { NextResponse } from "next/server.js";
|
|
18
18
|
export declare function GET(request: Request): Promise<NextResponse>;
|
|
19
|
-
export declare const POST: (request: Request, context
|
|
19
|
+
export declare const POST: (request: Request, context?: {
|
|
20
20
|
params: Promise<Record<string, string>>;
|
|
21
|
-
}) => Promise<Response>;
|
|
21
|
+
} | undefined) => Promise<Response>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|