@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.
Files changed (162) hide show
  1. package/dist/_internal/client/features/digital-code/CodeRevealPanel.js +5 -2
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +3 -3
  3. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +3 -2
  4. package/dist/_internal/client/features/maintenance/views/AnalysisRunnerView.js +5 -2
  5. package/dist/_internal/client/features/maintenance/views/CloudLogsListView.js +11 -3
  6. package/dist/_internal/client/hand-mode/HandModeProvider.js +2 -0
  7. package/dist/_internal/server/features/auctions/actions.js +14 -3
  8. package/dist/_internal/server/features/auctions/service.js +1 -1
  9. package/dist/_internal/server/features/blog/data.js +2 -2
  10. package/dist/_internal/server/features/blog/service.js +3 -3
  11. package/dist/_internal/server/features/brands/data.js +1 -1
  12. package/dist/_internal/server/features/brands/service.js +3 -3
  13. package/dist/_internal/server/features/bundles/actions.js +6 -1
  14. package/dist/_internal/server/features/bundles/data.js +21 -4
  15. package/dist/_internal/server/features/cart/data.js +3 -1
  16. package/dist/_internal/server/features/cart/service.js +13 -2
  17. package/dist/_internal/server/features/categories/data.js +12 -3
  18. package/dist/_internal/server/features/events/actions.js +17 -4
  19. package/dist/_internal/server/features/events/data.js +1 -1
  20. package/dist/_internal/server/features/events/service.js +1 -1
  21. package/dist/_internal/server/features/grouped/data.js +7 -2
  22. package/dist/_internal/server/features/item-requests/data.js +1 -1
  23. package/dist/_internal/server/features/lottery/actions.js +2 -2
  24. package/dist/_internal/server/features/lottery/data.js +1 -1
  25. package/dist/_internal/server/features/orders/actions.js +7 -7
  26. package/dist/_internal/server/features/orders/data.d.ts +1 -1
  27. package/dist/_internal/server/features/orders/data.js +14 -2
  28. package/dist/_internal/server/features/orders/service.js +1 -1
  29. package/dist/_internal/server/features/pre-orders/service.js +1 -1
  30. package/dist/_internal/server/features/prize-draws/actions.js +1 -1
  31. package/dist/_internal/server/features/products/data.js +7 -2
  32. package/dist/_internal/server/features/products/service.js +1 -1
  33. package/dist/_internal/server/features/promotions/actions.js +1 -1
  34. package/dist/_internal/server/features/promotions/data.js +1 -1
  35. package/dist/_internal/server/features/promotions/service.js +1 -1
  36. package/dist/_internal/server/features/reviews/data.d.ts +1 -1
  37. package/dist/_internal/server/features/reviews/data.js +6 -1
  38. package/dist/_internal/server/features/reviews/og-data.js +1 -1
  39. package/dist/_internal/server/features/shared/listing-data-factory.js +7 -2
  40. package/dist/_internal/server/features/stores/data.js +1 -1
  41. package/dist/_internal/server/features/sublisting-categories/data.js +1 -1
  42. package/dist/_internal/server/jobs/core/auctionSettlement.js +6 -1
  43. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +7 -15
  44. package/dist/_internal/server/jobs/core/revenueRollup.js +7 -1
  45. package/dist/client/api/useApiQuery.js +17 -6
  46. package/dist/client.d.ts +1 -0
  47. package/dist/client.js +3 -0
  48. package/dist/contracts/client-realtime.d.ts +6 -0
  49. package/dist/contracts/realtime-session.d.ts +12 -12
  50. package/dist/contracts/realtime-session.js +72 -77
  51. package/dist/errors/index.d.ts +1 -1
  52. package/dist/errors/index.js +1 -1
  53. package/dist/features/about/components/DeveloperView.js +7 -2
  54. package/dist/features/about/components/PolicyPageView.js +9 -1
  55. package/dist/features/about/components/PublicProfileView.js +23 -5
  56. package/dist/features/account/components/AddressForm.js +1 -1
  57. package/dist/features/account/components/NotificationBell.js +4 -3
  58. package/dist/features/account/components/NotificationPreferencesPanel.js +1 -1
  59. package/dist/features/account/components/UserOffersPanel.js +1 -1
  60. package/dist/features/admin/components/AdminFulfillmentView.js +5 -2
  61. package/dist/features/admin/components/AdminMediaView.js +3 -3
  62. package/dist/features/analytics/types.d.ts +1 -1
  63. package/dist/features/analytics/types.js +13 -0
  64. package/dist/features/auctions/components/AuctionDetailPageView.js +6 -2
  65. package/dist/features/auctions/components/MarketplaceAuctionCard.js +3 -1
  66. package/dist/features/auth/actions/profile-actions.js +4 -3
  67. package/dist/features/auth/components/ForgotPasswordView.js +1 -1
  68. package/dist/features/auth/components/LoginForm.js +1 -1
  69. package/dist/features/auth/components/RegisterForm.js +1 -1
  70. package/dist/features/auth/components/ResetPasswordView.js +1 -1
  71. package/dist/features/auth/hooks/useAuth.js +3 -0
  72. package/dist/features/blog/repository/blog.repository.js +3 -1
  73. package/dist/features/cart/actions/cart-actions.js +13 -4
  74. package/dist/features/cart/utils/pending-ops.js +1 -1
  75. package/dist/features/categories/components/BrandDetailPageView.js +28 -12
  76. package/dist/features/categories/components/CategoryDetailPageView.js +37 -20
  77. package/dist/features/categories/repository/categories.repository.js +3 -1
  78. package/dist/features/checkout/actions/checkout-value-otp-actions.js +10 -1
  79. package/dist/features/classified/components/ClassifiedContactSellerPanel.js +1 -1
  80. package/dist/features/classified/components/ClassifiedDetailPageView.js +10 -2
  81. package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +4 -1
  82. package/dist/features/events/components/EventBanner.js +5 -2
  83. package/dist/features/events/components/EventOfferCard.js +3 -1
  84. package/dist/features/events/components/EventPollWidget.js +1 -1
  85. package/dist/features/events/components/SpinWheelView.js +3 -2
  86. package/dist/features/grouped/components/GroupedListingEditorView.js +2 -1
  87. package/dist/features/history/hooks/useHistory.js +29 -27
  88. package/dist/features/homepage/components/AdSlot.js +3 -1
  89. package/dist/features/homepage/components/GoogleReviewsSection.js +8 -2
  90. package/dist/features/homepage/components/MarketplaceHomepageView.js +34 -10
  91. package/dist/features/homepage/components/SocialFeedSection.js +4 -1
  92. package/dist/features/homepage/lib/live-stats.js +8 -1
  93. package/dist/features/homepage/repository/carousel.repository.js +2 -1
  94. package/dist/features/live/components/LiveItemDetailPageView.js +4 -1
  95. package/dist/features/media/finalize.js +3 -1
  96. package/dist/features/media/modals/ImageEditor.js +1 -1
  97. package/dist/features/media/modals/VideoThumbnailSelector.js +5 -2
  98. package/dist/features/media/modals/VideoTrimModal.js +5 -2
  99. package/dist/features/media/upload/MediaUploadField.js +2 -2
  100. package/dist/features/messages/hooks/useConversation.js +18 -4
  101. package/dist/features/messages/hooks/useConversations.js +25 -8
  102. package/dist/features/orders/components/RefundRequestView.js +5 -2
  103. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -1
  104. package/dist/features/pre-orders/components/PreOrderActionsClient.js +1 -1
  105. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +4 -1
  106. package/dist/features/products/components/PrizeDrawDetailPageView.js +4 -1
  107. package/dist/features/products/components/ProductDetailPageView.js +8 -2
  108. package/dist/features/products/components/ShareButton.js +3 -1
  109. package/dist/features/products/repository/loadProductFeatures.js +10 -2
  110. package/dist/features/products/repository/products.repository.js +2 -1
  111. package/dist/features/promotions/components/CouponCard.js +3 -1
  112. package/dist/features/reviews/actions/review-actions.js +8 -1
  113. package/dist/features/reviews/components/ReviewDetailPageView.js +13 -3
  114. package/dist/features/reviews/components/ReviewDetailShell.js +3 -1
  115. package/dist/features/scams/actions/scam-actions.d.ts +9 -1
  116. package/dist/features/scams/actions/scam-actions.js +52 -12
  117. package/dist/features/scams/components/SellerTrustBadge.d.ts +1 -1
  118. package/dist/features/scams/components/SellerTrustBadge.js +13 -0
  119. package/dist/features/scams/repository/scammer.repository.js +2 -1
  120. package/dist/features/seller/actions/offer-actions.js +6 -1
  121. package/dist/features/seller/components/FulfillmentView.js +5 -2
  122. package/dist/features/seller/components/PhysicalLocationModal.js +1 -1
  123. package/dist/features/seller/components/PrintCenterView.js +3 -1
  124. package/dist/features/seller/components/SellerBidsView.js +1 -1
  125. package/dist/features/seller/components/SellerCouponsView.js +1 -1
  126. package/dist/features/seller/components/SellerGoogleReviewsView.js +2 -2
  127. package/dist/features/seller/components/SellerOrdersView.js +3 -3
  128. package/dist/features/seller/components/SellerPayoutMethodsView.js +2 -2
  129. package/dist/features/seller/components/SellerProductShell.d.ts +5 -0
  130. package/dist/features/seller/components/SellerProductShell.js +15 -5
  131. package/dist/features/seller/components/SellerProductsView.js +24 -10
  132. package/dist/features/seller/components/SellerReviewsView.js +47 -16
  133. package/dist/features/seller/components/SellerShippingConfigsView.js +5 -4
  134. package/dist/features/seller/components/SellerStoreCategoriesView.js +3 -2
  135. package/dist/features/stores/components/StoreDetailLayoutView.d.ts +1 -1
  136. package/dist/features/stores/components/StoreDetailLayoutView.js +12 -3
  137. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +16 -1
  138. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +3 -2
  139. package/dist/features/wishlist/hooks/useWishlistToggle.js +8 -1
  140. package/dist/index.d.ts +1 -1
  141. package/dist/index.js +5 -2
  142. package/dist/next/api/routeHandler.js +3 -1
  143. package/dist/providers/auth-firebase/session.js +3 -1
  144. package/dist/providers/db-firebase/admin-app-lite.js +8 -8
  145. package/dist/providers/db-firebase/admin.js +12 -12
  146. package/dist/providers/db-firebase/browser-stub.d.ts +1 -11
  147. package/dist/providers/db-firebase/browser-stub.js +6 -11
  148. package/dist/providers/db-firebase/index.d.ts +0 -1
  149. package/dist/providers/db-firebase/index.js +0 -1
  150. package/dist/providers/db-firebase/rtdb-paths.d.ts +15 -6
  151. package/dist/providers/db-firebase/rtdb-paths.js +15 -6
  152. package/dist/providers/db-firebase/rtdb-url.d.ts +27 -0
  153. package/dist/providers/db-firebase/rtdb-url.js +50 -0
  154. package/dist/providers/firebase-client/realtime.d.ts +20 -0
  155. package/dist/providers/firebase-client/realtime.js +34 -1
  156. package/dist/providers.d.ts +1 -1
  157. package/dist/providers.js +1 -1
  158. package/dist/react/hooks/use-action-dispatch.js +8 -2
  159. package/dist/react/hooks/useFormStatePreservation.js +3 -1
  160. package/dist/react/hooks/useRealtimeEvent.js +20 -9
  161. package/dist/ui/components/toolbar-collapse.js +1 -2
  162. package/package.json +1 -1
