@mohasinac/appkit 4.35.0 → 4.35.1
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/features/auth/actions/profile-actions.d.ts +14 -1
- package/dist/features/auth/actions/profile-actions.js +16 -2
- package/dist/features/tester/seed-data/carts-tester-seed-data.d.ts +3 -0
- package/dist/features/tester/seed-data/carts-tester-seed-data.js +70 -0
- package/dist/features/tester/seed-data/index.d.ts +1 -0
- package/dist/features/tester/seed-data/index.js +1 -0
- package/dist/seed/cart-seed-data.js +11 -0
- package/dist/seed/index.d.ts +1 -1
- package/dist/seed/index.js +1 -1
- package/dist/seed/manifest.js +2 -2
- package/dist/seed/notifications-seed-data.js +5 -1
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +752 -752
|
@@ -39,7 +39,20 @@ export declare function getUserSessions(userId: string): Promise<import("..").Se
|
|
|
39
39
|
* including ones inside sent notification emails.
|
|
40
40
|
*/
|
|
41
41
|
export declare function resolveProfileUser(identifier: string): Promise<UserDocument | null>;
|
|
42
|
-
|
|
42
|
+
/**
|
|
43
|
+
* 🛑 React.cache-wrapped because a page and its generateMetadata both read it.
|
|
44
|
+
*
|
|
45
|
+
* `resolveProfileUser` tries up to THREE lookups (slug, id, uid), and
|
|
46
|
+
* `profile/[userId]/page.tsx` called this once in generateMetadata and again in
|
|
47
|
+
* the page body — so an uncached profile view cost up to six Firestore reads to
|
|
48
|
+
* answer one question. `sellers/[id]` had the same pair.
|
|
49
|
+
*
|
|
50
|
+
* This is the rule the SSR guide already states: every data-fetch exposed to a
|
|
51
|
+
* page + generateMetadata pair is memoised per argument list, so the second call
|
|
52
|
+
* is free. Note it caches the NARROW public projection, which is what both
|
|
53
|
+
* callers want; anything needing the whole document still goes to the repository.
|
|
54
|
+
*/
|
|
55
|
+
export declare const getPublicUserProfile: (userId: string) => Promise<Pick<UserDocument, "id" | "slug" | "displayName" | "photoURL" | "role" | "createdAt" | "storeId" | "storeSlug" | "publicProfile" | "stats"> | null>;
|
|
43
56
|
/** Fetch approved reviews for a store. storeId === storeSlug in this project. */
|
|
44
57
|
export declare function getSellerReviews(storeId: string): Promise<Review[]>;
|
|
45
58
|
export declare function getProfileStoreProducts(storeId: string): Promise<import("../../products").ProductDocument[]>;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Pure business functions for profile CRUD, seller stats, and session reads.
|
|
5
5
|
* Auth, rate-limiting, and Next.js specifics are handled by the consumer.
|
|
6
6
|
*/
|
|
7
|
+
import { cache } from "react";
|
|
7
8
|
import { maskPublicReview } from "../../../security";
|
|
8
9
|
import { safeRead } from "../../../errors/safe-read";
|
|
9
10
|
import { finalizeStagedMediaField } from "../../media/finalize";
|
|
@@ -47,7 +48,20 @@ export async function resolveProfileUser(identifier) {
|
|
|
47
48
|
return byId;
|
|
48
49
|
return userRepository.findByUid(identifier);
|
|
49
50
|
}
|
|
50
|
-
|
|
51
|
+
/**
|
|
52
|
+
* 🛑 React.cache-wrapped because a page and its generateMetadata both read it.
|
|
53
|
+
*
|
|
54
|
+
* `resolveProfileUser` tries up to THREE lookups (slug, id, uid), and
|
|
55
|
+
* `profile/[userId]/page.tsx` called this once in generateMetadata and again in
|
|
56
|
+
* the page body — so an uncached profile view cost up to six Firestore reads to
|
|
57
|
+
* answer one question. `sellers/[id]` had the same pair.
|
|
58
|
+
*
|
|
59
|
+
* This is the rule the SSR guide already states: every data-fetch exposed to a
|
|
60
|
+
* page + generateMetadata pair is memoised per argument list, so the second call
|
|
61
|
+
* is free. Note it caches the NARROW public projection, which is what both
|
|
62
|
+
* callers want; anything needing the whole document still goes to the repository.
|
|
63
|
+
*/
|
|
64
|
+
export const getPublicUserProfile = cache(async function getPublicUserProfile(userId) {
|
|
51
65
|
const user = await resolveProfileUser(userId);
|
|
52
66
|
if (!user)
|
|
53
67
|
return null;
|
|
@@ -63,7 +77,7 @@ export async function getPublicUserProfile(userId) {
|
|
|
63
77
|
publicProfile: user.publicProfile,
|
|
64
78
|
stats: user.stats,
|
|
65
79
|
};
|
|
66
|
-
}
|
|
80
|
+
});
|
|
67
81
|
/** Fetch approved reviews for a store. storeId === storeSlug in this project. */
|
|
68
82
|
export async function getSellerReviews(storeId) {
|
|
69
83
|
const snapshot = await safeRead(() => reviewRepository.findApprovedByStore(storeId), { route: "/profile/[userId]", key: "reviews.findApprovedByStore", fallback: [] });
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: Shared tester sandbox — the LOCKED cart line a settled auction leaves behind.
|
|
3
|
+
* WHAT: One cart for user-tester-qa holding the win from auction-tester-sandbox-won.
|
|
4
|
+
*
|
|
5
|
+
* 🛑 WHY THIS FILE HAD TO EXIST.
|
|
6
|
+
*
|
|
7
|
+
* The sandbox seeded a won auction, its winning bid, and a delivered order made
|
|
8
|
+
* from it — the shape settlement produced BEFORE Root Cause #60, when it wrote an
|
|
9
|
+
* order document directly. That order was in a shape no orders list could render
|
|
10
|
+
* and no checkout could accept, so a winner had no way to pay at all; the fix
|
|
11
|
+
* routed a win through the CART as a locked line instead.
|
|
12
|
+
*
|
|
13
|
+
* The fixtures were never updated to match. So `win-auction` — the case whose
|
|
14
|
+
* label is literally "creates a payable locked cart line, not a stuck order" —
|
|
15
|
+
* could not pass: /user/bids offered "Pay now", it led to /checkout?lane=auction,
|
|
16
|
+
* and the cart was empty. Found by the tester 2026-09-06, which is exactly the
|
|
17
|
+
* kind of thing a case can only prove by being run.
|
|
18
|
+
*
|
|
19
|
+
* The delivered order fixture STAYS. It is the "win already completed" state and
|
|
20
|
+
* other cases read it. This adds the missing "win awaiting payment" state.
|
|
21
|
+
*
|
|
22
|
+
* 🛑 `isAuctionWin` is not decoration. `laneOf()` keys on `isAuctionWin || bidId`
|
|
23
|
+
* and ignores `listingType` and `locked` entirely — a line without it lands in the
|
|
24
|
+
* STANDARD lane, so the Won Auctions tab stays empty while a non-removable line
|
|
25
|
+
* sits in the ordinary cart. Both fields are set here so the lane is unambiguous.
|
|
26
|
+
*
|
|
27
|
+
* @tag domain:tester
|
|
28
|
+
* @tag layer:seed
|
|
29
|
+
* @tag pattern:none
|
|
30
|
+
* @tag access:server-only
|
|
31
|
+
* @tag consumers:seed/index.ts,seed/manifest.ts
|
|
32
|
+
* @tag sideEffects:none
|
|
33
|
+
*/
|
|
34
|
+
import { testDataExpiresAt } from "./tester-ttl";
|
|
35
|
+
const NOW = new Date();
|
|
36
|
+
const hoursAgo = (n) => new Date(NOW.getTime() - n * 3600000);
|
|
37
|
+
/** The cart document id IS the user id — one cart per account. */
|
|
38
|
+
export const cartsTesterSeedData = [
|
|
39
|
+
{
|
|
40
|
+
id: "user-tester-qa",
|
|
41
|
+
userId: "user-tester-qa",
|
|
42
|
+
items: [
|
|
43
|
+
{
|
|
44
|
+
itemId: "cartitem-tester-sandbox-auction-won",
|
|
45
|
+
productId: "auction-tester-sandbox-won",
|
|
46
|
+
productTitle: "Test Auction — Already Won",
|
|
47
|
+
price: 15000,
|
|
48
|
+
currency: "INR",
|
|
49
|
+
quantity: 1,
|
|
50
|
+
storeId: "store-tester-qa-seller",
|
|
51
|
+
storeName: "Tester QA Seller",
|
|
52
|
+
listingType: "auction",
|
|
53
|
+
// Both, deliberately — see the lane note in the header.
|
|
54
|
+
isAuctionWin: true,
|
|
55
|
+
bidId: "bid-tester-sandbox-won-tester-qa",
|
|
56
|
+
// A win is non-removable and fixed-quantity: settlement is the only writer
|
|
57
|
+
// of this line, and declining to pay is handled by the forfeit sweep rather
|
|
58
|
+
// than by the buyer deleting it.
|
|
59
|
+
locked: true,
|
|
60
|
+
lockedPrice: 15000,
|
|
61
|
+
addedAt: hoursAgo(2),
|
|
62
|
+
updatedAt: hoursAgo(2),
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
isTestData: true,
|
|
66
|
+
testDataExpiresAt: testDataExpiresAt(),
|
|
67
|
+
createdAt: hoursAgo(2),
|
|
68
|
+
updatedAt: hoursAgo(2),
|
|
69
|
+
},
|
|
70
|
+
];
|
|
@@ -6,5 +6,6 @@ export { blogTesterSeedData } from "./blog-tester-seed-data";
|
|
|
6
6
|
export { eventsTesterSeedData } from "./events-tester-seed-data";
|
|
7
7
|
export { couponsTesterSeedData } from "./coupons-tester-seed-data";
|
|
8
8
|
export { bidsTesterSeedData } from "./bids-tester-seed-data";
|
|
9
|
+
export { cartsTesterSeedData } from "./carts-tester-seed-data";
|
|
9
10
|
export { ordersTesterSeedData } from "./orders-tester-seed-data";
|
|
10
11
|
export { offersTesterSeedData } from "./offers-tester-seed-data";
|
|
@@ -6,5 +6,6 @@ export { blogTesterSeedData } from "./blog-tester-seed-data";
|
|
|
6
6
|
export { eventsTesterSeedData } from "./events-tester-seed-data";
|
|
7
7
|
export { couponsTesterSeedData } from "./coupons-tester-seed-data";
|
|
8
8
|
export { bidsTesterSeedData } from "./bids-tester-seed-data";
|
|
9
|
+
export { cartsTesterSeedData } from "./carts-tester-seed-data";
|
|
9
10
|
export { ordersTesterSeedData } from "./orders-tester-seed-data";
|
|
10
11
|
export { offersTesterSeedData } from "./offers-tester-seed-data";
|
|
@@ -76,6 +76,17 @@ export const cartsSeedData = [
|
|
|
76
76
|
storeId: "store-beyblade-arena",
|
|
77
77
|
storeName: "Beyblade Arena",
|
|
78
78
|
listingType: "auction",
|
|
79
|
+
/*
|
|
80
|
+
* 🛑 `isAuctionWin` is what puts this line in the AUCTION lane.
|
|
81
|
+
*
|
|
82
|
+
* `laneOf()` keys on `isAuctionWin || bidId` — `listingType: "auction"`
|
|
83
|
+
* and `locked: true` mean nothing to it. Without this flag the line fell
|
|
84
|
+
* into the STANDARD lane, so the cart's "Won Auctions" tab was empty
|
|
85
|
+
* while a non-removable auction line sat in the ordinary cart inflating
|
|
86
|
+
* its total, and the lane gate never blocked anything. Found by the
|
|
87
|
+
* tester 2026-09-06.
|
|
88
|
+
*/
|
|
89
|
+
isAuctionWin: true,
|
|
79
90
|
locked: true,
|
|
80
91
|
addedAt: daysAgo(1),
|
|
81
92
|
updatedAt: daysAgo(1),
|
package/dist/seed/index.d.ts
CHANGED
|
@@ -78,7 +78,7 @@ export { historySeedData } from "./history-seed-data";
|
|
|
78
78
|
export { groupedListingsSeedData } from "./grouped-listings-seed-data";
|
|
79
79
|
export { scammersSeedData } from "./scammers-seed-data";
|
|
80
80
|
export { supportTicketsSeedData } from "./support-tickets-seed-data";
|
|
81
|
-
export { testerChecklistSeedData, storesTesterSeedData, categoriesTesterSeedData, productsTesterSeedData, blogTesterSeedData, eventsTesterSeedData, couponsTesterSeedData, bidsTesterSeedData, ordersTesterSeedData, } from "../features/tester/seed-data";
|
|
81
|
+
export { testerChecklistSeedData, storesTesterSeedData, categoriesTesterSeedData, productsTesterSeedData, blogTesterSeedData, eventsTesterSeedData, couponsTesterSeedData, bidsTesterSeedData, cartsTesterSeedData, ordersTesterSeedData, } from "../features/tester/seed-data";
|
|
82
82
|
export { productFeaturesSeedData } from "./product-features-seed-data";
|
|
83
83
|
export { offersSeedData } from "./offers-seed-data";
|
|
84
84
|
export { payoutMethodsSeedData, shippingConfigsSeedData, analyticsCardsSeedData, analyticsAlertsSeedData, storeCategoriesSeedData, listingTemplatesSeedData, moderationQueueSeedData, reportsSeedData, itemRequestsSeedData, storeWhatsAppConfigSeedData, storeGoogleConfigSeedData, } from "./store-extensions-seed-data";
|
package/dist/seed/index.js
CHANGED
|
@@ -69,7 +69,7 @@ export { scammersSeedData } from "./scammers-seed-data";
|
|
|
69
69
|
export { supportTicketsSeedData } from "./support-tickets-seed-data";
|
|
70
70
|
// Tester sandbox seed fixtures live in ../features/tester/seed-data (isolated from this
|
|
71
71
|
// folder on purpose) — re-exported here so every other seed consumer keeps one import surface.
|
|
72
|
-
export { testerChecklistSeedData, storesTesterSeedData, categoriesTesterSeedData, productsTesterSeedData, blogTesterSeedData, eventsTesterSeedData, couponsTesterSeedData, bidsTesterSeedData, ordersTesterSeedData, } from "../features/tester/seed-data";
|
|
72
|
+
export { testerChecklistSeedData, storesTesterSeedData, categoriesTesterSeedData, productsTesterSeedData, blogTesterSeedData, eventsTesterSeedData, couponsTesterSeedData, bidsTesterSeedData, cartsTesterSeedData, ordersTesterSeedData, } from "../features/tester/seed-data";
|
|
73
73
|
export { productFeaturesSeedData } from "./product-features-seed-data";
|
|
74
74
|
export { offersSeedData } from "./offers-seed-data";
|
|
75
75
|
// S-STORE Extensions (Tier S-STORE) — 11 new collections
|
package/dist/seed/manifest.js
CHANGED
|
@@ -50,7 +50,7 @@ import { offersSeedData } from "./offers-seed-data";
|
|
|
50
50
|
import { couponUsageSeedData } from "./coupon-usage-seed-data";
|
|
51
51
|
import { claimedCouponsSeedData } from "./claimed-coupons-seed-data";
|
|
52
52
|
// Tester sandbox seed fixtures live in ../features/tester/seed-data (isolated on purpose).
|
|
53
|
-
import { testerChecklistSeedData, storesTesterSeedData, categoriesTesterSeedData, productsTesterSeedData, blogTesterSeedData, eventsTesterSeedData, couponsTesterSeedData, bidsTesterSeedData, ordersTesterSeedData, offersTesterSeedData, } from "../features/tester/seed-data";
|
|
53
|
+
import { testerChecklistSeedData, storesTesterSeedData, categoriesTesterSeedData, productsTesterSeedData, blogTesterSeedData, eventsTesterSeedData, couponsTesterSeedData, bidsTesterSeedData, cartsTesterSeedData, ordersTesterSeedData, offersTesterSeedData, } from "../features/tester/seed-data";
|
|
54
54
|
function asArr(items) {
|
|
55
55
|
return items ?? [];
|
|
56
56
|
}
|
|
@@ -166,7 +166,7 @@ export const SEED_MANIFEST = {
|
|
|
166
166
|
name: a.label ?? a.fullName ?? a.id,
|
|
167
167
|
})),
|
|
168
168
|
]),
|
|
169
|
-
carts: pick(asArr(cartsSeedData).map((c) => ({
|
|
169
|
+
carts: pick([...asArr(cartsSeedData), ...asArr(cartsTesterSeedData)].map((c) => ({
|
|
170
170
|
...c,
|
|
171
171
|
name: c.userId ?? c.id,
|
|
172
172
|
}))),
|
|
@@ -403,7 +403,11 @@ const notificationRecords = [
|
|
|
403
403
|
title: "Your auction ended with no winner",
|
|
404
404
|
message: "The highest bid did not meet your reserve price. The listing has been archived.",
|
|
405
405
|
isRead: false,
|
|
406
|
-
|
|
406
|
+
// 🛑 The real id carries `burst` — see products-auctions-seed-data.ts. Without
|
|
407
|
+
// it this notification's "view details" link resolves to nothing, which is the
|
|
408
|
+
// dangling-FK shape Root Cause #26 describes: the row renders perfectly and its
|
|
409
|
+
// only affordance goes nowhere.
|
|
410
|
+
relatedId: "auction-beyblade-burst-lord-spryzen-ended-unsold",
|
|
407
411
|
relatedType: "product",
|
|
408
412
|
createdAt: daysAgo(1),
|
|
409
413
|
updatedAt: daysAgo(1),
|