@mohasinac/appkit 4.30.18 → 4.30.19
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/digital-code/CodeRevealPanel.js +5 -2
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +3 -3
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +3 -2
- package/dist/_internal/client/features/maintenance/views/AnalysisRunnerView.js +5 -2
- package/dist/_internal/client/features/maintenance/views/CloudLogsListView.js +11 -3
- package/dist/_internal/client/hand-mode/HandModeProvider.js +2 -0
- package/dist/_internal/server/features/auctions/actions.js +14 -3
- package/dist/_internal/server/features/auctions/service.js +1 -1
- package/dist/_internal/server/features/blog/data.js +2 -2
- package/dist/_internal/server/features/blog/service.js +3 -3
- package/dist/_internal/server/features/brands/data.js +1 -1
- package/dist/_internal/server/features/brands/service.js +3 -3
- package/dist/_internal/server/features/bundles/actions.js +6 -1
- package/dist/_internal/server/features/bundles/data.js +21 -4
- package/dist/_internal/server/features/cart/data.js +3 -1
- package/dist/_internal/server/features/cart/service.js +13 -2
- package/dist/_internal/server/features/categories/data.js +12 -3
- package/dist/_internal/server/features/events/actions.js +17 -4
- package/dist/_internal/server/features/events/data.js +1 -1
- package/dist/_internal/server/features/events/service.js +1 -1
- package/dist/_internal/server/features/grouped/data.js +7 -2
- package/dist/_internal/server/features/item-requests/data.js +1 -1
- package/dist/_internal/server/features/lottery/actions.js +2 -2
- package/dist/_internal/server/features/lottery/data.js +1 -1
- package/dist/_internal/server/features/orders/actions.js +7 -7
- package/dist/_internal/server/features/orders/data.d.ts +1 -1
- package/dist/_internal/server/features/orders/data.js +14 -2
- package/dist/_internal/server/features/orders/service.js +1 -1
- package/dist/_internal/server/features/pre-orders/service.js +1 -1
- package/dist/_internal/server/features/prize-draws/actions.js +1 -1
- package/dist/_internal/server/features/products/data.js +7 -2
- package/dist/_internal/server/features/products/service.js +1 -1
- package/dist/_internal/server/features/promotions/actions.js +1 -1
- package/dist/_internal/server/features/promotions/data.js +1 -1
- package/dist/_internal/server/features/promotions/service.js +1 -1
- package/dist/_internal/server/features/reviews/data.d.ts +1 -1
- package/dist/_internal/server/features/reviews/data.js +6 -1
- package/dist/_internal/server/features/reviews/og-data.js +1 -1
- package/dist/_internal/server/features/shared/listing-data-factory.js +7 -2
- package/dist/_internal/server/features/stores/data.js +1 -1
- package/dist/_internal/server/features/sublisting-categories/data.js +1 -1
- package/dist/_internal/server/jobs/core/auctionSettlement.js +6 -1
- package/dist/_internal/server/jobs/core/onOrderStatusChange.js +7 -15
- package/dist/_internal/server/jobs/core/revenueRollup.js +7 -1
- package/dist/client/api/useApiQuery.js +17 -6
- package/dist/client.d.ts +1 -0
- package/dist/client.js +3 -0
- package/dist/contracts/client-realtime.d.ts +6 -0
- package/dist/contracts/realtime-session.d.ts +12 -12
- package/dist/contracts/realtime-session.js +72 -77
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/features/about/components/DeveloperView.js +7 -2
- package/dist/features/about/components/PolicyPageView.js +9 -1
- package/dist/features/about/components/PublicProfileView.js +23 -5
- package/dist/features/account/components/AddressForm.js +1 -1
- package/dist/features/account/components/NotificationBell.js +4 -3
- package/dist/features/account/components/NotificationPreferencesPanel.js +1 -1
- package/dist/features/account/components/UserOffersPanel.js +1 -1
- package/dist/features/admin/components/AdminFulfillmentView.js +5 -2
- package/dist/features/admin/components/AdminMediaView.js +3 -3
- package/dist/features/analytics/types.d.ts +1 -1
- package/dist/features/analytics/types.js +13 -0
- package/dist/features/auctions/components/AuctionDetailPageView.js +6 -2
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +3 -1
- package/dist/features/auth/actions/profile-actions.js +4 -3
- package/dist/features/auth/components/ForgotPasswordView.js +1 -1
- package/dist/features/auth/components/LoginForm.js +1 -1
- package/dist/features/auth/components/RegisterForm.js +1 -1
- package/dist/features/auth/components/ResetPasswordView.js +1 -1
- package/dist/features/auth/hooks/useAuth.js +3 -0
- package/dist/features/blog/repository/blog.repository.js +3 -1
- package/dist/features/cart/actions/cart-actions.js +13 -4
- package/dist/features/cart/utils/pending-ops.js +1 -1
- package/dist/features/categories/components/BrandDetailPageView.js +28 -12
- package/dist/features/categories/components/CategoryDetailPageView.js +37 -20
- package/dist/features/categories/repository/categories.repository.js +3 -1
- package/dist/features/checkout/actions/checkout-value-otp-actions.js +10 -1
- package/dist/features/classified/components/ClassifiedContactSellerPanel.js +1 -1
- package/dist/features/classified/components/ClassifiedDetailPageView.js +10 -2
- package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +4 -1
- package/dist/features/events/components/EventBanner.js +5 -2
- package/dist/features/events/components/EventOfferCard.js +3 -1
- package/dist/features/events/components/EventPollWidget.js +1 -1
- package/dist/features/events/components/SpinWheelView.js +3 -2
- package/dist/features/grouped/components/GroupedListingEditorView.js +2 -1
- package/dist/features/history/hooks/useHistory.js +29 -27
- package/dist/features/homepage/components/AdSlot.js +3 -1
- package/dist/features/homepage/components/GoogleReviewsSection.js +8 -2
- package/dist/features/homepage/components/MarketplaceHomepageView.js +34 -10
- package/dist/features/homepage/components/SocialFeedSection.js +4 -1
- package/dist/features/homepage/lib/live-stats.js +8 -1
- package/dist/features/homepage/repository/carousel.repository.js +2 -1
- package/dist/features/live/components/LiveItemDetailPageView.js +4 -1
- package/dist/features/media/finalize.js +3 -1
- package/dist/features/media/modals/ImageEditor.js +1 -1
- package/dist/features/media/modals/VideoThumbnailSelector.js +5 -2
- package/dist/features/media/modals/VideoTrimModal.js +5 -2
- package/dist/features/media/upload/MediaUploadField.js +2 -2
- package/dist/features/messages/hooks/useConversation.js +18 -4
- package/dist/features/messages/hooks/useConversations.js +25 -8
- package/dist/features/orders/components/RefundRequestView.js +5 -2
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -1
- package/dist/features/pre-orders/components/PreOrderActionsClient.js +1 -1
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +4 -1
- package/dist/features/products/components/PrizeDrawDetailPageView.js +4 -1
- package/dist/features/products/components/ProductDetailPageView.js +8 -2
- package/dist/features/products/components/ShareButton.js +3 -1
- package/dist/features/products/repository/loadProductFeatures.js +10 -2
- package/dist/features/products/repository/products.repository.js +2 -1
- package/dist/features/promotions/components/CouponCard.js +3 -1
- package/dist/features/reviews/actions/review-actions.js +8 -1
- package/dist/features/reviews/components/ReviewDetailPageView.js +13 -3
- package/dist/features/reviews/components/ReviewDetailShell.js +3 -1
- package/dist/features/scams/actions/scam-actions.d.ts +9 -1
- package/dist/features/scams/actions/scam-actions.js +52 -12
- package/dist/features/scams/components/SellerTrustBadge.d.ts +1 -1
- package/dist/features/scams/components/SellerTrustBadge.js +13 -0
- package/dist/features/scams/repository/scammer.repository.js +2 -1
- package/dist/features/seller/actions/offer-actions.js +6 -1
- package/dist/features/seller/components/FulfillmentView.js +5 -2
- package/dist/features/seller/components/PhysicalLocationModal.js +1 -1
- package/dist/features/seller/components/PrintCenterView.js +3 -1
- package/dist/features/seller/components/SellerBidsView.js +1 -1
- package/dist/features/seller/components/SellerCouponsView.js +1 -1
- package/dist/features/seller/components/SellerGoogleReviewsView.js +2 -2
- package/dist/features/seller/components/SellerOrdersView.js +3 -3
- package/dist/features/seller/components/SellerPayoutMethodsView.js +2 -2
- package/dist/features/seller/components/SellerProductShell.d.ts +5 -0
- package/dist/features/seller/components/SellerProductShell.js +15 -5
- package/dist/features/seller/components/SellerProductsView.js +24 -10
- package/dist/features/seller/components/SellerReviewsView.js +47 -16
- package/dist/features/seller/components/SellerShippingConfigsView.js +5 -4
- package/dist/features/seller/components/SellerStoreCategoriesView.js +3 -2
- package/dist/features/stores/components/StoreDetailLayoutView.d.ts +1 -1
- package/dist/features/stores/components/StoreDetailLayoutView.js +12 -3
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +16 -1
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +3 -2
- package/dist/features/wishlist/hooks/useWishlistToggle.js +8 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -2
- package/dist/next/api/routeHandler.js +3 -1
- package/dist/providers/auth-firebase/session.js +3 -1
- package/dist/providers/db-firebase/admin-app-lite.js +8 -8
- package/dist/providers/db-firebase/admin.js +12 -12
- package/dist/providers/db-firebase/browser-stub.d.ts +1 -11
- package/dist/providers/db-firebase/browser-stub.js +6 -11
- package/dist/providers/db-firebase/index.d.ts +0 -1
- package/dist/providers/db-firebase/index.js +0 -1
- package/dist/providers/db-firebase/rtdb-paths.d.ts +15 -6
- package/dist/providers/db-firebase/rtdb-paths.js +15 -6
- package/dist/providers/db-firebase/rtdb-url.d.ts +27 -0
- package/dist/providers/db-firebase/rtdb-url.js +50 -0
- package/dist/providers/firebase-client/realtime.d.ts +20 -0
- package/dist/providers/firebase-client/realtime.js +34 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +1 -1
- package/dist/react/hooks/use-action-dispatch.js +8 -2
- package/dist/react/hooks/useFormStatePreservation.js +3 -1
- package/dist/react/hooks/useRealtimeEvent.js +20 -9
- package/dist/ui/components/toolbar-collapse.js +1 -2
- package/package.json +1 -1
|
@@ -74,6 +74,11 @@ export interface SellerProductDraft {
|
|
|
74
74
|
/** Shape returned by `onSave`/`onPublish` — structurally compatible with appkit's `ActionResult<T>` failure/success arms, without importing the server-only type into this client component. */
|
|
75
75
|
export interface ProductActionResult {
|
|
76
76
|
ok: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Stable error code. This is what the failure toast resolves — `error` is the
|
|
79
|
+
* server's own sentence and is reported, never shown (audit-raw-error-text).
|
|
80
|
+
*/
|
|
81
|
+
code?: string;
|
|
77
82
|
error?: string;
|
|
78
83
|
issues?: unknown[];
|
|
79
84
|
}
|
|
@@ -13,6 +13,7 @@ import { BarcodeField } from "./BarcodeField";
|
|
|
13
13
|
import { pluginFor } from "../../../_internal/shared/listing-types/_registry";
|
|
14
14
|
import { DEFAULT_MIN_OFFER_PERCENT } from "../../../_internal/shared/features/offers/config";
|
|
15
15
|
import { normalizeError } from "../../../errors/normalize";
|
|
16
|
+
import { toUserMessage } from "../../../errors/error-display-map";
|
|
16
17
|
/**
|
|
17
18
|
* "bundle" is a categoryType, not a ListingType, so it can't live in
|
|
18
19
|
* LISTING_TYPE_REGISTRY — this is the single, explicit escape hatch that
|
|
@@ -346,18 +347,25 @@ export function SellerProductShell({ mode, listingType = "standard", initialValu
|
|
|
346
347
|
return;
|
|
347
348
|
}
|
|
348
349
|
applyZodIssues(result.issues ?? [], setFieldError);
|
|
349
|
-
if (!silent)
|
|
350
|
-
showToast(result.
|
|
350
|
+
if (!silent) {
|
|
351
|
+
showToast(toUserMessage(result.code, undefined, {
|
|
352
|
+
fallback: "Fix the highlighted errors and try again.",
|
|
353
|
+
}), "error");
|
|
354
|
+
}
|
|
351
355
|
}
|
|
352
356
|
catch (err) {
|
|
353
357
|
void normalizeError(err);
|
|
354
358
|
if (!silent)
|
|
355
|
-
showToast(
|
|
359
|
+
showToast("Failed to save.", "error");
|
|
356
360
|
}
|
|
357
361
|
}, [draft, onSave, markClean, clearErrors, setFieldError, showToast]);
|
|
358
362
|
const handleAutoSave = useCallback(async () => {
|
|
359
363
|
try {
|
|
360
364
|
await handleSave(true);
|
|
365
|
+
// handleSave was called with silent=true, which is the whole point of
|
|
366
|
+
// auto-save: it has already applied the field errors and populated
|
|
367
|
+
// FormErrorSummary, so a toast every two seconds while the seller is
|
|
368
|
+
// still typing would be worse than saying nothing at all here.
|
|
361
369
|
}
|
|
362
370
|
catch (err) {
|
|
363
371
|
void normalizeError(err);
|
|
@@ -383,11 +391,13 @@ export function SellerProductShell({ mode, listingType = "standard", initialValu
|
|
|
383
391
|
return;
|
|
384
392
|
}
|
|
385
393
|
applyZodIssues(result.issues ?? [], setFieldError);
|
|
386
|
-
showToast(result.
|
|
394
|
+
showToast(toUserMessage(result.code, undefined, {
|
|
395
|
+
fallback: "Fix the highlighted errors and try again.",
|
|
396
|
+
}), "error");
|
|
387
397
|
}
|
|
388
398
|
catch (err) {
|
|
389
399
|
void normalizeError(err);
|
|
390
|
-
showToast(
|
|
400
|
+
showToast("Failed to publish.", "error");
|
|
391
401
|
}
|
|
392
402
|
}, [draft, onPublish, markClean, clearErrors, setFieldError, showToast]);
|
|
393
403
|
const listingTypeLabel = pluginForMode(listingType).typeLabel;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { normalizeError } from "../../../errors/normalize";
|
|
4
|
+
import { toUserMessage } from "../../../errors/error-display-map";
|
|
4
5
|
import { SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit/client";
|
|
5
6
|
import { sortBy } from "@mohasinac/appkit/client";
|
|
6
7
|
import { useState, useCallback } from "react";
|
|
@@ -148,15 +149,23 @@ export function SellerProductsView({ onDeleteProduct, onCreateClick, }) {
|
|
|
148
149
|
await performDelete(row.id);
|
|
149
150
|
};
|
|
150
151
|
const handleDuplicate = async (row) => {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
// A swallowed failure here does nothing at all and says nothing — the
|
|
153
|
+
// seller clicks Duplicate and no copy appears, with no way to tell whether
|
|
154
|
+
// the request even left the browser.
|
|
155
|
+
try {
|
|
156
|
+
const res = await fetch(SELLER_ENDPOINTS.PRODUCT_DUPLICATE(row.id), {
|
|
157
|
+
method: "POST",
|
|
158
|
+
});
|
|
159
|
+
if (!res.ok)
|
|
160
|
+
throw new Error(`Duplicate failed (${res.status})`);
|
|
155
161
|
const json = await res.json().catch(() => null);
|
|
156
162
|
const newId = json?.data?.id;
|
|
157
163
|
if (newId)
|
|
158
164
|
handleEdit({ ...row, id: newId });
|
|
159
165
|
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
showToast(normalizeError(err).message, "error");
|
|
168
|
+
}
|
|
160
169
|
};
|
|
161
170
|
const handleTogglePublish = async (row) => {
|
|
162
171
|
const currentStatus = statusOverrides.get(row.id) ?? row.status;
|
|
@@ -167,10 +176,15 @@ export function SellerProductsView({ onDeleteProduct, onCreateClick, }) {
|
|
|
167
176
|
method: "PATCH",
|
|
168
177
|
headers: { "Content-Type": "application/json" },
|
|
169
178
|
body: JSON.stringify({ status: newStatus }),
|
|
170
|
-
})
|
|
171
|
-
if (res
|
|
172
|
-
|
|
173
|
-
|
|
179
|
+
});
|
|
180
|
+
if (!res.ok)
|
|
181
|
+
throw new Error(`Could not change status (${res.status})`);
|
|
182
|
+
setStatusOverrides((prev) => new Map([...prev, [row.id, newStatus]]));
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
// The row silently snapped back to its old status before this — a seller
|
|
186
|
+
// could not tell a rejected publish from a UI that had not repainted.
|
|
187
|
+
showToast(normalizeError(err).message, "error");
|
|
174
188
|
}
|
|
175
189
|
finally {
|
|
176
190
|
setPublishingId(null);
|
|
@@ -191,8 +205,8 @@ export function SellerProductsView({ onDeleteProduct, onCreateClick, }) {
|
|
|
191
205
|
setSetLocationOpen(false);
|
|
192
206
|
}
|
|
193
207
|
catch (err) {
|
|
194
|
-
|
|
195
|
-
showToast(
|
|
208
|
+
const e = normalizeError(err);
|
|
209
|
+
showToast(toUserMessage(e.code, undefined, { fallback: "Failed to update location." }), "error");
|
|
196
210
|
}
|
|
197
211
|
}, [showToast]);
|
|
198
212
|
const config = {
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { normalizeError } from "../../../errors/normalize";
|
|
4
4
|
import { useCallback, useState } from "react";
|
|
5
5
|
import { useQueryClient } from "@tanstack/react-query";
|
|
6
|
-
import { Alert, Badge, Button, Checkbox, Div, Modal, Row, Select, SideDrawer, Span, Stack, Text, Textarea } from "../../../ui";
|
|
6
|
+
import { Alert, Badge, Button, Checkbox, Div, Modal, Row, Select, SideDrawer, Span, Stack, Text, Textarea, useToast } from "../../../ui";
|
|
7
7
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
8
8
|
import { SELLER_BULK_ACTIONS, ROW_ACTION_META } from "../../products/constants/action-defs";
|
|
9
9
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
@@ -34,6 +34,7 @@ function statusBadge(status) {
|
|
|
34
34
|
}
|
|
35
35
|
export function SellerReviewsView({ reviewsApiBase = SELLER_ENDPOINTS.REVIEWS, replyApiBase = SELLER_ENDPOINTS.REVIEWS, }) {
|
|
36
36
|
const queryClient = useQueryClient();
|
|
37
|
+
const { showToast } = useToast();
|
|
37
38
|
const refetchReviews = useCallback(() => queryClient.invalidateQueries({ queryKey: ["seller", "reviews", "listing"] }), [queryClient]);
|
|
38
39
|
// Reply drawer
|
|
39
40
|
const [replyTarget, setReplyTarget] = useState(null);
|
|
@@ -80,45 +81,75 @@ export function SellerReviewsView({ reviewsApiBase = SELLER_ENDPOINTS.REVIEWS, r
|
|
|
80
81
|
setReplySaving(false);
|
|
81
82
|
}
|
|
82
83
|
};
|
|
84
|
+
// The three submits below all closed their modal unconditionally, so a
|
|
85
|
+
// rejected or unsent request looked exactly like a successful one — the
|
|
86
|
+
// seller believed the contest/feedback/reply had been filed.
|
|
83
87
|
const submitContest = useCallback(async (onDone) => {
|
|
84
88
|
if (!contestTarget)
|
|
85
89
|
return;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
try {
|
|
91
|
+
const res = await fetch(SELLER_ENDPOINTS.REVIEW_CONTEST(contestTarget.id), {
|
|
92
|
+
method: "POST",
|
|
93
|
+
headers: { "Content-Type": "application/json" },
|
|
94
|
+
body: JSON.stringify({ reason: contestReason }),
|
|
95
|
+
});
|
|
96
|
+
if (!res.ok)
|
|
97
|
+
throw new Error(`Could not submit the contest (${res.status})`);
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
showToast(normalizeError(err).message, "error");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
91
103
|
setContestTarget(null);
|
|
92
104
|
setContestReason("");
|
|
93
105
|
onDone();
|
|
94
|
-
}, [contestTarget, contestReason]);
|
|
106
|
+
}, [contestTarget, contestReason, showToast]);
|
|
95
107
|
const submitFeedback = useCallback(async () => {
|
|
96
108
|
if (!feedbackTarget)
|
|
97
109
|
return;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
110
|
+
try {
|
|
111
|
+
const res = await fetch(SELLER_ENDPOINTS.REVIEW_FEEDBACK(feedbackTarget.id), {
|
|
112
|
+
method: "POST",
|
|
113
|
+
headers: { "Content-Type": "application/json" },
|
|
114
|
+
body: JSON.stringify({ feedback: feedbackText }),
|
|
115
|
+
});
|
|
116
|
+
if (!res.ok)
|
|
117
|
+
throw new Error(`Could not send the feedback (${res.status})`);
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
showToast(normalizeError(err).message, "error");
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
103
123
|
setFeedbackTarget(null);
|
|
104
124
|
setFeedbackText("");
|
|
105
|
-
}, [feedbackTarget, feedbackText]);
|
|
125
|
+
}, [feedbackTarget, feedbackText, showToast]);
|
|
106
126
|
const submitBulkReply = useCallback(async () => {
|
|
107
127
|
if (!bulkReplyText.trim() || bulkTargetIds.length === 0)
|
|
108
128
|
return;
|
|
109
129
|
setBulkSaving(true);
|
|
110
|
-
|
|
130
|
+
// Partial success is the normal outcome of a bulk write, so report the
|
|
131
|
+
// count that failed rather than throwing away the whole batch's result.
|
|
132
|
+
const outcomes = await Promise.all(bulkTargetIds.map((id) => fetch(`${replyApiBase}/${id}/reply`, {
|
|
111
133
|
method: "POST",
|
|
112
134
|
headers: { "Content-Type": "application/json" },
|
|
113
135
|
body: JSON.stringify({ reply: bulkReplyText }),
|
|
114
|
-
})
|
|
136
|
+
})
|
|
137
|
+
.then((res) => res.ok)
|
|
138
|
+
.catch((err) => {
|
|
139
|
+
void normalizeError(err);
|
|
140
|
+
return false;
|
|
141
|
+
})));
|
|
142
|
+
const failed = outcomes.filter((ok) => !ok).length;
|
|
143
|
+
if (failed > 0) {
|
|
144
|
+
showToast(`${failed} of ${outcomes.length} replies could not be saved.`, "error");
|
|
145
|
+
}
|
|
115
146
|
setBulkSaving(false);
|
|
116
147
|
setBulkReplyOpen(false);
|
|
117
148
|
setBulkReplyText("");
|
|
118
149
|
bulkClearSelection?.();
|
|
119
150
|
setBulkTargetIds([]);
|
|
120
151
|
await refetchReviews();
|
|
121
|
-
}, [bulkReplyText, bulkTargetIds, replyApiBase, bulkClearSelection, refetchReviews]);
|
|
152
|
+
}, [bulkReplyText, bulkTargetIds, replyApiBase, bulkClearSelection, refetchReviews, showToast]);
|
|
122
153
|
const config = {
|
|
123
154
|
portal: "seller",
|
|
124
155
|
title: "Reviews",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { normalizeError } from "../../../errors/normalize";
|
|
4
|
+
import { toUserMessage } from "../../../errors/error-display-map";
|
|
4
5
|
import { sortBy } from "@mohasinac/appkit/client";
|
|
5
6
|
import { useState, useCallback } from "react";
|
|
6
7
|
import { useEntityDelete } from "../../../react/hooks/useEntityDelete";
|
|
@@ -53,8 +54,8 @@ export function SellerShippingConfigsView({ onCreateClick, onEditClick, onDelete
|
|
|
53
54
|
await performDelete(id);
|
|
54
55
|
}
|
|
55
56
|
catch (err) {
|
|
56
|
-
|
|
57
|
-
showToast(
|
|
57
|
+
const e = normalizeError(err);
|
|
58
|
+
showToast(toUserMessage(e.code, undefined, { fallback: "Failed to delete shipping config." }), "error");
|
|
58
59
|
}
|
|
59
60
|
setDeleteTargetId(null);
|
|
60
61
|
}, [performDelete, showToast]);
|
|
@@ -75,8 +76,8 @@ export function SellerShippingConfigsView({ onCreateClick, onEditClick, onDelete
|
|
|
75
76
|
showToast("Default shipping config updated.", "success");
|
|
76
77
|
}
|
|
77
78
|
catch (err) {
|
|
78
|
-
|
|
79
|
-
showToast(
|
|
79
|
+
const e = normalizeError(err);
|
|
80
|
+
showToast(toUserMessage(e.code, undefined, { fallback: "Failed to update default." }), "error");
|
|
80
81
|
}
|
|
81
82
|
finally {
|
|
82
83
|
setSettingDefaultId(null);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { normalizeError } from "../../../errors/normalize";
|
|
4
|
+
import { toUserMessage } from "../../../errors/error-display-map";
|
|
4
5
|
import { Badge, sortBy } from "@mohasinac/appkit/client";
|
|
5
6
|
import { useState, useCallback } from "react";
|
|
6
7
|
import { useEntityDelete } from "../../../react/hooks/useEntityDelete";
|
|
@@ -49,8 +50,8 @@ export function SellerStoreCategoriesView({ onCreateClick, onEditClick, onDelete
|
|
|
49
50
|
await performDelete(id);
|
|
50
51
|
}
|
|
51
52
|
catch (err) {
|
|
52
|
-
|
|
53
|
-
showToast(
|
|
53
|
+
const e = normalizeError(err);
|
|
54
|
+
showToast(toUserMessage(e.code, undefined, { fallback: "Failed to delete category." }), "error");
|
|
54
55
|
}
|
|
55
56
|
setDeleteTargetId(null);
|
|
56
57
|
}, [onDelete, performDelete, showToast]);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { ReactNode } from "react";
|
|
3
|
-
export declare const getStoreBySlug: (slug: string) => Promise<import("@mohasinac/appkit").StoreDocument | null
|
|
3
|
+
export declare const getStoreBySlug: (slug: string) => Promise<import("@mohasinac/appkit").StoreDocument | null>;
|
|
4
4
|
export interface StoreDetailLayoutViewProps {
|
|
5
5
|
storeSlug: string;
|
|
6
6
|
/** The current active tab value: "products" | "auctions" | "reviews" | "about" */
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { sieveFilter, sieveAnd, SIEVE_OP } from "@mohasinac/appkit";
|
|
3
3
|
import { cache } from "react";
|
|
4
4
|
import { storeRepository, siteSettingsRepository, reviewRepository, couponsRepository } from "../../../repositories";
|
|
5
|
+
import { safeRead } from "../../../errors/safe-read";
|
|
5
6
|
import { ROUTES } from "../../../next";
|
|
6
7
|
import { Container, Main, Section, Text } from "../../../ui";
|
|
7
8
|
import { STORE_PAGE_TABS } from "../../products/constants/listing-tabs";
|
|
@@ -26,7 +27,10 @@ const STORE_LISTING_HREF = {
|
|
|
26
27
|
// a real non-zero count and landed on a page with none of those items.
|
|
27
28
|
art: (slug) => String(ROUTES.PUBLIC.STORE_ART(slug)),
|
|
28
29
|
};
|
|
29
|
-
|
|
30
|
+
// The store IS this layout's subject, and `findBySlug` already returns null for
|
|
31
|
+
// "no such slug" — swallowing a read failure into the same value would render
|
|
32
|
+
// the "Store not found." branch below for a live store during an outage.
|
|
33
|
+
export const getStoreBySlug = cache((slug) => storeRepository.findBySlug(slug));
|
|
30
34
|
function tabLabel(base, count) {
|
|
31
35
|
if (!count)
|
|
32
36
|
return base;
|
|
@@ -39,9 +43,14 @@ export async function StoreDetailLayoutView({ storeSlug, activeTab, children, sc
|
|
|
39
43
|
}
|
|
40
44
|
const storeId = store?.id;
|
|
41
45
|
const [settings, trust] = await Promise.all([
|
|
42
|
-
siteSettingsRepository.findById("global")
|
|
46
|
+
safeRead(() => siteSettingsRepository.findById("global"), {
|
|
47
|
+
route: "/stores/[slug]", key: "store.siteSettings", fallback: null,
|
|
48
|
+
}),
|
|
43
49
|
scamRegistryEnabled && storeId
|
|
44
|
-
|
|
50
|
+
// Fails open to "no trust verdict", which hides the badge entirely rather
|
|
51
|
+
// than asserting a seller is clear — but that has to be recorded, since a
|
|
52
|
+
// missing badge is exactly what a clean seller looks like.
|
|
53
|
+
? safeRead(() => getSellerTrustStatus(storeId), { route: "/stores/[slug]", key: "store.sellerTrust", fallback: undefined })
|
|
45
54
|
: Promise.resolve(undefined),
|
|
46
55
|
]);
|
|
47
56
|
// One derived batch instead of nine copy-pasted per-type queries. The tabs
|
|
@@ -3728,7 +3728,22 @@ const rawTesterChecklistItems = [
|
|
|
3728
3728
|
href: "/admin/site",
|
|
3729
3729
|
},
|
|
3730
3730
|
{ key: "admin-dashboard-widgets", label: "Admin dashboard widgets show accurate data", href: "/admin/dashboard" },
|
|
3731
|
-
{
|
|
3731
|
+
{
|
|
3732
|
+
key: "analytics-admin",
|
|
3733
|
+
label: "Admin analytics dashboard shows accurate data",
|
|
3734
|
+
href: "/admin/analytics",
|
|
3735
|
+
},
|
|
3736
|
+
{
|
|
3737
|
+
key: "analytics-traffic-card-has-numbers",
|
|
3738
|
+
label: "The \"Traffic today\" card shows a real number, and it MATCHES the Page Views tab beside it",
|
|
3739
|
+
description: "Fixed 2026-08-29. BEFORE: the card showed an em-dash forever — never even a 0. It subscribed to two Realtime Database paths written by a presence hook whose writes had been denied since the day it shipped (the RTDB rules are backend-write-only), and both subscriptions passed no error callback, so a permission denial fired nothing at all and the tiles simply never left their initial state. AFTER: the card reads the same Firestore pageview counter that backs the Page Views tab in the same tab strip, so the two figures must agree — compare them. If the card errors it now SAYS so instead of showing a dash.",
|
|
3740
|
+
},
|
|
3741
|
+
{
|
|
3742
|
+
key: "analytics-no-permission-denied",
|
|
3743
|
+
label: "Opening /admin/analytics logs no PERMISSION_DENIED in the browser console",
|
|
3744
|
+
description: "Fixed 2026-08-29. BEFORE: every admin page load produced two Firebase permission_denied warnings from the dead presence/analytics subscriptions. AFTER: none — those subscriptions are gone entirely. Open devtools, hard-reload the page, and confirm the console is clean of Firebase permission errors.",
|
|
3745
|
+
href: "/admin/analytics",
|
|
3746
|
+
},
|
|
3732
3747
|
{
|
|
3733
3748
|
key: "pageviews-report-listing-standard",
|
|
3734
3749
|
label: "The \"Page Views\" tab in Admin Analytics is a full standard listing — a search box (by entity id or URL), an entity-type filter drawer, a \"Most views\"/\"Fewest views\" sort dropdown, and real pagination — not a bare unsearchable table",
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useApiMutation, useBulkEvent, RTDB_PATHS } from "@mohasinac/appkit/client";
|
|
4
4
|
import React from "react";
|
|
5
5
|
import { normalizeError } from "../../../errors/normalize";
|
|
6
|
+
import { toUserMessage } from "../../../errors/error-display-map";
|
|
6
7
|
const __P = {
|
|
7
8
|
p3: "p-[var(--appkit-space-3)]",
|
|
8
9
|
};
|
|
@@ -156,9 +157,9 @@ export function SellerWhatsAppSettingsView({ hasCapability }) {
|
|
|
156
157
|
}
|
|
157
158
|
}
|
|
158
159
|
catch (err) {
|
|
159
|
-
|
|
160
|
+
const e = normalizeError(err);
|
|
160
161
|
setImporting(false);
|
|
161
|
-
showToast(
|
|
162
|
+
showToast(toUserMessage(e.code, undefined, { fallback: "Failed to start catalog import." }), "error");
|
|
162
163
|
}
|
|
163
164
|
};
|
|
164
165
|
React.useEffect(() => {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useCallback, useState } from "react";
|
|
3
3
|
import { useToast } from "../../../ui";
|
|
4
|
+
import { normalizeError } from "../../../errors/normalize";
|
|
5
|
+
import { toUserMessage } from "../../../errors/error-display-map";
|
|
4
6
|
export function useWishlistToggle(productId, initial = false, actions) {
|
|
5
7
|
const [inWishlist, setInWishlist] = useState(initial);
|
|
6
8
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -25,8 +27,13 @@ export function useWishlistToggle(productId, initial = false, actions) {
|
|
|
25
27
|
}
|
|
26
28
|
}
|
|
27
29
|
catch (err) {
|
|
30
|
+
const e = normalizeError(err);
|
|
28
31
|
setInWishlist(prev);
|
|
29
|
-
showToast(
|
|
32
|
+
showToast(toUserMessage(e.code, undefined, {
|
|
33
|
+
fallback: prev
|
|
34
|
+
? "Could not remove this item from your wishlist."
|
|
35
|
+
: "Could not add this item to your wishlist.",
|
|
36
|
+
}), "error");
|
|
30
37
|
throw err;
|
|
31
38
|
}
|
|
32
39
|
finally {
|
package/dist/index.d.ts
CHANGED
|
@@ -333,6 +333,7 @@ export { RazorpayUnreachableError } from "./errors/index";
|
|
|
333
333
|
export { mapToHttpError, HTTP_ERROR_CODES } from "./errors/index";
|
|
334
334
|
export type { MappedError, HttpErrorCode } from "./errors/index";
|
|
335
335
|
export { ERROR_DISPLAY_MAP, getErrorDisplay } from "./errors/index";
|
|
336
|
+
export { toUserMessage, GENERIC_USER_MESSAGE } from "./errors/index";
|
|
336
337
|
export type { ErrorDisplayEntry } from "./errors/index";
|
|
337
338
|
export { parseJsonBody } from "./next/api/parseJsonBody";
|
|
338
339
|
export { SEO_CONFIG } from "./seo/index";
|
|
@@ -849,7 +850,6 @@ export { requireRoleUser } from "./providers/auth-firebase/index";
|
|
|
849
850
|
export { revokeUserTokens } from "./providers/auth-firebase/index";
|
|
850
851
|
export { verifyIdToken } from "./providers/auth-firebase/index";
|
|
851
852
|
export { verifySessionCookie } from "./providers/auth-firebase/index";
|
|
852
|
-
export { FirebaseRealtimeRepository } from "./providers/db-firebase/index";
|
|
853
853
|
export { FirebaseRepository } from "./providers/db-firebase/index";
|
|
854
854
|
export { FirebaseSieveRepository } from "./providers/db-firebase/index";
|
|
855
855
|
export { RTDB_PATHS } from "./providers/db-firebase/index";
|
package/dist/index.js
CHANGED
|
@@ -568,6 +568,11 @@ export { mapToHttpError, HTTP_ERROR_CODES } from "./errors/index";
|
|
|
568
568
|
// [UTIL]-Pure utility — client-safe lookup of inline-field + i18n key per error code.
|
|
569
569
|
// ERROR_DISPLAY_MAP - CLAUDE.md Rule #9 source-of-truth for client error rendering.
|
|
570
570
|
export { ERROR_DISPLAY_MAP, getErrorDisplay } from "./errors/index";
|
|
571
|
+
// [UTIL]-Pure utility — the ONLY sanctioned way to turn a failure into user copy.
|
|
572
|
+
// toUserMessage - resolves a stable error code through errors.codes.*; terminates
|
|
573
|
+
// in a constant, so a thrown value's own .message can never reach a user
|
|
574
|
+
// (audit-raw-error-text).
|
|
575
|
+
export { toUserMessage, GENERIC_USER_MESSAGE } from "./errors/index";
|
|
571
576
|
// [SERVER-ONLY] parseJsonBody — strict JSON request-body parser for API routes.
|
|
572
577
|
// Replaces the silent `request.json().catch(() => ({}))` anti-pattern. Throws
|
|
573
578
|
// ValidationError on malformed JSON → routeHandler wrapper maps to 400.
|
|
@@ -1726,8 +1731,6 @@ export { verifyIdToken } from "./providers/auth-firebase/index";
|
|
|
1726
1731
|
export { verifySessionCookie } from "./providers/auth-firebase/index";
|
|
1727
1732
|
// ./providers/db-firebase/index
|
|
1728
1733
|
// [DB]-Database layer â€" uses firebase-admin or another server-side DB SDK; can only run in a trusted server environment.
|
|
1729
|
-
// FirebaseRealtimeRepository - Shared export for firebase realtime repository.
|
|
1730
|
-
export { FirebaseRealtimeRepository } from "./providers/db-firebase/index";
|
|
1731
1734
|
// [DB]-Database layer â€" uses firebase-admin or another server-side DB SDK; can only run in a trusted server environment.
|
|
1732
1735
|
// FirebaseRepository - Shared export for firebase repository.
|
|
1733
1736
|
export { FirebaseRepository } from "./providers/db-firebase/index";
|
|
@@ -182,10 +182,12 @@ export function createRouteHandler(options) {
|
|
|
182
182
|
else if (options.authOptional) {
|
|
183
183
|
try {
|
|
184
184
|
user = await verifySession(request);
|
|
185
|
+
// Deliberate: this is the OPTIONAL-auth path. A bad or absent session means
|
|
186
|
+
// anonymous, which is a legitimate outcome here — routes that require a
|
|
187
|
+
// user use `auth: true`, which throws instead.
|
|
185
188
|
}
|
|
186
189
|
catch (_err) {
|
|
187
190
|
void normalizeError(_err);
|
|
188
|
-
// anonymous
|
|
189
191
|
}
|
|
190
192
|
}
|
|
191
193
|
// -- Role check --------------------------------------------------------
|
|
@@ -71,8 +71,10 @@ export const firebaseSessionProvider = {
|
|
|
71
71
|
await getAdminAuthLite().revokeRefreshTokens(decoded.uid);
|
|
72
72
|
}
|
|
73
73
|
catch (_err) {
|
|
74
|
+
// Best-effort teardown: an expired/revoked/malformed cookie cannot be
|
|
75
|
+
// verified, and that is the state destroySession is trying to reach —
|
|
76
|
+
// reporting it would alarm on the success case.
|
|
74
77
|
void normalizeError(_err);
|
|
75
|
-
// Best-effort: if cookie is already invalid, treat as already destroyed
|
|
76
78
|
}
|
|
77
79
|
},
|
|
78
80
|
};
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { initializeApp, getApps, cert } from "firebase-admin/app";
|
|
2
|
+
// One resolver, not a second copy — this file duplicated admin.ts's URL
|
|
3
|
+
// logic byte-for-byte, so the region-wrong fallback bug existed twice.
|
|
4
|
+
// Imported from its own dependency-free module, NOT from ./admin, because the
|
|
5
|
+
// whole point of admin-app-lite is not pulling the full admin module in.
|
|
6
|
+
import { resolveRtdbUrl } from "./rtdb-url";
|
|
2
7
|
function nodePath() { return require("path"); }
|
|
3
8
|
function nodeFs() { return require("fs"); }
|
|
4
9
|
// process.cwd() is Node.js-only; access via module.require so the Edge
|
|
@@ -26,17 +31,14 @@ export function getAdminAppLite() {
|
|
|
26
31
|
let app;
|
|
27
32
|
if (nodeFs().existsSync(keyPath)) {
|
|
28
33
|
const sa = JSON.parse(nodeFs().readFileSync(keyPath, "utf8"));
|
|
29
|
-
const dbUrl =
|
|
30
|
-
`https://${sa.project_id}-default-rtdb.firebaseio.com`;
|
|
34
|
+
const dbUrl = resolveRtdbUrl();
|
|
31
35
|
app = initializeApp({ credential: cert(keyPath), databaseURL: dbUrl, ...(storageBucket && { storageBucket }) });
|
|
32
36
|
}
|
|
33
37
|
else if (process.env.FIREBASE_ADMIN_PROJECT_ID &&
|
|
34
38
|
process.env.FIREBASE_ADMIN_CLIENT_EMAIL &&
|
|
35
39
|
process.env.FIREBASE_ADMIN_PRIVATE_KEY) {
|
|
36
40
|
const projectId = process.env.FIREBASE_ADMIN_PROJECT_ID.trim();
|
|
37
|
-
const dbUrl =
|
|
38
|
-
process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL?.trim() ??
|
|
39
|
-
`https://${projectId}-default-rtdb.firebaseio.com`;
|
|
41
|
+
const dbUrl = resolveRtdbUrl();
|
|
40
42
|
app = initializeApp({
|
|
41
43
|
credential: cert({
|
|
42
44
|
projectId,
|
|
@@ -63,9 +65,7 @@ export function getAdminAppLite() {
|
|
|
63
65
|
: {};
|
|
64
66
|
const projectId = firebaseConfig.projectId ?? process.env.GCLOUD_PROJECT ?? "";
|
|
65
67
|
const dbUrl = firebaseConfig.databaseURL ??
|
|
66
|
-
|
|
67
|
-
process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL ??
|
|
68
|
-
(projectId ? `https://${projectId}-default-rtdb.firebaseio.com` : undefined);
|
|
68
|
+
resolveRtdbUrl();
|
|
69
69
|
app = initializeApp({
|
|
70
70
|
...(dbUrl && { databaseURL: dbUrl }),
|
|
71
71
|
...(storageBucket && { storageBucket }),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { normalizeError } from "../../errors/normalize";
|
|
2
|
+
import { resolveRtdbUrl } from "./rtdb-url";
|
|
2
3
|
function nodePath() { return require("path"); }
|
|
3
4
|
function nodeFs() { return require("fs"); }
|
|
4
5
|
// process.cwd() is Node.js-only; access via module.require so the Edge
|
|
@@ -69,17 +70,16 @@ export function getAdminApp() {
|
|
|
69
70
|
try {
|
|
70
71
|
if (nodeFs().existsSync(keyPath)) {
|
|
71
72
|
const sa = JSON.parse(nodeFs().readFileSync(keyPath, "utf8"));
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
// Was NEXT_PUBLIC-only here while the other two branches honoured
|
|
74
|
+
// FIREBASE_ADMIN_DATABASE_URL, so setting it had no effect on this path.
|
|
75
|
+
const dbUrl = resolveRtdbUrl();
|
|
74
76
|
app = initializeApp({ credential: cert(keyPath), databaseURL: dbUrl, ...(storageBucket && { storageBucket }) });
|
|
75
77
|
}
|
|
76
78
|
else if (process.env.FIREBASE_ADMIN_PROJECT_ID &&
|
|
77
79
|
process.env.FIREBASE_ADMIN_CLIENT_EMAIL &&
|
|
78
80
|
process.env.FIREBASE_ADMIN_PRIVATE_KEY) {
|
|
79
81
|
const projectId = process.env.FIREBASE_ADMIN_PROJECT_ID.trim();
|
|
80
|
-
const dbUrl =
|
|
81
|
-
process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL?.trim() ??
|
|
82
|
-
`https://${projectId}-default-rtdb.firebaseio.com`;
|
|
82
|
+
const dbUrl = resolveRtdbUrl();
|
|
83
83
|
app = initializeApp({
|
|
84
84
|
credential: cert({
|
|
85
85
|
projectId,
|
|
@@ -105,12 +105,9 @@ export function getAdminApp() {
|
|
|
105
105
|
? JSON.parse(process.env.FIREBASE_CONFIG)
|
|
106
106
|
: {};
|
|
107
107
|
const projectId = firebaseConfig.projectId ?? process.env.GCLOUD_PROJECT ?? "";
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
(projectId
|
|
112
|
-
? `https://${projectId}-default-rtdb.firebaseio.com`
|
|
113
|
-
: undefined);
|
|
108
|
+
// FIREBASE_CONFIG is injected by the Functions runtime and is
|
|
109
|
+
// authoritative there; env vars are the fallback for other runtimes.
|
|
110
|
+
const dbUrl = firebaseConfig.databaseURL || resolveRtdbUrl();
|
|
114
111
|
app = initializeApp({
|
|
115
112
|
...(dbUrl && { databaseURL: dbUrl }),
|
|
116
113
|
...(storageBucket && { storageBucket }),
|
|
@@ -153,8 +150,11 @@ export function getAdminDb() {
|
|
|
153
150
|
db.settings({ ignoreUndefinedProperties: true });
|
|
154
151
|
}
|
|
155
152
|
catch (_err) {
|
|
153
|
+
// Firestore rejects a second settings() call on an already-initialised
|
|
154
|
+
// instance, which is the only way this throws (HMR reload / duplicate
|
|
155
|
+
// module instance re-entering the memoised bootstrap). The instance is
|
|
156
|
+
// fully usable either way, so there is nothing degraded to report.
|
|
156
157
|
void normalizeError(_err);
|
|
157
|
-
// Already configured — safe to ignore.
|
|
158
158
|
}
|
|
159
159
|
set("__mohasinac_firebase_admin_db__", db);
|
|
160
160
|
return db;
|
|
@@ -22,17 +22,7 @@ export declare function prepareForFirestore(data: any): any;
|
|
|
22
22
|
export declare function deserializeTimestamps<T>(data: any): T;
|
|
23
23
|
export declare const FirebaseRepository: any;
|
|
24
24
|
export declare const FirebaseSieveRepository: any;
|
|
25
|
-
export declare const FirebaseRealtimeRepository: any;
|
|
26
25
|
export declare const firebaseDbProvider: any;
|
|
27
|
-
export
|
|
28
|
-
readonly PRESENCE: "presence";
|
|
29
|
-
readonly CHAT: "chat";
|
|
30
|
-
readonly NOTIFICATIONS: "notifications";
|
|
31
|
-
readonly LIVE_UPDATES: "live_updates";
|
|
32
|
-
readonly AUTH_EVENTS: "auth_events";
|
|
33
|
-
readonly PAYMENT_EVENTS: "payment_events";
|
|
34
|
-
readonly BULK_EVENTS: "bulk_events";
|
|
35
|
-
readonly SEED_EVENTS: "seed_events";
|
|
36
|
-
};
|
|
26
|
+
export { RTDB_PATHS } from "./rtdb-paths";
|
|
37
27
|
export { buildFirebaseClientConfig, normalizeFirebaseConfigValue } from './client-config';
|
|
38
28
|
export type { FirebaseClientConfig } from './client-config';
|
|
@@ -40,17 +40,12 @@ export function deserializeTimestamps(data) {
|
|
|
40
40
|
}
|
|
41
41
|
export const FirebaseRepository = null;
|
|
42
42
|
export const FirebaseSieveRepository = null;
|
|
43
|
-
export const FirebaseRealtimeRepository = null;
|
|
44
43
|
export const firebaseDbProvider = null;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
PAYMENT_EVENTS: "payment_events",
|
|
52
|
-
BULK_EVENTS: "bulk_events",
|
|
53
|
-
SEED_EVENTS: "seed_events",
|
|
54
|
-
};
|
|
44
|
+
// Re-exported, NOT re-declared. This file is the `browser` resolution target, so
|
|
45
|
+
// the byte-for-byte copy that used to live here meant client code and server
|
|
46
|
+
// code read two independently-maintained definitions of the same constant, with
|
|
47
|
+
// nothing guarding the drift. `rtdb-paths.ts` has no imports, so it is safe to
|
|
48
|
+
// pull into a browser bundle directly.
|
|
49
|
+
export { RTDB_PATHS } from "./rtdb-paths";
|
|
55
50
|
// Client config builder � safe in all environments
|
|
56
51
|
export { buildFirebaseClientConfig, normalizeFirebaseConfigValue } from './client-config';
|
|
@@ -20,7 +20,6 @@ export type { DocumentSnapshot } from "firebase-admin/firestore";
|
|
|
20
20
|
export { FirebaseRepository } from "./base";
|
|
21
21
|
export { BaseRepository } from "./base.repository";
|
|
22
22
|
export { FirebaseSieveRepository } from "./sieve";
|
|
23
|
-
export { FirebaseRealtimeRepository } from "./realtime";
|
|
24
23
|
export { RTDB_PATHS } from "./rtdb-paths";
|
|
25
24
|
import type { IDbProvider } from "../../contracts";
|
|
26
25
|
export declare const firebaseDbProvider: IDbProvider;
|