@@ -27,7 +27,6 @@ export { firebaseFieldOps } from "./field-ops";
27
27
  export { FirebaseRepository } from "./base";
28
28
  export { BaseRepository } from "./base.repository";
29
29
  export { FirebaseSieveRepository } from "./sieve";
30
- export { FirebaseRealtimeRepository } from "./realtime";
31
30
  // Re-export RTDB paths
32
31
  export { RTDB_PATHS } from "./rtdb-paths";
33
32
  import { FirebaseRepository as _FirebaseRepository } from "./base";
@@ -1,13 +1,22 @@
1
1
  /**
2
- * Shared Realtime Database path constants for auth/payment/bulk event channels.
2
+ * Shared Realtime Database path constants for the auth/payment/bulk event channels.
3
+ *
4
+ * 🛑 Only paths that are actually referenced through this constant belong here.
5
+ * Five keys were removed on 2026-08-29 — `PRESENCE`, `CHAT`, `NOTIFICATIONS`,
6
+ * `LIVE_UPDATES`, `SEED_EVENTS` — every one with **zero consumers**, because
7
+ * each live call site hardcodes its path string instead. A constant nobody uses
8
+ * is not a source of truth; it is a second place for the value to be wrong, and
9
+ * three of those five named RTDB nodes that no longer exist at all.
10
+ *
11
+ * If you add a key, migrate the call sites to it in the same change.
12
+ *
13
+ * This module has NO imports on purpose — that is what lets `browser-stub.ts`
14
+ * re-export it rather than keep the byte-for-byte duplicate it used to carry.
15
+ * The stub is the `browser` resolution target, so that duplicate meant client
16
+ * code and server code were reading two independently-maintained copies.
3
17
  */
4
18
  export declare const RTDB_PATHS: {
5
- readonly PRESENCE: "presence";
6
- readonly CHAT: "chat";
7
- readonly NOTIFICATIONS: "notifications";
8
- readonly LIVE_UPDATES: "live_updates";
9
19
  readonly AUTH_EVENTS: "auth_events";
10
20
  readonly PAYMENT_EVENTS: "payment_events";
11
21
  readonly BULK_EVENTS: "bulk_events";
12
- readonly SEED_EVENTS: "seed_events";
13
22
  };
@@ -1,13 +1,22 @@
1
1
  /**
2
- * Shared Realtime Database path constants for auth/payment/bulk event channels.
2
+ * Shared Realtime Database path constants for the auth/payment/bulk event channels.
3
+ *
4
+ * 🛑 Only paths that are actually referenced through this constant belong here.
5
+ * Five keys were removed on 2026-08-29 — `PRESENCE`, `CHAT`, `NOTIFICATIONS`,
6
+ * `LIVE_UPDATES`, `SEED_EVENTS` — every one with **zero consumers**, because
7
+ * each live call site hardcodes its path string instead. A constant nobody uses
8
+ * is not a source of truth; it is a second place for the value to be wrong, and
9
+ * three of those five named RTDB nodes that no longer exist at all.
10
+ *
11
+ * If you add a key, migrate the call sites to it in the same change.
12
+ *
13
+ * This module has NO imports on purpose — that is what lets `browser-stub.ts`
14
+ * re-export it rather than keep the byte-for-byte duplicate it used to carry.
15
+ * The stub is the `browser` resolution target, so that duplicate meant client
16
+ * code and server code were reading two independently-maintained copies.
3
17
  */
4
18
  export const RTDB_PATHS = {
5
- PRESENCE: "presence",
6
- CHAT: "chat",
7
- NOTIFICATIONS: "notifications",
8
- LIVE_UPDATES: "live_updates",
9
19
  AUTH_EVENTS: "auth_events",
10
20
  PAYMENT_EVENTS: "payment_events",
11
21
  BULK_EVENTS: "bulk_events",
12
- SEED_EVENTS: "seed_events",
13
22
  };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Resolve the Realtime Database URL from the environment.
3
+ *
4
+ * Its own module, with **no imports**, because both `admin.ts` and
5
+ * `admin-app-lite.ts` need it and `admin-app-lite` exists precisely to avoid
6
+ * pulling in the full admin module. They previously carried byte-for-byte
7
+ * duplicate resolution logic, which meant the bug below existed twice.
8
+ *
9
+ * 🛑 Returns `undefined` rather than guessing.
10
+ *
11
+ * All six copies used to fall back to `https://${projectId}-default-rtdb.firebaseio.com`.
12
+ * That is the **us-central1** URL shape; this project's instance lives at
13
+ * `…-default-rtdb.asia-southeast1.firebasedatabase.app`. So whenever the env var
14
+ * was missing, every server RTDB write silently targeted a database that does
15
+ * not exist — and since all nine RTDB writers wrap their call in a swallowing
16
+ * try/catch, messaging, bids, job status and payment signalling would every one
17
+ * of them degrade to "nothing ever updates", with clean warn-level logs.
18
+ *
19
+ * A guessed URL cannot be correct for a non-default region, so guessing buys
20
+ * nothing and costs the ability to notice. With no `databaseURL`,
21
+ * `getAdminRealtimeDb()` fails loudly at first use instead, which is the
22
+ * outcome you want.
23
+ *
24
+ * `||` not `??`: an env var that is present but empty must fall through, and
25
+ * `??` only falls through on null/undefined.
26
+ */
27
+ export declare function resolveRtdbUrl(): string | undefined;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Resolve the Realtime Database URL from the environment.
3
+ *
4
+ * Its own module, with **no imports**, because both `admin.ts` and
5
+ * `admin-app-lite.ts` need it and `admin-app-lite` exists precisely to avoid
6
+ * pulling in the full admin module. They previously carried byte-for-byte
7
+ * duplicate resolution logic, which meant the bug below existed twice.
8
+ *
9
+ * 🛑 Returns `undefined` rather than guessing.
10
+ *
11
+ * All six copies used to fall back to `https://${projectId}-default-rtdb.firebaseio.com`.
12
+ * That is the **us-central1** URL shape; this project's instance lives at
13
+ * `…-default-rtdb.asia-southeast1.firebasedatabase.app`. So whenever the env var
14
+ * was missing, every server RTDB write silently targeted a database that does
15
+ * not exist — and since all nine RTDB writers wrap their call in a swallowing
16
+ * try/catch, messaging, bids, job status and payment signalling would every one
17
+ * of them degrade to "nothing ever updates", with clean warn-level logs.
18
+ *
19
+ * A guessed URL cannot be correct for a non-default region, so guessing buys
20
+ * nothing and costs the ability to notice. With no `databaseURL`,
21
+ * `getAdminRealtimeDb()` fails loudly at first use instead, which is the
22
+ * outcome you want.
23
+ *
24
+ * `||` not `??`: an env var that is present but empty must fall through, and
25
+ * `??` only falls through on null/undefined.
26
+ */
27
+ export function resolveRtdbUrl() {
28
+ // 🛑 Emulator first.
29
+ //
30
+ // `firebase.json` has declared a database emulator on port 9000 for a long
31
+ // time and NOTHING ever connected to it. Client writes were shielded only
32
+ // because every rule is `.write: false`; server writes use the Admin SDK and
33
+ // bypass rules entirely — so `npm run dev` wrote to PRODUCTION. A local bid
34
+ // overwrote the prod `auction-bids` node that live auction pages subscribe to,
35
+ // and a local message send wrote prod `chats/**`.
36
+ //
37
+ // `FIREBASE_DATABASE_EMULATOR_HOST` is the standard variable the Firebase CLI
38
+ // exports for exactly this, so honouring it costs one branch and makes
39
+ // `firebase emulators:start` do what everyone already assumed it did.
40
+ const emulatorHost = process.env.FIREBASE_DATABASE_EMULATOR_HOST?.trim();
41
+ if (emulatorHost) {
42
+ const ns = process.env.FIREBASE_ADMIN_PROJECT_ID?.trim() ||
43
+ process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID?.trim() ||
44
+ "default";
45
+ return `http://${emulatorHost}?ns=${ns}`;
46
+ }
47
+ return (process.env.FIREBASE_ADMIN_DATABASE_URL?.trim() ||
48
+ process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL?.trim() ||
49
+ undefined);
50
+ }
@@ -11,7 +11,27 @@ export interface FirebaseClientRealtimeConfig {
11
11
  export declare class FirebaseClientRealtimeProvider implements IClientRealtimeProvider {
12
12
  private app;
13
13
  private db;
14
+ private readonly config;
15
+ private readonly appName;
16
+ private readonly scoped;
14
17
  constructor(config: FirebaseClientRealtimeConfig);
18
+ /**
19
+ * A provider bound to its OWN Firebase app for this claim scope.
20
+ *
21
+ * 🛑 This is what makes concurrent channels safe. Firebase Auth state is
22
+ * per-app, so with one shared app every `signInWithToken` REPLACED the claims
23
+ * of every other live subscriber: a bulk job signing in with a
24
+ * `{ bulkJobId }`-only token instantly stripped `conversationIds` from an open
25
+ * messages subscription, and vice versa. Whoever signed in last won.
26
+ *
27
+ * Reference-counting fixed only the sign-OUT half of that (a finishing job no
28
+ * longer ends someone else's session). Sign-IN needed separate apps, because
29
+ * there is nothing to coordinate — two channels legitimately need two
30
+ * different claim sets at the same time.
31
+ *
32
+ * Apps are cached per scope and reused, so a remount does not leak one.
33
+ */
34
+ forScope(scope: string): IClientRealtimeProvider;
15
35
  signInWithToken(token: string): Promise<void>;
16
36
  signOut(): Promise<void>;
17
37
  subscribe(path: string, onData: (snapshot: RealtimeSnapshot) => void, onError?: (error: Error) => void): Unsubscribe;
@@ -7,12 +7,44 @@ import { getAuth, signInWithCustomToken, signOut } from "firebase/auth";
7
7
  */
8
8
  export class FirebaseClientRealtimeProvider {
9
9
  constructor(config) {
10
+ this.scoped = new Map();
10
11
  const appName = config.appName ?? "appkit-realtime";
12
+ this.config = config;
13
+ this.appName = appName;
11
14
  this.app =
12
15
  getApps().find((a) => a.name === appName) ??
13
16
  initializeApp(config.firebaseConfig, appName);
14
17
  this.db = getDatabase(this.app);
15
18
  }
19
+ /**
20
+ * A provider bound to its OWN Firebase app for this claim scope.
21
+ *
22
+ * 🛑 This is what makes concurrent channels safe. Firebase Auth state is
23
+ * per-app, so with one shared app every `signInWithToken` REPLACED the claims
24
+ * of every other live subscriber: a bulk job signing in with a
25
+ * `{ bulkJobId }`-only token instantly stripped `conversationIds` from an open
26
+ * messages subscription, and vice versa. Whoever signed in last won.
27
+ *
28
+ * Reference-counting fixed only the sign-OUT half of that (a finishing job no
29
+ * longer ends someone else's session). Sign-IN needed separate apps, because
30
+ * there is nothing to coordinate — two channels legitimately need two
31
+ * different claim sets at the same time.
32
+ *
33
+ * Apps are cached per scope and reused, so a remount does not leak one.
34
+ */
35
+ forScope(scope) {
36
+ const existing = this.scoped.get(scope);
37
+ if (existing)
38
+ return existing;
39
+ // Firebase app names are freeform, but keep them predictable in devtools.
40
+ const safe = scope.replace(/[^a-zA-Z0-9_-]/g, "-");
41
+ const child = new FirebaseClientRealtimeProvider({
42
+ ...this.config,
43
+ appName: `${this.appName}-${safe}`,
44
+ });
45
+ this.scoped.set(scope, child);
46
+ return child;
47
+ }
16
48
  async signInWithToken(token) {
17
49
  await signInWithCustomToken(getAuth(this.app), token);
18
50
  }
@@ -21,8 +53,9 @@ export class FirebaseClientRealtimeProvider {
21
53
  await signOut(getAuth(this.app));
22
54
  }
23
55
  catch (_err) {
56
+ // A secondary Firebase app that was never signed in has nothing to sign
57
+ // out of; the caller's goal (no credential on this app) already holds.
24
58
  void normalizeError(_err);
25
- // no-op — signing out of a secondary app can fail harmlessly
26
59
  }
27
60
  }
28
61
  subscribe(path, onData, onError) {
@@ -12,7 +12,7 @@
12
12
  export { registerProviders, getProviders } from "./contracts/index";
13
13
  export { configureMarketDefaults } from "./core/baseline-resolver";
14
14
  export { firebaseAuthProvider, firebaseSessionProvider, createSessionCookieFromToken, createSessionCookie, verifyIdToken, verifySessionCookie, createMiddlewareAuthChain, requireAuth, requireRole, requireAuthUser, requireRoleUser, getUserFromRequest, requireAuthFromRequest, requireRoleFromRequest, revokeUserTokens, } from "./providers/auth-firebase/index";
15
- export { firebaseDbProvider, getAdminApp, getAdminAuth, getAdminDb, getAdminStorage, getAdminRealtimeDb, _resetAdminSingletons, FirebaseRepository, BaseRepository, FirebaseSieveRepository, FirebaseRealtimeRepository, RTDB_PATHS, firebaseFieldOps, removeUndefined, prepareForFirestore, deserializeTimestamps, } from "./providers/db-firebase/index";
15
+ export { firebaseDbProvider, getAdminApp, getAdminAuth, getAdminDb, getAdminStorage, getAdminRealtimeDb, _resetAdminSingletons, FirebaseRepository, BaseRepository, FirebaseSieveRepository, RTDB_PATHS, firebaseFieldOps, removeUndefined, prepareForFirestore, deserializeTimestamps, } from "./providers/db-firebase/index";
16
16
  export type { DocumentSnapshot } from "firebase-admin/firestore";
17
17
  export { firebaseStorageProvider } from "./providers/storage-firebase/index";
18
18
  export { createResendProvider } from "./providers/email-resend/index";
package/dist/providers.js CHANGED
@@ -15,7 +15,7 @@ export { configureMarketDefaults } from "./core/baseline-resolver";
15
15
  // Auth (Firebase Admin)
16
16
  export { firebaseAuthProvider, firebaseSessionProvider, createSessionCookieFromToken, createSessionCookie, verifyIdToken, verifySessionCookie, createMiddlewareAuthChain, requireAuth, requireRole, requireAuthUser, requireRoleUser, getUserFromRequest, requireAuthFromRequest, requireRoleFromRequest, revokeUserTokens, } from "./providers/auth-firebase/index";
17
17
  // Database (Firebase Firestore / RTDB)
18
- export { firebaseDbProvider, getAdminApp, getAdminAuth, getAdminDb, getAdminStorage, getAdminRealtimeDb, _resetAdminSingletons, FirebaseRepository, BaseRepository, FirebaseSieveRepository, FirebaseRealtimeRepository, RTDB_PATHS, firebaseFieldOps, removeUndefined, prepareForFirestore, deserializeTimestamps, } from "./providers/db-firebase/index";
18
+ export { firebaseDbProvider, getAdminApp, getAdminAuth, getAdminDb, getAdminStorage, getAdminRealtimeDb, _resetAdminSingletons, FirebaseRepository, BaseRepository, FirebaseSieveRepository, RTDB_PATHS, firebaseFieldOps, removeUndefined, prepareForFirestore, deserializeTimestamps, } from "./providers/db-firebase/index";
19
19
  // Storage (Firebase Admin)
20
20
  export { firebaseStorageProvider } from "./providers/storage-firebase/index";
21
21
  // Email (Resend)
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { normalizeError } from "../../errors/normalize";
3
+ import { toUserMessage } from "../../errors/error-display-map";
3
4
  import { useCallback } from "react";
4
5
  import { useRouter } from "next/navigation";
5
6
  import { useToast } from "../../ui";
@@ -33,8 +34,13 @@ export function useActionDispatch(options) {
33
34
  showToast(action.successMessage, "success");
34
35
  }
35
36
  catch (err) {
36
- void normalizeError(err);
37
- showToast(action.errorMessage ?? (err instanceof Error ? err.message : "Something went wrong."), "error");
37
+ const e = normalizeError(err);
38
+ // The action's own errorMessage still wins — it is the most
39
+ // specific authored copy available. Only the tail changed: it now
40
+ // resolves the error's CODE and ends in a constant, instead of
41
+ // ending in the thrown value's developer-facing text.
42
+ showToast(action.errorMessage ??
43
+ toUserMessage(e.code, undefined, { fallback: "Something went wrong." }), "error");
38
44
  }
39
45
  break;
40
46
  }
@@ -28,10 +28,12 @@ export function useFormStatePreservation({ values, onRestore, stripFields = [],
28
28
  try {
29
29
  const decoded = JSON.parse(atob(encoded));
30
30
  onRestore(decoded);
31
+ // The payload is user-supplied via the URL, so corrupt input is expected
32
+ // rather than exceptional. Discarding it restores the empty form, which is
33
+ // the same state as arriving without one.
31
34
  }
32
35
  catch (_err) {
33
36
  void normalizeError(_err);
34
- // ignore corrupt payloads
35
37
  }
36
38
  }, [paramName, onRestore]);
37
39
  useEffect(() => {
@@ -2,7 +2,7 @@
2
2
  import { normalizeError } from "../../errors/normalize";
3
3
  import { useCallback, useEffect, useRef, useState } from "react";
4
4
  import { getClientRealtimeProvider, } from "../../contracts/client-realtime";
5
- import { acquireExternalRealtimeLease } from "../../contracts/realtime-session";
5
+ import { acquireRealtimeSession } from "../../contracts/realtime-session";
6
6
  import { RealtimeEventType, RealtimeEventStatus, RTDBPayloadStatus, } from "./realtime-event-constants";
7
7
  // Re-exported for existing client import paths — the canonical definitions
8
8
  // now live in realtime-event-constants.ts (a plain, non-"use client" module)
@@ -32,8 +32,10 @@ export function useRealtimeEvent(config) {
32
32
  const unsubscribeRef = useRef(null);
33
33
  const timeoutRef = useRef(null);
34
34
  const eventIdRef = useRef(null);
35
- /** Lease on the shared realtime session — see cleanup() for why. */
35
+ /** Lease on this event's realtime session — see cleanup() for why. */
36
36
  const releaseSessionRef = useRef(null);
37
+ /** Provider bound to THIS scope's app; subscribe through it, not the global. */
38
+ const scopedProviderRef = useRef(null);
37
39
  const cleanup = useCallback(() => {
38
40
  if (unsubscribeRef.current) {
39
41
  unsubscribeRef.current();
@@ -57,6 +59,7 @@ export function useRealtimeEvent(config) {
57
59
  // end one, and only once the last consumer has let go.
58
60
  releaseSessionRef.current?.();
59
61
  releaseSessionRef.current = null;
62
+ scopedProviderRef.current = null;
60
63
  }, []);
61
64
  const subscribe = useCallback((eventId, customToken) => {
62
65
  const { type, rtdbPath, realtimeProvider, timeoutMs = DEFAULT_TIMEOUT_MS, extractData, messages, onLogError, } = configRef.current;
@@ -72,12 +75,20 @@ export function useRealtimeEvent(config) {
72
75
  setData(null);
73
76
  (async () => {
74
77
  try {
75
- await provider.signInWithToken(customToken);
76
- // Hold a lease for as long as this subscription lives. This hook owns
77
- // its own sign-in (the token carries a job-scoped `bulkJobId` claim),
78
- // but it must still participate in the ref-count so its cleanup
79
- // cannot end a session another channel is relying on.
80
- releaseSessionRef.current = acquireExternalRealtimeLease();
78
+ // Sign in through the session, which gives this event its OWN Firebase
79
+ // app for the `event:*` scope.
80
+ //
81
+ // That isolation is the fix. Firebase Auth state is per-app, so while
82
+ // every channel shared one app, this hook's `{ bulkJobId }`-only token
83
+ // replaced the claims of any live messages or chat subscription the
84
+ // moment a job started — and theirs replaced this one's in return.
85
+ // Ref-counting had only stopped a FINISHING channel from signing the
86
+ // others out; it could not stop a STARTING one from signing itself in
87
+ // over them, because there was nothing to coordinate: two channels
88
+ // legitimately need two different claim sets at the same time.
89
+ const lease = await acquireRealtimeSession(`event:${type}:${eventId}`, () => Promise.resolve({ customToken, expiresAt: Date.now() + 3600000 }));
90
+ releaseSessionRef.current = lease.release;
91
+ scopedProviderRef.current = lease.provider;
81
92
  }
82
93
  catch (authErr) {
83
94
  void normalizeError(authErr);
@@ -102,7 +113,7 @@ export function useRealtimeEvent(config) {
102
113
  return;
103
114
  }
104
115
  setStatus(RealtimeEventStatus.PENDING);
105
- unsubscribeRef.current = provider.subscribe(`${rtdbPath}/${eventId}`, (snapshot) => {
116
+ unsubscribeRef.current = (scopedProviderRef.current ?? provider).subscribe(`${rtdbPath}/${eventId}`, (snapshot) => {
106
117
  if (!snapshot.exists())
107
118
  return;
108
119
  const raw = snapshot.val();
@@ -48,11 +48,10 @@ export function readCollapsedPref(id) {
48
48
  export function writeCollapsedPref(id, value) {
49
49
  try {
50
50
  window.sessionStorage.setItem(STORAGE_PREFIX + id, value ? "1" : "0");
51
+ // sessionStorage blocked (private browsing). Collapsing still works this render, it just will not survive a re-mount.
51
52
  }
52
53
  catch (_err) {
53
54
  void normalizeError(_err);
54
- // sessionStorage unavailable (private browsing, etc.) — collapsing still
55
- // works for the current render, it just doesn't survive a re-mount.
56
55
  }
57
56
  }
58
57
  export function defaultCollapsedForViewport() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "4.30.18",
3
+ "version": "4.30.19",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"